AI Cho Mọi Người

AI Cho Mọi Người

Logistic regression

 

 

 

Logistic regression được dùng cho bài toán phân loại nhị phân (binary classification). Ouput (đầu ra) của logistic regression là giá trị kiểu float nằm trong khoảng [0,1] và có thể được dùng để phân loại nhị phân. Ví dụ chúng ta muốn xây dựng mô hình logistic regression để xác định ảnh đầu vào có chứa con mèo hay không. Lúc này ảnh chứa con mèo có thể xác định bằng cách so sánh giá trị output với một giá trị T (thường cho T = 0.5). Nếu output > T, mô hình quyết định ảnh input có chứa con mèo. Ngược lại, ảnh input không chứa con mèo.

Dữ liệu label cho bài toán phân loại nhị phân  có kiểu số nguyên, hoặc có thể chuyển về kiểu số nguyên. Ví dụ bảng sau hiển thị 10 dòng dữ liệu cho bài toán phân loại nhị phân

Petal_LengthPetal_WidthLabel
1.40.20
1.40.20
1.30.20
1.50.20
1.40.20
4.21.21
4.21.31
4.31.31
31.11
4.11.31

Chú ý là dữ liệu label có giá trị 0 hoặc 1, biểu thị cho hai nhóm dữ liệu.

 

Mô hình losgistic

Cấu trúc của mô hình losgistic regression rất giống với linear regression. Cụ thể hơn, logistic regression dùng thêm hàm sigmoid \(\sigma \left( x \right)\) để ánh xạ giá trị x từ miền giá trị [-R,R] sang miền giá trị [0,1]. Hàm sigmoid là hàm không giảm, nên các giá trị qua hàm sigmoid không thay đổi thứ tự. Hàm sigmoid có biểu thức như sau

$$\sigma \left( x \right) = \frac{1}{{1 + {e^{ – x}}}}$$

Hàm sigmoid có độ thị như sau

Dựa vào đồ thị sigmoid chúng ta thấy rằng các giá trị đầu vào cho hàm sigmoid (trục x) thuộc miền [-R, R] và giá trị sigmoid (trục y) thuộc khoảng (0, 1). Hơn nữa, khi giá trị x đi từ trái sang phải (tăng dần) thì giá trị y quan độ thị hàm sigmoid (đường màu cam) cũng tăng dần.

Gọi x là vector đặc trưng và \(\theta\) là bộ tham số, mô hình logistic regression được biểu diễn như sau

$$z = {\theta ^T}x = {\theta _0}{x_0} + {\theta _1}{x_1} + … + {\theta _n}{x_n}$$

$${h_\theta }\left( x \right) = \sigma \left( {{\theta ^T}x} \right) = \frac{1}{{1 + {e^{ – {\theta ^T}x}}}}$$

Giá trị z được tính giống như bước feed forward của mô hình linear regression và được dùng làm input cho hàm sigmoid. Output \({h_\theta }\left( x \right)\) có giá trị trong khoảng [0, 1].

 

Thiết kế hàm loss

Mục tiêu của hàm loss nhằm phản ánh độ tối tưu của bộ tham số. Bộ tham số càng tối ưu thì  độ chính xác càng lớn và giá trị loss càng nhỏ. Với mô hình linear regression, thiết kế hàm loss tương đối đơn giản do chọn bộ tham số để giá trị tiên đoán gần giá trị ground truth nhất.

Với mô hình logistic regression, giá trị output nằm trong đoạn [0, 1]. Ví dụ chúng ta xây dựng mô hình logistic regression để phân loại ảnh đầu vào có chứa con mèo hay không, và chúng ta có 2 mẫu dữ liệu như sau

Mẫu dữ liệu 1 là ảnh có chứa mèo nên giá trị label là 1, và mẫu dữ liệu 2 là ảnh không chứa mèo nên có giá trị label tương ứng bằng 0. Gọi \(p\) và \(\bar p\) là xác suất cho một ảnh có chứa và không chứa mèo.  Ta có \(p + \bar p = 1\). Giả sử với bộ tham số hiện tại của mô hình, output cho mẫu dữ liệu 1 và 2 tương ứng là \(p_1 = 0.7\) và \(p_2 = 0.4\).

