sklearn:机器学习入门 (scikit-learn: ML Basics)


章节概述

scikit-learn(简称 sklearn)是 Python 的传统机器学习标准库。对于 C 程序员,它的重要性在于:(1) 统一的 fit/predict API 设计让你无需记忆每个算法的细节,(2) 底层计算由 Cython/C 完成,性能不低,(3) 模型可以用 joblib 保存后由 C++ 程序加载。本章从分类、回归、聚类三大任务切入,带你掌握 sklearn 的核心工作模式。

核心理念:机器学习的本质是 y = f(X) ——找到从输入 X 到输出 y 的映射函数。sklearn 把这个公式包装为 model.fit(X, y)model.predict(X_new)。作为 C 程序员,你需要理解的不只是一行 API 调用,而是数据流(NumPy 数组 → 训练 → 模型对象 → 预测 → 结果)和模型序列化(.joblib 文件 → C++ 加载)。本章所有示例都可在 python -c 中一行运行。


第一节:sklearn 统一 API 与数据准备

1.1 核心 API 模式

sklearn 的全部算法遵循三个核心方法:

from sklearn.xxx import SomeModel
 
model = SomeModel(hyper_param=value) # 1. 创建模型,设置超参数
model.fit(X_train, y_train) # 2. 训练:从数据中学习
result = model.predict(X_test) # 3. 预测:对新数据做推断

这种统一性意味着:当你学会一个模型的使用方式,你就学会了所有 sklearn 模型。在 C 中,相当于所有算法都实现了同一个函数指针接口:

// C 中的类比思维
typedef struct {
 void* model;
 void (*fit)(void* model, double* X, double* y, int n);
 void (*predict)(void* model, double* X, double* out, int n);
} BaseModel;

1.2 数据格式约定

sklearn 的数据输入必须是以 NumPy 数组或 pandas DataFrame 形式:

import numpy as np
 
# X: 特征矩阵,形状 (n_samples, n_features)
X = np.array([[1.0, 2.0],
 [3.0, 4.0],
 [5.0, 6.0]]) # 3 个样本,每个有 2 个特征
 
# y: 标签向量,形状 (n_samples,)
y = np.array([0, 1, 0]) # 3 个标签(分类)或连续值(回归)

C 对照X 等价于 C 中的 double X[3][2]y 等价于 int y[3]。sklearn 内部将这些数据传给 Cython/C 实现处理。

1.3 经典数据集加载

# 一行流:加载鸢尾花数据集并查看形状
python -c "
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
print(f'X shape: {X.shape}, y shape: {y.shape}')
print(f'Classes: {set(y)}')
"

输出:

X shape: (150, 4), y shape: (150,)
Classes: {0, 1, 2}

150 个样本,4 个特征(花萼长/宽、花瓣长/宽),3 个类别(三种鸢尾花)。

1.4 训练/测试拆分

python -c "
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
print(f'Train: {X_train.shape[0]}, Test: {X_test.shape[0]}')
"

random_state 是随机种子,确保每次拆分结果一致。等同于 C 中的 srand(42)


第二节:分类(Classification)

2.1 逻辑回归 — 最简单的分类器

python -c "
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
 
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
 
model = LogisticRegression(max_iter=200)
model.fit(X_train, y_train)
print(f'Accuracy: {model.score(X_test, y_test):.3f}')
print(f'Predictions: {model.predict(X_test[:5])}')
print(f'Probabilities:\n{model.predict_proba(X_test[:5])}')
"

2.2 随机森林 — 表格数据的王者

python -c "
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
 
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
 
model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)
model.fit(X_train, y_train)
print(f'Accuracy: {model.score(X_test, y_test):.3f}')
print(f'Feature importances: {model.feature_importances_}')
"

n_estimators=100 意味着 100 棵决策树投票。每棵树在 C++ 底层递归分裂节点,所有树并行独立构建。

2.3 SVM — 最大边界分类器

python -c "
from sklearn.datasets import load_iris
from sklearn.svm import SVC
 
X, y = load_iris(return_X_y=True)
model = SVC(kernel='rbf', C=1.0, probability=True)
model.fit(X, y)
print(f'Support vectors: {len(model.support_vectors_)}')
"

2.4 评估指标

python -c "
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, f1_score, confusion_matrix, classification_report
 
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
 
