Linear regression là gì

Hồi quy tuyến tính ( Linear Regression ) là bài toán cơ bản và đơn giản nhất của Machine Learning. Hồi quy tuyến tính thuộc nhóm Học có giám sát ( Supervised Learning ). Trong bài này mình sẽ hướng dẫn cho các bạn triển khai một Simple Linear Regression và Multiple Linear Regression trong môi trường thực tế sử dụng thư viện scikit learn.

I. Simple Linear Regression

Simple Linear Regression (Hồi quy tuyến tính đơn giản ) là là một phương pháp thống kê để hồi quy dữ liệu với biến phụ thuộc có giá trị liên tục trong khi các biến độc lập có thể có một trong hai giá trị liên tục hoặc là giá trị phân loại.

1. Bộ dữ liệu

Trường học A đã khảo sát số giờ học ở nhà trong tuần sinh viên giành môn học Giải tích 1 và kết quả đạt được sau khi kết thúc môn học, thống kê được như sau :

HoursScores
2.0 4.1
4.6 6.7
2.5 4.7
8.0 8.2
3.0 5.0

(Các bạn có thể tải file đầy đủ tại đây )

2. Import thư viện:

Đầu tiên chúng ta cần phải import các thư viện cần thiết cho bài bài toán hồi quy tuyến tính

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression 

để import được các thư viện trước tiên bạn phải đảm bảo đã cài đặt thư viện này tại môi trường python đang làm việc.

3. Đọc dữ liệu từ file csv :

Sau khi đã tải file csv về bạn cần đọc các dữ liệu từ file csv đó, chúng ta sử dụng hàm read_csv trong thư viện pandas và hiển thị nó ra màn hình để kiểm tra lại.

data = pd.read_csv('giai-tich2.csv')
data.head()

ngoài ra bạn còn có thể sử dụng các hàm describe trong pandas xem tổng quan về dữ liệu của mình

4. Biễu diễn dữ liệu trên biểu đồ :

Bạn có thể sử dụng thư viện matplotlib để biểu diễn dữ liệu của bài toán hồi quy tuyến tính một cách trực quan hơn.

data.plot(x='Hours', y='Scores', style='ro')
plt.xlabel('Số giờ học')
plt.ylabel('Số điểm')
plt.show()

Linear regression là gì

5. Tính hồi quy tuyến tính do dữ liệu :

Để tính hồi quy tuyến tính cho dữ liệu mình sẽ sử dụng hàm LinearRegression của thư viện scikit learn đã import ở trên

X_train = data.iloc[:, :-1].values
y_train = data.iloc[:, 1].values
regression = LinearRegression()
regression.fit(X_train, y_train)

6. Dự đoán đối với dữ liệu mới :

Để dự đoán kết quả tổng kết môn giải tích 1 với một sinh viên mới học ta chỉ cần sử dụng lại kết quả của hồi quy tuyến tính trên như sau : ví dụ số giờ về nhà sinh viên mới giành cho môn giải tích 1 là 5.3 giờ/tuần.

print('Điểm thi của người đó là: ',regression.predict(np.array([[5.3]]))[0])

Ngoài ra bạn còn có thể chia tập dữ liệu thành 80% để tính, và 20% để kiểm tra. Sau đó dùng các hàm tính độ chính xác để tính độ chính xác của hồi quy tuyến tính trên tập kiểm tra.

Bạn có thể chạy và tham khảo code tạiđây

II. Multiple Linear Regression :

Multiple Linear Regression (Hồi quy tuyến tính đa biến ) là là một phương pháp thống kê để hồi quy dữ liệu có nhiều biến độc lập.

Tương tự Simple Linear Regression, Bạn có thể sử dụng thư viện scikit learn để khảo sát bài toán này.


Linear Regression (hồi quy tuyến tính) là một trong những thuật toán cơ bản nhất của Machine Learning. Ở bài viết này, tôi sẽ giới thiệu đến các bạn khái niệm về thuật toán này, lý thuyết toán học và cách triển khai thuật toán trên Python. Bài viết này được viết bằng Jupyter Lab.

Trước hết hãy import và setup các thư viện cần thiết.

