用Python手搓神经网络:从输入层到输出层的完整实现(附代码)
·
用Python手搓神经网络:从输入层到输出层的完整实现(附代码)
1. 神经网络基础架构搭建
神经网络的本质是通过层级计算实现特征转换。我们先从最简单的三层结构开始——输入层负责接收数据,隐藏层进行特征提取,输出层生成预测结果。用NumPy实现时,关键在于理解权重矩阵的维度关系。
import numpy as np
class SimpleNN:
def __init__(self, input_size, hidden_size, output_size):
# 初始化权重矩阵(注意维度匹配)
self.W1 = np.random.randn(input_size, hidden_size) * 0.01
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.randn(hidden_size, output_size) * 0.01
self.b2 = np.zeros((1, output_size))
权重初始化技巧:
- 小随机数防止梯度爆炸
- 偏置初始化为零
- 输入/输出维度由数据特征决定
提示:隐藏层节点数通常取2的幂次方(如64、128),有利于GPU并行计算
2. 前向传播实现细节
前向传播是数据从输入到输出的流动过程,需要依次计算各层的线性变换和激活函数。
def forward(self, X):
# 第一层计算(输入层→隐藏层)
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.relu(self.z1)
# 第二层计算(隐藏层→输出层)
self.z2 = np.dot(self.a1, self.W2) + self.b2
return self.softmax(self.z2)
def relu(self, x):
return np.maximum(0, x)
def softmax(self, x):
exps = np.exp(x - np.max(x))
return exps / np.sum(exps, axis=1, keepdims=True)
激活函数对比:
| 函数类型 | 公式 | 优点 | 缺点 |
|---|---|---|---|
| ReLU | max(0, x) | 计算简单,缓解梯度消失 | 负数区失效 |
| Sigmoid | 1/(1+e^-x) | 输出范围(0,1) | 容易梯度饱和 |
| Tanh | (e^x-e^-x)/(e^x+e^-x) | 输出范围(-1,1) | 计算量较大 |
3. 反向传播与参数更新
反向传播通过链式法则计算梯度,是神经网络学习的核心机制。我们采用交叉熵损失函数配合Softmax输出:
def backward(self, X, y, output):
m = X.shape[0] # 样本数量
# 输出层梯度
delta3 = output
delta3[range(m), y] -= 1
delta3 /= m
# 隐藏层梯度
dW2 = np.dot(self.a1.T, delta3)
db2 = np.sum(delta3, axis=0, keepdims=True)
delta2 = np.dot(delta3, self.W2.T) * (self.a1 > 0)
dW1 = np.dot(X.T, delta2)
db1 = np.sum(delta2, axis=0)
return dW1, db1, dW2, db2
参数更新公式:
W = W - learning_rate * dW
b = b - learning_rate * db
4. MNIST实战示例
下面展示如何在MNIST数据集上应用我们的神经网络:
from sklearn.datasets import fetch_openml
from sklearn.preprocessing import MinMaxScaler
# 数据加载与预处理
mnist = fetch_openml('mnist_784', version=1)
X = MinMaxScaler().fit_transform(mnist.data)
y = mnist.target.astype(int)
# 创建网络实例
nn = SimpleNN(input_size=784, hidden_size=128, output_size=10)
# 训练循环
for epoch in range(100):
# 前向传播
output = nn.forward(X)
# 计算损失
loss = -np.log(output[range(len(X)), y]).mean()
# 反向传播
dW1, db1, dW2, db2 = nn.backward(X, y, output)
# 参数更新
nn.W1 -= 0.1 * dW1
nn.b1 -= 0.1 * db1
nn.W2 -= 0.1 * dW2
nn.b2 -= 0.1 * db2
if epoch % 10 == 0:
print(f"Epoch {epoch}, Loss: {loss:.4f}")
可视化调试技巧:
- 损失曲线监控:确保损失值单调下降
- 权重直方图:观察参数分布是否合理
- 梯度检查:验证反向传播实现正确性
# 梯度检查示例
def gradient_check():
epsilon = 1e-7
perturb = np.zeros_like(nn.W1)
perturb[0,0] = epsilon
loss1 = compute_loss(X, y)
nn.W1 += perturb
loss2 = compute_loss(X, y)
numerical_grad = (loss2 - loss1)/epsilon
analytic_grad = dW1[0,0]
diff = abs(numerical_grad - analytic_grad)
print(f"Gradient check: {diff}")
5. 性能优化策略
基础实现跑通后,可以考虑以下优化手段:
批量归一化:
def batchnorm_forward(x, gamma, beta, eps=1e-5):
mu = np.mean(x, axis=0)
var = np.var(x, axis=0)
x_hat = (x - mu) / np.sqrt(var + eps)
out = gamma * x_hat + beta
return out
Dropout正则化:
def dropout_forward(x, p_dropout):
mask = (np.random.rand(*x.shape) > p_dropout) / (1 - p_dropout)
return x * mask
优化器对比:
| 优化器 | 更新规则 | 适用场景 |
|---|---|---|
| SGD | θ = θ - η∇θ | 简单任务 |
| Momentum | v = γv + η∇θ; θ = θ - v | 逃离局部最优 |
| Adam | 综合动量与自适应学习率 | 大多数深度学习任务 |
实际项目中,建议先用Adam优化器快速验证模型可行性,再根据需求切换更专业的优化策略。
更多推荐



所有评论(0)