Bài 2: Logistic regression
Bài trước học về linear regression với đầu ra là giá trị thực, thì ở bài này sẽ giới thiệu thuật toán logistic regression với giá trị đầu ra là nhị phân, ví dụ: email gửi đến hòm thư của bạn có phải spam hay không; u là u lành tính hay ác tính,…
Nội dung
Bài toán
Ngân hàng bạn đang làm có chương trình cho vay ưu đãi cho các đối tượng mua chung cư. Tuy nhiên gần đây có một vài chung cư rất hấp dẫn (giá tốt, vị trí đẹp,…) nên lượng hồ sơ người nộp cho chương trình ưu đãi tăng đáng kể. Bình thường bạn có thể duyệt 10-20 hồ sơ một ngày để quyết định hồ sơ có được cho vay hay không, tuy nhiên gần đây bạn nhận được 1000-2000 hồ sơ mỗi ngày. Bạn không thể xử lý hết hồ sơ và bạn cần có một giải pháp để có thể dự đoán hồ sơ mới là có nên cho vay hay không.
Sau khi phân tích thì bạn nhận thấy là hai yếu tố chính quyết định đến việc được vay tiền đó là mức lương và thời gian công tác. Đây là dữ liệu bạn có từ trước đến nay:
Lương | Thời gian làm việc | Cho vay |
10 | 1 | 1 |
5 | 2 | 1 |
… | … | 1 |
8 | 0.1 | 0 |
7 | 0.15 | 0 |
… | … | 0 |
Về mặt logic, giờ mình cần tìm đường thẳng phân chia giữa các điểm cho vay và từ chối. Rồi quyết định điểm mới từ đường đấy
Ví dụ đường xanh là đường phân chia. Dự đoán cho hồ sơ của người có mức lương 6 triệu và 1 năm kinh nghiệm là không cho vay.
Tuy nhiên, do ngân hàng đang trong thời kỳ khó khăn nên việc cho vay bị thắt lại, chỉ những hồ sơ nào chắc chắn trên 80% mới được vay.
Vậy nên bây giờ bạn không những tìm là hồ sơ ấy cho vay hay không cho vay mà cần tìm xác xuất nên cho hồ sơ ấy vay là bao nhiêu.
Xác xuất
Xác xuất là gì??? Theo wiki, “Các nhà toán học coi xác suất là các số trong khoảng [0,1], được gán tương ứng với một biến cố mà khả năng xảy ra hoặc không xảy ra là ngẫu nhiên“. Ví dụ bạn tung đồng xu có 2 mặt, thì xác xuất bạn tung được mặt ngửa là 50% ( = 50/100 = 0.5).
Nhận xét:
- Xác xuất của 1 sự kiện trong khoảng [0,1]
- Sự kiện bạn càng chắc chắc xẩy ra thì xác xuất càng cao. Ví dụ bạn lương cao và còn đi làm lâu lăm thì xác xuất bạn được vay mua chung cư là cao.
- Tổng xác xuất của sự kiện A và và sự kiện phủ định của A là 100% (hay 1). Ví dụ sự kiện A: tung đồng xu mặt ngửa, xác xuất 50%; phủ định của sự kiện A: tung đồng xu mặt sấp, xác xuất 50% => tổng 100%
Bạn sẽ thấy xác xuất quan trọng hơn là chỉ 0 hay 1, ví dụ trước mỗi ca mổ khó, bác sĩ không thể chắc chắn là sẽ thất bại hay thành công mà chỉ có thể nói xác xuất thành công là bao nhiêu (ví dụ 80%).
Hàm sigmoid
Giờ mình cần tìm xác xuất của hồ sơ mới nên cho vay. Hay giá trị của hàm cần trong khoảng [0,1]. Rõ ràng là giá trị của phương trình đường thẳng như bài trước có thể ra ngoài khoảng [0,1] nên cần một hàm mới luôn có giá trị trong khoảng [0,1]. Đó là hàm sigmoid.
Nhận xét:
- Hàm số liên tục, nhận giá trị thực trong khoảng (0,1).
- Hàm có đạo hàm tại mọi điểm (để áp dụng gradient descent)
Thiết lập bài toán
Mọi người có để ý các bước trong bài 1 không nhỉ, các bước bao gồm:
- Thiết lập model
- Thiết lập loss function
- Tìm tham số bằng việc tối ưu loss function
- Dự đoán dữ liệu mới bằng model vừa tìm được
Đây là hô hình chung cho bài toán trong series này.
Model
Với dòng thứ i trong bảng dữ liệu, gọi x_1^{(i)} là lương và x_2^{(i)} là thời gian làm việc của hồ sơ thứ i .
p(x^{(i)}=1) = \hat{y_i} là xác xuất mà model dự đoán hồ sơ thứ i được cho vay.
p(x^{(i)}=0) = 1 - \hat{y_i} là xác xuất mà model dự đoán hồ sơ thứ i không được cho vay.
=> p(x^{(i)}=1) + p(x^{(i)}=0) = 1
Hàm sigmoid: \displaystyle\sigma(x) = \frac{1}{1+e^{-x}}.
Như bài trước công thức của linear regression là: \hat{y_i} = w_0 + w_1 * x_i thì giờ công thức của logistic regression là:
\displaystyle\hat{y_i} = \sigma({w_0 + w_1 * x_1^{(i)} + w_2*x_2^{(i)}}) = \frac{1}{1+e^{-(w_0 + w_1 * x_1^{(i)} + w_2*x_2^{(i)} )}}Ở phần cuối mọi người sẽ thấy được quan hệ giữa xác xuất và đường thẳng.
Loss function
Giờ cũng cần một hàm để đánh giá độ tốt của model. Như bài trước là \hat{y} càng gần y càng tốt, giờ cũng vậy:
- Nếu hồ sơ thứ i là cho vay, tức y_i = 1 thì ta cũng mong muốn \hat{y_i} càng gần 1 càng tốt hay model dự đoán xác xuất người thứ i được vay vốn càng cao càng tốt.
- Nếu hồ sơ thứ i không được vay, tức y_i = 0 thì ta cũng mong muốn \hat{y_i} càng gần 0 càng tốt hay model dự đoán xác xuất người thứ i được vay vốn càng thấp càng tốt
Với mỗi điểm (x^{(i)}, y_i), gọi hàm loss function
L = -(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i}))
Thử đánh giá làm L nhé
Nếu y_i = 1 => L = -log(\hat{y_i})
- Hàm L giảm dần từ 0 đến 1
- Khi model dự đoán \hat{y_i} gần 1, tức giá trị dự đoán gần với giá trị thật y_i thì L nhỏ, xấp xỉ 0
- Khi model dự đoán \hat{y_i} gần 0, tức giá trị dự đoán ngược lại giá trị thật y_i thì L rất lớn
Ngược lại, nếu y_i = 0 => L = -log(1 - \hat{y_i})
- Hàm L tăng dần từ 0 đến 1
- Khi model dự đoán \hat{y_i} gần 0, tức giá trị dự đoán gần với giá trị thật y_i thì L nhỏ, xấp xỉ 0
- Khi model dự đoán \hat{y_i} gần 1, tức giá trị dự đoán ngược lại giá trị thật y_i thì L rất lớn
=> Hàm L nhỏ khi giá trị model dự đoán gần với giá trị thật và rất lớn khi model dự đoán sai, hay nói cách khác L càng nhỏ thì model dự đoán càng gần với giá trị thật. => Bài toán tìm model trở thành tìm giá trị nhỏ nhất của L
Hàm loss function trên toàn bộ dữ liệu
\displaystyle J = - \sum_{i=1}^{N}(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i}))Chain rule
Chain rule là gì? Nếu \displaystyle z = f(y) và \displaystyle y = g(x) hay \displaystyle z = f(g(x)) thì \displaystyle \frac{dz}{dx} = \frac{dz}{dy} * \frac{dy}{dx}
Ví dụ cần tính đạo hàm \displaystyle z(x) = (2x+1)^2, có thể thấy \displaystyle z = f(g(x)) trong đó \displaystyle f(x) = x^2, g(x) = 2x+1 . Do đó áp dụng chain rule ta có:
\displaystyle \frac{dz}{dx} = \frac{dz}{dy} * \frac{dy}{dx} = \frac{d(2x+1)^2}{d(2x+1)} * \frac{d(2x+1)}{dx} = 2*(2x+1)*2 = 4*(2x+1)\newlineMọi người biết \displaystyle \frac{dt^2}{dt} = 2t =>\frac {d(2x+1)^2}{d(2x+1)} = 2(2x+1) bằng cách đặt t = 2x+1.
Có một cách dễ nhìn hơn để tính chain rule là dùng biểu đồ
Số và biến (2, x, 1) viết ở bên trái và không có hình tròn bao quanh, các hình tròn là các phép tính (*, +, ^2)
Giá trị của biểu thức sau khi thực hiện phép tính được viết màu đen ở phía trên đường sau phép tính. ví dụ, phép cộng của 2x và 1 có giá trị 2x+1, phép bình phương 2x+1 có giá trị là (2x+1)^2.
Giá trị của đạo hàm qua thực hiện phép tính được viết ở bên dưới với mực đỏ, ví dụ qua phép bình phương, \displaystyle \frac {d(2x+1)^2}{d(2x+1)} = 2(2x+1), hay qua phép cộng \displaystyle \frac {d(2x+1)}{d(2x)} = 1 và qua phép nhân \displaystyle \frac {d(2x)}{d(x)} = 2.
Ở bước cuối cùng đạo hàm được viết là 1 (có hay không cũng được vì nhân với 1 vẫn thế nhưng để 1 bạn biết đấy là điểm kết thúc). giờ cần tính \displaystyle \frac {d(2x+1)^2}{dx} thì bạn nhân tất cả các giá trị màu đỏ trên đường đi từ x đến (2x+1)^2
Do đó \displaystyle \frac {d(2x+1)^2}{dx} =2 * (2x+1) * 2 * 1 = 4*(2x+1)
Thực ra nếu bạn để ý biểu đồ chính là chain rule:
\displaystyle \frac{d(2x+1)^2}{dx}= \frac{d(2x+1)^2}{d(2x+1)} * \frac{d(2x+1)}{d(2x)} * \frac{d(2x)}{dx} = 2*(2x+1)*2 = 4*(2x+1)\newlineChỉ là biểu đồ dễ nhìn hơn.
Thử áp dụng tính đạo hàm của hàm sigmoid \displaystyle\sigma(x) = \frac{1}{1+e^{-x}}.
Nhắc lại kiến thức đạo hàm cơ bản \displaystyle \frac{d(\frac{1}{x})}{dx} = \frac{-1}{x^2}, \frac{d(e^x)}{dx} = e^x
Do đó \displaystyle \frac{d(\sigma(x))}{dx} = \frac{d(\frac{1}{1+e^{-x}})}{dx} = \frac{-1}{(1+e^{-x})^2} * 1 * e^{-x} * (-1) = \frac{e^{-x}}{(1 + e^{-x})^2}\newline \quad \quad \quad = \frac{1}{1+e^{-x}} * \frac{e^{-x}}{1 + e^{-x}} = \frac{1}{1+e^{-x}} * (1 - \frac{1}{1+e^{-x}} ) = \sigma(x) * (1 - \sigma(x))
Thực ra mọi người không cần vẽ đồ thị quá chi tiết như trên chỉ tách phép tính khi mà nó phức tạp
Áp dụng gradient descent
Với mỗi điểm (x^{(i)}, y_i), gọi hàm loss function
L = -(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i})) trong đó \hat{y_i} = \sigma({w_0 + w_1 * x_1^{(i)} + w_2*x_2^{(i)}}) là giá trị mà model dự đoán, còn y_i là giá trị thật của dữ liệu.
Nhắc lại đạo hàm cơ bản, \displaystyle \frac{d(log(x))}{dx} = \frac{1}{x}
Áp dụng chain rule ta có: \displaystyle \frac{dL}{dw_0} = \frac{dL}{d\hat{y_i}} * \frac{d\hat{y_i}}{dw_0}
\displaystyle \frac{dL}{d\hat{y_i}} = - \frac{d(y_i * log(\hat{y_i}) + (1 - y_i) * log(1 - \hat{y_i}))}{d\hat{y_i}}= - (\frac{y_i}{\hat{y_i}} - \frac{1-y_i}{(1-\hat{y})})\newline
Từ đồ thị ta thấy:
\displaystyle \frac{d\hat{y_i}}{dw_0} = \frac{\sigma({w_0 + w_1 * x_1^{(i)} + w_2*x_2^{(i)}})}{dw_0} = \hat{y_i} * (1-\hat{y_i})\newline \displaystyle \frac{d\hat{y_i}}{dw_1} = \frac{\sigma({w_0 + w_1 * x_1^{(i)} + w_2*x_2^{(i)}})}{dw_1} = x_1^{(i)}*\hat{y_i} * (1-\hat{y_i})\newline \displaystyle \frac{d\hat{y_i}}{dw_2} = \frac{\sigma({w_0 + w_1 * x_1^{(i)} + w_2*x_2^{(i)}})}{dw_2} = x_2^{(i)}* \hat{y_i} * (1-\hat{y_i})\newlineDo đó
\displaystyle \frac{dL}{dw_0} = \frac{dL}{d\hat{y_i}} * \frac {d\hat{y_i}}{dw_0} = - (\frac{y_i}{\hat{y_i}} - \frac{1-y_i}{(1-\hat{y_i})}) * \hat{y_i} * (1-\hat{y_i}) = -(y_i * (1-\hat{y_i}) - (1-y_i) * \hat{y_i})) = \hat{y_i}-y_iTương tự
\displaystyle \frac{dL}{dw_1} = x_1^{(i)} * (\hat{y_i}-y_i) \newline \frac {dL}{dw_2} = x_2^{(i)} * (\hat{y_i}-y_i)Đấy là trên một điểm dữ liệu, trên toàn bộ dữ liệu
\displaystyle \frac{dL}{dw_0} = \sum_{i=1}^{N}(\hat{y_i}-y_i) \newline \frac{dL}{dw_1} = \sum_{i=1}^{N} x_1^{(i)} * (\hat{y_i}-y_i) \newline \frac {dL}{dw_2} = \sum_{i=1}^{N} x_2^{(i)} * (\hat{y_i}-y_i)Biểu diễn bài toán dưới ma trận
Nếu mọi người thấy các công thức biểu diễn dưới ma trận vẫn lạ lạ thì nên xem lại bài 1 và lấy giấy bút tự tính và biểu diễn lại.
Sau khi thực hiện thuật toán gradient descent ta sẽ tìm được w_0, w_1, w_2. Với mỗi hồ sơ mới x^{(t)} ta sẽ tính được phần trăm nên cho vay \hat{y_t} = \sigma(w_0 + w_1 * x_1^{(t)} + w_2 * x_2^{(t)}) rồi so sánh với ngưỡng cho vay của công ty t (bình thường là t = 0.5, thời kì thiết chặt thì là t = 0.8) nếu \hat{y_t}>= t thì cho vay, không thì không cho vay.
Quan hệ giữa phần trăm và đường thẳng.
Xét đường thẳng y = ax+b, gọi f = y – (ax+b) thì đường thẳng chia mặt phẳng thành 2 miền, 1 miền hàm f có giá trị dương, 1 miền hàm f có giá trị âm và giá trị f các điểm trên đường thẳng bằng 0
Ví dụ, xét đường thẳng y = 3-x, hàm f = y – 3 + x.
- Tại điểm A(1,3) => f = 3 – 3 + 1 = 1 > 0
- Tại điểm B(2,1) nằm trên đường thẳng => f = 2 – 3 + 1 = 0
- Tại điểm C(1,1) => f = 1 – 3 + 1 = -1 < 0
Giả sử bạn lấy mốc ở chính giữa là 50% tức là nếu hồ sơ mới dự đoán \hat{y_i} >= 0.5 thì cho vay, còn nhỏ hơn 0.5 thì không cho vay.
\displaystyle \hat{y_i} >= 0.5 <=> \frac{1}{1+e^{-(w_0+w_1*x_1^{(i)} + w_2*x_2^{(i)}})} >= 0.5\newline <=> 2 >= 1+e^{-(w_0+w_1*x_1^{(i)} + w_2*x_2^{(i)})}\newline <=> e^{-(w_0+w_1*x_1^{(i)} + w_2*x_2^{(i)})} <= 1 = e^0 \newline <=> -(w_0+w_1*x_1^{(i)} + w_2*x_2^{(i)}) <= 0 \newline<=> w_0 + w_1*x_1^{(i)} + w_2*x_2^{(i)} >= 0Tương tự \displaystyle \hat{y_i} < 0.5 <=> w_0 + w_1*x_1^{(i)} + w_2*x_2^{(i)}< 0
=> đường thẳng w_0 + w_1*x + w_2*y = 0 chính là đường phân cách giữa các điểm cho vay và từ chối.
Trong trường hợp tổng quát bạn lấy xác xuất lớn hơn t (0<t<1) thì mới cho vay tiền
\displaystyle \hat{y_i} > t <=> w_0 + w_1*x_1^{(i)} + w_2*x_2^{(i)}> -ln(\frac{1}{t} - 1)
Ví dụ t = 0.8, bạn thấy đường phân chia gần với điểm màu đỏ hơn so với t = 0.5 thậm chí một số hồ sơ cũ trước được cho vay nhưng nếu giờ nộp lại cũng từ chối. Đồng nghĩa với việc công ty thắt chắt việc cho vay lại. Nghe hoàn toàn hợp lý đúng ko?
Ứng dụng
- Spam detection: Dự đoán mail gửi đến hòm thư của bạn có phải spam hay không.
- Credit card fraud: Dự đoán giao dịch ngân hàng có phải gian lận không.
- Health: Dự đoán 1 u là u lành hay u ác tính.
- Banking: Dự đoán khoản vay có trả được hay không.
- Investment: Dự đoán khoản đầu tư vào start-up có sinh lợi hay không
Python code
Code và dữ liệu mọi người có thể lấy ở đây.
# Thêm thư viện
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# Hàm sigmoid
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Load data từ file csv
data = pd.read_csv('dataset.csv').values
N, d = data.shape
x = data[:, 0:d-1].reshape(-1, d-1)
y = data[:, 2].reshape(-1, 1)
# Vẽ data bằng scatter
plt.scatter(x[:10, 0], x[:10, 1], c='red', edgecolors='none', s=30, label='cho vay')
plt.scatter(x[10:, 0], x[10:, 1], c='blue', edgecolors='none', s=30, label='từ chối')
plt.legend(loc=1)
plt.xlabel('mức lương (triệu)')
plt.ylabel('kinh nghiệm (năm)')
# Thêm cột 1 vào dữ liệu x
x = np.hstack((np.ones((N, 1)), x))
w = np.array([0.,0.1,0.1]).reshape(-1,1)
# Số lần lặp bước 2
numOfIteration = 1000
cost = np.zeros((numOfIteration,1))
learning_rate = 0.01
for i in range(1, numOfIteration):
# Tính giá trị dự đoán
y_predict = sigmoid(np.dot(x, w))
cost[i] = -np.sum(np.multiply(y, np.log(y_predict)) + np.multiply(1-y, np.log(1-y_predict)))
# Gradient descent
w = w - learning_rate * np.dot(x.T, y_predict-y)
print(cost[i])
# Vẽ đường phân cách.
t = 0.5
plt.plot((4, 10),(-(w[0]+4*w[1]+ np.log(1/t-1))/w[2], -(w[0] + 10*w[1]+ np.log(1/t-1))/w[2]), 'g')
plt.show()