1%matplotlib inline
2import matplotlib.pyplot as plt
3import numpy as np

I. Lý thuyết toán học

1. Bài toán tìm phương trình đường thẳng

Giả sử ta có phân bố ở một mặt phẳng dưới dạng một đường thẳng, nhiệm vụ của chúng ta là tìm ra phương trình đường thẳng khớp nhất với dữ liệu đó. Phương trình đường thẳng cho bài toán này khá đơn giản, hầu hết chúng ta đã quen thuộc với nó từ trung học:

$$ y = ax + b $$

trong đó $a$ có thể được coi là độ dốc hay slope, quyết định đến độ nghiêng của đường thẳng; còn $b$ có thể hiểu là chặn hay intercept của đường thẳng, quyết định sự dịch chuyển của đường thẳng so với gốc toạ độ. Với $b = 0$, đường thẳng sẽ đi qua gốc toạ độ.

Hãy xem xét một dữ liệu có dạng đường thẳng với $a = 3$ và $b = -5$ (là đường thẳng màu đỏ trong hình bên dưới). Ở đây tôi sẽ dùng np.random để tạo ra các điểm phân bố theo một đường thẳng (các chấm xanh ở trong hình vẽ). Ở phần sau của bài viết này, ta sẽ cùng tìm hiểu cách dùng Linear Regression để tìm phương trình đường thẳng ứng với các điểm dữ liệu rời rạc đó.

1rng = np.random.RandomState(1)
2x = 10 * rng.rand(25)
3y = 3 * x - 5 + rng.randn(25)
4plt.scatter(x, y)
5plt.plot(x, 3 * x - 5, linestyle='solid', color='red')

Linear regression là gì

2. Bài toán tổng quát

Tìm phương trình đường thẳng là một trường hợp hết sức đơn giản của có thể áp dụng Linear Regression. Trên thực tế, dữ liệu của bài toán Linear Regression có thể nằm trong không gian nhiều hơn 2 chiều. Khi đó, ta có thể hiểu một cách trực quan là output $ \hat{y} $ của bài toán chính là sự kết hợp các input $ x_i $ theo một tỷ lệ nào đó. Tỷ lệ này là các hệ số $ w_i $, và thường được gọi là trọng số của mô hình. Các giá trị $ x_i $ có thể được viết thành vector $\mathbf X$, các trọng số $w_i$ có thể được viết thành vector $\mathbf W$. Việc tối ưu mô hình Linear Regression là tìm ra vector $ \mathbf{W} $ sao cho từ input $ \mathbf{X} $ ta có thể tính ra được output $ \hat{y} $ của bài toán.

Hãy cùng xem xét biểu diễn bài toán trong không gian $n$ chiều. Ví dụ bài toán xác định gía nhà dựa trên $n$ thuộc tính của căn nhà như diện tích, số phòng, khoảng cách đến trung tâm thành phố... Khi đó input của bài toán sẽ là vector $ \mathbf{X} = [x_1, x_2, x_3, \dots, x_n] $ thể hiện các thuộc tính của căn nhà dưới dạng các số thực và output sẽ là $ \hat{y} = f(\mathbf{X}) \approx y $, với $y$ là giá trị thật của căn nhà. $ f(\mathbf{X}) $ có thể được tính bằng công thức sau:

$$ f(\mathbf{X}) = w_0 + w_1 x_1 + w_2 x_2 + w_3 x_3 + \dots + w_n x_n $$

Nhìn chung giá trị dự đoán $ \hat{y} $ và giá trị thật $ y $ thường là 2 giá trị khác nhau do sai số mô hình. Nhiệm vụ của bài toán này là đi tìm các tham số tối ưu $ {w_0, w_1, w_2, w_3, \dots, w_n } $ sao cho sự khác nhau này là nhỏ nhất. Bài toán xác định phương trình đường thẳng trong mục 1 chính là trường hợp $ n = 2 $, $w_1$ là $a$ và $w_0$ là $b$.

