實戰(zhàn):從數據清洗到PyQt5界面開發(fā)全流程)
簡介這是一份面向Python數據分析與GUI開發(fā)學習者的二手房價分析與預測期末大作業(yè)項目包基于Python和PyQt5完成覆蓋數據讀取、清洗統(tǒng)計、特征分析、模型訓練、房價預測和可視化展示等完整環(huán)節(jié)適合期末答辯、課程設計及自學進階。壓縮包共17個文件包含6個Python源碼文件、6張PNG界面素材、2個pyc緩存、1個Qt界面文件、1個CSV數據集和1個說明文檔整體體積僅132KB目錄結構清晰便于按模塊閱讀與復用。項目中利用Pandas完成數據清洗與統(tǒng)計摘要Scikit-Learn構建預測模型Matplotlib生成分布與結果圖表并通過PyQt5搭建交互界面實現加載數據、選擇模型、查看結果的一站式操作。源碼附帶詳細注釋可將Pandas、Scikit-Learn、Matplotlib、PyQt5各技術點與工程實現一一對應同時內置可用的CSV數據集與界面素材省去自行準備數據的麻煩。目前已有343人學習適合需要完整案例參考的在校學生和初級開發(fā)人員。1. 期末大作業(yè)的二手房價分析與預測系統(tǒng)它到底解決了什么問題期末項目選“二手房價分析與預測”這個題目幾乎是每年 Python 課程設計的保留曲目。需求看起來很簡單拿到一批二手房數據訓練一個能預測價格的模型再用圖形界面把結果展示出來。但真做起來數據清洗、特征構造、模型調參、界面聯(lián)調每一環(huán)都夠折騰幾天。這套系統(tǒng)就是把這些環(huán)節(jié)串成一個完整的閉環(huán)用 Python 處理數據、訓練模型用 PyQt5 做桌面界面用戶在界面上輸入戶型、面積、樓層、朝向等條件就能看到預測價格和周邊小區(qū)對比。適合正在做期末大作業(yè)、畢業(yè)設計或者想快速搭一個數據展示型桌面應用的讀者。想用最短路徑把作業(yè)跑通同時把每個模塊的坑提前踩掉這篇就是按這個思路寫的。2. 二手房價數據集的清洗與特征工程喂給模型之前先做這幾步2.1 先看數據長什么樣字段類型與缺失值排查打開二手房價數據集的 CSV 文件常見字段大概是這幾類小區(qū)名稱、區(qū)域、戶型室廳衛(wèi)、面積、朝向、樓層、裝修、建筑年代、總價、單價。第一件事不是直接訓練而是把數據讀進來逐字段看一眼。很多二手房價數據在采集時就有問題比如“朝向”列出現“南北通透”“南 北”兩種寫法“樓層”列混著“低樓層/共6層”這種文本這類字段直接丟給模型會變成災難。我一般會把數據讀取和初步體檢寫成一個腳本先跑一遍再決定怎么做特征import pandas as pd df pd.read_csv(house_data.csv, encodingutf-8) print(df.shape) print(df.dtypes) print(df.isnull().sum()) # 抽樣看幾行確認文本字段的寫法 print(df.head(10).to_string())這里有幾個關鍵點。encoding參數特別容易踩坑很多爬下來的 CSV 是gbk編碼直接用utf-8讀會拋UnicodeDecodeError。報錯時先嘗試encodinggbk或encodinggb18030。isnull().sum()是看缺失值的入口先知道哪些列缺得厲害再決定是刪行、填值還是拆特征。打印dtypes則是看數據類型的隱患比如面積列被讀成object說明里面有類似“89.5㎡”這種帶單位的臟數據。2.2 把文本特征變成數值特征面積、樓層、朝向的處理方式文本特征不處理模型就訓不了。目標是把每一列都收拾成干凈的數值或類別編碼。先說面積列如果里面混了單位先用正則把數字提取出來樓層列比較典型常見寫法是“低樓層/共6層”或“3/6層”這里可以拆成兩個特征當前樓層數、總樓層數再用總樓層數算出一個相對位置。朝向這類文本列用get_dummies轉成啞變量或者用LabelEncoder編碼。我的做法是能拆出數值就拆數值朝向這種沒有大小關系的列才做啞變量。import re # 提取面積中的數字89.5㎡ - 89.5 df[area] df[area].str.extract(r(\d\.?\d*)).astype(float) # 樓層列處理低樓層/共6層 - current1, total6 def parse_floor(s): m re.search(r共(\d)層, str(s)) c re.search(r(\d)/, str(s)) if c and m: return int(c.group(1)), int(m.group(1)) return None, None df[current_floor], df[total_floor] zip(*df[floor].apply(parse_floor)) df[floor_ratio] df[current_floor] / df[total_floor] # 朝向只保留第一個方向減少類別數 df[direction] df[direction].str.split(/).str[0] df pd.get_dummies(df, columns[direction], prefixdir)str.extract配合正則提取數字是處理混合格式最順手的方式記得提取后要astype(float)轉換不然還是文本。floor_ratio的含義是“樓層的相對高度”0.3 代表在總樓層偏下位置0.8 代表在高區(qū)。這個特征對房價的影響其實是有的高層采光好但也要看有沒有電梯。朝向拆成啞變量之前先只保留第一個方向能讓列數少一點避免“南北”“南 北”這種同一含義拆成兩列。2.3 對總價做歸一化為什么預測總價比預測單價更穩(wěn)作業(yè)里很多人直接拿總價做預測目標但二手房數據里的總價受面積影響太大同樣 300 萬30 平米和 120 平米的房子完全不是一個量級。這里有一個常見分歧預測總價還是預測單價。我建議做單價預測因為單價剔除了面積的影響更能反映地段、樓層、裝修這些特征的作用。預測完之后展示時再乘上用戶輸入的面積這樣用戶看到的總價也更直觀。# 計算單價作為預測目標 df[unit_price] df[total_price] / df[area] # 對連續(xù)特征做標準化 from sklearn.preprocessing import StandardScaler feature_cols [area, bedrooms, living_rooms, bathrooms, floor_ratio, building_age] scaler StandardScaler() df[feature_cols] scaler.fit_transform(df[feature_cols])單位價的單位是“萬元/平米”還是“元/平米”取決于原始總價單位分析時要先看好數據集里的字段說明。用StandardScaler做標準化的一個實際好處是后面用線性回歸、嶺回歸這類模型時收斂更快系數也能直接比較大小用來判斷“哪個特征對房價影響更大”。如果數據集里的房子坐標、板塊信息可用還可以考慮加“距地鐵站距離”這類衍生特征這類資料往往要去高德或百度地圖的 API 算作業(yè)時間緊的話用小區(qū)均價替代也行。重要提示fit_transform只應該在訓練集上調用驗證集和測試集要用訓練集擬合好的scaler做transform不要在驗證集上重新算均值方差。這是很多作業(yè)被老師看出“數據泄漏”的點后面我還會細說。3. 房價預測模型選型與訓練從線性回歸到集成模型的全過程3.1 先用線性回歸打基線結果再差也要有一個參照拿到干凈數據后的第一件事不是直接上隨機森林、XGBoost而是先用一個最樸素的線性回歸跑一遍得到一組“下限分數”。這樣做的價值是后面換任何模型都有了參照系——新模型比線性回歸高多少分才算真正有效果。很多作業(yè)翻車就是先上復雜模型結果調參半天也不知道自己調到什么程度算“好”。from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score X df[feature_cols [c for c in df.columns if c.startswith(dir_)]] y df[unit_price] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) print(R2:, r2_score(y_test, y_pred))這套流程是標準的分割、訓練、評估三件套。random_state42固定隨機種子保證每次運行結果一致這是作業(yè)里必須養(yǎng)成的習慣不然答辯的時候跑兩次結果不一樣沒法跟老師解釋。R2在房價預測里一般能到 0.6~0.8 就算不錯的線性結果如果低于 0.4說明數據里非線性關系強或者特征太粗糙這時候再去換集成模型才有意義。3.2 換隨機森林處理非線性關系的主要手段線性回歸跑完接下來上隨機森林。隨機森林對異常值和特征尺度不敏感不用再擔心標準化的問題而且能給出特征重要性方便你在答辯時跟老師說“哪些特征對房價影響最大”。隨機森林的主要參數是n_estimators、max_depth、min_samples_split這些參數不要盲目抄網上的值要結合數據量來定。from sklearn.ensemble import RandomForestRegressor rf RandomForestRegressor( n_estimators200, max_depth12, min_samples_split5, min_samples_leaf2, random_state42, n_jobs-1 ) rf.fit(X_train, y_train) y_pred_rf rf.predict(X_test) print(RF RMSE:, mean_squared_error(y_test, y_pred_rf, squaredFalse)) print(RF R2:, r2_score(y_test, y_pred_rf)) print(pd.Series(rf.feature_importances_, indexX.columns).sort_values(ascendingFalse))這里n_estimators200和max_depth12是我試過比較多組之后覺得比較穩(wěn)的起點。數據量在幾千條的時候200 棵樹比 100 棵的誤差略低再往上就邊際遞減了。max_depth如果設得太深比如 20 以上很容易過擬合訓練集 R2 接近 1但測試集掉得厲害。特征重要性輸出之后通常會發(fā)現面積、建筑年代、floor_ratio 排在最前朝向的啞變量普遍靠后這是數據本身的信號不代表朝向沒用只能說在這個數據集里變化不夠大。3.3 再用 XGBoost 調高一點網格搜索要控制時間隨機森林效果如果已經不錯可以再試 XGBoost。XGBoost 在房價這類表格式數據上成績穩(wěn)定而且處理缺失值、非線性都比線性模型強。常見的調參順序是先定學習率learning_rate和樹數量n_estimators再調max_depth和min_child_weight最后調subsample和colsample_bytree。作業(yè)時間有限不要做全量網格搜索用GridSearchCV也要控制參數組合數量不然一個交叉驗證可能跑半小時以上。import xgboost as xgb from sklearn.model_selection import GridSearchCV xgb_model xgb.XGBRegressor( learning_rate0.05, n_estimators300, max_depth6, subsample0.8, colsample_bytree0.8, random_state42 ) param_grid { max_depth: [4, 6, 8], subsample: [0.7, 0.8, 0.9] } grid GridSearchCV( xgb_model, param_grid, cv5, scoringr2, n_jobs-1, verbose1 ) grid.fit(X_train, y_train) print(grid.best_params_) print(grid.best_score_)cv5的意思是五折交叉驗證每一折都用訓練集的一部分做驗證最終best_score_是五折的平均 R2這比單次劃分測試集更穩(wěn)定答辯時也是一個可說的亮點。n_jobs-1會用滿所有 CPU 核心但如果你的電腦是 4 核老筆記本跑網格搜索時記得把verbose1打開能看到進度不然看起來像死機。還有一個實操細節(jié)XGBoost 在數據量小的時候容易過于自信測試集 R2 高但真實場景不一定好所以在作業(yè)里最好同時保留隨機森林和 XGBoost 兩個模型界面上做一個模型下拉切換效果對比更直觀。參數調優(yōu)是門玄學沒有一組參數適合所有數據集。比較穩(wěn)的辦法是固定一顆隨機種子用交叉驗證評估每次只動一個參數記錄分數變化這樣既能看到趨勢也能省時間。4. 用 PyQt5 把預測模型包裝成交互界面從模型文件到桌面應用4.1 界面布局輸入區(qū)和結果區(qū)怎么規(guī)劃預測模型跑通后下一步就是用 PyQt5 做一個桌面界面。界面一般分成三個區(qū)域左側是房源信息輸入區(qū)包括面積、戶型、朝向、樓層、建筑年代等右側是預測結果展示區(qū)顯示預測單價、總價和一個小區(qū)的對比底部放一個“開始預測”按鈕和狀態(tài)欄。PyQt5 的布局用QFormLayout加下拉框QComboBox和數值輸入框QSpinBox的組合比手寫坐標定位方便得多。from PyQt5.QtWidgets import ( QApplication, QMainWindow, QWidget, QFormLayout, QComboBox, QSpinBox, QDoubleSpinBox, QPushButton, QLabel, QVBoxLayout, QHBoxLayout ) class HousePriceWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(二手房價格分析與預測系統(tǒng)) self.setMinimumSize(720, 480) central QWidget() self.setCentralWidget(central) layout QVBoxLayout(central) form QFormLayout() self.area_input QDoubleSpinBox() self.area_input.setRange(20.0, 300.0) self.area_input.setSuffix( ㎡) self.room_input QSpinBox() self.room_input.setRange(1, 6) self.floor_input QSpinBox() self.floor_input.setRange(1, 40) self.direction_input QComboBox() self.direction_input.addItems([東, 南, 西, 北]) form.addRow(面積:, self.area_input) form.addRow(臥室數:, self.room_input) form.addRow(所在樓層:, self.floor_input) form.addRow(朝向:, self.direction_input) layout.addLayout(form)這里每個輸入控件的數據范圍都做了約束比如面積限制在 20 到 300 平米這是根據數據集分布來的。QDoubleSpinBox支持小數適合面積QSpinBox只能整數適合臥室數這類離散值。QComboBox做朝向選擇避免用戶輸入五花八門的文本這是桌面應用里很討巧的設計——從源頭杜絕臟數據。4.2 按鈕事件與預測邏輯打通加載模型、構造特征、輸出結果界面搭好之后核心工作量在“點按鈕之后干什么”。我習慣把模型保存成文件界面啟動時用joblib加載而不是每次點按鈕都重新訓練。預測的邏輯鏈是讀取界面控件里的值 → 按訓練時的順序構造特征向量 → 喂給模型 → 把輸出格式化成“單價 總價”展示。這里最容易出錯的點是特征順序錯亂。訓練時的特征列順序是固定的如果界面這邊少傳了一列、多傳了一列預測結果就跑偏而且這種錯很難查。import joblib # 訓練完成后保存模型和特征列 # joblib.dump(model, model_rf.pkl) # joblib.dump(list(X.columns), feature_columns.pkl) class HousePriceWindow(QMainWindow): def __init__(self): # ... 上面的控件初始化代碼 ... self.predict_btn QPushButton(開始預測) self.result_label QLabel(預測結果將顯示在這里) layout.addWidget(self.predict_btn) layout.addWidget(self.result_label) self.predict_btn.clicked.connect(self.on_predict) def on_predict(self): self.model joblib.load(model_rf.pkl) self.feature_cols joblib.load(feature_columns.pkl) area self.area_input.value() rooms self.room_input.value() floor self.floor_input.value() direction self.direction_input.currentText() # direction 的啞變量必須與訓練時一致 feat {} for c in self.feature_cols: if c.startswith(dir_): feat[c] 1 if c fdir_{direction} else 0 elif c area: feat[c] area elif c floor_ratio: feat[c] floor / (floor 9) # 假設數據中位總樓層 else: feat[c] 0 import pandas as pd sample pd.DataFrame([feat])[self.feature_cols] pred self.model.predict(sample)[0] total_price pred * area self.result_label.setText( f預測單價: {pred:.0f} 元/平米\n f預測總價: {total_price:.0f} 元 )這段代碼里有一個關鍵細節(jié)啞變量是按feature_cols里的順序生成的而且只生成訓練時出現過的方向列。如果訓練數據里有“東南”這個朝向界面里卻沒有這個選項預測時就會漏列并報錯。解決辦法就是上面這種“從feature_cols反推構造字典”的思路保證列名、列順序永遠跟著訓練時的記錄走。floor_ratio的計算這里用了floor / (floor 9)來模擬相對樓層實際作業(yè)時可以簡化成從界面?zhèn)骺倶菍雍退跇菍右黄鹚愀鼫蚀_。關于模型文件保存有一套配套命令先跑# 訓練完模型后執(zhí)行保存 import joblib joblib.dump(rf, model_rf.pkl) joblib.dump(list(X.columns), feature_columns.pkl)保存兩個文件的原因是為了還原特征順序這也避免了把scaler和模型綁在一起時出現的狀態(tài)錯亂。如果數據里還有標準化步驟最好用joblib把scaler也保存成一個文件加載模型后先做標準化再喂數據這個順序別顛倒。4.3 PyQt5 內嵌 matplotlib用圖表把預測結果講清楚只有一行數字的界面顯得單薄加分項是在界面里加一個圖表展示用戶輸入的房源與周邊小區(qū)均價對比或者展示該小區(qū)歷史價格走勢。PyQt5 內嵌 matplotlib 的常見做法是用FigureCanvasQTAgg把畫布嵌到窗口里替代傳統(tǒng)的plt.show()彈窗模式。import matplotlib.pyplot as plt from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure class ChartWidget(FigureCanvas): def __init__(self, parentNone): self.fig Figure(figsize(5, 3), dpi100) super().__init__(self.fig) self.setParent(parent) self.ax self.fig.add_subplot(111) def update_chart(self, price, avg_price): self.ax.clear() labels [預測單價, 小區(qū)均價] values [price, avg_price] bars self.ax.bar(labels, values, color[#4C72B0, #DD8452]) self.ax.set_ylabel(價格元/平米) for bar, v in zip(bars, values): self.ax.text(bar.get_x() bar.get_width()/2, v, f{v:.0f}, hacenter, vabottom) self.ax.set_title(預測價格 vs 周邊均價) self.draw()這里FigureCanvasQTAgg是把 matplotlib 的圖嵌入 PyQt5 的關鍵接口update_chart每次先clear()再重新繪制避免畫布重影。要注意的是dpi100在 1080p 屏幕上顯示還行分辨率更高的屏幕上字會偏小可以調到 120~150。圖表數據里的avg_price可以從數據集里按小區(qū)分組算好均值存成一個 dict界面加載時直接讀取即可不必實時查數據庫。5. 避坑期末大作業(yè)最常見的 6 個翻車現場與排查方法5.1 PyQt5 安裝失敗報錯信息對不上怎么辦期末季最熱門的問題之一就是“PyQt5 安裝不上”。常見現象是pip install pyqt5卡在下載階段或者裝完之后from PyQt5.QtWidgets導入報錯ModuleNotFoundError。原因有兩個一是 PyQt5 的 wheel 包體積大默認源下載慢二是 Python 版本和 PyQt5 版本不匹配。解決方法是換國內鏡像源安裝同時注意 Python 版本Python 3.9 以上裝 5.15 系列基本沒問題Python 3.6 太老的話裝新版會直接拒絕安裝。在命令行里執(zhí)行下面的命令裝完驗證一次導入能省去后面大量的界面調試時間pip install pyqt5 -i https://pypi.tuna.tsinghua.edu.cn/simple python -c from PyQt5.QtWidgets import QApplication; print(ok)如果提示ModuleNotFoundError: No module named PyQt5.sip這是 PyQt5 和 PyQt5-sip 版本不對齊的老問題單獨再裝一次pip install pyqt5-sip通常就解決了。5.2 模型預測結果幾乎全是同一個值辛苦訓練完之后發(fā)現不管是輸入什么戶型預測出來的價格都差不多圖表上是一條平線。這個現象的原因集中在特征上要么是特征列順序錯亂模型吃進去的數值和訓練時完全不對應要么是界面構造的feat字典缺失關鍵列模型只能用默認值填充。排查方法是先打印出每次預測時的特征向量檢查面積、樓層這類高權重特征是否真的隨界面輸入在變化。可以在預測代碼里臨時加一行print(pd.DataFrame([feat])[self.feature_cols].to_string())然后切換幾次界面的輸入值看對應列的數字有沒有變化。如果發(fā)現floor_ratio恒為某個固定值那問題多半出在樓層換算公式上如果所有列都不變那是feat字典的鍵名沒跟feature_cols對齊。5.3 PyQt5 界面點擊按鈕后無響應或卡死點“開始預測”之后窗口卡住轉圈幾秒甚至直接崩潰。這類問題幾乎都是因為在主線程里跑了重型計算。on_predict里如果直接加載幾百 MB 的模型文件或者把網格搜索也放進去了界面就會阻塞。解決方法是把模型加載放到窗口初始化階段只做一次另外把預測運算放到QThread里界面保持響應。期末作業(yè)如果數據量不大模型文件一般只有幾十 MB放在__init__里加載是足夠快的。線程問題是 PyQt5 里最典型的翻車點之一。簡單做法是寫一個Worker類用QThread的run方法執(zhí)行預測并發(fā)射信號返回結果from PyQt5.QtCore import QThread, pyqtSignal class PredictWorker(QThread): finished pyqtSignal(float, float) def __init__(self, model, sample_df): super().__init__() self.model model self.sample_df sample_df def run(self): pred self.model.predict(self.sample_df)[0] self.finished.emit(pred, pred * self.sample_df[area].values[0])界面上實例化這個 worker把finished信號連到一個槽函數更新self.result_label。信號槽機制是 PyQt5 里跨線程回傳數據的事實標準用好了整個界面都不會卡。注意線程對象要在界面類里持有引用防止被垃圾回收掉導致信號發(fā)不出來。5.4 數據集里出現重復或異常明顯的離群值二手房價數據里有大量重復的房子記錄很常見同一個小區(qū)同一套戶型在不同時間被抓了多次還有那種 2000 年的老破小賣 2000 萬或者郊區(qū) 500 平別墅單價 1 萬這樣的極端值。不去重、不處理離群值模型會被這些點拉偏。處理分兩步第一步按房源 ID 或“小區(qū)面積戶型”去重第二步用箱線圖法把單價在上下四分位之外 5 倍四分位距的點標出來人工判斷是刪還是留。# 去重 df df.drop_duplicates(subset[community, area, bedrooms, living_rooms]) # 單價的四分位距過濾 Q1 df[unit_price].quantile(0.25) Q3 df[unit_price].quantile(0.75) IQR Q3 - Q1 df df[(df[unit_price] Q1 - 5 * IQR) (df[unit_price] Q3 5 * IQR)]這里用5 * IQR而不是常見的1.5 * IQR是因為房價數據本身方差大用1.5會把大量正常房源誤殺。這個倍數沒有標準答案可以先畫出單價分布直方圖再按倍數調整看保留樣本量是否合理。離群值處理會直接影響最終分數值得多花時間。5.5 界面打包成 exe 后缺少依賴交作業(yè)時導師經常要求打包成可執(zhí)行文件雙擊就能跑。用pyinstaller -w main.py打完的 exe 經常在別人的電腦上報錯最常見的是缺 PyQt5 的插件目錄或者缺模型文件的路徑。解決方法是把模型文件和代碼放在同一個目錄打包時用--add-data把模型文件帶進去并且在代碼里用sys._MEIPASS處理臨時解壓路徑。pyinstaller -w -F main.py --add-data model_rf.pkl;. --add-data feature_columns.pkl;.-w是隱藏控制臺窗口-F是打包成單文件。--add-data后面的分號在 Windows 下是路徑分隔符Linux/macOS 下要換成冒號。代碼中讀取文件的路徑需要寫成import sys, os base_path getattr(sys, _MEIPASS, os.path.dirname(os.path.abspath(__file__))) model_path os.path.join(base_path, model_rf.pkl)用_MEIPASS的原因是 PyInstaller 單文件打包后資源文件會被解壓到臨時目錄直接讀相對路徑會找不到文件。這個坑比較隱蔽屬于打包階段才能發(fā)現的類型提前寫對能省很多時間。5.6 訓練集和驗證集的數據泄漏你精心調出來的分數可能無效這是最容易被老師問倒的一個點。如果先對整個數據集做了StandardScaler再切分訓練集和測試集驗證集的分布信息已經提前泄露給了訓練過程。這個泄漏會讓測試分數虛高答辯時老師抽查代碼很容易發(fā)現。正確順序是先train_test_split再在訓練集上fit然后transform兩個集。隨機森林這類樹模型不需要歸一化但如果你的流程里用了線性回歸或者神經網絡這個順序就必須嚴格。X_train, X_test, y_train, y_test train_test_split(...) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)這組代碼體現的規(guī)則是所有“學習參數”的操作都只允許在訓練集上做。缺失值填補、特征選擇、降維同理都要先把 fit 和 transform 分開。很多網上的教程沒有強調這一點作業(yè)里被老師提出來就是硬傷。6. 把作業(yè)做深一個檔次交叉驗證、特征重要性與答辯驗證技巧到了這個階段你的系統(tǒng)已經能跑通了數據處理、模型訓練、PyQt5 界面、打包成 exe 全套都有。但如果想讓老師覺得“這個學生真的理解自己在做什么”還有三件性價比很高的事可以做最后一件對答辯最有用。第一件事是把單次train_test_split評估換成cross_val_score。交叉驗證用多輪數據切分取平均能給出模型真實水平的上限和下限規(guī)避單次劃分運氣好或運氣壞的問題。把交叉驗證結果打成一個表格放到 README 或者答辯 PPT 里比只報一個測試 R2 要有說服力得多。代碼很簡單from sklearn.model_selection import cross_val_score scores cross_val_score(rf, X_train, y_train, cv10, scoringr2) print(f10折交叉驗證 R2: {scores.mean():.4f} ± {scores.std():.4f})±后面的標準差代表模型在不同數據子集上的穩(wěn)定性這個數字越小越好。如果標準差超過了 0.1說明模型對數據劃分太敏感這時候就要考慮是不是數據量太少或者模型過擬合了。第二件事是在界面上加一個“特征重要性”的展示。把隨機森林的feature_importances_畫成橫向條形圖面積、建筑年代、樓層位置排在前三名這個圖表既是模型的解釋工具也是答辯時的講解素材??梢岳们懊鎸懙腃hartWidget類把條形圖嵌到界面右側用戶點擊“模型分析”按鈕就能切換到這張圖。這個功能做起來半小時但對技術深度的展示效果遠超過預期。第三件事也是回報最高的是做一個簡單的模型對比表把線性回歸、隨機森林、XGBoost 三個模型的 RMSE、R2、訓練時間都列出來。不用做得多復雜一個QTableWidget就能搞定或者直接在報告里畫一張表格。內容上要寫清楚“為什么隨機森林在這個數據集上比 XGBoost 好”這個分析本身就體現了差異化的思考能力。常見原因是數據集量不大、特征不多時XGBoost 的復雜度優(yōu)勢發(fā)揮不出來而隨機森林更容易收斂。我個人的習慣是把整個項目按“數據 → 特征 → 模型 → 界面 → 驗證”分成五個目錄每個目錄放一個README.md寫明這個環(huán)節(jié)做了什么、選了什么參數、踩了什么坑。這個習慣在答辯時幫了大忙——老師說“你這個樓層比例特征怎么想到的”我直接打開特征工程的 README里面寫了兩行我當時對比過不同樓層算法的結果比現場組織語言要從容得多。整個項目做下來最大的感受是期末大作業(yè)拼的不只是調包能力更是工程化的組織能力。數據不亂、模型可復現、界面可交互、驗證說得清這四個維度做到位分數不會差。希望這篇筆記能幫到你在動手之前少走幾個彎路。本文還有配套的精品資源點擊獲取