神经网络训练循环问题:权重转Series后取值及循环正确性排查
咱们一步步来解决你的这些问题,先还原你的代码场景:
样本数据与变量定义
import pandas as pd import numpy as np # 原始样本数据,复制生成100条观测值 data = [ [3.5, 1.5, 1], [2.0, 1.0, 0], [4.0, 1.5, 1], [3.0, 1.0, 0], [3.5, 0.5, 1], [2.0, 0.5, 0], [5.5, 1.0, 1], [1.0, 1.0, 0] ] data = pd.DataFrame(data, columns = ["Length", "Width", "Class"]) # 初始化权重与偏置 w1 = np.random.randn() w2 = np.random.randn() b = np.random.randn() print(w1) print(w2) print(b)
训练循环代码
# 注意:这里需要先定义sigmoid和sigmoid_p函数,否则会报错 def sigmoid(z): return 1 / (1 + np.exp(-z)) def sigmoid_p(z): return sigmoid(z) * (1 - sigmoid(z)) # 扩展数据集为100条 train_data = pd.concat([data]*12 + [data[:4]], ignore_index=True) learning_rate = 0.2 for x in range(50000): z = train_data["Length"] * w1 + train_data["Width"] + b # 这里有错误! preds = sigmoid(z) target = train_data["Class"] cost = np.square(preds - target) derivcost_pred = 2 * (preds - target) derivpred_sigp = sigmoid_p(z) dcost_dz = derivcost_pred * derivpred_sigp dz_dw1 = train_data["Length"] dz_dw2 = train_data["Width"] dz_db = 1 dcost_dw1 = dcost_dz * dz_dw1 dcost_dw2 = dcost_dz * dz_dw2 dcost_db = dcost_dz * dz_db w1 = w1 - learning_rate * dcost_dw1 w2 = w2 - learning_rate * dcost_dw2 b = b - learning_rate * dcost_db
问题1:如何获取训练完成后w1、w2、b的最终值?
你现在训练完后参数变成Series,核心原因是参数更新时用了单个样本的梯度而非平均梯度。正确的做法是在计算梯度后取所有样本的均值,这样参数始终保持单个数值类型,训练结束后直接打印就能拿到最终值。
修正后的参数更新逻辑:
# 计算梯度时取均值 dcost_dw1 = (dcost_dz * dz_dw1).mean() dcost_dw2 = (dcost_dz * dz_dw2).mean() dcost_db = dcost_dz.mean() # dz_db是1,直接取dcost_dz的均值 # 更新参数 w1 = w1 - learning_rate * dcost_dw1 w2 = w2 - learning_rate * dcost_dw2 b = b - learning_rate * dcost_db
训练完成后,直接执行print("最终权重:w1={}, w2={}, b={}".format(w1, w2, b))就能得到单一的最终值。
问题2:当权重变为Series类型时,如何访问其最后一个值?
如果已经因为代码错误让参数变成了Series,有几种简单的方式取最后一个元素:
- 用pandas的位置索引:
w1.iloc[-1](最推荐的标准写法) - 转成numpy数组后取最后一个:
w1.values[-1] - 快速访问单个位置:
w1.iat[-1](适合取单个元素,速度略快)
不过还是建议优先修正训练循环的逻辑,让参数保持数值类型,避免后续麻烦。
问题3:训练循环的逻辑/代码错误排查
这里有几个关键错误,按严重程度排序:
线性组合z的计算遗漏w2乘法
原代码中z = train_data["Length"] * w1 + train_data["Width"] + b,正确的线性组合应该是z = train_data["Length"] * w1 + train_data["Width"] * w2 + b——你完全漏掉了w2和Width的乘积项,这直接导致w2的更新毫无意义,等于模型没用到第二个特征!梯度未取平均,导致参数类型异常
批量梯度下降需要用所有样本的平均梯度更新参数,但你现在计算的dcost_dw1等是每个样本对应的梯度(一个Series),直接用它更新参数会让w1从单个数值变成和样本数量一样长的Series,完全不符合参数更新的逻辑。必须对梯度取均值,如问题1中的修正代码。变量名不匹配
你前面定义的数据集是data,但训练循环里用的是train_data,需要先把data扩展成100条观测值并赋值给train_data,比如:train_data = pd.concat([data]*12 + [data[:4]], ignore_index=True)缺少sigmoid及导数函数的定义
代码里用到了sigmoid(z)和sigmoid_p(z),但未给出定义,必须先补充这两个函数(我已经在前面的代码示例中补上了)。可选优化:损失函数选择
你现在用的是平方损失,对于逻辑回归(sigmoid激活的二分类任务),交叉熵损失会更合适,平方损失在sigmoid的饱和区梯度会非常小,导致训练缓慢。如果需要优化,可以把损失换成交叉熵:# 交叉熵损失 cost = - (target * np.log(preds) + (1 - target) * np.log(1 - preds)).mean() # 对应的梯度会更简洁,dcost_dz = preds - target,不需要再乘2*(preds-target)
内容的提问来源于stack exchange,提问作者Sid

