ARTICLE DETAIL

资讯详情

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

房价预测AI大作业全攻略:从数据处理到模型调优

房价预测AI大作业全攻略:从数据处理到模型调优 简介一套面向高校人工智能课程设计与毕业设计的机器学习房价预测项目以二手房和新房房价为分析对象覆盖数据采集、清洗、特征工程、建模评估与可视化全流程。项目内含链家、安居客平台爬虫采集脚本与清洗后的CSV房价数据集附有带详细注释的Python源码、Notebook分析文档及说明材料新手可对照注释逐步理解并快速部署复现。资源压缩包共26个文件以脚本、分析笔记、数据表格和可视化图片等格式为主配合说明文档整体仅1.29MB轻量且结构清晰。目前已有161人学习下载特别适合用于期末大作业、课程设计或毕业设计可从数据预处理、模型比较和可视化展示中直接借鉴并按需修改扩展为独立项目是易上手、可落地的高分参考方案既能满足技能训练也便于答辩演示。1. 人工智能大作业选房价预测一个回归项目背后的五个得分点每年到了人工智能大作业季总有学生抱着“房价预测数据集”来找我说课上讲了波士顿房价但自己拿到的是一堆二手房源的 CSV总价、单价、朝向、楼层、装修、房龄什么都有不知道从哪里下手。其实房价预测是机器学习里最标准的回归问题特征是表格数据、目标是连续数值、评价指标也固定很适合用来检验特征工程和模型调参的基本功。这个方向能解决的不只是“交一份大作业”而是把数据清洗、特征构造、模型对比、过拟合诊断这一整条流水线跑通。适合正在学机器学习入门、想用项目源码做完整实践、又不想在选题上浪费时间的同学。下面按我平时给项目做技术评审的顺序把这个方案的每一步拆开讲。2. 房价预测数据集怎么用先看数据字典再定项目结构拿到任何一个房价预测数据集第一件事不是训练模型而是把字段含义弄清楚。很多大作业翻车都是栽在数据字典没看明白把不该当特征的列放进去了。2.1 先从数据字典下手区分连续特征、离散特征和目标列常见房价数据集里字段一般分三类。第一类是连续数值型比如面积、房龄、总价、单价第二类是离散类别型比如朝向、楼层、装修程度、所在区域第三类是标识型比如房源编号、小区名称。标识型字段不能进模型小区名称如果类别数太多直接做编码会把训练集撑爆而且容易过拟合。我一般会先写一段最基础的数据读取代码把每一列的类型和缺失情况摸清楚import pandas as pd df pd.read_csv(house_data.csv, encodingutf-8) print(df.shape) print(df.info()) print(df.describe().T)这段代码的核心作用有三个。df.shape看样本量和特征数确认数据规模是否支持后面的交叉验证df.info()列出每列的非空数量和数据类型缺失值在哪、哪些列被读成了 object一眼就能看到df.describe().T对数值列做统计摘要如果某一列的 min 和 max 差了几个数量级说明有异常值或者单位不统一。这里要注意两个点。第一目标列一定是连续数值比如总价或单价不能是分类。第二如果原始数据里同时有总价和单价你只能选一个做目标另一个要么丢弃要么小心处理否则就是数据泄漏。后面避坑章节会专门说这个问题。2.2 二手房价数据的特殊字段朝向、楼层、装修、学区怎么编码与波士顿房价这类经典数据集不同二手房数据集里会出现大量中文类别字段。朝向有南、北、东、西、东南、西南、东西等楼层有低楼层、中楼层、高楼层、顶层装修有精装、简装、毛坯。这些字段不能直接丢给模型因为在 sklearn 里字符串是不能参与距离计算的。处理顺序有讲究。先判断这个类别是有序还是无序。装修程度天然有顺序毛坯最差、简装中等、精装最好这种可以用数值映射。朝向和区域没有明确顺序更适合用独热编码或者按领域经验做手动分组。我一般这样处理df[装修] df[装修].map({毛坯: 0, 简装: 1, 精装: 2}) df[楼层] df[楼层].map({低: 0, 中: 1, 高: 2, 顶层: 2}) df pd.get_dummies(df, columns[区域, 朝向], drop_firstTrue)map操作是把离散文本变成带大小关系的数值模型能直接理解get_dummies生成多列 0/1 标志避免模型误判类别之间有远近关系。drop_firstTrue会丢掉每个类别第一列作用是防止多重共线性对线性回归尤其重要因为如果不丢区域有几个类别就会生成几列总和恒为 1X 矩阵变成奇异矩阵。很多同学在这个环节会纠结“朝向的分数怎么定”。说实话我们自己是拍不出合理权重的与其硬编码一个“南向1, 北向0”的规则不如把它当成无序类别做独热编码让模型自己去学。大作业文档里写清楚这个取舍反而能拿到特征工程部分的分数。2.3 最小可运行的项目结构源码、数据和文档怎么组织我见过太多大作业是一个 main.py 文件从头写到尾几千行代码堆在一起老师打开都头疼。房价预测项目虽然不是工程级别但至少要有清晰的分层。常见的做法是一个目录下放三个子目录data/放原始数据集和清洗后的数据src/放数据处理、特征工程、模型训练、评估四个脚本docs/放大作业文档和实验结果截图。训练脚本里用一行主函数把流程串起来其他功能拆成函数。需要注意数据集如果是别人提供的保留原始文件不要修改清洗后的版本单独存一份。文档里写清楚“原始数据有 8686 条清洗后剩 8214 条删除了哪些异常值”这就是项目源码之外最有说服力的过程记录。数据集本身如果包含时间字段保存前先按时间排序后面做时间切分才不用返工。3. 特征工程是分数分水岭从缺失值到归一化的完整流水线很多机器学习入门教程喜欢把重点放在模型上但实际做房价预测特征工程才是拉开分数的地方。模型选线性回归还是随机森林最后 RMSE 可能只差几百但特征处理好坏可能直接决定结果的量级对不对。3.1 缺失值处理均值填充为什么在这里可用二手房数据集里常见的缺失是户型字段的空值、装修程度为空、楼层信息缺失。处理前先统计缺失比例如果某一列缺失超过 30%要慎重考虑这列还能不能用如果只是零星缺失则看字段类型。连续数值型缺失我一般用中位数填充而不是均值因为房价和面积这类字段分布是偏态的少数几套超大平层会把均值拉高中位数更有代表性。类别型缺失最安全的是填众数或者单独加一列“缺失标记”让模型自己学习num_cols df.select_dtypes(include[float64, int64]).columns for col in num_cols: df[col] df[col].fillna(df[col].median()) cat_cols df.select_dtypes(include[object]).columns for col in cat_cols: df[col] df[col].fillna(df[col].mode()[0])这段代码先对数值列按列填充中位数再对文本列按众数填充。用循环而不是fillna(methodffill)就是为了避免前向填充把无关行之间建立起伪关联。模式值可能出现多个所以取mode()[0]。还有一类缺失要注意面积为空、总价不为空的情况面积缺失没法通过其他列推出来只能删掉整行。房龄缺失如果项目名称存在可以尝试从项目名称里解析建造年份这是比较进阶的做法大作业里时间不够可以不做。3.2 类别特征编码Label Encoding 与 One-Hot 的取舍顺序编码我前面已经演示过了这里要单独说 Label Encoding 的坑。sklearn.preprocessing.LabelEncoder会把字符串按字母表顺序映射成 0、1、2如果你的特征有顺序含义它完全不管你的特征没有顺序含义它反而强行制造了顺序。一个常见的反面案例把“区域”字段做 Label Encoding 后结果变成“朝阳区0, 海淀区1, 丰台区2”线性回归会学出一个“每增加一个区房价增加多少”的荒谬规律。正确做法是用pd.get_dummies或OneHotEncoder处理区域、板块、朝向这类无序字段。那么什么时候可以用 Label Encoding答案是对树模型。随机森林和 XGBoost 对特征的单调变换不敏感而且它们做的是“按某个阈值分裂”编号本身不影响分裂结果。如果数据维度太大比如区域有 30 多个类独热编码会生成 30 多列对线性回归没问题对树模型反而会拖慢训练。这一节的大作业报告里建议把自己试过的两种编码的结果对比白纸黑字写出来。3.3 特征组合与归一化把面积单价、房龄结构做成有效特征原始数据只给你面积、户型、总价、房龄但真正有用的信息往往藏在组合里。常见的做法是构造“单价 总价 / 面积”这种领域特征还有“卧室平均面积 面积 / 居室数”“楼龄新度 1 / (房龄 1)”。这个环节最依赖领域经验也是最容易出彩的地方。归一化要注意使用场景。线性回归、KNN、SVM 这类基于距离或梯度的模型必须归一化否则面积以平方米为单位数值上千朝向那一列是 0/1量级相差太大梯度下降会在小数值维度上来回震荡。树模型不依赖距离归一化反而没有必要做了也不会带来提升因为分裂节点只看阈值比较。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)注意fit_transform和transform的搭配这是考试和实践中都反复出现的考点归一化参数只能从训练集学测试集要沿用训练集的均值和标准差不能自己再算一遍。如果先对整个数据集做归一化再切分严格来说就已经发生了信息泄漏交叉验证的结果会偏乐观。我把它也算进了避坑清单因为它真的能看出一个人有没有真正做过项目。4. 模型选型与参数调整线性回归、随机森林与 XGBoost 的对比落地模型部分是大作业里最好写、也最容易显得空洞的部分。很多人的做法是每个模型跑一遍挑一个分数最高的写进报告却说不清为什么这个模型在这个数据集上更好。下面按我的习惯给出一套能自圆其说的思路。4.1 三个基线模型为什么先跑线性回归再看树模型我从不鼓励直接上集成模型。第一个要跑的是线性回归它的意义不是拿冠军而是给后面所有复杂模型定一个“及格线”。如果线性回归 R² 有 0.82随机森林只提升到 0.84说明特征工程已经做得差不多了模型复杂度没必要继续加如果线性回归 R² 只有 0.55随机森林跑到 0.83说明数据里有明显的非线性关系这才是你该花篇幅分析的地方。三件套代码是标准流程from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from xgboost import XGBRegressor from sklearn.metrics import mean_squared_error, r2_score X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) models { linear: LinearRegression(), rf: RandomForestRegressor(n_estimators300, max_depth10, min_samples_leaf3, random_state42), xgb: XGBRegressor(n_estimators300, learning_rate0.05, max_depth5, subsample0.8, random_state42) } for name, model in models.items(): model.fit(X_train, y_train) y_pred model.predict(X_test) rmse mean_squared_error(y_test, y_pred, squaredFalse) r2 r2_score(y_test, y_pred) print(f{name:6} RMSE{rmse:.2f} R2{r2:.4f})random_state42保证每次运行结果可复现是交作业的强制要求squaredFalse直接拿到 RMSE 而不是 MSE数值单位与房价一致报告里好说明。三个模型用同一份切分好的数据保证可比性。线性回归不用归一化也能跑但如果前面做了缩放记得把测试集也同步缩放。4.2 随机森林的三个必调参数n_estimators、max_depth、min_samples_leaf随机森林在大作业数据集上表现一般都不错原因是对异常值和缺失值包容度好也不用做太多特征缩放。但默认参数不是为你这个数据集定制的三个参数必须调。n_estimators是树的数量。我一般从 100 试到 500如果 300 以后误差基本不变就不再往上加。不是树越多越好训练时间线性增长收益却收敛大作业没必要烧机器。max_depth限制树深度不设的话树可能长到把训练集的每一条都记住测试集分数就会很难看。min_samples_leaf是叶节点最少样本数设成 3 或 5 能强拉伸制过拟合。先固定n_estimators300然后对max_depth和min_samples_leaf做网格搜索是更省时间的顺序。先调树的规模再调复杂度限制不要一开始就把两个参数同时扔进 GridSearchCV维度太多搜索空间爆炸而且大作业的计算时间有限。4.3 XGBoost 在房价数据上的调参顺序先学率后树深XGBoost 效果好但参数更多也更容易玄学。我的调整顺序是固定的先定learning_rate再调max_depth最后看subsample和colsample_bytree。learning_rate默认是 0.3训练速度快但容易震荡降到 0.05 之后每一步更小需要更多树来补偿但最终精度更高。注意它和n_estimators耦合学习率减半最优树数量通常会翻倍所以两者要一起看。max_depth在房价这种表格数据上3 到 6 之间就够用了太深会开始抓噪声。subsample控制每棵树用多少比例的样本0.8 是个保守的默认值能增加模型多样性降低方差。调参不是越复杂越好。大作业的加分项是画出“不同 max_depth 下训练集和测试集 RMSE 的曲线”直接证明你理解了欠拟合和过拟合的分界线比单纯丢出一个最优参数有说服力得多。4.4 评估指标怎么定RMSE 和 R² 哪个更能看出模型翻车回归任务最常用的两个指标各有分工。RMSE 单位与房价一致比如“RMSE 12.3 万元”对不懂机器学习的老师来说也很直观它就是“平均预测偏差”。R² 是一个相对得分0.85 意味着模型解释了 85% 的房价方差但看不出来误差绝对大小而且当数据本身方差很小时R² 会显得很高却没有实际意义。我会两个都打印但报告里以 RMSE 为主。还要补一个指标MAE也就是平均绝对误差。RMSE 对被预测偏远的少数样本放大了惩罚MAE 能反映常规样本的误差水平。如果 RMSE 很高但 MAE 不高说明问题集中在少数异常房价上这时候可以去看看是不是测试集里混进了几套特别贵的豪宅把它们单独分析而不是急着改模型。5. 常见问题与避坑清单数据泄漏、时间穿越和二手房的坑这一章的价值在于下面每一条都是我见过真实翻车的场景。把这些坑提前避掉等于给你的大作业买了后悔药不用等跑出离谱结果再回头一行行查。5.1 数据泄漏把目标列当特征用训练分数虚高现象训练时 R² 高达 0.98特征重要性里却出现了一个不合理的字段一到测试集或答辩现场手动输入几套房源预测结果完全不对。原因原始数据同时存在“总价”和“单价”这两个字段单价本来就是总价除以面积算出来的如果目标列是总价把单价放进特征矩阵模型等于直接抄答案。这种情况下 R² 再高也是假的。解决进入模型的特征必须只保留“预测时能拿到”的信息。总价是目标单价丢弃挂牌价如果和成交价高度重合也要做同样处理。报告里要主动说明这一点老师一看就知道你懂什么叫数据泄漏。5.2 时间穿越用未来数据预测过去现象数据集包含近五年的二手房交易记录直接按行随机切分训练集和测试集测试集的 RMSE 看起来不错但模型一上真实场景就失效。原因房价和年份强相关随机切分会让同一年份的数据同时出现在训练集和测试集里模型其实记住了年份规律。你的评估因此被美化真实场景是只能用过去预测未来。解决严格按时间做切分比如前四年做训练最后一年做验证。用train_test_split之前先按时间排序再用iloc直接按位置切分。大作业报告里画出训练年份和测试年份的分布图这个动作能直接把你的项目从“会跑模型”提升到“会做评估”。5.3 二手房朝向和楼层的编码顺序搞错模型跟着错现象把朝向LabelEncoder之后随机森林 RMSE 反而升高了特征重要性里“朝向”几乎为 0。原因LabelEncoder 按字母序给朝向编号东南西北的顺序全乱了而且被误当成有序数值。树模型虽然能忍受乱序但被强制赋予顺序信息的特征会干扰分裂点的选择尤其是朝向本身对房价只有弱影响时。解决朝向、所在区域一律用独热编码装修、楼层这种有天然递进关系的才用自定义映射。判断标准很简单你能不能说清楚这个类别的 0 和 2 比 0 和 1 差距更大说不清就不该用数值映射。5.4 归一化放在交叉验证外面验证分数被污染现象把整个数据集先StandardScaler().fit_transform()再做train_test_split交叉验证结果异常稳定换随机种子结果都不变。原因缩放时已经用到了测试集的数据分布严格讲测试集的信息混进了训练流程这就是一个典型的预处理泄漏。虽然对随机森林这种树模型影响不大但如果你同时用了线性回归线性回归的分数会被明显抬高。解决先切分再在训练集上fit_transform测试集上只transform。如果用了cross_val_score预处理步骤要放进Pipeline里让每一折都独立做缩放。5.5 中文列名与编码问题数据还没进模型就报错现象pd.read_csv一运行就报编码错误或者打印 DataFrame 时中文全是乱码df[小区]直接 KeyError。原因Windows 下默认的 CSV 编码是 gbk而 Python 默认按 utf-8 读另一部分数据集文件来自网络可能是 gbk、utf-8、utf-8-sig 三种之一含 BOM 的文件还会在列名前面多一个看不见的字符。解决读取时显式指定编码。乱码或报错就换一种试或直接用encodinggbk读再不行就用encodingutf-8-sig。读进来之后立刻把所有列名里的空格、换行符替换掉df.columns [c.strip().replace( , _) for c in df.columns]。别嫌这些小问题低级每年都有项目源码挂在这上面技术难度为零但确实浪费时间。6. 交叉验证与学习曲线交作业前最后确认模型真的能扛模型跑完、参数调完不要急着写文档。我习惯再做两件事来确认结果不是偶然五折交叉验证和一两条学习曲线。交叉验证能告诉你模型在不同的数据划分下表现是否稳定而学习曲线能看出当前模型是卡在数据量不够还是已经过拟合到训练集上。from sklearn.model_selection import cross_val_score import numpy as np cv_scores cross_val_score(model, X, y, cv5, scoringneg_root_mean_squared_error) print(CV RMSE:, -cv_scores.mean(), /-, cv_scores.std())这里scoringneg_root_mean_squared_error是因为 sklearn 的评分规则是“越大越好”RMSE 越小越好所以要取负号。五折的结果如果标准差很大说明你的模型在部分数据子集上翻了车很可能是有个别异常小区或户型导致的。接着画学习曲线from sklearn.model_selection import learning_curve train_sizes, train_scores, val_scores learning_curve( model, X, y, cv5, train_sizesnp.linspace(0.1, 1.0, 6), scoringneg_root_mean_squared_error )把结果用 Matplotlib 画出来横轴是训练样本量纵轴是 RMSE两条线分别是训练集和测试集。如果训练集误差低、测试集误差高中间有明显沟就继续加数据或加强限制如果两条线都偏高且靠得近说明模型容量不够该换更强的模型。这个图几乎不用解释老师看了就知道你的调参方向不是拍脑袋。最后还有一个值得做的事打印随机森林或 XGBoost 的特征重要性把前五名特征写进报告里结合房价直觉说一段“为什么面积排在第一位朝向排在最后一位”的解读。哪怕你不解释这个输出也能证明你是认真做了特征分析的人。这是我带项目时一直保持的习惯不拿第一次跑出来的数字交差交叉验证和学习曲线都看过、确认稳定了才敢说这个模型能落地。这个习惯也建议你带进以后的项目里希望帮到你。本文还有配套的精品资源点击获取
返回列表