【零基础学AI】第23讲:神经网络原理 - 前向传播与反向传播

在这里插入图片描述

本节课你将学到

  • 理解神经网络的基本工作原理
  • 掌握前向传播的计算过程
  • 理解反向传播的数学原理
  • 实现一个简单的手写数字识别网络

开始之前

环境要求

  • Python 3.8+
  • 需要安装的包:
    • numpy==1.21.0
    • matplotlib==3.4.0
    • tensorflow==2.8.0

前置知识

  • 基本Python编程能力(第1-8讲)
  • 矩阵运算基础(第4讲NumPy)
  • 机器学习基础概念(第9讲)

核心概念

什么是神经网络?

想象你是一个刚出生的婴儿,学习认识苹果的过程:

  1. 第一次看到苹果:妈妈告诉你这是"苹果"(输入数据+标签)
  2. 多次观察:你注意到苹果是圆的、红色的(提取特征)
  3. 犯错与纠正:把西红柿当成苹果时被纠正(误差反馈)
  4. 最终掌握:能准确识别各种苹果(模型收敛)

神经网络就是这样通过大量"观察"和"纠错"来学习的智能系统。

神经网络三要素

  1. 神经元(Neuron):计算的基本单元

    • 像生物神经元一样接收输入,产生输出
    • 数学表示:输出 = 激活函数(权重·输入 + 偏置)
  2. 层(Layer):神经元的集合

    • 输入层:接收原始数据
    • 隐藏层:进行特征变换(通常有多层)
    • 输出层:产生最终预测
  3. 连接权重(Weights):决定信息传递强度

    • 训练过程就是不断调整这些权重

前向传播(Forward Propagation)

就像工厂的生产流水线:

  1. 原材料(输入数据)进入第一道工序(输入层)
  2. 经过多道加工(隐藏层变换)
  3. 最终得到成品(输出预测)

数学过程:

层1输出 = 激活函数(权重1·输入 + 偏置1)
层2输出 = 激活函数(权重2·层1输出 + 偏置2)
...
最终输出 = 激活函数(权重N·层N-1输出 + 偏置N)

反向传播(Backpropagation)

当产品不合格时:

  1. 检查最终成品与标准的差距(计算损失函数)
  2. 逆向追溯每道工序的责任(计算各层梯度)
  3. 调整每道工序的参数(更新权重和偏置)

关键点:

  • 使用链式法则计算梯度
  • 从输出层向输入层逐层传播误差
  • 梯度下降法更新参数

激活函数的作用

为什么需要激活函数?没有它会发生什么?

  • 不加激活函数:多层网络等价于单层线性变换
  • 常用激活函数
    • Sigmoid:将输出压缩到(0,1)
    • ReLU:简单高效,解决梯度消失
    • Softmax:多分类输出概率分布

代码实战

1. 准备数据 - MNIST手写数字数据集

import tensorflow as tf
import matplotlib.pyplot as plt

# 加载数据
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 查看数据形状
print("训练集形状:", x_train.shape)  # (60000, 28, 28)
print("标签形状:", y_train.shape)    # (60000,)

# 数据预处理
x_train = x_train / 255.0  # 归一化到[0,1]
x_test = x_test / 255.0

# 可视化一个样本
plt.figure(figsize=(5,5))
plt.imshow(x_train[0], cmap='gray')
plt.title(f"标签: {y_train[0]}")
plt.axis('off')
plt.show()

2. 构建神经网络模型

model = tf.keras.Sequential([
    # 将28x28图像展平为784维向量
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    
    # 全连接层(128个神经元,ReLU激活)
    tf.keras.layers.Dense(128, activation='relu'),
    
    # Dropout层(防止过拟合)
    tf.keras.layers.Dropout(0.2),
    
    # 输出层(10个神经元对应0-9,Softmax激活)
    tf.keras.layers.Dense(10, activation='softmax')
])

# 编译模型
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 查看模型结构
model.summary()

3. 训练模型(前向+反向传播)

# 训练模型(实际发生前向传播和反向传播)
history = model.fit(x_train, y_train, 
                    epochs=5,  # 训练轮数
                    validation_data=(x_test, y_test))

# 绘制训练曲线
plt.plot(history.history['accuracy'], label='训练准确率')
plt.plot(history.history['val_accuracy'], label='验证准确率')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

4. 手动实现前向传播(理解原理)

import numpy as np

# 模拟一个极简神经网络
def simple_network(inputs):
    # 第一层权重(3个神经元)
    weights1 = np.array([[0.2, 0.8, -0.5],
                        [0.1, -0.3, 0.9]])
    bias1 = np.array([0.1, 0.2, 0.3])
    
    # 第二层权重(1个神经元)
    weights2 = np.array([[0.5], [-1.2], [0.7]])
    bias2 = np.array([0.4])
    
    # 前向传播
    layer1 = np.maximum(0, np.dot(inputs, weights1) + bias1)  # ReLU激活
    output = 1 / (1 + np.exp(-(np.dot(layer1, weights2) + bias2)))  # Sigmoid激活
    
    return output

# 测试输入
test_input = np.array([0.5, 0.8])
print("网络输出:", simple_network(test_input))

5. 手动实现反向传播(理解原理)

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))

