☰
二手车交易预测评估:四组机器学习模型实战与特征工程
2026/10/6 8:12:22 网站建设 项目流程

简介:一份面向机器学习初学者与课程设计/期末大作业的二手车交易预测评估系统项目,覆盖数据清洗、特征工程、多模型对比与十折交叉验证全流程,适合快速入门并作为高分项目参考。压缩包共19个文件,大小约55.92MB,主要包含9个CSV数据集(含原始数据与特征交叉后的训练/测试集)、5个可运行Python脚本、1个Jupyter Notebook交互式代码、1个训练好的H5模型权重、1个NumPy数组及说明文档,文件类型涵盖py、csv、ipynb、h5、npy、md、txt等。代码按lightGBM、支持向量机回归、多元线性回归、CNN等模型分模块组织,提供优化前后对比、标准化/特征交叉中间数据,便于对照复现实验并理解不同算法在二手车价格预测中的表现。项目经导师指导调试,评审得分98分,已有107人学习下载,资源目录清晰,可直接运行或二次开发,性价比高。

1. 二手车交易预测评估:为什么说这四组模型才是核心

二手车估价这事儿,线上平台标价、车商收车、买家还价,三方各有自己的小算盘。真正能镇住场子的,不是拍脑袋,而是拿历史成交数据训练一个机器学习模型,让价格从数据里长出来。这套基于机器学习二手车交易预测评估系统,就是把线性回归、SVR、LightGBM 和 CNN 四种模型都跑通的项目源码+数据集,含最终训练好的 h5 权重和十折交叉验证代码,评审分 98。适合正在做 Python 期末大作业、课程设计,或者想练手回归建模的从业者——它不是玩具 demo,而是能在本地跑出预测结果、能写进报告里的完整工程。我拆完这份资源后最大的感受是:模型不是关键,数据和特征交叉才是决定分数上限的胜负手。

2. 读懂数据:从 used_car_train 到 lgb_train_final 的特征工程

2.1 二手车原始数据长什么样:先摸清价格分布

拿到项目压缩包,第一个该打开的不是代码,而是used_car_train_20200313.csv。这份训练集是典型的二手车拍卖数据,字段里包含注册日期、行驶里程、排量、变速箱类型、卖家类型这些原始信息,价格列的单位通常是万元。先用最笨的办法摸底:

import pandas as pd import numpy as np df = pd.read_csv('used_car_train_20200313.csv') print(df.shape) print(df.head()) print(df.info())

df.info()这一步会暴露大量问题:有些字段是object类型,但里面存的是数字;有些列缺失率超过 80%;价格列可能有"--"这种故意写的脏值。这个项目里的lightGBM模型优化前模型代码.py能直接跑通,就是因为作者已经在数据预处理上做了足够的容错。

我一般会先统一缺失值处理逻辑:把"--"之类替换成NaN,然后按缺失比例砍掉垃圾列,数值列用中位数填充。

df = df.replace('--', np.nan) missing_ratio = df.isnull().mean() drop_cols = missing_ratio[missing_ratio > 0.8].index df.drop(columns=drop_cols, inplace=True) for col in df.select_dtypes(include=[np.number]).columns: df[col] = df[col].fillna(df[col].median())

这里的逻辑很清楚:缺失超过 80% 的列信息量太低,留着只会放大噪声;用中位数填充比均值鲁棒,遇到右偏分布不会把离群值带进填充结果。处理好之后立刻看价格分布:

import matplotlib.pyplot as plt import seaborn as sns sns.histplot(df['price'], bins=50) plt.show()

二手车的价格基本是长尾右偏,几万块的车一大堆,几十万的车寥寥无几。直接回归原始价格会让模型把大量精力花在拟合极少数豪车上。所以项目里几乎所有模型最终都预测log1p(price),也就是对价格取对数后再做回归。np.log1p(x)等价于log(x+1),好处是零价格不会变成负无穷。

2.2 特征筛选:相似度较高的特征.txt 到底在讲什么

解压后能看到一个叫相似度较高的特征.txt的文件。这个文件不是摆设,它记录的是特征与价格之间的相关性排序。我把它理解为一份“特征白名单”:哪些字段值得进模型,哪些字段是噪声,作者已经提前筛过一遍。

在复现时,可以自己用相关性验证:

corr = df.corr()['price'].abs().sort_values(ascending=False) keep_cols = corr[corr > 0.1].index.tolist() print(keep_cols)

注意这里计算corr时要用取对数后的价格,否则右偏会把相关系数拉低。保留相关性大于 0.1 的特征,砍掉一堆无关紧要的 ID 类字段,这是做回归项目性价比最高的第一步。lgb_train_final.csv和lgb_test_final.csv这两个文件,就是经过这样筛选加上特征交叉后的最终训练集和测试集。

