Keras+TensorFlow训练中TensorBoard梯度分布周期性尖峰问题
解决Keras 2.1.6+TF1.4.0 TensorBoard梯度直方图周期性尖峰问题
我之前也碰到过这个版本组合下的同款问题,结合你的配置细节,这个周期性尖峰的根源是Keras早期版本内置TensorBoard回调在步长级梯度记录时的逻辑缺陷。
问题原因
当你把histogram_freq设置为EPOCH_STEPS(也就是每个epoch的步数,相当于每步记录一次梯度)时,Keras的TensorBoard回调会在指定步长触发梯度统计,但这个过程中框架内部对梯度计算的上下文处理有bug:记录梯度的那个batch会被强制使用特殊的梯度计算路径,导致该batch的梯度值被异常统计(比如放大或者采样错误),而且这个错误会每隔histogram_freq步重复出现,正好和你观察到的现象一致。
可行的解决办法
1. 改为按epoch记录梯度(最简单方案)
如果不需要每步都记录梯度,把histogram_freq改为1(每个epoch记录一次),就能直接避开这个bug:
tensorboard = keras.callbacks.TensorBoard( log_dir=OUTPUT_PATH, histogram_freq=1, # 改为每个epoch记录一次梯度直方图 batch_size=BATCH_SIZE, write_grads=True )
这个改动对大多数场景来说足够用,而且不需要额外编写代码。
2. 自定义回调实现步长级梯度记录(若必须每步监控)
要是你确实需要步长级的梯度监控,可以绕过Keras内置的TensorBoard回调,自己实现梯度记录逻辑,彻底避开框架的bug:
from keras import backend as K import tensorflow as tf class CustomTensorBoard(keras.callbacks.Callback): def __init__(self, log_dir, histogram_freq, batch_size): super().__init__() self.log_dir = log_dir self.histogram_freq = histogram_freq self.batch_size = batch_size self.writer = tf.summary.FileWriter(log_dir) def on_batch_end(self, batch, logs=None): # 每隔指定步长记录一次梯度 if batch % self.histogram_freq == 0: # 遍历模型所有可训练权重,计算并记录梯度 for layer in self.model.layers: for weight in layer.trainable_weights: # 计算当前权重的梯度 grad_tensor = K.gradients(self.model.total_loss, weight)[0] grad_values = K.eval(grad_tensor) # 生成梯度直方图的summary grad_summary = tf.summary.histogram(f"{weight.name}/gradients", grad_values) # 写入TensorBoard,用优化器的迭代步数作为全局步长 self.writer.add_summary(grad_summary, self.model.optimizer.iterations.eval()) self.writer.flush() def on_train_end(self, logs=None): self.writer.close()
然后替换原来的回调即可:
tensorboard = CustomTensorBoard( log_dir=OUTPUT_PATH, histogram_freq=EPOCH_STEPS, batch_size=BATCH_SIZE )
3. 升级框架版本(根治问题)
这个bug在Keras 2.2.0以上版本和TensorFlow 1.10.0以上版本已经被官方修复,如果你的项目允许升级依赖,直接更新Keras和TensorFlow后,原来的配置就能正常工作,不会再出现尖峰。
验证建议
修改后重新启动训练,等待几个周期后打开TensorBoard查看梯度直方图,确认周期性尖峰是否消失。
内容的提问来源于stack exchange,提问作者Alvise
相关产品推荐
相关产品推荐

