Hàm sigmoid được xây dựng bằng cách nào

Theo Wikipedia , chức năng kích hoạt của một nút xác định đầu ra của nút đó với một đầu vào hoặc một tập hợp các đầu vào dưới dạng mạng nơ-ron nhân tạo. Một mạch tích hợp tiêu chuẩn có thể được coi là một mạng kỹ thuật số gồm các chức năng kích hoạt có thể “BẬT” [1] hoặc “TẮT” [0], tùy thuộc vào đầu vào.

Có nhiều loại chức năng kích hoạt khác nhau

· Binary Step · Linear · Sigmoid · Tanh · ReLU · Leaky ReLU · Parameterised ReLU · Exponential Linear Unit · Swish · Softmax

Hôm nay, chúng ta sẽ thảo luận thêm về loại kích hoạt phổ biến, hàm sigmoid.

Chức năng Sigmoid là gì?

Hàm sigmoid là một hàm toán học có đường cong đặc trưng hình chữ “S”, biến đổi các giá trị giữa phạm vi 0 và 1. Hàm sigmoid còn được gọi là đường cong sigmoidal hoặc hàm logistic. Nó là một trong những chức năng kích hoạt phi tuyến tính được sử dụng rộng rãi nhất.

Biểu thức toán học cho sigmoid:

Đồ thị

Trong đồ thị trên, nếu giá trị của x đi đến dương vô cùng thì giá trị dự đoán của y sẽ trở thành 1 và nếu nó đi đến âm vô cùng thì giá trị dự đoán của y sẽ trở thành 0.

Tôi giả sử bạn biết hồi quy logistic, là thuật toán phổ biến được sử dụng để phân loại nhị phân hoặc khi giá trị của biến mục tiêu có bản chất phân loại. Hàm logit hoặc sigmoid được sử dụng để dự đoán xác suất của một kết quả nhị phân. Ví dụ: chúng tôi sử dụng hồi quy logistic để phân loại trong phát hiện spam, phát hiện gian lận, v.v.

Hãy cùng xem qua một trong những ví dụ về việc triển khai Logistic Regression trong Python.

Đầu tiên, chúng ta phải nhập các thư viện cần thiết.

chúng ta hãy tải bộ dữ liệu, hôm nay chúng ta sẽ sử dụng với một số bác sĩ trên bệnh nhân đau tim. Chúng tôi đã sao chép dữ liệu từ đây vào trang tính excel.

Mục tiêu của chúng tôi là dự đoán liệu bệnh nhân có bị cơn đau tim thứ hai trong vòng 1 năm hay không [yes = 1]. Chúng tôi có hai biến độc lập; một là liệu bệnh nhân có hoàn thành một đợt điều trị bao gồm các phương pháp kiểm soát cơn tức giận hay không [có = 1]. IV còn lại là điểm trên thang điểm về đặc điểm lo lắng [điểm cao hơn có nghĩa là lo lắng hơn].

Tôi giả sử bạn có hiểu biết cơ bản về việc đào tạo bộ dữ liệu. Bây giờ, chúng ta sẽ sử dụng hàm “train_test_split” trong Scikit-learning để chia dữ liệu thành các phần đào tạo và kiểm tra.

Chúng tôi đã sử dụng StandardScaler từ Scikit-Learn để chia lại tỷ lệ dữ liệu mang lại các giá trị có thể có phạm vi hoặc đơn vị cực kỳ khác nhau.

Bây giờ, chúng ta sẽ nhập thuật toán hồi quy logistic từ sci-kit-learning và cấp dữ liệu vào hồi quy logistic và sau đó tạo một phiên bản của trình phân loại và phù hợp với dữ liệu đào tạo.

Khi chúng tôi đào tạo và phù hợp với dữ liệu, chúng tôi sẽ có thể thấy hệ số và hệ số chặn cho biến tương ứng.

Dưới đây là z [phương trình] trong hàm sigmoid với hệ số và hệ số chặn tương ứng.

Tính z cho biến khác nhau cho dữ liệu thử nghiệm bằng cách sử dụng một phương trình ở trên.

Thậm chí, chúng ta có thể lấy hàm quyết định hoặc z, chúng ta đã tính toán ở trên với hàm quyết định từ biến logistic_clf.

Chúng ta sẽ định nghĩa hàm sigmoid như bên dưới.

Chúng ta đã biết rằng hàm sigmoid sẽ chuyển đổi giá trị thực giữa 0 và 1. Chúng ta có thể kiểm tra kịch bản đó với hàm này với z hoặc hàm quyết định được tính toán của chúng ta. Ví dụ: chúng tôi tính toán z1 là -1.061 không nằm giữa 0 và 1. Dưới đây, chúng tôi có thể thấy nó đã được chuyển đổi thành 0,052.

Ngoài ra, chúng ta có thể sử dụng lớp Pred_prob:

Ở đây, chúng tôi sử dụng hàm sigmoid hoặc hàm logit để ánh xạ các giá trị dự đoán với xác suất. Các hàm sẽ ánh xạ bất kỳ giá trị thực nào thành một giá trị khác sẽ nằm trong khoảng từ 0 đến 1 hoặc nói cách khác là dự đoán xác suất.

Ranh giới quyết định

Điều gì sau khi chuyển đổi thành giá trị xác suất, từ hàm sigmoid của chúng tôi? Để ánh xạ lớp nhị phân, chúng tôi sẽ quyết định một số giá trị ngưỡng, thay đổi theo trường hợp sử dụng, để phân loại các giá trị thành hai loại khác nhau. Ngưỡng phổ biến là 0,5.

