---
area: "tech-engineering"
category: technology
date: '2025-11-05'
draft: true
guest: ''
insight: ''
layout: post.njk
media_books:
- SQuAD
products_models:
- BERT
project: []
series: ''
source: https://www.youtube.com/watch?v=Taj1eHmZyWw
speaker: Hung-yi Lee
status: evergreen
summary: 本讲座以一个有趣的问题“老师什么时候下课？”为引子，系统性地介绍了机器学习与深度学习的基本原理。内容涵盖了机器学习的“3+1”核心步骤：定义目标（损失函数）、划定函数范围（模型选择）、寻找最佳函数（优化），以及关键的验证环节。讲座深入浅出地解释了梯度下降、神经网络、过拟合等核心概念，并探讨了模型训练过程中的常见挑战与解决策略，为理解现代人工智能的运作机制提供了清晰的框架。
tags:
- gradient-descent
- learning
- model
- neural-network
- overfitting
title: 一堂课搞懂机器学习与深度学习：从“老师何时下课”案例入门
---
### 机器学习的核心：如何从数据中找出函数？

今天这堂课，我们将用一堂课的时间搞懂机器学习和深度学习的基本概念。到目前为止，我们已经探讨了生成式人工智能的基本原理：我们有一个函数 F，它接收一个未完成的句子 X 作为输入，然后输出下一个 Token，即 F(X)。在过去的课程中，我们详细剖析了 F 的内部结构及其运作方式，但始终没有讲解 F 是如何被找出来的。

今天，我们就要揭示这个核心问题：这个能够预测下一个 Token 的函数 F，是如何通过数据被寻找出来的。通过数据找出一个函数的这项技术，就统称为**机器学习**（Machine Learning）。

今天的课程将分为两部分：首先讲解机器学习的原理，然后通过一些实作案例，帮助大家更深入地理解所学内容。

为了让大家明白机器学习这项技术的普适性，我们今天不以语言模型为例，而是选择一个完全不相关的例子。我们要寻找一个函数，它的输入 X 是一份李宏毅老师的课程投影片，输出 Y 则是老师讲完这份投影片所需的时间（一个数字）。在机器学习中，当输出是一个数值时，这类任务被称为**回归**（Regression）。

这个函数有什么用呢？它可以回答一个大家上课时最关心的问题：“老师什么时候要下课？”有了这个函数，你只需将当前的投影片输入，它就能告诉你这堂课大概会持续多久。

### 机器学习三步骤：定义目标、划定范围与寻找最佳解

机器学习寻找函数的过程可以概括为“3+1”个步骤。我们先讲前三个核心步骤，这三步合起来就是学习（Learning）或训练（Training）的过程。

1.  **我要找什么样的函数？**（定义目标）
2.  **我有哪些函数可以选择？**（划定范围）
3.  **如何找出最好的函数？**（执行优化）

这三个步骤中，第一步和第二步没有固定的先后顺序，但理解了这两步之后，才能进入第三步。

#### 步骤一：定义目标 (Defining the Goal)

第一步是确定评估一个函数好坏的标准。假设有人给了我们一个函数 F，我们需要有能力判断它是否符合我们的要求。这与我们上周讲的“生成式人工智能能力检定”非常相似。评估一个模型通常需要准备一些输入，让模型产生输出，然后将输出与标准答案进行比较，计算它们之间的某种距离或相似度。所有这些度量的平均值，我们称之为评估指标（Evaluation Metric）。

在机器学习的第一个步骤中，我们做的事情和评估几乎一样。这也是为什么我们先讲评估，再讲机器学习原理。

具体到我们的例子，我们有一些老师过去上课的投影片（例如“机器学习2021”课程）以及对应的实际讲课时长。我们将这些投影片输入到某个给定的函数 F，它会预测一个时长。然后，我们计算预测时长与真实时长的差距。例如，某节课实际讲了100分钟，F 预测了99分钟，差距的平方就是1。我们将所有课程的差距平方取平均值，这个数值就代表了函数 F 的好坏。

