Python实现简单神经网络:从零开始手写数字识别

📅 发布时间:2026/8/4 11:53:09
Python实现简单神经网络:从零开始手写数字识别
1. 项目概述根据Python基础语法完成一个简单的深度学习模拟这个项目非常适合刚掌握Python基础语法想要迈入深度学习领域的初学者。通过这个实践你不仅能巩固Python编程基础还能直观理解深度学习中最核心的概念——前向传播和反向传播。我在指导新人学习时发现很多初学者在刚接触深度学习时容易被复杂的数学公式吓退。实际上用不到100行Python代码就能实现一个完整的神经网络训练过程。这个项目将使用纯Python不依赖任何深度学习框架来实现一个能识别手写数字的简单神经网络。2. 核心概念解析2.1 神经网络基本原理神经网络本质上是一个由多个函数组成的复合函数。以识别手写数字为例输入是28×28784个像素值输出是0-9十个数字的概率分布。中间的隐藏层就是我们要通过训练来确定的参数。这里我们实现最简单的全连接网络输入层784个神经元对应28×28图像隐藏层16个神经元这个数量可以调整输出层10个神经元对应0-9十个数字2.2 关键数学概念Sigmoid激活函数将神经元的输出压缩到(0,1)区间def sigmoid(x): return 1 / (1 np.exp(-x))Softmax函数将输出层的值转换为概率分布def softmax(x): exps np.exp(x - np.max(x)) return exps / np.sum(exps)交叉熵损失函数衡量预测结果与真实标签的差距def cross_entropy(y_pred, y_true): return -np.sum(y_true * np.log(y_pred))3. 完整实现步骤3.1 数据准备我们使用MNIST数据集包含60000张手写数字图片。可以通过以下方式加载from tensorflow.keras.datasets import mnist (train_images, train_labels), (test_images, test_labels) mnist.load_data() # 数据预处理 train_images train_images.reshape((60000, 28*28)) / 255.0 test_images test_images.reshape((10000, 28*28)) / 255.0 # 将标签转为one-hot编码 train_labels np.eye(10)[train_labels] test_labels np.eye(10)[test_labels]3.2 网络初始化class NeuralNetwork: def __init__(self, input_size, hidden_size, output_size): # 初始化权重和偏置 self.W1 np.random.randn(input_size, hidden_size) * 0.01 self.b1 np.zeros(hidden_size) self.W2 np.random.randn(hidden_size, output_size) * 0.01 self.b2 np.zeros(output_size)3.3 前向传播实现def forward(self, X): # 第一层计算 self.z1 np.dot(X, self.W1) self.b1 self.a1 sigmoid(self.z1) # 输出层计算 self.z2 np.dot(self.a1, self.W2) self.b2 self.a2 softmax(self.z2) return self.a23.4 反向传播实现这是最核心的部分计算损失函数对各个参数的梯度def backward(self, X, y, output): # 输出层误差 delta2 output - y # 隐藏层误差 delta1 np.dot(delta2, self.W2.T) * (self.a1 * (1 - self.a1)) # 计算梯度 dW2 np.dot(self.a1.T, delta2) db2 np.sum(delta2, axis0) dW1 np.dot(X.T, delta1) db1 np.sum(delta1, axis0) return dW1, db1, dW2, db23.5 参数更新使用梯度下降法更新参数def update_params(self, dW1, db1, dW2, db2, lr0.01): self.W1 - lr * dW1 self.b1 - lr * db1 self.W2 - lr * dW2 self.b2 - lr * db24. 训练过程与评估4.1 训练循环实现def train(self, X, y, epochs10, lr0.01): for epoch in range(epochs): # 前向传播 output self.forward(X) # 计算损失 loss cross_entropy(output, y) # 反向传播 dW1, db1, dW2, db2 self.backward(X, y, output) # 更新参数 self.update_params(dW1, db1, dW2, db2, lr) if epoch % 10 0: print(fEpoch {epoch}, Loss: {loss:.4f})4.2 模型评估def evaluate(self, X_test, y_test): predictions self.forward(X_test) predicted_labels np.argmax(predictions, axis1) true_labels np.argmax(y_test, axis1) accuracy np.mean(predicted_labels true_labels) print(fTest Accuracy: {accuracy*100:.2f}%)5. 完整代码示例import numpy as np from tensorflow.keras.datasets import mnist # 激活函数和损失函数 def sigmoid(x): return 1 / (1 np.exp(-x)) def softmax(x): exps np.exp(x - np.max(x)) return exps / np.sum(exps, axis1, keepdimsTrue) def cross_entropy(y_pred, y_true): return -np.sum(y_true * np.log(y_pred 1e-8)) class NeuralNetwork: def __init__(self, input_size, hidden_size, output_size): self.W1 np.random.randn(input_size, hidden_size) * 0.01 self.b1 np.zeros(hidden_size) self.W2 np.random.randn(hidden_size, output_size) * 0.01 self.b2 np.zeros(output_size) def forward(self, X): self.z1 np.dot(X, self.W1) self.b1 self.a1 sigmoid(self.z1) self.z2 np.dot(self.a1, self.W2) self.b2 self.a2 softmax(self.z2) return self.a2 def backward(self, X, y, output): delta2 output - y delta1 np.dot(delta2, self.W2.T) * (self.a1 * (1 - self.a1)) dW2 np.dot(self.a1.T, delta2) db2 np.sum(delta2, axis0) dW1 np.dot(X.T, delta1) db1 np.sum(delta1, axis0) return dW1, db1, dW2, db2 def update_params(self, dW1, db1, dW2, db2, lr0.01): self.W1 - lr * dW1 self.b1 - lr * db1 self.W2 - lr * dW2 self.b2 - lr * db2 def train(self, X, y, epochs10, lr0.01): for epoch in range(epochs): output self.forward(X) loss cross_entropy(output, y) dW1, db1, dW2, db2 self.backward(X, y, output) self.update_params(dW1, db1, dW2, db2, lr) if epoch % 10 0: print(fEpoch {epoch}, Loss: {loss:.4f}) def evaluate(self, X_test, y_test): predictions self.forward(X_test) predicted_labels np.argmax(predictions, axis1) true_labels np.argmax(y_test, axis1) accuracy np.mean(predicted_labels true_labels) print(fTest Accuracy: {accuracy*100:.2f}%) # 加载数据 (train_images, train_labels), (test_images, test_labels) mnist.load_data() train_images train_images.reshape((60000, 28*28)) / 255.0 test_images test_images.reshape((10000, 28*28)) / 255.0 train_labels np.eye(10)[train_labels] test_labels np.eye(10)[test_labels] # 创建并训练模型 nn NeuralNetwork(784, 16, 10) nn.train(train_images[:1000], train_labels[:1000], epochs100, lr0.1) nn.evaluate(test_images[:1000], test_labels[:1000])6. 常见问题与优化建议6.1 梯度消失问题当网络层数增加时Sigmoid函数容易导致梯度消失。这是因为Sigmoid的导数最大值为0.25在反向传播时梯度会逐层衰减。解决方案使用ReLU激活函数替代Sigmoid使用更先进的初始化方法如He初始化6.2 学习率选择学习率太大可能导致震荡不收敛太小则训练缓慢。建议初始尝试0.01-0.1范围实现学习率衰减策略考虑使用自适应优化器如Adam6.3 过拟合处理当训练集准确率远高于测试集时说明模型过拟合。解决方法增加训练数据量添加L2正则化实现Dropout技术6.4 性能优化技巧向量化计算避免使用for循环充分利用NumPy的矩阵运算批量训练不要一次性加载所有数据实现mini-batch训练GPU加速对于更大规模的数据考虑使用CUDA加速7. 项目扩展方向这个基础项目可以进一步扩展增加隐藏层数量实现更深网络实现卷积神经网络(CNN)处理图像添加批归一化(BatchNorm)层实现自动求导功能构建更复杂的损失函数我在实际教学中发现通过这个基础实现学生能更深刻地理解PyTorch/TensorFlow等框架背后的工作原理。当你知道每一行代码在做什么时使用高级框架就会更加得心应手。