Với output như trên, mô hình hoạt động tốt vì phân loại chính xác cả hai mẫu 1 và 2. Tuy nhiên bộ tham số vẫn cần cải tiến để giá trị \(p_1\) tăng và giá trị \(p_2\) giảm (đồng nghĩa với giá trị \(\bar p_2\)) tăng.

Một trường hợp khác với output như sau

Lúc này mô hình hoạt động không tốt khi không phân loại chính xác cả 2 mẫu dữ liệu. Bộ tham số vẫn cần cải tiến nhiều để giá trị \(p_1\) tăng nhanh và giá trị \(\bar p_2\) tăng nhanh.

Chúng ta quan sát thấy rằng

  • Khi label là 1, p càng lớn càng tốt (tiến về giá trị 1)
  • Khi label là 0, \(\bar p\) càng lớn càng tốt (tiến về giá trị 1)

Tuy nhiên loss có tính chất nghịch đảo so với xác suất. Do đó, chúng ta cần dùng một hàm số để nghịch đảo giá trị xác xuất. Ở đây hàm f(x) = -log(x) được sử dụng vì nó có những tính chất mà chúng ta mong đợi. Hàm  f(x) = -log(x) có đồ thị như sau

Khi giá trị p tăng từ 0 đến 1, giá trị hàm f(p) giảm không tuyến tính về giá trị 0. Giá trị p càng lớn, giá trị f(p) càng nhỏ.

 

Tính loss dùng binary cross entropy

Lập luận về cách thiết kế hàm loss cho mô hình logistic regression ở phần sẽ giúp chúng ta hiểu được rõ hơn vì sao áp dụng binary cross entropy (BCE) để tính loss. Hàm BCE có dạng sau

$$\begin{array}{l}
BCE = – y\log p – \left( {1 – y} \right)\log \left( {1 – p} \right)\\
\,\,\,\,\,\,\,\,\,\,\,\, = – y\log p – \left( {1 – y} \right)\log \left( {\bar p} \right)
\end{array}.$$

Áp dụng hàm BCE cho mẫu dữ liệu 1 với y = 1 và \(p_1 = 0.7\) ta được

$$\begin{array}{l}
BC{E_1} = – y\log p – \left( {1 – y} \right)\log \left( {\bar p} \right)\\
\,\,\,\,\,\,\,\,\,\,\,\,\,\, = – \log p
\end{array}$$

Tương tự, áp dụng hàm BCE cho mẫu dữ liệu 2 với y = 0 và \({\bar p_2} = 0.6\) ta được

$$\begin{array}{l}
BC{E_2} = – y\log p – \left( {1 – y} \right)\log \left( {\bar p} \right)\\
\,\,\,\,\,\,\,\,\,\,\,\,\,\, = – \log {\bar p}
\end{array}$$

Chúng ta thấy rằng hàm BCE thỏa mãn các tính chất chúng ta mong muốn cho một hàm loss. Hàm loss được viết lại với các biến dạng vector và để phù hợp với các ký hiệu của mô hình logistic regresion.

$$J\left( \theta \right) = \frac{1}{m}\left( { – {y^T}\log \left( h \right) – {{\left( {1 – y} \right)}^T}\log \left( {1 – h} \right)} \right)$$

Áp dụng chain rule để tính đạo hàm cho loss \(J\left( \theta \right)\) ta được

$$\frac{{\partial J\left( \theta \right)}}{{\partial \theta }} = \frac{{\partial J}}{{\partial h}}\frac{{\partial h}}{{\partial z}}\frac{{\partial z}}{{\partial \theta }} ,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\ (1)$$

trong đó

$$\frac{{\partial J}}{{\partial h}} = – \left( {\frac{y}{h} – \frac{{1 – y}}{{1 – h}}} \right) = \frac{{h – y}}{{h\left( {1 – h} \right)}},$$

$$\frac{{\partial h}}{{\partial z}} = h\left( {1 – h} \right),$$

$$\frac{{\partial z}}{{\partial \theta }} = x.$$

Thế vào phương trình (1) ra được 

$$\frac{{\partial J\left( \theta \right)}}{{\partial \theta }} = \left( {h – y} \right)x$$

 

Tính đạo hàm cho từng tham số

$$\frac{{\partial J\left( \theta \right)}}{{\partial {\theta _j}}} = \frac{1}{m}{x^T}\left( {\sigma \left( {{\theta ^T}x} \right) – y} \right)$$

 

