news 2026/9/8 21:03:47

ML-For-Beginners 分类入门实战:基于亚洲与印度菜系数据集理解分类原理并完成 SMOTE 数据平衡

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ML-For-Beginners 分类入门实战:基于亚洲与印度菜系数据集理解分类原理并完成 SMOTE 数据平衡

ML-For-Beginners 分类入门实战:基于亚洲与印度菜系数据集理解分类原理并完成 SMOTE 数据平衡

【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners

本文是 ML-For-Beginners 开源机器学习课程「4-Classification / 1-Introduction」单元的技术实践指南,核心主题是经典机器学习中的分类(classification)——一种与回归一脉相承的监督学习技术。我们将在课程配套的亚洲与印度菜系数据集上,从"二元分类 vs 多分类"的概念辨析出发,逐步完成数据导入、分布探查、特征筛选,并使用imblearnSMOTE技术对不均衡数据进行合成过采样平衡,最终产出可供后续分类算法直接使用的cleaned_cuisines.csv。读完本文,你将掌握分类问题的类型判定方法、数据清洗与类别不平衡处理的标准流程,以及它们背后的原理。

一、分类:监督学习的核心活动之一

在课程路线图中,你此前已经接触过两种预测技术:

  • 线性回归:帮你预测变量之间的关系,并推断一条直线附近新数据点的落点。例如预测"南瓜在 9 月与 12 月的价格分别是多少";
  • 逻辑回归:帮你发现"二元类别",例如在给定价格点下,"这只南瓜是橙色的,还是不是橙色的"。

分类(classification)则利用一系列算法,用另一种方式判定一个数据点所属的标签(label)或类别(class)。从统计分类(statistical classification)衍生而来的经典机器学习分类,会使用诸如smokerweightage之类的特征(features),去估计"患某病的可能性"这类结果。作为一种与回归练习类似的监督学习技术,你的数据是带标签(labeled)的,机器学习算法借助这些标签对数据集进行分类与预测,并把样本归入某个组或某种结果。

分类是机器学习研究人员与数据科学家最基本的工作之一——从"这封邮件是不是垃圾邮件"的二元判断,到借助计算机视觉完成复杂的图像分类与分割,把数据归入不同类别并针对它们提出问题是始终有用的能力。用更科学的方式表述:你的分类方法会构建一个预测模型,使你能够把输入变量与输出变量之间的映射关系建立起来。

上图展示了分类算法需要应对的二元 vs 多分类问题(课程"Hello, classifier"章节的配套信息图)。

二、二元分类与多分类:先判定问题的类型

分类通常分为两组:二元分类(binary classification)多分类(multiclass classification)

  • 二元分类:只有两个候选类。例如"此邮件是否为垃圾邮件"、"此南瓜是否为橙色"、"此人是否患病";
  • 多分类:候选类在两个以上。例如"这封信属于情书、账单还是投诉"。

课堂上的思考练习可以帮助你区分两者:设想一个关于菜系的数据集——

  • 多分类模型能回答什么问题?
  • 二元分类模型能回答什么问题?
  • 如果你想知道"某道菜是否可能使用葫芦巴(fenugreek)",这属于哪一类问题?
  • 如果给你一袋装有八角、洋蓟、菜花和辣根(horseradish)的食材,想判断"能否据此做出一道典型的印度菜",又属于哪一类问题?

在正式清洗、可视化和为机器学习任务准备数据之前,先厘清这些问题类型,会直接影响你后续的算法选型。

三、Hello, classifier:菜系数据集是一个多分类问题

本单元(共四课)使用的数据集是关于亚洲与印度各地美味菜系cuisines.csv,位于 4-Classification/data/cuisines.csv。我们希望向数据集提出的问题本质上是一个多分类问题:我们手头有多个潜在的国家菜系(泰国、日本、中国、印度、韩国),那么在给定一批食材的情况下,这些数据最可能匹配众多类别中的哪一类?

从数据结构的实际形态看,这个判断非常直观:cuisines.csv首行是列名,第一列cuisine为标签列,其余各列是成百上千种食材的名称。示例首行(表头)以,cuisine,almond,angelica,anise,...的形式出现,即每一行代表一道菜,菜系标签后跟随各食材的 0/1 取值。这就是一个典型的多分类输入——特征矩阵 + 类别标签。