因为我们计算的是差距，所以这个数值越小，代表函数预测得越精准。当评价指标是越小越好时，我们称之为**损失**（Loss）或成本（Cost）；如果是越大越好，则称为目标（Objective）。在本课程中，我们主要使用 Loss 的概念。

我们这里计算预测时长与真实时长差距的平方的平均值，这个方法叫做**均方误差**（Mean Squared Error，简称 **MSE**）。这个 Loss 定义了一个函数的好坏。用于定义 Loss 的这些数据，包括输入的投影片和对应的真实上课时长，合起来就叫做**训练数据**（Training Data）。

#### 步骤二：划定选择范围 (Defining the Function Set)

第二步是确定我们有哪些候选函数可供选择。由于函数只能处理数字输入，我们不能直接把投影片丢进去。因此，需要先将投影片转换成一系列数字，这些数字被称为**特征**（Feature）。例如：
*   x1: 投影片的页数
*   x2: 投影片的总字数
*   x3: 投影片标题的长度
*   x4: 投影片中是否提到“learning”这个词（0或1）

接下来，我们要假设函数 F 的形式。一个直观的假设是，课程时长 y 与投影片页数 x1 存在线性关系：`y = w1 * x1 + b`。
*   `w1` 代表每页投影片平均讲解时间的比例。
*   `b` 可能代表每次上课固定的开场白时间。

`w1` 和 `b` 是我们不知道的具体数值，在函数中，这类未知数值被称为**参数**（parameter）。当我们写下 `y = w1 * x1 + b` 这个式子时，由于 `w1` 和 `b` 未知，它实际上代表了一个无限大的函数集合。这个集合中的每一个点（每一组特定的 `w1` 和 `b`）都对应一个具体的函数。

这个函数的范围是由人类根据对问题的理解，即**领域知识**（domain knowledge）来划定的。我们相信页数和课程时长成正相关，所以选择了这个线性模型。这个由人类定下的函数范围，也就是所谓的**模型**（model）。模型是对复杂现实世界的一种简化理解。

#### 步骤三：寻找最佳函数 (Finding the Best Function)

有了计算 Loss 的方法和候选函数的范围后，第三步就是在我们划定的范围内，找出一个能让 Loss 最低的函数。

我们的 Loss 函数可以写成：`L(w1, b) = (1/n) * Σ( (w1*xi + b) - yi_hat )^2`。其中，`n` 是课程总数，`xi` 是第 `i` 堂课的投影片页数，`yi_hat` 是第 `i` 堂课的真实时长。

这个 Loss 本身也是一个以 `w1` 和 `b` 为参数的函数。我们的目标是找到一组 `w1*` 和 `b*`，使得 `L(w1*, b*)` 的值最小。这个问题就转化成了一个**优化**（optimization）问题。

最直接但最笨的方法是暴力穷举。我们可以设定 `w1` 和 `b` 的一个可能范围，然后遍历所有组合，计算每个组合对应的 Loss，最后选出 Loss 最小的那一个。对于只有两个参数的简单问题，这是可行的。通过这种方法，我们可以绘制出**损失表面**（Loss Surface），这是一个三维图形，展示了不同 `w1` 和 `b` 组合对应的 Loss 值。

然而，在真实场景中，模型可能非常复杂，参数成千上万，暴力搜索不可行。因此，我们需要一个更通用的优化方法。

### 核心算法：梯度下降法 (Gradient Descent)

一个几乎适用于所有模型和 Loss 函数的通用优化方法叫做**梯度下降法**（Gradient Descent）。

为了简化说明，我们先假设只有一个参数 `w1`。梯度下降法的思想很简单，就像“饿狼下坡”：
1.  随机选择一个起始点 `w0`。
2.  在这个点，判断往左走还是往右走能让 Loss 下降。
3.  朝着 Loss 下降的方向迈出一步，到达新位置 `w1`。
4.  重复这个过程，直到走到一个“山谷”的凹点，无论往左还是往右，Loss 都不会再减小，此时就停下来。

