机器学习入门(四):三种学习方式 + 数据从原料到模型
前情回顾:(二)讲了 f→g 的世界观------机器学习就是在噪声和特殊性的干扰下,从数据 D 里学出一个尽可能逼近真实规律 f 的近似函数 g。(三)讲了机器学习能做四类问题(分类/回归/聚类/降维),并简单提了「监督」和「无监督」两种学习方式。
这一篇做两件事:第一,把三种学习方式(监督、无监督、半监督)完整讲透------前两篇只是定性带过,这次给数学形式、给训练机制、给适用场景;第二,回到数据本身------数据从哪来、长什么样、怎么变成机器能吃的格式。整篇还是用房产中介这条故事线串起来。
一、机器学习的三种学习方式
机器学习按「训练数据有没有标签」可以划分成三类:监督、半监督、无监督。三类的差别不止是「有没有标签」这么浅------训练机制、能解决的问题、评估方法、工程代价全都不同。
1.1 监督学习(Supervised Learning)
定义
训练数据里每个样本都有对应的「正确答案」(标签),形式化地写成:
D={(x1,y1),(x2,y2),...,(xm,ym)}
其中 xi∈Rn 是第 i 个样本的特征向量, yi 是它对应的标签。
模型要做的事,是从这堆 (x,y) 配对中学出一个函数 g:
g:X→Y,g(xi)≈yi
这就是(二)里讲的 f → g 的世界观------只不过这里的 g 是用带标签的数据训练出来的。
核心机制:误差反馈
监督学习的核心训练循环:
-
模型对 xi 给出预测 y^i
-
计算预测和正确答案的差距: loss=L(yi,y^i)
-
根据 loss 反向调整模型参数
-
重复 1~3,直到 loss 收敛
回到房价场景:
-
输入 x = 面积=120, 楼层=3, 房龄=5, 地段=1.2
-
模型预测 y^ = 480 万
-
实际成交 y = 500 万
-
预测误差 loss=∣500−480∣=20 万------这里 L 是损失函数(Loss) ,本例用了最简单的一种:MAE (Mean Absolute Error,平均绝对误差------直接算差的绝对值)。常见的损失函数还有 MSE (Mean Squared Error,均方误差------把差先平方再平均,对大误差惩罚更重)和交叉熵(Cross-Entropy,分类任务专用,衡量两个概率分布之间的差距)。损失函数的选择会直接影响模型怎么"学"。
-
模型根据这 20 万的差距,反向调整自己的参数------下次同类房子的预测更靠近 500 万
🧮 数学坐标
- 学科归属:高等数学 → 函数与最优化;机器学习 → 损失函数与梯度下降
- 核心目标 : gmini∑L(g(xi),yi),其中 L 是损失函数
- 教材级展开 :留待后续《数学番外:损失函数与梯度下降》
两大类问题
监督学习按标签 y 的类型分两大类:
-
分类(Classification) : y 是离散类别, y∈{c1,c2,...,cK}。例:「优质房源 / 普通房源」二分类
-
回归(Regression) : y 是连续数值, y∈R。例:房价具体多少万
分类和回归用的算法、评估指标都不一样。
典型算法:线性回归、逻辑回归、决策树、随机森林、SVM、神经网络......机器学习一大半经典算法都在这一类下面。
最大代价:标注成本
监督学习的硬伤就一个------获得标签的代价。
回到 10 万套房源的例子:你想做「优质 / 普通」二分类,每条数据都要人工审核打标------这是真金白银的成本。这个代价直接决定了「能不能做」,也是后面促成半监督和主动学习诞生的根本原因。
1.2 无监督学习(Unsupervised Learning)
定义
训练数据没有任何标签:
D={x1,x2,...,xm}
模型只能从 x 本身去发现数据的结构。形式化地说,无监督学习是在估计数据的分布 P(X),或者寻找数据的内在几何结构。
能做的事
回到房源场景------这次一个标签都没有。无监督学习能做四件事:
1. 聚类(Clustering)
把相似的样本归到一起。比如对 10 万条房源做聚类,可能自然分出三群:
-
群 A:市中心 + 小户型 + 高单价
-
群 B:郊区 + 大户型 + 低单价
-
群 C:老城区 + 中户型 + 中单价 + 高房龄
注意------是模型自己根据相似度发现的群组,不是你告诉它有这三群。
2. 降维(Dimensionality Reduction)
把高维特征压缩到低维,保留主要信息。
比如描述房子用了 50 个特征,但其中很多是高度相关的(建筑面积和套内面积、卧室数和房间总数、地段评分和房价指数)。降维之后可能用 5 个「主成分」就能表达原始 50 维的 90% 信息------便于可视化、加快后续建模、减少过拟合。
🧮 数学坐标
- 学科归属:线性代数 → 特征值与特征向量、矩阵的特征分解
- 核心:PCA(主成分分析)通过协方差矩阵的特征分解,找出方差最大的几个方向
- 教材级展开:留待后续《数学番外:特征值分解与 PCA》
3. 密度估计(Density Estimation)
估算样本在特征空间里的密度分布。
应用:异常值检测。市场上 99% 的房子都集中在某个特征区域,那种「5000 ㎡ + 单价 1 元」的房子落在密度极低的区域------大概率是数据错误或者欺诈挂牌。
4. 关联规则挖掘(Association Rule Mining)
发现特征之间隐含的共现模式。经典例子是「啤酒 + 尿布」------超市数据发现买尿布的人经常一起买啤酒。在房产场景里:买「学区房」的人经常一起搜索「教培机构」「重点小学排名」。
典型算法:K-Means、DBSCAN、GMM(聚类);PCA、t-SNE、UMAP(降维);Apriori、FP-Growth(关联规则)。
核心难点:评估
无监督学习最大的难处------没有标准答案就没法直接评估「对不对」。
监督学习里你可以用测试集算准确率:90% 对就是 90% 对。但无监督------你聚出 3 群房子,是好是坏?没人能直接告诉你。
常见的折中评估:
-
业务判断:让领域专家看聚类结果,是否符合直觉
-
间接指标:轮廓系数(Silhouette Coefficient)、Davies-Bouldin 指数等
-
下游任务验证:把无监督结果当作监督学习的输入特征,看下游任务的表现间接反推
1.3 半监督学习(Semi-Supervised Learning, SSL)
定义
训练集中少量样本有标签,大量样本没有标签:
D=有标签 {(x1,y1),...,(xl,yl)}∪无标签 {xl+1,xl+2,...,xm}
通常 l≪m------已标注样本数远小于未标注样本数。
为什么需要这一类------标注瓶颈
回到 10 万套房源的场景。如果要做「优质 / 普通」分类,需要给每一条打标签。可是打标签很贵:
-
人工审核:一个有经验的审核员一天最多打 200 条,10 万条要打 500 天
-
让业主自标:他们说自己的房子都是优质的
-
用成交结果倒推:很多房子还没卖,没有成交数据
最后你只标了 2000 条,剩下 9.8 万条只有原始数据没有标签。
这时候选监督学习------只用那 2000 条,太可惜,9.8 万条数据白白浪费。选无监督学习------完全不用那 2000 条标签信息,也浪费。半监督学习就是解决这种场景的折中方案。
半监督凭什么能成立------三个核心假设
半监督不是免费午餐。要让大量无标签数据对学习产生帮助,必须假设无标签数据和有标签数据遵循同一套底层规律。具体来说,半监督学习依赖以下三个假设(实际算法常常同时依赖其中多条):
1. 平滑假设(Smoothness Assumption)
如果两个样本 x1,x2 在特征空间里离得很近,那它们的标签 y1,y2 也应该接近。
房价例子:两套房子都在西湖区文一西路、都是 90 ㎡、都是 5 年房龄------它们的「是否优质」标签大概率一致。其中一套已知是优质房源,另一套大概率也是。
2. 聚类假设(Cluster Assumption)
如果一群样本能自然聚成一团(cluster),那同一团里的样本大概率属于同一类------等价说法是:决策边界倾向于穿过低密度区域。
房价例子:所有「市中心 + 学区 + 小户型」的房子在特征空间里挤成一团,这一团里只要有几条被标为「优质」,整团基本都可以推断为「优质」。
3. 流形假设(Manifold Assumption)
高维特征空间里,真实数据其实分布在一个低维的「流形」上。沿着这个流形走,标签是连续变化的。
🧮 数学坐标
- 学科归属:拓扑学 → 流形(Manifold)
- 直觉:高维数据的「骨架」通常是低维的。地球表面是二维的,但塞进三维空间里------这个二维曲面就是「流形」
- 教材级展开:留待后续番外
这三个假设彼此相关、但不互为严格的包含关系------平滑假设强调函数光滑性、聚类假设强调决策边界在低密度区、流形假设强调数据的几何结构。实际算法里它们经常同时起作用,不同方法侧重不同假设。
半监督的四类任务
按要做的事划分:
-
半监督分类:少量标签 + 大量无标签,学一个分类器
-
半监督回归:同上,但输出是连续值
-
半监督聚类:聚类时利用少量「必须同类 / 必须异类」的约束信息
-
半监督降维:降维时利用少量标签,让结果更可分
两面性:传统场景里冷门,但正是 LLM 时代的根基
在「传统结构化数据 + 经典机器学习」的工业场景里,半监督使用得并不主流,原因主要是:
-
抗干扰能力弱:无标签数据里只要混入少量错标或异常样本,整个学习方向就会被带偏
-
强依赖假设是否成立:三个假设都是假设。如果数据其实分成不连贯的子群体,平滑假设就不成立,SSL 甚至会比纯监督更差
-
工程上常被「主动学习」替代:与其费劲让模型用无标签数据,不如让模型自己挑出「最不确定的几百条」让人工去标------这叫主动学习(Active Learning),代价更低、收益更高
所以在传统结构化场景,工业上常见的是这一套组合拳:先标少量种子数据 + 主动学习扩充 + 监督学习训练。
但深度学习时代彻底改变了半监督的地位------BERT、GPT 这类大模型的训练范式(海量无标签语料预训练 + 少量标注微调)本质上就是半监督/自监督的工业级实践,是现代 LLM 体系的根基。所以「半监督工业上没人用」是个过时的判断,更准确的说法是:在传统结构化数据上不主流,但在文本、图像、语音等非结构化数据 + 深度模型的组合下,它就是当下的事实标准。
1.4 三种学习方式对比
把三种学习方式拼成一张表:
| 类型 | 标签情况 | 数据形式 | 典型任务 | 难点 | 类比 |
|------|---------|---------|---------|------|------|
| 监督学习 | ✅ 全有 | {(x,y)} | 分类、回归 | 标注成本高 | 做有答案的练习题 |
| 半监督学习 | 🟡 少量有 | {(x,y)} + {x} | 上面四类的「半」版本 | 假设是否成立 | 老师给了几道答案,剩下自己悟 |
| 无监督学习 | ❌ 全没 | {x} | 聚类、降维、密度、关联 | 难以评估好坏 | 自己整理资料找规律 |
1.5 还有第四类:强化学习
为了完整性提一下------还有第四种学习方式叫强化学习(Reinforcement Learning, RL)。
它和上面三种的根本区别是:训练数据不是事先准备好的固定集合,而是模型和环境交互产生的。模型每做一个动作,环境给一个「奖励信号」(正向或负向),模型根据长期累计奖励调整策略。
AlphaGo、自动驾驶、机器人控制都是强化学习的典型场景;近年用得最广的 RLHF(基于人类反馈的强化学习) 也属于这一类------大模型对齐的关键一环。
强化学习的数学框架(马尔可夫决策过程 MDP)和监督/无监督完全不同,这一系列暂不展开。
二、回到数据本身:模型的「食材」从哪来
学习方式讲完,接下来回到这个系列的主线------数据。一个机器学习项目从启动到上线,真正写算法的时间不超过 20%,剩下 80% 都在跟数据打交道。
2.1 数据的三个来源
| 来源 | 含义 | 房产中介场景的例子 |
|------|------|---------------------|
| 用户访问行为 | 用户在产品里做了什么 | 用户在 APP 里看了哪些房源、停留多久、收藏几次、画了几次圈选区域 |
| 业务数据 | 业务系统沉淀的记录 | 成交记录、签约时间、价格、看房次数、中介带看记录 |
| 外部第三方 | 别家提供的数据 | 地图 API 的周边设施、政府公开的房价指数、运营商的人流数据 |
不同来源的「干净度」差异很大:业务数据通常最干净(来自结构化数据库),用户行为数据次之(埋点容易缺失),第三方数据最复杂(格式各异、可能要花钱、可能不准)。
2.2 数据存储的角色分工
机器学习项目里常见的数据存储工具不止一种------MySQL、HDFS、HBase、Solr、Elasticsearch、Kafka、Redis 都可能同时出现在一个项目里。新手看一眼容易犯懵,其实它们各司其职:
| 存储 | 适合场景 | 房产中介场景的例子 |
|------|---------|---------------------|
| MySQL | 强结构化、需要事务、量不大 | 成交记录、用户账号 |
| HDFS | 海量原始日志,便宜,批处理 | 三年的点击日志归档 |
| HBase | 海量、稀疏、需随机读写 | 用户行为时间线(每个用户几千条行为 × 10 亿用户) |
| Elasticsearch / Solr | 全文检索、模糊查询 | 「西湖区 三居 朝南」这种关键词搜房 |
| Kafka | 流式数据缓冲,生产消费解耦 | 实时点击事件 → 推荐系统 |
| Redis | 内存缓存,毫秒级响应 | 首页热门房源列表 |
Flume + Kafka 通常组合使用:Flume 从各个业务服务器收集日志、推送到 Kafka 队列,下游消费者按需消费------这是大数据采集的事实标准架构。
2.3 没业务数据时去哪找公开数据集
学习阶段最常用的几个:
-
UCI :archive.ics.uci.edu/ml/datasets... --- 学术界经典数据集大本营,鸢尾花、波士顿房价都在这
-
Kaggle :www.kaggle.com/competition... --- 比赛 + 数据集 + 内核分享,新手必逛
-
阿里天池 :tianchi.aliyun.com --- 中文场景比赛,更贴近国内业务
-
Hugging Face Datasets :huggingface.co/datasets --- NLP/多模态数据集的最大集散地
-
AWS Public Datasets :aws.amazon.com/public-data... --- 大规模数据,需要云资源
💡 入门阶段建议从 UCI 起手------数据干净、文档完善、规模小,跑起来快、不用等。
三、数据清洗:把脏原料洗干净
收到的数据基本不能直接用。常见操作分五类。
3.1 缺失值处理
房产数据里,「地段评分」可能 5% 的房源是 NULL(系统没采集到)。三种常见处理:
| 方法 | 做法 | 优缺点 |
|------|------|--------|
| 删除 | 直接扔掉有缺失的行 | 简单,但损失数据 |
| 填充 | 用均值 / 中位数 / 众数填 | 通用,但抹平真实分布 |
| 预测填充 | 用其他特征建小模型预测 | 精细,但成本高 |
工程上还有第四种:把缺失本身当作信号 ------多加一列 is_missing,把「这条数据缺了某个字段」这件事告诉模型。有时候「缺失」本身就有意义(比如「房主电话」缺失的房源往往是公盘房)。
3.2 异常值处理
(二)讲过的房子 C:100 ㎡ 市中心,成交价 50 万(少打了个 0)。怎么自动发现?
3-σ 原则 :假设数据近似正态分布,超过均值 ±3 个标准差的视为可疑值。判断条件是:若 ∣x−μ∣>3σ,则 x 被视为异常候选。
IQR 法 :用四分位距。设 Q1 是第 25 百分位、 Q3 是第 75 百分位, IQR=Q3−Q1,则异常值是落在以下区间外的点:
Q1−1.5×IQR,Q3+1.5×IQR
🧮 数学坐标
- 学科归属:概率论与数理统计 → 正态分布、四分位数
- 核心 :在数据近似正态时,±3σ 区间会覆盖约 99.7% 的真实样本 ,也就是说仅有约 0.3% 的真实样本会落在该区间之外 。所以 3-σ 原则是一种工程启发式:把超出范围的点当作"可疑值"做人工或规则复查,而不是直接断定"这条数据不真实"------真要做"是否真实"的概率判定,需要结合业务先验做贝叶斯反向推断。
- 教材级展开:参见后续《数学番外:正态分布与异常值检测》
3.3 错误值处理
不是「异常」,是格式错误。例如:
-
「房龄」列出现负数
-
「城市」列里「杭州」和「Hangzhou」两种写法并存
-
「面积」是字符串
"120平米"而不是数字120
这些只能人工写规则修。
3.4 多数据源合并
业务库的「房源 ID」和第三方地图 API 的「地址字符串」对不上------需要先做关键字匹配或地理编码,把两边的同一个房子合并到一行。
3.5 数据汇总
原始数据可能太细。用户每次点击都有一条记录,做模型时需要汇总成「每个用户过去 30 天点击次数」。这是从「事件粒度」到「用户粒度」的转换------也叫****聚合(aggregation)** **。
四、特征转换:从「人话」到「模型话」
数据洗干净了,下一步是把它变成模型能吃的格式------数值向量。
4.1 总原则:模型只认数字
绝大多数机器学习模型(神经网络、SVM、逻辑回归、线性模型......)只能接受一种输入: Rn 空间里的向量。一行特征 = 一个 n 维数字向量。
一个常见的例外是部分树模型实现 (LightGBM、CatBoost,以及 sklearn 较新版本的 HistGradientBoosting*)------它们可以原生支持类别特征,内部走专门的分裂逻辑,不必先 one-hot。但即使在这些场景里,纯数值向量仍然是绝大多数算法的统一输入格式,所以「转成数值」依然是特征工程的基本功。
🧮 数学坐标
- 学科归属 :线性代数 → 向量空间 Rn
- 教材级展开:参见《数学番外 01》Part 2
这就有了一个根本问题:现实数据里大量是非数字------「西湖区」是字符串、「南向 三居 学区」是文本、户型示意图是图像。要喂给模型,必须先转换。
4.2 七种常见转换
| 原始数据形态 | 转换方法 | 转换后形态 |
|-------------|---------|-----------|
| 类别(如朝向) | 1-of-k 哑编码 | 多个 0/1 列 |
| 文本(如描述) | 词袋 / TF-IDF / 词嵌入 | 词频或权重向量 |
| 图像 | 像素矩阵 / 卷积特征 | 高维数值向量 |
| 音频 | 傅里叶变换 → 频谱 | 频域数值向量 |
| 数值范围差异大 | 标准化 / 归一化 | 同量级数值 |
| 连续数值 | 分箱 | 离散类别 |
| 单个特征不够 | 特征组合 | 衍生新特征 |
下面挑两个最常用、需要展开的:哑编码 (处理类别)、TF-IDF(处理文本)。
五、深入:1-of-k 哑编码(One-Hot Encoding)
5.1 问题:为什么不能直接编成 1/2/3
房屋朝向有 4 种:南、北、东、西。最朴素的想法是编成数字:
| 原始 | 朴素编码 |
|------|---------|
| 南 | 1 |
| 北 | 2 |
| 东 | 3 |
| 西 | 4 |
但这会出大问题 ------模型会把 1/2/3/4 当作有大小、有距离的数值:在线性模型里"西−南"的差距(4−1=3)会被当作真实的数量级关系;在距离类算法(kNN、K-Means)里"西"和"东"被认为差 1、"西"和"南"差 3。但朝向只是类别,根本没有"大小"或"距离"的概念。
⚠️ 这个坑非常常见。直接用整数编码非有序类别,模型会被骗,输出毫无意义。
5.2 解法:每个类别一个独立维度
1-of-k 编码(也叫 One-Hot)的思路:给 k 个类别分配 k 个独立的维度,每个样本在自己所属的那个维度上是 1,其他维度是 0。
| 原始 | 南 | 北 | 东 | 西 |
|------|----|----|----|----|
| 南 | 1 | 0 | 0 | 0 |
| 北 | 0 | 1 | 0 | 0 |
| 东 | 0 | 0 | 1 | 0 |
| 西 | 0 | 0 | 0 | 1 |
四个类别在向量空间里两两正交(彼此垂直),地位平等,没有任何「谁大谁小」的歧义。
🧮 数学坐标
- 学科归属:线性代数 → 向量空间、基向量、正交性
- 核心 :one-hot 向量就是 Rk 空间的标准基向量 e 1,e 2,...,e k,两两正交(点积为 0)
- 教材级展开:参见《数学番外 01》Part 2.4
5.3 用房价数据走一遍完整例子
原始数据(截取前 5 条):
| 房源 ID | 面积 | 朝向 | 楼层类型 | 单价(万/㎡) |
|--------|-----|------|---------|--------------|
| H001 | 120 | 南 | 中楼层 | 5.5 |
| H002 | 90 | 东 | 低楼层 | 4.2 |
| H003 | 150 | 南 | 高楼层 | 6.8 |
| H004 | 80 | 北 | 低楼层 | 3.5 |
| H005 | 110 | 西 | 中楼层 | 4.8 |
「朝向」4 类,「楼层类型」3 类。哑编码后:
| ID | 面积 | 南 | 北 | 东 | 西 | 低 | 中 | 高 | 单价 |
|----|-----|----|----|----|----|----|----|----|------|
| H001 | 120 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 5.5 |
| H002 | 90 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 4.2 |
| H003 | 150 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 6.8 |
| H004 | 80 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 3.5 |
| H005 | 110 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 4.8 |
原本 4 列(不算 ID)变成 9 列。维度膨胀了,但每一列含义都干净。
5.4 哑编码的两个工程坑
坑 1:维度爆炸
如果有一个「城市」特征,包含全国 300 个城市,one-hot 后就是 300 维。再加「区」(每个城市 10 个区,共 3000 个),又是 3000 维。维度高了之后:
-
内存吃紧
-
训练变慢
-
容易过拟合(参考(二)讲的过拟合根源------维度高 = 更容易学到伪规律)
应对 :高基数(high cardinality)类别用更紧凑的编码方式,不直接 one-hot------常见两种:target encoding (目标编码,用该类别对应的目标变量平均值代替类别本身,例如用每个城市的平均房价代替城市名)和 embedding(嵌入,把每个类别学成一个低维稠密向量,相似类别的向量在空间里距离也近------是深度学习处理类别特征的标配做法)。
坑 2:共线性
严格说,在"每个样本必有且仅有一个类别"的前提下 ,k 个类别只需要 k-1 个维度------四个朝向只要知道是不是南、北、东,剩下不是这三个就是西。这种"k 个 one-hot 列加起来恒为 1"的关系叫完全共线性。
但要注意一个边界:如果允许「未知」或「缺失」朝向,则需要保留 k 列 + 一列 is_missing 标记。
完全共线性对不同模型的影响也不同:
-
无正则化的线性模型 (普通最小二乘线性回归、不带正则化的逻辑回归):参数估不准、协方差矩阵不可逆,需要用
drop_first=True删一列 -
带正则化的线性模型 (Ridge、L2-LR,也就是 sklearn
LogisticRegression的默认行为):正则项让解唯一、对共线性的鲁棒性强很多,保留全部 k 列影响有限 -
树模型(决策树、随机森林、GBDT):完全不受共线性影响,保留全部 k 列即可
六、深入:词袋 + TF-IDF
6.1 问题:怎么把「南向 三居 学区 装修」变成向量
房源描述是一段自然语言文本。要让模型用,必须变成数值向量。
最简单的做法叫词袋法(Bag of Words, BoW):
-
把所有房源描述里出现过的词,整理成一个「词表」
-
每条描述用一个向量表示,向量长度 = 词表大小
-
向量的每个位置存这个词在当前描述里出现的次数
举例,假设我们有 3 条房源描述:
-
D1:「南向 三居 学区 装修」
-
D2:「南向 两居 地铁 装修」
-
D3:「北向 三居 学区 老破小」
词表 = {南向, 北向, 两居, 三居, 学区, 地铁, 装修, 老破小},共 8 个词。
词袋向量:
| | 南向 | 北向 | 两居 | 三居 | 学区 | 地铁 | 装修 | 老破小 |
|--|----|----|----|----|----|----|----|------|
| D1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | 0 |
| D2 | 1 | 0 | 1 | 0 | 0 | 1 | 1 | 0 |
| D3 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 1 |
这就是词袋------简单粗暴,但有用。
6.2 词袋的两个缺点
缺点 1:忽略词序
「装修 不好」和「不好 装修」在词袋下完全一样,但语义相反。
缺点 2:所有词权重一样
「南向」和「装修」在房产语料里到处都是------几乎每条描述都有。这种词没什么区分度,但在词袋里和「老破小」这种稀有词权重一样。
TF-IDF 解决的是第二个缺点。
6.3 TF-IDF:给词加权重
直觉是这样的:
-
一个词在当前文档里出现得多 → 这个词对当前文档重要
-
一个词在所有文档里到处都是 → 这个词没区分度,权重该低
把这两点变成数学:
TF(Term Frequency,词频)
TF(t,d)=Ndnt,d
其中 nt,d 是词 t 在文档 d 中出现的次数, Nd 是文档 d 的总词数。
为什么要除以总词数?归一化------让长文档和短文档可比。一篇 100 词的文档里「南向」出现 3 次,和一篇 1000 词的文档里「南向」出现 3 次,重要性不一样。本质上这是把原始词频向量按 L1 范数归一化(因为词频之和 = 总词数 = L1 范数)。
🧮 数学坐标
- 学科归属:线性代数 → L1 范数与归一化
- 教材级展开:参见《数学番外 01》Part 3
IDF(Inverse Document Frequency,逆文档频率)
IDF(t)=logdf(t)N
其中 N 是文档总数, df(t) 是包含词 t 的文档数。
为什么是 log?
🧮 数学坐标
- 学科归属:高等数学 → 函数 → 对数函数
- 核心:log 把指数差距压成线性差距
- 教材级展开:参见《数学番外 01》Part 1
如果不套 log,假设语料库有 10000 篇:
-
「南向」在 9000 篇出现 → 原始比值 = 10000/9000 ≈ 1.11
-
「老破小」在 100 篇出现 → 原始比值 = 10000/100 = 100
-
「凶宅」在 1 篇出现 → 原始比值 = 10000/1 = 10000
差距是 1 : 100 : 10000------稀有词的权重会压倒一切,模型一看见「凶宅」就直接定调,其他词全失效。
套上 log(取自然对数)后:
-
ln(10000/9000)≈0.105
-
ln(10000/100)≈4.6
-
ln(10000/1)≈9.2
差距压成 0.1 : 4.6 : 9.2,稀有词仍然权重更高,但不再独霸天下。这是 log 在所有「量级跨数量级」场景的核心作用。
TF-IDF
TF-IDF(t,d)=TF(t,d)×IDF(t)
一个词既在当前文档里频繁出现,又在所有文档里相对稀有时,TF-IDF 才高------这正是「对当前文档有强代表性」的词。
6.4 完整算一遍:3 条房源描述
接前面 3 条 + 8 词的例子, N=3。
Step 1:算 TF
每条描述都是 4 个词、每个词出现 1 次,所以 TF = 1/4 = 0.25。
| | 南向 | 北向 | 两居 | 三居 | 学区 | 地铁 | 装修 | 老破小 |
|--|------|------|------|------|------|------|------|--------|
| D1 | 0.25 | 0 | 0 | 0.25 | 0.25 | 0 | 0.25 | 0 |
| D2 | 0.25 | 0 | 0.25 | 0 | 0 | 0.25 | 0.25 | 0 |
| D3 | 0 | 0.25 | 0 | 0.25 | 0.25 | 0 | 0 | 0.25 |
Step 2:算 IDF(用自然对数 ln)
| 词 | df | IDF = ln(3/df) |
|----|----|----------------|
| 南向 | 2 | ln(1.5) ≈ 0.405 |
| 北向 | 1 | ln(3) ≈ 1.099 |
| 两居 | 1 | ln(3) ≈ 1.099 |
| 三居 | 2 | ln(1.5) ≈ 0.405 |
| 学区 | 2 | ln(1.5) ≈ 0.405 |
| 地铁 | 1 | ln(3) ≈ 1.099 |
| 装修 | 2 | ln(1.5) ≈ 0.405 |
| 老破小 | 1 | ln(3) ≈ 1.099 |
Step 3:TF × IDF
| | 南向 | 北向 | 两居 | 三居 | 学区 | 地铁 | 装修 | 老破小 |
|--|------|------|------|------|------|------|------|--------|
| D1 | 0.101 | 0 | 0 | 0.101 | 0.101 | 0 | 0.101 | 0 |
| D2 | 0.101 | 0 | 0.275 | 0 | 0 | 0.275 | 0.101 | 0 |
| D3 | 0 | 0.275 | 0 | 0.101 | 0.101 | 0 | 0 | 0.275 |
观察 D3:稀有词「北向」「老破小」权重 0.275,常见词「三居」「学区」只有 0.101------稀有词权重明显更高,达到了我们想要的效果。
6.5 工程实现的三个坑
坑 1:sklearn 公式和教科书不完全一样
sklearn 用的是平滑版本:
IDFsklearn(t)=log1+df(t)1+N+1
-
分子分母都 +1:相当于"虚拟添加一个所有词都至少出现一次的文档",避免除零、做拉普拉斯平滑
-
最后 +1:让出现在所有文档的词 IDF 也不为 0(避免被完全消除)
跑代码时你的手算结果和 sklearn 输出对不上,多半是这个原因。
坑 2:log 的底数
教科书有的用 ln(自然对数),有的用 log₁₀,有的用 log₂。不影响相对排序,只影响数值大小------同一份代码内部统一即可。
坑 3:稀疏存储
实际语料库词表往往是几万到几十万维,一篇文档只用到几百个词。直接存稠密矩阵会爆内存。sklearn 默认返回稀疏矩阵 (scipy.sparse),只存非零位置。
python
from sklearn.feature_extraction.text import TfidfVectorizer
docs = [
"南向 三居 学区 装修",
"南向 两居 地铁 装修",
"北向 三居 学区 老破小",
]
vectorizer = TfidfVectorizer(token_pattern=r"\S+") # 默认按空格切词
X = vectorizer.fit_transform(docs)
print(vectorizer.get_feature_names_out())
print(X.toarray()) # 转成稠密矩阵看一眼
跑出来的数值会和我们手算的略有差异------正是因为 sklearn 用了 1+df1+N+1 平滑公式,并且默认对 TF-IDF 向量做了 L2 归一化(每行变成单位向量)。
总结
把这一篇串起来:
text
三种学习方式(监督 / 半监督 / 无监督)------ 按标签情况划分
↓
数据从哪来(业务 / 行为 / 第三方)
↓
存储分工(MySQL / HDFS / HBase / Kafka...)
↓
数据清洗(缺失 / 异常 / 错误 / 合并 / 汇总)
↓
特征转换(让模型能吃)
├── 哑编码 → 类别 → 0/1 正交向量
└── TF-IDF → 文本 → 权重向量
↓
[准备好喂给模型的干净数据]
数据准备完了,下一步就是把它喂给算法、训练出模型------以及更关键的一件事:评估模型考得好不好。