吴恩达深度学习课程一:神经网络和深度学习 第三周:浅层神经网络(二)
·
吴恩达深度学习课程一:神经网络和深度学习 第三周:浅层神经网络(二)
回顾与引入:从单神经元到浅层网络在上一部分中,我们学习了浅层神经网络的基础结构:输入层、一个隐藏层和输出层。我们了解了如何通过前向传播计算输出,以及神经网络如何通过激活函数引入非线性。本周第二部分将更深入地探讨神经网络的训练过程,包括反向传播的数学原理、参数初始化的重要性,以及如何用代码实现一个完整的浅层神经网络。我们将以二元分类问题为例,逐步构建一个可运行的模型。### 反向传播:梯度计算的基石反向传播是训练神经网络的核心算法。它通过链式法则计算损失函数对每个参数的梯度,然后使用梯度下降更新参数。对于浅层神经网络,我们需要计算输出层和隐藏层的梯度。#### 输出层的梯度计算假设我们使用二元分类,输出层激活函数为 Sigmoid,损失函数为交叉熵损失。对于单个样本,损失为:L = - (y * log(a) + (1-y) * log(1-a))其中 a 是输出层的激活值。对输出层参数 W2 和 b2 的梯度为:dZ2 = A2 - YdW2 = (1/m) * dZ2 * A1.Tdb2 = (1/m) * sum(dZ2)这里 m 是样本数量,A1 是隐藏层的激活值。#### 隐藏层的梯度计算隐藏层使用 tanh 激活函数(或 ReLU)。假设使用 tanh,其导数 g’(z) = 1 - (tanh(z))^2。隐藏层的梯度为:dZ1 = W2.T * dZ2 * g'(Z1)dW1 = (1/m) * dZ1 * X.Tdb1 = (1/m) * sum(dZ1)### 参数初始化:影响收敛的关键参数初始化不当会导致梯度消失或爆炸。对于浅层网络,通常使用随机初始化打破对称性,且权重初始值不宜过大。常见做法是使用小随机数(如 np.random.randn(shape) * 0.01)。偏置可以初始化为 0。#### 为什么需要随机初始化?如果所有参数初始化为相同值,隐藏层神经元将计算相同输出,导致梯度相同,无法学习不同特征。### 完整代码实现:构建浅层神经网络下面我们实现一个包含一个隐藏层的浅层神经网络,用于二元分类。我们将使用 Python 和 NumPy,从零开始实现前向传播、反向传播和梯度下降。pythonimport numpy as npimport matplotlib.pyplot as plt# 生成模拟数据集(两个半圆形分布)def load_planar_dataset(): np.random.seed(1) m = 400 # 样本数 N = int(m / 2) # 每类样本数 D = 2 # 维度 X = np.zeros((m, D)) Y = np.zeros((m, 1), dtype='uint8') a = 4 # 半圆半径 for j in range(2): ix = range(N * j, N * (j + 1)) t = np.linspace(j * 3.12, (j + 1) * 3.12, N) + np.random.randn(N) * 0.2 r = a * np.sin(4 * t) + np.random.randn(N) * 0.2 X[ix] = np.c_[r * np.sin(t), r * np.cos(t)] Y[ix] = j X = X.T Y = Y.T return X, Y# 数据准备X, Y = load_planar_dataset()print("数据集形状:X:", X.shape, "Y:", Y.shape)# 输出:数据集形状:X: (2, 400) Y: (1, 400)# 定义激活函数及其导数def sigmoid(z): return 1 / (1 + np.exp(-z))def tanh(z): return np.tanh(z)def tanh_derivative(z): return 1 - np.tanh(z)**2# 初始化参数def initialize_parameters(n_x, n_h, n_y): np.random.seed(2) W1 = np.random.randn(n_h, n_x) * 0.01 b1 = np.zeros((n_h, 1)) W2 = np.random.randn(n_y, n_h) * 0.01 b2 = np.zeros((n_y, 1)) return {"W1": W1, "b1": b1, "W2": W2, "b2": b2}# 前向传播def forward_propagation(X, parameters): W1 = parameters["W1"] b1 = parameters["b1"] W2 = parameters["W2"] b2 = parameters["b2"] Z1 = np.dot(W1, X) + b1 A1 = tanh(Z1) # 隐藏层使用tanh Z2 = np.dot(W2, A1) + b2 A2 = sigmoid(Z2) # 输出层使用sigmoid cache = {"Z1": Z1, "A1": A1, "Z2": Z2, "A2": A2} return A2, cache# 计算损失def compute_cost(A2, Y): m = Y.shape[1] cost = -(1/m) * np.sum(Y * np.log(A2) + (1 - Y) * np.log(1 - A2)) cost = np.squeeze(cost) # 确保是标量 return cost# 反向传播def backward_propagation(parameters, cache, X, Y): m = X.shape[1] W2 = parameters["W2"] A1 = cache["A1"] A2 = cache["A2"] Z1 = cache["Z1"] dZ2 = A2 - Y dW2 = (1/m) * np.dot(dZ2, A1.T) db2 = (1/m) * np.sum(dZ2, axis=1, keepdims=True) dZ1 = np.dot(W2.T, dZ2) * tanh_derivative(Z1) dW1 = (1/m) * np.dot(dZ1, X.T) db1 = (1/m) * np.sum(dZ1, axis=1, keepdims=True) grads = {"dW1": dW1, "db1": db1, "dW2": dW2, "db2": db2} return grads# 更新参数def update_parameters(parameters, grads, learning_rate=1.2): parameters["W1"] -= learning_rate * grads["dW1"] parameters["b1"] -= learning_rate * grads["db1"] parameters["W2"] -= learning_rate * grads["dW2"] parameters["b2"] -= learning_rate * grads["db2"] return parameters# 训练模型def nn_model(X, Y, n_h, num_iterations=10000, print_cost=False): n_x = X.shape[0] n_y = Y.shape[0] parameters = initialize_parameters(n_x, n_h, n_y) for i in range(num_iterations): A2, cache = forward_propagation(X, parameters) cost = compute_cost(A2, Y) grads = backward_propagation(parameters, cache, X, Y) parameters = update_parameters(parameters, grads) if print_cost and i % 1000 == 0: print(f"迭代 {i} 后的损失: {cost:.6f}") return parameters# 训练模型(隐藏层4个神经元)parameters = nn_model(X, Y, n_h=4, num_iterations=10000, print_cost=True)# 预测函数def predict(parameters, X): A2, cache = forward_propagation(X, parameters) predictions = (A2 > 0.5).astype(int) return predictions# 计算准确率predictions = predict(parameters, X)accuracy = np.mean(predictions == Y) * 100print(f"训练集准确率: {accuracy:.2f}%")运行此代码,您将看到损失逐渐下降,最终准确率约为 90% 左右。这展示了浅层神经网络在非线性分类问题上的强大能力。### 深入理解:隐藏层神经元数量对模型的影响隐藏层神经元数量是一个重要超参数。过少会导致欠拟合(无法捕获复杂模式),过多则可能导致过拟合(记忆噪声)。下面我们测试不同隐藏层大小对模型性能的影响。python# 测试不同隐藏层大小hidden_layer_sizes = [1, 2, 4, 8, 16, 32]accuracies = []for n_h in hidden_layer_sizes: parameters = nn_model(X, Y, n_h, num_iterations=5000, print_cost=False) predictions = predict(parameters, X) accuracy = np.mean(predictions == Y) * 100 accuracies.append(accuracy) print(f"隐藏层 {n_h} 个神经元,准确率: {accuracy:.2f}%")# 可视化结果plt.figure(figsize=(10, 5))plt.plot(hidden_layer_sizes, accuracies, marker='o')plt.xlabel('隐藏层神经元数量')plt.ylabel('准确率 (%)')plt.title('隐藏层大小对分类准确率的影响')plt.grid(True)plt.show()通过此实验,您可以观察到:当隐藏层神经元从 1 增加到 4 时,准确率显著提升;继续增加神经元,准确率可能趋于平稳或略微下降(由于过拟合)。这强调了选择合适模型复杂度的必要性。### 总结在本部分中,我们深入学习了浅层神经网络的完整训练流程:- 反向传播的数学原理:通过链式法则计算梯度,理解输出层和隐藏层的梯度公式。- 参数初始化策略:使用小随机数打破对称性,避免梯度问题。- 完整实现:从零构建了包含一个隐藏层的神经网络,并成功应用于非线性分类问题。- 超参数实验:通过改变隐藏层神经元数量,直观感受模型容量与性能的关系。浅层神经网络是理解深度学习的基础。掌握这部分内容后,您将能更好地理解更复杂的网络结构(如深层神经网络)。建议动手修改代码中的激活函数(如将 tanh 替换为 ReLU)或学习率,观察对训练过程的影响。实践是巩固知识的最佳方式。
更多推荐

所有评论(0)