这种方法的一个明显缺点是，它可能会停在一个**局部最小值**（local minimum），而不是我们真正想要的**全局最小值**（global minimum）。

在实际操作中，我们不是真的去试探左右两边，而是计算当前位置的**切线斜率**。
*   如果斜率是负的（左高右低），就向右走。
*   如果斜率是正的（左低右高），就向左走。
*   如果斜率是零，就停下来。

这个切线斜率，就是 Loss `L` 对参数 `w1` 的**偏微分**。幸运的是，现代的深度学习框架（如 PyTorch、TensorFlow）可以自动计算这些偏微分，我们无需手动推导。

每一步要迈多大，取决于两个因素：
1.  **学习率**（learning rate）：一个人为设定的超参数，代表学习的速度。学习率太大可能导致“跑飞”，永远无法收敛；太小则学习过程会非常缓慢。
2.  **切线斜率的大小**：山坡越陡，步伐就越大。

当有多个参数（如 `w1` 和 `b`）时，我们会计算 Loss 对每个参数的偏微分，这些偏微分集合起来形成一个向量，称为**梯度**（gradient）。然后，我们沿着梯度的反方向更新所有参数。这个“计算梯度 -> 更新参数”的过程会反复进行，每一次更新称为一次迭代（iteration）或更新（update）。

### 训练中的挑战与技巧

在实际训练中，我们会遇到一些挑战，并需要运用一些技巧来应对。

#### 批次 (Batch) 与 周期 (Epoch)

计算整个训练数据集的 Loss 和梯度可能非常耗时，特别是当数据量巨大时。为了解决这个问题，我们通常会将数据分成一个个的小份，称为**批次**（batch）。模型每处理完一个 batch 的数据，就计算一次梯度并更新一次参数，而不是等所有数据都处理完。

*   **周期 (Epoch)**：当模型看完了训练数据中的所有 batch 一次，就称为完成了一个 Epoch。
*   **随机梯度下降 (Stochastic Gradient Descent, SGD)**：当 batch size 设为1时，就是SGD。

使用 batch 的好处是更新速度快，但缺点是每次更新的方向可能不太稳定，因为只基于一小部分数据。因此，batch size 的选择是一个需要权衡的**超参数**（hyperparameter），即需要人为设定的参数。此外，在每个 Epoch 开始前，通常会对数据进行**洗牌**（shuffle），以增加随机性，避免模型反复看到同样组合的 batch。

### 关键的“+1”步骤：验证 (Validation)

当我们通过训练找到了一个看似不错的函数后，还不能直接拿去用。我们需要进行一个额外的步骤——**验证**（validation）。

验证好比是模拟考，而最终的应用或比赛则是真正的大考。模拟考可以多次进行，考得不好可以回去复习调整；大考则通常只有一次机会。

我们需要准备一份独立的验证数据集（validation set），这份数据没有参与训练过程。当我们用训练好的模型在验证集上测试时，如果结果很差，就需要回头检查前面的三个步骤哪里出了问题。

例如，我们用“机器学习2021”的数据训练出的模型，在预测“生成式AI导论2025”这门课的时长时表现很差。通过分析发现，两门课的性质不同（一门是专业课，一门是导论课），导致同样页数的投影片对应的讲课时长有系统性差异。这说明我们第一步就错了——训练数据与实际应用场景不匹配。解决方法是更换训练数据，使用性质更接近的“生成式AI导论2024”的数据重新训练。

### 从线性模型到深度学习：构建更强大的函数集

如果更换数据后，验证结果依然不理想，可能是第二步——我们划定的函数范围太小了。线性模型 `y = w1*x1 + b` 只能表示一条直线，但真实世界中变量间的关系可能要复杂得多。