Scikit-learn 依据要解决的问题类型,提供了多种分类算法(逻辑回归、SVM、决策树、朴素贝叶斯、K 近邻等)。后续两课会逐一介绍这些算法,而本课的任务是为它们准备好一份"干净、均衡"的数据。

四、数据准备第一步:清洗与查看数据集

4.1 安装并导入依赖

本课起始的空 notebook 位于 4-Classification/1-Introduction/notebook.ipynb。第一个要安装的是imblearn(imbalanced-learn)——它是 Scikit-learn 生态的配套包,用于更好地平衡数据:

pip install imblearn

随后导入数据处理、可视化所需的包,并从imblearn引入SMOTE

import pandas as pd import matplotlib.pyplot as plt import matplotlib as mpl import numpy as np from imblearn.over_sampling import SMOTE

4.2 读取 CSV 并检查数据形态

df = pd.read_csv('../data/cuisines.csv')

说明:在原课程目录结构中,notebook.ipynb位于 4-Classification/1-Introduction/ 下,因此相对路径../data/cuisines.csv指向的就是仓库中的 4-Classification/data/cuisines.csv。若在仓库根目录下运行,等价于pd.read_csv('4-Classification/data/cuisines.csv')

调用df.head()查看前五行:

df.head()

输出(截断展示):

| | Unnamed: 0 | cuisine | almond | angelica | anise | anise_seed | apple | ... | whiskey | white_bread | ... | yogurt | zucchini | | --- | ---------- | ------- | ------ | -------- | ----- | ---------- | ----- | --- | ------- | ----------- | --- | ------ | -------- | | 0 | 65 | indian | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | ... | 0 | 0 | | 1 | 66 | indian | 1 | 0 | 0 | 0 | 0 | ... | 0 | 0 | ... | 0 | 0 | | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |

再调用df.info()获取数据类型与内存占用信息:

df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 2448 entries, 0 to 2447 Columns: 385 entries, Unnamed: 0 to zucchini dtypes: int64(384), object(1) memory usage: 7.2+ MB

这份输出揭示了三个关键事实,也与仓库中的真实数据吻合:

  1. 样本量:2448 条样本记录(cuisines.csv实际为 2448 行数据 + 1 行表头);
  2. 列结构:共 385 列,其中 384 列为int64(食材的 0/1 编码),仅有cuisine一列为object(字符串类型的菜系标签);
  3. 无索引脏列Unnamed: 0是读入时多出的一列(源自 CSV 中残留的行序号),后续需要剔除。

五、探索菜系分布:先发现数据不平衡

5.1 绘制每类样本的柱状图

df.cuisine.value_counts().plot.barh()

输出即下图所示——菜系种类有限(5 类),但各类样本数量分布不均衡

由图可见:korean 样本最多(接近 800),而 thai 样本最少(约 250)。这种不均衡正是后续需要解决的问题。

5.2 精确统计各类别样本量

用布尔索引切分出每一菜系的子集,并打印其形状:

thai_df = df[(df.cuisine == "thai")] japanese_df = df[(df.cuisine == "japanese")] chinese_df = df[(df.cuisine == "chinese")] indian_df = df[(df.cuisine == "indian")] korean_df = df[(df.cuisine == "korean")] print(f'thai df: {thai_df.shape}') print(f'japanese df: {japanese_df.shape}') print(f'chinese df: {chinese_df.shape}') print(f'indian df: {indian_df.shape}') print(f'korean df: {korean_df.shape}')

输出:

thai df: (289, 385) japanese df: (320, 385) chinese df: (442, 385) indian df: (598, 385) korean df: (799, 385)

各菜系形状均为(n, 385)(与df.info()的 385 列一致),但 n 从 289 到 799 不等,最大与最小相差约 2.8 倍。如果不加处理,后续分类器会天然偏向样本多的类别(详见第七节),因此需要"平衡"它们。

六、发掘典型食材:特征工程与降噪

6.1 按菜系统计食材热度

深入数据内部,我们想知道每类菜系的"典型食材"。先编写一个工具函数create_ingredient_df():转置 DataFrame、剔除无用的cuisineUnnamed: 0列、按出现次数求和排序:

def create_ingredient_df(df): ingredient_df = df.T.drop(['cuisine','Unnamed: 0']).sum(axis=1).to_frame('value') ingredient_df = ingredient_df[(ingredient_df.T != 0).any()] ingredient_df = ingredient_df.sort_values(by='value', ascending=False, inplace=False) return ingredient_df

