在前面几节中,我们学习了传统机器学习方法、知识表示与检索以及强化学习。今天,我们将进入深度学习的世界,从最基础的感知机开始,逐步深入到多层感知机和反向传播算法,这些是现代深度学习的基石。
神经网络发展概览
神经网络的发展经历了从简单到复杂、从浅层到深层的过程,每一次突破都推动了AI技术的巨大进步。
timeline
title 神经网络发展史
1943年 : McCulloch-Pitts神经元模型提出
1957年 : 感知机(Perceptron)诞生
1969年 : 感知机局限性被指出,第一次AI寒冬
1986年 : 反向传播算法被重新发现
1998年 : LeNet-5在手写数字识别上取得突破
2006年 : 深度学习概念提出,神经网络复兴
2012年 : AlexNet在ImageNet竞赛中大获成功
2017年 : Transformer架构改变NLP领域
感知机详解
感知机是最早的人工神经网络模型,由Frank Rosenblatt在1957年提出。
感知机结构
感知机由输入层和输出层组成,通过学习权重和偏置来实现线性分类。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
# 感知机实现
class Perceptron:
"""感知机实现"""
def __init__(self, learning_rate=0.01, n_iterations=1000):
self.learning_rate = learning_rate
self.n_iterations = n_iterations
self.weights = None
self.bias = None
def fit(self, X, y):
"""训练感知机"""
n_samples, n_features = X.shape
# 初始化权重和偏置
self.weights = np.zeros(n_features)
self.bias = 0
# 将标签转换为-1和1
y_ = np.where(y <= 0, -1, 1)
# 训练过程
for _ in range(self.n_iterations):
for idx, x_i in enumerate(X):
# 计算线性输出
linear_output = np.dot(x_i, self.weights) + self.bias
# 预测结果
y_predicted = np.where(linear_output >= 0, 1, -1)
# 更新权重和偏置
update = self.learning_rate * (y_[idx] - y_predicted)
self.weights += update * x_i
self.bias += update
def predict(self, X):
"""预测"""
linear_output = np.dot(X, self.weights) + self.bias
return np.where(linear_output >= 0, 1, 0)
def decision_boundary(self, x):
"""计算决策边界"""
return -(self.weights[0] * x + self.bias) / self.weights[1] if self.weights[1] != 0 else 0
# 生成线性可分数据
X_linear, y_linear = make_classification(
n_samples=100,
n_features=2,
n_redundant=0,
n_informative=2,
n_clusters_per_class=1,
random_state=42
)
# 训练感知机
perceptron = Perceptron(learning_rate=0.01, n_iterations=1000)
perceptron.fit(X_linear, y_linear)
# 预测
predictions = perceptron.predict(X_linear)
accuracy = np.mean(predictions == y_linear)
print(f"感知机在训练集上的准确率: {accuracy:.4f}")
# 可视化感知机分类结果
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
# 绘制原始数据
scatter = plt.scatter(X_linear[:, 0], X_linear[:, 1], c=y_linear, cmap='viridis', alpha=0.7)
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.title('原始数据')
plt.colorbar(scatter)
plt.subplot(1, 2, 2)
# 绘制分类结果
scatter = plt.scatter(X_linear[:, 0], X_linear[:, 1], c=predictions, cmap='viridis', alpha=0.7)
# 绘制决策边界
x_range = np.linspace(X_linear[:, 0].min()-1, X_linear[:, 0].max()+1, 100)
y_range = perceptron.decision_boundary(x_range)
plt.plot(x_range, y_range, 'r-', linewidth=2, label='决策边界')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.title(f'感知机分类结果 (准确率: {accuracy:.4f})')
plt.legend()
plt.colorbar(scatter)
plt.tight_layout()
plt.show()
# 感知机权重可视化
print(f"学习到的权重: {perceptron.weights}")
print(f"学习到的偏置: {perceptron.bias}")
感知机的局限性
感知机只能解决线性可分问题,无法解决XOR等非线性问题。
# XOR问题演示
def xor_problem_demo():
"""XOR问题演示感知机的局限性"""
# XOR数据
X_xor = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_xor = np.array([0, 1, 1, 0])
# 训练感知机
xor_perceptron = Perceptron(learning_rate=0.1, n_iterations=1000)
xor_perceptron.fit(X_xor, y_xor)
# 预测
xor_predictions = xor_perceptron.predict(X_xor)
print("XOR问题:")
print("输入\t\t实际输出\t感知机输出")
for i in range(len(X_xor)):
print(f"{X_xor[i]}\t\t{y_xor[i]}\t\t{xor_predictions[i]}")
print("\n注意:感知机无法正确解决XOR问题,因为XOR不是线性可分的!")
# 可视化
plt.figure(figsize=(10, 6))
# 绘制数据点
colors = ['blue', 'red', 'red', 'blue'] # XOR模式
markers = ['s', 'o', 'o', 's']
for i in range(len(X_xor)):
plt.scatter(X_xor[i, 0], X_xor[i, 1], c=colors[i], s=100, marker=markers[i],
label=f'类别 {y_xor[i]}' if i in [0, 1] else "")
# 绘制决策边界
x_range = np.linspace(-0.5, 1.5, 100)
y_range = xor_perceptron.decision_boundary(x_range)
plt.plot(x_range, y_range, 'g--', linewidth=2, label='感知机决策边界')
plt.xlabel('输入1')
plt.ylabel('输入2')
plt.title('感知机尝试解决XOR问题(线性不可分)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.xlim(-0.5, 1.5)
plt.ylim(-0.5, 1.5)
plt.show()
xor_problem_demo()
多层感知机(MLP)
多层感知机通过增加隐藏层来解决非线性问题,是现代神经网络的基础。
MLP结构
MLP由输入层、一个或多个隐藏层和输出层组成,每层包含多个神经元。
# 神经元实现
class Neuron:
"""神经元实现"""
def __init__(self, n_inputs):
self.weights = np.random.randn(n_inputs) * 0.1
self.bias = np.random.randn() * 0.1
def activate(self, inputs):
"""激活函数(使用Sigmoid)"""
z = np.dot(inputs, self.weights) + self.bias
return 1 / (1 + np.exp(-np.clip(z, -500, 500))) # 防止溢出
# 多层感知机实现
class MLP:
"""多层感知机实现"""
def __init__(self, layer_sizes, learning_rate=0.1):
self.layer_sizes = layer_sizes
self.learning_rate = learning_rate
self.layers = []
# 初始化网络层
for i in range(1, len(layer_sizes)):
layer = []
for _ in range(layer_sizes[i]):
layer.append(Neuron(layer_sizes[i-1]))
self.layers.append(layer)
def forward(self, inputs):
"""前向传播"""
self.activations = [inputs]
for layer in self.layers:
layer_outputs = []
for neuron in layer:
output = neuron.activate(self.activations[-1])
layer_outputs.append(output)
self.activations.append(np.array(layer_outputs))
return self.activations[-1]
def backward(self, targets):
"""反向传播"""
# 计算输出层误差
output = self.activations[-1]
output_error = targets - output
output_delta = output_error * output * (1 - output)
# 计算隐藏层误差
deltas = [output_delta]
for i in range(len(self.layers) - 2, -1, -1):
layer = self.layers[i]
layer_delta = []
for j, neuron in enumerate(layer):
error = 0
next_layer = self.layers[i + 1]
for k, next_neuron in enumerate(next_layer):
error += next_neuron.weights[j] * deltas[0][k]
delta = error * self.activations[i+1][j] * (1 - self.activations[i+1][j])
layer_delta.append(delta)
deltas.insert(0, np.array(layer_delta))
# 更新权重和偏置
for i, layer in enumerate(self.layers):
for j, neuron in enumerate(layer):
for k in range(len(neuron.weights)):
neuron.weights[k] += self.learning_rate * deltas[i][j] * self.activations[i][k]
neuron.bias += self.learning_rate * deltas[i][j]
def train(self, X, y, epochs=1000):
"""训练网络"""
losses = []
for epoch in range(epochs):
total_loss = 0
for i in range(len(X)):
# 前向传播
output = self.forward(X[i])
# 计算损失
loss = 0.5 * np.sum((y[i] - output) ** 2)
total_loss += loss
# 反向传播
self.backward(y[i])
avg_loss = total_loss / len(X)
losses.append(avg_loss)
if epoch % 100 == 0:
print(f"Epoch {epoch}, Loss: {avg_loss:.6f}")
return losses
# 使用MLP解决XOR问题
def mlp_xor_demo():
"""MLP解决XOR问题"""
# XOR数据
X_xor = np.array([[0, 0], [0, 1], [1, 0], [1, 1]], dtype=float)
y_xor = np.array([[0], [1], [1], [0]], dtype=float)
# 创建MLP (2输入 -> 4隐藏 -> 1输出)
mlp = MLP([2, 4, 1], learning_rate=5.0)
# 训练
print("训练MLP解决XOR问题:")
losses = mlp.train(X_xor, y_xor, epochs=2000)
# 测试
print("\nMLP解决XOR问题结果:")
print("输入\t\t目标输出\t实际输出")
for i in range(len(X_xor)):
output = mlp.forward(X_xor[i])
print(f"{X_xor[i]}\t\t{y_xor[i][0]}\t\t{output[0]:.4f}")
# 绘制损失曲线
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('训练损失曲线')
plt.grid(True, alpha=0.3)
# 可视化决策边界
plt.subplot(1, 2, 2)
# 创建网格点
xx, yy = np.meshgrid(np.linspace(-0.5, 1.5, 100),
np.linspace(-0.5, 1.5, 100))
grid_points = np.c_[xx.ravel(), yy.ravel()]
# 预测网格点
Z = np.array([mlp.forward(point)[0] for point in grid_points])
Z = Z.reshape(xx.shape)
# 绘制决策边界
plt.contourf(xx, yy, Z, levels=50, alpha=0.8, cmap='viridis')
plt.colorbar(label='输出值')
# 绘制原始数据点
colors = ['blue', 'red', 'red', 'blue']
markers = ['s', 'o', 'o', 's']
for i in range(len(X_xor)):
plt.scatter(X_xor[i, 0], X_xor[i, 1], c=colors[i], s=100, marker=markers[i],
edgecolors='white', linewidth=2)
plt.xlabel('输入1')
plt.ylabel('输入2')
plt.title('MLP解决XOR问题的决策边界')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
mlp_xor_demo()
反向传播算法详解
反向传播是训练神经网络的核心算法,通过链式法则计算梯度并更新网络参数。
反向传播原理
反向传播通过以下步骤计算梯度:
- 前向传播计算输出
- 计算输出误差
- 反向传播误差
- 计算参数梯度
- 更新参数
# 详细的反向传播实现
class DetailedMLP:
"""详细MLP实现,展示反向传播过程"""
def __init__(self, input_size, hidden_size, output_size, learning_rate=0.1):
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
self.learning_rate = learning_rate
# 初始化权重和偏置
self.W1 = np.random.randn(self.input_size, self.hidden_size) * 0.1
self.b1 = np.zeros((1, self.hidden_size))
self.W2 = np.random.randn(self.hidden_size, self.output_size) * 0.1
self.b2 = np.zeros((1, self.output_size))
def sigmoid(self, x):
"""Sigmoid激活函数"""
return 1 / (1 + np.exp(-np.clip(x, -500, 500)))
def sigmoid_derivative(self, x):
"""Sigmoid函数的导数"""
return x * (1 - x)
def forward(self, X):
"""前向传播"""
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.sigmoid(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
self.a2 = self.sigmoid(self.z2)
return self.a2
def backward(self, X, y, output):
"""反向传播"""
m = X.shape[0]
# 输出层误差
dz2 = output - y
dW2 = (1/m) * np.dot(self.a1.T, dz2)
db2 = (1/m) * np.sum(dz2, axis=0, keepdims=True)
# 隐藏层误差
da1 = np.dot(dz2, self.W2.T)
dz1 = da1 * self.sigmoid_derivative(self.a1)
dW1 = (1/m) * np.dot(X.T, dz1)
db1 = (1/m) * np.sum(dz1, axis=0, keepdims=True)
# 更新参数
self.W2 -= self.learning_rate * dW2
self.b2 -= self.learning_rate * db2
self.W1 -= self.learning_rate * dW1
self.b1 -= self.learning_rate * db1
def train(self, X, y, epochs=1000):
"""训练网络"""
losses = []
for epoch in range(epochs):
# 前向传播
output = self.forward(X)
# 计算损失 (均方误差)
loss = np.mean((output - y) ** 2)
losses.append(loss)
# 反向传播
self.backward(X, y, output)
if epoch % 200 == 0:
print(f"Epoch {epoch}, Loss: {loss:.6f}")
return losses
def predict(self, X):
"""预测"""
return self.forward(X)
# 使用详细MLP进行分类任务
def detailed_mlp_demo():
"""详细MLP演示"""
# 生成分类数据
X, y = make_classification(
n_samples=200,
n_features=2,
n_redundant=0,
n_informative=2,
n_clusters_per_class=1,
random_state=42
)
# 转换标签格式
y = y.reshape(-1, 1)
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# 标准化数据
X_mean, X_std = X_train.mean(axis=0), X_train.std(axis=0)
X_train = (X_train - X_mean) / X_std
X_test = (X_test - X_mean) / X_std
# 创建并训练MLP
mlp = DetailedMLP(input_size=2, hidden_size=10, output_size=1, learning_rate=1.0)
print("训练详细MLP:")
losses = mlp.train(X_train, y_train, epochs=1000)
# 预测
train_pred = (mlp.predict(X_train) > 0.5).astype(int)
test_pred = (mlp.predict(X_test) > 0.5).astype(int)
train_accuracy = np.mean(train_pred == y_train)
test_accuracy = np.mean(test_pred == y_test)
print(f"\n训练集准确率: {train_accuracy:.4f}")
print(f"测试集准确率: {test_accuracy:.4f}")
# 可视化结果
plt.figure(figsize=(15, 5))
# 损失曲线
plt.subplot(1, 3, 1)
plt.plot(losses)
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('训练损失曲线')
plt.grid(True, alpha=0.3)
# 训练集分类结果
plt.subplot(1, 3, 2)
scatter = plt.scatter(X_train[:, 0], X_train[:, 1], c=train_pred.ravel(), cmap='viridis', alpha=0.7)
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.title(f'训练集分类结果 (准确率: {train_accuracy:.4f})')
plt.colorbar(scatter)
plt.grid(True, alpha=0.3)
# 测试集分类结果
plt.subplot(1, 3, 3)
scatter = plt.scatter(X_test[:, 0], X_test[:, 1], c=test_pred.ravel(), cmap='viridis', alpha=0.7)
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.title(f'测试集分类结果 (准确率: {test_accuracy:.4f})')
plt.colorbar(scatter)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
detailed_mlp_demo()
神经网络的挑战与解决方案
神经网络在训练过程中面临梯度消失和梯度爆炸等问题,现代技术提供了多种解决方案。
# 梯度消失问题演示
def gradient_vanishing_demo():
"""梯度消失问题演示"""
print("梯度消失问题说明:")
print("在深层网络中,梯度在反向传播过程中会逐层减小,导致前面层的权重更新缓慢。")
print("这使得深层网络难以训练。")
# 模拟深层网络中的梯度变化
depths = range(1, 21)
# 假设每层的梯度衰减因子为0.8
gradient_factors = [0.8 ** d for d in depths]
plt.figure(figsize=(10, 6))
plt.plot(depths, gradient_factors, 'bo-')
plt.xlabel('网络深度')
plt.ylabel('梯度相对大小')
plt.title('梯度消失问题演示')
plt.grid(True, alpha=0.3)
plt.yscale('log')
plt.show()
print("解决方案:")
print("1. 使用ReLU等激活函数替代Sigmoid")
print("2. 批量归一化(Batch Normalization)")
print("3. 残差连接(Residual Connections)")
print("4. 合适的权重初始化(Xavier/He初始化)")
gradient_vanishing_demo()
# 现代神经网络技术
def modern_nn_techniques():
"""现代神经网络技术"""
techniques = {
'激活函数': ['ReLU', 'Leaky ReLU', 'ELU', 'Swish'],
'正则化': ['Dropout', 'BatchNorm', 'LayerNorm'],
'优化器': ['SGD', 'Adam', 'RMSprop', 'AdaGrad'],
'初始化': ['Xavier', 'He', 'LeCun'],
'架构': ['ResNet', 'DenseNet', 'EfficientNet']
}
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
axes = axes.flatten()
for i, (category, items) in enumerate(techniques.items()):
if i < len(axes):
ax = axes[i]
y_pos = np.arange(len(items))
ax.barh(y_pos, [1]*len(items), color=plt.cm.Set3(np.linspace(0, 1, len(items))))
ax.set_yticks(y_pos)
ax.set_yticklabels(items)
ax.set_title(category)
ax.set_xlabel('重要性')
ax.grid(True, alpha=0.3)
# 隐藏多余的子图
for i in range(len(techniques), len(axes)):
axes[i].set_visible(False)
plt.tight_layout()
plt.show()
modern_nn_techniques()
print("现代神经网络的关键技术:")
print("1. ReLU激活函数: 解决梯度消失问题")
print("2. 批量归一化: 加速训练并提高稳定性")
print("3. Dropout: 防止过拟合")
print("4. Adam优化器: 自适应学习率")
print("5. 残差连接: 允许训练更深的网络")
本周学习总结
今天我们深入学习了神经网络的基础知识:
-
感知机
- 理解了感知机的工作原理
- 认识了感知机的局限性(只能解决线性可分问题)
-
多层感知机
- 学习了MLP的结构和工作原理
- 实现了MLP解决XOR问题
-
反向传播算法
- 掌握了反向传播的数学原理
- 实现了详细的反向传播过程
-
现代技术
- 了解了梯度消失问题及其解决方案
- 学习了现代神经网络的关键技术
graph TD
A[神经网络基础] --> B[感知机]
A --> C[多层感知机]
A --> D[反向传播]
A --> E[现代技术]
B --> F[结构]
B --> G[局限性]
C --> H[隐藏层]
C --> I[非线性]
D --> J[链式法则]
D --> K[梯度计算]
E --> L[梯度问题]
E --> M[解决方案]
课后练习
- 运行本节所有代码示例,理解神经网络的工作原理
- 修改MLP的结构(增加隐藏层、改变神经元数量),观察性能变化
- 实现不同的激活函数(如tanh、ReLU),比较它们的效果
- 研究批量归一化和Dropout的实现原理
下节预告
下一节我们将学习现代神经网络架构,包括卷积神经网络(CNN)和循环神经网络(RNN),这些是处理图像和序列数据的重要工具,敬请期待!
有任何疑问请在讨论区留言,我们会定期回复大家的问题。