TensorFlow中损失值始终不变的问题排查求助
我正在尝试使用TensorFlow搭建神经网络以更好地学习该库,但我的损失值始终保持不变。以下是我的代码:
import tensorflow as tf import numpy as np import pandas as pd import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' all_data = pd.read_csv('/projects/data/testfile.csv') all_data = all_data.values size_layer1 = 1 size_layer2 = 10 size_layer3 = 1 labels = all_data[:, 9]; labels = tf.convert_to_tensor(labels, np.float32); labels = tf.reshape(labels, [985, 1]) data = all_data[:, 6]; data = tf.convert_to_tensor(data, np.float32) theta1 = tf.Variable(tf.zeros([size_layer2, size_layer1])); theta1 = tf.reshape(theta1, [10, 1]) theta2 = tf.Variable(tf.zeros([size_layer3, size_layer2])); theta2 = tf.reshape(theta2, [1, 10]) a1 = data; a1 = tf.reshape(a1, [1, 985]) z2 = tf.matmul(theta1, a1) a2 = tf.nn.relu(z2) z3 = tf.matmul(theta2, a2) a3 = tf.nn.sigmoid(z3) h = tf.transpose(a3) cost = tf.losses.mean_squared_error(labels, h) train = tf.train.GradientDescentOptimizer(0.01).minimize(cost) init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) for i in range(10): sess.run(train) print(sess.run(cost))
我的数据集为985×12的矩阵,但多数列是文本类型,因此我提取了两列数据。我清楚这种1:10:1节点结构搭配实数值标签的神经网络设计并不合理,但我并非要优化网络,只是学习TensorFlow语法。我也知道应该进行特征缩放/均值归一化,但同样并非要完美优化网络。
我的输出结果为:
73948990000.0 73948990000.0 73948990000.0 73948990000.0 73948990000.0 73948990000.0 73948990000.0 73948990000.0 73948990000.0 73948990000.0
我已经尝试了多种方法:最初使用普通交叉熵作为损失函数,但由于数据是实数值,我改为均方误差(mean squared error);也尝试更换优化器,但均无效果。请问问题出在网络架构设计不佳,还是其他原因?
咱们先从代码细节里找问题,你遇到的损失不更新,核心原因不是架构简单,而是初始化、维度匹配和激活函数选择这几个基础错误,和架构关系不大:
1. 全0权重初始化导致梯度消失
你把theta1和theta2都初始化成了全0张量:
theta1 = tf.Variable(tf.zeros([size_layer2, size_layer1])) theta2 = tf.Variable(tf.zeros([size_layer3, size_layer2]))
当所有权重都是0时,第一层的所有神经元输出完全相同,反向传播时所有权重的梯度也完全一致,权重永远不会产生差异,网络根本无法学习——这是神经网络初始化的经典坑!
解决方法:用随机初始化替代全0,比如正态分布初始化:
theta1 = tf.Variable(tf.random.normal([size_layer2, size_layer1], mean=0, stddev=0.1)) theta2 = tf.Variable(tf.random.normal([size_layer3, size_layer2], mean=0, stddev=0.1))
2. 输入与权重的矩阵乘法维度逻辑混乱
你的输入维度处理不符合常规训练逻辑:
a1被reshape成[1, 985](1个特征,985个样本),但通常我们会把样本放在第一维度([样本数, 特征数]),这样计算和梯度传播更直观,也不容易出错。- 后续的矩阵乘法虽然维度能运算,但最终输出的转置操作容易掩盖逻辑问题,导致梯度传播异常。
调整输入与权重维度:
# 把data改成[985, 1](985个样本,每个样本1个特征) data = tf.reshape(data, [985, 1]) # theta1改成[1, 10](输入特征数1,输出神经元数10) theta1 = tf.Variable(tf.random.normal([size_layer1, size_layer2], mean=0, stddev=0.1)) # z2计算为[985,1] × [1,10] = [985,10],符合样本在前的逻辑 z2 = tf.matmul(data, theta1) a2 = tf.nn.relu(z2) # theta2改成[10,1](输入10个神经元,输出1个) theta2 = tf.Variable(tf.random.normal([size_layer2, size_layer3], mean=0, stddev=0.1)) z3 = tf.matmul(a2, theta2) # 此时a3直接是[985,1],和labels维度完全匹配,不需要转置
3. Sigmoid激活函数不适合回归任务输出层
你的标签是实数值,但Sigmoid的输出范围固定在[0,1],如果你的标签值远大于1或者小于0,模型根本无法拟合,损失自然不会变化。对于回归任务,输出层应该用线性输出:
# 去掉sigmoid,直接用线性输出 a3 = z3
4. 学习率太小且训练轮数不足
你用了0.01的学习率,而且只训练10轮,就算前面的问题都解决了,这么少的轮数也很难看到损失变化。可以先把学习率调到0.1,训练轮数增加到100或者更多,观察损失是否下降。
调整后的完整代码示例
import tensorflow as tf import numpy as np import pandas as pd import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' all_data = pd.read_csv('/projects/data/testfile.csv') all_data = all_data.values size_layer1 = 1 size_layer2 = 10 size_layer3 = 1 # 处理标签和输入数据 labels = all_data[:, 9] labels = tf.convert_to_tensor(labels, dtype=tf.float32) labels = tf.reshape(labels, [985, 1]) data = all_data[:, 6] data = tf.convert_to_tensor(data, dtype=tf.float32) data = tf.reshape(data, [985, 1]) # 样本数在前,特征数在后 # 随机初始化权重,避免全0 theta1 = tf.Variable(tf.random.normal([size_layer1, size_layer2], mean=0, stddev=0.1)) theta2 = tf.Variable(tf.random.normal([size_layer2, size_layer3], mean=0, stddev=0.1)) # 前向传播 z2 = tf.matmul(data, theta1) a2 = tf.nn.relu(z2) z3 = tf.matmul(a2, theta2) a3 = z3 # 回归任务用线性输出 # 计算损失和优化器 cost = tf.losses.mean_squared_error(labels, a3) train = tf.train.GradientDescentOptimizer(0.1).minimize(cost) # 增大学习率 init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) for i in range(100): # 增加训练轮数 sess.run(train) if (i+1) % 10 == 0: # 每10轮打印一次损失 print(f"第{i+1}轮损失: {sess.run(cost)}")
总结
你的核心问题不是架构简单,而是权重全0初始化、维度匹配错误、激活函数选择不当这几个基础问题。调整后应该能看到损失值开始下降,虽然因为数据没做归一化可能下降得不够平滑,但至少能验证TensorFlow的训练逻辑是正常的。
内容的提问来源于stack exchange,提问作者Ronan Venkat

