import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'Times New Roman'
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_excel('california.xlsx')
from sklearn.model_selection import train_test_split, KFold
X = df.drop(['price'],axis=1)
y = df['price']
# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
df.head()
從Excel文件中讀取數據,將特征變量與目標變量(房價 price)分離,并劃分為訓練集和測試集,用于機器學習模型的訓練和評估。數據集中包括了房齡(HouseAge)、中位收入(MedInc)、平均房間數(AveRooms)等特征,目標變量為房價(price)
from sklearn.metrics import root_mean_squared_error
from catboost import CatBoostRegressor
# CatBoost模型參數
params_cat = {
'learning_rate': 0.02, # 學習率,控制每一步的步長,用于防止過擬合。典型值范圍:0.01 - 0.1
'iterations': 1000, # 弱學習器(決策樹)的數量
'depth': 6, # 決策樹的深度,控制模型復雜度
'eval_metric': 'RMSE', # 評估指標,這里使用均方根誤差(Root Mean Squared Error,簡稱RMSE)
'random_seed': 42, # 隨機種子,用于重現模型的結果
'verbose': 500 # 控制CatBoost輸出信息的詳細程度,每100次迭代輸出一次
}
# 準備k折交叉驗證
kf = KFold(n_splits=5, shuffle=True, random_state=42)
scores = []
best_score = np.inf
best_model = None
# 交叉驗證
for fold, (train_index, val_index) in enumerate(kf.split(X_train, y_train)):
X_train_fold, X_val_fold = X_train.iloc[train_index], X_train.iloc[val_index]
y_train_fold, y_val_fold = y_train.iloc[train_index], y_train.iloc[val_index]
model = CatBoostRegressor(**params_cat)
model.fit(X_train_fold, y_train_fold, eval_set=(X_val_fold, y_val_fold), early_stopping_rounds=100)
# 預測并計算得分
y_val_pred = model.predict(X_val_fold)
score = root_mean_squared_error(y_val_fold, y_val_pred) # RMSE
scores.append(score)
print(f'第 {fold + 1} 折 RMSE: {score}')
# 保存得分最好的模型
if score < best_score:
best_score = score
best_model = model
print(f'最佳 RMSE: {best_score}')
通過使用5折交叉驗證來訓練CatBoost回歸模型,每一折模型都會基于訓練集進行訓練,并在驗證集上進行評估,計算出對應的RMSE(均方根誤差),在每一折訓練中,模型會自動調整參數以避免過擬合,使用早停策略來選擇最佳的迭代次數,最終,程序會輸出每一折的RMSE得分,并保存得分最好的模型,最后輸出整個交叉驗證中最優模型的RMSE,表示模型在驗證集上取得的最低預測誤差,這一過程有助于評估模型的穩健性并防止過擬合,為PDP解釋做準備
2D PDP解釋
from sklearn.inspection import PartialDependenceDisplay
# 選擇兩個特征繪制2D PDP
features = ['MedInc', 'AveOccup']
# 使用 contour_kw 參數繪制2D PDP
fig, ax = plt.subplots(figsize=(10, 6), dpi=1200)
PartialDependenceDisplay.from_estimator(
best_model,
X_test,
features=[features],
kind='average',
grid_resolution=50,
contour_kw={'cmap': 'viridis', 'alpha': 0.8},
ax=ax
)
plt.suptitle('2D Partial Dependence Plot for MedInc and AveOccup')
plt.savefig("2D Partial Dependence Plot for MedInc and AveOccup.pdf", format='pdf',bbox_inches='tight')
plt.show()
使用PartialDependenceDisplay生成一個2D部分依賴圖(PDP),用于展示兩個特征(MedInc和AveOccup)對目標變量(房價)的影響,代碼中的best_model是之前訓練好的CatBoost模型,X_test是測試集中的特征數據,features指定了感興趣的兩個特征,即中位收入(MedInc)和平均家庭規模(AveOccup)
2D PDP圖通過將MedInc(中位收入)和AveOccup(平均家庭規模)組合的不同值映射到房價預測結果的等高線圖上,展示了模型對這兩個特征共同作用的反應,圖中的顏色表示房價的預測值,顏色從左到右逐漸變化,反映出隨著中位收入增加,房價預測值也逐步增加,同時,等高線代表了不同的房價預測范圍,等高線的數值越大,表示在這些區域中房價預測越高,通過觀察可以發現,MedInc的增大對房價的影響更加顯著,而AveOccup的影響相對較小,但它們的共同作用仍然對房價預測產生綜合影響,這一可視化有助于直觀地理解兩個特征如何相互配合影響目標變量的趨勢
3D?PDP解釋
from mpl_toolkits.mplot3d import Axes3D
def plot_3d_pdp_scatter(model, X, features, grid_resolution=20):
"""
繪制三個特征的3D PDP散點圖,不固定任何特征值。
參數:
- model: 訓練好的機器學習模型(例如:隨機森林,XGBoost等)。
- X: 數據集(特征矩陣)。
- features: 需要分析的三個特征名列表,格式為 ['feature1', 'feature2', 'feature3']。
- grid_resolution: 網格分辨率,決定網格點的密度
"""
# 獲取三個特征的數據
feature_1, feature_2, feature_3 = features
# 構建網格
feature_1_vals = np.linspace(X[feature_1].min(), X[feature_1].max(), grid_resolution)
feature_2_vals = np.linspace(X[feature_2].min(), X[feature_2].max(), grid_resolution)
feature_3_vals = np.linspace(X[feature_3].min(), X[feature_3].max(), grid_resolution)
# 生成三維網格
grid_1, grid_2, grid_3 = np.meshgrid(feature_1_vals, feature_2_vals, feature_3_vals)
# 將網格數據轉換為數據框的格式,傳遞給模型進行預測
grid_points = np.c_[grid_1.ravel(), grid_2.ravel(), grid_3.ravel()]
X_grid = np.zeros((grid_points.shape[0], X.shape[1])) # 創建空白矩陣,維度與X相同
X_grid[:, X.columns.get_loc(feature_1)] = grid_points[:, 0]
X_grid[:, X.columns.get_loc(feature_2)] = grid_points[:, 1]
X_grid[:, X.columns.get_loc(feature_3)] = grid_points[:, 2]
# 對網格數據進行預測
preds = model.predict(X_grid)
# 繪制3D散點圖
fig = plt.figure(figsize=(10, 8),dpi=1200)
ax = fig.add_subplot(111, projection='3d')
# 使用散點圖繪制三維特征,并使用預測值作為顏色
sc = ax.scatter(grid_1.ravel(), grid_2.ravel(), grid_3.ravel(), c=preds, cmap='viridis', alpha=0.8)
# 添加顏色條
plt.colorbar(sc, ax=ax, label='Prediction')
ax.set_xlabel(feature_1)
ax.set_ylabel(feature_2)
ax.set_zlabel(feature_3)
plt.title(f'3D Partial Dependence Scatter Plot for {feature_1}, {feature_2}, and {feature_3}')
plt.savefig("3D.pdf", format='pdf', bbox_inches='tight')
plt.show()
features = ['MedInc', 'AveOccup', 'HouseAge']
plot_3d_pdp_scatter(best_model, X_test, features)
通過構建三維網格實現3D PDP(部分依賴圖)的可視化,展示了三個特征(MedInc,AveOccup,HouseAge)對目標變量(房價)預測的影響,具體做法是,首先為每個特征構建取值范圍并生成三維網格,然后將這些網格點組合成一組數據點,傳遞給訓練好的模型進行預測。接著,代碼使用三維散點圖顯示這些特征組合下的預測值,其中顏色代表房價預測值,顏色從深到淺依次表示房價從低到高,通過這種3D可視化,可以清晰地看到這三個特征對房價預測的共同作用和交互影響,例如,中位收入較高和房齡較新的區域往往房價預測較高,而家庭規模的影響較小但仍有一定作用,這種可視化方法幫助深入理解模型對多個特征組合的反應
def plot_3d_pdp(model, X, features, fixed_feature=None, fixed_value=None, grid_resolution=50):
"""
繪制三個特征的3D PDP圖,其中一個特征固定值,并加上熱度條。
參數:
- model: 訓練好的機器學習模型(例如:隨機森林,XGBoost等)。
- X: 數據集(特征矩陣)。
- features: 需要分析的三個特征名列表,格式為 ['feature1', 'feature2', 'feature3']。
- fixed_feature: 需要固定的特征名(默認為features列表中的第三個特征)。
- fixed_value: 第三個特征的固定值(可以是其均值、中位數或任意值,默認為其均值)。
- grid_resolution: 網格分辨率,決定網格點的密度。
"""
feature_1, feature_2, feature_3 = features
# 如果沒有指定固定特征,默認將第三個特征固定
if fixed_feature is None:
fixed_feature = feature_3
# 如果沒有指定固定值,則使用該特征的均值
if fixed_value is None:
fixed_value = X[fixed_feature].mean()
# 構建網格,針對兩個未固定的特征
if fixed_feature == feature_1:
varying_features = [feature_2, feature_3]
elif fixed_feature == feature_2:
varying_features = [feature_1, feature_3]
else:
varying_features = [feature_1, feature_2]
feature_1_vals = np.linspace(X[varying_features[0]].min(), X[varying_features[0]].max(), grid_resolution)
feature_2_vals = np.linspace(X[varying_features[1]].min(), X[varying_features[1]].max(), grid_resolution)
grid_1, grid_2 = np.meshgrid(feature_1_vals, feature_2_vals)
# 構建網格數據,傳遞給模型進行預測
grid_points = np.c_[grid_1.ravel(), grid_2.ravel()]
X_grid = np.zeros((grid_points.shape[0], X.shape[1])) # 創建空白矩陣,維度與X相同
X_grid[:, X.columns.get_loc(varying_features[0])] = grid_points[:, 0]
X_grid[:, X.columns.get_loc(varying_features[1])] = grid_points[:, 1]
X_grid[:, X.columns.get_loc(fixed_feature)] = fixed_value # 固定特征值
# 對網格數據進行預測
preds = model.predict(X_grid).reshape(grid_1.shape)
# 繪制3D圖像
fig = plt.figure(figsize=(10, 8), dpi=1200)
ax = fig.add_subplot(111, projection='3d')
# 繪制等高線圖
surface = ax.plot_surface(grid_1, grid_2, preds, cmap='viridis', alpha=0.8)
# 添加顏色熱度條
fig.colorbar(surface, ax=ax, shrink=0.5, aspect=10)
ax.set_xlabel(varying_features[0])
ax.set_ylabel(varying_features[1])
ax.set_zlabel('Prediction')
plt.title(f'3D Partial Dependence Plot for {varying_features[0]}, {varying_features[1]} with {fixed_feature}={fixed_value}')
plt.savefig("3D one.pdf", format='pdf', bbox_inches='tight')
plt.show()
features = ['MedInc', 'AveOccup', 'HouseAge']
plot_3d_pdp(best_model, X_test, features, fixed_feature='HouseAge')
在進行模型解釋時,如果對所有特征進行可視化展示(如3個特征),會形成一個三維正方體圖像,代表所有特征組合下的模型預測值,雖然這種全方位的可視化非常全面,但對于實際理解模型各特征如何結合影響預測來說,復雜度較高,因此,固定一個特征值可以簡化分析,并展示另外兩個特征的組合如何影響預測結果,通過固定某個特征,能更容易觀察這兩個特征對目標變量的影響,同時保持模型預測的復雜性
代碼通過生成一個3D部分依賴圖,固定HouseAge的特定值(默認是其均值或用戶指定值),然后分析MedInc(中位收入)和AveOccup(平均家庭規模)對房價預測的影響。代碼首先創建了一個包含MedInc和AveOccup的網格,將這兩個特征在不同取值下組合,然后傳遞給模型進行預測,最后,通過3D等高線圖將預測結果可視化,其中顏色表示房價的預測值
在圖中,HouseAge被固定為31.14歲,表示房齡固定情況下中位收入(MedInc)和平均家庭規模(AveOccup)如何共同作用影響房價預測。顏色從深紫色到亮黃色逐漸變化,表明預測值從低到高,可以觀察到,中位收入對房價預測的影響相對明顯,隨著MedInc的增大,房價預測值顯著增加;而AveOccup的影響較為平緩,整體變化不大,通過這張圖,能更直觀地看到這兩個特征的組合如何在特定房齡下影響房價預測
本文章轉載微信公眾號@Python機器學習AI