print(f'Accuracy: {accuracy_score(y_test, y_pred):.3f}')
print(f'F1 (macro): {f1_score(y_test, y_pred, average=\"macro\"):.3f}')
print('Confusion Matrix:')
print(confusion_matrix(y_test, y_pred))
print('Report:')
print(classification_report(y_test, y_pred, target_names=load_iris().target_names))
"
指标含义C 实现等价
Accuracy预测正确的比例sum(y_pred[i]==y_true[i]) / n
Precision预测为正类中真正正类的比例TP/(TP+FP)
Recall真正正类被预测出的比例TP/(TP+FN)
F1 ScorePrecision 和 Recall 的调和平均2*P*R/(P+R)

第三节:回归与聚类

3.1 回归任务 — 预测连续值

python -c "
from sklearn.datasets import load_diabetes
from sklearn.linear_model import LinearRegression, Ridge
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error, r2_score
 
X, y = load_diabetes(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
 
for name, model in [
 ('Linear', LinearRegression()),
 ('Ridge', Ridge(alpha=1.0)),
 ('RandomForest', RandomForestRegressor(n_estimators=100, random_state=42))
]:
 model.fit(X_train, y_train)
 y_pred = model.predict(X_test)
 print(f'{name:15s} | MSE: {mean_squared_error(y_test, y_pred):7.1f} | R²: {r2_score(y_test, y_pred):.3f}')
"

3.2 聚类任务 — 无标签数据的分组

python -c "
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
import numpy as np
 
X, _ = make_blobs(n_samples=300, centers=4, n_features=2, random_state=42)
kmeans = KMeans(n_clusters=4, random_state=42, n_init='auto')
labels = kmeans.fit_predict(X)
print(f'Cluster centers:\n{kmeans.cluster_centers_}')
print(f'Inertia: {kmeans.inertia_:.2f}')
print(f'Label counts: {np.bincount(labels)}')
"

KMeans 使用 Lloyd 算法迭代优化:初始化中心 → 分配样本到最近中心 → 重新计算中心 → 重复。在 C 中,你可以用一个三重循环和一个距离函数实现相同的逻辑。

3.3 交叉验证 — 稳定评估模型性能

python -c "
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
 
X, y = load_iris(return_X_y=True)
model = RandomForestClassifier(n_estimators=100, random_state=42)
scores = cross_val_score(model, X, y, cv=5) # 5 折交叉验证
print(f'CV scores: {scores}')
print(f'Mean: {scores.mean():.3f} ± {scores.std():.3f}')
"

5 折交叉验证 = 把数据分 5 份,轮流用 4 份训练、1 份测试,取 5 次指标的平均值。这比单次拆分更可靠。


第四节:Pipeline 与模型持久化

4.1 数据预处理

python -c "
from sklearn.preprocessing import StandardScaler, LabelEncoder
import numpy as np
 
# 标准化:使每个特征的均值为 0,方差为 1
X = np.array([[100, 2023],
 [200, 2024],
 [150, 2025]], dtype=float)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
print(f'Original:\n{X}')
print(f'Scaled (mean=0, std=1):\n{X_scaled}')
print(f'Mean: {X_scaled.mean(axis=0)}, Std: {X_scaled.std(axis=0)}')
"

4.2 Pipeline —— 组装预处理和模型

python -c "
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline
 
X, y = load_iris(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
 
pipe = Pipeline([
 ('scaler', StandardScaler()), # 第1步:标准化
 ('classifier', RandomForestClassifier(n_estimators=100, random_state=42)) # 第2步:分类
])
pipe.fit(X_train, y_train)
print(f'Pipeline accuracy: {pipe.score(X_test, y_test):.3f}')
"

Pipeline 的思维和 Unix 管道一致:数据 | 标准化 | 模型 | 预测。在 C 中,相当于将多个函数组成一个执行链 predict(model, scale(data))

4.3 模型保存与加载

import joblib
 
# 整个 Pipeline 作为一个整体保存
joblib.dump(pipe, 'iris_pipeline.joblib')
 
# C++ 程序或后续 Python 脚本中加载
loaded_pipe = joblib.load('iris_pipeline.joblib')
loaded_pipe.predict(X_new)

C++ 部署注意:joblib 是 Python 的 pickle 协议,C++ 不能直接读取。要 C++ 部署 sklearn 模型,你需要:(1) 提取模型参数(如树的节点、SVM 的支持向量),手动用 C 实现推理;(2) 或者改用 XGBoost/LightGBM(有原生 C API)。



练习

以下题目用于验证本章所学内容:

题号题目链接涉及知识点
本章无对应力扣题请用动手练习题自检