尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
天池二手车价格预测实战:从数据清洗到模型融合全流程
简介这份资源面向数据挖掘与机器学习学习者围绕天池「二手车交易价格预测」赛题提供一套完整的建模实践材料适合希望系统掌握回归预测流程、提升特征工程与模型融合能力的中高级选手。压缩包共22个文件约65.01MB涵盖csv与tsv格式的训练测试数据及预测结果、tfevents训练日志、json与joblib保存的模型与策略信息、cbm与txt模型文件以及py脚本和ipynb笔记覆盖从数据读取到模型落地的完整链路。已有170人学习下载。资源内含EDA与建模思路说明、多模型预测结果含CatBoost、LightGBM、XGBoost及Stacking融合输出与保存好的模型文件读者可据此复现特征处理、调参与集成策略理解缺失值、异常值处理及泛化能力评估等关键环节快速搭建可参考的二手车价格预测方案。1. 从一份二手车价格预测赛题说起它到底能帮你练什么如果你正在找一份能完整跑通「数据清洗 → 特征工程 → 模型融合 → 结果提交」全流程的实战项目二手车交易价格预测这份赛题资源值得放进你的练手清单。它来自天池平台本质是一个回归任务给定一辆车的品牌、里程、上牌时间、变速箱类型、排量、车型等字段预测它的成交价。数据里既有结构化字段也有大量匿名特征价格分布还带明显的长尾这就逼着你去处理缺失值、异常值、类别编码和偏态分布。它适合谁刚学完 pandas 和 sklearn、想找一个真实脏数据练手的新手以及想复习特征工程和树模型调参的老手。整份资源的核心价值不在「预测得多准」而在于它把工业界做回归任务时最常踩的坑都摆在了明面上。2. 数据摸底与清洗先搞清楚你手里有什么拿到数据别急着上模型先做三件事看字段类型、看缺失比例、看价格分布。这份赛题的数据通常包含训练集和测试集两个文件训练集带 price 列测试集不带。字段里既有数值型如里程、排量也有类别型如品牌、变速箱还有一堆匿名特征v_0 到 v_N。匿名特征不是没用它们往往是经过脱敏的统计量对树模型来说信息量不小。2.1 用 pandas 做一次快速体检先跑一段摸底代码把数据的基本情况摸清楚。这一步不需要任何花哨技巧就是老老实实看。import pandas as pd import numpy as np # 读取训练集和测试集注意编码和分隔符按实际文件调整 train pd.read_csv(train.csv) test pd.read_csv(test.csv) # 看形状和字段类型 print(train shape:, train.shape) print(test shape:, test.shape) print(train.dtypes.value_counts()) # 看缺失比例按列排序 missing train.isnull().sum() / len(train) missing missing[missing 0].sort_values(ascendingFalse) print(缺失比例 top:) print(missing.head(20)) # 看价格分布重点看偏度和峰度 print(price skew:, train[price].skew()) print(price kurt:, train[price].kurt()) print(train[price].describe())这段代码的逻辑很直接先确认数据规模再按类型统计字段数量然后找出缺失严重的列最后看目标变量的分布形态。参数上isnull().sum() / len(train)得到的是缺失比例而不是绝对数量因为绝对数量在几万行数据里没有直观意义。skew()和kurt()是判断要不要对价格做对数变换的依据如果偏度大于 1通常建议先取 log 再建模。2.2 缺失值和异常值的处理策略缺失值分两种一种是真缺失比如某辆车的排量没记录另一种是「不适用」比如电动车没有排量。这两种在业务上含义不同但在这份赛题里通常统一按缺失处理。常见做法是类别型字段用众数填充或单独标记为「未知」数值型字段用中位数填充因为中位数对异常值不敏感。异常值方面价格字段里可能有极低或极高的值。我一般会先看分位数把低于 1% 分位和高于 99% 分位的样本单独拎出来看一眼。如果明显是录入错误比如价格是 1 元直接删掉如果是真实的长尾比如豪车保留但考虑做 log 变换。这里没有绝对标准关键是你要能说清楚为什么这么处理。# 价格做 log 变换缓解长尾 train[price_log] np.log1p(train[price]) # 数值型缺失用中位数填充 num_cols train.select_dtypes(include[np.number]).columns for col in num_cols: if col not in [price, price_log]: train[col] train[col].fillna(train[col].median()) test[col] test[col].fillna(test[col].median()) # 类别型缺失用众数填充 cat_cols train.select_dtypes(include[object]).columns for col in cat_cols: train[col] train[col].fillna(train[col].mode()[0]) test[col] test[col].fillna(test[col].mode()[0])注意np.log1p而不是np.log因为价格可能为 0log1p 能避免负无穷。填充时训练集和测试集要用同一套统计量否则会引入数据泄露。这一点新手很容易翻车在测试集上单独算中位数填充看起来没问题实际上测试集的分布和训练集不一致会导致线上线下表现差距很大。3. 特征工程把原始字段变成模型能吃的信号特征工程是这份赛题拉开差距的地方。原始字段里品牌、车型、变速箱这些类别型变量不能直接喂给模型需要编码里程、排量这些数值型变量需要看分布和相关性匿名特征虽然看不懂含义但可以用相关性筛选。下面按类别型、数值型、时间型三类分别说。3.1 类别型字段的编码选择类别型字段的编码方式直接影响模型效果。常见的有三种标签编码Label Encoding、独热编码One-Hot Encoding、目标编码Target Encoding。标签编码简单但会引入大小关系适合树模型独热编码适合类别数少的字段类别数一多维度爆炸目标编码用目标变量的均值来编码效果好但容易过拟合必须配合交叉验证。这份赛题里品牌、车型、变速箱这些字段类别数不算特别多我一般先用标签编码跑一版基线再对高基数类别试目标编码。下面是一个带平滑的目标编码实现from sklearn.model_selection import KFold def target_encode(train_df, test_df, col, target, n_splits5, smooth10): # 用 K 折计算目标编码避免过拟合 kf KFold(n_splitsn_splits, shuffleTrue, random_state42) train_df[col _target] np.nan for tr_idx, val_idx in kf.split(train_df): tr_x train_df.iloc[tr_idx] val_x train_df.iloc[val_idx] # 计算每类的均值和全局均值 means tr_x.groupby(col)[target].mean() global_mean tr_x[target].mean() # 平滑类别样本少时向全局均值靠拢 counts tr_x.groupby(col)[target].count() smooth_means (means * counts global_mean * smooth) / (counts smooth) train_df.loc[val_idx, col _target] val_x[col].map(smooth_means) # 测试集用全量训练集的统计量 means train_df.groupby(col)[target].mean() counts train_df.groupby(col)[target].count() global_mean train_df[target].mean() smooth_means (means * counts global_mean * smooth) / (counts smooth) test_df[col _target] test_df[col].map(smooth_means) return train_df, test_df这段代码的关键在smooth参数它控制平滑强度。类别样本越少编码值越向全局均值靠拢避免小样本类别被极端值带偏。n_splits是交叉验证折数一般 5 折够用。注意训练集的目标编码是折外预测测试集用的是全量统计量这样能最大程度模拟线上场景。3.2 数值型字段的变换与组合数值型字段里里程、排量、车龄这些通常需要做变换。里程和价格往往是非线性关系直接线性回归效果一般树模型能自动捕捉非线性但做一下分箱或 log 变换有时能提升稳定性。车龄可以从上牌时间推算这是一个典型的特征构造用当前年份减去上牌年份得到车龄比原始日期字段更有业务含义。# 构造车龄特征 train[reg_year] pd.to_datetime(train[regDate], format%Y%m%d, errorscoerce).dt.year test[reg_year] pd.to_datetime(test[regDate], format%Y%m%d, errorscoerce).dt.year train[car_age] 2024 - train[reg_year] test[car_age] 2024 - test[reg_year] # 里程做 log 变换 train[kilometer_log] np.log1p(train[kilometer]) test[kilometer_log] np.log1p(test[kilometer]) # 数值字段之间的比值特征 train[power_per_km] train[power] / (train[kilometer] 1) test[power_per_km] test[power] / (test[kilometer] 1)errorscoerce是为了处理日期格式不规范的记录解析失败会变成 NaT后续填充即可。1是防止除零。比值特征在业务上解释得通单位里程的动力输出往往和车况、价格相关。这类构造特征不需要多复杂关键是能说出业务逻辑。3.3 匿名特征的筛选与降维匿名特征 v_0 到 v_N 通常有几十个全部塞进模型未必好。我一般先算它们和价格的相关性保留相关性绝对值大于某个阈值的再用 PCA 或树模型的特征重要性做二次筛选。注意匿名特征之间可能存在共线性树模型对共线性不敏感但线性模型会受影响。from sklearn.feature_selection import SelectKBest, f_regression # 匿名特征列 v_cols [c for c in train.columns if c.startswith(v_)] # 用 F 检验选前 20 个 selector SelectKBest(score_funcf_regression, k20) selector.fit(train[v_cols].fillna(0), train[price_log]) selected_v [v_cols[i] for i in selector.get_support(indicesTrue)] print(selected anonymous features:, selected_v)f_regression计算的是每个特征和目标的线性相关性适合快速筛选。k20是保留数量可以根据实际效果调整。如果匿名特征特别多也可以先用方差过滤去掉低方差的列再跑相关性筛选。4. 模型训练与融合从单模型到 stacking特征工程做完接下来是模型。这份赛题是回归任务常用的模型有 LightGBM、XGBoost、CatBoost 和线性回归。树模型对类别特征和缺失值处理友好通常是主力线性回归可以作为基线也可以用来做融合。下面按单模型调参、交叉验证、模型融合三步走。4.1 LightGBM 单模型调参LightGBM 在这类结构化数据上表现稳定训练速度快。调参时我一般先固定学习率调树的数量和深度再调正则化参数。下面是一个基础配置import lightgbm as lgb from sklearn.model_selection import KFold from sklearn.metrics import mean_squared_error # 特征列 features [c for c in train.columns if c not in [price, price_log, regDate, reg_year]] X train[features] y train[price_log] X_test test[features] # 五折交叉验证 kf KFold(n_splits5, shuffleTrue, random_state42) oof_preds np.zeros(len(X)) test_preds np.zeros(len(X_test)) for fold, (tr_idx, val_idx) in enumerate(kf.split(X)): tr_x, val_x X.iloc[tr_idx], X.iloc[val_idx] tr_y, val_y y.iloc[tr_idx], y.iloc[val_idx] model lgb.LGBMRegressor( n_estimators2000, learning_rate0.05, max_depth7, num_leaves31, subsample0.8, colsample_bytree0.8, reg_alpha0.1, reg_lambda0.1, random_state42 ) model.fit(tr_x, tr_y, eval_set[(val_x, val_y)], eval_metricrmse, callbacks[lgb.early_stopping(100), lgb.log_evaluation(200)]) oof_preds[val_idx] model.predict(val_x) test_preds model.predict(X_test) / kf.n_splits # 还原 log 变换 oof_price np.expm1(oof_preds) test_price np.expm1(test_preds) print(oof rmse:, mean_squared_error(np.expm1(y), oof_price, squaredFalse))early_stopping(100)表示验证集指标 100 轮不提升就停避免过拟合。subsample和colsample_bytree控制行采样和列采样比例一般 0.7 到 0.9 之间。reg_alpha和reg_lambda是 L1 和 L2 正则数据量大时可以调小数据量小就调大。注意最后要用expm1还原因为训练时对价格做了 log1p。4.2 多模型融合与 stacking单模型调到头之后融合往往能再涨一点。最简单的融合是加权平均权重可以用验证集上的表现来定。进阶一点的是 stacking用多个基模型的折外预测作为新特征再训一个元模型。下面是一个两层 stacking 的骨架from sklearn.linear_model import Ridge from sklearn.ensemble import RandomForestRegressor # 基模型列表 base_models [ lgb.LGBMRegressor(n_estimators1500, learning_rate0.05, max_depth7, random_state42), RandomForestRegressor(n_estimators500, max_depth12, random_state42) ] # 第一层生成折外预测 stack_train np.zeros((len(X), len(base_models))) stack_test np.zeros((len(X_test), len(base_models))) for i, model in enumerate(base_models): for tr_idx, val_idx in kf.split(X): tr_x, val_x X.iloc[tr_idx], X.iloc[val_idx] tr_y, val_y y.iloc[tr_idx], y.iloc[val_idx] model.fit(tr_x, tr_y) stack_train[val_idx, i] model.predict(val_x) stack_test[:, i] model.predict(X_test) / kf.n_splits # 第二层用 Ridge 做元模型 meta Ridge(alpha1.0) meta.fit(stack_train, y) final_pred np.expm1(meta.predict(stack_test))stacking 的关键是基模型的折外预测必须用相同的折划分否则会泄露。元模型一般选简单的线性模型避免过拟合。alpha1.0是 Ridge 的正则强度可以调。融合不是万能的如果基模型相关性太高融合收益有限这时候不如把精力放回特征工程。5. 避坑与排查那些我踩过的坑这一章记录几个我在跑这类赛题时真实翻车的地方每条按现象、原因、解决来说。现象线下验证 RMSE 很低线上提交后分数差一大截。原因最常见的是数据泄露。比如在填充缺失值时用了全量数据的中位数或者在目标编码时没有做折外预测导致验证集的信息泄露到了训练过程。 解决所有统计量均值、中位数、众数必须只在训练折上计算再应用到验证折和测试集。目标编码必须用 K 折折外预测测试集用全量训练集统计量。现象模型训练时 loss 一直下降但验证集 loss 很早就开始上升。原因过拟合。树模型尤其容易在特征多、数据少的时候过拟合。 解决先降max_depth和num_leaves再调大reg_alpha和reg_lambda同时开early_stopping。如果还不行就减特征用特征重要性筛掉一批。现象类别型字段编码后模型效果反而变差了。原因标签编码给无序类别引入了虚假的大小关系线性模型会受影响独热编码在类别数多时导致维度爆炸树模型训练变慢且容易过拟合。 解决树模型优先用标签编码或目标编码线性模型用独热编码。高基数类别比如车型用目标编码低基数类别比如变速箱用独热编码。现象价格预测出来有负数。原因直接对原始价格建模线性模型或某些配置下的树模型可能预测出负值。 解决对价格做 log1p 变换后再建模预测结果用 expm1 还原这样输出一定是正数。或者用objectiveregression_l1等对负值不敏感的损失函数。现象匿名特征全部塞进去训练特别慢效果还没提升。原因匿名特征之间共线性严重或者大部分是噪声。 解决先算相关性保留和价格相关性高的再用 PCA 或特征重要性做二次筛选。树模型对共线性不敏感但特征太多会拖慢训练适当降维有好处。6. 进阶技巧用贝叶斯优化把调参从玄学变成工程调参这件事网格搜索太慢随机搜索靠运气我后来习惯用 Optuna 做贝叶斯优化。它根据历史试验结果选择下一组超参数通常几十轮就能找到不错的配置。下面是一个针对 LightGBM 的调参示例搜索空间和早停策略都写在里面。import optuna from sklearn.metrics import mean_squared_error def objective(trial): params { n_estimators: trial.suggest_int(n_estimators, 500, 3000), learning_rate: trial.suggest_float(learning_rate, 0.01, 0.1, logTrue), max_depth: trial.suggest_int(max_depth, 4, 12), num_leaves: trial.suggest_int(num_leaves, 15, 63), subsample: trial.suggest_float(subsample, 0.6, 1.0), colsample_bytree: trial.suggest_float(colsample_bytree, 0.6, 1.0), reg_alpha: trial.suggest_float(reg_alpha, 1e-3, 10, logTrue), reg_lambda: trial.suggest_float(reg_lambda, 1e-3, 10, logTrue), random_state: 42 } # 用单折验证快速评估减少调参时间 tr_idx, val_idx next(iter(kf.split(X))) tr_x, val_x X.iloc[tr_idx], X.iloc[val_idx] tr_y, val_y y.iloc[tr_idx], y.iloc[val_idx] model lgb.LGBMRegressor(**params) model.fit(tr_x, tr_y, eval_set[(val_x, val_y)], eval_metricrmse, callbacks[lgb.early_stopping(50)]) pred model.predict(val_x) return mean_squared_error(val_y, pred, squaredFalse) study optuna.create_study(directionminimize) study.optimize(objective, n_trials50) print(best params:, study.best_params) print(best rmse:, study.best_value)logTrue表示在对数尺度上采样适合学习率、正则系数这类跨数量级的参数。n_trials50是试验次数实际跑的时候可以按时间预算调整。用单折验证是为了快找到大致范围后再用五折确认。调参之外还有一个容易被忽略的点验证策略要和线上评估一致。如果线上按 RMSE 排名线下就用 RMSE如果线上按 MAE线下就换 MAE。我见过有人线下用 RMSE 调参线上按 MAE 排名结果怎么调都上不去。最后说一个习惯每次跑完一版模型我都会把特征列表、超参数、验证分数记在一个表格里下次调参时先看历史记录避免重复试错。这份赛题的价值不在于刷到多高的分而在于你把整套流程走通之后换一份数据也能照着复现。从那以后我每次做回归任务都强制先跑一遍数据摸底和缺失检查再动模型。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

