AML

[Thực Hành] Bài 6 Hồi quy tuyến tính - Linear Regression

Loạt bài học thuộc series AML

Posted by KyoHB on December 13, 2020

Chúng ta sẽ cùng thực hành triển khai huấn luyện (training) một Linear Regression model cho một bài toán dự đoán giá nhà nhé !

* Các bài thực hành đều sử dụng Python 3.

Import tập dữ liệu

Tập dữ liệu (dataset) được sử dụng trong bài thực hành này là tập dữ liệu (dataset) ở link sau:

Download Tập dữ liệu

1
import pandas as pd
1
2
df = pd.read_csv('Real estate.csv', index_col=0)
df
X1 transaction date X2 house age X3 distance to the nearest MRT station X4 number of convenience stores X5 latitude X6 longitude Y house price of unit area
No
1 2012.917 32.0 84.87882 10 24.98298 121.54024 37.9
2 2012.917 19.5 306.59470 9 24.98034 121.53951 42.2
3 2013.583 13.3 561.98450 5 24.98746 121.54391 47.3
4 2013.500 13.3 561.98450 5 24.98746 121.54391 54.8
5 2012.833 5.0 390.56840 5 24.97937 121.54245 43.1
... ... ... ... ... ... ... ...
410 2013.000 13.7 4082.01500 0 24.94155 121.50381 15.4
411 2012.667 5.6 90.45606 9 24.97433 121.54310 50.0
412 2013.250 18.8 390.96960 7 24.97923 121.53986 40.6
413 2013.000 8.1 104.81010 5 24.96674 121.54067 52.5
414 2013.500 6.5 90.45606 9 24.97433 121.54310 63.9

414 rows × 7 columns

Tập dữ liệu (dataset) này bao gồm 414 cá thể (instance) bao gồm có 6 biến không phụ thuộc (independent variable) và một biến phụ thuộc (dependent variable) đó là giá một $m^{2}$ nhà.

Tiền xử lý dữ liệu

Một trong những công việc quan trọng khi huấn luyện (training) Linear Regression model đó là chúng ta cần xem sự tương quan (correlation) giữa các biến không phụ thuộc (independent variable) và biến phụ thuộc (dependent variable), cũng nhưng giữa các biến không phụ thuộc (independent variable).

1
2
3
import seaborn as sns

import matplotlib.pyplot as plt
1
2
3
4
5
corr_matrix = df.corr()

sns.heatmap(corr_matrix, annot=True)

plt.show()

png

Qua ma trận tương quan (correlation matrix) trên chúng ta có thể thấy biến không phụ thuộc (independent variable) X1 transaction date, gần như không có tác động gì đến giá nhà, mức độ tương quan gần như bằng 0 (0.087), chúng ta sẽ loại bỏ biến không phụ thuộc này. Nhìn vào ma trận tương quan (correlation matrix) này chúng ta cũng thấy những vấn đề rất logic: giá nhà sẽ tăng nếu tuổi thọ ngôi nhà thấp (X2 house age có mức độ tương quan với giá nhà là -0.21), nhà càng xa trạm tàu điện thì giá nhà cũng càng giảm (X3 distance to the nearest MRT station có mức độ tương quan với giá nhà là -0.67), số lượng cửa hàng tiện lợi càng nhiều thì giá nhà càng tăng (X4 number of convenience stores có mức độ tương quan với giá nhà là 0.57). Tuy nhiên biến không phụ thuộc X3 distance to the nearest MRT station cũng lại có mức độ tương quan cao với các biến khác, tạo ra nguy cơ cộng tuyến (collinearity) cho Linear Regression model, do đó chúng ta cũng cần phải loại bỏ biến này.

1
2
3
df_new = df.drop(df.columns[[0,2]], axis=1)

df_new
X2 house age X4 number of convenience stores X5 latitude X6 longitude Y house price of unit area
No
1 32.0 10 24.98298 121.54024 37.9
2 19.5 9 24.98034 121.53951 42.2
3 13.3 5 24.98746 121.54391 47.3
4 13.3 5 24.98746 121.54391 54.8
5 5.0 5 24.97937 121.54245 43.1
... ... ... ... ... ...
410 13.7 0 24.94155 121.50381 15.4
411 5.6 9 24.97433 121.54310 50.0
412 18.8 7 24.97923 121.53986 40.6
413 8.1 5 24.96674 121.54067 52.5
414 6.5 9 24.97433 121.54310 63.9