Đưa ra dự đoán

Sử dụng kiến ​​thức của chúng tôi về các hàm sigmoid và ranh giới quyết định, bây giờ chúng tôi có thể viết một hàm dự đoán. Một hàm dự đoán trong hồi quy logistic trả về xác suất quan sát của chúng ta là dương, Đúng hoặc “Có”.

Ví dụ: nếu ngưỡng của chúng tôi là 0,5 và chức năng dự đoán của chúng tôi trả về .7, chúng tôi sẽ phân loại quan sát là một người sẽ bị đau tim lần thứ hai với 70% khả năng. Nếu dự đoán của chúng tôi là .2, chúng tôi sẽ phân loại quan sát là một người sẽ bị đau tim lần thứ hai với 20% cơ hội. Khi xác suất tiến gần đến 1, mô hình của chúng tôi càng tin tưởng rằng quan sát là tích cực.

Bây giờ, bạn có thể hiểu cách hàm sigmoid được sử dụng trong hồi quy logistic và một số phép toán đằng sau nó. Nếu bạn có một số nhận xét hoặc phản hồi, xin vui lòng bình luận. Cảm ơn bạn đã đọc!

Các mô hình phân loại đều tìm cách xác định đường biên phân chia tốt nhất các nhóm giữa liệu. Trong hồi qui Logistic chúng ta cũng tìm kiếm một đường biên phân chia như vậy để giải quyết bài toán phân loại nhị phân giữa hai nhóm 0 và 1.

Trong hồi qui tuyến tính chúng ta dựạ vào một hàm hồi qui giả thuyết \[h_{\mathbf{w}}[\mathbf{x}] = \mathbf{w}^{\intercal}\mathbf{x}\] để dự báo biến mục tiêu liên tục \[y\]. Vì giá trị của \[y\] có thể vượt ngoài khoảng \[[0, 1]\] nên trong hồi qui Logistic cần một hàm số có tác dụng chiếu giá trị dự báo lên không gian xác suất nằm trong khoảng \[[0, 1]\] và đồng thời tạo ra tính phi tuyến cho phương trình hồi qui nhằm giúp nó có đường biên phân chia giữa hai nhóm tốt hơn. Đó chính là hàm Sigmoid hoặc hàm Logistic mà chúng ta sẽ tìm hiểu bên dưới.

Mô hình hồi qui Logistic là sự tiếp nối ý tưởng của hồi qui tuyến tính vào các bài toán phân loại. Từ đầu ra của hàm tuyến tính chúng ta đưa vào hàm Sigmoid để tìm ra phân phối xác suất của dữ liệu. Lưu ý rằng hàm Sigmoid chỉ được sử dụng trong bài toán phân loại nhị phân. Đối với bài toán phân loại nhiều hơn hai nhãn, hàm Softmax [sẽ được tìm hiểu ở những chương sau] là một dạng hàm tổng quát của Sigmoid sẽ được sử dụng. Hàm Sigmoid thực chất là một hàm biến đổi phi tuyến dựa trên công thức:

\[\sigma{[x]} = \frac{1}{1+e^{-x}}\]

Bên dưới là khảo sát sơ bộ của hàm Sigmoid.

import numpy as np x = np.linspace[-10, 10, 200] def _sigmoid[x]: s = 1/[1+np.exp[-x]] return s # Xác suất dự báo từ hàm sigmoid y = [_sigmoid[xi] for xi in x]

Vẽ đồ thị hàm Sigmoid:

import matplotlib.pyplot as plt # Visualize hàm sigmoid plt.figure[figsize = [16, 8]] plt.plot[x, y, marker = 'o'] plt.axvline[0] plt.text[0.0, 0.3, "x=0.5"] plt.axhline[1, color="green"] plt.text[-7.5, 0.9, "y=1.0"] plt.axhline[0, color="red"] plt.text[-7.5, 0.05, "y=0.0"] plt.xlabel['x'] plt.ylabel['y'] plt.title['Sigmoid Function'] plt.show[]

Ta nhận thấy: Hàm Sigmoid có hình dạng là một đường cong chữ Sđơn điệu tăng. Chính vì thế nên nó còn có tên một tên gọi khác là hàm chữ S. Một vài tài liệu còn gọi nó là hàm Logistic đại diện cho hồi qui Logistic.

Ngoài ra ta dễ dàng chứng minh giá trị của hàm Sigmoid nằm trong khoảng \[[0, 1]\]. Thật vậy:

\[\lim_{x \rightarrow +\infty} \sigma[x] = \lim_{x \rightarrow +\infty} \frac{1}{1+e^{-x}}=1\]

\[\lim_{x \rightarrow -\infty} \sigma[x] = \lim_{x \rightarrow -\infty} \frac{1}{1+e^{-x}}=0\]

Do đó hàm Sigmoid rất phù hợp để áp dụng vào dự báo xác suất ở các bài toán phân loại.

Quay trở lại với bài toán hồi qui tuyến tính. Với 2 biến đầu vào dự báo là \[\mathbf{x} = [1, x_1, x_2]\] ta thu được một hàm hồi qui:

\[\hat{y} = g[x] = w_0 + w_1 x_1 + w_2 x_2 = \mathbf{w}^{\intercal}\mathbf{x}\]

Ở đây \[\mathbf{w} = [w_0, w_1, w_2]\] là véc tơ dòng của các hệ số hồi qui.