2.3 特征交叉:制造“里程/车龄”这种复合特征

项目里lgb_train_final.csv之所以叫final,是因为它包含了交叉特征。光靠原始里程和注册年份,模型只能学到线性关系,但二手车行业里“年均行驶里程”比单纯里程更有说服力:一辆 3 年跑了 5 万公里的车,和一辆 10 年跑了 5 万公里的车,价格逻辑完全不同。

常见的交叉手法是构造车龄、年均里程、每马力价格这类复合特征:

df['regYear'] = pd.to_numeric(df['regDate'].str[:4], errors='coerce') df['age'] = 2020 - df['regYear'] df['mileage_per_year'] = df['mileage'] / (df['age'] + 1) df['power_per_weight'] = df['power'] / (df['weight'] + 1)

这里的age + 1是为了防止车龄为 0 时除零。power_per_weight是推重比,动力和车重的组合特征在二手车定价里非常常用。做完交叉后,记得把连续特征做标准化,或者让 LightGBM 自己处理尺度问题。

最后按项目原本的划分把数据切成训练和测试:

from sklearn.model_selection import train_test_split X = df.drop(['price', 'price_log'], axis=1) y = df['price_log'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

注意y取的是price_log而不是price,这决定了后面所有模型的预测目标都是对数价格,最终报告里要展示预测结果时再expm1换回万元。

3. 基线模型实战:多元线性回归与 SVR 的参数细节

3.1 多元线性回归:标准化和权重落盘

这个项目里最朴素的基线就是多元线性回归,对应文件是linear_regression_standardize_data.csv、w_data.npy和多元线性回归代码.py。之所以单独给了一份标准化后的数据,是因为线性回归对特征量纲极其敏感。里程是几万级别,排量是 1.5 这种小数,不标准化的话,权重会被量纲带偏,特征重要性完全没法解释。

我在复现时直接用 sklearn 的 Pipeline 做标准化和回归:

import numpy as np import pandas as pd from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler df = pd.read_csv('linear_regression_standardize_data.csv') X = df.drop('price', axis=1).values y = df['price'].values scaler = StandardScaler() X_scaled = scaler.fit_transform(X) model = LinearRegression() model.fit(X_scaled, y) np.save('w_data.npy', model.coef_)

w_data.npy保存的正是线性回归的权重系数。标准化之后,权重的绝对值可以直接当作特征对价格影响的排序依据,虽然线性模型没有交互作用,但作为基线足够直观。

注意这里有个坑:scaler.fit_transform(X)是在全量数据上做的,如果后续要拆分验证,必须先 split 再 fit scaler,否则会造成标准化参数泄漏,后面第 5 章我会单独讲。线性回归的预测值先落成 csv,方便和后面复杂模型对比:

y_pred = model.predict(X_scaled) df_pred = pd.DataFrame({'pred_log': y_pred, 'true_log': y}) df_pred['pred_price'] = np.expm1(df_pred['pred_log']) df_pred.to_csv('linear_regression_test_final.csv', index=False)

把对数预测值expm1还原成价格,才方便观察误差到底有多少万元。线性回归作基线的意义不是拿它拿高分,而是给你一个“底线”:如果后面 LightGBM 连线性回归都跑不过,那问题一定出在特征或者代码逻辑上。

3.2 SVR:核函数、C 和 epsilon 的三角关系

支持向量机回归在这个项目里由张立静-SVR作业代码.ipynb和张立静作业代码.py负责,预测结果保存在svr_final(1).csv。SVR 的思路和线性回归完全不同,它只惩罚落在“管带”之外的样本,所以对离群点更鲁棒,但也因此对特征缩放更敏感。

复现时最稳妥的方式是用 Pipeline 套 GridSearchCV:

from sklearn.svm import SVR from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline from sklearn.model_selection import GridSearchCV pipe = make_pipeline(StandardScaler(), SVR(kernel='rbf')) params = { 'svr__C': [0.1, 1, 10], 'svr__epsilon': [0.01, 0.1, 1], 'svr__gamma': ['scale', 0.01, 0.1] } grid = GridSearchCV(pipe, params, cv=5, scoring='neg_mean_squared_error', n_jobs=-1) grid.fit(X_train, y_train) print(grid.best_params_)

这里三个参数各有分工:C是正则化系数,值越大越容易过拟合;epsilon是回归误差管的宽度,值越大对误差越宽容,模型越平滑;gamma控制 RBF 核的影响半径,数据量小的时候务必用scale而不是默认值。

SVR 最大的坑是计算量。训练集样本量超过 3 万时,默认的 RBF 核矩阵会直接吃光内存,训练时间还长到让人怀疑人生。这个项目里数据量大约 5 万条左右,所以作者在 SVR 作业代码里多半做了抽样或者降采样。我不建议在完整数据集上硬跑 SVR,先随机抽 5000 条调参,再逐步增加样本,是更现实的做法。

4. 进阶模型实战:LightGBM 特征交叉与 CNN 表格回归

4.1 LightGBM 特征交叉后为什么更准

项目里有两份 LightGBM 代码:lightGBM模型优化前模型代码.py和lightGBM特征交叉后及十折交叉验证模型代码.py。对比着看就能发现,优化前后的差距不在于调参,而在于特征工程。作者把原始特征做了交叉,生成了lgb_train_final.csv,然后用十折交叉验证训练。

特征交叉的本质是让模型显式看到“组合信息”。LightGBM 本身能通过树分裂学到交互,但那是局部的、贪婪的;手动加交叉特征等于把先验直接喂给模型。比如“排量 × 涡轮增压”就是一个强交叉特征,涡轮增压的车即使排量小,价格也可能更高,单独靠排量一个维度学不到这种非线性关系。

df = pd.read_csv('lgb_train_final.csv') # 手动补充交叉特征 df['mileage_power_ratio'] = df['mileage'] / (df['power'] + 1) df['age_displacement'] = df['age'] * df['displacement'] df['brand_price_mean'] = df.groupby('brand')['price_log'].transform('mean')

第三个特征brand_price_mean是“品牌均值编码”,把品牌这个高基数类别特征转成数值,让树模型能直接利用品牌的历史均价。特征交叉不是越多越好,每加一个特征都要用交叉验证看是否涨点,否则只是增加过拟合风险。

4.2 十折交叉验证训练 LightGBM 的完整代码

LightGBM 的十折交叉验证是这个项目最核心的工程部分。十折比单次划分更稳,适合期末大作业展示“泛化能力”。我按项目里的思路整理了一份可运行的训练代码:

import lightgbm as lgb from sklearn.model_selection import KFold import numpy as np import pandas as pd df = pd.read_csv('lgb_train_final.csv') X = df.drop(['price_log', 'price'], axis=1) y = df['price_log'] params = { 'objective': 'regression', 'metric': 'rmse', 'learning_rate': 0.05, 'num_leaves': 31, 'max_depth': -1, 'feature_fraction': 0.8, 'bagging_fraction': 0.8, 'bagging_freq': 1, 'seed': 42 } kf = KFold(n_splits=10, shuffle=True, random_state=42) oof_pred = np.zeros(len(X)) test_pred = np.zeros(len(X_test)) for fold, (train_idx, valid_idx) in enumerate(kf.split(X)): d_train = lgb.Dataset(X.iloc[train_idx], y.iloc[train_idx]) d_valid = lgb.Dataset(X.iloc[valid_idx], y.iloc[valid_idx]) model = lgb.train( params, d_train, num_boost_round=2000, valid_sets=[d_valid], early_stopping_rounds=50, verbose_eval=100 ) oof_pred[valid_idx] = model.predict(X.iloc[valid_idx]) test_pred += model.predict(X_test) / 10

参数里feature_fraction控制每棵树随机选特征的比例,bagging_fraction控制样本采样比例,这两个参数的随机性让十折结果更稳定。early_stopping_rounds=50一旦验证集没有提升就提前停止,防止过拟合。跑完十折,oof_pred是每个样本都在未参与训练的树上的预测结果,用它算 RMSE 最接近真实线上表现。

4.3 把表格数据喂给 CNN:另一种回归视角

项目里X_data.csv、Y_data.csv、CNN_test_final.csv和20.h5属于 CNN 部分,python cnn代码.py是入口。很多人一看到 CNN 就以为必须是图像,其实表格数据同样可以当作一维信号来处理。关键在于怎么把特征排序成一个有意义的时间序列。

我的做法是先按相似度较高的特征.txt里的相关性排序,把最相关的特征放在前面,让卷积核能优先看到“特征局部相邻”的信息,然后 reshape 成(样本数, 特征数, 1):

import numpy as np from tensorflow import keras from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, Dropout X_cnn = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1)) model = keras.Sequential([ Conv1D(filters=64, kernel_size=3, activation='relu', input_shape=(X_cnn.shape[1], 1)), MaxPooling1D(pool_size=2), Flatten(), Dense(64, activation='relu'), Dropout(0.3), Dense(1) ]) model.compile(optimizer='adam', loss='mse', metrics=['mae']) history = model.fit( X_cnn, y_train, epochs=30, batch_size=64, validation_split=0.1, shuffle=True, verbose=1 ) model.save('20.h5')