6.2 观察五类菜系的 Top10 食材

对每一菜系子集调用该函数并绘制水平条形图(barh()),即可直观比较彼此的特色食材:

thai_ingredient_df = create_ingredient_df(thai_df) thai_ingredient_df.head(10).plot.barh()
japanese_ingredient_df = create_ingredient_df(japanese_df) japanese_ingredient_df.head(10).plot.barh()
chinese_ingredient_df = create_ingredient_df(chinese_df) chinese_ingredient_df.head(10).plot.barh()
indian_ingredient_df = create_ingredient_df(indian_df) indian_ingredient_df.head(10).plot.barh()
korean_ingredient_df = create_ingredient_df(korean_df) korean_ingredient_df.head(10).plot.barh()

五张图对应的原始图片与可视化细节分别见 thai 图、japanese 图、chinese 图、indian 图 与 korean 图。这一步的价值在于:哪些食材在多个菜系中高频出现、从而"区分度"低,哪些食材只在单一菜系中高频出现、从而"判别力"强,为下一步特征筛选提供依据。

6.3 剔除造成混淆的共性食材

当多种菜系都高频使用某种食材时,它会干扰分类器对"国别来源"的判断。最常见的干扰项是米饭、大蒜、姜——几乎人人都爱它们。使用drop()移除这些共性食材列,同时去除cuisine(标签列将单独存放)与Unnamed: 0(无用列),得到特征矩阵feature_df

feature_df = df.drop(['cuisine','Unnamed: 0','rice','garlic','ginger'], axis=1) labels_df = df.cuisine # .unique() feature_df.head()

至此:

  • feature_df:只有食材编码列(约 380 列),不含标签;
  • labels_df:Series,保存每条样本的菜系标签;
  • 特征矩阵仍是不均衡的(行数未变),下一步用 SMOTE 进行样本层面的平衡。

七、用 SMOTE 平衡数据集:原理与实操

7.1 为什么不均衡数据有害

想象一个二元分类问题:如果绝大多数数据属于某个类别,机器学习模型会更频繁地预测该类别——仅仅因为它的样本更多。数据平衡会消除这种由样本量悬殊带来的系统性偏差,让分类器"公平"地学习每一个类别。对多分类的菜系数据集同样如此。

7.2 SMOTE 是什么

SMOTE 的全称是Synthetic Minority Over-sampling Technique(合成少数类过采样技术)。与简单复制少数类样本不同,SMOTE 通过在少数类样本之间做插值(interpolation)来生成全新的合成样本,从而在不引入过多重复数据的前提下扩充少数类。

7.3 调用 fit_resample 完成平衡

oversample = SMOTE() transformed_feature_df, transformed_label_df = oversample.fit_resample(feature_df, labels_df)

fit_resample()一次性完成"拟合 + 重采样":它会学习各类别在特征空间中的分布,然后为样本量不足的类别合成新样本,直到所有类别样本数一致。

7.4 验证平衡效果

分别打印新、旧标签计数进行对比:

print(f'new label count: {transformed_label_df.value_counts()}') print(f'old label count: {df.cuisine.value_counts()}')

输出:

new label count: korean 799 chinese 799 indian 799 japanese 799 thai 799 Name: cuisine, dtype: int64 old label count: korean 799 indian 598 chinese 442 japanese 320 thai 289 Name: cuisine, dtype: int64

可以看到,经过 SMOTE 处理后五个菜系的样本数被统一到 799,数据"干净、平衡,而且很美味"。这也与仓库中平衡后产物的真实规模吻合:4-Classification/data/cleaned_cuisines.csv 实际包含 3995 行数据(5 类 × 799)+ 1 行表头,而平衡前的 cuisines.csv 只有 2448 行数据。

7.5 合并标签与特征并导出

把平衡后的标签与特征按列合并回一个完整 DataFrame,join='outer'确保两侧行索引全部保留:

transformed_df = pd.concat([transformed_label_df, transformed_feature_df], axis=1, join='outer')

最后再检查一遍数据,并保存到数据目录供后续课程使用:

transformed_df.head() transformed_df.info() transformed_df.to_csv("../data/cleaned_cuisines.csv")