# 模拟训练过程
def train_one_step(inputs, target, weights, learning_rate=0.1):
    # 前向传播
    net = np.dot(inputs, weights)
    output = sigmoid(net)
    
    # 计算误差
    error = target - output
    
    # 反向传播(链式法则)
    delta = error * sigmoid_derivative(net)
    
    # 更新权重
    weights += learning_rate * np.dot(inputs.reshape(-1,1), delta.reshape(1,-1))
    
    return weights, output

# 初始化权重
weights = np.array([0.5, -0.3])

# 训练数据
inputs = np.array([0.8, 0.2])
target = 1.0

# 训练过程可视化
print("初始权重:", weights)
for i in range(5):
    weights, output = train_one_step(inputs, target, weights)
    print(f"第{i+1}次训练 - 输出:{output:.4f}, 权重:{weights}")

完整项目

项目结构

lesson_23_neural_networks/
├── README.md
├── requirements.txt
├── neural_network.py       # 主程序文件
├── manual_implementation.py # 手动实现代码
├── data/                   # 存放数据
└── output/                 # 输出结果
    ├── training_curve.png
    └── sample_prediction.png

requirements.txt

numpy==1.21.0
matplotlib==3.4.0
tensorflow==2.8.0

neural_network.py

import tensorflow as tf
import matplotlib.pyplot as plt

# 加载数据
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

# 构建模型
model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(128, activation='relu'),
    tf.keras.layers.Dropout(0.2),
    tf.keras.layers.Dense(10, activation='softmax')
])

# 编译模型
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 训练模型
history = model.fit(x_train, y_train, 
                    epochs=5, 
                    validation_data=(x_test, y_test))

# 保存模型
model.save('output/mnist_model.h5')

# 评估模型
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print(f"\n测试准确率: {test_acc*100:.2f}%")

# 可视化训练过程
plt.plot(history.history['accuracy'], label='训练准确率')
plt.plot(history.history['val_accuracy'], label='验证准确率')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.savefig('output/training_curve.png')
plt.show()

# 预测示例
predictions = model.predict(x_test)
plt.figure(figsize=(10,5))
for i in range(5):
    plt.subplot(1,5,i+1)
    plt.imshow(x_test[i], cmap='gray')
    pred_label = tf.argmax(predictions[i]).numpy()
    plt.title(f"预测:{pred_label}")
    plt.axis('off')
plt.savefig('output/sample_prediction.png')
plt.show()

运行效果

控制台输出

Epoch 1/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.2960 - accuracy: 0.9150 - val_loss: 0.1421 - val_accuracy: 0.9573
Epoch 2/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.1437 - accuracy: 0.9573 - val_loss: 0.0989 - val_accuracy: 0.9699
Epoch 3/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.1076 - accuracy: 0.9674 - val_loss: 0.0850 - val_accuracy: 0.9735
Epoch 4/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0876 - accuracy: 0.9733 - val_loss: 0.0773 - val_accuracy: 0.9758
Epoch 5/5
1875/1875 [==============================] - 3s 2ms/step - loss: 0.0750 - accuracy: 0.9768 - val_loss: 0.0721 - val_accuracy: 0.9776

测试准确率: 97.76%

生成的文件

  • output/mnist_model.h5: 训练好的模型
  • output/training_curve.png: 训练过程准确率曲线
  • output/sample_prediction.png: 测试集预测示例

预期结果说明

  1. 训练准确率应达到97%以上:表示模型学习效果良好
  2. 验证准确率接近训练准确率:说明没有过拟合
  3. 预测示例应正确识别大部分数字:直观展示模型能力

常见问题

Q1: 为什么我的准确率比示例低?

可能原因:

  • 训练轮数不足(尝试增加epochs)
  • 学习率不合适(尝试调整optimizer参数)
  • 数据预处理不一致(确保进行了归一化)

Q2: 如何提高模型性能?

建议方法:

  • 增加网络深度(添加更多隐藏层)
  • 使用卷积神经网络(更适合图像数据)
  • 数据增强(旋转/平移图像增加数据多样性)

Q3: 反向传播的数学原理太难理解怎么办?

学习建议:

  1. 先掌握如何使用框架实现
  2. 再逐步理解梯度下降概念
  3. 最后研究链式法则细节

Q4: 出现显存不足错误

解决方法:

  • 减小batch_size参数
  • 使用Google Colab的GPU环境(参考第8讲)
  • 简化模型结构

课后练习

基础练习

  • 修改神经网络结构(如增加/减少隐藏层神经元数量),观察性能变化
  • 尝试不同的激活函数(如sigmoid、tanh),比较效果
  • 调整学习率参数,观察训练过程变化

进阶挑战

  • 不使用框架,完全手动实现一个3层神经网络
  • 在Fashion MNIST数据集上测试模型
  • 添加L2正则化防止过拟合

项目扩展

  • 开发一个网页应用,上传图片实时识别手写数字
  • 将模型部署为API服务(参考第35讲)
  • 尝试识别自己手写的数字(需处理图像尺寸和颜色)

技术总结

通过本讲我们掌握了:

  1. 神经网络的基本结构和原理
  2. 前向传播如何计算预测结果
  3. 反向传播如何更新网络参数
  4. 使用TensorFlow快速构建和训练模型
  5. 手写数字识别的完整实现

神经网络是深度学习的基石,理解这些原理将为你学习CNN、RNN等复杂模型打下坚实基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值