You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:53:56