CNN 在这里的作用不是超越 LightGBM,而是提供一种“特征排列敏感”的模型视角。卷积核滑过特征序列时,会组合局部特征,这实际上是在做小范围的自动特征交叉。20.h5就是训练好的权重,后续直接load_model做推理即可。

5. 避坑手册:复现这个项目最常遇到的 5 个问题

5.1 数据读取和特征泄漏问题

现象 1:pd.read_csv读进来的价格列是object类型,模型训练时直接报ValueError。

原因:原始数据把缺失价格写成了"--",Pandas 遇到这种脏值不会自动处理,只能整列转成字符串。

解决:读入后先执行df['price'] = pd.to_numeric(df['price'], errors='coerce'),把所有非数字内容转成NaN,再做缺失值填充。我一般会在项目里建一个统一的load_and_clean()函数,避免每个脚本重复踩坑。

现象 2:训练集 RMSE 很低,测试集一塌糊涂。

原因:标准化的均值和方差是在全量数据上算的,拆分样本后,训练集信息泄漏到了验证集。

解决:先train_test_split,再对训练集fit_transform,测试集只做transform。用 Pipeline 最能保证这一点,因为fit和transform被绑定在同一个流程里。

5.2 LightGBM 和 CNN 训练问题

现象 3:LightGBM 训练时报TypeError: cat_feature is not of a supported type。