一人公司全链路AI内容获客工作流实战指南

一人公司全链路AI内容获客工作流实战指南

1. 项目概述:为什么“一人公司”必须重构内容获客的底层逻辑“必备 AI 工具:一人公司全链路内容获客实战工作流”——这个标题里没有一个生僻词,但每个词都踩在当下真实创业者的神经末梢上。“一人公司”不是浪漫主义的自由职业幻想&#xff…

📅 2026/10/12 13:03:51
PCA动态风险平价:让投资组合真正抗波动的实战方法

PCA动态风险平价:让投资组合真正抗波动的实战方法

1. 这不是“稳赚不赔”的玄学,而是用数学重新定义“分散投资”你有没有遇到过这种情况:手里的基金组合里,股票、债券、黄金、商品全配齐了,标榜“全球资产配置”,结果一到市场剧烈波动,账户净值还是跟着股票…

📅 2026/10/12 13:03:51
代码审查自动化实践:open-code-review 工具的设计与落地

代码审查自动化实践:open-code-review 工具的设计与落地

写代码这么多年,我越来越觉得“代码审查”这件事,不是流程问题,是成本问题。大家嘴上都说要评审、要交叉检查,实际做过的人都知道:一个大功能合进去之前,真正逐行看代码的人可能只有作者自己。于是我用掉了…