Ta có thể biểu diễn tham số dưới dạng một vector cột $ \mathbf{W} = [w_0, w_1, w_2, w_3, \dots, w_n]^T $ và input $\mathbf X$ mở rộng dưới dạng $ \mathbf{\bar{X}} = [1, x_1, x_2, x_3, \dots, x_n] $. Ở đây, ta thêm số 1 vào $\mathbf{\bar{X}}$ để không cần phải xử lý riêng một trường hợp tham số tự do $w_0$.

Việc tính toán giá trị output dự đoán trở thành:

$$ \hat{y} = \mathbf{\bar{X}}\mathbf{W} $$

Sai số dự đoán $ e = y - \hat{y} $ được gọi là sai số dự đoán. Giá trị này sẽ được tối ưu sao cho gần 0 nhất.

3. Hàm mất mát (loss function)

Chúng ta cần ước lượng xem mô hình của chúng ta bị sai, bị lỗi đến đâu, để từ đó tối ưu mô hình để có thể hoạt động ít lỗi nhất. Việc tính lỗi này không thể dựa vào cảm tính, mà phải dựa vào tính toán số học. Đó là lý do định nghĩa hàm mất mát (loss function) ra đời. Ta có thể hiểu hàm này được thiết kế để đánh giá độ lỗi, độ sai của mô hình. Kết quả của hàm này có giá trị càng lớn thì mô hình của chúng ta càng sai. Việc tối ưu bài toán được đưa về việc tối ưu các giá trị trọng số để hàm mất mát có giá trị nhỏ nhất. Ở bài toán này, dữ liệu huấn luyện gồm $n$ mẫu - $n$ cặp giá trị $(\mathbf{X}_i, y_i) $ với $ i = 1, 2, \dots, n $. Hàm mất mát có thể được định nghĩa là:

$$ \mathcal{L}(\mathbf{W}) = \frac{1}{2}\sum_{i=1}^n (y_i - \mathbf{\bar{X}_i}\mathbf{W})^2 = \frac{1}{2} |\mathbf{y} - \mathbf{\bar{X}}\mathbf{W} |_2^2 $$

Ở công thức trên, chúng ta lấy giá trị output thực tế ở trong tập dữ liệu huấn luyện $ \mathbf{y} $ trừ đi giá trị dự đoán $ \hat{y} = \mathbf{\bar{X}}\mathbf{w} $ sau đó lấy bình phương của kết quả đó để ước lượng sai số của 1 điểm dữ liệu. Tại sao không phải lấy trị tuyệt đối, vì chỉ cần lấy giá trị tuyệt đối là ta đã có thể có một dạng sai số rồi? Câu trả lời là do chúng ta cần một hàm để dễ tính toán đạo hàm ở bước tìm nghiệm cho bài toán. Hàm bình phương có đạo hàm tại mọi điểm, còn trị tuyệt đối có đạo hàm bị đứt tại điểm 0. Số $ \frac{1}{2} $ trong công thức trên chỉ có ý nghĩa làm đẹp kết quả của đạo hàm.

Giá trị tối ưu của $ w $ được ký hiệu là:

$$ \mathbf{w}^* = \arg\min_{\mathbf{w}} \mathcal{L}(\mathbf{w}) $$

4. Tìm nghiệm cho bài toán

Để tìm nghiệm cho bài toán Linear Regression, chúng ta có thể giải phương trình đạo hàm của hàm loss bằng 0. Đạo hàm theo $w$ của hàm loss có dạng:

$$ \frac{\partial{\mathcal{L}(\mathbf{w})}}{\partial{\mathbf{w}}} = \mathbf{\bar{X}}^T(\mathbf{\bar{X}}\mathbf{w} - \mathbf{y}) $$

Nghiệm tối ưu cho bài toán này có dạng như sau (xem thêm tại đây).

$$ \mathbf{w} = (\mathbf{\bar{X}}^T\mathbf{\bar{X}})^{\dagger} \mathbf{\bar{X}}^T\mathbf{y} $$

II. Cài đặt với Python

