课程承诺:延续机器学习课的黄金标准:每节课 1 个核心概念 + 1 个核心思想 + 1 段可运行代码。从零开始,不跳步、不黑盒,学完能看懂所有大模型的底层逻辑。
本节课目标:彻底搞懂深度学习和传统机器学习的本质区别,不用任何深度学习框架,纯 NumPy 手写一个完整的两层神经网络,亲眼看到它从随机猜数到学会分类的全过程。
🧩 先衔接上一节课
我们已经学完了所有传统机器学习算法,它们有一个共同的致命弱点:需要人手工做特征工程。
- 垃圾邮件分类:需要人先定义哪些词是关键词
- 房价预测:需要人先提取面积、房间数、房龄这些特征
- 图像识别:传统算法几乎做不了,因为人根本无法手工定义 "猫" 的特征
而深度学习解决了这个问题:它能自动从原始数据中学习特征,不需要人手工设计。这就是为什么深度学习能在图像、语音、自然语言等领域取得革命性突破的原因。
🧠 第一个核心概念:神经网络(Neural Network)
深度学习的本质就是多层的神经网络。它的灵感来自于人脑的结构。
第一步:单个神经元(感知机)
人脑的神经细胞(神经元)工作原理很简单:接收多个输入信号,经过处理后输出一个信号。
人工神经元完全复刻了这个过程:
- 接收多个输入
x1, x2, ..., xn - 每个输入乘以一个权重
w1, w2, ..., wn(表示这个输入的重要性) - 加上一个偏置
b - 经过一个激活函数,得到输出
y
公式:y = σ(w1*x1 + w2*x2 + ... + wn*xn + b)
你看,这和我们之前学的逻辑回归一模一样!单个神经元其实就是一个逻辑回归。
第二步:多个神经元组成 "层"
把多个神经元并排放在一起,就组成了一个层。
- 输入层:接收原始数据
- 隐藏层:中间处理数据的层(有多少个隐藏层,就叫 "多少层神经网络")
- 输出层:输出最终结果
第三步:多个层组成 "深度神经网络"
把多个层堆叠在一起,就组成了深度神经网络。
- 输入层 → 隐藏层 1 → 隐藏层 2 → ... → 输出层
这就是所有深度学习模型的基本结构,从最简单的手写数字识别,到最复杂的 GPT-4,本质上都是这样的多层堆叠。
💡 第一个核心思想:自动分层特征提取
这是深度学习和传统机器学习最本质、最核心的区别,没有之一。
最经典的例子:图像识别
- 传统机器学习:人需要手工定义 "边缘"、"角"、"纹理" 这些特征,然后用分类器分类
- 深度学习:神经网络自动学习分层的特征
- 第一层:学习最基本的边缘和线条
- 第二层:把边缘组合成纹理
- 第三层:把纹理组合成形状(眼睛、鼻子、耳朵)
- 第四层:把形状组合成物体(猫、狗、人)
神奇的地方:这些特征不是人设计的,是神经网络自己从数据中学习到的!而且层数越深,学到的特征越抽象、越高级。
这就是为什么深度学习这么强大:它能自动发现数据中隐藏的复杂模式,而不需要人类的先验知识。
💻 第一个纯手写代码:两层神经网络实现二分类
我们不用 PyTorch、不用 TensorFlow,纯 NumPy 手写一个两层神经网络,解决之前学过的垃圾邮件分类问题。你将看到神经网络训练的完整过程,没有任何黑魔法。
完整代码(复制粘贴就能运行)
python
运行
import numpy as np # 1. 准备和第4课完全一样的垃圾邮件数据集 # 特征:[包含"免费", 包含"中奖", 包含"点击", 包含"领取", 包含"紧急"] X = np.array([ [0,0,0,0,0], [0,0,0,0,1], [0,0,0,1,0], [0,0,1,0,0], [0,1,0,0,0], [1,0,0,0,0], [1,1,0,0,0], [1,0,1,0,0], [1,0,0,1,0], [1,1,1,0,0], [1,1,0,1,0], [1,1,1,1,0], [1,1,1,1,1], [0,1,1,1,1], [0,0,1,1,1], [0,0,0,1,1], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0], [0,0,0,0,0] ]) # 标签:0=正常邮件,1=垃圾邮件 y = np.array([[0],[0],[0],[0],[0],[1],[1],[1],[1],[1],[1],[1],[1],[1],[0],[0],[0],[0],[0],[0]]) # 2. 定义Sigmoid激活函数(和逻辑回归一样) def sigmoid(z): return 1 / (1 + np.exp(-z)) # 3. 定义Sigmoid函数的导数(用于反向传播) def sigmoid_derivative(z): return z * (1 - z) # 4. 初始化神经网络参数(随机初始化) np.random.seed(42) # 输入层5个神经元,隐藏层8个神经元,输出层1个神经元 w1 = np.random.randn(5, 8) # 输入层→隐藏层的权重 b1 = np.random.randn(1, 8) # 隐藏层的偏置 w2 = np.random.randn(8, 1) # 隐藏层→输出层的权重 b2 = np.random.randn(1, 1) # 输出层的偏置 # 5. 设置超参数 learning_rate = 0.1 epochs = 10000 loss_history = [] # 6. 神经网络训练过程(核心!) for i in range(epochs): # -------------------------- 前向传播(预测) -------------------------- # 隐藏层输出 hidden_layer = sigmoid(np.dot(X, w1) + b1) # 输出层输出(最终预测) output_layer = sigmoid(np.dot(hidden_layer, w2) + b2) # -------------------------- 计算损失 -------------------------- loss = np.mean((y - output_layer) ** 2) loss_history.append(loss) # -------------------------- 反向传播(更新参数) -------------------------- # 输出层误差 output_error = y - output_layer output_delta = output_error * sigmoid_derivative(output_layer) # 隐藏层误差(误差从输出层往回传) hidden_error = np.dot(output_delta, w2.T) hidden_delta = hidden_error * sigmoid_derivative(hidden_layer) # 更新权重和偏置 w2 += np.dot(hidden_layer.T, output_delta) * learning_rate b2 += np.sum(output_delta, axis=0, keepdims=True) * learning_rate w1 += np.dot(X.T, hidden_delta) * learning_rate b1 += np.sum(hidden_delta, axis=0, keepdims=True) * learning_rate # 每1000次打印一次训练进度 if i % 1000 == 0: print(f"第{i}次训练,损失:{loss:.4f}") # 7. 训练完成,测试模型 print("\n训练完成!") print("\n测试结果:") test_emails = [ [1,1,0,0,0], # "免费中奖,快来参与!" [0,0,0,0,0], # "明天下午开会" [1,0,0,1,1], # "免费领取紧急通知" [0,1,0,0,0] # "恭喜你中奖了" ] for email in test_emails: hidden = sigmoid(np.dot(email, w1) + b1) pred = sigmoid(np.dot(hidden, w2) + b2)[0][0] result = "垃圾邮件" if pred > 0.5 else "正常邮件" print(f"是垃圾邮件的概率:{pred:.4f} → {result}") # 8. 画出损失下降曲线 import matplotlib.pyplot as plt plt.figure(figsize=(10, 6)) plt.plot(loss_history) plt.xlabel("训练次数") plt.ylabel("损失") plt.title("神经网络损失下降曲线") plt.show()🔍 逐行拆解核心训练过程
这就是所有神经网络训练的完整流程,和我们之前学的梯度下降一模一样!
plaintext
循环很多次: 1. 前向传播:用当前参数计算预测值 2. 计算损失:预测值和真实值的差距 3. 反向传播:把误差从输出层往回传,计算每个参数的梯度 4. 更新参数:用梯度下降更新权重和偏置关键细节解释
参数初始化:权重必须随机初始化!如果所有权重都初始化为 0,那么所有神经元的输出都一样,神经网络永远学不到任何东西。
前向传播:数据从输入层一层一层往后传,最终得到预测值。这就是我们用模型做预测的过程。
反向传播:这是神经网络最核心的部分。它的本质就是链式法则:先计算输出层的误差,然后一层一层往回传,计算每个参数对最终损失的贡献(梯度)。
- 你不需要记住复杂的数学推导,只要知道:误差从后往前传,谁贡献的误差大,就更新谁的参数多。
参数更新:和之前的梯度下降完全一样,朝着梯度的反方向走一小步,让损失变小。
✨ 神奇的实验:神经网络的容量
这是本节课最重要的实验,一定要做!
修改隐藏层的神经元数量,观察对模型效果的影响:
python
运行
# 实验1:隐藏层只有1个神经元 w1 = np.random.randn(5, 1) b1 = np.random.randn(1, 1) w2 = np.random.randn(1, 1) # 实验2:隐藏层有8个神经元(默认) w1 = np.random.randn(5, 8) b1 = np.random.randn(1, 8) w2 = np.random.randn(8, 1) # 实验3:隐藏层有32个神经元 w1 = np.random.randn(5, 32) b1 = np.random.randn(1, 32) w2 = np.random.randn(32, 1)你会发现:
- 隐藏层神经元太少:模型太简单,学不到复杂的规律,损失降不下来
- 隐藏层神经元太多:模型太复杂,容易过拟合,训练时间变长
- 合适的神经元数量:损失能降到最低,泛化能力最好
结论:神经网络的 "容量" 由隐藏层的数量和每个隐藏层的神经元数量决定。容量越大,能学习的规律越复杂,但也越容易过拟合。
📝 本节课总结
- 核心概念:深度学习就是多层的神经网络,由输入层、隐藏层和输出层组成
- 核心思想:深度学习能自动从数据中学习分层的特征,不需要人手工做特征工程
- 核心流程:所有神经网络的训练都是 "前向传播→算损失→反向传播→更新参数" 这个循环
- 你已经做到了:不用任何深度学习框架,纯手写了一个完整的神经网络,并且理解了它的每一步工作原理
最重要的一句话:从今天起,世界上再也没有 "黑魔法" 一样的深度学习了。不管是 ChatGPT 还是 Stable Diffusion,它们的训练过程和你刚才写的这几十行代码,本质上是完全一样的。
🎯 课后作业(必须做)
- 运行上面的代码,确保能看到损失下降曲线和正确的测试结果
- 尝试不同的隐藏层神经元数量和学习率,观察对训练过程的影响
- 把数据集换成之前的贷款审批数据集,用这个神经网络训练,看看和逻辑回归的效果对比
- 思考:如果我们再加一个隐藏层,变成三层神经网络,代码应该怎么改?