首页
/ ML-For-Beginners 分类进阶:五种分类器在亚洲菜系数据集上的对比实战

ML-For-Beginners 分类进阶:五种分类器在亚洲菜系数据集上的对比实战

2026-09-05 11:51:28作者:宣聪麟

本篇是 ML-For-Beginners 第 4 章「分类(Classification)」系列的第二课,教你沿着 scikit-learn 官方模型选择地图,在同一个菜系数据集上依次构建 Linear SVC、K-Neighbors、SVC、Random Forest 和 AdaBoost 五种分类器,并通过精确的 precision/recall/f1 报告对比它们的表现差异。读完本篇,你将掌握"数据画像 → 按决策路径选型 → 统一评测框架 → 依据指标做分类器取舍"的完整实战流程,并理解每个关键参数(C、kernel、probability、n_estimators 等)对模型质量的影响。

scikit-learn 算法选择地图

课程定位与数据基础

本课是 4-Classification 分类系列中"Yet other classifiers"一节,承接 2-Classifiers-1 前一课 中已完成的线性与对数分类器训练,重点转向更多数值型分类方案,以及"选择某个分类器而非另一个"所带来的实际影响(ramifications)。

课程对数据的前置假设是:你已在该系列的根目录 data 文件夹中准备好了清洗后的数据集 cleaned_cuisines.csv。实际检查该文件可以确认它的规模与结构:

  • 3995 条菜谱记录,382 列:1 列原始索引(Unnamed: 0)、1 列目标标签 cuisine、其余 380 列食材特征almondangelica……zucchini),均为 0/1 的二值编码;
  • 五个类别完全均衡:chineseindianjapanesekoreanthai799 条。

课程随附的 notebook.ipynb 已经预置了数据加载和 X/y 划分,模型构建时可以直接使用。它的前三个代码单元格分别完成:

import pandas as pd
cuisines_df = pd.read_csv("../data/cleaned_cuisines.csv")
cuisines_df.head()
cuisines_label_df = cuisines_df['cuisine']
cuisines_label_df.head()
cuisines_features_df = cuisines_df.drop(['Unnamed: 0', 'cuisine'], axis=1)
cuisines_features_df.head()   # 5 rows x 380 columns

也就是说,进入本课时的起点是:cuisines_features_df(380 个食材特征)与 cuisines_label_df(菜系标签)两个 DataFrame 已就绪。

用 scikit-learn 模型选择地图决定建模路径

在上一课中我们借助微软的 cheat sheet 了解过分类任务的可选方案;scikit-learn 提供了一张粒度更细的算法选择地图(即本文开头的插图),可以进一步帮助收敛候选的 estimator(分类器的另一个叫法)。当你已经清楚自己的数据画像时,这张地图非常有用——你可以沿着它的路径"走"到一个决策。

结合菜系数据集的具体情况,本课选择的决策路径如下:

  • 样本量 >50(3995 条,满足)
  • 要预测的是类别(predicting a category,走分类分支)
  • 标注数据(labeled data)
  • 样本数 <100K(满足)
  • ✨ 于是首先可以尝试 Linear SVC
  • 如果 Linear SVC 效果不佳,由于数据是数值型:
    • 可以尝试 ✨ KNeighbors Classifier
      • 如果还不理想,再尝试 ✨ SVC 和 ✨ Ensemble Classifiers

这条路径就是本课逐一试验五个分类器的依据,而不是一次性随意堆砌模型。

练习一:导入库并切分训练/测试数据

按照上面的路径,先导入所需的库:

from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import accuracy_score,precision_score,confusion_matrix,classification_report, precision_recall_curve
import numpy as np

导入中包含了本课要用的全部分类器(KNN、SVC、RandomForest、AdaBoost)、train_test_split/cross_val_score 两种划分验证工具,以及一套完整的分类评估指标。注意 LogisticRegression 也在此处导入,但本课的对比对象是上面四个"新面孔"。

然后切分训练集与测试集:

X_train, X_test, y_train, y_test = train_test_split(cuisines_features_df, cuisines_label_df, test_size=0.3)

test_size=0.3 意味着约 1199 条样本进入测试集(后文各 classification_report 中 support 总数均为 1199,与此吻合),其余约 2796 条用于训练。

分类器 1:Linear SVC(线性支持向量分类器)

SVC 属于支持向量机(Support-Vector Machine)家族的分类技术。在构造时需要关注几个核心参数:

参数 本课取值 作用
kernel 'linear' 决定如何划分标签空间;设为 linear 即强制使用线性核,构建 Linear SVC
C 10 正则化强度,调节各参数(误分类惩罚)的影响力
probability True 默认关闭;打开后才能获取概率估计
random_state 0 为获取概率而启用内部数据打乱时固定随机种子,保证可复现

练习:应用 Linear SVC

先创建一个分类器字典,后续试验将逐个往里添加:

C = 10
# Create different classifiers.
classifiers = {
    'Linear SVC': SVC(kernel='linear', C=C, probability=True, random_state=0)
}

