楼层: 首页/ 软件技术/ Python AI 数据科学/ Scikit-learn:机器学习瑞士军刀
5

Scikit-learn:机器学习瑞士军刀

Scikit-learn ML Pipeline

Scikit-learn 的精髓是统一 API:不管你用线性回归还是随机森林,都是 model.fit(X, y) 训练、model.predict(X) 预测。学会一个,就会用所有。这章把机器学习的标准流程走一遍。

机器学习标准流程

论六步走

① 拿数据 → ② 预处理(标准化、编码分类变量)→ ③ 切分训练集/测试集 → ④ 选模型训练 → ⑤ 评估 → ⑥ 预测新数据。这套流程适用于所有经典机器学习任务,闭着眼背下来。

预处理与切分

from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # X 是特征矩阵(每行一个样本,每列一个特征),y 是标签 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 80% 训练,20% 测试;random_state 保证每次切分一样 # 标准化:把特征缩到均值0、方差1 scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 测试集用训练集的 scaler,别重新 fit!

监督学习:分类与回归

模型干什么什么时候用
LinearRegression回归(预测连续值)房价预测、销量预测。
LogisticRegression分类(虽然名字叫回归)二分类:垃圾邮件/正常邮件。
DecisionTree决策树,可解释需要"为什么这么判"的场景。
RandomForest随机森林,一堆树投票表格数据上的强力 baseline。
SVM支持向量机小数据集、高维。
KNNK 近邻,看邻居是谁简单 baseline,速度慢。
朴素贝叶斯基于概率,文本分类快垃圾邮件、文本分类。

KNN 和朴素贝叶斯的最小例子

from sklearn.neighbors import KNeighborsClassifier from sklearn.naive_bayes import GaussianNB # KNN:K=3,看最近的 3 个邻居投票 knn = KNeighborsClassifier(n_neighbors=3) knn.fit(X_train, y_train) print("KNN 准确率:", knn.score(X_test, y_test)) # 朴素贝叶斯:假设特征独立,快到飞起 nb = GaussianNB() nb.fit(X_train, y_train) print("贝叶斯准确率:", nb.score(X_test, y_test)) # 逻辑回归:名字叫回归,其实是分类 from sklearn.linear_model import LogisticRegression lr = LogisticRegression(max_iter=1000) lr.fit(X_train_scaled, y_train) print("逻辑回归准确率:", lr.score(X_test_scaled, y_test)) # max_iter 加大是因为数据多了默认迭代次数不够 # SVM:小数据集、高维数据上表现好 from sklearn.svm import SVC svm = SVC(kernel="rbf", C=1.0, random_state=42) svm.fit(X_train_scaled, y_train) print("SVM 准确率:", svm.score(X_test_scaled, y_test)) # 注意:SVM 对量纲敏感,必须先标准化!

完整例子:鸢尾花分类

from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, confusion_matrix # 1. 拿数据(sklearn 自带鸢尾花数据集) iris = load_iris() X, y = iris.data, iris.target # 2. 切分 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 3. 训练随机森林 model = RandomForestClassifier(n_estimators=100, random_state=42) model.fit(X_train, y_train) # 4. 评估 pred = model.predict(X_test) print("准确率:", accuracy_score(y_test, pred)) # 准确率:1.0(这个数据集太简单了) print("混淆矩阵:") print(confusion_matrix(y_test, pred)) # 最常用的一张表:precision/recall/F1 一把全给 from sklearn.metrics import classification_report print(classification_report(y_test, pred, target_names=iris.target_names)) # 5. 看哪个特征最重要 for name, imp in zip(iris.feature_names, model.feature_importances_): print(f"{name}: {imp:.3f}")

预处理:编码分类变量

from sklearn.preprocessing import LabelEncoder, OneHotEncoder # 标签编码:把"红/绿/蓝"变成 0/1/2(有序类别用) le = LabelEncoder() y = le.fit_transform(["猫", "狗", "猫", "鸟"]) # 反向:le.inverse_transform([0,1,2]) # One-Hot:把城市列变成"北京/上海/广州"三列 0/1(无序类别用) # 树模型不用 One-Hot,线性/SVM 必须用 ohe = OneHotEncoder(sparse_output=False) city_encoded = ohe.fit_transform(df[["城市"]])

交叉验证与网格搜索