Ở phần này, chúng ta sẽ giải một bài toán đơn giản, đó là việc tìm phương trình đường thẳng với dữ liệu tự sinh ra ở mục I.1. Việc mở rộng mã nguồn để giải các bài toán khác như dự đoán giá nhà, dự đoán chất lượng rượu, dự đoán giá xe... bạn đọc có thể tự tìm tòi và mở rộng mã nguồn bên dưới. Dữ liệu thử nghiệm cho các bài toán khác có thể được tải về từ đây. Việc mở rộng này sẽ giúp các bạn hiểu sâu hơn về Linear Regression, đống thời có được kĩ năng áp dụng Linear Regression cho các bài toán khác nhau. Đó cũng chính là mục đích cuối cùng của một kĩ sư AI.

Quay lại bài toán tìm phương trình đường thẳng. Bạn có thể sử dụng Jupyter Lab để cài đặt thuật toán, hay đơn giản là viết tất cả vào một file Python. Trước hết hãy đảm bảo là bạn đã import hết các thư viện cần thiết bằng phần code ở đầu bài viết này:

1%matplotlib inline
2import matplotlib.pyplot as plt
3import numpy as np

Sau đấy là việc sinh ra các dữ liệu ngẫu nhiên, phân bố dưới hình dạng một đường thẳng.

1rng = np.random.RandomState(1)
2x = 10 * rng.rand(25)
3y = 3 * x - 5 + rng.randn(25)
4plt.scatter(x, y);
5# plt.plot(x, 3 * x - 5, linestyle='solid', color='red')

Linear regression là gì

1. Sử dụng công thức trong phần lý thuyết

Để cho tiện việc ký hiệu, ta viết lại phương trình đường thẳng ở mục I.1 như sau:

$$ y = ax + b = w_1 x + w_0 $$

Ở phần lý thuyết, ta đã biết được nghiệm tối ưu của bài toán có dạng như sau:

$$ \mathbf{w} = (\mathbf{\bar{X}}^T\mathbf{\bar{X}})^{\dagger} \mathbf{\bar{X}}^T\mathbf{y} $$

Ta sẽ thử cài đặt thuật toán bằng công thức này để tìm ra phương trình đường thẳng ở trên.

 1# Hiện tại, x đang có dạng một array kích thước (25,), tương ứng với 25 giá trị của x vừa được sinh ra.
 2# Ta cần chuyển x về dạng ma trận 2D có kích thước (25, 1) - ứng với 25 hàng và 1 cột.
 3# Tiếp đó ta sẽ nối giá trị 1 vào đầu mỗi mẫu dữ liệu để tạo thành ma trận có kích thước (25, 2) - 
 4# ứng với 25 hàng và 2 cột, trong đó cột đầu toàn là số 1. Bạn đọc có thể dùng thêm lệnh print(Xbar) để xem các 
 5# giá trị trong ma trận được sinh ra.
 6x_reshaped = x.reshape((x.shape[0], 1)) 
 7one = np.ones((x_reshaped.shape[0], 1))
 8Xbar = np.concatenate((one, x_reshaped), axis = 1)
 9
10# Tính toán giá trị tối ưu của w theo công thức như phần trên
11A = np.dot(Xbar.T, Xbar) # Tính phần trong ngoặc của công thức
12b = np.dot(Xbar.T, y) # Tính phần bên ngoài ngoặc
13w = np.dot(np.linalg.pinv(A), b) # Tính giá trị tối ưu của w, trong đó hàm np.linalg.pinv() được dùng để tính giả nghịch đảo
14
15np.set_printoptions(precision=3)
16print("Phương trình đường thẳng: y = {:0.2f}x + ({:0.3f})".format(float(w[1]), w[0]))

Output:

1Phương trình đường thẳng: y = 3.00x + (-5.018)

Vậy là chúng ta đã có thể tìm ra một phương trình đường thẳng cho dữ liệu vừa tạo $y = 3.00x + (-5.018)$. Phương trình này khá gần với phương trình gốc mà chúng ta dùng để tạo ra đường thẳng đó ($y = 3x - 5$). Có thể nói thuật toán của chúng ta đã hoạt động khá tốt. Hãy cùng vẽ đường thẳng chúng ta tìm được cùng với dữ liệu ban đầu:

1rng = np.random.RandomState(1)
2x = 10 * rng.rand(25)
3y = 3 * x - 5 + rng.randn(25)
4plt.scatter(x, y);
5plt.plot(x, w[1] * x + w[0], linestyle='solid', color='red')