接着用该模型训练并打印报告:

n_classifiers = len(classifiers)

for index, (name, classifier) in enumerate(classifiers.items()):
    classifier.fit(X_train, np.ravel(y_train))

    y_pred = classifier.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    print("Accuracy (train) for %s: %0.1f%% " % (name, accuracy * 100))
    print(classification_report(y_test, y_pred))

np.ravel(y_train) 的作用是把标签列压平为一维数组,满足 scikit-learn 对 y 的输入要求。这里的评测循环是一个可复用的框架:每新增一个分类器,只需往字典里加一项,同一套 fit → predict → accuracy_score → classification_report 流程自动覆盖它。

结果相当不错:

Accuracy (train) for Linear SVC: 78.6%
              precision    recall  f1-score   support

     chinese       0.71      0.67      0.69       242
      indian       0.88      0.86      0.87       234
    japanese       0.79      0.74      0.76       254
      korean       0.85      0.81      0.83       242
        thai       0.71      0.86      0.78       227

    accuracy                           0.79      1199
   macro avg       0.79      0.79      0.79      1199
weighted avg       0.79      0.79      0.79      1199

(一个值得注意的细节:打印文案虽写 "Accuracy (train)",但从源码看,计算实际使用的是 accuracy_score(y_test, y_pred),即测试集上的准确率。)

分类器 2:K-Neighbors Classifier(K 近邻)

K-Neighbors 属于机器学习中的"neighbors"家族,既可用于监督学习也可用于无监督学习。其方法是预设一定数量的点,把数据聚集在这些点周围,从而为数据预测出泛化后的标签。

上一课的分类器表现良好,但也许还能做得更好。尝试 K-Neighbors:在字典中 Linear SVC 条目后加逗号,然后新增一行:

'KNN classifier': KNeighborsClassifier(C),

这里复用了 C = 10 作为邻居数(即 n_neighbors=10)。结果略有下降:

Accuracy (train) for KNN classifier: 73.8%
              precision    recall  f1-score   support

     chinese       0.64      0.67      0.66       242
      indian       0.86      0.78      0.82       234
    japanese       0.66      0.83      0.74       254
      korean       0.94      0.58      0.72       242
        thai       0.71      0.82      0.76       227

    accuracy                           0.74      1199
   macro avg       0.76      0.74      0.74      1199
weighted avg       0.76      0.74      0.74      1199

对比 Linear SVC 的报告可以看出 KNN 的短板:korean 类别的 recall 只有 0.58(Linear SVC 为 0.81),precision/recall 的失衡拉低了整体表现。这说明"在模型选择地图上靠后的位置"并不等于更优,必须用指标说话。

分类器 3:SVC(默认核的支持向量分类器)

支持向量分类器是支持向量机(SVM)家族中用于分类与回归的方法。SVM 的核心思想是"把训练样本映射到空间中的点",并使两个类别之间的间隔最大化;后续数据被映射进同一空间后即可预测其类别。

SVM 最大间隔超平面示意

如上图所示,多条超平面(H1、H2、H3)都能分开两类样本,但只有把两侧最近样本点距离做到最大的那条间隔(margin)才是 SVM 追求的最优决策边界——这正是 kernel 参数发挥作用的地方:线性核(kernel='linear')对应图中这类线性可分情形,而更复杂的核则用于处理非线性分布。

继续往字典里添加(在 K-Neighbors 条目后加逗号,再加一行):

'SVC': SVC(),

注意这里调用的是无参 SVC(),与第一个分类器的区别在于使用默认核(RBF 高斯核)和默认 C,属于 scikit-learn 官方地图中"数值数据且 Linear SVC 不理想"时的升级选项。结果相当好:

Accuracy (train) for SVC: 83.2%
              precision    recall  f1-score   support

     chinese       0.79      0.74      0.76       242
      indian       0.88      0.90      0.89       234
    japanese       0.87      0.81      0.84       254
      korean       0.91      0.82      0.86       242
        thai       0.74      0.90      0.81       227

    accuracy                           0.83      1199
   macro avg       0.84      0.83      0.83      1199
weighted avg       0.84      0.83      0.83      1199

五个类别的 precision 全部升到 0.74 以上,测试集准确率从 Linear SVC 的 78.6% 提升到 83.2%,换用默认 RBF 核带来了约 4.6 个百分点的增益。

分类器 4:Ensemble Classifiers(集成学习——Random Forest 与 AdaBoost)

即使上一步的结果已经不错,课程仍要求把选择地图走到终点,尝试集成分类器中的 Random Forest 和 AdaBoost:

'RFST': RandomForestClassifier(n_estimators=100),
'ADA': AdaBoostClassifier(n_estimators=100)

集成方法的思想是把多个基础估计器(base estimators)的预测组合起来以提升模型质量。本课用到的两种机制差异明显:

  • Random Forest(平均法,averaging method):构建一片由"决策树"组成的"森林",并注入随机性以避免过拟合。n_estimators 参数即树的棵数,这里设为 100。
  • AdaBoost:先对一个数据集拟合一个分类器,然后针对同一份数据依次拟合该分类器的多个副本,通过聚焦被错误分类样本的权重、并调整下一次拟合来逐步纠正前者的错误( boosting 机制)。

