在上一节中,我们学习了神经网络的基础知识,包括感知机、多层感知机和反向传播算法。今天,我们将深入学习两种重要的现代神经网络架构:卷积神经网络(CNN)和循环神经网络(RNN),它们分别在图像处理和序列数据处理方面表现出色。
现代神经网络架构概览
现代深度学习主要依赖几种核心神经网络架构,每种架构都针对特定类型的数据和任务进行了优化。
graph TD
A[神经网络架构] --> B[CNN]
A --> C[RNN]
A --> D[Transformer]
B --> E[图像处理]
B --> F[卷积层]
B --> G[池化层]
C --> H[序列数据]
C --> I[循环层]
C --> J[LSTM/GRU]
D --> K[注意力机制]
D --> L[自注意力]
卷积神经网络(CNN)
卷积神经网络专门用于处理具有网格结构的数据,如图像。它通过卷积层、池化层和全连接层的组合来提取特征。
CNN核心概念
CNN的核心组件包括:
- 卷积层(Convolutional Layer) - 提取局部特征
- 池化层(Pooling Layer) - 降低维度和参数数量
- 全连接层(Fully Connected Layer) - 进行分类或其他任务
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
# 简单的CNN实现
class SimpleCNN:
"""简化版CNN实现"""
def __init__(self):
# 初始化卷积核 (3x3 filters)
self.conv_filters = [np.random.randn(3, 3) * 0.1 for _ in range(4)]
# 初始化全连接层权重
self.fc_weights = np.random.randn(4 * 6 * 6, 10) * 0.1
self.fc_bias = np.zeros(10)
def relu(self, x):
"""ReLU激活函数"""
return np.maximum(0, x)
def softmax(self, x):
"""Softmax函数"""
exp_x = np.exp(x - np.max(x, axis=1, keepdims=True))
return exp_x / np.sum(exp_x, axis=1, keepdims=True)
def conv2d(self, image, kernel, stride=1):
"""2D卷积操作"""
image_h, image_w = image.shape
kernel_h, kernel_w = kernel.shape
output_h = (image_h - kernel_h) // stride + 1
output_w = (image_w - kernel_w) // stride + 1
output = np.zeros((output_h, output_w))
for i in range(0, output_h):
for j in range(0, output_w):
region = image[i:i+kernel_h, j:j+kernel_w]
output[i, j] = np.sum(region * kernel)
return output
def max_pool2d(self, image, pool_size=2, stride=2):
"""2D最大池化操作"""
image_h, image_w = image.shape
output_h = (image_h - pool_size) // stride + 1
output_w = (image_w - pool_size) // stride + 1
output = np.zeros((output_h, output_w))
for i in range(0, output_h):
for j in range(0, output_w):
region = image[i*stride:i*stride+pool_size, j*stride:j*stride+pool_size]
output[i, j] = np.max(region)
return output
def forward(self, image):
"""前向传播"""
# 卷积层
conv_outputs = []
for filter in self.conv_filters:
conv_out = self.conv2d(image, filter)
conv_outputs.append(conv_out)
# 激活函数
activated_outputs = [self.relu(out) for out in conv_outputs]
# 池化层
pooled_outputs = []
for out in activated_outputs:
pooled = self.max_pool2d(out)
pooled_outputs.append(pooled)
# 展平
flattened = np.concatenate([out.flatten() for out in pooled_outputs])
# 全连接层
logits = np.dot(flattened, self.fc_weights) + self.fc_bias
output = self.softmax(logits.reshape(1, -1))
return output.flatten(), flattened
def cross_entropy_loss(self, predictions, targets):
"""交叉熵损失"""
return -np.sum(targets * np.log(predictions + 1e-15))
def train_step(self, image, label, learning_rate=0.01):
"""训练步骤"""
# 前向传播
predictions, features = self.forward(image)
# 计算损失
target = np.zeros(10)
target[label] = 1
loss = self.cross_entropy_loss(predictions, target)
# 简化的反向传播(这里只展示概念)
# 实际实现会更复杂
output_error = predictions - target
self.fc_weights -= learning_rate * np.outer(features, output_error)
self.fc_bias -= learning_rate * output_error
return loss
# 可视化卷积和池化操作
def visualize_conv_pool():
"""可视化卷积和池化操作"""
# 创建示例图像
image = np.random.rand(8, 8)
# 创建卷积核
kernel = np.array([[1, 0, -1],
[1, 0, -1],
[1, 0, -1]])
# 执行卷积
cnn = SimpleCNN()
conv_result = cnn.conv2d(image, kernel)
pool_result = cnn.max_pool2d(conv_result, pool_size=2)
# 可视化
fig, axes = plt.subplots(1, 4, figsize=(16, 4))
axes[0].imshow(image, cmap='gray')
axes[0].set_title('原始图像 (8x8)')
axes[0].axis('off')
axes[1].imshow(kernel, cmap='RdBu', vmin=-1, vmax=1)
axes[1].set_title('卷积核 (3x3)')
axes[1].axis('off')
axes[2].imshow(conv_result, cmap='gray')
axes[2].set_title(f'卷积结果 ({conv_result.shape[0]}x{conv_result.shape[1]})')
axes[2].axis('off')
axes[3].imshow(pool_result, cmap='gray')
axes[3].set_title(f'池化结果 ({pool_result.shape[0]}x{pool_result.shape[1]})')
axes[3].axis('off')
plt.tight_layout()
plt.show()
visualize_conv_pool()
print("卷积神经网络核心概念:")
print("1. 卷积层: 通过卷积核提取局部特征")
print("2. 池化层: 降低特征图维度,保留重要信息")
print("3. 全连接层: 将提取的特征用于分类等任务")
CNN在图像分类中的应用
# 使用手写数字数据集演示CNN
def cnn_digit_classification():
"""CNN手写数字分类演示"""
# 加载数据
digits = load_digits()
X, y = digits.data, digits.target
# 重塑为8x8图像
X_images = X.reshape(-1, 8, 8)
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(
X_images, y, test_size=0.3, random_state=42
)
print(f"训练集大小: {X_train.shape}")
print(f"测试集大小: {X_test.shape}")
# 创建简单CNN
cnn = SimpleCNN()
# 训练几个样本(简化演示)
print("\n开始训练CNN...")
losses = []
# 只训练前50个样本进行演示
for i in range(min(50, len(X_train))):
loss = cnn.train_step(X_train[i], y_train[i], learning_rate=0.1)
losses.append(loss)
if (i + 1) % 10 == 0:
print(f"样本 {i+1}, 损失: {loss:.4f}")
# 测试几个样本
print("\n测试结果:")
correct = 0
total = min(20, len(X_test))
for i in range(total):
predictions, _ = cnn.forward(X_test[i])
predicted_label = np.argmax(predictions)
if predicted_label == y_test[i]:
correct += 1
if i < 5: # 显示前5个测试样本
plt.figure(figsize=(10, 2))
plt.subplot(1, 5, 1)
plt.imshow(X_test[i], cmap='gray')
plt.title(f'真实: {y_test[i]}')
plt.axis('off')
for j in range(min(4, len(predictions))):
plt.subplot(1, 5, j+2)
plt.bar(range(10), [predictions[k] if k < len(predictions) else 0 for k in range(10)])
plt.title(f'预测分布')
plt.xlabel('数字')
plt.ylabel('概率')
break # 只显示一次
plt.tight_layout()
plt.show()
accuracy = correct / total
print(f"测试准确率: {accuracy:.4f} ({correct}/{total})")
# 损失曲线
plt.figure(figsize=(10, 6))
plt.plot(losses)
plt.xlabel('训练样本')
plt.ylabel('损失')
plt.title('CNN训练损失曲线')
plt.grid(True, alpha=0.3)
plt.show()
cnn_digit_classification()
循环神经网络(RNN)
循环神经网络专门用于处理序列数据,如文本、时间序列等。它通过在时间步之间共享参数来捕获序列中的依赖关系。
RNN核心概念
RNN的核心特点是具有"记忆"能力,当前时间步的输出不仅依赖于当前输入,还依赖于之前时间步的信息。
# 简单RNN实现
class SimpleRNN:
"""简化版RNN实现"""
def __init__(self, input_size, hidden_size, output_size):
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
# 初始化权重
self.W_hh = np.random.randn(hidden_size, hidden_size) * 0.1 # 隐藏层到隐藏层
self.W_xh = np.random.randn(input_size, hidden_size) * 0.1 # 输入到隐藏层
self.W_hy = np.random.randn(hidden_size, output_size) * 0.1 # 隐藏层到输出
self.b_h = np.zeros(hidden_size) # 隐藏层偏置
self.b_y = np.zeros(output_size) # 输出层偏置
def tanh(self, x):
"""Tanh激活函数"""
return np.tanh(x)
def softmax(self, x):
"""Softmax函数"""
exp_x = np.exp(x - np.max(x))
return exp_x / np.sum(exp_x)
def forward(self, inputs):
"""前向传播"""
hidden_states = []
outputs = []
# 初始化隐藏状态
h = np.zeros(self.hidden_size)
# 对每个时间步进行处理
for x in inputs:
# 计算隐藏状态
h = self.tanh(np.dot(x, self.W_xh) + np.dot(h, self.W_hh) + self.b_h)
hidden_states.append(h)
# 计算输出
y = np.dot(h, self.W_hy) + self.b_y
output = self.softmax(y)
outputs.append(output)
return outputs, hidden_states
def predict(self, inputs):
"""预测"""
outputs, _ = self.forward(inputs)
return np.argmax(outputs[-1]) # 返回最后一个时间步的预测
# 文本序列处理示例
def text_sequence_demo():
"""文本序列处理演示"""
# 简单的字符级文本处理
text = "hello world"
# 创建字符到索引的映射
chars = list(set(text))
char_to_idx = {ch: i for i, ch in enumerate(chars)}
idx_to_char = {i: ch for i, ch in enumerate(chars)}
print("字符映射:")
for ch, idx in char_to_idx.items():
print(f" '{ch}' -> {idx}")
# 将文本转换为索引序列
sequence = [char_to_idx[ch] for ch in text]
print(f"\n文本序列: {sequence}")
# 转换为one-hot编码
def to_one_hot(indices, vocab_size):
one_hot = np.zeros((len(indices), vocab_size))
for i, idx in enumerate(indices):
one_hot[i, idx] = 1
return one_hot
one_hot_sequence = to_one_hot(sequence, len(chars))
print(f"序列形状: {one_hot_sequence.shape}")
# 创建简单RNN
rnn = SimpleRNN(input_size=len(chars), hidden_size=10, output_size=len(chars))
# 前向传播
outputs, hidden_states = rnn.forward(one_hot_sequence)
print(f"\n输出数量: {len(outputs)}")
print(f"隐藏状态数量: {len(hidden_states)}")
print(f"每个输出形状: {outputs[0].shape}")
print(f"每个隐藏状态形状: {hidden_states[0].shape}")
# 可视化隐藏状态
plt.figure(figsize=(12, 6))
# 绘制隐藏状态热力图
hidden_matrix = np.array(hidden_states)
plt.subplot(1, 2, 1)
plt.imshow(hidden_matrix, cmap='viridis', aspect='auto')
plt.xlabel('隐藏单元')
plt.ylabel('时间步')
plt.title('RNN隐藏状态演化')
plt.colorbar(label='激活值')
# 绘制最后一个时间步的输出分布
plt.subplot(1, 2, 2)
plt.bar(range(len(chars)), outputs[-1])
plt.xlabel('字符索引')
plt.ylabel('概率')
plt.title('最后一个时间步的输出分布')
plt.xticks(range(len(chars)), [idx_to_char[i] for i in range(len(chars))])
plt.tight_layout()
plt.show()
text_sequence_demo()
LSTM和GRU
为了解决RNN的梯度消失问题,LSTM和GRU引入了门控机制。
# LSTM单元实现(简化版)
class SimpleLSTM:
"""简化版LSTM实现"""
def __init__(self, input_size, hidden_size):
self.input_size = input_size
self.hidden_size = hidden_size
# 为简化,我们只展示LSTM的概念结构
self.W = np.random.randn(input_size + hidden_size, 4 * hidden_size) * 0.1
self.b = np.zeros(4 * hidden_size)
def sigmoid(self, x):
"""Sigmoid函数"""
return 1 / (1 + np.exp(-np.clip(x, -500, 500)))
def tanh(self, x):
"""Tanh函数"""
return np.tanh(x)
def forward_step(self, x, h_prev, c_prev):
"""单步前向传播"""
# 拼接输入和前一隐藏状态
concat = np.concatenate([x, h_prev])
# 计算门控值
gates = np.dot(concat, self.W) + self.b
i, f, o, g = np.split(gates, 4) # 输入门、遗忘门、输出门、候选值
# 应用激活函数
i = self.sigmoid(i) # 输入门
f = self.sigmoid(f) # 遗忘门
o = self.sigmoid(o) # 输出门
g = self.tanh(g) # 候选值
# 计算当前细胞状态
c = f * c_prev + i * g
# 计算当前隐藏状态
h = o * self.tanh(c)
return h, c
# 可视化LSTM门控机制
def visualize_lstm_gates():
"""可视化LSTM门控机制"""
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 模拟门控值
time_steps = range(10)
forget_gate = np.exp(-np.array(time_steps) * 0.2) # 遗忘门随时间衰减
input_gate = 1 - forget_gate # 输入门与遗忘门互补
output_gate = 0.5 + 0.3 * np.sin(np.array(time_steps) * 0.5) # 输出门周期性变化
candidate_values = np.random.randn(10) # 候选值
axes[0, 0].plot(time_steps, forget_gate, 'b-o')
axes[0, 0].set_title('遗忘门')
axes[0, 0].set_ylabel('门控值')
axes[0, 0].grid(True, alpha=0.3)
axes[0, 1].plot(time_steps, input_gate, 'g-o')
axes[0, 1].set_title('输入门')
axes[0, 1].grid(True, alpha=0.3)
axes[1, 0].plot(time_steps, output_gate, 'r-o')
axes[1, 0].set_title('输出门')
axes[1, 0].set_xlabel('时间步')
axes[1, 0].set_ylabel('门控值')
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].plot(time_steps, candidate_values, 'm-o')
axes[1, 1].set_title('候选值')
axes[1, 1].set_xlabel('时间步')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
visualize_lstm_gates()
print("LSTM门控机制:")
print("1. 遗忘门: 决定从细胞状态中丢弃什么信息")
print("2. 输入门: 决定在细胞状态中存储什么新信息")
print("3. 输出门: 决定基于细胞状态输出什么")
print("4. 候选值: 创建新的候选值向量")
CNN与RNN的应用对比
# 应用场景对比
def compare_cnn_rnn_applications():
"""对比CNN和RNN的应用场景"""
applications = {
'CNN': [
'图像分类',
'目标检测',
'人脸识别',
'医学图像分析',
'风格迁移',
'图像生成'
],
'RNN': [
'文本分类',
'机器翻译',
'语音识别',
'时间序列预测',
'情感分析',
'文本生成'
]
}
plt.figure(figsize=(15, 6))
# CNN应用
plt.subplot(1, 2, 1)
y_pos = np.arange(len(applications['CNN']))
plt.barh(y_pos, [1]*len(applications['CNN']), color='skyblue')
plt.yticks(y_pos, applications['CNN'])
plt.title('CNN主要应用场景')
plt.xlabel('适用性')
plt.grid(True, alpha=0.3)
# RNN应用
plt.subplot(1, 2, 2)
y_pos = np.arange(len(applications['RNN']))
plt.barh(y_pos, [1]*len(applications['RNN']), color='lightcoral')
plt.yticks(y_pos, applications['RNN'])
plt.title('RNN主要应用场景')
plt.xlabel('适用性')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
compare_cnn_rnn_applications()
# 现代架构发展
def modern_architecture_evolution():
"""现代架构发展时间线"""
architectures = {
'1980s': ['LeNet (CNN)'],
'1990s': ['LSTM (RNN)'],
'2012': ['AlexNet (CNN)'],
'2014': ['GRU (RNN)'],
'2015': ['ResNet (CNN)'],
'2017': ['Transformer'],
'2018': ['BERT (Transformer)'],
'2020': ['GPT-3 (Transformer)']
}
plt.figure(figsize=(12, 8))
years = list(architectures.keys())
year_nums = range(len(years))
plt.hlines(1, 0, len(years)-1, alpha=0.3)
plt.scatter(year_nums, [1]*len(year_nums), s=100, color='red')
for i, (year, archs) in enumerate(architectures.items()):
plt.annotate(f"{year}\n{', '.join(archs)}", (i, 1),
xytext=(0, 30 if i % 2 == 0 else -60),
textcoords='offset points',
ha='center', va='bottom' if i % 2 == 0 else 'top',
bbox=dict(boxstyle='round,pad=0.3', fc='lightgreen', alpha=0.7),
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
plt.xlim(-0.5, len(years)-0.5)
plt.ylim(0.5, 1.5)
plt.yticks([])
plt.xlabel('时间')
plt.title('神经网络架构发展时间线')
plt.tight_layout()
plt.show()
modern_architecture_evolution()
本周学习总结
今天我们深入学习了两种重要的现代神经网络架构:
-
卷积神经网络(CNN)
- 理解了卷积层、池化层和全连接层的作用
- 实现了简单的CNN进行图像分类
- 学习了CNN在计算机视觉中的应用
-
循环神经网络(RNN)
- 掌握了RNN处理序列数据的原理
- 实现了简单的RNN处理文本序列
- 了解了LSTM和GRU的门控机制
-
应用对比
- 比较了CNN和RNN的不同应用场景
- 了解了现代神经网络架构的发展历程
graph TD
A[现代神经网络] --> B[CNN]
A --> C[RNN]
B --> D[卷积层]
B --> E[池化层]
B --> F[应用]
C --> G[循环层]
C --> H[LSTM/GRU]
C --> I[应用]
F --> J[图像处理]
I --> K[序列处理]
课后练习
- 运行本节所有代码示例,理解CNN和RNN的工作原理
- 修改SimpleCNN,增加更多的卷积层和池化层,观察效果变化
- 实现一个简单的文本生成器,使用RNN生成字符序列
- 研究现代CNN架构(如ResNet、EfficientNet)和Transformer架构的原理
下节预告
下一节我们将学习Transformer和大语言模型,包括自注意力机制、BERT、GPT等现代NLP技术,这些是当前AI领域最热门的研究方向,敬请期待!
有任何疑问请在讨论区留言,我们会定期回复大家的问题。