机器学习入门(scikit-learn)
掌握机器学习的核心概念和工作流程。scikit-learn 提供了统一的 API,让你用几行代码就能训练和评估模型。
机器学习核心概念
什么是机器学习?
机器学习是让计算机从数据中自动学习规律,而不是手动编写规则。传统编程:输入数据 + 规则 → 输出结果;机器学习:输入数据 + 期望结果 → 学出规则(模型)。
- 特征(Feature)与标签(Label) 特征是模型的输入变量(如房屋面积、位置、层数),标签是要预测的目标值(如房价)。在代码中通常用 X 表示特征矩阵,y 表示标签向量。
- 训练集 / 验证集 / 测试集 训练集用于模型学习参数;验证集用于调整超参数和早停;测试集用于最终性能评估(只用一次!)。常见比例:训练 70% / 验证 15% / 测试 15%。
- 过拟合(Overfitting) 模型在训练集上表现很好,在测试集上表现差——记住了训练数据的噪声而不是真正的规律。应对方法:正则化、Dropout、数据增强、减小模型复杂度。
- 欠拟合(Underfitting) 模型连训练集都学不好,通常因为模型太简单或训练不足。应对方法:增大模型复杂度、增加特征、减少正则化、更长时间训练。
- 偏差-方差权衡(Bias-Variance Tradeoff) 偏差高=欠拟合(模型太简单,系统性地预测错误);方差高=过拟合(模型太复杂,对训练数据过于敏感)。理想模型需要平衡两者。
- 超参数(Hyperparameter) 不由训练数据学到,需要人工设定的参数,如学习率、决策树深度、随机森林的树的数量。超参数调整是提升模型性能的重要手段。
| 类型 | 特点 | 典型算法 | 应用场景 |
|---|---|---|---|
| 监督学习 | 有标签数据 | 线性回归、随机森林 | 房价预测、垃圾邮件分类 |
| 无监督学习 | 无标签数据 | K-Means、PCA | 用户聚类、降维 |
| 强化学习 | 通过奖惩学习 | PPO、DQN | 游戏 AI、机器人控制 |
过拟合(Overfitting)与欠拟合(Underfitting)
过拟合:模型在训练集上准确率极高,但在新数据上表现差——死记硬背,不会举一反三。欠拟合:模型太简单,连训练集都学不好。理想状态是训练集和验证集准确率都高且相近。
scikit-learn 的统一 API
scikit-learn 最大的优点是所有算法都有统一的接口:fit() 训练,predict() 预测,score() 评分。换算法只需改一行代码。
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, classification_report
# 1. 加载数据集(鸢尾花,经典入门数据集)
iris = load_iris()
X, y = iris.data, iris.target
print(f"特征形状: {X.shape}, 类别数: {len(set(y))}")
# 2. 切分数据集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 3. 特征标准化(零均值,单位方差)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train) # 在训练集上 fit
X_test = scaler.transform(X_test) # 测试集只用 transform!
# 4. 训练模型
model = LogisticRegression(max_iter=1000)
model.fit(X_train, y_train) # 两行搞定训练
# 5. 评估
y_pred = model.predict(X_test)
print(f"准确率: {accuracy_score(y_test, y_pred):.4f}")
print(classification_report(y_test, y_pred))
数据泄露(Data Leakage)陷阱
StandardScaler 必须在训练集上 fit,然后用同样的参数 transform 测试集。如果用测试集做 fit_transform,测试集的信息就"泄露"给了模型,评估结果会虚高。这是初学者最常犯的错误之一。
常用算法一览
scikit-learn 统一的 API 让换算法变得极其简单。了解各算法的特点,在合适的场景选对工具:
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from sklearn.neighbors import KNeighborsClassifier
# 所有算法接口完全一样!
models = {
"逻辑回归": LogisticRegression(),
"决策树": DecisionTreeClassifier(max_depth=5),
"随机森林": RandomForestClassifier(n_estimators=100),
"梯度提升": GradientBoostingClassifier(),
"SVM": SVC(kernel="rbf"),
"KNN": KNeighborsClassifier(n_neighbors=5),
}
# 快速对比所有算法的效果
for name, clf in models.items():
clf.fit(X_train, y_train)
acc = clf.score(X_test, y_test)
print(f"{name:10s}: {acc:.4f}")
| 算法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 逻辑回归 | 可解释性强、速度快 | 只能线性决策边界 | 二分类基线模型 |
| 随机森林 | 强大、抗过拟合 | 黑盒、速度慢 | 结构化数据首选 |
| 梯度提升 (XGBoost) | Kaggle 竞赛冠军 | 参数多、训练慢 | 结构化数据最强 |
| SVM | 小数据集效果好 | 大数据慢、参数敏感 | 文本分类、小数据集 |
模型评估与交叉验证
单次划分训练/测试集的结果受随机性影响。交叉验证(Cross-Validation)通过多次不同划分取平均,得到更可靠的评估结果。
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.metrics import (
accuracy_score, precision_score,
recall_score, f1_score, roc_auc_score,
confusion_matrix
)
model = RandomForestClassifier(n_estimators=100, random_state=42)
# 5折交叉验证
cv_scores = cross_val_score(model, X, y, cv=5, scoring="accuracy")
print(f"CV 准确率: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}")
# 评估各指标(在测试集上)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f"准确率 (Accuracy): {accuracy_score(y_test, y_pred):.4f}")
print(f"精确率 (Precision): {precision_score(y_test, y_pred, average='macro'):.4f}")
print(f"召回率 (Recall): {recall_score(y_test, y_pred, average='macro'):.4f}")
print(f"F1 Score: {f1_score(y_test, y_pred, average='macro'):.4f}")
# 混淆矩阵:可视化预测错误的分布
cm = confusion_matrix(y_test, y_pred)
print("混淆矩阵:\n", cm)
准确率不是万能的
如果正负样本比例严重不均(比如 99% 负样本),模型只要全预测负就有 99% 准确率,但毫无用处。这时应该用 F1 Score 或 AUC-ROC 来评估。在医疗 AI(疾病检测)中,召回率比准确率更重要——漏诊代价更高。
超参数调优
from sklearn.model_selection import GridSearchCV
model = RandomForestClassifier(random_state=42)
# 定义要搜索的参数范围
param_grid = {
"n_estimators": [50, 100, 200],
"max_depth": [None, 5, 10],
"min_samples_split": [2, 5],
}
# 5折交叉验证 + 网格搜索
grid_search = GridSearchCV(
model, param_grid,
cv=5, scoring="f1_macro",
n_jobs=-1 # 用所有 CPU 核心并行
)
grid_search.fit(X_train, y_train)
print(f"最佳参数: {grid_search.best_params_}")
print(f"最佳得分: {grid_search.best_score_:.4f}")
# 用最佳参数的模型进行预测
best_model = grid_search.best_estimator_
final_score = best_model.score(X_test, y_test)
print(f"测试集得分: {final_score:.4f}")
调参的核心原则
不要在测试集上调参!正确流程:训练集训练 → 验证集(或交叉验证)调参 → 确定模型后最终用测试集评估一次。测试集必须保持"纯洁",代表真实未见数据。
常见误区与实战注意事项
数据泄露(Data Leakage)是最严重的错误
将测试集的信息泄露给训练过程,会导致虚高的评估结果但在生产中失败。常见形式:在全量数据上做归一化(应只在训练集 fit);使用未来数据预测过去(时序数据);特征中包含目标变量的代理特征。StandardScaler 必须在训练集 fit_transform,在测试集只 transform。
类别不平衡:准确率不是万能指标
如果数据中 99% 是负样本,全部预测负就有 99% 准确率,但毫无用处。对于不平衡数据集,使用 F1 Score、AUC-ROC 或 PR 曲线。scikit-learn 的 train_test_split 提供 stratify=y 参数,保证切分后各类别比例与原始数据相同。
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
# Pipeline 确保每次交叉验证折叠中,
# scaler 只在训练折上 fit,不会泄露验证折数据
pipeline = Pipeline([
("scaler", StandardScaler()), # 步骤1:标准化
("model", RandomForestClassifier( # 步骤2:分类器
n_estimators=100,
random_state=42
))
])
# 交叉验证时 Pipeline 自动处理数据泄露问题
cv_scores = cross_val_score(pipeline, X, y, cv=5)
print(f"CV 准确率: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}")
# 训练完整管道并预测
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
# 用 GridSearchCV 优化 Pipeline 参数
from sklearn.model_selection import GridSearchCV
param_grid = {
# Pipeline 参数格式:步骤名__参数名
"model__n_estimators": [50, 100, 200],
"model__max_depth": [None, 5, 10],
}
grid_search = GridSearchCV(pipeline, param_grid, cv=5)
grid_search.fit(X_train, y_train)
本章小结
机器学习的核心流程:加载数据 → 切分数据集(含 stratify)→ 特征处理(StandardScaler)→ 训练模型(fit)→ 评估(score / cross_val_score)→ 调参(GridSearchCV)。scikit-learn 统一 API 让换算法只需改一行代码。关键误区:数据泄露(Scaler 只能在训练集 fit)、准确率不适用于不平衡数据集(用 F1 或 AUC-ROC)、不能在测试集上调参。推荐用 Pipeline 将预处理和模型串联,从根本上防止数据泄露。