完整字典此时为(与 solution/notebook.ipynb 中参考实现一致):

C = 10
classifiers = {
    'Linear SVC': SVC(kernel='linear', C=C, probability=True, random_state=0),
    'KNN classifier': KNeighborsClassifier(C),
    'SVC': SVC(),
    'RFST': RandomForestClassifier(n_estimators=100),
    'ADA': AdaBoostClassifier(n_estimators=100)
}

重新运行评测循环,结果非常好,尤其是 Random Forest:

Accuracy (train) for RFST: 84.5%
              precision    recall  f1-score   support

     chinese       0.80      0.77      0.78       242
      indian       0.89      0.92      0.90       234
    japanese       0.86      0.84      0.85       254
      korean       0.88      0.83      0.85       242
        thai       0.80      0.87      0.83       227

    accuracy                           0.84      1199
   macro avg       0.85      0.85      0.84      1199
weighted avg       0.85      0.84      0.84      1199

Accuracy (train) for ADA: 72.4%
              precision    recall  f1-score   support

     chinese       0.64      0.49      0.56       242
      indian       0.91      0.83      0.87       234
    japanese       0.68      0.69      0.69       254
      korean       0.73      0.79      0.76       242
        thai       0.67      0.83      0.74       227

    accuracy                           0.72      1199
   macro avg       0.73      0.73      0.72      1199
weighted avg       0.73      0.72      0.72      1199

把五个分类器放在一张表里,本课"选择分类器的影响"一目了然:

分类器 测试集准确率 最强类别 (f1) 最弱类别 (f1)
Linear SVC (C=10) 78.6% indian 0.87 chinese 0.69
KNN (neighbors=10) 73.8% indian 0.82 korean 0.72
SVC(默认 RBF 核) 83.2% indian 0.89 thai 0.81
RandomForest (100 棵树) 84.5% indian 0.90 chinese 0.78
AdaBoost (100 个估计器) 72.4% indian 0.87 chinese 0.56

几个可以据此得出的观察:

  1. 地图上的位置 ≠ 最终排名:Linear SVC 是路径上最先推荐的,却并非最优;Random Forest 走到了路径末端才登顶 84.5%。
  2. AdaBoost 在此数据上反而垫底:boosting 对错误样本加权的机制放大了对最难类别(chinese,recall 仅 0.49)的拟合偏差,说明"组合多个基估计器"并非无条件带来增益,机制要与数据分布匹配。
  3. 类别层面比总准确率更有信息量:五个分类器的总准确率相差仅 12 个百分点,但看 macro/weighted 均分与单类别 f1,差异的结构性来源很清楚——chinese 是所有模型的普遍短板,indian 则普遍最好。
  4. KNN 的邻居数直接来自正则化参数 C:本课把 C=10 复用为 n_neighbors,这属于"恰好能跑"的写法;n_neighbors 的合理取值应通过交叉验证单独确定,这也是课后练习要研究的方向之一。

🚀 挑战:参数实验(Parameter Play)

每种技术都有大量可调参数。课程布置的挑战是:研究上述各分类器的默认参数,思考调整这些参数会对模型质量产生什么影响。

配套的正式作业 assignment.md(Parameter Play)要求:采用本课的一种 ML 分类技术,调整各种参数值重新训练模型,并构建一个 notebook 解释为什么某些改动能提升模型质量、而另一些会使其退化,要求答案详尽。评分标准(Rubric)如下:

维度 优秀 合格 待改进
交付物 提交完整构建分类器、调整参数并用文本框解释变化的 notebook notebook 不完整或解释不佳 notebook 有 bug 或有缺陷

提示:可以在 VS Code 中借助 Intellisense 快速查看各分类器(如 SVCRandomForestClassifierAdaBoostClassifierKNeighborsClassifier)的完整参数签名与默认值,再决定实验变量,例如:SVC 的 Cgamma、KNN 的 n_neighborsweights、RandomForest 的 n_estimatorsmax_depth、AdaBoost 的 learning_rate

小结

本课完成了一次完整的分类器选型实践:依据数据画像(3995 条、380 维二值特征、5 类均衡)在 scikit-learn 模型选择地图上确定路径,用统一的可复现评测循环(固定 train_test_splitnp.ravel(y_train)classification_report)横向对比 Linear SVC、KNN、SVC、Random Forest、AdaBoost 五种分类器,最终得到 Random Forest 84.5%、SVC 83.2% 领先、AdaBoost 垫底的量化结论。它传递的核心方法论是:分类器没有绝对优劣,只有"与该数据集匹配与否"——而判断匹配与否的唯一可靠手段,是在同一份测试集上比较 per-class 的 precision/recall/f1,而非只看单一准确率。

学完本课可以继续:

登录后查看全文
热门项目推荐
相关项目推荐