TensorFlow GradientDescentOptimizer未更新tf.Variables问题求助
问题排查与修复方案
嘿,我帮你梳理了代码里的几个关键问题,正是这些问题导致权重始终没变化,咱们一个个来解决:
1. 损失函数的形状不兼容问题
你现在把每个样本的预测值存在y_pred_col列表里,直接用y_ - y_pred_col会出问题——y_是形状为(19,10,1)的张量,而y_pred_col是19个(10,1)张量的列表,TensorFlow没法直接对列表和张量做减法。得把列表拼接成和y_同形状的张量才行:
y_pred_col = [] for n_batch in range(0, len(x_data)): y_pred = neural(x[n_batch]) y_pred_col.append(y_pred) # 把列表转成(19,10,1)的张量,和y_形状匹配 y_pred_tensor = tf.stack(y_pred_col, axis=0) loss = tf.reduce_mean(tf.square(y_ - y_pred_tensor))
2. Sigmoid激活导致的梯度消失
你的输出层用了sigmoid,但股价数据一般都是几十、几百的数值,sigmoid会把输出死死压缩在0-1之间,这会让预测值和真实值差十万八千里,而且sigmoid输出接近0或1的时候,梯度几乎为0(进入饱和区),权重根本没法更新。
解决办法:
- 输出层删掉
sigmoid,回归任务不需要这个(它是给二分类用的),直接用线性输出:def neural(X): a01 = tf.matmul(W01, X) + b01 X2 = tf.sigmoid(a01) a02 = tf.matmul(W02, X2) + b02 X3 = tf.sigmoid(a02) a03 = tf.matmul(W03, X3) + b03 y_prediction = a03 # 这里去掉sigmoid,用线性输出 return y_prediction - 给数据做归一化!把股价缩到
0-1或者-1-1区间,比如用MinMaxScaler,这样既不会让激活函数饱和,梯度更新也会更稳定。
3. 学习率太小了
你用的0.0005学习率对于回归任务来说太小了,尤其是数据没归一化的情况下,梯度更新的步长小到根本动不了权重。先调到0.01试试,之后根据损失的变化再微调。
4. 权重初始化的问题
你用tf.random_uniform把初始权重设为0-1的范围,对于sigmoid来说,初始权重太大容易让输入到sigmoid的值直接爆炸,进入饱和区。可以试试更小的初始化范围,比如(-0.1, 0.1),或者用Xavier初始化(专门针对激活函数的初始化方法):
W01 = tf.Variable(tf.glorot_uniform_initializer()([n_nodes_l1, 15], dtype=tf.float64), name="W01")
修复后的完整代码示例
import tensorflow as tf from sklearn.preprocessing import MinMaxScaler import numpy as np # 先给数据做归一化(假设x_data、y_data是你的原始数据) scaler_x = MinMaxScaler() scaler_y = MinMaxScaler() # 调整形状适配归一化工具,之后再转回来 x_data_scaled = scaler_x.fit_transform(x_data.reshape(-1, 15)).reshape(19,15,1) y_data_scaled = scaler_y.fit_transform(y_data.reshape(-1, 10)).reshape(19,10,1) # 输入输出占位符 x = tf.placeholder(tf.float64, shape=(19,15,1), name = 'Input') y_ = tf.placeholder(tf.float64, shape=(19,10,1), name = 'Output') # 网络参数 n_layers = 3 n_nodes_l1 = 20 n_nodes_l2 = 30 n_nodes_l3 = 10 # 用Xavier初始化权重,避免激活函数饱和 W01 = tf.Variable(tf.glorot_uniform_initializer()([n_nodes_l1, 15], dtype=tf.float64), name="W01") W02 = tf.Variable(tf.glorot_uniform_initializer()([n_nodes_l2, n_nodes_l1], dtype=tf.float64),name='W02') W03 = tf.Variable(tf.glorot_uniform_initializer()([n_nodes_l3, n_nodes_l2], dtype=tf.float64),name='W03') # 偏置用0初始化就行 b01 = tf.Variable(tf.zeros([n_nodes_l1,1], dtype=tf.float64),name='b01') b02 = tf.Variable(tf.zeros([n_nodes_l2,1], dtype=tf.float64),name='b02') b03 = tf.Variable(tf.zeros([n_nodes_l3,1], dtype=tf.float64),name='b03') # 网络结构 def neural(X): a01 = tf.matmul(W01, X) + b01 X2 = tf.sigmoid(a01) a02 = tf.matmul(W02, X2) + b02 X3 = tf.sigmoid(a02) a03 = tf.matmul(W03, X3) + b03 y_prediction = a03 # 输出层用线性激活 return y_prediction # 计算所有样本的预测值和损失 y_pred_col = [] for n_batch in range(0, len(x_data_scaled)): y_pred = neural(x[n_batch]) y_pred_col.append(y_pred) # 拼接成统一形状的张量 y_pred_tensor = tf.stack(y_pred_col, axis=0) loss = tf.reduce_mean(tf.square(y_ - y_pred_tensor)) # 调大学习率 optimizer = tf.train.GradientDescentOptimizer(0.01).minimize(loss) # 启动会话训练 sess = tf.Session() init = tf.global_variables_initializer() sess.run(init) n_steps = 300 # 可以多训练几步看看效果 for iter in range(n_steps): _, l, W01_train = sess.run([optimizer, loss, W01], feed_dict = {x: x_data_scaled, y_: y_data_scaled}) if iter % 10 == 0: print(f"训练步数 {iter},当前损失: {l}") # 预测后记得反归一化,得到真实股价 y_pred_scaled = sess.run(y_pred_tensor, feed_dict={x: x_data_scaled}) y_pred = scaler_y.inverse_transform(y_pred_scaled.reshape(-1,10)).reshape(19,10,1)
额外小建议
- 试试批量训练,每次取几个样本(比如3-5个)而不是一次性用全部19个,梯度更新会更稳定。
- 把隐藏层的sigmoid换成ReLU,ReLU的梯度消失问题比sigmoid轻很多。
- 可以加个L2正则化,防止模型过拟合(股价预测很容易出现过拟合)。
内容的提问来源于stack exchange,提问作者kkt21
相关产品推荐
相关产品推荐

