第三章:深度学习实战
3.1 神经网络基础
深度学习是机器学习的一个重要分支,而神经网络是深度学习的核心。本章将带您深入了解神经网络的基本原理,从简单的感知机到复杂的深度网络,通过实际案例帮助您掌握深度学习的核心技术。
3.1.1 什么是神经网络?
神经网络是一种受人脑神经系统启发的计算模型,它由大量的相互连接的处理单元(神经元)组成,能够学习和模拟复杂的非线性关系。
神经元的基本结构
每个神经元可以看作是一个简单的计算单元,它接收多个输入信号,进行加权求和,然后通过激活函数产生输出:
数学表示:
output = activation(∑(input_i * weight_i) + bias)
组成部分:
-
输入: 来自其他神经元或外部数据
-
权重: 连接的强度,通过学习调整
-
偏置: 调整神经元激活的阈值
-
激活函数: 引入非线性,使网络能够学习复杂模式
神经网络的基本架构
单层神经网络:
-
只有一个输入层和一个输出层
-
可以解决线性可分的问题
-
计算简单但表达能力有限
多层神经网络:
-
包含输入层、隐藏层和输出层
-
隐藏层越多,网络越深
-
能够学习更复杂的非线性关系
3.1.2 激活函数
激活函数是神经网络中非常重要的组成部分,它为网络引入非线性特性,使网络能够学习复杂的模式。
常用激活函数
Sigmoid函数:
f(x) = 1 / (1 + exp(-x))
特点:
-
输出范围:0到1
-
适用于二分类问题
-
存在梯度消失问题
Tanh函数:
f(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))
特点:
-
输出范围:-1到1
-
零中心化
-
仍然存在梯度消失问题
ReLU函数:
f(x) = max(0, x)
特点:
-
计算简单
-
缓解梯度消失问题
-
存在神经元死亡问题
Leaky ReLU函数:
f(x) = max(0.01x, x)
特点:
-
解决ReLU的神经元死亡问题
-
保持简单计算
3.1.3 神经网络的前向传播
前向传播是神经网络的基本计算过程,信号从输入层经过隐藏层最终到达输出层。
前向传播步骤
步骤1: 输入层到隐藏层
hidden = activation(input * W1 + b1)
步骤2: 隐藏层到输出层
output = activation(hidden * W2 + b2)
步骤3: 计算损失
loss = loss_function(output, target)
示例代码
import numpy as np
import matplotlib.pyplot as plt
# 定义激活函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def relu(x):
return np.maximum(0, x)
# 神经网络参数
input_size = 2
hidden_size = 4
output_size = 1
# 初始化权重和偏置
np.random.seed(42)
W1 = np.random.randn(input_size, hidden_size) * 0.01
b1 = np.zeros((1, hidden_size))
W2 = np.random.randn(hidden_size, output_size) * 0.01
b2 = np.zeros((1, output_size))
# 前向传播函数
def forward_propagation(X):
# 输入层到隐藏层
Z1 = np.dot(X, W1) + b1
A1 = relu(Z1)
# 隐藏层到输出层
Z2 = np.dot(A1, W2) + b2
A2 = sigmoid(Z2)
return Z1, A1, Z2, A2
# 示例数据
X = np.array([[0.5, 0.3], [0.2, 0.8], [0.9, 0.1], [0.4, 0.6]])
# 前向传播
Z1, A1, Z2, A2 = forward_propagation(X)
print("输入数据:")
print(X)
print("\n隐藏层输出:")
print(A1)
print("\n输出层结果:")
print(A2)
3.1.4 神经网络的反向传播
反向传播是神经网络训练的核心算法,它通过计算损失函数对各个参数的梯度来更新网络的权重。
反向传播原理
梯度计算:
-
从输出层开始,逐层计算梯度
-
使用链式法则传播误差
-
更新权重和偏置
反向传播步骤
步骤1: 计算输出层梯度
dZ2 = A2 - target
dW2 = (1/m) * np.dot(A1.T, dZ2)
db2 = (1/m) * np.sum(dZ2, axis=0, keepdims=True)
步骤2: 计算隐藏层梯度
dA1 = np.dot(dZ2, W2.T)
dZ1 = dA1 * (Z1 > 0) # ReLU的导数
dW1 = (1/m) * np.dot(X.T, dZ1)
db1 = (1/m) * np.sum(dZ1, axis=0, keepdims=True)
步骤3: 参数更新
W1 = W1 - learning_rate * dW1
b1 = b1 - learning_rate * db1
W2 = W2 - learning_rate * dW2
b2 = b2 - learning_rate * db2
3.1.5 实战案例:手写数字识别
让我们通过一个经典的案例来理解神经网络的实际应用 - 手写数字识别。
项目概述
我们将使用MNIST数据集来训练一个简单的神经网络,实现手写数字的0-9识别。
数据加载和预处理
import tensorflow as tf
from tensorflow.keras.datasets import mnist
import numpy as np
# 加载数据集
(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
# 标准化
X_train = X_train.astype(np.float32)
X_test = X_test.astype(np.float32)
# 标签转换为one-hot编码
y_train = tf.keras.utils.to_categorical(y_train, 10)
y_test = tf.keras.utils.to_categorical(y_test, 10)
print(f"训练数据形状: {X_train.shape}")
print(f"测试数据形状: {X_test.shape}")
print(f"标签形状: {y_train.shape}")
构建神经网络模型
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
# 构建模型
model = Sequential([
Dense(512, activation='relu', input_shape=(784,)),
Dropout(0.2),
Dense(256, activation='relu'),
Dropout(0.2),
Dense(128, activation='relu'),
Dense(10, activation='softmax')
])
# 编译模型
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
# 显示模型结构
model.summary()
训练模型
# 训练模型
history = model.fit(X_train, y_train,
batch_size=128,
epochs=10,
validation_split=0.2,
verbose=1)
# 评估模型
test_loss, test_acc = model.evaluate(X_test, y_test, verbose=0)
print(f"测试准确率: {test_acc:.4f}")
模型预测和可视化
import matplotlib.pyplot as plt
# 选择测试集中的前10个样本
sample_images = X_test[:10]
sample_labels = np.argmax(y_test[:10], axis=1)
# 预测
predictions = model.predict(sample_images)
predicted_labels = np.argmax(predictions, axis=1)
# 显示结果
plt.figure(figsize=(12, 6))
for i in range(10):
plt.subplot(2, 5, i+1)
plt.imshow(sample_images[i].reshape(28, 28), cmap='gray')
plt.title(f"True: {sample_labels[i]}\nPred: {predicted_labels[i]}")
plt.axis('off')
plt.tight_layout()
plt.show()
# 显示训练过程
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(history.history['accuracy'], label='Training Accuracy')
plt.plot(history.history['val_accuracy'], label='Validation Accuracy')
plt.title('Accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(history.history['loss'], label='Training Loss')
plt.plot(history.history['val_loss'], label='Validation Loss')
plt.title('Loss')
plt.legend()
plt.show()
3.1.6 神经网络调优技巧
在实际应用中,神经网络的调优是非常重要的。以下是一些常用的调优技巧:
超参数调优
学习率:
-
太小:收敛慢
-
太大:震荡不收敛
-
建议:从0.001开始,尝试0.01、0.0001等
批量大小:
-
太小:梯度估计不稳定
-
太大:收敛速度慢
-
建议:32、64、128、256等
网络深度:
-
太浅:表达能力不足
-
太深:梯度消失/爆炸
-
建议:从2-3层开始,逐步增加
正则化技术
Dropout:
-
随机丢弃一些神经元
-
防止过拟合
-
建议:0.2-0.5之间
L2正则化:
-
惩罚大权重
-
防止过拟合
-
建议:0.001-0.01之间
激活函数选择
输出层:
-
二分类:Sigmoid
-
多分类:Softmax
-
回归:Linear
隐藏层:
-
默认:ReLU
-
特殊情况:Leaky ReLU、ELU、Swish等
通过本节的学习,您应该已经掌握了神经网络的基本原理和实现方法。在下一节中,我们将介绍深度学习的主要框架,帮助您选择合适的工具进行深度学习开发。