ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

机器学习实战:从数据预处理到模型构建的完整流程

机器学习实战:从数据预处理到模型构建的完整流程 我收到过一条留言“Python基础语法已经会了机器学习的概念也翻过几篇入门文章但一拿到真实数据就懵完全不知道第一步该干嘛。”说实话这个问题太典型了。因为Python和机器学习中间的鸿沟不是“算法看不懂”而是缺一条能把知识与数据串起来的完整链路。今天我就打算用一份经典到不能再经典的鸢尾花数据集带着你从读数据开始一路走到模型评估和调参把数据预处理到模型构建这一整套流程跑通。这篇文章适合刚入门、懂一点Python但没实际跑过机器学习项目的人也适合想复盘一整套流程的老读者。1. 为什么机器学习项目80%的时间都耗在预处理上很多人误以为做机器学习就是选算法、调包、跑模型真正上手后才发现模型训练可能只占整个项目里很小的一段大头全在数据上。现实中的数据几乎不会干干净净摆在那里等着你训练它可能有缺失值、单位不统一、类别标签混乱、数值量纲差异极大等问题。要是跳过清洗直接训练轻则模型飘重则直接训练失败或者损失函数不收敛。1.1 先看整体流程给自己画一张地图一个标准机器学习项目大致会走过下面几个环节数据采集、数据清洗、特征工程、模型选择、模型训练、模型评估、调优、部署上线。入门阶段不需要真的去走部署但前面几步是必须练熟的。这里面的逻辑是模型只能从数据里学规律如果喂给它的数据是脏的、混乱的它学出来的也是错的。业内有一句话叫“垃圾进垃圾出”说的就是这个意思。插一句题外话很多人一上来就冲着神经网络或者深度学习去结果连Pandas的DataFrame都操作不熟练。我个人的建议是入门阶段先用传统机器学习把数据组织和模型评估的底子打好深度学习后面再慢慢补。工具不在多够用、能解决问题才关键。1.2 入门阶段的工具链选型内容要落地工具选型就得先明确。Python做机器学习有一套非常成熟的基础栈Pandas处理结构化数据的主要工具读表、筛选、分组、合并全靠它。NumPy数值计算的底层库Pandas底层也是基于NumPy的。scikit-learn最经典的机器学习库数据切分、预处理、模型训练、模型评估都在这一个库里完成。Matplotlib / Seaborn画图看数据分布辅助判断特征情况。入门阶段不建议碰PyTorch或TensorFlow这种深度学习框架。不是说它们没用而是它们的学习曲线更陡环境的依赖也更重很容易把初学者的注意力从“数据流”上带偏到“装环境”上去。1.3 用一份经典数据集作为练手对象我这次选择的是鸢尾花数据集。它小、干净、类别清楚简直是为入门量身定做的。它有150条样本每条样本有4个特征花萼长度、花萼宽度、花瓣长度、花瓣宽度标签是三种鸢尾花品种。拿到一份数据后通用的操作套路基本都是一样的所以我们可以把它当成一个练习模板来复现以后再接触更复杂的业务数据也能沿用同一套方法论。2. 数据预处理实操从脏数据到干净数据集很多人容易直接忽略这一步或者说“看一眼数据”之后就直接开训练。但真实世界的数据往往要复杂得多所以我们一步步来看预处理阶段具体要做什么以及每一步背后的原因。2.1 第一步不是跑模型而是把数据看明白拿到任何数据第一件事永远是“看”用代码去看它的结构。import pandas as pd # 读取鸢尾花数据集数据在scikit-learn里自带也可以从UCI下载 from sklearn.datasets import load_iris iris load_iris() # 转换成DataFrame方便观察 df pd.DataFrame(datairis.data, columnsiris.feature_names) df[target] iris.target # 查看前5行 print(df.head()) # 查看数据基本信息 print(df.info()) print(df.describe())这三行代码看起来基础但非常有价值。head()让你直观看到数据长什么样info()能告诉你每列的数据类型、有没有缺失值describe()则会给出每列特征的均值、标准差、最小值、四分位数、最大值让你对数值分布有个大体概念。之后建议顺手做一步可视化检查用直方图或者箱线图看一下每个特征的分布范围。import matplotlib.pyplot as plt df.hist(bins20, figsize(10, 8)) plt.tight_layout() plt.show()画图的意义在于某些问题比如极端离群值光看描述统计不一定能发现但图上一眼就能看出来。比如某个特征的直方图在极远处多出一个小孤岛那就是明显的异常值信号。2.2 缺失值处理填还是删想清楚了再动手真实数据几乎没有完全干净的缺失值是最常见的坑。缺失值处理没有万能标准要看业务背景和数据量决定。如果缺失比例很小比如不到总体样本的5%通常可以直接删除对应行。如果缺失比例特别大比如某列缺失70%那这列的实际信息量已经大打折扣通常建议直接删除整列而不是去强行补。如果填充常见的选择有均值/中位数填充适合数值型特征且数据分布不严重偏斜。偏斜严重时中位数更稳妥。众数填充适合类别型特征。前向/后向填充适合时序数据用上一个观测值补下一个缺失值。# 用中位数填充某列基于真实业务场景的通用做法 df[花瓣宽度].fillna(df[花瓣宽度].median(), inplaceTrue)这里要特别提醒填充缺失值的逻辑必须“先切分、再填充”否则容易造成数据泄漏这一点后面第4章会详细展开。这里先记住不要对全量数据一次性填充后再切训练集和测试集。2.3 特征编码让机器读得懂文字标签鸢尾花数据集的4个特征全是数值所以不需要做编码。但如果你用的是泰坦尼克号数据集Sex、Embarked这些列都是文本类别数据那就必须转换成机器能理解的数字。最简单的方式是用LabelEncoder它会把类别映射成0、1、2……比如“男”“女”编码成0和1。from sklearn.preprocessing import LabelEncoder encoder LabelEncoder() df[sex_encoded] encoder.fit_transform(df[Sex])但这里有个坑LabelEncoder对“有大小关系”的顺序类别比如“低、中、高”比较合适对纯粹的类别标签比如颜色、港口名直接分配数字会引入一个“1比0大”的伪顺序影响模型学习。所以真正推荐的是独热编码。独热编码的做法是把一个类别列拆成多列每列用0和1表示是否属于某个类别。用一句话概括它的逻辑让类别特征平行地存在谁也不比谁高一等。from sklearn.preprocessing import OneHotEncoder import pandas as pd onehot OneHotEncoder(sparse_outputFalse) encoded onehot.fit_transform(df[[Embarked]]) encoded_df pd.DataFrame(encoded, columnsonehot.categories_[0]) df_combined pd.concat([df, encoded_df], axis1)当然Pandas里也有现成的get_dummies()一行搞定效果等价。对于树模型这种对特征顺序不敏感的模型编码时不用太焦虑但对于线性模型、KNN、SVM这类距离敏感模型独热编码才比较稳妥。2.4 特征缩放量纲统一是必修课量纲差异是个容易被新手忽略的问题。比如某个特征是以“年”为单位取值范围在0到100而另一个特征是以“元”为单位取值范围在0到10000不少算法就会认为数值更大的特征更重要这就带歪了模型。比如K近邻算法算距离时量纲大的特征会在欧氏距离计算中占据主导等于模型把注意力全放在了一个特征上。再比如逻辑回归、神经网络这类用梯度下降优化的模型特征量级差异大会拖慢收敛速度。所以标准化不是可有可无的。主流做法有两种from sklearn.preprocessing import StandardScaler, MinMaxScaler # 标准化转为均值为0标准差为1 scaler StandardScaler() X_scaled scaler.fit_transform(X) # 归一化缩放到[0,1]区间 scaler_minmax MinMaxScaler() X_minmax scaler_minmax.fit_transform(X)StandardScaler更常用因为它基本不受离群值影响能保持数据的分布形态。MinMaxScaler则适合需要将特征压缩到一个固定范围的情况比如图像像素值。2.5 划分训练集与测试集别把考卷答案提前漏出来划分训练集和测试集是预处理环节里非常重要的一步。如果不做划分你用全量数据训练完再用全量数据自我评价得到的结果会过于乐观。就像学生拿着答案书复习再用同一份卷子考试考出来的分数当然没有参考价值。scikit-learn里提供train_test_splitfrom sklearn.model_selection import train_test_split X df.drop(target, axis1) y df[target] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy )test_size0.2表示留出20%的数据做测试random_state是随机种子固定下来后每次跑的结果才会一致stratifyy是分层抽样用来保证划分后训练集和测试集里的类别比例和原始数据一致这对分类任务非常重要。如果数据的类别很不平衡加这个参数能明显降低“某个类别在测试集里一个都没有”的风险。做完这一步前面的数据处理阶段才算完整。接下来才能进入真正让人兴奋的部分——建模型。3. 模型构建实操从基准模型到调优数据准备好之后就可以上模型了。入门阶段我更推荐走一条稳扎稳打的路线先选简单模型跑通流程再考虑复杂模型或调优。不要一上来就搞XGBoost或者深度学习先让整体框架跑起来才最重要。3.1 入门阶段该怎么选模型选模型有个基本原则先用简单模型把整体流程打通建立baseline基准结果再根据问题复杂度决定要不要升级。分类问题可以从这三个模型开始逻辑回归简单、可解释性强、训练效率高十分适合用来做第一个分类器。K近邻原理非常直观样本距离谁最近就归为哪一类很适合理解“模型是怎么做决策的”。决策树很好解释画出来就是一棵if-else嵌套的树容易发现特征的重要性。这三个模型虽然都简单但算法思想完全不同。跑完它们你对“分类器是怎么工作的”会有直观认识。型号选型不需要太深思熟虑因为你之后会通过交叉验证比较结果先跑一个baseline是核心。3.2 训练你的第一个分类模型以逻辑回归为例实际代码非常简洁from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier # 方式一逻辑回归 model_lr LogisticRegression(max_iter1000) model_lr.fit(X_train_scaled, y_train) # 方式二K近邻 model_knn KNeighborsClassifier(n_neighbors3) model_knn.fit(X_train_scaled, y_train)这里有一个前提如果前面做了标准化模型的训练集必须用scaler.transform转换后的数据。这是一个新手特别容易踩的坑我在第4章再详细讲。fit就是训练过程逻辑回归在拟合时通过梯度下降的方式寻找一组参数让预测结果和真实标签的差异最小化KNN则简单很多它并不真正学习参数而是在预测阶段开始“记忆”所有训练样本计算待预测样本与所有样本的距离。虽然KNN的fit没有过多计算但它的预测成本很高。3.3 模型评估准确率只是一个起点训练完之后就看效果了。一般会先用测试集给模型打分from sklearn.metrics import accuracy_score, classification_report, confusion_matrix y_pred model_lr.predict(X_test_scaled) print(准确率, accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred)) print(confusion_matrix(y_test, y_pred))准确率是大家最容易理解的指标但它并不是一个“足够全面”的指标——尤其是遇到类别不平衡的情况时。比如一个二分类数据集里正样本只占1%我如果是个“无脑预测负样本”的模型准确率也能达到99%。这种时候准确率的参考价值就不大了。所以需要看classification_report它给出的精确率、召回率和F1分数更能说明问题精确率预测为正的样本里真正是正类的比例。召回率实际为正的样本里被正确找出来的比例。F1精确率和召回率的调和平均是两者的综合平衡。混淆矩阵也很好用它的四个格子真正例、假正例、真负例、假负例能帮你快速定位模型偏爱哪类、哪类老分错。3.4 交叉验证与超参数调优如果只看一次训练-测试的划分结果很容易被随机性影响。比如这次的测试集恰好简单分数虚高下次遇到那20%测试数据里恰好都是难样本分数又会掉。为了稳一点业内普遍会用K折交叉验证把数据平均分成K份轮流拿其中1份做验证其余K-1份做训练最后取K次的平均结果。from sklearn.model_selection import cross_val_score scores cross_val_score(model_lr, X_scaled, y, cv5) print(交叉验证平均分数, scores.mean())如果还想进一步调参GridSearchCV是很好用的工具。它会自动遍历你给出的参数组合并用K折交叉验证逐个评估。from sklearn.model_selection import GridSearchCV param_grid { n_neighbors: [3, 5, 7, 9], weights: [uniform, distance] } grid GridSearchCV( KNeighborsClassifier(), param_grid, cv5, scoringaccuracy ) grid.fit(X_train_scaled, y_train) print(最佳参数, grid.best_params_) print(最佳交叉验证得分, grid.best_score_)GridSearchCV是入门阶段最值得掌握的调参工具虽然还不是最优效率的方法但胜在简单可靠。保存训练好的模型也是个好习惯。用joblib.dump()可以把模型持久化下来下次直接加载预测import joblib joblib.dump(model_lr, logistic_model.pkl) # model joblib.load(logistic_model.pkl)模型已经能跑通但真正要进入实操还是得把常见的坑认全。4. 常见错误、排查技巧与避坑实录这一部分是我最想对新人说的。数据预处理和模型训练代码都不难但掉过的坑基本都集中在下面几个地方。4.1 数据泄漏问题前面提了好几次数据泄漏它是新手会踩的最大坑之一。数据泄漏指的是模型在训练阶段接触到了不应该接触的信息导致训练时分数很好看一上真实场景就垮掉。最常见的两个错误一个是先对全量数据做fit_transform再做train_test_split另一个是先填充缺失值再切分。这两种行为的问题本质是一样的标准化时你已经偷偷把测试集的统计信息均值和标准差用了进去等于模型预看了测试答案。正确的流程是先切分训练集和测试集再对训练集fit_transform对测试集只transform。填充缺失值也同理要先用训练集的数据统计去填充测试集。# 错误演示 scaler StandardScaler() X_scaled scaler.fit_transform(X) X_train, X_test train_test_split(X_scaled, y, test_size0.2) # 正确做法 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)提示凡是要计算全局统计量的预处理步骤标准化、PCA、特征选择都要遵循这个“先切分后fit”的原则。4.2 类别不平衡问题假设你做一个二分类任务正样本只占5%模型只需把所有样本都预测为负类准确率就能到95%。但这不是你想要的结果。遇到这种场景不要只看准确率要重点关注少数类的召回率或F1分数。解决办法有几条路调整模型参数逻辑回归和SVM等模型通常有class_weightbalanced参数能自动给少数类更大的权重。重采样对少数类进行上采样复制样本或者对多数类下采样丢弃样本让训练数据类别接近均衡。选择更适合的评估指标比如用roc_auc_score、F1分数替代准确率。model LogisticRegression(class_weightbalanced)我用class_weight这个参数解决过一个银行风控场景里的“渠道率低”问题比手动上采样更省事效果也稳。建议入门阶段先掌握这个手段。4.3 欠拟合与过拟合的判断方法训练集表现差测试集大概率也差这是欠拟合意思是模型太简单没学够。解决办法是换复杂模型、加特征或减少正则化。麻烦的是另一种情况训练集分数高得离谱但测试集拉胯。这是过拟合——模型把训练数据里的噪声和细节全部背了下来缺乏泛化能力。你可以用下面的办法识别它比较训练分数和交叉验证分数如果训练分数明显更高就有过拟合嫌疑。观察学习曲线训练集和验证集分数在某个点之后逐渐分离说明模型开始背数据了。解决过拟合的办法也明确简化模型、增加正则化参数、增加训练数据、减少特征数量。我用过最有效的一招是给决策树限制max_depth和min_samples_split效果立竿见影。树一旦长太深就很容易过度记忆细节加深度限制后反而会提高测试集上的表现。4.4 新手最容易忽略的几件事除了上面说的坑还有一些操作层面的经验想分享一是要固定随机种子。机器学习里处处有随机性包括数据切分、模型初始化、参数搜索的随机抽样。不固定种子你每次跑出来的结果都不一样落在文档里也会一塌糊涂。固定一个random_state能让结果稳定可控可复现性本身也就是工作能力的一部分。二是不要迷信单次实验分数。一次分数高低有很多随机成分要用交叉验证的平均分来评估模型。如果调参后平均分提高了那才是真的提高。三是多看官方文档。scikit-learn的官方文档写得非常清楚不仅有每个函数的参数说明还有大量示例代码。遇到问题先查文档比到处刷零散教程有效率得多。5. 结尾最后再分享一点个人经验说了这么多我的体会是机器学习的门槛其实没有很多人想象的那么高但它有一条必须靠实操才能迈过去的坎——建立“数据意识”。你只有亲手处理过一批脏数据踩过填充顺序不对导致数据泄漏的坑试过不同编码方式对模型分数的影响才会真正理解“数据质量决定模型上限”这句话的含义。拿一份数据练手把它从读入、清洗、编码、切分、训练到评估完整地跑一遍剩下的都是在这个基础上的延伸。下次遇到真实业务数据时多花点时间先摸清数据分布、缺失情况和特征含义效果多半会好过你去纠结一个新算法。只要你完整跑通一次这个流程后面的路就会顺很多。
返回列表