【机器学习】(14)—— 数值特征处理入门

【机器学习】(14)—— 数值特征处理入门 数值特征处理入门特征向量、归一化与分桶文章目录数值特征处理入门特征向量、归一化与分桶1. 为什么模型前面还要加一道「数据处理」2. 特征向量模型到底吃进去的是什么2.1 为什么特征向量重要2.2 数字不一定就是数值特征3. 归一化先把量纲拉到一个频道3.1 最常见的两种方法3.2 汽车数据里的直觉3.3 长尾特征怎么办4. 分桶把连续数值切成区间4.1 为什么要分桶4.2 分桶也有代价4.3 怎么切桶更靠谱5. 清洗与离群点别让一个极端值带偏全场5.1 先问它真的是错的吗5.2 常见处理手段5.3 缺失值也属于清洗的一部分6. 什么样的数值特征算「好特征」6.1 有意义6.2 变化范围合理6.3 值要稳定6.4 能在训练和推理阶段一致获得7. 多项式变换让线性模型多一点弯度7.1 什么时候值得加7.2 什么时候要克制8. 可运行代码标准化、分桶、平方项9. 初学者最常见的几个坑10. 关键术语速查11. 延伸阅读12. 小结摘要前面的专栏一直在讲模型但真到项目里很多时间其实花在「把数据喂对」上。数值特征看起来最省心实际上也最容易埋坑量纲不统一、离群点太大、长尾分布太歪、缺失值乱填都会把模型带偏。本文结合汽车重量与油耗示例讲清特征向量、归一化、分桶、数据清洗与多项式变换。适合作为分类系列之后、特征工程系列的第一篇。1. 为什么模型前面还要加一道「数据处理」学完前面的线性回归、逻辑回归、ROC、Bias很多人会有一个错觉只要模型选对效果就差不了。我一开始也是这么想的后来才发现真正花时间的往往不是调学习率而是处理输入数据。尤其是数值特征看起来只是一些数字实际上里面有很多坑车重用千磅发动机排量用升数值尺度完全不同一列数据大部分在 010突然混进一个 10000某些特征分布极歪绝大多数样本挤在一头年龄、收入、里程看起来都是数字但不一定适合直接原样喂给模型这也是为什么数据处理在机器学习里一直是单独的重点。模型只是后半段前半段得先保证输入是「能学」的。一句话概括模型学不学得好先看你给它的数是不是像样。2. 特征向量模型到底吃进去的是什么模型不会直接理解「这是一辆车」。它吃进去的是一串数字也就是特征向量Feature Vector。比如一辆车可以写成x ( weight , horsepower , displacement ) \mathbf{x} (\text{weight}, \text{horsepower}, \text{displacement})x(weight,horsepower,displacement)如果只用重量一个特征那么x ( 3.43 ) \mathbf{x} (3.43)x(3.43)如果再加上马力和排量x ( 3.43 , 150 , 2.8 ) \mathbf{x} (3.43,\ 150,\ 2.8)x(3.43,150,2.8)特征含义单位weight车重千磅horsepower马力hpdisplacement排量L2.1 为什么特征向量重要因为模型看到的世界就是这串数字。如果你漏掉关键特征模型就只能「盲猜」如果你把有问题的特征直接喂进去模型学出来的规律也会很怪。拿汽车数据说若只给车重模型能学到「越重通常越不省油」若再加排量、马力模型就有机会学得更细。但前提是这些特征本身要干净、尺度合理、没有明显异常值。2.2 数字不一定就是数值特征这里也要提醒一个初学者常犯的错看起来像数字不一定就该当数值特征处理。比如邮政编码、身份证前几位、学校编号这些虽然长得像数字但本质更像类别标签不应该拿去做加减乘除。像邮编这种「数字外观、类别本质」的数据应当按类别特征处理而不是按数值特征处理。3. 归一化先把量纲拉到一个频道很多模型尤其是线性模型、神经网络、KNN一旦输入特征的尺度差异太大训练就容易别扭。比如车重是3.43马力是150年里程可能是120000这三个数字放在一起里程会在数值上「压住」其他特征哪怕它未必最重要。所以通常要先做归一化Normalization。3.1 最常见的两种方法1. Min-Max 归一化把特征压到 01x ′ x − x min ⁡ x max ⁡ − x min ⁡ x \frac{x - x_{\min}}{x_{\max} - x_{\min}}x′xmax​−xmin​x−xmin​​适合知道上下界、且想保留相对位置的时候。2. Z-score 标准化让特征变成「均值为 0、标准差为 1」x ′ x − μ σ x \frac{x - \mu}{\sigma}x′σx−μ​更常见也更适合很多通用模型。方法结果适合什么时候Min-Max压到 01边界清晰、要保留比例关系Z-score以均值为中心通用默认选项3.2 汽车数据里的直觉若车重分布在2.3~4.4马力分布在70~220原样喂进去时两个特征的数值范围完全不是一个级别。标准化后模型在更新参数时不会总被「大数值列」牵着走。你可以把它想成几个人一起开会有人拿着扩音器有人声音很小。归一化就是先把每个人的麦克风调平再讨论谁说得更有道理。3.3 长尾特征怎么办收入、浏览次数、订单金额这类特征常常不是均匀分布而是长尾大部分样本很小少量样本特别大。这时常见做法是先做对数变换再考虑标准化x ′ log ⁡ ( 1 x ) x \log(1 x)x′log(1x)这样可以把特别夸张的大值压回来一些避免模型被极端值牵着跑。4. 分桶把连续数值切成区间有时连续数值未必直接用最好可以先切成几个区间这就是分桶Binning。比如把年龄切成0181930314546车重也可以分成轻型车中型车重型车4.1 为什么要分桶分桶不是为了「看起来高级」而是因为它在某些场景下确实有用增强可解释性比起说「车重 3.43」很多业务方更容易理解「中型车」缓解噪声相近数值被放进同一桶减少微小波动的影响表达非线性关系某些变量不是平滑变化而是到某个区间后规律突变4.2 分桶也有代价分桶不是白赚的。你把连续值切成桶相当于扔掉了一部分细节。比如 3.01 和 3.49 都落进「中型车」但它们其实并不完全一样。桶切得太粗信息会损失切得太细又会变回原始连续值的问题。所以我通常把分桶当成一种业务解释工具或者是在明显非线性时的工程手段而不是默认第一选择。4.3 怎么切桶更靠谱常见三种思路方法做法特点等宽分桶每个桶宽度一样简单直观等频分桶每个桶样本数差不多更稳不容易某桶太空业务分桶按业务阈值切最好解释如果是写专栏、做演示业务分桶最容易讲明白如果是建模实验等频分桶往往更稳一点。5. 清洗与离群点别让一个极端值带偏全场数值特征最怕的不是普通噪声而是离群点Outliers。比如一列里程大部分在1万~20万突然来一个9999999或者一列年龄里冒出个-3。这些值要么录错了要么不符合业务常识直接喂给模型容易出事。5.1 先问它真的是错的吗不是所有大值都该删。有些极端值是真实存在的比如超级高消费用户、极重型卡车、超高客单价订单。如果你一上来就删除模型会失去对这类重要样本的认识。所以清洗时我通常按这个顺序想这个值业务上可能存在吗它是少数真实极端样本还是明显录入错误留着它对模型是帮助更大还是干扰更大5.2 常见处理手段手段适用情况删除明显错误值如负年龄截断 / 裁剪极端值太大影响训练对数变换长尾分布但值是真实的单独成桶极端区间本身有业务意义5.3 缺失值也属于清洗的一部分空值不处理模型通常没法直接吃。常见填法有用均值 / 中位数填用 0 填但要明确 0 的业务意义单独加一列is_missing初学阶段记住一点就够别默默填了一个数然后忘了这列本来缺过值。6. 什么样的数值特征算「好特征」好的数值特征通常有几个共同点。我把它总结成更容易记的版本6.1 有意义这个数得和任务有关。如果你预测车是否高效车重、排量、马力就有意义车主手机号最后一位通常没意义。6.2 变化范围合理若一列几乎所有样本都一样它对模型帮助很小。比如 99% 的样本某列都等于 1这列通常没什么区分度。6.3 值要稳定训练时一套规则部署时另一套规则模型会很痛苦。比如训练集里重量单位是「千磅」上线后接口传来的是「公斤」模型参数再好也白搭。6.4 能在训练和推理阶段一致获得这是工程里很重要的一点。某些特征在训练后才知道线上根本拿不到那它就不该出现在模型输入里。7. 多项式变换让线性模型多一点弯度有时一个数值特征和目标的关系不是直线而是有弯的。比如房价和面积、温度和耗电、速度和油耗常常不是「每增加一点影响都一样」。这时可以做多项式变换Polynomial Transform给模型补一些非线性信息。最常见的是加平方项x ( x , x 2 ) \mathbf{x} (x,\ x^2)x(x,x2)如果原来只有车重x现在就同时给模型xx^2这样线性模型虽然形式还是线性的但能拟合出弯曲关系。7.1 什么时候值得加散点图明显有弯你不想一下换成更复杂模型特征数不多还能控制住过拟合7.2 什么时候要克制多项式项一多特征数量会膨胀得很快还更容易过拟合。所以我的建议是先做清洗和归一化再考虑多项式别一上来就堆高次项。8. 可运行代码标准化、分桶、平方项importnumpyasnp# 汽车重量千磅weightnp.array([3.50,3.69,3.44,3.43,4.34,4.42,2.37],dtypefloat)# 1) z-score 标准化muweight.mean()sigmaweight.std()weight_z(weight-mu)/sigma# 2) 简单分桶轻 / 中 / 重binsnp.array([0.0,3.0,3.8,10.0])bucket_idnp.digitize(weight,bins)-1bucket_namenp.array([light,mid,heavy])# 3) 平方项weight_sqweight**2print(weight ,np.round(weight,2))print(z-score ,np.round(weight_z,3))print(bucket ,bucket_name[bucket_id].tolist())print(weight^2 ,np.round(weight_sq,3))运行环境Python 3.8NumPy。预期输出约weight [3.5 3.69 3.44 3.43 4.34 4.42 2.37] z-score [ 0.007 0.228 -0.063 -0.075 0.984 1.077 -1.157] bucket [mid, mid, mid, mid, heavy, heavy, light] weight^2 [12.25 13.616 11.834 11.765 18.836 19.536 5.617]这段代码把本篇最核心的三个动作都跑了一遍归一化、分桶、多项式扩展。后面真做项目时你可以把weight换成任意连续数值列去试。9. 初学者最常见的几个坑坑正解数字都当数值特征邮编、编号很多时候其实是类别原始数据直接喂模型先看尺度、分布、异常值所有特征都做同一种归一化要看分布长尾和常规列不一样分桶越细越好太细会把连续值的优势丢掉发现离群点就全删先判断它是真异常还是重要少数样本一上来就加x 2 , x 3 , x 4 x^2, x^3, x^4x2,x3,x4容易过拟合先把基础处理做好10. 关键术语速查术语一句话解释数值特征真正可比较大小、可做数学运算的特征特征向量一个样本的全部输入特征组成的向量归一化把不同尺度的特征拉到更可比的范围标准化常指 z-score减均值、除标准差分桶把连续值切成若干区间离群点明显偏离大多数样本的极端值多项式变换给模型添加x 2 x^2x2、x 3 x^3x3等高阶特征11. 延伸阅读资源适合看什么NumPy 官方文档数值数组处理、归一化与简单特征变换代码实践scikit-learn PreprocessingStandardScaler、MinMaxScaler、多项式特征等常用工具scikit-learn PolynomialFeatures多项式特征扩展的直接用法专栏第 0107 篇线性回归、梯度下降、逻辑回归基础专栏第 0813 篇分类、阈值、混淆矩阵、AUC、Bias、多分类12. 小结数值特征不是「把数字扔给模型」这么简单。真正好用的流程通常是先确认它是不是数值特征 ↓ 再看分布、异常值、缺失值 ↓ 需要时做归一化 / 分桶 / 对数变换 ↓ 最后才考虑更复杂的特征扩展模型当然重要但很多时候效果提升最明显的那一步恰恰不是换模型而是把输入数据处理得更像样。系列导航上一篇【机器学习】13—— 分类多分类下一篇预告类别特征处理入门——One-Hot、词表与特征交叉如果本篇对你有帮助欢迎点赞、收藏、关注博主机器学习专栏持续更新中下次更新不迷路。