Cập nhật tham số

$${\theta _j} = {\theta _j} – \eta \frac{{\partial J\left( \theta \right)}}{{\partial {\theta _j}}}$$

 

Áp dụng logistic regression để phân loại hoa Iris

Chúng ta sẽ sử dụng bộ dữ liệu hoa Iris trong bảng sau

Petal_LengthPetal_WidthLabel
1.40.20.0
1.40.20.0
1.30.20.0
1.50.20.0
1.40.20.0
1.70.40.0
1.40.30.0
1.50.20.0
1.40.20.0
1.50.10.0
1.50.20.0
1.60.20.0
1.40.10.0
1.10.10.0
1.20.20.0
1.50.40.0
1.30.40.0
1.40.30.0
1.70.30.0
1.50.30.0
1.70.20.0
1.50.40.0
1.00.20.0
1.70.50.0
1.90.20.0
1.60.20.0
1.60.40.0
1.50.20.0
1.40.20.0
1.60.20.0
1.60.20.0
1.50.40.0
1.50.10.0
1.40.20.0
1.50.10.0
1.20.20.0
1.30.20.0
1.50.10.0
1.30.20.0
1.50.20.0
1.30.30.0
1.30.30.0
1.30.20.0
1.60.60.0
1.90.40.0
1.40.30.0
1.60.20.0
1.40.20.0
1.50.20.0
1.40.20.0
4.71.41.0
4.51.51.0
4.91.51.0
4.01.31.0
4.61.51.0
4.51.31.0
4.71.61.0
3.31.01.0
4.61.31.0
3.91.41.0
3.51.01.0
4.21.51.0
4.01.01.0
4.71.41.0
3.61.31.0
4.41.41.0
4.51.51.0
4.11.01.0
4.51.51.0
3.91.11.0
4.81.81.0
4.01.31.0
4.91.51.0
4.71.21.0
4.31.31.0
4.41.41.0
4.81.41.0
5.01.71.0
4.51.51.0
3.51.01.0
3.81.11.0
3.71.01.0
3.91.21.0
5.11.61.0
4.51.51.0
4.51.61.0
4.71.51.0
4.41.31.0
4.11.31.0
4.01.31.0
4.41.21.0
4.61.41.0
4.01.21.0
3.31.01.0
4.21.31.0
4.21.21.0
4.21.31.0
4.31.31.0
3.01.11.0
4.11.31.0

 

Bộ dữ liệu có 100 mẫu cho 2 loại hoa. Có hai đặc trưng là chiều dài và chiều rộng của cánh hoa pedal.

 

Đọc và xử lý data

import numpy as np
import matplotlib.pyplot as plt

iris = np.genfromtxt('iris_Petal_Logistic.csv', dtype=None, delimiter=',', skip_header=1) 
X = iris[:, :2]
y = iris[:, 2]

plt.figure(figsize=(10, 6))
plt.scatter(X[y == 0][:, 0], X[y == 0][:, 1], color='b', label='0')
plt.scatter(X[y == 1][:, 0], X[y == 1][:, 1], color='r', label='1')
plt.legend()

intercept = np.ones((X.shape[0], 1))
X = np.concatenate((intercept, X), axis=1)

 

Code để huấn luyện mô hình logistic

def sigmoid_function(z):
    return 1 / (1 + np.exp(-z))

def loss_function(h, y):
    return (-y * np.log(h) - (1 - y) * np.log(1 - h)).mean()

def predict(X, theta):    
    h = sigmoid_function(np.dot(X, theta))
    return h.round()


lr=0.01
num_iter=1500


theta = np.random.randn(X.shape[1])

losses = []
accs   = []

for i in range(num_iter):
    z = np.dot(X, theta)
    h = sigmoid_function(z)
    loss = loss_function(h, y)

    gradient = np.dot(X.T, (h - y)) / y.size
    theta -= lr*gradient

    if(i % 5 == 0):
        # loss
        #print(f'loss: {loss} \t')
        losses.append(loss)
        
        # accuracy
        preds = predict(X, theta)
        acc = (preds == y).mean()
        #print(f'acc: {acc} \t')
        accs.append(acc)

 

Giá trị loss

 

Độ chính xác của mô hình qua từng lần lặp