Chuyển tiếp giá trị này qua hàm Sigmoid để dự báo xác suất và tạo tính phi tuyến cho mô hình hồi qui:

\[P[y=1 | \mathbf{x}; \mathbf{w}] = \sigma[\mathbf{w}^\intercal\mathbf{x}] = \frac{1}{1+e^{-\mathbf{w}^\intercal\mathbf{x}}}\]

Ở công thức trên thì \[P[y=1 | \mathbf{x}; \mathbf{w}]\] chính là xác suất có điều kiện để xảy ra sự kiện \[y=1\] tương ứng với đầu vào \[\mathbf{x}\], và trọng số \[\mathbf{w}\]. Lưu ý chúng ta không thể viết xác suất có điều kiện theo \[\mathbf{w}\]\[P[y=1 | \mathbf{x}, \mathbf{w}]\]\[\mathbf{w}\] không phải là biến ngẫu nhiên.

Trong bài toán phân loại nhị phân chúng ta sẽ lựa chọn một ngưỡng threshold về xác suất để đưa ra dự báo nhãn cho một quan sát. Giả định ta chọn ngưỡng xác suất là 0.5. Khi đó dự báo nhãn sẽ là:

\[\begin{split} \left\{ \begin{matrix} 0 \text{ if } P[y=1|\mathbf{x}; \mathbf{w}] \leq 0.5 \\ 1 \text{ if } P[y=1|\mathbf{x}; \mathbf{w}] > 0.5 \end{matrix} \right.\end{split}\]

  • Trong trường hợp \[y=1\]:

\[\begin{split}\begin{eqnarray}h_{\mathbf{w}}[\mathbf{x}] & > & 0.5 \\ & \leftrightarrow & \frac{1}{1+e^{-\mathbf{w}^{\intercal}\mathbf{x}}} > 0.5 \\ & \leftrightarrow & e^{-\mathbf{w}^{\intercal}\mathbf{x}} < 1 \\ & \leftrightarrow & \mathbf{w}^{\intercal}\mathbf{x} > 0 \\ \end{eqnarray}\end{split}\]

  • Trong trường hợp \[y=0\]:

\[\begin{split}\begin{eqnarray}h_{\mathbf{w}}[\mathbf{x}] & \leq & 0.5 \\ & \leftrightarrow & \frac{1}{1+e^{-\mathbf{w}^{\intercal}\mathbf{x}}} \leq 0.5 \\ & \leftrightarrow & e^{-\mathbf{w}^{\intercal}\mathbf{x}} \geq 1 \\ & \leftrightarrow & \mathbf{w}^{\intercal}\mathbf{x} \leq 0 \\ \end{eqnarray}\end{split}\]

Thêm hình vẽ về đường biên phân chia

Như vậy ta có thể nhận ra những điểm thuộc về nhãn 1 sẽ nằm bên phải đường biên phân chia \[\mathbf{w}^{\intercal}\mathbf{x}\] trong khi những điểm thuộc về nhãn 0 sẽ nằm bên trái. Đồng thời đường biên phân chia hai nhãn 0 và 1 cũng là một phương trình tuyến tính.

Odd ratio là một chỉ số đo lường tỷ lệ xác suất giữa trường hợp tích cực và tiêu cực được dự báo từ mô hình hồi qui logistic. Một dự đoán có tỷ lệ Odd ratio càng lớn thì khả năng rơi vào nhãn tích cực sẽ càng cao. Nếu Odd ratio > 1 thì mẫu được dự báo có xác suất thuộc nhãn tích cực là lớn hơn so với tiêu cực và ngược lại.

\[\text{Odd Ratio} = \frac{P[y=1|\mathbf{x}; \mathbf{w}]}{P[y=0|\mathbf{x}; \mathbf{w}]} = \frac{P[y=1|\mathbf{x}; \mathbf{w}]}{1-P[y=1|\mathbf{x}; \mathbf{w}]} = e^{-\mathbf{w}^{\intercal}\mathbf{x}}\]

Ngoài ra để nhận biết xác suất tích cực hay tiêu cực lớn hơn, ta thường căn cứ vào log Odd Ratio bằng cách so sánh \[\mathbf{w}^{\intercal}\mathbf{x}\] với 0 để đưa ra kết luận.

Chúng ta có thể xem hàm Sigmoid là một hàm phi tuyến giúp biến đổi giá trị đầu ra. Chúng ta sẽ còn gặp nhiều dạng hàm biến đổi phi tuyến khác trong các mô hình Softmax, MLP, SVM, …

Dưới góc nhìn của graphic model thì mô hình Logistic regression có dạng như sau:

Đồ thị trên sẽ bao gồm hai bước:

  • Bước 1: Kết hợp tuyến tính.

Mỗi một node [hình tròn] đại diễn cho 1 biến đầu vào. Các cạnh là hình mũi tên có hướng thể hiện hướng tính toán của đồ thị. Đầu vào sẽ là node ở gốc mũi tên và đầu ra là node ở ngọn mũi tên? Giá trị này sẽ được điều tiết bằng cách nhân với hệ số \[w_i\]. Cuối cùng ta sẽ kết hợp tuyến tính các nodes đầu vào để tính ra đầu ra \[\hat{y}\].

Về căn bản bước này tương đương với quá trình dự báo trong hồi qui tuyến tính.

  • Bước 2: Biểu diễn hàm Sigmoid.

Giá trị \[\hat{y}\] lại tiếp tục được đưa qua hàm \[\sigma\] để tính ra xác suất \[P[y=1]\] ở output.

Chúng ta còn nhớ về phân phối Bernoulli chứ? Giả sử một sự kiện vỡ nợ xảy ra với xác suất là \[p\]. Phân phối Bernoulli cho chúng ta biết xác suất xảy ra của sự kiện khi thực hiện một phép thử như sau:

\[\begin{split}P[X=k]={\begin{cases}p&{\text{if }}k=1,\\[6pt]1-p&{\text{if }}k=0.\end{cases}}\end{split}\]

Tìm hiểu thêm về phân phối Bernoulli tại Chương 2, phân phối xác suất Bernoulli.

Như vậy xác suất trong bài toán phân loại nhị phân tuân theo phân phối Bernoulli. Chúng ta còn có thể khái quát hoá xác suất này qua phương trình tổng quát cho cả hai trường hợp \[0\]\[1\] như sau:

\[P[X=k] = p^{k}[1-p]^{1-k} \tag{1}\]

Thật vậy, trường hợp \[k=1\] thì:

\[P[X=1]=p^{1}[1-p]^{0} = p\]

và tương tự với trường hợp \[k=0\] ta cũng có:

\[P[X=0]=p^{0}[1-p]^{1} = 1-p\]

Xác suất xảy ra của điểm \[\mathbf{x}_i\] theo hàm Sigmoid:

\[\begin{split} \left\{ \begin{matrix} P[y=1| \mathbf{x}_i] &=& \sigma[\mathbf{w}^\intercal\mathbf{x}_i]~~~ \\ P[y=0| \mathbf{x}_i] &=& 1-\sigma[\mathbf{w}^\intercal\mathbf{x}_i]~~~ \end{matrix} \right.\end{split}\]

Như vậy trong mô hình hồi qui Logistic, xác suất tổng quát cho một mẫu cho cả hai trường hợp \[\{0, 1 \}\] sẽ là:

\[P[y_i|\mathbf{x}_i, \mathbf{w}] = P[y=1]^{y_i}[1-P[y=1]]^{[1-y_i]}\]

Ở trên là xác suất tại một điểm dữ liệu. Giả sử các quan sát trong bộ dữ liệu của chúng ta là độc lập. Khi đó xác suất đồng thời của toàn bộ các quan sát trong bộ dữ liệu sẽ bằng tích các xác suất tại từng điểm dữ liệu và bằng:

\[P[\mathbf{y}|\mathbf{X}; \mathbf{w}] = \prod_{i=1}^{n} P[y_i|\mathbf{x}_i; \mathbf{w}] \tag{2}\]

Vế phải của biểu thức \[[2]\] chính là một hàm hợp lý [Likelihood Function] đo lường mức độ hợp lý [goodness of fit] của mô hình thống kê đối với dữ liệu.

Chúng ta kỳ vọng giá trị của hàm hợp lý phải lớn. Điều đó đồng nghĩa với các trường hợp tích cực phải có xác suất càng gần 1 và tiêu cực có xác suất gần bằng 0. Do đó mục tiêu của chúng ta là tìm \[\mathbf{w}\] sao cho biểu thức [2] là lớn nhất.

Như vậy quá trình tìm nghiệm \[\mathbf{w}\] thực chất là giải bài toán tối ưu hàm hợp lý [Maximum Likelihood Function]. Phương pháp tìm nghiệm \[\mathbf{w}\] dựa trên hàm hợp lý còn được gọi là ước lượng hợp lý tối đa [Maximum Likelihood Estimation].

Do việc tối ưu trực tiếp \[[2]\] là khó khăn nên chúng ta sẽ logarith để chuyển tích sang tổng để tối ưu nhẹ nhàng hơn. Khi đó qui về bài toán tối ưu hàm Log Likelihood như sau:

\[\begin{split}\begin{eqnarray}\log P[y_i|\mathbf{x}_i; \mathbf{w}] & = & \log [P[y=1]^{y_i}[1-P[y=1]]^{[1-y_i]}] \\ & = & y_i\log P[y=1] + [1-y_i]\log [1-P[y=1]]\end{eqnarray}\end{split}\]

Việc tìm giá trị cực đại của phương trình [2] tương ứng với bài toán tối ưu:

\[\begin{split}\begin{eqnarray}\hat{\mathbf{w}} & = & \arg \max_{\mathbf{w}} ~~~ \log[\prod_{i=1}^{n} P[y_i|\mathbf{x}_i; \mathbf{w}]] \\ & = & \arg \max_{\mathbf{w}} \sum_{i=1}^{n} y_i\log[P[y_i=1]] + [1-y_i]\log[1-P[y_i=1]] \\ & = & \arg \min_{\mathbf{w}} \sum_{i=1}^{n} -[y_i\log[\hat{y_i}] + [1-y_i]\log[1-\hat{y}_i]] \end{eqnarray} \end{split}\]

Ở đây \[\hat{y}_i = P[y_i=1]\] là ước lượng xác suất tại điểm \[\mathbf{x}_i\]. Từ dòng 2 chuyển sang dòng 3 là vì chúng ta đổi dấu. Khi đó hàm mất mát [Loss function] sẽ có dạng:

\[\mathcal{L}[\mathbf{y}, \hat{\mathbf{y}}] = \sum_{i=1}^{n} -[y_i\log[\hat{y_i}] + [1-y_i]\log[1-\hat{y}_i]]\]

Hàm mất mát trên còn được gọi là hàm Cross Entropy. Nó là một độ đo [metric] đo lường mức độ tương quan giữa phân phối xác suất dự báo \[[\hat{y}_i, 1-\hat{y}_i]\] và phân phối xác suất thực tế \[[y_i, 1-y_i]\]. Giá trị của Cross Entropy sẽ càng nhỏ nếu hai phân phối xác suất càng sát nhau, tức là giá trị dự báo giống với thực tế nhất.

Để minh chứng cho nhận định trên chúng ta sẽ mô phỏng hàm Cross Entropy cho các trường hợp \[y=0, 1\]\[0.5\]. Cho \[\hat{y}\] di chuyển liên tục trong khoảng từ \[[0, 1]\] và tính giá trị của Cross Entropy. Sau đó biểu diễn trên đồ thị để tìm cực trị.

# Tính cross entropy theo yhat và y def _cross_entropy[yhat, y]: return -[y*np.log[yhat]+[1-y]*np.log[[1-yhat]]] # Khởi tạo gía trị yhat từ 0 đến 1 yhat = np.linspace[0.001, 0.999, 200] # Hàm visualize cross entropy def _plot_crs[yhat, y]: cross_entropy = _cross_entropy[yhat, y] plt.figure[figsize = [8, 4]] plt.plot[yhat, cross_entropy] plt.xlabel['yhat'] plt.ylabel['cross entropy'] plt.title['Cross Entropy With y={}'.format[y]] plt.show[] _plot_crs[yhat, y=1]

Biểu đồ chung cho cả 3 trường hợp

crs1 = _cross_entropy[yhat, y=0.5] crs2 = _cross_entropy[yhat, y=0.7] crs3 = _cross_entropy[yhat, y=0.1] plt.figure[figsize = [12, 6]] plt.plot[yhat, crs1, label='y=0.5'] plt.plot[yhat, crs2, label='y=0.7'] plt.plot[yhat, crs3, label='y=0.1'] plt.xlabel['yhat'] plt.ylabel['cross entropy'] plt.title['Cross Entropy'] plt.legend[loc='best'] plt.show[]

Như vậy ta nhận thấy giá trị cực tiểu của hàm Cross Entropy luôn đạt được tại \[y=\hat{y}\]

Để chứng minh cho nhận định giá trị của Cross Entropy đạt cực tiểu tại \[y = \hat{y}\] không quá khó. Ở phần này tôi sẽ đưa ra một chứng minh trực quan cho bạn nào yêu toán bằng phương pháp Lagrange. Đối với những bạn không thực sự quan tâm tới toán có thể xem mục 3.3 về thực hành.

Để giải bài toán cực trị của hàm Cross Entropy thì chúng ta phải làm quen với phương pháp nhân tử Lagrange trong tối ưu. Đây là một phương pháp giúp tìm kiếm cực trị địa phương cho các hàm mục tiêu \[f[\mathbf{x}]\] đi kèm với điều kiện ràng buộc là những đẳng thức hoặc bất đẳng thức đối với \[\mathbf{x}\]. Ví dụ về bài toán tối ưu với điều kiện ràng buộc:

\[\mathbf{x} = \arg \min_{\mathbf{x}} f[\mathbf{x}]\]

Thoả mãn: \[g[\mathbf{x}] = 0\]

Trong đó \[g[\mathbf{x}] = 0\] được gọi là điều kiện ràng buộc. Một bài toán có thể có một hoặc nhiều điều kiện ràng buộc. Chúng ta gọi chung những điều kiện mà \[\mathbf{x}\] cần thoả mãn là hệ điều kiện ràng buộc.

Ý tưởng của phương pháp nhân tử Lagrange là chuyển từ bài toán ràng buộc sang bài toán không ràng buộc và sử dụng khảo sát đạo hàm bậc nhất hàm Lagrange có dạng:

\[\mathcal{L}[\lambda, x_1, x_2] = f[\mathbf{x}] + \lambda g[\mathbf{x}]\]

với \[\lambda \geq 0\].

Thông qua tính đạo hàm bậc nhất theo \[\lambda\] thì tại cực trị các điều kiện ràng buộc sẽ phải được thoả mãn hoặc \[\lambda = 0\]. Do đó chúng ta không cần thêm điều kiện ràng buộc. Bên dưới là ứng dụng của phương pháp nhân tử Lagrange để giải bài toán tối ưu.

Giả sử \[\mathbf{y} = [y_1, ..., y_C]\] là phân phối xác suất ground truth đã biết và \[\hat{\mathbf{y}} = [\hat{y}_1, \dots , \hat{y}_C]\] là phân phối xác suất dự báo thỏa mãn điều kiện ràng buộc \[\sum_{i=1}^{C} \hat{y}_i = 1\]. Tìm nghiệm tối ưu của hàm Cross Entropy:

\[f[\mathbf{\hat{y}}|\mathbf{y}] = \sum_{i=1}^C {-y_i\log[\hat{y_i}]}\]

Ở đây ta viết \[f[\mathbf{\hat{y}} | \mathbf{y}]\] có nghĩa là hàm \[f[.]\] là một hàm phụ thuộc vào \[\mathbf{\hat{y}}\] trong điều kiện đã biết trước \[\mathbf{y}\]. Mục tiêu của ta là đi tìm phân phối \[\mathbf{\hat{y}}\] để hàm mục tiêu \[f[.]\] là lớn nhất.

Ta có hàm Lagrange:

\[\begin{split}\begin{eqnarray}\mathcal{L}[\lambda, \mathbf{\hat{y}}] & = & f[\mathbf{\hat{y}}|\mathbf{y}] + \lambda [1-\sum_{i=1}^{C} \hat{y}_i] \\ & = & \sum_{i=1}^C {-y_i\log[\hat{y_i}]} + \lambda [1-\sum_{i=1}^{C} \hat{y}_i] \end{eqnarray}\end{split}\]

Hệ phương trình đạo hàm bậc nhất theo các biến \[\hat{y}_i, \lambda\]:

\[\begin{split} \left\{ \begin{matrix} \nabla_{\hat{y}_i} \mathcal{L}[\lambda, \mathbf{\hat{y}}] &=& - {\frac{y_i}{\hat{y_i}}} - \lambda~~~ &, \forall i=\overline{1, C} ~~~ \\ \nabla_{\lambda} \mathcal{L}[\lambda, \mathbf{\hat{y}}] &=& 1-\sum_{i=1}^{C} \hat{y}_i & ~~~ \end{matrix} \right.\end{split}\]

Điều kiện cần của cực trị là các phương trình đạo hàm bậc nhất bằng 0. Từ đó ta suy ra nghiệm \[y_i = \hat{y}_i, \forall i=\overline{1, C}\]. Tức là phân phối xác suất dự báo \[\hat{\mathbf{y}}\] phải bằng ground truth \[\mathbf{y}\]. Đây chính là lý do vì sao chúng ta coi Cross Entropy là một độ đo đánh giá mức độ tương đồng giữa phân phối xác suất của giá trị dự báo và ground truth.

Phương pháp hạ dốc [gradient descent] là một kỹ thuật quan trọng trong học máy và đặc biệt là học sâu, giúp ta tìm cực trị địa phương của mọi hàm số dựa trên gradient. Trên thực tế việc tìm ra được lời giải chính xác cho một số dạng hàm mất mát là không dễ dàng, đặc biệt là những hàm số có đạo hàm quá phức tạp và những hàm không lồi. Do đó phương pháp hạ dốc là một trong những lựa chọn tốt nhất để tiến dần đến cực trị cho những bài toán như vậy. Tuy nhiên hạn chế của phương pháp này đó là cực trị tìm được chỉ là nghiệm gần đúng và không đảm bảo chắc chắn là cực trị toàn cục.

Để hiểu về phương pháp hạ dốc là gì chúng ta sẽ cùng phân tích một ví dụ đơn giản đó là bài toán tìm cực trị của hàm \[f[x] = x^2-2x+5\] .

Hàm này có đạo hàm là \[f'[x] = 2x-2\]

Không khó để phát hiện ra \[f'[x]\] có nghiệm \[x=1\] và là hàm lồi tại nghiệm đó nên nó có cực tiểu là \[[x^*, y^*]=[1, 5]\].

Tiếp theo ta sẽ vẽ đồ thị của hàm số này.

import numpy as np # Khởi tạo x x = np.arange[-9, 11, 0.1] def _f[x]: return x**2-2*x+5 # Tính f[x] y = _f[x] # Đạo hàm f'[x] y_grad = 2*x-2 # Lấy ra các điểm ngẫu nhiên x0, y0 = x[10], y[10] x1, y1 = x[-20], y[-20] # Cực tiểu của hàm số x_star=1 y_star=4

Vẽ đồ thị

import matplotlib.pyplot as plt import matplotlib matplotlib.rcParams.update[{'font.size': 22}] plt.figure[figsize=[8, 8]] plt.plot[x, y] plt.ylabel["f[x]"] plt.plot[x0, y0, marker="o", markersize=15, color="red"] plt.text[x0, y0, "[x0, y0]"] plt.plot[x1, y1, marker="o", markersize=15, color="blue"] plt.text[x1, y1, "[x1, y1]"] plt.text[x_star, y_star, "[x*, y*]"] plt.plot[x_star, y_star, marker="o", markersize=15, color="green"] plt.plot[x, y_grad, linestyle='-'] plt.axhline[0] plt.axvline[x0, linestyle="--"] plt.axvline[x1, linestyle="--"] plt.text[x0, 2*x0-2, "f'[x]"] plt.xlabel["x"] plt.title["f[x]"] plt.show[]

Từ đồ thị ta thấy điểm \[[x_0, y_0]\] nằm ở bên trái điểm cực tiểu thì giá trị đạo hàm là âm. Để di chuyển tới \[[x^*, y^*]\] thì ta phải tăng \[x_0\].

Tương tự tại điểm \[[x_1, y_1]\] nằm bên phải của điểm cực tiểu thì giá trị của đạo hàm sẽ dương. Để đi tới \[[x^*, y^*]\] thì cần giảm \[x_1\].

Như vậy trong cả hai trường hợp ta đều cần di chuyển ngược chiều đạo hàm để tiến gần hơn tới cực trị. Ta có thể cập nhật dần dần nghiệm sau mỗi bước bằng một hệ số học tập [learning rate] \[\alpha\] có dạng như sau:

\[x_{new} := x_0-\alpha \nabla_{x} f[x_0]\]

Trong đó ký hiệu a:=b trong lập trình có nghĩa là chúng ta gán giá trị a bằng giá trị b. Còn nếu ta viết a=b có nghĩa rằng đây là một khẳng định thực tế rằng giá trị của a bằng với b.

Như vậy tại mọi vị trí, chỉ cần di chuyển ngược chiều của đạo hàm tại một điểm một khoảng rất nhỏ thì có khả năng rất cao là ta sẽ thu được một giá trị nhỏ nhơn.

Thế nhưng có khi nào di chuyển ngược chiều đạo hàm mà khiến giá trị \[f[x]\] lớn hơn không? Đó là khi ta đã vượt dốc, chẳng hạn như khi đã đến rất gần điểm cực trị \[[x^*, y^*]\] nhưng hệ số học tập quá lớn làm cho khoảng thay đổi ở bước tiếp theo cũng lớn theo và là nguyên nhân khiến nghiệm cập nhật vượt quá điểm cực trị. Trường hợp này gọi là nhảy dốc [Step Over].

Hình 2: Hình bên trái là chiến lược học tập được thiết lập với hệ số học tập phù hợp. Hình bên phải xảy ra hiện tượng nhảy dốc. Sau mỗi lượt cập nhật nghiệm thì các điểm có xu hướng nhảy qua lại hai bên xung quanh cực trị địa phương thay vì hội tụ từ từ.

Để hạn chế hiện tượng nhảy dốc thì ta cần lựa chọn \[\alpha\] rất nhỏ từ \[0.001\] tới \[0.005\] và áp dụng những phương pháp tối ưu [optimizer] khác nhau để kiểm soát quá trình huấn luyện. Một số phương pháp tối ưu phổ biến là Adam, Ada, RMProp, ... và hiện chúng đều đã có sẵn trong các framework Deep Learning.

Để tìm ra nghiệm của hồi qui Logistic thì chúng ta sẽ thực hiện cập nhật nghiệm trên từng điểm dữ liệu \[[\mathbf{x}_i, y_i]\]. Các điểm được lựa chọn một cách ngẫu nhiên ở mỗi lượt cập nhật. Phương pháp cập nhật gradient descent như vậy còn được gọi là Stochastic Gradient Descent.

\[\mathbf{w} := \mathbf{w} - \alpha ~ \frac{\delta \mathcal{L}[\mathbf{w}; \mathbf{x}_i, y_i]}{\delta \mathbf{w}} \tag{3}\]

Mặt khác:

\[\mathcal{L}[\mathbf{w}; \mathbf{x}_i, y_i] = -[y_i \log {\hat{y}}_i + [1-y_i] \log [1 - \hat{y}_i]]\]

Ngoài ra ta dễ dàng chứng minh được:

\[\begin{split}\begin{eqnarray}\frac{\delta \mathcal{L}[\mathbf{w}; \mathbf{x}_i, y_i]}{\delta \mathbf{w}} & = & -[y_i \frac{\delta \log {\hat{y}}_i}{\delta \mathbf{w}} + [1-y_i] \frac{\delta \log {[1-\hat{y}}_i]}{\delta \mathbf{w}}] \\ & = & -[y_i \frac{\delta \log \hat{y}_i}{\delta \hat{y}_i}\frac{\delta \hat{y}_i}{\delta \mathbf{w}} + [1-y_i] \frac{\delta \log {[1-\hat{y}}_i]}{\delta \hat{y}_i} \frac{\delta \hat{y}_i}{\delta \mathbf{w}}] \\ & = & -[y_i \frac{1}{\hat{y}_i} - [1-y_i] \frac{1}{[1-\hat{y}_i]}] \frac{\delta \hat{y}_i}{\delta \mathbf{w}} \\ & = & - [\frac{y_i-\hat{y}_i}{\hat{y}_i[1-\hat{y}_i]}] \frac{\delta \hat{y}_i}{\delta \mathbf{w}} \tag{4} \end{eqnarray}\end{split}\]

Dòng 1 suy ra dòng 2 là vì ta sử dụng công thức chain rule trong vi phân. Đặt \[z = e^{-\mathbf{w}^{\intercal} \mathbf{x}}\]. Tiếp tục khai triển:

\[\frac{\delta \hat{y}_i}{\delta \mathbf{w}} = \frac{\delta \frac{1}{1+z_i}}{\delta \mathbf{w}} = \frac{\delta \frac{1}{1+z_i}}{\delta z_i} \frac{\delta z_i}{\delta \mathbf{w}} = \frac{-1}{[1+z_i]^2} [-z_i\mathbf{x}_i] = -\mathbf{x}\frac{z_i}{[1+z_i]^2} = \mathbf{x}_i\hat{y}_i[1-\hat{y}_i]\]

Từ đó thế vào \[[4]\] ta được:

\[\frac{\delta \mathcal{L}[\mathbf{w}; \mathbf{x}_i, y_i]}{\delta \mathbf{w}} = \mathbf{x}_i [y_i-\hat{y}_i]\]

Như vậy công thức \[[3]\] cập nhật nghiệm theo gradient descent sẽ được rút ngắn xuống thành:

\[\mathbf{w} := \mathbf{w} - \alpha ~ \mathbf{x}_i[y_i-\hat{y}_i]\]

Quá trình cập nhật nghiệm theo phương pháp Stochastic Gradient Descent được thể hiện tại từng điểm như sau:

Loop { for i=1 to n, { for j=1 to m { w_j:=w_j - \alpha ~ \mathbf{x}_i[y_i-\hat{y}_i] } } } `

Trong đó \[n\] là số quan sát và \[m\] là số lượng trọng số mô hình [bao gồm cả hệ số tự do].

Một lưu ý khá quan trọng đó là các \[w_i\] được cập nhật là đồng thời. Tức là ở cùng một quan sát, sau khi đã cập nhật \[w_0\] thì chúng ta sẽ không lấy giá trị mới của \[w_0\] để sử dụng vào cập nhật \[w_1\].

Để xây dựng mô hình hồi qui Logistic trên sklearn chúng ta sử dụng module sklearn.linear_model.LogisticRegression.

Tiếp theo chúng ta sẽ cùng xây dựng một pipeline đơn giản cho bài toán phân loại nợ xấu sử dụng mô hình hồi qui Logistic. Dữ liệu đầu vào là hmeq. Bộ dữ liệu HMEQ bao gồm các đặc trưng thông tin về nợ của 5960 khoản vay mua nhà. Đây là những khoản vay mua nhà mà người vay sử dụng vốn chủ sở hữu làm tài sản thế chấp. Tập dữ liệu bao gồm những trường sau:

  • BAD: 1 = Hồ sơ vay là vi phạm hoặc mất khả năng trả nợ; 0 = hồ sơ vay đã và đang trả nợ.

  • LOAN: Số tiền yêu cầu cho vay.

  • MORTDUE: Số tiền đến hạn của khoản thế chấp hiện có.

  • VALUE: Giá trị tài sản hiện tại.

  • REASON: DebtCon = nợ hợp nhất; HomeImp = cải thiện nhà.

  • JOB: Thể loại nghề nghiệp.

  • YOJ: Số năm kinh nghiệm trong nghề nghiệp hiện tại.

  • DEROG: Số lượng báo cáo không tín nhiệm.

  • DELINQ: Số hạn mức tín dụng quá hạn.

  • CLAGE: Tuổi của hạn mức tín dụng cũ nhất tính theo tháng.

  • NINQ: Số câu hỏi tín dụng gần đây.

  • CLNO: Số lượng hạn mức tín dụng.

  • DEBTINC: Tỷ lệ nợ trên thu nhập.

Mục tiêu của chúng ta sẽ là dựa vào các biến đầu vào để phân loại một hồ sơ có khả năng nợ xấu hay không.

Như thông lệ, qui trình xây dựng mô hình sẽ bao gồm các bước theo tuần tự:

  1. Khảo sát dữ liệu.

  2. Phân chia tập huấn luyện/kiểm tra.

  3. Xử lý missing và outliers.

  4. Lựa chọn mô hình.

  5. Huấn luyện mô hình.

  6. Đánh giá mô hình.

Bên dưới chúng ta sẽ tuần tự thực hành những bước này:

import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt from sklearn.compose import ColumnTransformer from sklearn.naive_bayes import GaussianNB from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.svm import LinearSVC from sklearn.neural_network import MLPClassifier from sklearn.ensemble import GradientBoostingClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.impute import KNNImputer, SimpleImputer from sklearn.metrics import accuracy_score from sklearn.model_selection import train_test_split from sklearn.pipeline import Pipeline from sklearn.model_selection import cross_val_score from sklearn.metrics import fbeta_score from sklearn.metrics import make_scorer from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold from sklearn.preprocessing import OneHotEncoder, MinMaxScaler

Đọc dữ liệu từ pandas

import pandas as pd df = pd.read_csv['//www.creditriskanalytics.net/uploads/1/9/5/1/19511601/hmeq.csv', header = 0, sep = ','] df.describe[]

BAD LOAN MORTDUE VALUE YOJ DEROG DELINQ CLAGE NINQ CLNO DEBTINC count mean std min 25% 50% 75% max
5960.000000 5960.000000 5442.000000 5848.000000 5445.000000 5252.000000 5380.000000 5652.000000 5450.000000 5738.000000 4693.000000
0.199497 18607.969799 73760.817200 101776.048741 8.922268 0.254570 0.449442 179.766275 1.186055 21.296096 33.779915
0.399656 11207.480417 44457.609458 57385.775334 7.573982 0.846047 1.127266 85.810092 1.728675 10.138933 8.601746
0.000000 1100.000000 2063.000000 8000.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.524499
0.000000 11100.000000 46276.000000 66075.500000 3.000000 0.000000 0.000000 115.116702 0.000000 15.000000 29.140031
0.000000 16300.000000 65019.000000 89235.500000 7.000000 0.000000 0.000000 173.466667 1.000000 20.000000 34.818262
0.000000 23300.000000 91488.000000 119824.250000 13.000000 0.000000 0.000000 231.562278 2.000000 26.000000 39.003141
1.000000 89900.000000 399550.000000 855909.000000 41.000000 10.000000 15.000000 1168.233561 17.000000 71.000000 203.312149

Vẽ biểu đồ khảo sát phân phối của dữ liệu.

import seaborn as sns import warnings warnings.simplefilter[action='ignore', category=FutureWarning] numeric_cols = df.select_dtypes[include=['float','int']].columns def _plot_numeric_classes[df, col, bins=10, hist=True, kde=True]: sns.distplot[df[col], bins = bins, hist = hist, kde = kde] def _distribution_numeric[df, numeric_cols, row=3, col=3, figsize=[20, 15], bins = 10]: ''' numeric_cols: list các tên cột row: số lượng dòng trong lưới đồ thị col: số lượng cột trong lưới đồ thị figsize: kích thước biểu đồ bins: số lượng bins phân chia trong biểu đồ distribution ''' print['number of numeric field: ', len[numeric_cols]] assert row*[col-1]

Chủ Đề