为了构建一个能逼近任意复杂函数的范围，我们可以引入**深度学习**（Deep Learning）的思想。
1.  任何复杂的曲线都可以用一系列短的直线段（Piecewise Linear Curve）来逼近。
2.  任何由直线段组成的曲线，又可以由一个常数项和多个“山坡形状”的函数叠加而成。
3.  这种“山坡形状”的函数，又可以由两个更基础的、只有一个转折点的函数组合而成。
4.  这个基础的函数，其数学形式为 `max(0, wx+b)`，它有一个专门的名字，叫做**修正线性单元**（Rectified Linear Unit，简称 **ReLU**）。

一个接收输入、乘以权重、加上偏置、再通过 ReLU 函数的计算单元，被称为一个**神经元**（Neuron）。多个神经元并排组成一个**层**（Layer）。将多个层堆叠起来，就构成了**神经网络**（Neural Network）。当网络中包含很多隐藏层（Hidden Layer）时，就是所谓的**深度学习**。

理论上，只要有足够多的神经元，神经网络可以模拟任何函数。在神经网络中计算梯度的有效算法被称为**反向传播**（Backpropagation）。

### 深入优化与过拟合的挑战

当我们使用更强大的神经网络模型时，又会遇到新的问题。

首先是优化困难。神经网络的损失表面非常复杂，除了局部最小值，还可能卡在**鞍点**（Saddle Point，即梯度为零但并非最小值的点）或者梯度极其平缓的区域，导致训练停滞。这需要我们通过调整超参数（如学习率、batch size）或使用更高级的优化器（如 Adam）来解决。

其次，一个更严重的问题是**过拟合**（Overfitting）。当我们用一个非常强大的模型（函数范围非常大）去拟合训练数据时，模型可能会“死记硬背”训练数据中的每一个细节，而不是学习到底层的规律。这会导致模型在训练数据上表现极好（Loss很低），但在未见过的验证数据上表现极差（Loss很高）。

这就像在驾训班学车，你不是学习看路况开车，而是记住了“看到后照镜里某个贴纸时方向盘左打四分之一”这样的口诀。在同一个场地（训练集）考试你次次满分，但换一个场地（验证集）就完全不会开了。

解决过拟合的一个常用方法是**早停**（Early Stopping）。在训练过程中，我们不仅监控训练集上的 Loss，也同时监控验证集上的 Loss。当训练 Loss 持续下降，但验证 Loss 开始上升时，我们就及时停止训练，并选用验证 Loss 最低时的那个模型。

### 验证与测试的陷阱：避免对测试集过拟合

在模型开发过程中，我们会反复使用验证集来调整模型和超参数。但这样做也存在风险：我们可能会在不经意间对验证集本身产生过拟合。

最终，我们需要一个从未在开发过程中使用过的**测试集**（Test Set）来对模型进行最终的、一次性的评估。然而，在学术界和工业界的许多基准测试（Benchmark）中，由于测试集可以被反复提交测试，长期下来，研究社区整体也可能对测试集产生过拟合。这就是为什么有时AI在某些榜单上的分数超过人类，但在实际应用中却表现平平。

为了缓解这个问题，许多机器学习竞赛会将测试集分为**公开集**（Public Set）和**私有集**（Private Set）。参赛者可以看到在公开集上的排名，但最终成绩由在私有集上仅有一次的测试结果决定。

### 实作环节：在代码中重现学习过程

在课程的后半部分，我们通过一个代码实例，完整地重现了上述学习过程：
1.  **数据准备**：收集了不同课程的投影片页数、字数和实际讲课时长。
2.  **线性模型训练**：实现了暴力穷举和梯度下降法来寻找最佳的 `w` 和 `b`。
3.  **验证与迭代**：展示了更换训练数据对验证结果的改善。
4.  **特征工程**：通过添加“平均每页字数”这一新特征，提升了模型性能。
5.  **神经网络训练**：构建并训练了一个简单的神经网络，并展示了优化过程的困难以及过拟合现象。
6.  **最终测试**：运用早停策略，选出了在验证集上表现最好的模型，并用它来预测当天课程的真实时长。

最终，模型预测的时长与实际时长惊人地一致，为这堂关于预测的课程画上了一个圆满的句号。