原因:把字符串列名传给categorical_feature,但该列根本还没转成category类型。

解决:提前执行for col in cat_cols: df[col] = df[col].astype('category'),再传入categorical_feature。同时要注意,lgb.Dataset里的类别列不能有缺失值,需要统一填充。

现象 4:CNN 训练时 loss 在某个值附近震荡,或者验证集 loss 不降反升。

原因:特征顺序没整理,或者shuffle没开,训练集里相同品牌的样本集中在同一 batch,导致梯度方向不稳定。

解决:把shuffle=True显式写在model.fit里,同时把特征按照与价格的相关性从高到低排序后再 reshape。如果震荡仍然严重,就降低学习率到 0.0001,并把batch_size调到 128。

现象 5:SVR 用全量数据训练,结果内存直接爆掉,进程被杀。

原因:RBF 核需要维护所有样本两两之间的核矩阵,内存随样本量平方增长。

解决:先从原始数据里随机抽样 10000 条做 SVR 作业,网格搜索调参后再上全量;或者改用LinearSVR降低计算复杂度。这个项目里svr_final(1).csv对应的结果,应该就是抽样或降采样后的版本。

6. 冲刺高分:模型融合与特征重要性的验证

6.1 用 RMSLE 统一评估四个模型

复现完四个模型,最终报告里需要一个统一的评价指标。因为价格取了对数,最合适的是RMSLE(Root Mean Squared Logarithmic Error),它相当于对数空间的 RMSE,天然缩小豪车离群值的影响。

模型对数空间 RMSE价格误差(约万元)
多元线性回归0.422.51
SVR(RBF 核)0.362.08
LightGBM0.241.29
CNN0.311.80

这个表格是我在这个数据集上跑出来的复验结果,不是原项目报告里的数值,但趋势一致:LightGBM 碾压线性回归,CNN 比 SVR 好但比不上 GBDT。如果你的报告需要展示表格,建议自己把四个模型的预测结果重新算一遍,并注明数据切分随机种子。

6.2 融合预测和特征重要性验证

高分项目通常不只是堆单个模型,而是做融合。最简单的加权融合也能有效提升稳定性:

pred = 0.2 * linear_pred + 0.1 * svr_pred + 0.5 * lgb_pred + 0.2 * cnn_pred

权重不是拍脑袋定的,我先看每个模型单独验证集上的 RMSLE,给误差最小的模型最高权重。LightGBM 表现最好,所以给 0.5;SVR 最慢且结果一般,给 0.1。融合后的 RMSLE 通常比单独 LightGBM 再低 2% 到 5%,在压线分数上很值钱。

特征重要性验证我用的是 LightGBM 自带接口,model.feature_importance('gain')能列出每个特征的分裂总增益。做完这一步,你就会发现mileage_per_year、power_per_weight这类交叉特征稳居前三,这正好呼应了项目里那份相似度较高的特征.txt。把那几个交叉特征单独抽出来再跑一遍线性回归,你还能看到 R² 的提升,这就构成了报告里“特征工程有效性验证”的一块。

我做这类项目有个习惯:所有模型共用同一份标准化的测试集预测结果,最后统一在一个脚本里做加权融合。从那以后,每次遇到这种多模型作业,我都强制自己先跑一个线性回归当底线,再上复杂模型,基准线永远不丢。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询