机器学习 (Machine Learning) 是让计算机通过数据自动学习规律,无需显式编程的技术。本文从核心概念出发,建立机器学习的完整认知框架。
1. 机器学习三大范式
1.1 监督学习 (Supervised Learning)
训练数据包含 输入特征 和 标签 (label),模型学习从输入到输出的映射关系。
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# 特征 X 与标签 y
X = np.array([[1], [2], [3], [4], [5]]) # 房屋面积
y = np.array([100, 180, 270, 340, 430]) # 价格(万元)
# 划分训练集/测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42)
# 训练线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)
# 预测
prediction = model.predict([[6]]) # 预测 600㎡ 的价格
print(f"预测价格: {prediction[0]:.2f} 万元")
任务类型:
- 回归:输出是连续值(房价、温度)
- 分类:输出是离散类别(邮件是否垃圾、肿瘤良恶性)
1.2 无监督学习 (Unsupervised Learning)
训练数据 只有特征,没有标签。模型自动发现数据内在结构。
from sklearn.cluster import KMeans
# 客户消费数据(无标签)
X = np.array([
[500, 2], [600, 3], [550, 2], # 群体 A:低频低消
[3000, 20], [3200, 22], [2800, 18] # 群体 B:高频高消
])
# K-Means 聚类
kmeans = KMeans(n_clusters=2, random_state=42, n_init=10)
labels = kmeans.fit_predict(X)
print(f"聚类结果: {labels}") # [0, 0, 0, 1, 1, 1]
任务类型:
- 聚类:将数据分组(客户分群、文档聚类)
- 降维:减少特征维度(PCA、t-SNE、可视化)
- 关联规则:发现项集关系(购物篮分析)
1.3 强化学习 (Reinforcement Learning)
智能体 (Agent) 在环境 (Environment) 中通过 试错 学习策略,以最大化累积奖励。
当前状态 s → 选择动作 a → 环境反馈奖励 r → 转移到新状态 s'
↑_________________|
核心要素:
- 状态 (State):环境观测
- 动作 (Action):智能体行为
- 奖励 (Reward):环境反馈信号
- 策略 (Policy):状态到动作的映射
- 价值函数 (Value Function):长期累积奖励期望
2. 模型评估指标
2.1 回归指标
| 指标 | 公式 | 说明 |
|---|---|---|
| MAE | $\frac{1}{n}\sum|y_i - \hat{y}_i|$ | 平均绝对误差,单位与目标一致 |
| MSE | $\frac{1}{n}\sum(y_i - \hat{y}_i)^2$ | 均方误差,对异常值敏感 |
| RMSE | $\sqrt{MSE}$ | 与目标量纲一致 |
| R² | $1 - \frac{SS_{res}}{SS_{tot}}$ | 可解释方差比例,1 为完美 |
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
y_true = np.array([3, -0.5, 2, 7])
y_pred = np.array([2.5, 0.0, 2, 8])
print(f"MAE: {mean_absolute_error(y_true, y_pred):.3f}")
print(f"RMSE: {np.sqrt(mean_squared_error(y_true, y_pred)):.3f}")
print(f"R²: {r2_score(y_true, y_pred):.3f}")
2.2 分类指标
混淆矩阵:
| 实际 \ 预测 | 正类 | 负类 |
|---|---|---|
| 正类 | TP (真正例) | FN (假反例) |
| 负类 | FP (假正例) | TN (真反例) |
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import classification_report, confusion_matrix
y_true = [0, 1, 0, 0, 1, 1, 0, 1]
y_pred = [0, 1, 0, 1, 1, 0, 0, 1]
print(confusion_matrix(y_true, y_pred))
# [[3 1]
# [1 3]]
print(f"Accuracy: {accuracy_score(y_true, y_pred):.3f}") # (3+3)/8 = 0.75
print(f"Precision: {precision_score(y_true, y_pred):.3f}") # 3/(3+1) = 0.75
print(f"Recall: {recall_score(y_true, y_pred):.3f}") # 3/(3+1) = 0.75
print(f"F1: {f1_score(y_true, y_pred):.3f}") # 2*0.75*0.75/(0.75+0.75) = 0.75
# 完整报告
print(classification_report(y_true, y_pred, target_names=['负类', '正类']))
不平衡数据的评估:
from sklearn.metrics import roc_auc_score, average_precision_score
# AUC-ROC:不受类别分布影响,随机猜为 0.5,完美为 1.0
auc = roc_auc_score(y_true, y_proba)
# PR 曲线下的面积,更适合极度不平衡数据
ap = average_precision_score(y_true, y_proba)
2.3 交叉验证
from sklearn.model_selection import cross_val_score, KFold, StratifiedKFold
# K 折交叉验证(分类问题用 StratifiedKFold 保证每折类别比例一致)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(model, X, y, cv=cv, scoring='f1_macro')
print(f"各折得分: {scores}")
print(f"平均 F1: {scores.mean():.3f} (+/- {scores.std()*2:.3f})")
3. 过拟合与正则化
3.1 偏差-方差权衡
| 问题 | 偏差 (Bias) | 方差 (Variance) | 表现 | 解决 |
|---|---|---|---|---|
| 欠拟合 | 高 | 低 | 训练/测试误差都高 | 增加模型复杂度、更多特征 |
| 过拟合 | 低 | 高 | 训练误差低,测试误差高 | 正则化、更多数据、降维 |
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
# 展示过拟合:高阶多项式
degrees = [1, 3, 15]
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, degree in zip(axes, degrees):
model = make_pipeline(
PolynomialFeatures(degree),
LinearRegression()
)
model.fit(X_train, y_train)
train_score = model.score(X_train, y_train)
test_score = model.score(X_test, y_test)
ax.set_title(f"Degree {degree}\nTrain: {train_score:.3f}, Test: {test_score:.3f}")
3.2 L1/L2 正则化
| 类型 | 惩罚项 | 效果 | 适用 |
|---|---|---|---|
| L1 (Lasso) | $\lambda\sum|w_i|$ | 产生稀疏解(特征选择) | 高维稀疏特征 |
| L2 (Ridge) | $\lambda\sum w_i^2$ | 权重平滑收缩 | 多重共线性 |
| ElasticNet | $\alpha\cdot L1 + (1-\alpha)\cdot L2$ | 兼顾两者 | 通用场景 |
from sklearn.linear_model import Ridge, Lasso, ElasticNet
# L2 正则化岭回归
ridge = Ridge(alpha=1.0) # alpha 越大约束越强
ridge.fit(X_train, y_train)
# L1 正则化 Lasso
lasso = Lasso(alpha=0.1)
lasso.fit(X_train, y_train)
print(f"非零系数数量: {np.sum(lasso.coef_ != 0)}") # 自动特征选择
3.3 早停 (Early Stopping)
from sklearn.linear_model import SGDRegressor
model = SGDRegressor(early_stopping=True, validation_fraction=0.1,
n_iter_no_change=5, max_iter=1000)
model.fit(X_train, y_train)
4. 特征工程入门
4.1 数据预处理
import pandas as pd
from sklearn.preprocessing import StandardScaler, MinMaxScaler, LabelEncoder
df = pd.DataFrame({
'age': [25, 30, None, 35], # 数值型,有缺失
'salary': [5000, 8000, 6000, 10000], # 数值型
'city': ['Beijing', 'Shanghai', 'Beijing', 'Guangzhou'], # 类别型
'level': ['junior', 'senior', 'mid', 'senior'] # 有序类别
})
# 缺失值处理
# 删除:df.dropna()
# 填充:df['age'].fillna(df['age'].median(), inplace=True)
# 标准化:均值为 0,方差为 1
scaler = StandardScaler()
df[['age', 'salary']] = scaler.fit_transform(df[['age', 'salary']])
# 归一化:缩放到 [0, 1]
normalizer = MinMaxScaler()
df[['age', 'salary']] = normalizer.fit_transform(df[['age', 'salary']])
# 类别编码:one-hot
df_encoded = pd.get_dummies(df, columns=['city'], prefix='city')
# 有序标签编码
level_map = {'junior': 0, 'mid': 1, 'senior': 2}
df['level_encoded'] = df['level'].map(level_map)
4.2 特征构造
# 时间特征
df['date'] = pd.to_datetime(['2024-01-15', '2024-06-20', '2024-12-01'])
df['year'] = df['date'].dt.year
df['month'] = df['date'].dt.month
df['dayofweek'] = df['date'].dt.dayofweek
df['is_weekend'] = df['dayofweek'].isin([5, 6]).astype(int)
# 交叉特征
df['age_salary_ratio'] = df['age'] / df['salary']
# 分箱(Binning)
df['age_group'] = pd.cut(df['age'], bins=[0, 30, 40, 100],
labels=['young', 'mid', 'senior'])
# 多项式特征
from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(df[['age', 'salary']])
4.3 特征选择
from sklearn.feature_selection import SelectKBest, f_classif, mutual_info_classif
from sklearn.feature_selection import RFE
# 过滤法:基于统计检验
selector = SelectKBest(score_func=f_classif, k=10)
X_selected = selector.fit_transform(X, y)
# 包装法:递归特征消除
estimator = LogisticRegression(max_iter=1000)
rfe = RFE(estimator, n_features_to_select=10)
X_rfe = rfe.fit_transform(X, y)
# 查看选中特征
selected_features = X.columns[rfe.support_]
print(f"选中特征: {selected_features.tolist()}")
5. Scikit-learn 工作流
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
# 定义数值与类别列
numeric_features = ['age', 'salary', 'tenure']
categorical_features = ['department', 'city']
# 数值处理流水线
numeric_transformer = Pipeline([
('imputer', SimpleImputer(strategy='median')),
('scaler', StandardScaler())
])
# 类别处理流水线
categorical_transformer = Pipeline([
('imputer', SimpleImputer(strategy='most_frequent')),
('onehot', OneHotEncoder(handle_unknown='ignore'))
])
# 组合预处理
preprocess = ColumnTransformer([
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
])
# 完整流水线
pipeline = Pipeline([
('preprocess', preprocess),
('classifier', RandomForestClassifier(n_estimators=100, random_state=42))
])
# 训练与评估
pipeline.fit(X_train, y_train)
print(f"测试集准确率: {pipeline.score(X_test, y_test):.3f}")
总结
机器学习工程的核心流程:
- 理解数据:EDA(探索性数据分析),了解分布、相关性、缺失情况
- 特征工程:数据清洗 → 变换 → 构造 → 选择,往往比调参更重要
- 模型选择与训练:从简单基线开始,逐步复杂化
- 评估与诊断:交叉验证 + 多维度指标,区分过拟合/欠拟合
- 正则化与调优:L1/L2、早停、超参数搜索
- 部署与监控:关注数据漂移,持续评估线上表现
“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限。”
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。