核心问题:经济学家为何要学习机器学习?
传统计量经济学与机器学习是解决问题的两种不同“文化”。
- 计量经济学: 核心在于因果推断 (Causal Inference)。
- 目标是理解世界“为何”如此运转。
- 关心的是参数估计的无偏性和一致性。
- 问题范例:最低工资上调 导致 失业率变化了多少?
- 机器学习: 核心在于预测 (Prediction)。
- 目标是预测世界“将会”发生什么。
- 关心的是模型在未知数据上的泛化能力。
- 问题范例:根据当前宏观指标,预测下季度的GDP增长率。
两种文化的视觉对比
两种方法论在模型选择和目标上存在根本差异。
为何预测对经济学家很重要?
在数据驱动的时代,预测能力本身就是一种强大的经济工具。
- 金融市场: 预测资产价格、波动性和信用风险。
- 宏观经济: 预测通货膨胀、GDP增长和失业率,为政策制定提供依据。
- 企业决策: 预测产品销量、客户流失率和供应链需求。
- 政策评估: 预测一项政策(如税收减免)可能带来的经济影响。
因果推断解释过去,精准预测洞见未来。两者结合,威力倍增。
本章目标:构建机器学习的完整思维框架
学完本章,你将能从“四大支柱”的角度,系统性地理解任何机器学习项目。
支柱 I: 定义问题 (Framing)
这是所有工作的起点。
在开始任何技术细节之前,必须清晰地定义业务问题,并将其转化为一个明确的机器学习任务。
- 你要预测什么?
- 一个连续的数值 (e.g., 明天的股价) \(\rightarrow\) 回归 (Regression)
- 一个离散的类别 (e.g., 客户是否会违约) \(\rightarrow\) 分类 (Classification)
- 你拥有什么数据?
- 数据是否带有我们想预测的“答案”(即标签
y
)?
- 是 \(\rightarrow\) 监督学习 (Supervised Learning)
- 否 \(\rightarrow\) 无监督学习 (Unsupervised Learning)
支柱 II: 定义模型 (Modeling)
模型,本质上是一个数学函数 \(f(x, \theta)\),它试图捕捉输入特征 \(x\) 和输出 \(y\) 之间的关系。
- \(x\): 输入的特征向量 (e.g., 房屋面积、地段)。
- \(\theta\): 模型的参数。这些是需要通过“学习”来确定的值 (e.g., 线性回归中的系数)。
- \(f\): 函数的形式。这是我们作为建模者需要选择的。
模型的选择范围极广:
- 简单模型: 线性回归、逻辑回归 (可解释性强)。
- 复杂模型: 随机森林、梯度提升树、神经网络 (预测能力强)。
支柱 III: 定义“好坏” (Evaluation)
如何客观地衡量一个模型的好坏?我们需要一个评估指标。
- 这个指标必须能反映业务目标。
- 它必须在模型从未见过的数据(测试集)上计算。
常见的评估指标:
- 回归任务:
- 均方误差 (Mean Squared Error, MSE)
- 决定系数 (R-squared, R²)
- 分类任务:
- 准确率 (Accuracy)
- 精确率 (Precision)、召回率 (Recall)、F1-Score
什么是机器学习?—— 从数据中学习函数
机器学习 (ML) 的本质是让计算机从数据中自动地、而不是通过显式编程,学习出一个函数,这个函数能对未知数据做出预测。
机器学习的数据表示:万物皆可为向量
在机器学习中,我们需要将现实世界中的事物转化为计算机可以理解的语言——数字。
- 数据集 (Dataset):
N
个样本的集合 \(X = \{x_1, x_2, \dots, x_N\}\)。
- 样本 (Sample): 每一个独立的数据点 (一栋房子、一个客户)。
- 特征 (Feature): 描述一个样本的各个维度 (面积、卧室数量)。
- 特征向量 (Feature Vector): 一个样本的所有特征组成的向量 \(x = (x_{\text{面积}}, x_{\text{卧室}}, \dots)^T\)。
- 标签 (Label): 我们希望预测的目标值
y
(房价)。
从现实世界到数学对象
这个转换过程是数据预处理的核心。
一个样本 = d 维空间中的一个点
一旦我们将样本表示为特征向量,每个样本就可以被看作是 d 维特征空间中的一个点。
这为我们用几何的视角理解机器学习算法提供了基础。
案例:用经济数据理解特征向量
假设我们想预测美国的个人消费支出 (PCE)。我们可以从FRED获取数据。
2023-01-01 |
19,800 |
102.9 |
18,000 |
2023-02-01 |
19,850 |
103.4 |
18,050 |
… |
… |
… |
… |
- 一个样本 是一行数据,代表一个月的情况。
- 特征向量 \(x_t = (\text{DPI}_t, \text{CONF}_t)^T\)。这是2维空间中的一个点。
- 标签 \(y_t = \text{PCE}_t\)。
类别 1: 监督学习 (Supervised Learning)
当数据带有明确的“答案”或“标签”时使用。
类别 2: 无监督学习 (Unsupervised Learning)
当数据没有“答案”,我们想发现其内在结构时使用。
类别 3: 强化学习 (Reinforcement Learning)
当我们需要通过与环境的“试错”来学习最优策略时使用。
聚焦监督学习:回归 vs. 分类
在经济和商业应用中,绝大多数任务都属于监督学习。它又可根据标签 y
的类型分为两大任务。
- 回归 (Regression):
- 目标: 预测一个连续的数值。
- 输出: \(y \in \mathbb{R}\)
- 例子: 预测GDP增长率、公司销售额。
- 分类 (Classification):
- 目标: 预测一个离散的类别。
- 输出: \(y \in \{C_1, C_2, \dots, C_K\}\)
- 例子: 判断客户是否会流失、交易是否为欺诈。
回归与分类的几何直观
实战背景:凯恩斯消费函数
在进入代码之前,让我们回顾一个经典的经济学理论:凯恩斯的消费函数。
\[ C = a + b Y_d \]
- \(C\): 总消费
- \(Y_d\): 可支配收入
- \(a\): 自发性消费 (即使没有收入也要进行的消费)
- \(b\): 边际消费倾向 (MPC, 每增加一单位收入,会用多少来消费)
这是一个典型的线性关系。我们可以用最简单的机器学习模型——线性回归——来从数据中估计这个关系。
实战:用 Python 进行一次简单的回归分析
任务: 使用美国个人可支配收入 (DPI) 来预测个人消费支出 (PCE)。
这是一个典型的回归问题。我们将使用 statsmodels
库。
import pandas as pd
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt
# --- 创建模拟数据 ---
# 假设真实关系为 PCE = 500 + 0.9 * DPI + noise
np.random.seed(42)
dpi_mock = np.linspace(10000, 20000, 150)
noise = np.random.normal(0, 300, 150)
pce_mock = 500 + 0.9 * dpi_mock + noise
df = pd.DataFrame({'DPI': dpi_mock, 'PCE': pce_mock})
# 定义自变量 (X) 和因变量 (y)
X = df['DPI']
y = df['PCE']
X = sm.add_constant(X) # 添加截距项
# 拟合 OLS 模型
model = sm.OLS(y, X).fit()
# 可视化
fig, ax = plt.subplots(figsize=(8, 5))
# 绘制散点图和拟合线
ax.scatter(df['DPI'], df['PCE'], alpha=0.6, label='模拟月度数据', color='gray')
ax.plot(df['DPI'], model.predict(X), color='crimson', linewidth=2.5, label='OLS 拟合线')
ax.set_title('凯恩斯消费函数:消费由收入决定', fontsize=16, fontweight='bold')
ax.set_xlabel('实际个人可支配收入 (十亿美元)', fontsize=12)
ax.set_ylabel('个人消费支出 (十亿美元)', fontsize=12)
# 设置图例
ax.legend(fontsize=11, loc='upper left')
# 设置刻度标签
ax.tick_params(axis='both', which='major', labelsize=11)
# 添加网格以提高可读性
ax.grid(True, alpha=0.3)
fig.tight_layout()
plt.show()
# 打印模型摘要 (部分)
print(f"R-squared: {model.rsquared:.4f}")
print(f"DPI 系数 (估计的MPC): {model.params['DPI']:.4f}")
R-squared: 0.9886
DPI 系数 (估计的MPC): 0.9040
支柱 III: 如何评估模型的好坏?
我们如何知道训练出的模型 \(f(x; \theta)\) 是一个“好”模型?
核心原则: 模型在从未见过的数据(测试集)上的表现才是衡量其泛化能力的唯一标准。
这引出了机器学习中最重要的实践:训练集-测试集划分 (Train-Test Split)。
黄金法则:训练集-测试集划分
我们必须把数据分成至少两部分。
为何必须划分?—— 过拟合的幽灵
过拟合 (Overfitting) 是指模型对训练数据学习得“太好”,以至于把数据中的噪声和偶然性也当作了普适规律。
- 表现: 在训练集上表现极好,但在测试集上表现很差。
- 原因: 模型过于复杂,相对于数据量来说自由度太高。
测试集就像一场“模拟考”,它能公平地检验出模型是否真的学到了知识,还是只会“背答案”。
分类任务的评估基石:混淆矩阵
对于二分类问题(例如,预测客户是否违约),所有评估指标都源于一个简单的表格:混淆矩阵 (Confusion Matrix)。
实际为正例 |
真正例 (TP) |
假反例 (FN) |
实际为负例 |
假正例 (FP) |
真反例 (TN) |
- 正例 (Positive): 我们关心的事件,如“违约”、“欺诈”。
- 反例 (Negative): 其他情况,如“不违约”。
- 真/假 (True/False): 指的是预测是否正确。
理解混淆矩阵的四个象限
- TP (True Positive): 预测正确,确实违约了。(命中)
- TN (True Negative): 预测正确,确实没违约。(正确拒绝)
- FP (False Positive): 预测错误,预测会违约,结果没有。(误报,Type I Error)
- FN (False Negative): 预测错误,预测不违约,结果违约了。(漏报,Type II Error)
在金融风控等领域,FN (漏掉一个坏客户) 的代价通常远高于 FP (误判一个好客户)。
分类指标(1): 准确率 (Accuracy)
准确率衡量的是模型预测正确的样本占总样本的比例。
\[ \large \text{Accuracy} = \frac{\text{预测正确的样本数}}{\text{总样本数}} = \frac{TP + TN}{TP + TN + FP + FN} \]
优点: 非常直观,容易理解。
缺点: 在类别不平衡的数据集上具有强烈的误导性。
准确率陷阱:一个例子
想象一个信用卡欺诈检测场景:
- 总交易笔数: 10,000
- 正常交易: 9,990 (99.9%)
- 欺诈交易: 10 (0.1%)
如果一个“懒惰”模型将所有交易都预测为“正常”,它的表现如何?
- TP = 0, TN = 9990
- FP = 0, FN = 10
- Accuracy = (0 + 9990) / 10000 = 99.9%
这个模型准确率极高,但毫无用处,因为它一个欺诈案例也找不到。
分类指标(2): 精确率 (Precision)
精确率衡量的是所有被预测为正例的样本中,真正是正例的比例。
\[ \large \text{Precision} = \frac{TP}{TP + FP} \]
- 业务含义: “在我发出的所有欺诈警报中,有多少是真的?”
- 关注点: 预测的纯度。高精确率意味着更少的误报 (FP)。
分类指标(3): 召回率 (Recall)
召回率衡量的是所有真正是正例的样本中,被我们成功找出来的比例。
\[ \large \text{Recall} = \frac{TP}{TP + FN} \]
- 业务含义: “在所有实际发生的欺诈案中,我的模型成功识别了多少?”
- 关注点: 找得有多全。高召回率意味着更少的漏报 (FN)。
精确率与召回率:一个永恒的权衡
在现实世界中,精确率和召回率通常是负相关的。
- 想提高召回率? 降低模型的“报警”门槛,宁可错杀一千,不放过一个。这会导致误报 (FP) 增加,从而降低精确率。
- 想提高精确率? 提高模型的“报警”门槛,只对证据确凿的案例报警。这会导致漏报 (FN) 增加,从而降低召回率。
业务决策: 我们需要根据FP和FN的不同业务成本,来决定在这个权衡中选择哪一个平衡点。
精确率-召回率权衡的可视化
分类指标(4): F1-Score
为了综合考量精确率和召回率,我们使用 F1-Score,它是两者的调和平均数。
\[ \large F_1 = 2 \cdot \frac{\text{Precision} \cdot \text{Recall}}{\text{Precision} + \text{Recall}} \]
- 只有当精确率和召回率都比较高时,F1-Score才会高。
- 如果其中一个指标很低,F1-Score也会被拉低。
- 它是在不平衡数据集上比准确率更鲁棒的单一评估指标。
实战:用scikit-learn
计算分类指标
让我们用一个虚拟的信用违约预测例子来演示如何计算这些指标。
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
import seaborn as sns
import matplotlib.pyplot as plt
# 设置matplotlib中文字体支持
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
import numpy as np
# --- 创建模拟数据 (类别不平衡) ---
# 假设有1000个客户,其中50个实际违约 (5%)
y_true = np.array([0]*950 + [1]*50)
# 假设模型预测出了40个违约,其中30个是正确的 (TP=30),10个是错误的 (FP=10)
# 那么,在50个实际违约中,模型漏掉了20个 (FN=20)
y_pred = np.array([0]*940 + [1]*10 + [0]*20 + [1]*30)
# 创建一个随机排列的索引
p = np.random.permutation(len(y_true))
y_true, y_pred = y_true[p], y_pred[p]
# 1. 计算各项指标
accuracy = accuracy_score(y_true, y_pred)
precision = precision_score(y_true, y_pred, zero_division=0)
recall = recall_score(y_true, y_pred, zero_division=0)
f1 = f1_score(y_true, y_pred, zero_division=0)
print(f"准确率 (Accuracy): {accuracy:.3f}")
print(f"精确率 (Precision): {precision:.3f}")
print(f"召回率 (Recall): {recall:.3f}")
print(f"F1-Score: {f1:.3f}")
# 2. 计算并可视化混淆矩阵
cm = confusion_matrix(y_true, y_pred)
print("\n混淆矩阵:\n", cm)
fig, ax = plt.subplots(figsize=(6, 4.5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=['预测不违约', '预测违约'],
yticklabels=['实际不违约', '实际违约'], ax=ax, annot_kws={"size": 14})
ax.set_ylabel('实际标签', fontsize=12)
ax.set_xlabel('预测标签', fontsize=12)
ax.set_title('混淆矩阵', fontsize=14)
plt.show()
准确率 (Accuracy): 0.970
精确率 (Precision): 0.750
召回率 (Recall): 0.600
F1-Score: 0.667
混淆矩阵:
[[940 10]
[ 20 30]]
回归任务的评估指标:均方误差 (MSE)
对于回归任务(预测连续值),最常用的评估指标是均方误差 (Mean Squared Error, MSE)。
\[ \large \text{MSE} = \frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)^2 \]
- \(y_i\) 是第 \(i\) 个样本的真实值。
- \(\hat{y}_i\) 是模型对第 \(i\) 个样本的预测值。
- \((y_i - \hat{y}_i)\) 是残差 (residual)。
MSE计算的是残差平方的平均值。因为它使用了平方,所以它对大的误差给予了更重的惩罚。
均方误差 (MSE) 的可视化
损失函数:优化过程中的“导航地图”
损失函数 \(J(\theta)\) 描绘了一个“地形图”,其中地面的高度就是损失值。我们的目标是从某个随机的出发点,走到这个地形的最低谷。
分类任务的损失函数:交叉熵 (Cross-Entropy)
对于分类问题,我们常用交叉熵损失 (Cross-Entropy Loss)。
- 直观理解: 衡量模型预测的概率分布与真实的概率分布之间的“距离”。
- 对于二分类: \[ \large L(\theta) = - \frac{1}{N} \sum_{i=1}^N \left[ y_i \log(\hat{y}_i) + (1-y_i) \log(1-\hat{y}_i) \right] \] 其中 \(y_i \in \{0, 1\}\) 是真实标签,\(\hat{y}_i \in\) 是模型预测为类别1的概率。
这个函数有一个很好的特性:当模型做出非常自信且错误的预测时,损失会变得非常大,从而给模型一个强烈的“惩罚”信号。
支柱 IV: 定义“学习” (Optimization)
我们有了地图(损失函数),但如何找到下山的路?
最经典、最重要的方法是梯度下降法 (Gradient Descent)。
核心思想: 想象你站在一个浓雾笼罩的山坡上,能见度只有你脚下的一小块地方。为了最快下山,你应该沿着当前位置最陡峭的方向迈出一步。
在数学上,函数在某一点梯度 (Gradient) 的反方向,就是函数值下降最快的方向。
梯度下降法的数学原理
梯度下降是一个迭代算法。在每一步 t
,都按照以下规则更新参数 \(\theta\):
\[ \large \theta_{t+1} = \theta_t - \eta \nabla J(\theta_t) \]
- \(\theta_t\): 参数在第
t
步的值。
- \(\nabla J(\theta_t)\): 损失函数 \(J\) 在 \(\theta_t\) 处的梯度。它是一个向量,指向函数值上升最快的方向。
- \(\eta\): 学习率 (Learning Rate),它是一个超参数,控制我们每一步“走多远”。
- \(-\eta \nabla J(\theta_t)\): 我们沿着梯度相反的方向走一小步。
我们不断重复这个过程,直到参数收敛。
梯度下降法的可视化
学习率(η):决定优化的效果和速度
- \(\eta\) 太小: 像蜗牛一样爬下山,收敛速度非常慢。
- \(\eta\) 太大: 像一个醉汉下山,可能会在最低点附近来回“振荡”,甚至“跳”到山的另一边,导致发散。
梯度下降的变种:处理大规模数据
当我们的训练集非常大时,计算整个数据集的梯度会非常耗时。为此,我们发展出了梯度下降的变种。
梯度下降变种的对比
批量梯度下降 (BGD) |
使用所有训练样本 |
梯度方向准确,收敛路径平滑 |
计算成本高,速度慢 |
随机梯度下降 (SGD) |
随机选取一个样本 |
速度快,能够跳出局部最优 |
梯度随机性大,收敛路径嘈杂 |
小批量梯度下降 (MBGD) |
随机选取一小批样本 (e.g., 32) |
结合BGD和SGD优点,是默认选择 |
需额外设置批量大小 (batch size) |
进阶优化器:让下山之路更智能
基础的梯度下降法有时会在复杂的损失地形中遇到困难。现代深度学习中,我们使用更先进的优化器。
- 动量法 (Momentum)
- 思想: 模拟物理学中的动量。梯度更新时不仅考虑当前梯度,还考虑上一步的更新方向,像一个滚下山的小球。
- 效果: 帮助算法冲出平坦区域和局部最小值,加速收敛。
- Adam (Adaptive Moment Estimation)
- 思想: 结合了动量法和自适应学习率(为每个参数独立调整学习率)。
- 效果: 在各种任务中都表现出色,通常是首选的默认优化器。
对于初学者: 直接使用 Adam 优化器通常能得到很好的结果。
综合案例:用线性回归预测房价
我们来把今天学到的所有知识串联起来,用scikit-learn
完成一个完整的机器学习项目。
- 任务: 预测加州地区的房价中位数(回归问题)。
- 数据: Scikit-learn 内置的加州房价数据集。
- 模型: 线性回归。
- 评估: 均方误差 (MSE)。
- 优化: Scikit-learn 的
LinearRegression
在底层使用了高效的优化算法。
综合案例(1): 加载和划分数据
第一步永远是准备数据:加载它,并将其分为训练集和测试集。
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.datasets import fetch_california_housing
# 1. 加载加州房价数据集
housing = fetch_california_housing()
X = pd.DataFrame(housing.data, columns=housing.feature_names)
y = pd.Series(housing.target, name='MedHouseVal')
# 2. 将数据划分为训练集和测试集 (80% 训练, 20% 测试)
# random_state 保证每次划分结果都一样,便于复现
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("测试集大小:", X_test.shape)
print("\n部分特征数据预览:")
print(X_train.head())
训练集大小: (16512, 8)
测试集大小: (4128, 8)
部分特征数据预览:
MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude \
14196 3.2596 33.0 5.017657 1.006421 2300.0 3.691814 32.71
8267 3.8125 49.0 4.473545 1.041005 1314.0 1.738095 33.77
17445 4.1563 4.0 5.645833 0.985119 915.0 2.723214 34.66
14265 1.9425 36.0 4.002817 1.033803 1418.0 3.994366 32.69
2271 3.5542 43.0 6.268421 1.134211 874.0 2.300000 36.78
Longitude
14196 -117.03
8267 -118.16
17445 -120.48
14265 -117.11
2271 -119.80
综合案例(2): 训练模型与做出预测
用训练集来“教”我们的模型,然后在测试集上检验它的学习成果。
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
import numpy as np
# (接上一个代码块的变量 X_train, y_train, X_test, y_test)
# 1. 初始化一个线性回归模型
model = LinearRegression()
# 2. 在训练集上训练模型 (即寻找最佳参数θ)
# .fit() 方法执行了学习/优化过程
model.fit(X_train, y_train)
# 3. 在测试集上做出预测
# .predict() 使用学习到的模型进行预测
y_pred = model.predict(X_test)
# 打印部分预测结果
print("真实房价 (前5个):", np.round(y_test.head().values, 2))
print("预测房价 (前5个):", np.round(y_pred[:5], 2))
真实房价 (前5个): [0.48 0.46 5. 2.19 2.78]
预测房价 (前5个): [0.72 1.76 2.71 2.84 2.6 ]
综合案例(3): 评估模型与解释结果
最后,我们计算评估指标,并查看模型学到了什么。
import pandas as pd
from sklearn.metrics import mean_squared_error
# (接上一个代码块的变量 y_test, y_pred, model, X)
# 4. 在测试集上评估模型性能
mse = mean_squared_error(y_test, y_pred)
print(f"\n模型在测试集上的均方误差 (MSE) 为: {mse:.4f}")
# 5. 查看学到的参数 (部分)
# model.coef_ 对应于回归方程中的斜率系数
coef_df = pd.DataFrame(model.coef_, index=X.columns, columns=['系数 (Coefficient)'])
print("\n学到的部分系数 (θ):")
print(coef_df.head())
# model.intercept_ 对应于截距项
print(f"\n学到的截距项: {model.intercept_:.4f}")
模型在测试集上的均方误差 (MSE) 为: 0.5559
学到的部分系数 (θ):
系数 (Coefficient)
MedInc 0.448675
HouseAge 0.009724
AveRooms -0.123323
AveBedrms 0.783145
Population -0.000002
学到的截距项: -37.0233
综合案例(4): 可视化预测结果
将模型的预测值与真实值进行比较,是检验模型效果最直观的方式。
如果预测是完美的,所有的点都应该落在红色的虚线上。
本章总结:机器学习的四大支柱
我们今天建立了一个完整的框架来思考机器学习问题。
1. 定义问题 (Framing)
- 任务: 回归 vs. 分类
- 学习类型: 监督 vs. 无监督
2. 定义模型 (Modeling)
- 选择一个函数 \(f(x, \theta)\)
- e.g., 线性回归
3. 定义“好坏” (Evaluation)
- 核心: 在测试集上评估
- 回归: MSE, R²
- 分类: F1-Score, Recall
4. 定义“学习” (Optimization)
- 最小化损失函数 \(J(\theta)\)
- 梯度下降是核心算法