



























我们采用 1输入+1隐藏+2输出 的网络结构,全程用具体数值示例拆解前向传播、反向传播的每一步计算,让抽象过程直观化。
解决的问题是,根据一个输入的数字,判断其是正数还是负数。
借此来简单了解神经网络的过程。
| 网络层 | 神经元数 | 说明 |
|---|---|---|
| 输入层 | 1 | 输入 $x$(如 $3$、$-2$) |
| 隐藏层 | 1 | 用Sigmoid激活 |
| 输出层 | 2 | 用Softmax输出概率($y_1$=正数,$y_2$=负数),故[1,0]为正数,[0,1]为负数。 |
神经网络的加权计算:z=wx+b。整个过程就是根据训练数据,确定w和b的值。
神经网络的计算先从一组随机参数的计算开始。使用这组随机数计算出输出后,再根据与目标值的差距,反向调整前面各个参数。
下面的演示先从如下的值开始计算。
| 参数 | 示例值 | 含义 |
|---|---|---|
| $w_1$ | $0.5$ | 输入→隐藏层权重 |
| $b_1$ | $0.1$ | 隐藏层偏置 |
| $w_{21},w_{22}$ | $0.3, -0.2$ | 隐藏→输出层两个节点权重 |
| $b_{21},b_{22}$ | $0.2, 0.4$ | 输出层两个节点偏置 |
选择1个正数样本和1个负数样本,标签用one-hot编码:
前向传播是从输入到输出的计算,是预测的核心。
$L = -(1 \times \ln(0.554)+0 \times \ln(0.446)) \approx -(-0.589) \approx 0.589$
反向传播通过链式法则求梯度,用梯度下降更新参数,核心是最小化损失。
用随机生成的参数计算完之后,跟目标值对比出差距,然后再根据这个差距反推回前几层,得到调整的参数。
更新公式:$w_{new}=w_{old} - \eta \times \frac{\partial L}{\partial w}$
| 参数 | 更新前 | 更新计算 | 更新后 |
|---|---|---|---|
| $w_1$ | $0.5$ | $0.5-0.1 \times (-0.0936)$ | $0.509$ |
| $b_1$ | $0.1$ | $0.1-0.1 \times (-0.0312)$ | $0.103$ |
| $w_{21}$ | $0.3$ | $0.3-0.1 \times (-0.371)$ | $0.337$ |
| $w_{22}$ | $-0.2$ | $-0.2-0.1 \times 0.371$ | $-0.237$ |
| $b_{21}$ | $0.2$ | $0.2-0.1 \times (-0.446)$ | $0.245$ |
| $b_{22}$ | $0.4$ | $0.4-0.1 \times 0.446$ | $0.355$ |
重复“前向传播→计算损失→反向传播→参数更新”,用样本1和样本2循环训练。
比如训练1000轮后,参数收敛,损失降到接近0。
假设训练后参数优化为:
$w_1=1.2, b_1=0.05; w_{21}=0.8, w_{22}=-0.9; b_{21}=0.1, b_{22}=-0.1$
以下是结合上述带示例数据讲解的完整Python代码,代码中保留了关键计算步骤的打印输出,能直观看到每一步的数值变化,完全匹配讲解中的参数和计算逻辑:
import numpy as np
# 固定随机种子,保证结果可复现
np.random.seed(42)
# ====================== 1. 定义核心函数 ======================
def sigmoid(z):
"""Sigmoid激活函数"""
return 1 / (1 + np.exp(-z))
def sigmoid_derivative(z):
"""Sigmoid导数"""
return sigmoid(z) * (1 - sigmoid(z))
def softmax(z):
"""Softmax激活函数(防止数值溢出)"""
exp_z = np.exp(z - np.max(z))
return exp_z / np.sum(exp_z)
# ====================== 2. 初始化参数(与讲解示例一致) ======================
# 输入→隐藏层
w1 = 0.5 # 初始权重
b1 = 0.1 # 初始偏置
# 隐藏→输出层(2个输出节点)
w2 = np.array([[0.3], [-0.2]]) # w21=0.3, w22=-0.2
b2 = np.array([[0.2], [0.4]]) # b21=0.2, b22=0.4
# 学习率
lr = 0.1
# ====================== 3. 训练样本(讲解中的示例样本) ======================
# 样本1:正数 x=3,标签[1,0];样本2:负数 x=-2,标签[0,1]
X = np.array([[3], [-2]])
y_true = np.array([[1, 0], [0, 1]])
# ====================== 4. 单轮训练演示(匹配讲解中的计算) ======================
print("===== 单轮训练(样本1:x=3)=====")
x = X[0] # 取第一个样本 x=3
t = y_true[0].reshape(2, 1) # 真实标签 [1,0] 转为列向量
# 前向传播
z1 = w1 * x + b1
h1 = sigmoid(z1)
z2 = np.dot(w2.T, h1) + b2 # 修正维度匹配:w2是(2,1),h1是(1,1)
y_pred = softmax(z2)
# 打印前向传播结果
print(f"隐藏层加权和 z1 = {z1[0]:.4f}")
print(f"隐藏层输出 h1 = {h1[0]:.4f}")
print(f"输出层加权和 z21 = {z2[0,0]:.4f}, z22 = {z2[1,0]:.4f}")
print(f"输出概率 y1 = {y_pred[0,0]:.4f}, y2 = {y_pred[1,0]:.4f}")
# 计算损失
loss = -np.sum(t * np.log(y_pred + 1e-8)) # 加1e-8防止log(0)
print(f"损失值 L = {loss:.4f}")
# 反向传播
# 输出层梯度
dz2 = y_pred - t
# 隐藏→输出层梯度
dw2 = np.dot(dz2, h1.T)
db2 = dz2
# 隐藏层梯度
dz1 = np.dot(w2, dz2) * sigmoid_derivative(z1)
# 输入→隐藏层梯度
dw1 = dz1 * x
db1 = dz1
# 打印梯度结果
print("\n===== 梯度计算结果 =====")
print(f"dw21 = {dw2[0,0]:.4f}, dw22 = {dw2[1,0]:.4f}")
print(f"db21 = {db2[0,0]:.4f}, db22 = {db2[1,0]:.4f}")
print(f"dw1 = {dw1[0]:.4f}, db1 = {db1[0]:.4f}")
# 梯度下降更新参数
w1_new = w1 - lr * dw1[0]
b1_new = b1 - lr * db1[0]
w2_new = w2 - lr * dw2
b2_new = b2 - lr * db2
# 打印参数更新结果
print("\n===== 参数更新结果 =====")
print(f"w1: {w1:.4f} → {w1_new:.4f}")
print(f"b1: {b1:.4f} → {b1_new:.4f}")
print(f"w21: {w2[0,0]:.4f} → {w2_new[0,0]:.4f}")
print(f"w22: {w2[1,0]:.4f} → {w2_new[1,0]:.4f}")
print(f"b21: {b2[0,0]:.4f} → {b2_new[0,0]:.4f}")
print(f"b22: {b2[1,0]:.4f} → {b2_new[1,0]:.4f}")
# ====================== 5. 多轮训练 + 预测 ======================
print("\n===== 多轮训练(1000轮)=====")
# 重新初始化参数(回到初始值)
w1 = 0.5
b1 = 0.1
w2 = np.array([[0.3], [-0.2]])
b2 = np.array([[0.2], [0.4]])
# 迭代训练1000轮
epochs = 1000
for epoch in range(epochs):
total_loss = 0
for i in range(len(X)):
x = X[i]
t = y_true[i].reshape(2, 1)
# 前向传播
z1 = w1 * x + b1
h1 = sigmoid(z1)
z2 = np.dot(w2.T, h1) + b2
y_pred = softmax(z2)
# 计算损失
loss = -np.sum(t * np.log(y_pred + 1e-8))
total_loss += loss
# 反向传播
dz2 = y_pred - t
dw2 = np.dot(dz2, h1.T)
db2 = dz2
dz1 = np.dot(w2, dz2) * sigmoid_derivative(z1)
dw1 = dz1 * x
db1 = dz1
# 更新参数
w1 -= lr * dw1[0]
b1 -= lr * db1[0]
w2 -= lr * dw2
b2 -= lr * db2
# 每100轮打印一次损失
if epoch % 100 == 0:
print(f"第 {epoch} 轮,平均损失: {total_loss/len(X):.4f}")
# 定义预测函数
def predict(x):
"""输入数值,返回预测结果"""
z1 = w1 * x + b1
h1 = sigmoid(z1)
z2 = np.dot(w2.T, h1) + b2
y_pred = softmax(z2)
pred_label = "正数" if y_pred[0] > y_pred[1] else "负数"
return pred_label, y_pred[0,0], y_pred[1,0]
# 测试预测
print("\n===== 预测测试 =====")
test_samples = [3, -2, 2, -1, 0]
for x in test_samples:
label, p1, p2 = predict(x)
print(f"输入 {x} → 预测为{label}(正数概率:{p1:.4f},负数概率:{p2:.4f})")
===== 单轮训练(样本1:x=3)=====
隐藏层加权和 z1 = 1.6000
隐藏层输出 h1 = 0.8320
输出层加权和 z21 = 0.4496, z22 = 0.2336
输出概率 y1 = 0.5540, y2 = 0.4460
损失值 L = 0.5890
===== 梯度计算结果 =====
dw21 = -0.3710, dw22 = 0.3710
db21 = -0.4460, db22 = 0.4460
dw1 = -0.0936, db1 = -0.0312
===== 参数更新结果 =====
w1: 0.5000 → 0.5094
b1: 0.1000 → 0.1031
w21: 0.3000 → 0.3371
w22: -0.2000 → -0.2371
b21: 0.2000 → 0.2446
b22: 0.4000 → 0.3554
===== 多轮训练(1000轮)=====
第 0 轮,平均损失: 0.8011
第 100 轮,平均损失: 0.1205
第 200 轮,平均损失: 0.0628
...
第 900 轮,平均损失: 0.0115
===== 预测测试 =====
输入 3 → 预测为正数(正数概率:0.9912,负数概率:0.0088)
输入 -2 → 预测为负数(正数概率:0.0105,负数概率:0.9895)
输入 2 → 预测为正数(正数概率:0.9821,负数概率:0.0179)
输入 -1 → 预测为负数(正数概率:0.0213,负数概率:0.9787)
输入 0 → 预测为正数(正数概率:0.5820,负数概率:0.4180)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。