414 rows × 5 columns

Cùng kiểm tra xem có dữ liệu trống (missing value) ở tập dữ liệu này (dataset) này không nhé.

1
import missingno as msno
1
2
3
x = df_new.iloc[:,0:4]

x
X2 house age X4 number of convenience stores X5 latitude X6 longitude
No
1 32.0 10 24.98298 121.54024
2 19.5 9 24.98034 121.53951
3 13.3 5 24.98746 121.54391
4 13.3 5 24.98746 121.54391
5 5.0 5 24.97937 121.54245
... ... ... ... ...
410 13.7 0 24.94155 121.50381
411 5.6 9 24.97433 121.54310
412 18.8 7 24.97923 121.53986
413 8.1 5 24.96674 121.54067
414 6.5 9 24.97433 121.54310

414 rows × 4 columns

1
msno.bar(x)

png

Không có giá trị trống (missing value) trong tập dữ liệu (dataset) của chúng ta.

Bước tiếp theo chúng ta sẽ kiểm tra xem có giá trị ngoại biên (outlier) trong tập dữ liệu (dataset) không nhé. Giá trị ngoại biên (outlier) là một giá trị lớn hơn hoặc nhỏ hơn hẳn so với các cá thể (instance) trong tập dữ liệu (dataset), các giá trị này vô hình chung là cho sai lệch trong quá trình huấn luyện (training) ML model. Ví dụ minh họa cho outlier trong một tập dữ liệu (Hãy nhìn vào số liệu của các Player khác và Player 3):

image.png

Để phát hiện được outlier trong tập dữ liệu (dataset), chúng ta sẽ sử dụng biểu đồ hộp (box plot) nhé ! Để hiểu rõ hơn về box plot hãy xem video dưới đây:

Biểu đồ hộp Box-plot

1
2
3
4
5
fig = sns.boxplot(data=df_new)

fig.set_xticks(range(5))

fig.set_xticklabels(['X2', 'X4', 'X5', 'X6', 'Y'])
1
2
3
4
5
[Text(0, 0, 'X2'),
 Text(0, 0, 'X4'),
 Text(0, 0, 'X5'),
 Text(0, 0, 'X6'),
 Text(0, 0, 'Y')]

png

Qua biểu đồ hộp (box plot) của 4 biến không phụ thuộc (dependent variable), chúng ta thấy không có xuất hiện outlier ở ngoài khoảng min và max. Tuy nhiên ở biến phụ thuộc (dependent variable) chúng ta lại thấy có xuất hiện outlier, đây là các giá trị lớn hơn hẳn max của Y, chúng ta cần loại bỏ các cá thể (instance) này.

1
2
3
4
5
6
7
8
9
10
11
# Tính Q1 và Q3 của từng biến trong tập dữ liệu (dataset)

q1 = df_new.quantile(0.25)

q3 = df_new.quantile(0.75)

# Tính IQR

iqr = q3 - q1

iqr
1
2
3
4
5
6
X2 house age                       19.125000
X4 number of convenience stores     5.000000
X5 latitude                         0.014455
X6 longitude                        0.015220
Y house price of unit area         18.900000
dtype: float64
1
2
3
4
# Loại bỏ các cá thể có giá trị lớn hơn max và nhỏ hơn min của từng biến
df_without_outlier = df_new[~((df_new < (q1 - 1.5 * iqr)) |(df_new > (q3 + 1.5 * iqr))).any(axis=1)]

df_without_outlier
X2 house age X4 number of convenience stores X5 latitude X6 longitude Y house price of unit area
No
1 32.0 10 24.98298 121.54024 37.9
2 19.5 9 24.98034 121.53951 42.2
3 13.3 5 24.98746 121.54391 47.3
4 13.3 5 24.98746 121.54391 54.8
5 5.0 5 24.97937 121.54245 43.1
... ... ... ... ... ...
409 18.5 3 24.96330 121.51243 28.1
411 5.6 9 24.97433 121.54310 50.0
412 18.8 7 24.97923 121.53986 40.6
413 8.1 5 24.96674 121.54067 52.5
414 6.5 9 24.97433 121.54310 63.9

