使用Python实现一个简单的神经网络
在机器学习和人工智能领域,神经网络是一种非常强大的工具。它模仿人脑的结构和功能,通过多层非线性变换来学习数据中的复杂模式。本文将介绍如何使用 Python 实现一个简单的全连接前馈神经网络(Feedforward Neural Network),并通过一个简单的分类任务来演示其训练过程。
我们将使用 NumPy
库进行数值计算,并手动实现反向传播算法。整个实现不依赖于任何深度学习框架(如 TensorFlow 或 PyTorch),以帮助读者更深入理解神经网络的工作原理。
1. 神经网络的基本概念
一个基本的神经网络由输入层、隐藏层和输出层组成。每一层都包含多个神经元,每个神经元与下一层的所有神经元相连,因此也称为全连接层。
神经网络的核心操作包括:
前向传播(Forward Propagation):输入数据经过各层神经元计算,最终得到预测结果。损失函数(Loss Function):衡量预测值与真实值之间的误差。反向传播(Backward Propagation):根据损失函数对参数求导,更新权重矩阵。优化器(Optimizer):使用梯度下降等方法来更新参数。2. 数据准备
为了简化问题,我们使用一个合成的二分类数据集。我们可以使用 sklearn.datasets.make_blobs
来生成一些二维数据点。
import numpy as npimport matplotlib.pyplot as pltfrom sklearn.datasets import make_blobsfrom sklearn.model_selection import train_test_split# 生成数据X, y = make_blobs(n_samples=300, centers=2, cluster_std=1.5, random_state=42)y = y.reshape((y.shape[0], 1)) # 将标签转换为列向量# 划分训练集和测试集X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)print("训练集特征维度:", X_train.shape)print("训练集标签维度:", y_train.shape)
3. 激活函数与损失函数
常用的激活函数有 Sigmoid 和 ReLU。在这里我们使用 Sigmoid 函数作为隐藏层的激活函数,以及输出层用于二分类的概率输出。
Sigmoid 函数及其导数:
$$\sigma(x) = \frac{1}{1 + e^{-x}}, \quad \sigma'(x) = \sigma(x)(1 - \sigma(x))$$
二分类交叉熵损失函数:
$$L = -\frac{1}{N} \sum_{i=1}^{N} \left[ y_i \log(\hat{y}_i) + (1 - y_i) \log(1 - \hat{y}_i) \right]$$
下面是对应的 Python 实现:
def sigmoid(x): return 1 / (1 + np.exp(-x))def sigmoid_derivative(x): s = sigmoid(x) return s * (1 - s)def binary_cross_entropy(y_true, y_pred): epsilon = 1e-15 # 防止 log(0) loss = -np.mean(y_true * np.log(y_pred + epsilon) + (1 - y_true) * np.log(1 - y_pred + epsilon)) return loss
4. 网络结构定义
我们构建一个具有以下结构的神经网络:
输入层:2个神经元(对应二维特征)隐藏层:4个神经元输出层:1个神经元(二分类)初始化权重矩阵和偏置项:
input_size = 2hidden_size = 4output_size = 1# 初始化权重和偏置W1 = np.random.randn(input_size, hidden_size)b1 = np.zeros((1, hidden_size))W2 = np.random.randn(hidden_size, output_size)b2 = np.zeros((1, output_size))
5. 前向传播
定义前向传播函数:
def forward(X, W1, b1, W2, b2): Z1 = np.dot(X, W1) + b1 A1 = sigmoid(Z1) Z2 = np.dot(A1, W2) + b2 A2 = sigmoid(Z2) return Z1, A1, Z2, A2
6. 反向传播与参数更新
根据链式法则计算梯度并更新参数:
def backward(X, y, Z1, A1, Z2, A2, W1, W2, learning_rate=0.01): m = X.shape[0] dZ2 = A2 - y dW2 = np.dot(A1.T, dZ2) / m db2 = np.sum(dZ2, axis=0, keepdims=True) / m dA1 = np.dot(dZ2, W2.T) dZ1 = dA1 * sigmoid_derivative(Z1) dW1 = np.dot(X.T, dZ1) / m db1 = np.sum(dZ1, axis=0, keepdims=True) / m # 更新参数 W1 -= learning_rate * dW1 b1 -= learning_rate * db1 W2 -= learning_rate * dW2 b2 -= learning_rate * db2 return W1, b1, W2, b2
7. 训练模型
接下来,我们训练神经网络:
# 超参数epochs = 1000learning_rate = 0.1# 存储损失值losses = []for epoch in range(epochs): Z1, A1, Z2, A2 = forward(X_train, W1, b1, W2, b2) loss = binary_cross_entropy(y_train, A2) losses.append(loss) if epoch % 100 == 0: print(f"Epoch {epoch}, Loss: {loss:.4f}") W1, b1, W2, b2 = backward(X_train, y_train, Z1, A1, Z2, A2, W1, b2, learning_rate)
8. 模型评估
最后,我们在测试集上评估模型表现:
# 在测试集上进行预测_, _, _, predictions = forward(X_test, W1, b1, W2, b2)predictions = (predictions > 0.5).astype(int)# 计算准确率accuracy = np.mean(predictions == y_test)print(f"测试集准确率: {accuracy * 100:.2f}%")# 绘制损失曲线plt.plot(losses)plt.xlabel('Epoch')plt.ylabel('Loss')plt.title('Training Loss Curve')plt.show()
9. 总结
在本文中,我们使用纯 Python 和 NumPy 实现了一个简单的两层前馈神经网络。我们详细介绍了神经网络的基本组成部分,包括前向传播、损失函数、反向传播和参数更新。此外,我们还实现了数据准备、模型训练和评估流程。
虽然这个神经网络比较简单,但它的结构可以扩展到更多层和更复杂的任务中。对于更高级的应用,建议使用成熟的深度学习框架(如 PyTorch 或 TensorFlow),它们提供了自动微分、GPU加速等强大功能。
附录:完整代码汇总
import numpy as npimport matplotlib.pyplot as pltfrom sklearn.datasets import make_blobsfrom sklearn.model_selection import train_test_split# 生成数据X, y = make_blobs(n_samples=300, centers=2, cluster_std=1.5, random_state=42)y = y.reshape((y.shape[0], 1))X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# 激活函数与损失函数def sigmoid(x): return 1 / (1 + np.exp(-x))def sigmoid_derivative(x): s = sigmoid(x) return s * (1 - s)def binary_cross_entropy(y_true, y_pred): epsilon = 1e-15 loss = -np.mean(y_true * np.log(y_pred + epsilon) + (1 - y_true) * np.log(1 - y_pred + epsilon)) return loss# 初始化参数input_size = 2hidden_size = 4output_size = 1W1 = np.random.randn(input_size, hidden_size)b1 = np.zeros((1, hidden_size))W2 = np.random.randn(hidden_size, output_size)b2 = np.zeros((1, output_size))# 前向传播def forward(X, W1, b1, W2, b2): Z1 = np.dot(X, W1) + b1 A1 = sigmoid(Z1) Z2 = np.dot(A1, W2) + b2 A2 = sigmoid(Z2) return Z1, A1, Z2, A2# 反向传播def backward(X, y, Z1, A1, Z2, A2, W1, W2, learning_rate=0.01): m = X.shape[0] dZ2 = A2 - y dW2 = np.dot(A1.T, dZ2) / m db2 = np.sum(dZ2, axis=0, keepdims=True) / m dA1 = np.dot(dZ2, W2.T) dZ1 = dA1 * sigmoid_derivative(Z1) dW1 = np.dot(X.T, dZ1) / m db1 = np.sum(dZ1, axis=0, keepdims=True) / m W1 -= learning_rate * dW1 b1 -= learning_rate * db1 W2 -= learning_rate * dW2 b2 -= learning_rate * db2 return W1, b1, W2, b2# 训练模型epochs = 1000learning_rate = 0.1losses = []for epoch in range(epochs): Z1, A1, Z2, A2 = forward(X_train, W1, b1, W2, b2) loss = binary_cross_entropy(y_train, A2) losses.append(loss) if epoch % 100 == 0: print(f"Epoch {epoch}, Loss: {loss:.4f}") W1, b1, W2, b2 = backward(X_train, y_train, Z1, A1, Z2, A2, W1, W2, learning_rate)# 测试模型_, _, _, predictions = forward(X_test, W1, b1, W2, b2)predictions = (predictions > 0.5).astype(int)accuracy = np.mean(predictions == y_test)print(f"测试集准确率: {accuracy * 100:.2f}%")# 绘制损失曲线plt.plot(losses)plt.xlabel('Epoch')plt.ylabel('Loss')plt.title('Training Loss Curve')plt.show()
如果你有任何关于神经网络或代码的问题,欢迎继续提问!