每年到毕设选题的旺季群里总有人问同一个问题题目怎么选才能既有技术含量、又不至于做不出来还保证答辩时评委听得懂、问不倒房价预测这个方向之所以被反复选中正因为它把“大数据”“机器学习”这些关键词和真实生活场景绑在了一起后端可以用 Django 撑起完整的数据服务前端可以用 Vue 做出漂亮的可视化界面。如果你也在做或者正准备做这个题目这篇文章可以帮你少走一大半弯路。从数据怎么来、特征怎么做、模型怎么选到后端接口怎么写、前端图表怎么画、答辩现场怎么讲我把整条链路完整过一遍。我做的这套 Django Vue 基于机器学习的房价预测数据分析系统整体跑下来大概花了三周中间踩了不少坑。文中会写清楚每一步的思路、能直接抄作业的代码片段、以及我在实际项目中验证过的参数。适合正在做毕设的同学也适合想快速搭一个全栈机器学习 Demo、顺便学点工程化思路的开发者。内容偏实操理论部分我只讲到够用的程度太深的东西留给论文展开。1. 选题逻辑与系统整体设计1.1 选题思路这个题为什么能讲满二十分钟毕设答辩本质上是“讲一个完整的故事”而不是秀一段代码。房价预测的天然优势在于它的业务链条足够长数据采集、数据清洗、特征工程、模型训练、后端接口、前端可视化每一环都有东西可以讲而且每一环都独立可演示。更重要的是房价预测是典型的回归问题数据特征大多是结构化表格字段评委不需要额外理解复杂的业务背景。他问“这几个特征是怎么影响房价的”你就能从面积、房龄、地段、楼层这些直观因素入手解释聊得深了还能扯到特征工程和模型选择。相比之下“基于深度学习的图像识别”这类题目虽然听着高级一旦被追问数据集规模和训练细节很容易答不上来。另外这个题目和数据可视化天然契合。房价数据里有时间维度、地理维度、价格维度折线图、散点图、热力图、地图想画什么都有素材。而可视化恰恰是“大数据”这个概念里评委最容易感知的部分。我的经验是评委会花大量时间盯着你的图表看而不是盯着你的代码看所以把可视化做好比把模型调高零点几个点更划算。1.2 技术选型Django 与 Vue 的分工逻辑先说明一下这个项目用的是前后端分离架构而不是 Django 传统的模板渲染方案。原因很直接后端只负责提供数据和模型预测服务前端专注做交互和可视化两者解耦之后答辩时可以分别演示代码结构也更清晰。后端选 Django理由有三个。第一Django 自带 Admin 后台和 ORM毕设里最常见的“数据录入和管理”需求不需要额外写页面改改 Model 注册一下就能用。第二Django REST framework 写 API 非常顺手序列化器一配JSON 接口直接出来。第三Django 对 Channels 的支持比较成熟后面做 WebSocket 增量推送时不用另起服务。前端选 Vue是因为它对新手友好、生态齐全。Vue 的单文件组件配合 Element Plus 这类 UI 库可以快速搭出仪表盘页面ECharts 的可视化图表接入成本低数据格式调整一下就能渲染。我用的 Vue 3 Vite Vue Router Pinia 这个组合开发和构建体验都很顺。至于为什么用机器学习而不是深度学习我在第三章节详细说。简单先给个结论在房价这种小规模表格数据上调好特征的传统机器学习模型效果大概率优于深度学习而且可解释性更强答辩时更好讲。1.3 架构分层从数据到展示的四层闭环整个系统可以划分为四个层次每一层的职责非常清晰数据层负责数据采集与预处理包括开源数据集、爬虫数据、数据清洗和特征工程产物。模型层负责训练房价预测模型输出模型文件和特征配置用 joblib 序列化保存。服务层基于 Django 提供 REST API负责数据查询、统计分析、模型预测调用以及 WebSocket 实时推送。表现层基于 Vue 构建前端应用展示数据大盘、价格趋势、预测结果和管理页面。这个分层的价值在于答辩时你可以顺着数据流讲原始数据进来之后经过清洗变成特征特征喂给模型得到预测结果预测结果通过接口暴露给前端前端以图表形式呈现。评委沿着这条线提问你很难被问倒因为每一层你都实际做过。2. 数据准备与特征工程2.1 数据来源开源数据集和爬虫怎么选房价预测最常见的数据集是波士顿房价但这个数据集在 scikit-learn 1.2 版本之后被移除了原因是它存在一些伦理争议和统计问题。如果你用新版 sklearn直接from sklearn.datasets import load_boston会报错。我用的是加州住房数据集fetch_california_housing包含两万条左右样本、八个特征刚好适合做毕设。它的特征是区域层面的统计值比如该区域平均收入、房龄中位数、房间数中位数、人口等目标值是该区域房价中位数单位是十万美元。另一个常用方案是爬取链家、贝壳这类平台的二手房数据。这种方式的好处是数据真实、字段丰富有小区名、户型、朝向、楼层、装修、挂牌单价等做特征工程时最有发挥空间。但要注意爬虫要控制节奏不要高频请求数据量也不宜贪大抓几百条到一千条足够支撑项目演示。我个人最后用了加州住房数据集做主模型数据同时抓了几百条真实房源做补充展示让答辩材料里既有标准数据集也有真实业务数据比单一来源更有说服力。2.2 清洗规则缺失值、异常值和单位换算数据清洗是评委大概率会问到的环节因为它在整个项目里最琐碎、最能体现工程素养。以爬取的房源数据为例我最常遇到的几个问题总价和单价字段不一致有的房子按总价挂牌有的按单价挂牌必须统一换算成“元/平方米”。楼层字段是“18/33”这种格式需要拆分成“当前楼层”和“总楼层”两个特征再进一步生成“楼层率”。朝向字段缺失很多老旧小区没有写朝向我选择填充“未知”而不是删掉整行这样能保留样本量。面积字段存在异常值比如 5 平方米的“杂物间”和 500 平方米的“别墅”出现在同一批数据里需要加业务逻辑过滤否则模型会被极端值带偏。针对这些情况我建了一张清洗规则表每一步都记录处理逻辑答辩时直接拿这张表讲既清晰又有说服力。字段常见问题处理方式面积极小或极大异常值按小区位数的 1%/99% 分位截断单价与总价不一致换算为统一单位元/平方米楼层字符串形式“18/33”拆分为当前楼层、总楼层、楼层率朝向缺失、口径混乱统一映射为枚举值缺失填“未知”装修精装/简装/毛坯独热编码为多个二值特征挂牌时间时间格式不统一统一为日期格式计算挂牌天数这个规则表是纯手工总结出来的不同小区实际分布差异很大建议大家先画箱线图看一眼分布再决定截断阈值。一上来就删数据很容易把好样本误删。2.3 特征工程把业务直觉变成模型能懂的数值特征工程做得好不好直接决定模型分数的上限。加州住房数据集的特征已经处理过了但爬虫抓到的原始字段不能直接用需要做变换。我处理的特征包括三组数值特征面积、总楼层、当前楼层、房龄、卧室数、客厅数。这类特征我会先做标准化把量纲统一尤其对线性模型很重要。类别特征朝向、装修、电梯、所在城区。朝向和装修这类用独热编码转换成多个 0/1 列所在城区如果类别很多可以用目标编码用该城区历史平均房价作为编码值。衍生特征楼层率当前楼层/总楼层、单位面积价格、距离地铁站的直线距离、小区均价与整体均价的比值。其中“楼层率”是个很实用的衍生特征。经验上电梯房的中高楼层价格更高楼梯房则是低楼层更受欢迎单纯用“当前楼层”一个数字很难表达这种关系但“楼层率”配合是否有电梯就能让模型学到这个规律。还有一个容易忽略的点训练集和测试集的特征处理必须一致。很多人先对全量数据做标准化再划分训练集和测试集这是错的。正确做法是先切分再用训练集的均值和标准差去变换测试集否则会引入数据泄漏测试指标虚高。2.4 相关性分析用热力图找到影响房价的关键变量在做模型之前我习惯先画相关性矩阵热力图把每个特征和目标值的皮尔逊相关系数列出来。这一步看似简单但价值很大一方面帮你筛掉无关特征另一方面答辩时可以证明你不是拍脑袋选特征的。在我跑的加州住房数据上几个关键变量和房价中位数的相关度排序大致是收入水平 房龄 房间数量 人口密度。收入水平和房价呈明显正相关房龄呈负相关这和直觉一致。这个结果可以直接在答辩时展示评委看到你的分析过程和业务逻辑对得上第一印象就稳了。画图我用的是 Python 的 matplotlib 和 seaborn。核心代码不超过十行热力图输出后保存成图片后续放到演示 PPT 里也好看。import matplotlib.pyplot as plt import seaborn as sns corr df.corr() plt.figure(figsize(12, 10)) sns.heatmap(corr, annotTrue, fmt.2f, cmapRdBu_r, squareTrue) plt.title(Feature Correlation Heatmap) plt.savefig(correlation.png, dpi200, bbox_inchestight)这里有一个实操细节热力图上如果特征数量太多每个格子的数字会挤成一团看起来像马赛克。建议先手动筛选出和目标值相关性绝对值大于某个阈值的特征再画子图别把所有特征全丢进去。3. 房价预测模型从线性回归到集成学习3.1 基线模型先跑通线性回归再谈优化不要一上来就堆 XGBoost。我的习惯是先跑一个最基础的线性回归作为性能基线和代码正确性的校验。如果线性回归结果一塌糊涂说明前面数据处理或者特征工程一定出了问题这时候去调复杂模型毫无意义。线性回归的原理一句话就能讲清楚假设目标值和特征是线性关系通过最小化预测值与真实值的均方误差来求解系数。评价回归模型我主要看两个指标R² 决定系数表示模型解释了多少方差越接近 1 越好RMSE 均方根误差表示预测值和真实值的平均偏差单位与目标值一致便于理解。在加州住房数据集上跑的第一个线性回归测试集 R² 大约是 0.71RMSE 约 0.62目标值单位是十万美元。这个结果不算好但已经能说明特征整体有效。我把它记录下来作为基线后面每个模型都跟它对比。3.2 Lasso 正则化牺牲一点拟合换可解释性第二个模型我选 Lasso 回归。Lasso 在线性模型上加了一个 L1 正则项它有一个特性会把部分特征的系数压缩到 0相当于自动做特征选择。这在答辩时是一个非常值得展开的点——你用实际数据证明了模型自己挑出了哪些特征。调参关键是正则化系数 alpha。我用交叉验证搜索了一组候选值从 0.001 到 0.1 按对数刻度排列最后选出的 alpha 在 0.01 这个量级。测试集 R² 比线性回归略高几个百分点同时有几个系数为零的特征被剔除。from sklearn.linear_model import LassoCV from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_s scaler.fit_transform(X_train) X_test_s scaler.transform(X_test) lasso LassoCV(alphasNone, cv5, random_state42) lasso.fit(X_train_s, y_train) print(fbest alpha: {lasso.alpha_:.4f}) print(coefficients:, lasso.coef_)一个常见的误区是 alpha 设得过大。之前我试过 alpha1.0结果所有特征系数全都变成 0模型输出恒定值R² 直接变成负数。从那次之后我养成了先画“系数随 alpha 变化路径图”的习惯看特征先被压缩、后消失的过程。3.3 树模型进阶随机森林和 XGBoost 的可解释性线性模型表现有限因为特征和房价之间还有大量非线性关系。比如同样增加 10 平方米小户型和大户型的价格变化幅度完全不同这种交互效应线性模型很难捕捉。这时候就该上树模型。我先后试了随机森林和 XGBoost。随机森林通过构建多棵决策树、对结果取平均来降低过拟合对异常值和小噪声比较鲁棒。XGBoost 是梯度提升框架每一棵树都在拟合前面所有树的残差表达能力更强但调参也更敏感。在同一个测试集上随机森林测试集 R² 到了 0.85 左右XGBoost 到了 0.88 左右已经明显超过线性基线。不过要注意树模型如果不加约束训练集分数很容易冲到 0.97 以上而测试集分数跟不上这就是过拟合信号。我的应对方式是对树的深度、叶子节点最小样本数做了限制并用早停法控制提升轮数。树模型还有一个很有价值的产出特征重要性排序。随机森林可以输出每个特征对预测的贡献占比我把它做成柱状图放到答辩 PPT 里。评委问“你凭什么说这几个特征是关键特征”时直接把图亮出来就好。特征重要性的排序通常和相关性热力图一致但又不完全相同因为它捕捉的是特征在高阶交互中的价值。3.4 模型对比用一张表终结选型争论我最终把所有模型的测试集指标汇总成了一张表。不同数据集上具体数值会有差异但趋势固定树模型明显优于线性模型正则化和特征工程都能带来边际提升。模型测试集 R²RMSE十万美元训练时间说明线性回归0.710.62秒级基线可用作正确性校验Lasso 回归0.740.58秒级自动特征选择可解释性强随机森林0.850.45十几秒稳定调参简单XGBoost0.880.41半分钟左右集成树效果最好最后部署到 Django 里的是随机森林原因有三它的效果和 XGBoost 差距很小对超参数不敏感代码里不用写一堆调参逻辑模型体积小加载快特征重要性已经足够支撑答辩。部署前我用 joblib 把训练好的模型和标准化器一起打包import joblib joblib.dump(model, model/random_forest.pkl) joblib.dump(scaler, model/scaler.pkl)这里有个版本坑需要提醒joblib 在跨 Python 小版本加载时偶尔会报不兼容错误最稳妥的方案是训练环境和部署环境用同一个 Python 版本。我就是因为实验室电脑和笔记本 Python 版本不一致部署时模型加载直接报错折腾了半天才发现。4. Django 后端接口与业务闭环4.1 工程骨架与数据库设计后端我用 Django 4 Django REST framework Channels 搭的。先创建项目和应用django-admin startproject house_project cd house_project python manage.py startapp house核心数据模型有两个。第一个叫 HouseInfo存房源基础信息和实际成交单价对应数据管理页面第二个叫 PredictRecord存每次预测的输入特征、预测价格和预测时间用于在日志页面展示历史预测记录。from django.db import models class HouseInfo(models.Model): area models.FloatField(面积㎡) house_age models.IntegerField(房龄年) floor_rate models.FloatField(楼层率) has_elevator models.BooleanField(有无电梯) total_price models.FloatField(总价万元) unit_price models.FloatField(单价元/㎡) district models.CharField(所在城区, max_length32) created_at models.DateTimeField(入库时间, auto_now_addTrue) class PredictRecord(models.Model): features models.JSONField(预测输入特征) predicted_price models.FloatField(预测价格元/㎡) created_at models.DateTimeField(预测时间, auto_now_addTrue)Django 的 JSONField 在存档输入特征时特别方便直接把前端传过来的特征字典原样存进去省去建一堆字段的麻烦以后查记录也直观。4.2 用 DRF 快速构建查询与统计接口接口设计我用了 RESTful 风格前后端通过 JSON 通信。主要接口有这几个GET /api/houses/分页返回房源列表支持按城区、价格区间过滤。GET /api/houses/statistics/按月统计平均挂牌价走势供前端画折线图。GET /api/houses/district/按城区统计平均单价供前端画柱状图或地图。POST /api/predict/接收房源特征返回模型预测价格。GET /api/predict/records/查询历史预测记录。GET /ws/price/WebSocket 通道实时推送新增房源和预测结果。以POST /api/predict/为例核心逻辑是加载模型文件、标准化输入、返回预测值。import joblib import numpy as np from rest_framework.views import APIView from rest_framework.response import Response model joblib.load(model/random_forest.pkl) scaler joblib.load(model/scaler.pkl) class PredictView(APIView): def post(self, request): features request.data.get(features) if not features: return Response({error: missing features}, status400) scaled scaler.transform(np.array(features).reshape(1, -1)) price float(model.predict(scaled)[0]) PredictRecord.objects.create( featuresfeatures, predicted_priceround(price, 2) ) return Response({price: round(price, 2), unit: 元/㎡})一个小经验模型文件千万别放在 views.py 里重复加载。如果在模块顶部加载一次所有请求共用同一份模型节省内存和时间如果放在每次请求里加载接口响应会慢到一个不可接受的程度答辩现场演示时就会很尴尬。4.3 跨域配置与前端联调约定前后端分离之后跨域问题躲不开。前端开发服务器跑在http://localhost:5173Django 跑在http://localhost:8000浏览器出于同源策略会拦截请求。我的处理方式是后端直接配django-cors-headers在前端开发阶段再用 Vite 的 proxy 做一层转发双保险。Django 侧的settings.py配置INSTALLED_APPS [ # ... corsheaders, ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, # ... ] CORS_ALLOWED_ORIGINS [ http://localhost:5173, http://127.0.0.1:5173, ]Vite 侧的代理配置在vite.config.js里export default { server: { proxy: { /api: { target: http://localhost:8000, changeOrigin: true } } } }用 Vite 代理之后前端代码里可以直接请求/api/predict/不需要写完整的http://localhost:8000前缀后面部署时只需改代理目标前端代码一行都不用动。4.4 WebSocket 增量推送让页面自己“长”出新数据这个功能是整个项目里答辩最亮眼的一部分。我实现了这样一个场景管理员在后台录入了一条新房源或者用户刚发起一次新的预测不需要刷新页面前端所有在线的客户端就能立刻看到数据更新——折线图自动延伸、列表自动新增一行。背后的技术是 Django Channels 提供的 WebSocket 支持。Channels 给 Django 加了一层异步能力处理长连接。核心是一个消费者类定义连接建立、断开、接收消息时的行为。import json from channels.generic.websocket import AsyncJsonWebsocketConsumer class PricePushConsumer(AsyncJsonWebsocketConsumer): async def connect(self): await self.channel_layer.group_add(price_group, self.channel_name) await self.accept() async def disconnect(self, close_code): await self.channel_layer.group_discard(price_group, self.channel_name) async def price_update(self, event): await self.send_json(event[data])在 Django 视图里每次新增房源或完成一次预测后向组推送消息from channels.layers import get_channel_layer from asgiref.sync import async_to_sync def notify_new_record(payload): channel_layer get_channel_layer() async_to_sync(channel_layer.group_send)( price_group, {type: price.update, data: payload} )这里有一个常见的坑Channels 的默认 channel layer 是 Redis但很多同学本地没有装 Redis项目跑不起来。实际上 Channels 内置了InMemoryChannelLayer只用于本地开发和演示配置很简单。CHANNEL_LAYERS { default: { BACKEND: channels.layers.InMemoryChannelLayer } }用内存 channel layer 时多个 uvicorn worker 进程之间没法互通消息但毕设演示通常只开一个进程完全够用。如果哪天要上生产环境再换 Redis 就行。前端接收推送的代码也很简单原生 WebSocket 就够用const ws new WebSocket(ws://localhost:8000/ws/price/) ws.onmessage (event) { const data JSON.parse(event.data) pointList.push(data) chart.setOption({ series: [{ data: pointList }] }) }当 WebSocket 推送触发 ECharts 的setOption时图表会自己动起来没有任何刷新操作。这个效果在答辩现场特别加分而且实现逻辑不复杂几句话就能讲清楚。5. Vue 前端可视化与交互5.1 页面规划仪表盘、趋势分析、单房预测、管理页前端页面我分了四个主模块每个模块对应一个 Vue 路由。首页是数据总览仪表盘顶部一行指标卡展示房源总数、平均单价、最高单价、在售套数中间区域放城区均价柱状图和面积-单价散点图。次页是趋势分析页用折线图展示近一年房价走势用热力图展示特征相关性这个页面的素材全部来自后端统计接口。第三页是单房预测页也是最核心的功能页。用户填写面积、房龄、楼层率、朝向、装修等表单点击预测按钮后端实时返回模型预测价格前端展示预测结果卡片同时把本次预测记录插入下方的历史记录表。第四页是数据管理页通过 Django Admin 嵌入的方式管理房源数据前端只展示入口不重复开发。工具类页面还有一个 WebSocket 演示页专门用来展示增量推送直播效果。这个页面平时不体现在导航里答辩时单独打开。5.2 ECharts 图表配置让数据自己说话前端可视化选的是 ECharts它最成熟、文档最全遇到问题基本都能搜到答案。我只挑了四个核心图表折线图、散点图、柱状图、热力图。每个图表的配置都不复杂关键在于数据格式。以散点图为例配置一个双轴散点X 轴是面积Y 轴是单价点的大小代表总价颜色代表城区一张图塞进三个维度。import * as echarts from echarts const chart echarts.init(document.getElementById(scatterChart)) chart.setOption({ xAxis: { name: 面积㎡ }, yAxis: { name: 单价元/㎡ }, series: [{ type: scatter, data: houses.map(h ({ value: [h.area, h.unit_price, h.total_price], itemStyle: { color: districtColor[h.district] } })) }] })这里有个细节如果你的散点数据点很多建议把data里的symbolSize改成按第三个维度映射不然图表会变成一坨密度相似的圆点视觉上完全不区分信息。地图类可视化我没有用 ECharts 自带的地图因为成都、长沙这类城市的手工地理数据需要额外加载并校准边界折腾起来性价比低。更轻量的方案是用散点图叠加在城市底图的图片上按经纬度映射坐标演示效果轻盈且稳定。这个方案在毕设场景下非常推荐。5.3 Vue Router 路由与请求封装前端路由我用的 Vue Router懒加载配置好之后每个页面独立打包首屏加载速度明显提升。路由配置类似这样const routes [ { path: /, component: () import(/views/Dashboard.vue) }, { path: /trend, component: () import(/views/Trend.vue) }, { path: /predict, component: () import(/views/Predict.vue) }, { path: /manage, component: () import(/views/Manage.vue) } ]请求层我封装了一个 axios 实例统一处理 baseURL、超时时间、错误提示避免每个页面重复写拦截逻辑。import axios from axios import { ElMessage } from element-plus const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.response.use( response response.data, error { ElMessage.error(error.response?.data?.message || 请求失败) return Promise.reject(error) } ) export default request用请求函数时业务代码会变得非常干净。预测接口的调用就一行const res await request.post(/predict/, { features: formFeatures })状态管理我用的 Pinia主要存全局的房源列表和当前筛选条件。如果项目里只有两三个页面共享数据其实用 Pinia 有点过度设计但答辩时可以多一个“页面状态管理方案选型”的谈资并且后续扩展不会乱套。5.4 联调踩坑接口字段约定与开发环境代理前后端联调中最容易出的问题就是字段名对不上。后端返回unit_price前端读price结果页面上永远显示 undefined。我的解决办法是列一张接口字段对照表前后端各留一份开发时按表对接。我贴一个我自己的对照片段前端字段后端字段含义类型areaarea面积floatunitPriceunit_price单价floatdistrictdistrict城区stringhouseAgehouse_age房龄int这个对照表看似简单但它让我少走了很多弯路。特别是当接口返回字段几十个时不约定好规范排查起来非常痛苦。另一个坑是表单前后端校验不一致。前端限制了面积必须大于 0后端接口也该同样校验。别人直接拿 Postman 绕过前端发送非法参数时后端至少返回 400 而不是 500。我在 Django 里就吃了这个亏用户输入一个超大负数面积模型预测出荒谬结果图表直接崩。后来我在接口层加了参数范围校验非法输入统一返回 400 错误前端再友好提示。6. 答辩实操指南常见问题与避坑清单6.1 开场三分钟讲稿怎么组织答辩开场的三分钟直接决定评委的注意力。我的讲稿结构是这样组织的先抛出业务背景也就是“房价预测对购房者和市场分析者有什么价值”紧接着说自己做了什么从数据清洗、特征工程、模型选型一路带过再演示系统先看数据大盘再演示单房预测最后展示 WebSocket 实时推送最后总结技术难点和下一步计划。这里要注意不要按代码文件一个个讲而要按“数据怎么流转”讲数据从哪里来、变成了什么特征、模型怎么用特征预测出价格、价格怎么显示在页面上。这条流水线讲清楚了评委对你的系统全貌就有了准确认知后续提问也不会跑偏。我的实际操作中开场大概一页 PPT 的背景介绍用了三十秒整个流程控制在十分钟内剩五分钟给评委提问。时间规划好了答辩节奏基本可控。6.2 高频问题速查表几乎必问的十个问题答辩中有些问题几乎是必考的提前准备是性价比最高的复习方式。我整理了一张表每一条都是评委大概率会追着问的点。评委问题回答思路为什么用 MSE 而不是 MAEMSE 连续可导对较大误差惩罚更重梯度优化更稳定但解释性比 MAE 差所以同时报告 RMSE为什么没用深度学习表格数据规模小树模型拟合能力强且可解释性更好深度学习在小样本表格数据上常见劣势Lasso 和岭回归区别Lasso 用 L1 正则会把系数压到 0适合作特征选择岭回归用 L2 正则只缩小系数但不会归零怎么判断模型过拟合对比训练集和测试集 R² 差距差距过大说明过拟合再用交叉验证分数进一步确认特征重要性怎么算的随机森林基于袋外误差或节点纯度下降来度量展示特征重要性排序图说明数据量这么小够吗加州住房约两万条配合交叉验证可以给出可靠估计实际部署中可通过爬虫和增量入库持续扩充业务上预测价格有什么偏差模型预测的是挂牌价的表层影响因素不包含学区政策、市场情绪等非结构化因素模型多久重新训练一次设计了一个指标当累积预测数据的 MAE 超过阈值触发后台重跑训练脚本WebSocket 和 HTTP 区别WebSocket 是长连接服务端可主动推送HTTP 是请求-响应模式服务端不能主动发数据你项目里最大的技术难点我梳理为跨域前后端联调以及模型上线加载的体积与稳定问题具体经验和优化过程展开讲答辩时最忌讳的是背答案。每道题你都先用两三句话讲清逻辑再结合自己项目里的数据说一个实例评委会明显感觉到你真的做过了。6.3 现场演示脚本先展示什么再展示什么演示环节的先后顺序很有讲究。我的顺序是第一步先打开首页数据大盘。展示房源总数、均价、分布图评委一眼看到信息量会觉得“数据量到位”。第二步切到趋势分析页用热力图和相关性分析引出特征工程告诉评委“我理解哪些因素影响房价”。第三步切到单房预测页手动输入几个真实房源参数点击预测。这时要选择一个和真实挂牌价接近的户型让结果看起来可信。第四步展示 WebSocket 页面。让管理员后台新增一条房源或提交一条预测页面图表自动更新不用刷新。评委看到这个动态效果基本就对一个毕设项目的前端深度有了认可。现场演示最大的风险是网络和依赖服务。我的建议是所有接口全部走本地服务不要依赖外网演示前五分钟先清一遍流程确认模型文件能正常加载。另外如果 WebSocket 演示时前端页面没反应不要着急重启服务先检查端口是否被占用多半是开发者工具里残留了旧连接。6.4 我踩过的坑清单给后来者的一份护身符整个项目做下来有几个坑我每次想起来都觉得值得提醒后来的同学。第一个坑是 sklearn 版本问题导致开篇就卡住。旧教程全是load_boston新版直接删了翻文档才发现已经换成fetch_california_housing这个改动让很多同学开局就怀疑人生。解决方案就是换数据集没有任何兼容的捷径。第二个坑是模型文件路径写死。之前我在 Windows 上开发模型文件放在桌面某个路径后来项目迁移到 Linux路径找不到模型加载直接抛异常。后来我统一改成用BASE_DIR拼接相对路径项目迁移到哪都不怕。路径问题看着小部署时才暴露提前处理省心。第三个坑是跨域配置漏了 OPTIONS 预检请求。当时前端 POST 请求一直报跨域排查半天才发现CORS_ALLOWED_ORIGINS配了但中间件顺序和预检响应头没配好。这个问题的排查思路是先在浏览器开发者工具里看Access-Control-Allow-Origin响应头再决定到底是前端还是后端的问题别乱改代码。第四个坑是模型效果在演示时翻车。我训练时用的随机森林测试集 R² 0.85 看着不错但现场输入一个超出训练数据分布范围的极端户型预测结果就特别离谱。后来我在前端表单里加了输入范围校验并在后端也同步校验保证演示时输入的参数都在训练数据范围内预测结果自然稳定。还有第五个坑是答辩 PPT 里放模型代码太多。评委根本没时间看代码应该放数据流转图、特征热力图、模型对比表、系统截图。代码最多贴一个预测接口的关键函数就够了。图比代码有说服力数据比文字有说服力这个颠扑不破。最后再说一点个人体会。房价预测这个题目做完之后回头看最值钱的部分其实不是那几个模型指标而是数据处理和系统集成的完整链路。很多同学交上去的毕设是从网上找一段模型代码另找一份前端模板拼起来自己根本不知道中间怎么衔接。你如果认真把数据清洗表、特征对比图、模型对比表、接口文档全部留档答辩时材料一摆评委自然会认为这是一个有全局视野的工程实践。真心建议后来者不要只盯着模型分数调参把系统讲完整、把过程讲扎实才是毕设答辩最稳的路线。