语义分割CNN训练报错:deconv2/biases含NaN及protobuf超限问题
解决TensorFlow语义分割训练中的NaN与Protobuf超限问题
针对你遇到的两个核心训练错误,结合你的硬件环境(4GB显存GTX960M、TensorFlow 1.4)和模型场景,以下是针对性的解决方案:
问题1:float16导致的Nan in summary histogram错误
根本原因
float16的数值范围(±65504)远小于float32(±3.4e38),在模型训练的梯度更新、权重初始化(tf.truncated_normal生成的数值可能在float16下精度丢失)或ReLU激活后的数值处理中,极易出现数值溢出/下溢,进而产生NaN,反映在summary直方图中。
解决方案
不要直接将整个模型输出强制转为float16,而是采用手动混合精度训练适配TF1.4:
- 模型内部计算保持float32,仅在显存存储时用float16:
# 修改conv_layer和deconv_layer的返回值,去掉强制转float16 # 改为仅在最终输出时转float16 def conv_layer(...): # ... 原有代码 ... return activation # 保留float32 def deconv_layer(...): # ... 原有代码 ... return activation # 保留float32 # 模型最终输出转float16 def semantic_seg_model(...): # ... 原有代码 ... return tf.cast(deconv, dtype=tf.float16) - 优化权重初始化方式:
替换tf.truncated_normal为更稳定的初始化器,避免初始权重过大导致激活值溢出:filter_tensor = tf.variance_scaling_initializer(scale=2.0)( [kernel_size[0], kernel_size[1], input_shape[3], filters] ) - 添加激活值裁剪:
用tf.nn.relu6代替tf.nn.relu,限制激活值范围,防止float16下溢出:activation = tf.nn.relu6(conv2d + bias)
问题2:Protobuf超过2GB限制错误
根本原因
你设置的tf.summary.image('input', features, batch_size)会一次性写入1000张28x28x3的图像到summary,再加上所有层的权重、偏置、激活值的直方图,summary文件体积会迅速膨胀,超过Protobuf的2GB单文件限制。即使调小batch size,23万+的总样本量累计写入后仍会触发问题。
解决方案
- 大幅削减summary写入量:
- 限制图像summary的输出数量:将
max_outputs从batch_size改为10,每次仅写入少量样本:tf.summary.image('input', features, max_outputs=10) - 关闭不必要的直方图:仅保留权重的直方图,去掉偏置和激活值的统计,减少summary体积:
# 在conv_layer和deconv_layer中,仅保留以下代码 tf.summary.histogram("weights", filter) # 移除tf.summary.histogram("biases", bias)和tf.summary.histogram("activations", activation)
- 限制图像summary的输出数量:将
- 间隔写入summary:
不要每个epoch都写入summary,每隔10个epoch写一次:for epoch in range(epochs): # ... 训练代码 ... if epoch % 10 == 0: s = sess.run(merged_summary) writer.add_summary(s, epoch) - 改用更高效的数据管道:
替换旧的tf.train.slice_input_producer队列API为tf.data.Dataset,降低显存占用和数据处理开销:dataset = tf.data.Dataset.from_tensor_slices((features, labels)) dataset = dataset.batch(256, drop_remainder=True) # 调小batch size到256 dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE) iterator = dataset.make_initializable_iterator() image_batch, label_batch = iterator.get_next() # 训练时初始化迭代器 sess.run(iterator.initializer)
针对你的硬件的额外优化
由于GTX960M只有4GB显存,TF1.4显存优化能力有限,建议:
- 保持简化后的模型结构(小滤波器尺寸、少滤波器数量),避免显存过载
- 调整学习率:你之前设置的
1e-50过小,模型根本无法更新,改为1e-4并添加学习率衰减:global_step = tf.Variable(0, trainable=False) learning_rate = tf.train.exponential_decay( 1e-4, global_step, 1000, 0.96, staircase=True ) optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost, global_step=global_step) - 关闭设备日志输出:
config.log_device_placement = True会增加显存占用,改为False
验证步骤
- 先禁用所有非必要的summary,仅保留损失和精度的标量统计
- 应用混合精度训练调整
- 调小batch size到256,设置合理的学习率
- 启动训练,观察是否还出现NaN或Protobuf错误
如果仍有问题,检查输入数据是否存在NaN/异常值,提前对图像块做归一化(如除以255转成0-1范围),避免数值过大导致的计算异常。
内容的提问来源于stack exchange,提问作者Fawad Khalil
相关产品推荐
相关产品推荐

