gdyshi | 孤独隐士 MindMap, Machine Learning, Medical Equipment

tensorflow使用高阶api导致训练不收敛问题


摘要

本文将低级api实现的tensorflow网络移植到高级api上遇到的loss值不变和训练结果不收敛问题

引言

tensorflow版本更新很快,猛一回头发现已经推出更高级的api了

主题

tensorflow高级api

api

上图是tensorflow软件栈图,我之前学习和实现的网络模型(0.12a)使用的是 低级api, 现在的新版本(1.10)对低级api进行了封装,形成了高级api(estimator keras),所以对原有模型进行了一次api替换

移植

整个代码的移植过程还是比较顺利的,移植完成以后,代码量比以前减少了很多,但移植完成,在运行时却发现了一些奇怪的现象

问题现象

我这里有两个自己的数据集,其中一个数据集(单个数据量130)在移植后的代码上运行正常,另一个数据集(单个数据集60000)在移植后的代码上运行却出现以下问题:

  • 二分类问题的准确率在50%左右
  • 训练过程中loss值会变化到一个固定值,然后就不再变化了

api

epoch:0

Evaluation results:	{'true_negatives': 48.0, 'global_step': 0, 'loss': 0.7953733, 'true_positives': 0.0, 'accuracy': 0.48, 'false_negatives': 52.0, 'false_positives': 0.0}

epoch:1

Evaluation results:	{'true_negatives': 0.0, 'global_step': 1, 'loss': 0.79326165, 'true_positives': 52.0, 'accuracy': 0.52, 'false_negatives': 0.0, 'false_positives': 48.0}

epoch:2

Evaluation results:	{'true_negatives': 0.0, 'global_step': 2, 'loss': 0.79326165, 'true_positives': 52.0, 'accuracy': 0.52, 'false_negatives': 0.0, 'false_positives': 48.0}

epoch:3

Evaluation results:	{'true_negatives': 0.0, 'global_step': 3, 'loss': 0.79326165, 'true_positives': 52.0, 'accuracy': 0.52, 'false_negatives': 0.0, 'false_positives': 48.0}

epoch:4

Evaluation results:	{'true_negatives': 0.0, 'global_step': 4, 'loss': 0.79326165, 'true_positives': 52.0, 'accuracy': 0.52, 'false_negatives': 0.0, 'false_positives': 48.0}

epoch:5

Evaluation results:	{'true_negatives': 0.0, 'global_step': 5, 'loss': 0.79326165, 'true_positives': 52.0, 'accuracy': 0.52, 'false_negatives': 0.0, 'false_positives': 48.0}

问题分析

因为不同数据集对应的结论不同,所以问题的排查就主要集中在对比两份代码的差异上了。训练正常的代码简称为T代码,训练异常的代码简称为F代码

  • 网络排查

    怀疑模型代码有问题。将F代码尽量用T代码代替,最终替换后,只剩下tfrecord文件读取和解析、网络输入层不一样,然后再训练更新后的F代码,现象依然存在

  • 数据排查

    通过网络排查基本排除网络问题,唯一不同在于数据,于是进行数据验证。将F代码训练过程中的输入数据记录到文件,然后对比F代码读到的数据和制作tfrecord的数据。排查数据编号、数据内容是否一致。最终发现数据一致。 ``` class _LoggerHook(tf.train.SessionRunHook): “"”Logs loss and runtime.”””

    def begin(self): # print(‘begin’) self._step = -1

    def before_run(self, run_context): # print(‘before_run’) self._step += 1 return tf.train.SessionRunArgs(features) # Asks for loss value.

    def after_run(self, run_context, run_values): if self._step == 2: logit_value = run_values.results print(‘step ‘ + str(self._step) + ‘, features = ‘ + str(logit_value)) f1.write(logit_value[‘data’]) # 训练准确率写入文件 f1.flush() numpy.savetxt(r”/home/zq537/ckpt/ecg_data.txt”, logit_value[‘data’]) numpy.savetxt(r”/home/zq537/ckpt/index.txt”, logit_value[‘name’]) ```

  • 内部训练过程排查

    如果网络和数据都没问题,那么问题排查起来就比较困难了。接下来的方向可能需要深入模型内部的训练过程,看哪些步骤导致loss和准确率不变,将所有操作的输出和反向传播的梯度都记录到tensorboard中进行查看,发现进行少量的训练后,反向传播的梯度值分布都在0附近,这样网络权重基本就不会更新了,网络参数没有变化,自然准确率和loss也不会变化了。api 是什么原因导致梯度为0?梯度是从loss开始,一层一层往前传的;而loss是由预测值和实际标签共同决定的。于是我开始查看预测值和实际标签的数值。先把数据集进行简化,生成10个数据的数据集,batch-size设为10,然后在训练回调中把稳定(loss不变)时的预测值和实际标签打印出来,发现了问题:稳定状态下预测值大部分两分类的准确率都是1,这很明显是给网络的评判标准(loss函数)有问题 ``` labels = [[1 0] [1 0] [0 1] [1 0] [1 0] [0 1] [0 1] [0 1] [0 1] [0 1]]

    logits = [[1.0000000e+00 1.0000000e+00] [1.0000000e+00 1.0000000e+00] [1.0000000e+00 1.0000000e+00] [1.0000000e+00 1.4896728e-36] [1.0000000e+00 5.1295980e-18] [1.0000000e+00 1.0000000e+00] [1.0000000e+00 1.0000000e+00] [1.0000000e+00 1.0000000e+00] [1.0000000e+00 1.0000000e+00] [1.0000000e+00 1.0000000e+00]] ```

    新代码中使用的损失函数是tf.losses.softmax_cross_entropy(onehot_labels=labels, logits=logits),搜索并替换为tensorflow官方module库中的损失函数tf.losses.sparse_softmax_cross_entropy(labels=labels, logits=logits)再进行训练,发现一切正常

问题原因

  • 网络排查
  • 数据排查
  • 内部训练过程排查

解决方案

总结

  • 在pypi官网上找相关模块信息

    最开始在网上搜到的方案是ConcurrentLogHandler,但在13年就停止维护了, 合入代码也无法运行。于是又在网上找其他方案(这里浪费了不少时间),其实 ConcurrentLogHandler的homepage页已经说明了替代的库

附录

参考



Content