📅 2026/10/12 13:03:51
MORE NEWS

更多资讯

📰

计算机网络实验全链路:从Boson Netsim拓扑到SOCKET编程实战

简介:这份华中科技大学计算机学院《计算机网络》实验报告,面向高校计算机专业学生及网络工程初学者,帮助读者系统掌握网络组建与协议配置的实践方法。报告围绕IP协议、网络层与数据链路层协议、子网划分、RIP与OSPF路由协议、路由器及二/三层…

📰

基于Python的微博舆情分析可视化系统:爬虫、情感分析与展示全流程

简介:基于Python的微博舆情分析可视化系统,是一套面向毕业设计、课程设计与期末大作业的完整项目源码。整合爬虫采集、情感分析与可视化展示三大模块,适合计算机专业学生作为高分毕设参考。代码带有详细注释,新手也能较快上手&…

📰

近红外光谱回归建模:物理约束驱动的深度学习方法

简介:本资源是一套面向计算机专业本科生的毕业设计与课程作业级深度学习实践项目,聚焦近红外光谱(NIR)数据的回归建模任务,适用于化学分析、食品检测、生物医学等交叉领域中的成分定量预测场景。压缩包共9个文件&#…

📰

开题报告写作新思路:千笔与灵感风暴AI的分工协作指南

开题这件事,我见过太多种“卡法”。有人卡在“这个题值不值得做”,坐了一下午连题目方向都定不下来;有人卡在文献综述,读了二三十篇摘要,落到Word里还是写不出三句人话;还有人卡得最冤——初稿都交了&#…

📰

网红罗盘时钟:物理+代码+光影的时间装置入门指南

1. 这个“网红时钟罗盘”到底在火什么?——从零理解它的底层逻辑与真实价值你刷短视频时,有没有被那种悬浮在空中的金属圆盘吸引过?它缓缓旋转,指针不是指向数字,而是嵌着细小的指南针、经纬度刻线,甚至微缩…

📰

Advanced Installer 20.7.1:打造干净可复现的MSI安装包与升级链路

简介:Advanced Installer 20.7.1 是一款面向软件开发者与打包工程师的 Windows 安装包制作工具,专注于生成符合 MS Windows 认证的 MSI 安装文件,同时支持自定义欢迎界面与安装过程界面,适合需要规范化发布桌面应用的团队或个人使…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