[Machine Learning] Logistic Regression và bài toán phân loại cảm xúc âm nhạc

I – Lời nói đầu

Ở mỗi thời điểm khác nhau con người có những cảm xúc khác nhau như buồn, vui, hưng phấn,.. hay một cảm xúc đặc biệt hơn cả là “thất tình”. Khi đau khổ con người thường tìm đến những thứ có thể khuếch đại cảm xúc của họ như các trò chơi điện tử hoặc nghe nhạc hơn là tìm những thứ có thể xoa dịu nỗi đau của họ. Và trong đó âm nhạc là thứ họ hướng đến nhiều nhất.

Tìm đến âm nhạc có lẽ là một trong những phương pháp trị liệu hiệu quả nhất cho cảm xúc của bạn trong những trường hợp như vậy. Nhưng có bao giờ bạn cảm thấy phát ngán với những bài nhạc sau khi đã nghe đi nghe lại nhiều lần, và không biết liệu còn bài hát nào có thể mang lại cảm giác hưng phấn như lúc mới nghe những bài ấy?

Với những bài hát ta chưa từng nghe, để biết bài hát này có hợp với gu âm nhạc của chúng ta không thì ta phải nghe qua nó ít nhất là một lần, việc này thường sẽ làm chúng ta mất khoảng 3-5 phút. Đối với nhiều người, họ thường không thích làm những việc như vậy, thay vì nghe trọn vẹn một bài hát thì họ sẽ dùng một vài biện pháp để đốt cháy quá trình như tua, chỉnh nhanh speed, … Những hành động đó có thể vô tình bỏ qua một số đoạn hay nhất của bài hát.

Vậy với ý tưởng để giải quyết những vấn đề trên là ta tìm cách xác định mood của âm nhạc hay cảm xúc của bài hát chỉ bằng những thứ được nhìn lướt qua (tiêu đề, lyrics, nhạc sĩ…). Machine learning có thể làm điều này không ?

Câu trả lời là có, bài viết này sẽ đề cập tới cách giải quyết vấn đề này với thuật toán Logistic Regression.

Trước khi đến với cách thực hiện bài toán này với Python ta cùng đi vào phần lý thuyết để hiểu rõ cách thức mà Logistic Regression hoạt động. Nếu các bạn chỉ quan tâm đến code thực hiện bài toán này hãy nhảy đến phần 5 của bài viết này nhé này nhé. Notebook Colab sẽ được kèm link ở cuối bài viết để các bạn có thể thực hành ngay!

II. Logistic Regression

1.1.Logistic Regression – Giới Thiệu

