从吴恩达课程到实战:手把手教你用Python实现一个5层深度神经网络(附完整代码)
·
从吴恩达课程到实战:手把手教你用Python实现一个5层深度神经网络(附完整代码)
深度学习的魅力在于它能够通过层层抽象自动提取特征,而不仅仅是依赖人工设计的特征工程。吴恩达教授的课程为我们奠定了坚实的理论基础,但真正掌握深度神经网络的精髓,还需要将数学公式转化为可运行的代码。本文将带你从零开始构建一个5层深度神经网络,重点关注维度匹配、梯度流动和参数优化等实战关键点。
1. 环境准备与数据加载
在开始构建神经网络之前,我们需要确保开发环境配置正确。推荐使用Python 3.8+版本,并安装以下核心库:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
我们使用 make_moons 生成非线性可分数据集,这种数据非常适合验证深度神经网络的表达能力:
X, y = make_moons(n_samples=1000, noise=0.2, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
数据预处理是模型成功的关键第一步:
- 标准化 :将特征值缩放到相似范围
- 维度调整 :确保输入数据形状符合网络要求
- 可视化 :直观理解数据分布特征
# 数据标准化
mean = X_train.mean(axis=0)
std = X_train.std(axis=0)
X_train = (X_train - mean) / std
X_test = (X_test - mean) / std
# 调整维度
X_train = X_train.T
y_train = y_train.reshape(1, -1)
X_test = X_test.T
y_test = y_test.reshape(1, -1)
2. 网络架构设计与初始化
我们构建的5层神经网络结构如下:
| 层类型 | 单元数量 | 激活函数 |
|---|---|---|
| 输入层 | 2 | - |
| 隐藏层1 | 64 | ReLU |
| 隐藏层2 | 32 | ReLU |
| 隐藏层3 | 16 | ReLU |
| 输出层 | 1 | Sigmoid |
参数初始化对深度神经网络的训练至关重要。不恰当的初始化可能导致梯度消失或爆炸:
def initialize_parameters(layer_dims):
np.random.seed(42)
parameters = {}
L = len(layer_dims)
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l-1]) * 0.01
parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))
return parameters
layer_dims = [2, 64, 32, 16, 1]
parameters = initialize_parameters(layer_dims)
注意 :权重初始化使用较小的随机值可以避免初始阶段神经元输出饱和,而偏置初始化为零是常见做法。
3. 前向传播实现与维度核对
前向传播需要为每一层实现两个关键操作:
- 线性变换:Z = W·A_prev + b
- 非线性激活:A = g(Z)
def relu(Z):
return np.maximum(0, Z)
def sigmoid(Z):
return 1 / (1 + np.exp(-Z))
def forward_propagation(X, parameters):
caches = []
A = X
L = len(parameters) // 2
for l in range(1, L):
A_prev = A
W = parameters['W' + str(l)]
b = parameters['b' + str(l)]
Z = np.dot(W, A_prev) + b
A = relu(Z)
caches.append((A_prev, W, b, Z))
# 输出层使用sigmoid激活
W = parameters['W' + str(L)]
b = parameters['b' + str(L)]
Z = np.dot(W, A) + b
AL = sigmoid(Z)
caches.append((A, W, b, Z))
return AL, caches
维度核对是调试神经网络的关键技能。以下表格展示了各层矩阵的正确维度:
| 变量 | 维度公式 | 示例维度 |
|---|---|---|
| W^[l] | (n^[l], n^[l-1]) | W1: (64, 2) |
| b^[l] | (n^[l], 1) | b1: (64, 1) |
| Z^[l], A^[l] | (n^[l], m) | A1: (64, 700) |
4. 损失函数计算与反向传播
二元分类问题使用交叉熵损失函数:
def compute_cost(AL, Y):
m = Y.shape[1]
cost = -np.mean(Y * np.log(AL) + (1-Y) * np.log(1-AL))
return np.squeeze(cost)
反向传播计算梯度是深度神经网络训练的核心。我们需要为每一层计算三个关键梯度:
- dW:权重梯度
- db:偏置梯度
- dA_prev:前一层激活梯度
def relu_backward(dA, Z):
dZ = np.array(dA, copy=True)
dZ[Z <= 0] = 0
return dZ
def backward_propagation(AL, Y, caches):
grads = {}
L = len(caches)
m = AL.shape[1]
Y = Y.reshape(AL.shape)
# 初始化反向传播
dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
# 输出层梯度
current_cache = caches[L-1]
A_prev, W, b, Z = current_cache
dZ = AL - Y
grads['dW' + str(L)] = np.dot(dZ, A_prev.T) / m
grads['db' + str(L)] = np.sum(dZ, axis=1, keepdims=True) / m
grads['dA' + str(L-1)] = np.dot(W.T, dZ)
# 隐藏层梯度
for l in reversed(range(L-1)):
current_cache = caches[l]
A_prev, W, b, Z = current_cache
dA = grads['dA' + str(l+1)]
dZ = relu_backward(dA, Z)
grads['dW' + str(l+1)] = np.dot(dZ, A_prev.T) / m
grads['db' + str(l+1)] = np.sum(dZ, axis=1, keepdims=True) / m
grads['dA' + str(l)] = np.dot(W.T, dZ)
return grads
5. 参数更新与训练循环
使用梯度下降算法更新参数:
def update_parameters(parameters, grads, learning_rate):
L = len(parameters) // 2
for l in range(1, L+1):
parameters['W' + str(l)] -= learning_rate * grads['dW' + str(l)]
parameters['b' + str(l)] -= learning_rate * grads['db' + str(l)]
return parameters
完整的训练循环整合了前向传播、损失计算、反向传播和参数更新:
def model(X, Y, layer_dims, learning_rate=0.01, num_iterations=3000, print_cost=False):
costs = []
parameters = initialize_parameters(layer_dims)
for i in range(num_iterations):
# 前向传播
AL, caches = forward_propagation(X, parameters)
# 计算损失
cost = compute_cost(AL, Y)
# 反向传播
grads = backward_propagation(AL, Y, caches)
# 参数更新
parameters = update_parameters(parameters, grads, learning_rate)
# 记录损失
if i % 100 == 0:
costs.append(cost)
if print_cost:
print(f"迭代次数 {i}: 损失 {cost}")
# 绘制学习曲线
plt.plot(costs)
plt.xlabel('迭代次数(每100次)')
plt.ylabel('损失')
plt.title('学习率 =' + str(learning_rate))
plt.show()
return parameters
6. 模型评估与可视化
训练完成后,我们需要评估模型在测试集上的表现:
def predict(X, parameters):
AL, _ = forward_propagation(X, parameters)
predictions = (AL > 0.5).astype(int)
return predictions
def accuracy(y_true, y_pred):
return np.mean(y_true == y_pred) * 100
# 训练模型
parameters = model(X_train, y_train, layer_dims, learning_rate=0.03, num_iterations=3000, print_cost=True)
# 测试集评估
predictions = predict(X_test, parameters)
print(f"测试集准确率: {accuracy(y_test, predictions):.2f}%")
可视化决策边界可以直观理解模型的分类能力:
def plot_decision_boundary(X, y, parameters):
x_min, x_max = X[0, :].min() - 1, X[0, :].max() + 1
y_min, y_max = X[1, :].min() - 1, X[1, :].max() + 1
h = 0.01
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = predict(np.c_[xx.ravel(), yy.ravel()].T, parameters)
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral, alpha=0.8)
plt.scatter(X[0, :], X[1, :], c=y, cmap=plt.cm.Spectral)
plt.xlabel('特征1')
plt.ylabel('特征2')
plt.title('决策边界')
plt.show()
plot_decision_boundary(X_test, y_test[0], parameters)
7. 超参数调优与实战技巧
深度神经网络的性能很大程度上取决于超参数的选择。以下是几个关键超参数的经验范围:
| 超参数 | 推荐范围 | 调整策略 |
|---|---|---|
| 学习率 | 0.0001-0.1 | 指数衰减 |
| 批量大小 | 32-256 | 2的幂次 |
| 隐藏层数 | 2-5 | 逐步增加 |
| 单元数量 | 16-512 | 网格搜索 |
调试深度神经网络时常见的几个问题及解决方案:
- 梯度消失 :使用ReLU激活函数、批归一化、残差连接
- 过拟合 :添加L2正则化、Dropout层、数据增强
- 训练不稳定 :梯度裁剪、学习率预热、自适应优化器
# 添加L2正则化的损失计算
def compute_cost_with_regularization(AL, Y, parameters, lambd):
m = Y.shape[1]
cross_entropy_cost = compute_cost(AL, Y)
L = len(parameters) // 2
L2_cost = 0
for l in range(1, L+1):
L2_cost += np.sum(np.square(parameters['W' + str(l)]))
L2_cost = (lambd / (2 * m)) * L2_cost
cost = cross_entropy_cost + L2_cost
return np.squeeze(cost)
在实际项目中,我发现使用学习率衰减策略可以显著提升模型性能:
def learning_rate_decay(initial_lr, epoch, decay_rate=0.1, decay_step=100):
return initial_lr * (decay_rate ** (epoch // decay_step))
更多推荐


所有评论(0)