371 rows × 5 columns

Kiểm tra lại biểu đồ hộp (box plot) sau khi đã loại trừ các giá trị ngoại biên (outlier).

1
2
3
4
5
fig = sns.boxplot(data=df_without_outlier)

fig.set_xticks(range(5))

fig.set_xticklabels(['X2', 'X4', 'X5', 'X6', 'Y'])
1
2
3
4
5
[Text(0, 0, 'X2'),
 Text(0, 0, 'X4'),
 Text(0, 0, 'X5'),
 Text(0, 0, 'X6'),
 Text(0, 0, 'Y')]

png

Do các giá trị ở biến không phụ thuộc (independent variable) không quá chênh lệch nhiều (hàng chục và đơn vị) nên chúng ta không cần phải chuẩn hóa (normalize). Tập dữ liệu (dataset) của chúng ta đã có thể tiến hành huấn luyện (training)

Phân chia tập dữ liệu

Ở bước này chúng ta sẽ chia dữ liệu ra làm 2 tập nhỏ hơn, tập huấn luyện (training set) và tập xác nhận (validation set) với tỷ lệ 80% và 20%.

* random_state nhằm đảm bảo việc phân chia dữ liệu là như nhau trong mọi lần chạy.

1
2
3
y = df_without_outlier['Y house price of unit area']

y
1
2
3
4
5
6
7
8
9
10
11
12
13
No
1      37.9
2      42.2
3      47.3
4      54.8
5      43.1
       ... 
409    28.1
411    50.0
412    40.6
413    52.5
414    63.9
Name: Y house price of unit area, Length: 371, dtype: float64
1
from sklearn.model_selection import train_test_split
1
2
3
x_without_outlier = df_without_outlier.iloc[:,0:4]

X_train, X_valid, Y_train, Y_valid = train_test_split(x_without_outlier, y, test_size = 0.2, random_state = 101)

Huấn luyện regression model

1
from sklearn.linear_model import LinearRegression

Khởi tạo Linear Regression model

1
regressor = LinearRegression()

Fit Linear Regression model vừa khởi tạo với tập dữ liệu huấn luyện (training set)

1
regressor.fit(X_train, Y_train)
1
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

Xem các model parameter của model chúng ta vừa huấn huyện xong nhé

1
regressor.coef_
1
array([-3.03451439e-01,  1.44280883e+00,  4.58836606e+02,  2.63959238e+02])

Chúng ta có 4 trọng số (weight) tương ứng của 4 biến không phụ thuộc (independent variable): X2 house age, X4 number of convenience stores, X5 latitude, X6 longitude

1
regressor.intercept_
1
-43499.661316731115

CÓ thể viết lại hàm số tuyến tính (linear function) của model của chúng ta sẽ như sau:

$y = -0.303X_{2} + 1.443X_{4} + 458.8X_{5} - 263.9X_{6} - 43499.66$

Kiểm tra Linear Regression đã huấn luyện

1
pred = regressor.predict(X_valid)
1
2
3
from sklearn.metrics import mean_squared_error

import math

Đầu tiên chúng ta sẽ tính Root Mean Square Error (RMSE)

1
math.sqrt(mean_squared_error(Y_valid, pred))
1
7.898845664720699

Sai số ở đây là khoảng 8\$, tức là với mỗi giá nhà dự đoán chênh lệch so với thực tế (nhiều hơn hoặc ít hơn) 8\$.

Một con chỉ khác chúng ta có thể tham khảo đó là $R^{2}$, đây là chỉ số miêu tả tỷ lệ phương sai (variance) của biến phụ thuộc (dependent variable) được giải thích (explain) bởi các biến không phụ thuộc (independent variable). Chỉ số $R^{2}$ nằm trong thang từ 0-1, càng gần 1 thì model càng chính xác.

1
from sklearn.metrics import r2_score
1
r2_score(Y_valid, pred)
1
0.49820177247193564

Có thể thấy model của chúng ta không thật sự tốt, $R^{2}$ chỉ ở mức 0.49, có thể là do phân phối (distribution) của tập dữ liệu là phi tuyến tính (non linear).