从仓库路径看,该文件最终写入 4-Classification/data/cleaned_cuisines.csv(课程同时提供了 R 版本 cleaned_cuisines_R.csv 与食材索引辅助文件 ingredient_indexes.csv)。这份新 CSV 将成为后续"分类器 1 / 分类器 2 / 应用"等课程训练模型的标准输入。

八、数据流向与产物对照(仓库证据)

把本课产出与仓库中的真实文件对应起来,可以清晰看到整条数据流水线:

阶段仓库中的对应文件说明
原始数据4-Classification/data/cuisines.csv2448 条样本、385 列(1 标签 + 384 食材特征),类别不均衡
特征清洗df.drop(['cuisine','Unnamed: 0','rice','garlic','ginger'], axis=1)剔除标签、无用索引列与共性干扰食材
类别平衡SMOTE().fit_resample(...)五类统一至每类 799 条
平衡产物4-Classification/data/cleaned_cuisines.csv3995 条样本,供后续课程复用

同时,仓库为本科目提供了多种语言与工具的完整解答资源,可用于对照验证你的 notebook 步骤:Python 解答见 4-Classification/1-Introduction/solution/notebook.ipynb,R 语言版本见 4-Classification/1-Introduction/solution/R/lesson_10-R.ipynb(对应 HTML 输出为 lesson_10.html)。如果你希望在 R 生态中完成同样的流程,原课程还提供 Julia 与 R 两个方向的替代实现:solution/R 与 solution/Julia。

九、延伸练习与课后巩固

9.1 挑战:为仓库其他数据集判定分类任务类型

本套课程还包含若干有趣的数据集。翻一翻各单元的data目录,判断哪些数据集适合做二元分类、哪些适合做多分类,并思考:如果面对这些数据,你会提出什么问题?可重点考察 2-Regression/data、3-Web-App/1-Web-App/data、5-Clustering/data 与 7-TimeSeries/data 中的数据集,比较它们与菜系数据集在"标签形态"上的差异。

9.2 深入研究 SMOTE

对照 assignment.md 的课后作业要求:在 Scikit-learn 文档中检索各类分类方法,为课程中的某个数据集匹配"一个可问的问题 + 一种分类技术",并整理成表格或文档。此外,深入阅读 SMOTE 的 API 文档,思考它最适合哪些使用场景、解决了什么问题,以及它可能的局限(例如对高维稀疏特征与极端离群点的处理方式),为下一课选择合适的分类器打下基础。

小结

本课以"亚洲与印度菜系溯源"这一多分类任务为主线,走通了分类学习的标准前置流程:判定问题类型(多分类)→ 读取并体检数据(2448 行 × 385 列)→ 发现类别不均衡(289~799)→ 探查与筛选特征(剔除 rice/garlic/ginger 等共性食材)→ 用 SMOTE 将五类统一平衡至 799 条 → 导出 cleaned_cuisines.csv 供下游算法使用。掌握了这套"清洗 + 平衡"方法论后,你就可以在下一课放心地把它交给各种分类器,检验"给出一组食材、判断其菜系来源"这一多分类任务的真实预测效果了。

【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 21:03:09

从陶瓷工业百强看京尚“市场与品质双轮驱动”的实战逻辑

前段时间陶瓷行业圈子里最热闹的一件事&#xff0c;就是新一届全国陶瓷工业百强名单出炉。京尚这个品牌不仅稳稳上榜&#xff0c;还成了榜单里被反复提及的“双轮驱动”典型——市场和品质两头都抓得硬。我做这行十几年&#xff0c;见过太多企业要么拼命冲销量把品质丢了&#…

作者头像 李华
网站建设 2026/9/8 21:01:01

Agent执行边界安全实践:从SandBox配置到软边界防护

1. 一个报错把我带到的话题&#xff1a;Agent 执行边界到底是什么 有段时间我运行一个自动分析项目时&#xff0c;日志里反复出现一段看起来很像代码写错了的报错&#xff1a;disabled no sandbox&#xff0c;接着就是 agent execution terminated due to error. 。起初我很不…

作者头像 李华
网站建设 2026/9/8 20:58:34

30 分钟搭好 ESP-IDF v5.4.1:ESP32 开发环境安装与自检指南

30 分钟搭好 ESP-IDF v5.4.1&#xff1a;ESP32 开发环境安装与自检指南 【免费下载链接】esp-idf Espressif IoT Development Framework. Official development framework for Espressif SoCs. 项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf 运行 install.…

作者头像 李华