學習筆記(29):訓練集與測試集劃分詳解:train_test_split 函數深度解析
一、為什么需要劃分訓練集和測試集?
在機器學習中,模型需要經歷兩個核心階段:
- 訓練階段:用訓練集數據學習特征與目標值的映射關系(如線性回歸的權重)。
- 測試階段:用測試集評估模型在未見過的數據上的表現,避免 “過擬合”(模型只記住訓練數據的噪聲,無法泛化到新數據)。
類比場景:學生通過 “練習題”(訓練集)學習知識,再通過 “考試題”(測試集)檢驗真實水平。
二、train_test_split
?函數的核心參數與邏輯
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42
)
1.?輸入參數解析
X_scaled
:特征矩陣(已標準化的面積、房齡等特征)。y
:目標變量(房價)。test_size=0.2
:測試集占總數據的比例(20%),也可設為整數(如?test_size=20
?表示取 20 個樣本)。random_state=42
:隨機種子,確保每次劃分結果一致(與?np.random.seed(42)
?作用類似)。
2.?劃分邏輯
- 隨機抽樣:按?
test_size
?比例從原始數據中隨機抽取樣本作為測試集,剩余作為訓練集。 - 數據對齊:確保?
X
?和?y
?的樣本順序一一對應(如第 i 個特征向量對應第 i 個房價標簽)。
三、劃分結果的維度與含義
假設原始數據有 100 個樣本(n_samples=100
):
- 訓練集:80 個樣本(
X_train.shape=(80, 2)
,?y_train.shape=(80,)
),用于模型學習。 - 測試集:20 個樣本(
X_test.shape=(20, 2)
,?y_test.shape=(20,)
),用于評估模型泛化能力。
四、關鍵參數深度解析
1.?test_size
:平衡訓練與測試的樣本量
- 取值建議:
- 小數據集(<1000 樣本):常用?
test_size=0.2~0.3
(20%-30% 作為測試集)。 - 大數據集(>10000 樣本):可設?
test_size=0.1
?甚至更低(因少量樣本已足夠評估)。
- 小數據集(<1000 樣本):常用?
- 極端案例:若?
test_size=1.0
,則所有數據都是測試集,無訓練集;若?test_size=0
,則全是訓練集。
2.?random_state
:確保可復現的 “隨機” 劃分
- 作用:固定隨機種子后,每次運行代碼時,訓練集和測試集的樣本索引完全相同。
- 示例對比:
- 不設置?
random_state
:每次劃分結果不同,導致模型評估指標波動。 - 設置?
random_state=42
:多次運行代碼,劃分結果一致,便于對比不同模型效果。
- 不設置?
3.?shuffle=True
(默認參數):打亂數據順序
- 為什么需要打亂?
若數據按順序排列(如前 50 個是小戶型,后 50 個是大戶型),不打亂會導致訓練集和測試集樣本分布不均(如測試集全是大戶型)。 - 參數設置:
train_test_split
?默認為?shuffle=True
,即先打亂數據再劃分;若數據已隨機排列,可設?shuffle=False
。
五、進階應用:分層抽樣(Stratified Sampling)
當目標變量是分類變量(如二分類 “是否違約”)時,普通隨機劃分可能導致訓練 / 測試集的類別比例失衡(如測試集全是 “違約” 樣本)。此時需用?StratifiedShuffleSplit
?實現分層抽樣:
from sklearn.model_selection import StratifiedShuffleSplit# 4. 使用分層抽樣(確保類別比例平衡)
sss = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_idx, test_idx in sss.split(X_scaled, y_binary):X_train, X_test = X_scaled[train_idx], X_scaled[test_idx]y_train, y_test = y_binary[train_idx], y_binary[test_idx]print("===== 分類模型結果 =====")
print(f"原始數據類別比例:{np.bincount(y_binary)/len(y_binary)}")
print(f"訓練集類別比例:{np.bincount(y_train)/len(y_train)}")
print(f"測試集類別比例:{np.bincount(y_test)/len(y_test)}")
六、實戰誤區與注意事項
- 禁止在測試集上訓練:測試集只能用于評估,若根據測試集結果調整模型參數(如調優正則化系數),本質上是 “偷看答案”,會導致評估結果過于樂觀。
- 數據標準化的順序:
- 正確流程:先劃分訓練測試集,再對訓練集擬合標準化器(
scaler.fit(X_train)
),最后用訓練集的標準化參數轉換測試集(scaler.transform(X_test)
)。 - 錯誤操作:對全量數據標準化后再劃分,會導致測試集 “偷看到” 全量數據的統計特征,違反 “未知數據” 假設。
- 正確流程:先劃分訓練測試集,再對訓練集擬合標準化器(
- 多輪劃分與交叉驗證:當數據量較小時,可使用 K 折交叉驗證(如 10 折),將數據分成 10 份,每次用 9 份訓練、1 份測試,重復 10 次取平均,減少單次劃分的隨機性誤差。
七、總結:劃分訓練測試集的核心原則
- 獨立性:測試集數據必須是模型未見過的,模擬真實應用場景。
- 代表性:訓練集和測試集的樣本分布應盡可能一致(如特征取值范圍、類別比例)。
- 可復現性:通過設置隨機種子,確保實驗結果可重復驗證。
通過合理劃分訓練集與測試集,你可以更準確地評估模型的實際能力,避免被 “過擬合” 的假象誤導 —— 這是機器學習工程化中至關重要的一步!
二分類問題(房價是否高于中位數)-全代碼
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split, StratifiedShuffleSplit
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error, r2_score# 配置中文顯示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# 1. 生成模擬數據(假設房價與面積、房齡的關系)
np.random.seed(42)
n_samples = 100
# 面積(平方米),房齡(年)
X = np.random.rand(n_samples, 2) * 100
X[:, 0] = X[:, 0] # 面積范圍:0-100
X[:, 1] = X[:, 1] # 房齡范圍:0-100# 真實房價 = 5000*面積 + 1000*房齡 + 隨機噪聲(模擬真實場景)
y = 5000 * X[:, 0] + 1000 * X[:, 1] + np.random.randn(n_samples) * 10000# 2. 將連續的房價y轉換為分類標簽(例如分為低、中、高3個類別)
y_category = pd.qcut(y, q=3, labels=[0, 1, 2]) # 使用pandas的qcut進行分位數切割# 3. 數據預處理:標準化特征
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)# 4. 使用分層抽樣
sss = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_idx, test_idx in sss.split(X_scaled, y_category):X_train, X_test = X_scaled[train_idx], X_scaled[test_idx]y_train, y_test = y[train_idx], y[test_idx] # 注意:這里仍然使用原始的連續房價作為目標_# 確保訓練集和測試集的類別比例與原始數據一致
print(f"原始數據類別比例:{np.bincount(y_category)/len(y_category)}")
print(f"訓練集類別比例:{np.bincount(y_category[train_idx])/len(y_category[train_idx])}")
print(f"測試集類別比例:{np.bincount(y_category[test_idx])/len(y_category[test_idx])}")# 后續回歸模型訓練和評估代碼保持不變
model = LinearRegression()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)# 評估模型
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"均方誤差: {mse:.2f}")
print(f"決定系數R2: {r2:.2f}")
打印:
原始數據類別比例:[0.34 0.32 0.34]
訓練集類別比例:[0.3375 0.325 ?0.3375]
測試集類別比例:[0.35 0.3 ?0.35]
均方誤差: 101112597.45
決定系數R2: 1.00
代碼解析:
核心步驟解析
-
數據準備與二分類轉換
- 生成與方案 1 相同的模擬數據(面積、房齡 → 房價)。
- 將連續的房價
y
轉換為二分類標簽:
threshold = np.median(y) # 使用中位數作為閾值
y_binary = (y > threshold).astype(int) # 0=低于中位數,1=高于中位數
- 這樣做的目的是將 “預測具體房價” 轉化為 “判斷房價高低”。
分層抽樣(Stratified Sampling)
- 使用
StratifiedShuffleSplit
確保訓練集和測試集中高低房價的比例與原始數據一致:
sss = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)
for train_idx, test_idx in sss.split(X_scaled, y_category):X_train, X_test = X_scaled[train_idx], X_scaled[test_idx]y_train, y_test = y[train_idx], y[test_idx] # 注意:這里仍然使用原始的連續房價作為目標_