Linear regression là gì

2. Sử dụng thư viện scikit-learn

scikit-learn là thư viện rất phổ biến trong Machine Learning. Hãy dùng xem cách cài đặt thuật toán tương tự như trên trong thư viện này.

1from sklearn import datasets, linear_model
2
3regr = linear_model.LinearRegression(fit_intercept=False)
4regr.fit(Xbar, y)
5w = regr.coef_
6
7print("Phương trình đường thẳng: y = {:0.2f}x + ({:0.3f})".format(float(w[1]), w[0]))

Output:

1Phương trình đường thẳng: y = 3.00x + (-5.018)

Vậy là phương trình đường thẳng chúng ta có được cũng tương tự với cách tính bằng Python thuần phía trên. Trên thực tế, bạn nên dùng thư viện scikit-learn thay vì tự tính theo công thức mình khai triển vì các thuật toán trong thư viện scikit-learn đã được đánh giá kĩ càng bởi các chuyên gia và được tối ưu về mặt tốc độ.

III. Thảo luận

Linear Regression là một thuật toán đơn giản và dễ cài đặt cho một bài toán với quan hệ tuyến tính giữa input và output. Tuy vậy Linear Regression có một nhược điểm lớn là nhạy cảm với nhiễu. Hãy cùng thử nghiệm bằng cách thêm một điểm nhiễu $(12.5, 5)$ vào dữ liệu sinh ra.

 1# Sinh dữ liệu ngẫu nhiên tương tự như trước
 2rng = np.random.RandomState(1)
 3x = 10 * rng.rand(25)
 4y = 3 * x - 5 + rng.randn(25)
 5
 6# Thêm điểm nhiễu (12.5, 5)
 7x = np.append(x, 12.5)
 8y = np.append(y, 5)
 9
10# Chuẩn bị Xbar
11x_reshaped = x.reshape((x.shape[0], 1)) 
12one = np.ones((x_reshaped.shape[0], 1))
13Xbar = np.concatenate((one, x_reshaped), axis = 1)
14
15# Tối ưu mô hình
16regr = linear_model.LinearRegression(fit_intercept=False)
17regr.fit(Xbar, y)
18w = regr.coef_
19
20# Vẽ kết quả
21plt.scatter(x, y);
22plt.plot(x, w[1] * x + w[0], linestyle='solid', color='red')

Linear regression là gì

Có thể thấy, chỉ một điểm nhiễu $(12.5, 5)$ đã có thể kéo lệch đường thẳng được tìm ra bởi Linear Regression khá nhiều. Điều này cho thấy thuật toán rất nhạy cảm với nhiễu.

Linear Regression cho bài toán phi tuyến: Ta vẫn có thể sử dụng Linear Regression cho một bài toán phi tuyến tính bằng cách biến đổi nó một chút. Hãy bắt đầu với phương trình cơ bản cho Linear Regression:

$$ y = f(\mathbf{x}) = w_0 + w_1 x_1 + w_2 x_2 + w_3 x_3 + \dots + w_n x_n $$

Ta đặt $x_n = f_n(x)$ với $f_n()$ là một hàm dùng để biến đổi dữ liệu đầu vào. Ví dụ với $f_n(x) = x^n$, bài toán được chuyển thành hồi quy đa thức.

$$ y = f(\mathbf{x}) = w_0 + w_1 x_1 + w_2 x_2^2 + w_3 x_3^3 + \dots + w_n x_n^n $$

Cách làm này giúp chúng ta dùng Linear Regression với các quan hệ phức tạp hơn giữa $x$ và $y$. Tuy nhiên, nó lại dế gây ra hiện tượng overfitting, dẫn đến việc phải có thêm các phương pháp regularization.

Tham khảo

  • https://machinelearningcoban.com/2016/12/28/linearregression/.
  • https://en.wikipedia.org/wiki/Linear_regression.
  • https://github.com/jakevdp/PythonDataScienceHandbook/blob/master/notebooks/05.06-Linear-Regression.ipynb.
  • https://web.stanford.edu/~hastie/Papers/ESLII.pdf