第9章:机器学习(Machine Learning)
一、导读
1.1 本章学习目标
第9章介绍机器学习——让AI系统从数据中自动学习改进的技术。与前面章节中手工设计算法不同,机器学习让系统自己从数据中发现模式和规律。这是现代AI最活跃的领域,也是深度学习、大语言模型等技术的基础。
本章的核心学习目标包括:
- 理解机器学习的基本概念:监督学习、无监督学习、强化学习的区别和适用场景。
- 掌握监督学习的基本方法:线性回归、逻辑回归、决策树、K近邻。
- 理解模型评估:训练集/测试集、交叉验证、过拟合与欠拟合。
- 掌握神经网络基础:感知机、多层网络、反向传播算法。
- 了解深度学习:CNN、RNN、Transformer 的基本概念。
- 了解强化学习:通过与环境交互学习最优策略。
1.2 为什么第九章如此重要
机器学习是现代AI的核心驱动力。从图像识别到语音识别,从自然语言处理到推荐系统,机器学习技术无处不在。特别是深度学习,它在过去十年中彻底改变了AI的面貌。
理解机器学习的基本原理,不仅是学习AI的必经之路,也是理解当前AI技术(如ChatGPT、自动驾驶)的基础。本章将从最基本的概念开始,逐步深入到神经网络和深度学习。
1.3 核心问题引导
在阅读本章时,请带着以下问题思考:
为什么需要机器学习? 手工设计算法有什么局限性?机器学习如何解决这些问题?
过拟合的本质:为什么模型在训练数据上表现好但在测试数据上表现差?如何避免?
神经网络的威力:为什么多层神经网络比浅层网络更强大?深度学习的"深度"指什么?
反向传播的原理:梯度如何从输出层传播到输入层?链式法则在其中扮演什么角色?
强化学习与监督学习的区别:为什么有些问题需要强化学习而不是监督学习?
二、核心概念详解
2.1 机器学习基础
2.1.1 学习的定义
学习是通过经验(数据)改进系统性能的过程。Tom Mitchell 给出了一个经典的定义:
如果一个计算机程序在任务 T 上的性能 P 随着经验 E 而改进,那么我们说这个程序在学习。
class Learner:
def __init__(self):
self.model = None
self.experience = []
def learn(self, data):
self.experience.extend(data)
self.model = self.fit(self.experience)
def fit(self, data):
raise NotImplementedError
def predict(self, x):
return self.model(x)
# 简单的记忆型学习者
class MemorizingLearner(Learner):
def fit(self, data):
memory = {tuple(x): y for x, y in data}
return lambda x: memory.get(tuple(x), None)
data = [([1, 2], 3), ([2, 3], 5), ([3, 4], 7)]
learner = MemorizingLearner()
learner.learn(data)
print(f"预测 [1,2]: {learner.predict([1, 2])}")2.1.2 监督学习
监督学习从标注数据中学习输入到输出的映射关系。
import numpy as np
class SupervisedLearner:
def __init__(self):
self.X_train = None
self.y_train = None
def fit(self, X, y):
self.X_train = np.array(X)
self.y_train = np.array(y)
def predict(self, X):
raise NotImplementedError
def score(self, X, y):
predictions = self.predict(X)
return np.mean(predictions == np.array(y))
# 线性回归
class LinearRegression(SupervisedLearner):
def fit(self, X, y):
super().fit(X, y)
X = np.array(X)
y = np.array(y)
X_b = np.c_[np.ones(len(X)), X]
self.weights = np.linalg.lstsq(X_b, y, rcond=None)[0]
def predict(self, X):
X = np.array(X)
X_b = np.c_[np.ones(len(X)), X]
return X_b @ self.weights
# 测试
X = np.array([[1], [2], [3], [4], [5]])
y = np.array([2, 4, 6, 8, 10])
lr = LinearRegression()
lr.fit(X, y)
print(f"预测 X=6: {lr.predict([[6]])[0]:.2f}")2.1.3 无监督学习
无监督学习从无标注数据中发现结构。
class KMeans:
def __init__(self, k=3, max_iter=100):
self.k = k
self.max_iter = max_iter
self.centroids = None
def fit(self, X):
X = np.array(X)
n = len(X)
indices = np.random.choice(n, self.k, replace=False)
self.centroids = X[indices].copy()
for _ in range(self.max_iter):
labels = self._assign_clusters(X)
new_centroids = np.array([
X[labels == i].mean(axis=0) if np.sum(labels == i) > 0
else self.centroids[i]
for i in range(self.k)
])
if np.allclose(self.centroids, new_centroids):
break
self.centroids = new_centroids
def _assign_clusters(self, X):
distances = np.array([
np.linalg.norm(X - c, axis=1) for c in self.centroids
])
return np.argmin(distances, axis=0)
def predict(self, X):
return self._assign_clusters(np.array(X))
# 测试 K-Means
np.random.seed(42)
X = np.vstack([
np.random.randn(50, 2) + [0, 0],
np.random.randn(50, 2) + [5, 5],
np.random.randn(50, 2) + [10, 0],
])
kmeans = KMeans(k=3)
kmeans.fit(X)
print(f"聚类中心:\n{kmeans.centroids}")2.1.4 强化学习
强化学习通过与环境交互学习最优策略。
class QLearning:
def __init__(self, states, actions, lr=0.1, gamma=0.9, epsilon=0.1):
self.q_table = {s: {a: 0.0 for a in actions} for s in states}
self.lr = lr
self.gamma = gamma
self.epsilon = epsilon
self.actions = actions
def choose_action(self, state):
if np.random.random() < self.epsilon:
return np.random.choice(self.actions)
return max(self.q_table[state], key=self.q_table[state].get)
def update(self, state, action, reward, next_state):
old_q = self.q_table[state][action]
max_next_q = max(self.q_table[next_state].values())
new_q = old_q + self.lr * (reward + self.gamma * max_next_q - old_q)
self.q_table[state][action] = new_q
# 简单的网格世界
class GridWorld:
def __init__(self, size=5):
self.size = size
self.pos = (0, 0)
self.goal = (size-1, size-1)
def step(self, action):
x, y = self.pos
if action == 'up' and y > 0: y -= 1
elif action == 'down' and y < self.size-1: y += 1
elif action == 'left' and x > 0: x -= 1
elif action == 'right' and x < self.size-1: x += 1
self.pos = (x, y)
reward = 10 if self.pos == self.goal else -1
return self.pos, reward, self.pos == self.goal
def reset(self):
self.pos = (0, 0)
return self.pos
env = GridWorld()
states = [(x, y) for x in range(5) for y in range(5)]
actions = ['up', 'down', 'left', 'right']
agent = QLearning(states, actions)
for episode in range(1000):
state = env.reset()
done = False
while not done:
action = agent.choose_action(state)
next_state, reward, done = env.step(action)
agent.update(state, action, reward, next_state)
state = next_state
print(f"Q-table 示例 (0,0): {agent.q_table[(0,0)]}")2.2 模型评估
2.2.1 训练集与测试集
def train_test_split(X, y, test_ratio=0.2):
n = len(X)
indices = np.random.permutation(n)
test_size = int(n * test_ratio)
test_idx = indices[:test_size]
train_idx = indices[test_size:]
return X[train_idx], X[test_idx], y[train_idx], y[test_idx]
# 交叉验证
def cross_validate(model_class, X, y, k=5):
n = len(X)
fold_size = n // k
scores = []
for i in range(k):
test_start = i * fold_size
test_end = test_start + fold_size
X_test = X[test_start:test_end]
y_test = y[test_start:test_end]
X_train = np.vstack([X[:test_start], X[test_end:]])
y_train = np.concatenate([y[:test_start], y[test_end:]])
model = model_class()
model.fit(X_train, y_train)
scores.append(model.score(X_test, y_test))
return np.mean(scores), np.std(scores)2.2.2 过拟合与欠拟合
class PolynomialRegression:
def __init__(self, degree=1):
self.degree = degree
self.coeffs = None
def fit(self, X, y):
X_poly = self._poly_features(X)
self.coeffs = np.linalg.lstsq(X_poly, y, rcond=None)[0]
def predict(self, X):
X_poly = self._poly_features(X)
return X_poly @ self.coeffs
def _poly_features(self, X):
X = np.array(X).flatten()
return np.column_stack([X**i for i in range(self.degree + 1)])
# 演示过拟合
np.random.seed(42)
X = np.linspace(0, 1, 20)
y = np.sin(2 * np.pi * X) + np.random.randn(20) * 0.3
for degree in [1, 3, 10]:
model = PolynomialRegression(degree)
model.fit(X, y)
train_pred = model.predict(X)
train_mse = np.mean((train_pred - y) ** 2)
print(f"Degree {degree}: Train MSE = {train_mse:.4f}")2.3 神经网络
2.3.1 感知机
class Perceptron:
def __init__(self, lr=0.01, n_iter=100):
self.lr = lr
self.n_iter = n_iter
self.weights = None
self.bias = None
def fit(self, X, y):
X = np.array(X)
y = np.array(y)
self.weights = np.zeros(X.shape[1])
self.bias = 0.0
for _ in range(self.n_iter):
for xi, yi in zip(X, y):
prediction = self.predict(xi)
update = self.lr * (yi - prediction)
self.weights += update * xi
self.bias += update
def predict(self, X):
linear = np.dot(X, self.weights) + self.bias
return np.where(linear >= 0, 1, 0)
# AND 门
X_and = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_and = np.array([0, 0, 0, 1])
perceptron = Perceptron()
perceptron.fit(X_and, y_and)
print(f"AND 门预测: {perceptron.predict(X_and)}")2.3.2 多层神经网络
class NeuralNetwork:
def __init__(self, layers):
self.layers = layers
self.weights = []
self.biases = []
for i in range(len(layers) - 1):
w = np.random.randn(layers[i], layers[i+1]) * 0.01
b = np.zeros((1, layers[i+1]))
self.weights.append(w)
self.biases.append(b)
def sigmoid(self, z):
return 1 / (1 + np.exp(-np.clip(z, -500, 500)))
def sigmoid_derivative(self, z):
s = self.sigmoid(z)
return s * (1 - s)
def forward(self, X):
self.activations = [X]
self.z_values = []
current = X
for i in range(len(self.weights)):
z = current @ self.weights[i] + self.biases[i]
self.z_values.append(z)
current = self.sigmoid(z)
self.activations.append(current)
return current
def backward(self, y, lr=0.01):
m = len(y)
y = y.reshape(-1, 1) if len(y.shape) == 1 else y
delta = self.activations[-1] - y
for i in reversed(range(len(self.weights))):
dw = self.activations[i].T @ delta / m
db = np.sum(delta, axis=0, keepdims=True) / m
if i > 0:
delta = delta @ self.weights[i].T * self.sigmoid_derivative(self.z_values[i-1])
self.weights[i] -= lr * dw
self.biases[i] -= lr * db
def fit(self, X, y, epochs=1000, lr=0.01):
for epoch in range(epochs):
output = self.forward(X)
self.backward(y, lr)
if epoch % 100 == 0:
loss = np.mean((output - y.reshape(-1, 1)) ** 2)
if epoch % 500 == 0:
print(f"Epoch {epoch}, Loss: {loss:.6f}")
def predict(self, X):
return self.forward(X)
# XOR 问题
X_xor = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_xor = np.array([[0], [1], [1], [0]])
nn = NeuralNetwork([2, 4, 1])
nn.fit(X_xor, y_xor, epochs=2000, lr=0.1)
print(f"XOR 预测:\n{nn.predict(X_xor)}")2.4 深度学习简介
2.4.1 卷积神经网络(CNN)
class Conv2D:
def __init__(self, in_channels, out_channels, kernel_size):
self.kernel_size = kernel_size
self.weights = np.random.randn(out_channels, in_channels, kernel_size, kernel_size) * 0.01
self.bias = np.zeros(out_channels)
def forward(self, X):
batch, c, h, w = X.shape
out_h = h - self.kernel_size + 1
out_w = w - self.kernel_size + 1
output = np.zeros((batch, len(self.weights), out_h, out_w))
for i in range(out_h):
for j in range(out_w):
patch = X[:, :, i:i+self.kernel_size, j:j+self.kernel_size]
for k in range(len(self.weights)):
output[:, k, i, j] = np.sum(patch * self.weights[k], axis=(1, 2, 3)) + self.bias[k]
return output
# 简单的卷积操作
conv = Conv2D(1, 2, 3)
X = np.random.randn(1, 1, 5, 5)
output = conv.forward(X)
print(f"卷积输出形状: {output.shape}")2.4.2 循环神经网络(RNN)
class SimpleRNN:
def __init__(self, input_size, hidden_size, output_size):
self.hidden_size = hidden_size
self.Wxh = np.random.randn(hidden_size, input_size) * 0.01
self.Whh = np.random.randn(hidden_size, hidden_size) * 0.01
self.Why = np.random.randn(output_size, hidden_size) * 0.01
self.bh = np.zeros(hidden_size)
self.by = np.zeros(output_size)
def forward(self, inputs):
h = np.zeros(self.hidden_size)
self.h_states = [h]
outputs = []
for x in inputs:
h = np.tanh(self.Wxh @ x + self.Whh @ h + self.bh)
self.h_states.append(h)
y = self.Why @ h + self.by
outputs.append(y)
return outputs
# 简单的序列预测
rnn = SimpleRNN(input_size=3, hidden_size=4, output_size=2)
inputs = [np.random.randn(3) for _ in range(5)]
outputs = rnn.forward(inputs)
print(f"RNN 输出数量: {len(outputs)}")2.4.3 Transformer 与注意力机制
class SelfAttention:
def __init__(self, d_model, n_heads):
self.d_model = d_model
self.n_heads = n_heads
self.d_k = d_model // n_heads
self.W_q = np.random.randn(d_model, d_model) * 0.01
self.W_k = np.random.randn(d_model, d_model) * 0.01
self.W_v = np.random.randn(d_model, d_model) * 0.01
def forward(self, X):
Q = X @ self.W_q
K = X @ self.W_k
V = X @ self.W_v
scores = Q @ K.T / np.sqrt(self.d_k)
weights = np.exp(scores - np.max(scores, axis=-1, keepdims=True))
weights = weights / np.sum(weights, axis=-1, keepdims=True)
return weights @ V
# 自注意力示例
attention = SelfAttention(d_model=8, n_heads=2)
X = np.random.randn(4, 8)
output = attention.forward(X)
print(f"注意力输出形状: {output.shape}")三、实战案例
3.1 案例一:完整的机器学习流程
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
class MLWorkflow:
def __init__(self):
self.scaler = StandardScaler()
self.model = None
def load_data(self):
iris = load_iris()
X, y = iris.data, iris.target
return train_test_split(X, y, test_size=0.3, random_state=42)
def preprocess(self, X_train, X_test):
X_train_scaled = self.scaler.fit_transform(X_train)
X_test_scaled = self.scaler.transform(X_test)
return X_train_scaled, X_test_scaled
def train(self, X, y):
self.model = KNN(k=5)
self.model.fit(X, y)
def evaluate(self, X, y):
accuracy = self.model.score(X, y)
print(f"准确率: {accuracy:.4f}")
return accuracy
class KNN(SupervisedLearner):
def __init__(self, k=3):
self.k = k
def fit(self, X, y):
self.X_train = np.array(X)
self.y_train = np.array(y)
def predict(self, X):
predictions = []
for x in X:
distances = np.sqrt(np.sum((self.X_train - x) ** 2, axis=1))
k_indices = np.argsort(distances)[:self.k]
k_labels = self.y_train[k_indices]
from collections import Counter
predictions.append(Counter(k_labels).most_common(1)[0][0])
return np.array(predictions)
workflow = MLWorkflow()
X_train, X_test, y_train, y_test = workflow.load_data()
X_train, X_test = workflow.preprocess(X_train, X_test)
workflow.train(X_train, y_train)
workflow.evaluate(X_test, y_test)四、本章小结
本章介绍了机器学习的核心概念和方法。监督学习从标注数据中学习映射关系,无监督学习从无标注数据中发现结构,强化学习通过与环境交互学习最优策略。神经网络是机器学习的核心模型,反向传播是训练神经网络的关键算法。
核心知识点回顾:
- 监督学习、无监督学习、强化学习的区别
- 过拟合与欠拟合,正则化的作用
- 感知机是简单的线性分类器
- 多层神经网络通过反向传播训练
- CNN 适合图像处理,RNN 适合序列数据
- Transformer 的自注意力机制是现代 NLP 的基础
五、思考与练习
思考题
偏差-方差权衡:模型复杂度过高导致过拟合,过低导致欠拟合。如何找到平衡点?
深度学习的本质:为什么深度网络比浅层网络更强大?"深度"带来了什么优势?
编程练习
实现决策树:用基尼指数实现 CART 决策树算法。
实现 SVM:用梯度下降实现线性支持向量机。
实现简单的 GAN:生成对抗网络的基本实现。