from sklearn.model_selection import cross_val_score, GridSearchCV # 交叉验证:把数据切 5 份,轮流当训练/验证,更可靠 scores = cross_val_score(model, X_train, y_train, cv=5, scoring="accuracy") print("5折平均分:", scores.mean()) # 网格搜索:自动试一堆参数组合 param_grid = { "n_estimators": [50, 100, 200], "max_depth": [3, 5, None] } grid = GridSearchCV( RandomForestClassifier(random_state=42), param_grid, cv=5, scoring="accuracy" ) grid.fit(X_train, y_train) print("最优参数:", grid.best_params_) print("最优分数:", grid.best_score_) best_model = grid.best_estimator_

实战:房价预测(回归任务)

from sklearn.datasets import fetch_california_housing from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score house = fetch_california_housing() X, y = house.data, house.target X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # 标准化(线性模型对量纲敏感) scaler = StandardScaler() X_train_s = scaler.fit_transform(X_train) X_test_s = scaler.transform(X_test) # 训练线性回归 reg = LinearRegression() reg.fit(X_train_s, y_train) pred = reg.predict(X_test_s) # 回归指标:MSE 越小越好,R² 越接近 1 越好 print("MSE:", mean_squared_error(y_test, pred)) print("R²:", r2_score(y_test, pred))

模型评估指标

指标什么时候看
accuracy 准确率类别均衡时用;不均衡时会骗人。
precision 精确率预测为正的里面,真的有多少正。
recall 召回率所有真的正例,被你找出来多少。
F1precision 和 recall 的调和平均,两者都要。
ROC-AUC二分类综合指标,0.5 是瞎猜,1.0 完美。
交叉验证cross_val_score:把数据切多份轮流当训练/测试,更稳。
GridSearchCV自动试一堆参数组合,找最优。

一句话记忆:看病(漏诊代价高)看重 recall,抓坏人(误伤代价高)看重 precision。

看模型:特征重要性

树模型(随机森林、XGBoost)能告诉你"哪个特征最管用"——这在业务上很值钱,比单纯一个准确率数字有用得多。

# 训练完后画个条形图 import pandas as pd import matplotlib.pyplot as plt importance = pd.Series(model.feature_importances_, index=iris.feature_names) importance.sort_values().plot(kind="barh") plt.title("特征重要性") plt.show() # 一眼看出"花瓣长度"对鸢尾花分类最关键

无监督:K-Means 聚类 & PCA

from sklearn.cluster import KMeans from sklearn.decomposition import PCA # K-Means:把数据自动分成 K 组(无标签) kmeans = KMeans(n_clusters=3, random_state=42, n_init=10) labels = kmeans.fit_predict(X) # PCA:降维,把高维数据压成 2 维方便画图 pca = PCA(n_components=2) X_2d = pca.fit_transform(X) print("保留的方差比例:", pca.explained_variance_ratio_)

机器学习流水线:Pipeline + ColumnTransformer

论为什么要 Pipeline

手动调预处理步骤很容易搞混顺序、导致数据泄漏。Pipeline 把"预处理 + 模型"打包成一个对象,fit 时按顺序跑,predict 时也按顺序跑,杜绝泄漏。ColumnTransformer 让你对不同列做不同预处理(数值列标准化、类别列 One-Hot)。

from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler, OneHotEncoder from sklearn.impute import SimpleImputer from sklearn.ensemble import RandomForestClassifier # 假设 df 有数值列 [年龄, 收入] 和类别列 [城市, 性别] numeric_features = ["年龄", "收入"] categorical_features = ["城市", "性别"] # 数值列:先填缺失值,再标准化 numeric_transformer = Pipeline(steps=[ ("imputer", SimpleImputer(strategy="median")), ("scaler", StandardScaler()) ]) # 类别列:填缺失值,再 One-Hot categorical_transformer = Pipeline(steps=[ ("imputer", SimpleImputer(strategy="most_frequent")), ("onehot", OneHotEncoder(handle_unknown="ignore")) ]) # 把两个预处理拼成一个"列转换器" preprocessor = ColumnTransformer( transformers=[ ("num", numeric_transformer, numeric_features), ("cat", categorical_transformer, categorical_features) ]) # 完整流水线:预处理 → 分类器 clf = Pipeline(steps=[ ("preprocessor", preprocessor), ("classifier", RandomForestClassifier(n_estimators=100, random_state=42)) ]) clf.fit(X_train, y_train) print("准确率:", clf.score(X_test, y_test)) # 新数据来时直接 predict,预处理自动跟上 clf.predict(X_new)