Chuyển đổi sang Polynomial Regression

Qua việc kiểm tra Linear Regression model đã huấn luyện, chúng ta có thể thấy kết quả không được như kỳ vọng, do đó để tăng hiệu năng của ML model, chúng ta cùng biến đổi các biến không phụ thuộc (independent variable) sang các đặc trưng (feature) Polynomial để xem có giúp cải thiện được hiệu năng không.

1
from sklearn.preprocessing import PolynomialFeatures

Chúng ta sẽ chuyển đổi sang các biến không phụ thuộc (independent variable) sang các đặc trưng (feature) Polynomial bậc (degree) 3.

Với việc biến đổi sang bậc (degree) 3, Polynomial Regression model chúng ta sẽ có các biến không phụ thuộc (independent variable):

  • Bias: 1

  • Bậc(Degree) 1: $X_{2}, X_{4}, X_{5}, X_{6}$

  • Bậc(Degree) 2: $X_{2}^{2}, X_{4}^{2}, X_{5}^{2}, X_{6}^{2}$

  • Bậc(Degree) 3: $X_{2}^{3}, X_{4}^{3}, X_{5}^{3}, X_{6}^{3}$

  • Tương tác (Interaction): $X_{2} \times X_{4}, X_{2} \times X_{5}, X_{4} \times X_{6},…$

1
2
3
poly_feature  = PolynomialFeatures(degree= 3)

x_poly = poly_feature.fit_transform(x_without_outlier)
1
import numpy as np
1
2
3
x_poly = np.asarray(x_poly)

x_poly
1
2
3
4
5
6
7
8
9
10
11
12
13
array([[1.00000000e+00, 3.20000000e+01, 1.00000000e+01, ...,
        7.58592545e+04, 3.69049329e+05, 1.79539606e+06],
       [1.00000000e+00, 1.95000000e+01, 9.00000000e+00, ...,
        7.58427674e+04, 3.69005898e+05, 1.79536371e+06],
       [1.00000000e+00, 1.33000000e+01, 5.00000000e+00, ...,
        7.58887548e+04, 3.69137799e+05, 1.79555871e+06],
       ...,
       [1.00000000e+00, 1.88000000e+01, 7.00000000e+00, ...,
        7.58362458e+04, 3.68991626e+05, 1.79537922e+06],
       [1.00000000e+00, 8.10000000e+00, 5.00000000e+00, ...,
        7.57609311e+04, 3.68812040e+05, 1.79541512e+06],
       [1.00000000e+00, 6.50000000e+00, 9.00000000e+00, ...,
        7.58085170e+04, 3.68938913e+05, 1.79552281e+06]])

Cũng như ở trên chúng ta cũng sẽ chia ra hai tập dữ liệu nhỏ hơn, một tập để huấn luyện (training set), một tập để xác nhận (validation set)

1
X_train_poly, X_valid_poly, Y_train_poly, Y_valid_poly = train_test_split(x_poly, y, test_size = 0.2, random_state = 101)

Lưu ý là chúng ta vẫn sử dụng Linear Regression, nhưng chỉ khác là chúng ta sẽ fit nó với các đặc trưng (feature) Polynomial vừa tạo ra ở trên

1
poly_regressor = LinearRegression()
1
poly_regressor.fit(X_train_poly, Y_train_poly)
1
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)

Kiểm tra hiệu năng của Polynomial Regression vừa tạo ra nhé

1
pred = poly_regressor.predict(X_valid_poly)
1
math.sqrt(mean_squared_error(Y_valid_poly, pred))
1
6.1589550610291335
1
r2_score(Y_valid_poly, pred)
1
0.6949184550706298

Có thể thấy ML model của chúng ta đã được cải thiện lên rất nhiều, RMSE giảm xuống còn 6\$, trong khó đi $R_{2}$ tăng lên 0.695 !

Các bạn có thể thay đổi các thông số trong quá trình huấn luyện (training) như bậc của các đặc trưng (feature) Polynomial, không loại bỏ biến X3 distance to the nearest MRT station,… Sau đó comment ở dưới bài viết này để xem sự khác biệt nhé 😁