小白python入门 - 67. 特征与预处理 小白python入门 - 67. 特征与预处理0. 写给同学的话上一课你已经会了划分训练/测试集用 sklearn 做第一次fit。这一课解决更「脏」也更关键的问题——喂给模型之前数据要怎么整理。现实表格里有的列是「年龄」、有的是「年收入元」、有的是「城市名」字符串。模型大多只吃数字不同列的数量级差几百倍时有的算法会「只盯着数字大的列」。还有一个新手最容易中的致命坑数据泄漏——测试集的信息偷偷混进了训练分数虚高上线翻车。项目说明本节目标理解泄漏坚持先划分再预处理会缩放、独热编码会用ColumnTransformerPipeline学完能干什么写出「可复现、可上线雏形」的预处理流水线而不是在 notebook 里东 scale 西 fillna预计时间阅读 45–70 分钟敲代码 40–50 分钟前置66 课的 train/test 与 fit/predictpandas 会选列更好心态预处理不是边角料经常比「换一个炫算法」更能涨分、更能防翻车。1. 生活里的例子 / 背景1.1 考试比喻标准分两门课数学满分 150体育满分 10。若直接把原始分加起来评「综合优秀」数学几乎决定一切。老师有时会先把各科变成「相对位置」标准分再比较——这和特征缩放的直觉类似让不同量纲的列在同一「起跑线」上说话。1.2 表格比喻城市名怎么进模型姓名城市月消费是否续费小明北京3201小红上海2800小刚北京4101「北京」「上海」是字符串。很多经典模型不能直接读汉字。常见做法独热编码one-hot——变成多列 0/1城市_北京城市_上海月消费…1032001280104101.3 泄漏比喻期末卷答案订进练习册你把全班期末成绩的平均分算出来再当成一列特征塞进「预测会不会挂科」的模型而且这个平均分是用包含测试同学的全班算的——等于练习册里夹带了期末信息。训练分会很好看换一届新生就穿帮。这就是数据泄漏data leakage的生活版。2. 核心概念白话 表格 相关图2.1 什么叫特征工程 / 预处理词大白话原始字段业务表里直接有的列年龄、城市、点击次数特征真正喂给模型的输入可能是原始列也可能是变换后的预处理缺失填充、缩放、编码、截断异常值等「打扫」步骤特征工程更宽一点还可以构造「近 7 日均值」「是否周末」等新列本课聚焦 sklearn 里最常用、最容易踩坑的缩放 类别编码 流水线并死磕泄漏。2.2 数据泄漏为什么「分数很好却上线很差」泄漏类型例子后果目标泄漏用「是否已解约」相关的事后字段预测「会不会流失」训练像开天眼训练/测试污染用全体数据算均值再划分或先 fit 缩放器再 split测试分虚高时间泄漏用「未来」的信息预测「过去/当下」回测很美实盘很惨铁律请背下来先train_test_split再在训练集上fit预处理器只对测试集transform。错误顺序全体数据 → StandardScaler.fit_transform(全体) → 再 split → 泄漏风险正确顺序全体数据 → split → scaler.fit(训练) → transform(训练) / transform(测试)2.3 数值特征缩放为什么有时必须做方法在干什么直觉常见类标准化 Standardization减均值、除标准差 → 大约变成均值 0、方差 1变成「相对平均偏了几档」StandardScaler归一化到区间 MinMax线性压到 [0,1] 等像把成绩映射到百分制另一套MinMaxScaler稳健缩放用中位数/四分位减轻极端值有土豪异常值时更稳RobustScaler哪些算法对缩放敏感先记结论较敏感常要缩放树模型等往往不强制k 近邻、SVM、逻辑回归、线性回归、神经网络、K-Means…决策树、随机森林、梯度提升树…原因大白话看「距离」或「系数大小」的算法会被「数字特别大的列」带节奏树模型是按阈值切分不太吃量纲。2.4 类别特征独热编码编码做法适合注意独热 One-Hot每个类别一列 0/1无顺序的类别红/绿/蓝类别特别多时列会爆炸序数编码红0, 蓝1…真有顺序小学/初中/高中乱给无序类别编号会误导线性模型sklearnOneHotEncoder。新版本常配合handle_unknownignore避免测试集冒出训练集没见过的类别就直接报错。2.5 ColumnTransformer不同列不同待遇一张表往往数值列 → 缩放或许再填缺失类别列 → 独热ColumnTransformer的大白话「按列名/列位置指定走哪条流水线」。2.6 Pipeline把步骤焊成一条管不用 Pipeline用 Pipeline训练时 scale 一遍预测时忘了 scalefit/predict自动按同一套步骤走交叉验证时容易泄漏后面 71 课验证时每折重新 fit 预处理更干净步骤散落十几个单元格一个对象保存/部署更省心黑话翻译黑话大白话fit只在训练数据上「学会」均值、类别列表、模型参数等transform用已学会的规则去变换数据不再偷看新数据重算均值fit_transform先 fit 再 transform通常只对训练集predict模型吐预测Pipeline 会先自动做前面的 transform3. 流程用图说清楚3.1 正确预处理流水线请对照这张脑图写代码原始表 X, y | v train_test_split ----------------- | | v v X_train, y_train X_test, y_test | | | preprocessor.fit(X_train) | | X_train_t transform | X_test_t transform | (只用训练学到的规则) | (禁止 fit 测试集!) v v model.fit(X_train_t, y_train) | | | -------- predict(X_test_t) --- | v 和 y_test 比分数用Pipeline后你甚至不用手写X_train_t一行pipe.fit(X_train, y_train)内部按顺序做完。3.2 本课手把手要搭的管子ColumnTransformer ├─ 数值列: SimpleImputer(中位数) → StandardScaler └─ 类别列: SimpleImputer(众数) → OneHotEncoder ↓ 分类器如逻辑回归缺失值空格子先填再缩放/编码避免 NaN 把后面步骤噎死。4. 手把手环境与代码4.1 环境pipinstall-Uscikit-learn pandas numpy4.2 反面教材先缩放全体再划分泄漏示范importnumpyasnpfromsklearn.datasetsimportload_breast_cancerfromsklearn.model_selectionimporttrain_test_splitfromsklearn.preprocessingimportStandardScalerfromsklearn.linear_modelimportLogisticRegressionfromsklearn.metricsimportaccuracy_score dataload_breast_cancer()X,ydata.data,data.target# 错误全体 fit_transform 再 split X_badStandardScaler().fit_transform(X)# 均值/方差用了未来的测试行X_tr,X_te,y_tr,y_tetrain_test_split(X_bad,y,test_size0.25,random_state42,stratifyy)clfLogisticRegression(max_iter5000)clf.fit(X_tr,y_tr)print(错误流程 测试准确率:,round(accuracy_score(y_te,clf.predict(X_te)),4))# 正确先 split再只在 train 上 fit X_tr,X_te,y_tr,y_tetrain_test_split(X,y,test_size0.25,random_state42,stratifyy)scalerStandardScaler()X_tr_sscaler.fit_transform(X_tr)# 只看训练X_te_sscaler.transform(X_te)# 测试只用训练的均值方差clf2LogisticRegression(max_iter5000)clf2.fit(X_tr_s,y_tr)print(正确流程 测试准确率:,round(accuracy_score(y_te,clf2.predict(X_te_s)),4))说明在这个干净的玩具集上两个分数可能很接近——泄漏不一定每次都让你「一眼假到离谱」但它在真实脏数据、时间序列、目标相关字段上会致命。习惯必须按正确流程写。预期输出形态两行「…测试准确率: 0.9x」之类的小数。4.3 自造混合类型表数值 类别importpandasaspdfromsklearn.composeimportColumnTransformerfromsklearn.pipelineimportPipelinefromsklearn.imputeimportSimpleImputerfromsklearn.preprocessingimportOneHotEncoder,StandardScalerfromsklearn.linear_modelimportLogisticRegressionfromsklearn.model_selectionimporttrain_test_splitfromsklearn.metricsimportclassification_report# 小玩具表假装是「是否续费」dfpd.DataFrame({age:[18,22,None,30,41,19,35,28,50,23],city:[北京,上海,北京,广州,None,上海,北京,广州,上海,北京],spend:[120,80,200,150,300,60,220,180,90,110],renew:[1,0,1,1,0,0,1,1,0,0],})Xdf[[age,city,spend]]ydf[renew]X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.3,random_state0)num_cols[age,spend]cat_cols[city]numeric_pipePipeline(steps[(imputer,SimpleImputer(strategymedian)),(scaler,StandardScaler()),])categorical_pipePipeline(steps[(imputer,SimpleImputer(strategymost_frequent)),(onehot,OneHotEncoder(handle_unknownignore)),])preprocessColumnTransformer(transformers[(num,numeric_pipe,num_cols),(cat,categorical_pipe,cat_cols),])clfPipeline(steps[(prep,preprocess),(model,LogisticRegression(max_iter1000)),])clf.fit(X_train,y_train)predclf.predict(X_test)print(classification_report(y_test,pred,zero_division0))读这段时抓住先 split再clf.fit(X_train, y_train)。数值走 imputer→scaler类别走 imputer→onehot。预测时clf.predict(X_test)不要对测试集再fit预处理。预期输出形态类似precision / recall / f1-score的小表格样本极少分数波动大重在流程。4.4 看 Pipeline 内部学到了什么可选调试# 训练后的独热类别oheclf.named_steps[prep].named_transformers_[cat].named_steps[onehot]print(类别取值:,ohe.categories_)# 数值缩放用的均值只来自训练集scalerclf.named_steps[prep].named_transformers_[num].named_steps[scaler]print(训练集均值:,scaler.mean_)print(训练集标准差:,scaler.scale_)预期打印出 city 的类别列表以及 age/spend 的均值、标准差数组。4.5 对比同一模型缩不缩放乳腺癌数据fromsklearn.neighborsimportKNeighborsClassifier X,yload_breast_cancer(return_X_yTrue)X_tr,X_te,y_tr,y_tetrain_test_split(X,y,test_size0.25,random_state42,stratifyy)defeval_knn(scale:bool):ifscale:pipePipeline([(scaler,StandardScaler()),(knn,KNeighborsClassifier(n_neighbors5)),])pipe.fit(X_tr,y_tr)returnaccuracy_score(y_te,pipe.predict(X_te))else:knnKNeighborsClassifier(n_neighbors5)knn.fit(X_tr,y_tr)returnaccuracy_score(y_te,knn.predict(X_te))print(kNN 不缩放:,round(eval_knn(False),4))print(kNN 有缩放:,round(eval_knn(True),4))预期有缩放的准确率通常明显高于不缩放kNN 靠距离量纲影响大。具体数字随版本可能略变看相对关系即可。4.6 保存整条管子避免「训练一套、预测另一套」importjoblib joblib.dump(clf,renew_pipeline.joblib)loadedjoblib.load(renew_pipeline.joblib)print(loaded.predict(X_test))# 与 clf.predict 一致上线时加载的是Pipeline 整体而不是只存模型系数却丢了编码映射。5. 常见坑大白话坑现象正确做法先全表 scale 再 split测试分偏乐观先 split再 fit 在 train用 y 的统计量当特征且含泄漏离奇的高分特征只能用「预测时刻能拿到」的信息类别先pd.get_dummies全表再 split测试独有类别列对不齐或泄漏类别分布用OneHotEncoder放进 Pipeline测试集fit了 Imputer/Scaler等于偷看测试分布测试集只transform忘了处理缺失报错或 silently 怪结果SimpleImputer或先清洗对树模型死磕 StandardScaler未必涨分白忙知道「何时需要缩放」手写十步预处理不封装预测漏一步Pipeline/ColumnTransformer把 ID、电话号码当数值特征模型学到编号规律去掉无意义标识列随机划分时间序列用未来预测过去按时间切分后续专题口诀Split first. Fit on train. Transform test. Pipe it.先划分训练集上 fit测试集只变换焊成管道。6. 对照表、小结6.1 工具对照你想做的事sklearn 常见选择划分数据train_test_split填缺失SimpleImputer标准化StandardScaler压到 0–1MinMaxScaler类别转 0/1OneHotEncoder不同列不同处理ColumnTransformer步骤串联 防漏步骤Pipeline6.2 fit / transform 谁对谁对象训练集测试集Imputer / Scaler / OneHotfittransform仅transform模型fitpredict不要 fit6.3 本节三句话泄漏会让分数说谎先划分再预处理。缩放让「看距离/系数」的算法公平对待各列独热让类别变成数字。ColumnTransformerPipeline是可复现工程的标配不是花架子。7. 小练笔由易到难题 1用一句话解释为什么StandardScaler不能在「包含测试集的全体数据」上fit题 2判断正误随机森林一定要先StandardScaler。说明理由。题 3下面顺序哪里错了请改正。读入数据 → StandardScaler.fit_transform(X) → train_test_split → 模型 fit题 4代码在 4.3 的 Pipeline 上把模型换成KNeighborsClassifier(n_neighbors3)跑通fit/predict。思考kNN 放进同一条含StandardScaler的管子是否合理题 5业务表多了一列label_update_time标签审核时间和days_to_churn_event距离流失事件还有几天。预测「30 天内是否流失」时这两列能当特征吗为什么参考思路1 会把测试分布信息渗进缩放参数。2 假树不依赖量纲。3 应先 split。4 合理kNN 需要缩放。5 高度可疑/通常不能含未来或目标相关信息易泄漏。8. 下一课预告68. 分类入门有了干净的划分与预处理我们正式看两个「分类」入门算法k 近邻近朱者赤邻居投票逻辑回归用 sigmoid 把分数压成 0~1 概率并学会读混淆矩阵、精确率/召回率/F1认清「准确率陷阱」类别极不均衡时 99% 也可能是废模型。引用与参考scikit-learn 预处理指南https://scikit-learn.org/stable/modules/preprocessing.htmlPipeline与复合估计器https://scikit-learn.org/stable/modules/compose.htmlColumnTransformerhttps://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.htmlOneHotEncoderhttps://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.htmlStandardScalerhttps://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html数据泄漏讨论sklearn FAQ / 常见陷阱相关阅读https://scikit-learn.org/stable/common_pitfalls.htmlGoogle MLCC - 数据准备与特征https://developers.google.com/machine-learning/crash-course/representationWikipedia - Data leakage (machine learning)https://en.wikipedia.org/wiki/Leakage_(machine_learning)再讲一个完整小故事泄漏有多坑你做「是否会逾期还信用卡」的预测。特征里有一列叫「下月是否逾期」——这是未来才知道的结果却被当成输入。模型会学成几乎直接抄答案测试分漂亮得吓人。上线后真实申请时根本没有「下月是否逾期」这列模型立刻傻眼。这就是泄漏的极端版把标签的变种、未来信息、测试集统计量喂进了训练。安全习惯口诀建议背下来先切分再统计变换只 fit 训练测试只 transform整条链放进 Pipeline。把口诀贴在显示器边上比背十个函数名更有用。课堂讨论题可分组 10 分钟如果老板只要一个数字「准确率」你怎么用两分钟说服他看混淆矩阵数据只有 80 条你还上机器学习吗为什么你更愿意维护100 条清晰业务规则还是一个 90 分但没人能解释的模型把讨论结论写在笔记里——比多抄 50 行 API 更接近真实工作。自我检测不看稿口头答我能不看笔记讲清本课最重要的一张图在说什么我能指出一段「错误代码」错在哪我能举一个生活例子对应本课任务类型我知道下一课大概要解决什么痛点全部打勾再进入下一课效率更高。附录给大一的 FAQ本课补充下面这些问题是第一次学本课内容时最容易卡住的地方。用白话再过一遍。Q1我是不是一定要背公式不必先背公式。你要先会讲故事输入是什么、输出是什么、模型在怕什么过拟合、泄漏、指标骗人。公式是为了精确表达故事故事通了公式只是翻译。Q2代码跑不通怎么办按这个顺序排查虚拟环境激活了吗提示符前有没有 .venv包装了吗python -c “import sklearn; print(sklearn.version)”报错最后一行是什么把Error 类型 最后一行记下来再搜路径、文件名、中文引号有没有混用仍不行换一个最小例子本课最前面的 10 行代码确认环境 OKQ3我和同学分数差很多是不是我很差不一定。可能是andom_state 不同、数据划分不同、指标不同、甚至泄漏导致虚高。先对齐评估协议再比分数。Q4这课和「人工智能 / ChatGPT」是什么关系ChatGPT 一类是很大的深度学习系统偏语言与对话。本课练的是表格/经典机器学习基本功分类、回归、评估、Pipeline。基本功会了你以后学深度学习或用大模型 API才知道自己在解决什么问题、如何公平比较。Q5我需要买 GPU 吗本阶段不需要。sklearn 在普通笔记本 CPU 上就够。GPU 主要是深度学习训练时才刚需。Q6作业要做到什么程度算合格最低标准能用自己的话讲清本课 3 个核心概念能跑通本课主线代码并看懂输出含义能指出至少 2 个常见坑小练笔完成一半以上鼓励全做Q7我想继续深入课外看什么优先官方文档对应章节见文末引用其次 ISLR 中文/英文入门章节。别一上来就啃很厚的证明书——容易劝退。本课概念速记卡可抄笔记本我用大白话怎么说对应术语用历史数据猜新情况机器学习 / 预测拿来学的那部分数据训练集假装是新客户的那部分测试集背答案背过头过拟合笨到学不会欠拟合偷看了考题数据泄漏步骤焊成一条龙Pipeline建议学习节奏时间做什么第 1 小时只读例子与图不写代码第 2 小时抄跑主线代码改一个参数观察变化第 3 小时做小练笔 写 5 句笔记之后隔一天不看稿子复述一遍记住大一阶段「讲清楚 跑得通 知道坑」比「一次记住全部 API」重要得多。