交叉验证:StratifiedKFold / TimeSeriesSplit

from sklearn.model_selection import ( StratifiedKFold, TimeSeriesSplit, cross_val_score ) # 1. 分层 K 折:分类任务标配(每折类别比例一致) skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) scores = cross_val_score(clf, X, y, cv=skf, scoring="accuracy") print("每折分数:", scores) # [0.93 0.91 0.94 0.92 0.90] print("平均:", scores.mean(), "标准差:", scores.std()) # 平均:0.92 标准差:0.014 # 2. 时间序列切分:不能随机打乱(未来数据不能当过去用) tscv = TimeSeriesSplit(n_splits=5) for fold, (train_idx, test_idx) in enumerate(tscv.split(X)): print(f"Fold {fold}: train={len(train_idx)}, test={len(test_idx)}") # Fold 0: train=100, test=100 # Fold 1: train=200, test=100 # ...

超参数搜索:GridSearchCV / RandomizedSearchCV

from sklearn.model_selection import GridSearchCV, RandomizedSearchCV from scipy.stats import randint # 1. 网格搜索:穷举所有组合(小范围用) param_grid = { "classifier__n_estimators": [50, 100, 200], "classifier__max_depth": [3, 5, None], "classifier__min_samples_leaf": [1, 2, 4] } grid = GridSearchCV(clf, param_grid, cv=5, scoring="f1", n_jobs=-1) grid.fit(X_train, y_train) print("最优参数:", grid.best_params_) print("最优分数:", grid.best_score_) best_model = grid.best_estimator_ # 2. 随机搜索:参数空间大时更高效 param_dist = { "classifier__n_estimators": randint(50, 500), "classifier__max_depth": [3, 5, 10, None], "classifier__min_samples_split": randint(2, 20) } random_search = RandomizedSearchCV( clf, param_dist, n_iter=50, cv=5, scoring="roc_auc", random_state=42, n_jobs=-1 ) random_search.fit(X_train, y_train)

特征工程全套路

操作什么时候用代码
标准化 StandardScaler线性模型、SVM、神经网络(量纲敏感)(x - mean) / std
归一化 MinMaxScaler希望数据压缩到 [0,1](如图像像素)(x - min) / (max - min)
鲁棒缩放 RobustScaler有异常值时(用中位数和 IQR)(x - median) / IQR
One-Hot 编码无序类别(城市、血型)OneHotEncoder(sparse_output=False)
标签编码有序类别(学历、评分)LabelEncoder()
目标编码类别基数高(比如用户 ID)TargetEncoder(sklearn 1.3+)
多项式特征线性模型想拟合非线性关系PolynomialFeatures(degree=2)
方差过滤去掉不变特征VarianceThreshold(threshold=0.01)
互信息 / SelectKBest挑和标签最相关的 K 个特征SelectKBest(mutual_info_classif, k=20)

Scikit-learn 章节面试题

面试 · Scikit-learn

Q1. 过拟合和欠拟合怎么判断?分别怎么解决?

查看答案

过拟合:训练集好、测试集差(高方差)——加数据、简化模型、加正则、Dropout、早停。欠拟合:训练集和测试集都差(高偏差)——换更复杂模型、加特征、减少正则。

Q2. 偏差-方差权衡是什么?

查看答案

偏差:模型假设和真实规律的差距(欠拟合);方差:模型对训练集波动的敏感度(过拟合)。模型越复杂方差越高、偏差越低;总误差 = 偏差² + 方差 + 不可约误差,要找平衡。

Q3. 为什么分类任务用 StratifiedKFold?

查看答案

普通 KFold 随机切分,类别不平衡时某一折可能没有少数类。分层 K 折保证每折的类别比例和整体一致,评估更可靠。

Q4. 什么时候用标准化,什么时候不用?

查看答案

线性模型、SVM、KNN、神经网络对量纲敏感,必须标准化;树模型(决策树、随机森林、XGBoost)不敏感,可以不标准化。

Q5. Pipeline 的好处?

查看答案

① 防止数据泄漏(预处理只 fit 在训练折上);② 代码简洁;③ 和 GridSearchCV 配合时超参数搜索自动包含预处理步骤;④ 部署时一个对象端到端。