共计 3509 个字符,预计需要花费 9 分钟才能阅读完成。
神经网络基础概念
神经网络是一种模仿生物神经元工作方式的数学模型。它由输入层、隐藏层(可能有多个)和输出层组成,每层包含若干个神经元。神经元之间的连接具有权重,这些权重在训练过程中不断调整,使得网络能够学习输入数据与输出结果之间的映射关系。

BP(Back Propagation)神经网络是最基础的前馈神经网络之一,它通过误差反向传播算法来更新网络权重。BP 算法主要包括两个阶段:前向传播和反向传播。
前向传播数学原理
前向传播是指输入数据从输入层经过隐藏层最终到达输出层的过程。每个神经元的输出值由以下公式计算:
- 加权求和:z = w·x + b,其中 w 是权重,x 是输入,b 是偏置
- 激活函数:a = σ(z),σ 代表激活函数(如 Sigmoid、ReLU 等)
对于多层网络,前一层的输出就是下一层的输入。这个过程一直持续到输出层,得到网络的预测输出。
误差反向传播推导
反向传播是通过计算损失函数对各个参数的梯度,然后使用梯度下降法更新参数的过程。核心是链式法则的应用:
- 计算输出层误差:δ^L = ∇_aC ⊙ σ'(z^L)
- 反向传播误差:δ^l = ((w^{l+1})^T δ^{l+1}) ⊙ σ'(z^l)
- 计算梯度:∂C/∂w^l = δ^l (a^{l-1})^T
- 参数更新:w = w – η·∂C/∂w
其中⊙表示逐元素相乘,η 是学习率。
Python 实现(含注释)
import numpy as np
class NeuralNetwork:
def __init__(self, layer_sizes):
# 初始化权重和偏置
self.weights = [np.random.randn(y, x)
for x, y in zip(layer_sizes[:-1], layer_sizes[1:])]
self.biases = [np.random.randn(y, 1) for y in layer_sizes[1:]]
def sigmoid(self, z):
return 1/(1+np.exp(-z))
def sigmoid_prime(self, z):
return self.sigmoid(z)*(1-self.sigmoid(z))
def feedforward(self, a):
# 前向传播
for w, b in zip(self.weights, self.biases):
a = self.sigmoid(np.dot(w, a) + b)
return a
def backprop(self, x, y):
# 反向传播
nabla_w = [np.zeros(w.shape) for w in self.weights]
nabla_b = [np.zeros(b.shape) for b in self.biases]
# 前向传播
activation = x
activations = [x]
zs = []
for w, b in zip(self.weights, self.biases):
z = np.dot(w, activation) + b
zs.append(z)
activation = self.sigmoid(z)
activations.append(activation)
# 输出层误差
delta = (activations[-1] - y) * self.sigmoid_prime(zs[-1])
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].T)
# 反向传播误差
for l in range(2, len(self.weights)+1):
z = zs[-l]
sp = self.sigmoid_prime(z)
delta = np.dot(self.weights[-l+1].T, delta) * sp
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l-1].T)
return (nabla_w, nabla_b)
def train(self, training_data, epochs, batch_size, eta):
# 训练网络
n = len(training_data)
for j in range(epochs):
np.random.shuffle(training_data)
batches = [training_data[k:k+batch_size]
for k in range(0, n, batch_size)]
for batch in batches:
self.update_batch(batch, eta)
def update_batch(self, batch, eta):
# 批量更新
nabla_w = [np.zeros(w.shape) for w in self.weights]
nabla_b = [np.zeros(b.shape) for b in self.biases]
for x, y in batch:
delta_nabla_w, delta_nabla_b = self.backprop(x, y)
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
self.weights = [w-(eta/len(batch))*nw
for w, nw in zip(self.weights, nabla_w)]
self.biases = [b-(eta/len(batch))*nb
for b, nb in zip(self.biases, nabla_b)]
常见问题与解决方案
- 梯度消失问题:当使用 Sigmoid 激活函数时,深层网络容易出现梯度消失。解决方案包括:
- 使用 ReLU 等激活函数
- 使用批归一化(Batch Normalization)
-
使用残差连接(ResNet)
-
梯度爆炸问题:权重更新时梯度变得非常大。解决方案包括:
- 梯度裁剪(Gradient Clipping)
- 权重初始化技巧(Xavier 初始化等)
-
使用较小的学习率
-
过拟合问题:模型在训练集上表现好但在测试集上表现差。解决方案包括:
- 增加训练数据
- 使用 Dropout
- 添加 L1/L2 正则化
MNIST 实战示例
import tensorflow as tf
from tensorflow.keras.datasets import mnist
# 加载数据
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 数据预处理
x_train = x_train.reshape(-1, 28*28) / 255.0
x_test = x_test.reshape(-1, 28*28) / 255.0
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)
# 创建网络
network = NeuralNetwork([784, 30, 10])
# 准备训练数据
training_data = [(x.reshape(-1,1), y.reshape(-1,1))
for x, y in zip(x_train, y_train)]
# 训练
network.train(training_data, epochs=30, batch_size=10, eta=3.0)
# 测试
correct = 0
for x, y in zip(x_test, y_test):
output = network.feedforward(x.reshape(-1,1))
if np.argmax(output) == np.argmax(y):
correct += 1
print(f"准确率: {correct/len(x_test):.2%}")
调参与优化建议
-
学习率选择:通常从 0.1、0.01、0.001 等值开始尝试,可以使用学习率衰减策略
-
批量大小:一般选择 16、32、64、128 等 2 的幂次方,较大的批量可以更稳定但可能泛化性差
-
网络结构:从浅层网络开始,逐步增加层数和神经元数量
-
激活函数:ReLU 是很好的默认选择,输出层根据任务选择(如分类用 Softmax,回归用线性)
-
正则化:L2 正则化系数通常设为 0.0001 到 0.01,Dropout 率设为 0.2 到 0.5
-
优化器:SGD 配合动量 (momentum) 是基础,也可以尝试 Adam 等自适应优化器
-
早停(Early Stopping):监控验证集性能,当不再提升时停止训练
通过理解 BP 神经网络的前向计算和反向传播机制,并配合适当的调参技巧,你可以在各种任务上构建有效的神经网络模型。在实践中,建议从小规模网络和数据集开始,逐步扩展到更复杂的场景。
