|
| 1 | +# 🔥XAI - Thực hành Biểu đồ Phụ thuộc Từng phần (PDP)🔥 |
| 2 | + |
| 3 | +## 1. Tạo mô hình với bộ dữ liệu về giá nhà trung bình California Housing Dataset |
| 4 | + |
| 5 | +California Housing Dataset gồm 20640 bản ghi với 8 đặc trưng dự đoán và nhãn `target`, trong đó các đặc trưng mang ý nghĩa như sau: |
| 6 | + |
| 7 | +|Đặc trưng| Ý nghĩa| |
| 8 | +|-|-| |
| 9 | +|`MedInc` | Thu nhập trung bình | |
| 10 | +|`HouseAge` | Tuổi nhà trung bình | |
| 11 | +|`AveRooms` | Số phòng trung bình | |
| 12 | +|`AveBedrms` | Số phòng ngủ trung bình | |
| 13 | +|`Population`| Số thành viên hộ gia đình trung bình | |
| 14 | +|`AveOccup` | Số thành viên hộ gia đình trung bình | |
| 15 | +|`Latitude` | Vĩ độ | |
| 16 | +|`Longitude` | Kinh độ | |
| 17 | + |
| 18 | +Đây là tập dữ liệu có sẵn trong thư viện Sklearn nên chúng ta hoàn toàn có thế gọi ra thông qua hàm `fetch_california_housing()`. Tương tự như các bài toán học máy thông thường, ta chia dữ liệu thành tập huấn luyện và tập kiểm thử và sử dụng mô hình Random Forest để huấn luyện và dự đoán trên cả 2 tập dữ liệu đã chia. |
| 19 | + |
| 20 | + |
| 21 | +```python |
| 22 | +import pandas as pd |
| 23 | +import numpy as np |
| 24 | +import matplotlib.pyplot as plt |
| 25 | +from sklearn.datasets import fetch_california_housing |
| 26 | +from sklearn.model_selection import train_test_split |
| 27 | +from sklearn.ensemble import RandomForestRegressor |
| 28 | +from sklearn.inspection import permutation_importance |
| 29 | +from sklearn.metrics import mean_squared_error |
| 30 | +``` |
| 31 | + |
| 32 | + |
| 33 | +```python |
| 34 | +# Khởi tạo dữ liệu |
| 35 | +housing = fetch_california_housing() |
| 36 | +X = pd.DataFrame(housing.data, columns=housing.feature_names) |
| 37 | +Y = pd.Series(housing.target) |
| 38 | +X.info() |
| 39 | +``` |
| 40 | + |
| 41 | + <class 'pandas.core.frame.DataFrame'> |
| 42 | + RangeIndex: 20640 entries, 0 to 20639 |
| 43 | + Data columns (total 8 columns): |
| 44 | + # Column Non-Null Count Dtype |
| 45 | + --- ------ -------------- ----- |
| 46 | + 0 MedInc 20640 non-null float64 |
| 47 | + 1 HouseAge 20640 non-null float64 |
| 48 | + 2 AveRooms 20640 non-null float64 |
| 49 | + 3 AveBedrms 20640 non-null float64 |
| 50 | + 4 Population 20640 non-null float64 |
| 51 | + 5 AveOccup 20640 non-null float64 |
| 52 | + 6 Latitude 20640 non-null float64 |
| 53 | + 7 Longitude 20640 non-null float64 |
| 54 | + dtypes: float64(8) |
| 55 | + memory usage: 1.3 MB |
| 56 | + |
| 57 | + |
| 58 | + |
| 59 | +```python |
| 60 | +# Chia dữ liệu thành tập huấn luyện và tập kiểm thử |
| 61 | +X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.2, random_state=42) |
| 62 | +# Khởi tạo mô hình Random Forest |
| 63 | +regr = RandomForestRegressor(max_depth = 5, random_state = 42, n_estimators = 100) |
| 64 | +# Fit dữ liệu huấn luyện vào mô hình đã khởi tạo |
| 65 | +regr.fit(X_train, Y_train) |
| 66 | + |
| 67 | +# Dự đoán nhãn trên tập huấn luyện và tập kiểm thử |
| 68 | +Y_predict_train = regr.predict(X_train) |
| 69 | +Y_predict = regr.predict(X_test) |
| 70 | +``` |
| 71 | + |
| 72 | +## 2. Lựa chọn những đặc trưng quan trọng (feature importance) |
| 73 | + |
| 74 | +Một trong những lí do chúng mình chọn mô hình Random Forest đó là bởi chúng ta có thể dễ dàng trực quan những đặc trưng quan trọng, được sử dụng nhiều trong mô hình. Để đánh giá đặc trưng quan trọng cũng như trực quan hoá thứ tự quan trọng của các đặc trưng này, chúng ta có thể làm như sau: |
| 75 | + |
| 76 | + |
| 77 | +```python |
| 78 | +# Hàm trực quan hoá trung bình mức quan trọng của các đặc trưng |
| 79 | +def feature_plot_importance(importance_df): |
| 80 | + plt.rcParams.update({'font.size': 16}) |
| 81 | + plt.rcParams["figure.figsize"] = (12,5) |
| 82 | + fig, ax = plt.subplots() |
| 83 | + x = np.arange(len(importance_df)) |
| 84 | + width = 0.77 |
| 85 | + rects1 = ax.bar(x, |
| 86 | + height = importance_df['means'], |
| 87 | + width = width, |
| 88 | + yerr = importance_df['stds'], |
| 89 | + align='center', |
| 90 | + ecolor='black', |
| 91 | + capsize=5) |
| 92 | + ax.set_ylabel('Feature Importance') |
| 93 | + ax.set_xticks(x) |
| 94 | + ax.set_xticklabels(importance_df['feature_labels']) |
| 95 | + ax.set_title('Feature Importance Initial Model') |
| 96 | + plt.tight_layout() |
| 97 | +``` |
| 98 | + |
| 99 | + |
| 100 | +```python |
| 101 | +# Đánh giá đặc trưng quan trọng trên tập huấn luyện |
| 102 | +perm_feature_importance = permutation_importance(regr, X_train, Y_train) |
| 103 | + |
| 104 | +# Tạo DataFrame chứa nhãn và trung bình, độ lệch chuẩn mức quan trọng |
| 105 | +importance_df = pd.DataFrame({'feature_labels': X_train.columns, |
| 106 | + 'means' : perm_feature_importance['importances_mean'], |
| 107 | + 'stds' : perm_feature_importance['importances_std']}) |
| 108 | + |
| 109 | +# Trực quan hoá trung bình mức quan trọng của các đặc trưng |
| 110 | +feature_plot_importance(importance_df) |
| 111 | +``` |
| 112 | + |
| 113 | + |
| 114 | + |
| 115 | + |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | +Dựa vào feature importance từ mô hình Random Forest, có thể thấy `MedInc`, `AveOccup`, `HouseAge` là 3 đặc trưng quan trọng. Ta hoàn toàn có thể xây dựng 1 mô hình Random Forest mới với chỉ những đặc trưng quan trọng. |
| 120 | + |
| 121 | + |
| 122 | +```python |
| 123 | +# Chọn lọc những đặc trưng có điểm feature importance cao |
| 124 | +X_train_reduced = X_train.loc[:,['MedInc','HouseAge','AveOccup']] |
| 125 | + |
| 126 | +# Khởi tạo mô hình Random Forest |
| 127 | +regr = RandomForestRegressor(max_depth = 5, random_state = 42, n_estimators = 100) |
| 128 | +regr.fit(X_train_reduced, Y_train) |
| 129 | + |
| 130 | +perm_feature_importance = permutation_importance(regr, X_train_reduced, Y_train) |
| 131 | + |
| 132 | +# Tạo DataFrame chứa nhãn và trung bình, độ lệch chuẩn mức quan trọng |
| 133 | +importance_df_reduced = pd.DataFrame({'feature_labels': X_train_reduced.columns, |
| 134 | + 'means' : perm_feature_importance['importances_mean'], |
| 135 | + 'stds' : perm_feature_importance['importances_std']}) |
| 136 | + |
| 137 | +# Trực quan hoá trung bình mức quan trọng của các đặc trưng |
| 138 | +feature_plot_importance(importance_df_reduced) |
| 139 | +``` |
| 140 | + |
| 141 | + |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | +Dựa vào feature importance từ mô hình Random Forest, có thể thấy `MedInc`, `AveOccup`, `HouseAge` là 3 đặc trưng quan trọng. Nhưng chỉ dựa vào đặc trưng thì khó có thể thấy được mối liên hệ giữa đặc trưng đó với việc mô hình đưa ra kết quả dự đoán. Để giải quyết vấn đề này, chúng ta sẽ cùng nhau đến với Biểu đồ Phụ thuộc Từng phần (Partial Dependence Plot). |
| 148 | + |
| 149 | +# 3. Minh họa cách tạo ra Biểu đồ Phụ thuộc Từng phần |
| 150 | +### 3.1. Sử dụng Scikit-Learn |
| 151 | +Biểu đồ PDP: |
| 152 | +- 1D: biểu diễn sự phụ thuộc giữa mục tiêu dự đoán và những đặc trương được quan tâm (features of interest - những đặc trưng được lựa chọn bởi feature importance) |
| 153 | +- 2D: biểu diễn sự phụ thuộc giữa 2 đặc trưng được quan tâm và tác động của nó lên mục tiêu dự đoán |
| 154 | + |
| 155 | +Chúng ta có thể biểu diễn biểu đồ này thông qua `PartialDependenceDisplay.from_estimator()` từ thư viện `sklearn.inspection`. |
| 156 | + |
| 157 | + |
| 158 | +```python |
| 159 | +from sklearn.inspection import PartialDependenceDisplay |
| 160 | +``` |
| 161 | + |
| 162 | +Với biểu đồ DPD 1D, ta có: |
| 163 | + |
| 164 | + |
| 165 | +```python |
| 166 | +PartialDependenceDisplay.from_estimator(regr, |
| 167 | + X_train_reduced, |
| 168 | + features = ['MedInc'], |
| 169 | + percentiles = [0,1]); |
| 170 | +``` |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | + |
| 175 | + |
| 176 | + |
| 177 | + |
| 178 | + |
| 179 | +```python |
| 180 | +PartialDependenceDisplay.from_estimator(regr, |
| 181 | + X_train_reduced, |
| 182 | + features = ['HouseAge'], |
| 183 | + percentiles = [0,1]); |
| 184 | +``` |
| 185 | + |
| 186 | + |
| 187 | + |
| 188 | + |
| 189 | + |
| 190 | + |
| 191 | + |
| 192 | + |
| 193 | +```python |
| 194 | +PartialDependenceDisplay.from_estimator(regr, |
| 195 | + X_train_reduced, |
| 196 | + features = ['AveOccup'], |
| 197 | + percentiles = [0,1]); |
| 198 | +``` |
| 199 | + |
| 200 | + |
| 201 | + |
| 202 | + |
| 203 | + |
| 204 | + |
| 205 | + |
| 206 | +Với biểu đồ DPD 2D, ta có: |
| 207 | + |
| 208 | + |
| 209 | +```python |
| 210 | +MedInc_AvcOccup_disp = PartialDependenceDisplay.from_estimator(regr, |
| 211 | + X_train_reduced, |
| 212 | + features = [(0,2)], |
| 213 | + percentiles = [0,1], |
| 214 | + grid_resolution = 20) |
| 215 | +``` |
| 216 | + |
| 217 | + |
| 218 | + |
| 219 | + |
| 220 | + |
| 221 | + |
| 222 | + |
| 223 | + |
| 224 | +```python |
| 225 | +MedInc_HouseAge_disp = PartialDependenceDisplay.from_estimator(regr, |
| 226 | + X_train_reduced, |
| 227 | + features = [(0,1)], |
| 228 | + percentiles = [0,1], |
| 229 | + grid_resolution = 20) |
| 230 | +``` |
| 231 | + |
| 232 | + |
| 233 | + |
| 234 | + |
| 235 | + |
| 236 | + |
| 237 | + |
| 238 | + |
| 239 | +```python |
| 240 | +AveOccup_HouseAge_disp = PartialDependenceDisplay.from_estimator(regr, |
| 241 | + X_train_reduced, |
| 242 | + features = [(2,1)], |
| 243 | + percentiles = [0,1], |
| 244 | + grid_resolution = 10) |
| 245 | +``` |
| 246 | + |
| 247 | + |
| 248 | + |
| 249 | + |
| 250 | + |
| 251 | + |
| 252 | + |
| 253 | +Ta hoàn toàn có thể thêm các điểm dữ liệu vào biểu đồ PDP bằng cách sau: |
| 254 | + |
| 255 | + |
| 256 | +```python |
| 257 | +fig, ax = plt.subplots(figsize=(12, 5)) |
| 258 | +AveOccup_HouseAge_disp.plot(ax = ax) |
| 259 | +plt.scatter(X_train_reduced['AveOccup'], |
| 260 | + X_train_reduced['HouseAge'], |
| 261 | + alpha = 0.2, |
| 262 | + color = 'black'); |
| 263 | +``` |
| 264 | + |
| 265 | + |
| 266 | + |
| 267 | + |
| 268 | + |
| 269 | + |
| 270 | + |
| 271 | +## 3.2. Sử dụng thư viện PDPbox |
| 272 | +- Cài đặt thư viện: !pip install pdpbox |
| 273 | +- Link: https://github.com/SauceCat/PDPbox |
| 274 | + |
| 275 | + |
| 276 | +```python |
| 277 | +from pdpbox import pdp |
| 278 | +``` |
| 279 | + |
| 280 | +Có thể thấy tương tự như biểu đồ PDP của Scikit-Learn, với đặc trưng `MedInc`, giá nhà trung bình cũng tăng khi thu nhập trung bình tăng (với `MedInc` lớn hơn 2). Với thu nhập trung bình lớn hơn khoảng 6, giá nhà tăng khá nhiều và ổn định. |
| 281 | + |
| 282 | + |
| 283 | +```python |
| 284 | +pdp_MedInc = pdp.pdp_isolate(model=regr, |
| 285 | + dataset=X_train_reduced, |
| 286 | + model_features=X_train_reduced.columns, |
| 287 | + feature='MedInc') |
| 288 | +fig, axes = pdp.pdp_plot(pdp_MedInc, 'MedInc', plot_lines=True, frac_to_plot=100, plot_pts_dist=True); |
| 289 | +``` |
| 290 | + |
| 291 | + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. |
| 292 | + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. |
| 293 | + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. |
| 294 | + findfont: Font family ['Arial'] not found. Falling back to DejaVu Sans. |
| 295 | + |
| 296 | + |
| 297 | + |
| 298 | + |
| 299 | + |
| 300 | + |
| 301 | + |
| 302 | + |
| 303 | + |
| 304 | +```python |
| 305 | +pdp_MedInc = pdp.pdp_isolate(model=regr, |
| 306 | + dataset=X_train_reduced, |
| 307 | + model_features=X_train_reduced.columns, |
| 308 | + feature='HouseAge') |
| 309 | +fig, axes = pdp.pdp_plot(pdp_MedInc, 'HouseAge', plot_lines=True, frac_to_plot=100, plot_pts_dist=True); |
| 310 | +``` |
| 311 | + |
| 312 | + |
| 313 | + |
| 314 | + |
| 315 | + |
| 316 | + |
| 317 | + |
| 318 | + |
| 319 | +```python |
| 320 | +pdp_MedInc = pdp.pdp_isolate(model=regr, |
| 321 | + dataset=X_train_reduced, |
| 322 | + model_features=X_train_reduced.columns, |
| 323 | + feature='AveOccup') |
| 324 | +fig, axes = pdp.pdp_plot(pdp_MedInc, 'AveOccup', plot_lines=True, frac_to_plot=100, plot_pts_dist=True); |
| 325 | +``` |
| 326 | + |
| 327 | + |
| 328 | + |
| 329 | + |
| 330 | + |
| 331 | + |
| 332 | + |
| 333 | +Thư viện PDPbox cũng cung cấp cả 2 biểu đồ dưới dạng 1D và 2D. Biều đồ 2D phía dưới cho thấy mối quan hệ của giá nhà trung bình với 2 đặc trưng là thu nhập bình quân và độ tuổi của nhà. |
| 334 | + |
| 335 | + |
| 336 | +```python |
| 337 | +feats = ['MedInc','HouseAge'] |
| 338 | +p = pdp.pdp_interact(regr,X_train_reduced, |
| 339 | + X_train_reduced.columns, |
| 340 | + feats) |
| 341 | +pdp.pdp_interact_plot(p,feats); |
| 342 | +``` |
| 343 | + |
| 344 | + |
| 345 | + |
| 346 | + |
| 347 | + |
| 348 | + |
| 349 | + |
| 350 | +Đón xem những trải nghiệm và phân tích cụ thể từ Zootopi tại: |
| 351 | + |
| 352 | +- 👉Website: https://zootopi.dev/blog |
| 353 | +- 👉Youtube: https://youtu.be/jCCbCPVXcpQ |
| 354 | +- 👉Facebook: https://www.facebook.com/aizootopi |
0 commit comments