Hình 1 Logistic Model (nguồn:http://dataaspirant.com/2017/03/02/how-logistic-regression-model-works/)

Trong bài này chúng ta sẽ tìm hiểu thuật toán loại Classification đầu tiên, đó là Logistic Regression.
Mặc dù tên gọi chứa từ “Regression” nhưng đây là thuật toán thuộc loại Classification. Thực tế cho thấy nó là một trong những thuật toán Machine Learning được sử dụng phổ biến nhất. Các bài toán ví dụ tiêu biểu cho thuật toán này sẽ là:

  • Dự đoán xem một bức thư có phải là thư rác hay không.
  • Chẩn đoán xem một người có nguy cơ bị đột quỵ hay không dựa trên kết quả khám tổng quát của họ.

Xem xét các ví dụ trên ta thấy rằng Logistic Regresion là một thuật toán dựa vào thống kê đánh giá các input đầu vào (1 vài feature X) và trả về kết quả (y) là một giá trị đại diện cho một sự kiện có xảy ra hay không . Với y = 1 nói lên là sự kiện đó sẽ xảy ra còn với y = 0 thì sự kiện đó không xảy ra .
Với các bài toán Regression thông thường kết quả sẽ trả về sẽ là một đại lượng liên tục còn với Logistic Regression thì kết quả trả về sẽ là một đại lượng rời rạc

(*Chú Thích : Với bài toán như Linear Regression kết quả trả về đại lượng liên tục ví dụ như là giá tiền của một ngôi nhà sau khi biết được diện tích của nó còn với Logistic Regression kết quả trả về sẽ là đai lượng rời rạc như 0,1 biểu thị cho một sự kiện được dự đoán có xảy ra hay không)

1.2 Lý Thuyết Toán học

Logistic Regression là một loại thuật toán supervised learning tính toán mối quan hệ giữa các feature trong input và output dựa trên hàm logistic/sigmoid. Mặc dù gọi là Logistic Regression nhưng thuật toán này không dự đoán ra giá trị thực như các thuật toán Regression khác, Logistic Regression được dùng để dự đoán ra một kết quả nhị phân (với giá trị 0/1 hay -1/1 hay True/False) dựa vào input của nó. Nhưng Logistic Regression cũng có một chút giống với Linear Regression trong quá trình xây dựng model.

Trong Linear Regression output y được tính toán bằng tổng tích giữa các biến input và hệ số w của model

\( y=~h_0x_0~+~h_1x_1~+~h_2x_2~+…+~h_nx_n \)

Mục đích của Linear Regression là ước tính giá trị cho các hệ số mô hình c, w1, w2, w3… .wn và fit nó với dữ liệu huấn luyện với hàm loss tối thiểu và dự đoán đầu ra y.
Logistic Regression thực hiện điều tương tự, nhưng với một bổ sung. Nó chạy kết quả thông qua một hàm non-linear (phi tuyến tính) đặc biệt được gọi là hàm logistic hoặc hàm sigmoid để tạo ra đầu ra là một xác suất p.
Công thức hồi quy của model Logistic Regression:

Với \( \log( \frac{p}{1-p} ) \) được gọi là logit(p) hay còn gọi là log-odds ta sẽ tính được xác suất p như sau

\( p~=~\frac{1}{1~+ ~e^{-(h_0~+~h_1x_1~+…+~h_nx_n)}} \)

Logistic được ký hiệu là \(S_0\) là hàm sigmoid với đầu ra là một số có giá trị từ 0 đến 1 được định nghĩa với công thức bên dưới.

\(S_0(t)~=~\frac{1}{1+exp(-t)}\) với đồ thị được biểu thị bên dưới.

Hình 2 : Biễu diễn đồ thị của hàm S0

Chú ý rằng So(t) <0.5 khi t < 0 và So(t) \(\geq\) 0.5 khi t  \(\geq\)  0
mỗi khi Logistic Regression tính xác xuất P model sẽ đưa ra dự đoán với công thức sau :

\(\begin{cases}0 & Nếu~p~<~ 0.5\\1 & Nếu~p~\geq 0.5\end{cases}\)

Với y^ là kết quả dự đoán

2.Decision Boundary

Các bài toán classifications “học” cách xác định các nhãn (label) cho các điểm dữ liệu được đưa vào để dự đoán, mặc dù mỗi thuật toán có mỗi cách xác định cụ thể khác nhau nhưng mục tiêu của nó là vẽ được 1 đường phân loại các lớp dữ liệu từ các điểm dữ liệu được cho vào ban đầu đường này được gọi là Decision Boundary đường này có hình dạng tùy ý dựa vào thuật toán mình sử dụng , Ví dụ với bài toán có 2 lớp dữ liệu , model sẽ “học” để vẽ ra được 1 đường phần chia các điểm dữ liệu, với các điểm dữ liệu nằm ở bên phải thuộc class “Male” và điểm dữ liệu nằm ở mặt bên trái đường thẳng thuộc class “Female”

Decison boundary(Hình minh họa) Nguồn : medium.com

Dựa vào Decision Boundary trên model có thể dự đoán dữ liệu test bằng cách vẽ các điểm dữ liệu mới dựa trên các feature có sẵn , nếu điểm dữ liệu nằm bên trái thì sẽ được dự đoán là “Female” hay điểm dữ liệu nằm bên phải boundary sẽ được dự đoán là “Male”.

3.Hàm Mất mát (Loss Function)

3.1.Ý Tưởng Để Thiết Kế Hàm Loss Cho Logistic Regression

Mục tiêu của hàm loss nhằm phản ánh độ tối ư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ố để các điểm (vector \(h\)) dữ liệu gần với đường hồi quy nhất. Với mô hình Logistic Regression đầu ra là xác xuất với miền giá trị nằm [0,1] hay [-1,1] dùng để phân loại nhị phân (dự đoán đầu ra là 1 trong 2 kết quả) . Ví dụ ta có dữ liệu cho bài toán dự đoán trong ảnh có phải là con chó hay không .

*Chú thích : ở phần này mình chỉ giới thiệu về hàm log() bởi vì với hàm sigmoid có chưa hàm exp() thì sử dụng hàm log cho cross-entropy sẽ triệt tiêu được giá trị với exp()

Ví dụ ta sẽ mô tả cho bài toán đã được đề cập ở phần giới thiệu là bài toán phân loại cảm xúc bài hát . Ta có dữ liệu là một tập hợp danh sách hơn 2000 các tên bài hát kèm với lyric. Bài toán của chúng ta là dự đoán xem cảm xúc của bài hát có phải là cảm xúc buồn hay không (trainning set)

Indextitle singer moodbinary_moodmood_stringlyrics
0 Falling For You Boyzed 1 0 hung-phan chỉ một lần gặp em ||| mà mong nhớ suốt cả ngà…
1 Đông Kiếm Em 31 buon tôi hát cho màu xanh mãi xanh cho một người lặ…
2 OTĐ For Life! Lăng LD 10hung-phan lăng ld lên mic ||| otđ for life ||| tiền gian.
3 Bài Toán Tình Yêu Akira Phan31buon hỏi rằng người yêu anh thế nào ||| hỏi rằng ng…
4 Thiên Đường Vắng Em Xuân Sơn, Tùng Tic30buon từ khi gặp được em một ánh mắt ||| để cho lòng…

Với dữ liệu trên ta thấy cột mood _binary  nhãn = 1 (ứng với bài hát có cảm xúc buồn ) nhãn = 0 (ứng với bài hát không có cảm xúc buồn . Gọi P là xác suất với bộ tham số hiện tại model dự đoán ở tập test xem bài hát đó có phải là bài hát buồn hay không . Với dư liệu test của bài chúng ta như sau :

Index title singer mood binary_mood mood_string lyrics
0 Chiều Vắng Trung Quân31buon tìm trong quên lãng mênhmôngvươnghồn ai|||…
1 Ta Là Của Nhau Đông Nhi, Ông Cao Thắng 20hy-vong khi gặp nhau phút đầu ||| từ ánh mắt anh đã tr…
2 Cứ Yêu Đi Ngô Kiến Huy, Sĩ Thanh 10hung-phan đi bàn chân ta đi lang thang khắp nơi phương t…
3 Túy Tofu, Xesi, NamLee, VoVanDuc 31buon tofu ||| góc cũ vài chai say ||| giờ ta là ai …
4 Đôi Mắt Người
Xưa
Đan Nguyên31buon chuyện tình của tôi tan vỡ từ lâu rồi tưởng kh…

Ta xem xét 2 bài hát trong tập test là bài “Ta Là Của Nhau” và bài “Đôi Mắt Người Xưa” ta thấy

titlesingermoodmood_binarymood_stringlyrics
1 Ta Là Của Nhau Đông Nhi, Ông Cao Thắng 20 hy-vong khi gặp nhau phút đầu ||| từ ánh mắt anh đã tr…
4 Đôi Mắt Người
Xưa
Đan Nguyên 31 buon chuyện tình của tôi tan vỡ từ lâu rồi tưởng kh…

Giả sử xác xuất mà model dự đoán cho bài hát đầu tiên ( “Ta Là Của Nhau” ) \(P_1\) = 0.2 với nhãn ( \(Y_1=0\) còn xác suất của bài thứ 4 ( “Đôi Mắt Người Xưa” ) \(P_4\) = 0.7 với nhãn (Y_4=1\), xác suất dự đoán được có giá trị khá khớp với nhãn(Dựa trên Decision Boundary đã đề cập ở trên). Vậy nên với bộ tham số (vector \(h\)) của model hiện tại đã dự đoán đúng , câu hỏi đặt ra ở đây là liệu model hiện tại đã đủ tốt hay chưa?

Câu trả lời là chưa, mục tiêu của hàm loss là phản ánh độ tối ưu của bộ tham số hay nói cách khác là độ thông minh của model. Dựa vào hàm loss ta có thể giúp các bộ tham số có giá trị phù hợp hơn nữa để model dự đoán xác suất \(P_1\) của bài hát 1 càng gần 0 càng tốt và xác xuất \(P_4\) càng gần 1 càng tốt.
Với các dữ liệu có mẫu dương (y=1) xác suất dự đoán càng gân 1 thì hàm loss nên càng nhỏ càng tốt, như vậy trường hợp này với trường hợp này thì hàm loss có tính chất nghịch đảo với xác suất.Còn với những input có mẫu âm(y=0) thì ngược lại xác suất càng nhỏ(càng gần 0) thì hàm loss có giá trị càng nhỏ(vì thế để khớp với toàn bộ dữ liệu ta nghịch đảo những xcas suất có mẫu âm bằng cách P(X|Y=0) = 1 – P. Vì thế với bài toán phân loại nhị phân hàm loss có tính chất nghịch đảo với xác suất, để biểu thị ta cần dùng một hàm số để nghịch đảo giá trị xác suấ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 ( vì p gần 1 thì -log càng nhỏ)

3.2. Cross-Entropy loss

Các bạn đã nắm lý thuyết tổng quát về Logistic Regression vậy bây giờ làm sao để model được huấn luyện? Đối tượng mà model Logistic Regression thiết lập trong quá trình đào tạo là vector w (giống như linear Regression) .

Không may rằng không thể (hoặc ít nhất là không nên) sử dụng cùng một hàm mất mát MSE như Linear Regression. Tại sao? đơn giản là vì hàm dự đoán này là phi tuyến tính (do biến đổi sigmoid). Bình phương dự đoán này như chúng ta làm trong MSE dẫn đến một hàm không lồi với nhiều cực tiểu. Nếu hàm mất mát của chúng ta có nhiều điểm cực tiểu, thì sẽ rất phức tạp để xác định điểm tối thiểu của model trong quá trình training. Thay vì dùng hàm MSE thì thay thế bằng một hàm mất mát khác có tên là Cross-Entropy cũng có thể gọi là log loss. Cross-Entropy chia hàm mất mát thành 2 phần, một phần tính toán cost cho mẫu dương (y=1) một phần tính toán cost cho mẫu âm (y=0)

\(\begin{cases}-log(p) & Nếu~y~=~1\\-log(1-p) & Nếu~y~=~0\end{cases}\)

Với -log(t) sẽ có giá trị lớn nếu t gần giá trị 0 vậy nên hàm mất mát sẽ có giá trị lớn nếu model dự đoán xác xuất p (chú ý : p = h(x) ) với giá trị gần 0 cho mẫu dương (y=1) và ngược lại với mẫu âm (y=0) . Hàm mất mát này sẽ đi qua toàn bộ mẫu trong training set và có công thức chung là :

\(L(\theta) = \frac{1}{m}\sum_1^m[y^ilog(p^i)+(1-y^i)log(1-p^i)]\)

Mặc dù không có công thức nhất định nào để tính toán giá trị của vector h tối thiểu được hàm mất mát này nhưng đồ thị của hàm mất mát này có cực tiểu nhất định và dựa vào Gradient Decent có thể tìm ra được điểm nhỏ nhất trong model khi training.
Công thức tính đạo hàm riêng từng tham số h của hàm loss

\(\frac{\partial}{\partial \theta_j}L(\theta)~=~\frac{1}{m}\sum_1^m(sigmoid(\theta^Tx^i)-y^i)x_j^i\)

Công thức cập nhật trọng số \(\theta\)

\( h_j~=~h_j~-~n\frac{\partial}{\partial h_j}j(h)\)

với n là Learning Rate

4.Học với Gradient Decent

Ta có thể ước tính giá trị của các hệ số bằng cách sử dụng Gradient Decent.

Đây là một phương thức đơn giản có thể được sử dụng bởi nhiều thuật toán trong Machine Learning. Nó hoạt động bằng cách sử dụng mô hình để tính toán dự đoán cho mỗi trường hợp trong tập huấn luyện và tính toán sai số(Loss) cho mỗi dự đoán.

Ta có thể tìm ra bộ hệ số ứng với hàm Loss đạt giá trị nhỏ nhất cho bài toán tìm hệ số cho mô hình logistic Regression như sau:

  1. Dự đoán đầu ra với model hiện tại
  2. Tính giá trị mới cho bộ hệ số dựa vào hàm loss (chênh lệch giữa giá trị dự đoán và kết quả thực tế )
  3. Lặp lại 2 bước trên đến khi nào thấy hàm loss có giá trị là nhỏ nhất

Quá trình lặp lại kết thúc khi những vòng lặp sau giá trị của hàm Loss không còn giảm nữa (để làm như vậy tham khảo phương thức Early Stopping ) hoặc đã chạy hết số vòng lặp đã chỉ định. Thông thường, một ý tưởng hay là random thứ tự của các trường hợp huấn luyện được hiển thị cho mô hình để kết hợp các hiệu chỉnh đã thực hiện.

Bằng cách cập nhật mô hình cho từng mẫu mô hình đào tạo,đây là online learning. Cũng có thể thu thập tất cả các thay đổi đối với mô hình trong tất cả các trường hợp đào tạo và thực hiện một bản cập nhật lớn. Biến thể này được gọi là batch learning mà chúng ta sẽ đề cập đến nó sau khi tiếp cận đến Deep Learning. Bây giờ hãy cùng theo dõi Step By Step để nhìn rõ hơn cách GD học và tối ưu model

Tính toán đầu ra của model hiện tại

Giả sử input của model hiện tại có 2 feature [x1,x2] và có giá trị là [2,3] với nhãn là y = 1.
Cho nên ứng với 2 feature ta có vector hệ số gồm 3 thành phần [h_0,h_1,h_2] ,bắt đầu với giá trị tương ứng là [0,0,0]
Ta tính được \( P ~= ~\frac{1} { (1 + e^(-(h_0 + h_1x1 + h_2x2))} = 0.5\)

Tính toán hệ số mới của model dựa trên độ chệnh lệch của giá trị dự đoán với đầu ra thực tế (loss function)

Ta tính được hệ số mới dựa vào công thức sau :
\( h_j~=~h_j~-~n\frac{\partial}{\partial h_j}j(h)\)

với hj là hệ số thứ j trong bộ hệ số của model t cần cập nhật.
n là 1 giá trị gọi là learning rate . giá trị này t cần chỉ định giá trị của nó trước khi tiến hành training model, nó biểu thị tốc độ học và mức độ thay đỗi của các hệ số mỗi khi nó được cập nhật. Với những model thông thường nên chỉ định nó với giá trị vào khoảng 0.1 đến 0.3, với bài toán này t chỉ định là 0.3. Áp dụng vào công thức trên ta thu được bộ hệ số mới với

h0 = 0.15

h1 = 0.3

h2 = 0.45

Lặp lại các bước trên

Ta có thể lặp lại quá trình này và cập nhật mô hình cho từng mẫu training trong tập dữ liệu.
Một lần lặp lại thông qua tập dữ liệu huấn luyện được gọi là một epoch. Người ta thường lặp lại quy trình gradient descent cho một số epochs cố định.

Vào cuối epoch, bạn có thể tính toán các giá trị lỗi cho mô hình. Bởi vì đây là một vấn đề phân loại, sẽ rất tuyệt nếu có được ý tưởng về mức độ chính xác của mô hình ở mỗi lần lặp.
Biểu đồ dưới đây cho thấy giá trị của hàm Loss giảm như thế nào trong 100 epochs(hình chỉ mang tính chất minh họa )

5.Thực hiện bài toán với Python

Như đã đề cập ở trên bài toán của chúng ta sử dụng bộ data có hơn 2000 bài hát được lưu trữ gồm các features như : title, lyrics, singer, key nhưng mình sẽ chỉ sử dụng 2 features trong đó là “title” và “lyrics” để train cho model Logistic Regression bởi vì nó có liên quan mật thiết đến cảm xúc của bài hát. Đôi lúc chúng ta chỉ cần đọc qua title hay là lyric là chúng ta có thể xác định thể loại nhạc mà nó thuộc vào phải không nào cho nên model của chúng ta cũng vậy, việc chúng ta xác định các features có ảnh hưởng nhiều đến model hay không cũng là một bước rất quan trong trong Machine Learning.

Quay lại các features được chúng ta sử dụng khi training là “title” và “lyrics”, chúng đều là các data dạng chữ viết, vậy liệu Logistic Regression có thể “học” được chúng như những đứa bé mới sinh ra đời học chữ viết không? câu trả lời rất tiếc là khonggg

Các model trong Machine Learning “học” bằng cách cải thiện kết quả dựa trên đánh giá hàm loss, mà hàm loss là tập hợp các hàm được sử dụng với từng loại model thích hợp mà đa phần các hàm này đều ở dạng hàm toán học, vậy làm sao hàm toán học có thể xử lý và tính toán các giá trị từ những chữ viết đây.
Cách đơn giản nhất là chuyển các chữ số này về dạng mà máy tính có thể hiểu được hay cụ thể hơn là chuyển nó về dạng số, đây là một bài toán khá phổ biển được gọi với một cái tên chung là NLP(Natural Language Processing).

Có rất nhiều thuật toán để giải quyết vấn đề này nhưng bài viết này vẫn là một bài viết chuyên sâu vào Logistic Regression nên ở đây mình sử dụng một thuật toán khá phổ biến đó là Bag Of Words và giải thích từng bước cho mọi người hiểu về thuật toán này. Những thuật toán về NLP mọi người hãy mong chờ những series khác đến từ CLB AI UIT sau này có thể sẽ cùng nhau thảo luận tiếp để hiểu được nhiều khía cạnh từ NLP hơn nữa nhé .

Bag of words là một thuật toán khá phổ biến trên thế giới, nó hoạt động bằng cách chuyển hóa các câu trong data để đưa về dạng vector các “số” (số như thế nào thì mình sẽ giải thích ở vài câu tiếp theo) trước khi đưa vào model để phân loại.
Thuật toán này không quan tâm tới từ ngữ có ý nghĩa như thế nào, sắp xếp trong câu ra sao,”Nó” hoạt động bằng cách thu thập tất cả các từ có trong bộ dữ liệu và chuẩn hóa từng câu về dạng vector của các “số” mà “số” đó biểu thị tần suất suất hiện của từ ngữ đó trong câu so với toàn bộ từ được thu thập.
Các bước thực hiện Bag of words như sau:

  1. Xây dựng vocabulary(tập hợp tất cả các từ vựng trong tập dữ liệu) từ document được sử dụng

Ví dụ : ta có 2 câu trong 1 văn bản đó là

  • Hôm nay trời đẹp quá.
  • Bạn gái của tôi đẹp quá.

Vậy với document trên tập vocabulary được xây dựng với những từ sau
{Hôm,
nay,
trời,
đẹp,
quá,
Bạn,
gái,
của,
tôi,}
có được 9 từ lấy từ document có 11 từ

2. Vectorization(vector hóa) từ 2 câu chữ thành 2 vector với mỗi phần tử là tần suất xuất hiện trong câu

Bước tiếp theo là ta sẽ tiến hành chuyển đổi từ câu chữ về dạng vector với fixed-length(số lượng phần tử của vector tương ứng với số lượng từ trong vocab) bằng 9 để đưa vào input hay output của model . Với từng phần tử trong vector sẽ là số lượng tần suất xuất hiện của các từ trong câu
Ví dụ với câu đầu tiên “Hôm nay trời đẹp quá, ra ngoài chơi thôi.” sẽ được vector hóa dựa trên tần suất xuất hiện của các từ trong câu như sau :
Hôm nay trời đẹp quá = [1,1,1,1,1,0,0,0,0] với “Hôm” xuất hiện 1 lần trong câu,”nay” xuất hiện 1 lần,…
Bạn gái của tôi đẹp quá = [0,0,0,1,1,1,1,1,1] với”Hôm” xuất hiện 0 lần trong câu,”nay xuất hiện 0 lần,…

Bag of words với từng câu trong document được so sánh với các từ trong vocabulary được tạo ở bước 1. Dựa vào tần suất xuất hiện trong câu các phần tử trong vector này có thể tăng lên 2,3,.. và các vector được sử dụng trong các thuật toán Machine Learning để dự đoán hay phân loại các document.
Đó là cách đơn giản nhất để cho thấy cách hoạt động của Bag of words các bộ dữ liệu được sử dụng trong Machine Learning có thể rất lớn từ vài nghìn cho đến hàng triệu từ nên việc tiền xử lý với bộ dữ liệu dạng text cũng rất quan trong để model đạt được hiệu quả tốt, sau đây mình sẽ giới thiệu một cách để preprocessing đối với bài toán Bag of words

Xóa các “StopWords”:
StopWords được hiểu là những từ có thể sẽ xuất hiện nhiều trong câu nhưng về ý nghĩa thì nó không mang lại nhiều cho câu với tiếng việt như : để , còn, … còn với tiếng anh thì có các mạo từ :a, an, the,..
việc loại bỏ các từ này giúp làm nhẹ dữ liệu cần phải xử lý và giảm thời gian training

Thực hiện bài toán phân loại cảm xúc bài hát với Bag of words và Logistic Regression

Bây giờ hãy cùng dùng Logistic Regression để phân loại bài toán từ tập “lyric” và “title” của tập data ở trên để dự đoán rằng cảm xúc của bài hát có phải là cảm xúc buồn hay không nhé.

Việc đầu tiên trước khi thực hiện bất kì một bài toán Machine Learning nào thì chúng ta phải đọc dữ liệu và chia nó làm 2 tập, tập train và test , tập train như sau :

Indextitle singer moodbinary_moodmood_stringlyrics
0 Falling For You Boyzed 1 0 hung-phan chỉ một lần gặp em ||| mà mong nhớ suốt cả ngà…
1 Đông Kiếm Em 31 buon tôi hát cho màu xanh mãi xanh cho một người lặ…
2 OTĐ For Life! Lăng LD 10hung-phan lăng ld lên mic ||| otđ for life ||| tiền gian.
3 Bài Toán Tình Yêu Akira Phan31buon hỏi rằng người yêu anh thế nào ||| hỏi rằng ng…
4 Thiên Đường Vắng Em Xuân Sơn, Tùng Tic30buon từ khi gặp được em một ánh mắt ||| để cho lòng…

Tập test như sau:

Index title singer mood binary_mood mood_string lyrics
0 Chiều Vắng Trung Quân31buon tìm trong quên lãng mênhmôngvươnghồn ai|||…
1 Ta Là Của Nhau Đông Nhi, Ông Cao Thắng 20hy-vong khi gặp nhau phút đầu ||| từ ánh mắt anh đã tr…
2 Cứ Yêu Đi Ngô Kiến Huy, Sĩ Thanh 10hung-phan đi bàn chân ta đi lang thang khắp nơi phương t…
3 Túy Tofu, Xesi, NamLee, VoVanDuc 31buon tofu ||| góc cũ vài chai say ||| giờ ta là ai …
4 Đôi Mắt Người
Xưa
Đan Nguyên31buon chuyện tình của tôi tan vỡ từ lâu rồi tưởng kh…

Viết các hàm xử lý ký tự chữ bằng Bag of words, với bài toán này chúng ta chỉ cần 3 hàm để xử lý nlp trước khi vào training model

Hàm word_extraction() có mục đích là trích xuất tất cả các từ trong câu và trả về 1 list các từ đơn.

def word_extraction(sentence):
    # sentence = re.sub("[^\w]", " ", sentence)
    words = sentence.strip().split()
    cleaned_text = [w.lower() for w in words]
    return cleaned_text

sau khi trích xuất từng từ thành 1 list các từ thì việc tiếp theo chúng ta cần làm để hoàn thành bước 1 của Bag of words là tổng hợp tất cả các từ được trích xuất thành 1 tập hợp các từ xuất hiện trong document gọi là vocabulary. Hàm xử lý là hàm tokenize() như bên dưới

def tokenize(sentences):
    vocab = {}
    count = 0
    for sentence in sentences:
        # Tách câu ra thành các từ
        words = word_extraction(sentence)
        for word in words:
            # Nếu từ điển chưa có từ này thì thêm vào và tăng count lên
            if word not in vocab:
                vocab[word] = count
                count += 1
    return vocab, count

Và viết cuối cùng để xử lý nlp bằng bag of words là vector hóa tất cả các câu trong document thành các vector mà các phần tử trong đó biểu thị cho tần suất xuất hiện của mỗi từ trong vocabulary xuất hiện trong từng câu, ở đây chúng ta có thêm 1 bước là normalize các vector này để đưa giá trị các phần tử về khoảng từ [0,1] (normalize luôn là một lựa chọn hàng đầu mỗi khi training với data dạng số để giảm thời gian trainning) . Hàm xử lý là hàm generate_bow()

def generate_bow(allsentences):
    X = np.zeros((len(allsentences), vocab_count))
    for sentence, bow in zip(allsentences, X):
        # Tách câu thành các từ
        words = word_extraction(sentence)
        for word in words:
            # Nếu từ đang xét nằm trong từ điển thì thêm nó vào bag of words
            if word in vocab:
                word_index = vocab[word] # Vị trí của từ trong từ điển
                bow[word_index] += 1

    X = X.transpose()

sau khi chuẩn bị xong tất cả các hàm cần thiết để tiền xử lý data thì ta cần apply các hàm này với data.

# Load title và lyrics từ data_train
lyrics_train = data_train['lyrics'].values
title_train = data_train['title'].values
# Gộp các dòng của lyrics lại
lyrics_train = [lyrics.replace(' ||| ', ' ') for lyrics in lyrics_train]

X_train = []
# Tạo từ điển
vocab, vocab_count = tokenize(lyrics_train)
# Tạo bag of words cho data_train
X_train = generate_bow(lyrics_train)

print(np.shape(X_train))
n_features = np.shape(X_train)[0]

# Load nhãn cảm xúc của các lyrics
Y_train = data_train['mood_binary'].values
# Chuyển kiểu dữ liệu từ vô hướng thành vector
Y_train = np.reshape(Y_train, (-1, 1))

Sau khi tiền xử lý với bag of words thì data có dạng như sau

Các câu trong

Đây là dạng mà model có thể hiểu . Bây giờ chúng ta có thể trainning với Logistic Regression được rồi, Trước khi trainning mình sẽ show cho các bạn thấy các hàm cần thiết mà Logistic cần để tính toán trong trainning

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

def z(theta, x):
    return x.T @ theta

def hypothesis(theta, x):
    return sigmoid(z(theta, x))

def loss(theta, x, y):
    h = hypothesis(theta, x)
    one_case = np.matmul(-y.T, np.log(h))
    zero_case = np.matmul(-(1 - y).T, np.log(1- h))
    cross_entropy = (one_case + zero_case) / len(x)
    return cross_entropy

Những hàm trên đều folow theo lý thuyết về Logistic Regression ở trên, nếu các bạn thắc mắc phần nào có thể kéo lên trên để xem lại cách hàm đó hoạt động mà mình đã đề cập. Bây giờ hãy bắt đầu trainning với hàm minimize() được đề cập dưới đây . minize() tối thiểu giá trị từng theta(bộ tham số \(h\)) qua mỗi vòng lặp bằng Gradient Decent bằng cách dựa vào giá trị hàm loss hiện thời và learning rate để tính toán giá trị theta tiếp theo cho đến khi kết thúc vòng lặp yêu cầu (iterations). Còn cách nó tính như thế nào các bạn nhớ xem kỹ phần lý thuyết và đối chiếu với code sẽ thấy.

def minimize(theta, x, y, iterations, learning_rate):
    costs = []
    for i in range(iterations):
        # Tính đạo hàm của hàm mất mát
        error = hypothesis(theta, x) - y
        gradient = learning_rate * (x @ error) / len(x)
        # Cập nhật trọng số theta
        theta = theta - gradient
        costs.append(loss(theta, x, y)[0][0])
        if i % 100 == 0:
            print('Iteration: {}, loss = {}'.format(i, costs[-1]))
    return theta, costs

Tất cả các hàm cần thiết cho trainning đã xong bây giờ ta cần khởi tạo bộ tham số theta có giá trị bất kỳ (giá trị ban đầu bằng bao nhiêu không quan trọng vì nó sẽ được thay đỗi để fix với data qua từng iteration) sau đó join vào hàm minimize để bắt đầu training

# Khởi tạo theta ngẫu nhiên
theta = np.random.randn(n_features, 1)

iterations = 10001 # Chạy thuật toán GD lặp 10001 bước (có thể tùy chỉnh số lần lặp tùy ý)
theta, costs = minimize(theta, X_train, Y_train, iterations=iterations, learning_rate=0.5)
Quá trình training

Dựa vào biểu đồ training cho thấy giá trị cost(loss function value) liên tục giảm chứng minh quá trình training rất hiệu quả

plt.plot(range(iterations), costs)
plt.show()
Training curve

Sau khi đã training xong sẽ tới bước test model .Trước khi test mọi người nhớ convert data về dạng vector trước khi test với bag of words giống như sử dụng với training data . kết quả cho thấy khá tốt, model đạt được độ chính xác bằng 0.83%

y_hat = predictions.copy()
# Lấy ngưỡng 0.5
y_hat[y_hat >= 0.5] = 1
y_hat[y_hat < 0.5] = 0
# Đếm số lần dự đoán đúng (y_hat == Y_test)
accuracy = np.where(y_hat == Y_test)[0].shape[0] / y_hat.shape[0]
print('Accuracy: ', accuracy)

Accuracy : 0.8391

Chúng ta hãy cùng xem kết quả vài bài hát trong tập test xem nó dự đoán như thế nào nhé

kết quả trong tập test

Vậy là bài toán qua một khoảng thời gian khá dài về cả lý thuyết và thực hành đã hoàn thành và đã cho thấy kết quả khá khả quan, với dữ liệu này mình xử lý nó chỉ với feature lyrics và với output binary classification, Ngoài ra Logistic Regression còn có thể xử lý với cả những bài toán yêu cầu multiple class, nếu các bạn hứng thú với nó hãy cùng mình thảo luận tiếp tục về phương thức này ở phần kế tiếp nhé !!!!

6. Multiple Classification with Logistic Regression

Tất cả các lý thuyết và ví dụ trên đều nói về bài toán mà output chỉ có 2 class, tức Logistic Regression được train với binary classifier. Vậy làm cách nào để train model Logistic Regression với output có nhiều hơn 2 class?

Với các bài toán có 2 class ta giải quyết chúng bằng cách trả lời câu hỏi có hay không cho output của bài toán dựa vào input của chúng, với bài toán phân loại cảm xúc bài hát ở trên thì giải quyết với câu hỏi liệu bài hát đó có cảm xúc buồn hay không, hay với bài toán phân loại email rác thì phải trả lời câu hỏi email được gửi này có phải email rác hay không. Thì bài toán có nhiều hơn 2 class cách giải quyết đơn giản chỉ là trả lời nhiều câu hỏi có hay không.

Tương đương với việc train với nhiều classifier sử dụng Logistic Regression.Có nghĩa là với mỗi classifier( \(h_\theta^i(X)\) ) ta dùng cho một class với nhãn của nó sẽ bằng 1 và những class còn lại có nhãn là 0.Cách làm này còn được gọi với cái tên như one-vs-all hay one-vs-rest Classifier.

Ví dụ với bài toán cần phân loại 4 class động vật gồm : chó , mèo, gà, vịt thì one-vs-rest Classifiers sẽ gồm 4 classifiers (\(h_\theta^i(X)\)) với classifier thứ 1 (\(h_\theta^1(X)\)) train cho output có 2 nhãn y = 1 biểu thị cho động vật là con chó và y = 0 biểu thị cho động vật không phải là con chó(tức là mèo,gà,vịt) tương tự với classifier thứ 2 (\(h_\theta^2(X)\)) train cho output có 2 nhãn y = 1 biểu thị cho động vật là con mèo và y = 0 biểu thị cho động vật không phải là con mèo(tức là chó,gà,vịt) và tương tự với classifier 3 và 4.

Predict với multiclass

Như đã nói sử dụng Logistic Regression để phân loại n class bằng cách train n classifiers với bộ tham số (\(h_\theta^i(X)\) tương ứng cho từng class, các quá trình tính cost , minimize() đều giống với binary classification việc duy nhất của chúng ta là viết 1 cái vòng lặp cho nó train độc lập giữa các classifier với nhau , mỗi classifier sẽ có 1 bộ tham số \(h_\theta^i(X)\) riêng được dùng để dự đoán kết quả.

Ví dụ ta có dữ liệu test là một bức ảnh được cho vào ví dụ ở trên thì sẽ có dự đoán như sau :

P( (\(h_\theta^1(X)\)) = 0.5
P( (\(h_\theta^2(X)\)) = 0.1
P( (\(h_\theta^3(X)\)) = 0.3
P( (\(h_\theta^4(X)\)) = 0.2

với P( (\(h_\theta^1(X)\)) với xác suất được dự đoán ở bộ tham số thứ 1 có giá trị cao nhất thì kết quả trả về sẽ là con chó.

Lời Kết

Vậy là qua bài viết này mình đã giới thiệu cho mọi người chi tiết về Logistic Regression, lý thuyết toán học, và thực hành Logistic Regression với bài toán phân loại cảm xúc bài hát kèm code. Mình hy vọng qua bài viết này các bạn đọc có thể hiểu được Logistic Regression cách thức nó hoạt động và tại sao nó lại phổ biến đến vậy. Các bạn có thể tham khảo thêm source code của mình tại đây. Hãy thử một bài tập về nhà nho nhỏ là cài đặt thêm phần dự đoán multiclass cho các loại cảm xúc khác trong bộ dữ liệu nhé!

CLB AI rất mong nhận được những góp ý từ quý bạn đọc. Những nhận xét của các bạn luôn được lắng nghe, tiếp thu nhằm giúp CLB AI cải thiện chất lượng bài viết để mang đến các bạn sinh viên những kiến thức về Machine Learning một cách chính xác và dễ hiểu nhất. Chân thành cảm ơn sự quan tâm của các bạn!

Quân Hà – KHDL2019.

References

kadikoy moto kurye umraniye moto kurye tuzla moto kurye atasehir moto kurye moto kurye moto kurye moto kurye moto kurye

Leave a Reply

Your email address will not be published. Required fields are marked *