Lần đầu tham gia kaggle: toxic comment classification challenge

Nguồn: medium

Giới thiệu cho bạn nào chưa biết, Kaggle là sân chơi chung cho những người thích phân tích dữ liệu. Các hoạt động chính của kaggle đều xoay quanh dữ liệu, nổi tiếng và dữ dội nhất trong đó là các cuộc thi đấu giành huy chương. Ngoài ra mọi người còn có thể thảo luận về các chủ đề khác của dữ liệu như hình ảnh hóa, phân tích mô tả, hay các phương pháp tiếp cận bài toán...

Nếu sự nghiệp của bạn gắn liền với phân tích dữ liệu chuyên sâu thì chắc chắn bạn nên tham gia các cuộc thi của kaggle để học hỏi kinh nghiệm. Tôi biết đến kaggle khá lâu trước khi tham gia một cuộc thi thực sự trên này. Đợt này vì đang rảnh nên tôi bắt đầu tìm hiểu về machine learning và NLP bằng cách tham gia khóa học của Jeremy Howard, anh này thì toàn khuyến khích học viên đi thi thố cọ xát nên tôi quyết định chọn Toxic Comment Classification Challenge để thử sức.

Mục đích cuộc thi rất ý nghĩa đó là nhằm hạn chế những bình luận lạm dụng, quấy rối, tục tĩu trên mạng để giúp môi trường ảo lành mạnh hơn. Bài toán đặt ra đó là phân loại các bình luận theo 6 nhãn toxic, severe_toxic, obscene, threat, insult, identity_hate. Đây thực ra là bài toán surpervised learning quen thuộc, hay trong NLP nó là dạng text classification.

Fasttext: Đánh nhanh thắng nhanh

Cách đầu tiên tôi nghĩ tới đó là sử dụng gói fasttext của facebook vì nó khá hiệu quả trong phân loại văn bản. Cách sử dụng gói này cũng rất đơn giản, đầu tiên bạn cần dùng python (hoặc các ngôn ngữ lập trình bất kì) xử lý các văn bản sao cho có cấu trúc __label__<> text content và lưu lại dưới định dạng txt. Sau đó thì chỉ việc đẩy vào fasttext để nó làm các công việc còn lại.

Tuy nhiên log loss ban đầu của tôi rất cao.

Sau khi kiểm tra lại thì tôi mới nhận ra là vì quá hào hứng nên đã viết code sai :D. Nhưng hơi lạ là tôi vẫn xếp trên một số bạn, chắc mấy bạn này cũng code sai :D.

Công việc tiếp theo là chỉnh lại code, argument của fasttext cho phù hợp và nhìn log loss giảm xuống.

Kết quả dự đoán của fasttext có log loss khá cao mặc dù nó sử dụng phương pháp bag of tricks. L̶ý̶ ̶d̶o̶ ̶c̶ó̶ ̶t̶h̶ể̶ ̶v̶ì̶ ̶n̶ó̶ ̶k̶h̶ô̶n̶g̶ ̶x̶ử̶ ̶l̶ý̶ ̶đ̶ư̶ợ̶c̶ ̶c̶á̶c̶ ̶c̶á̶c̶h̶ ̶v̶i̶ế̶t̶ ̶k̶h̶á̶c̶ ̶n̶h̶a̶u̶ ̶c̶ủ̶a̶ ̶c̶ù̶n̶g̶ ̶m̶ộ̶t̶ ̶t̶ừ̶.̶ ̶T̶ô̶i̶ ̶s̶ẽ̶ ̶n̶ó̶i̶ ̶v̶ề̶ ̶c̶á̶i̶ ̶n̶à̶y̶ ̶ở̶ ̶p̶h̶ầ̶n̶ ̶d̶ư̶ớ̶i̶.̶

Feature engineering: Rác vào thì rác ra (garbed in, garbed out)

Tôi quay trở lại với các mô hình đơn giản như logistic hay naive bayes khi thấy fasttext không cải thiện được nhiều kết quả trên bảng xếp hạng nữa.

Việc đầu tiên cần làm trong feature engineering đó là làm sạch dữ liệu, sau đó là chuyển văn bản thành vector để đưa vào mô hình.

Trong bước làm sạch dữ liệu, đầu tiên tôi chỉ lấy các kí tự từ a-z trong văn bản vì nghĩ rằng training và test set chỉ bao gồm các câu tiếng anh.

def clean_text(s):
#s1 = s.lower()
s1 = " ".join(re.findall("[a-z]+", s.lower()))
return(s1)

Tuy nhiên thì sự thực không phải vậy.

Thêm nữa trong comment có những icon có ý nghĩa nên cách chỉ lấy các kí tự chữ sẽ làm mất đi thông tin của câu. Vì vậy tôi quyết định dùng một hàm khác để làm sạch dữ liêu

import re, string
re_tok = re.compile(f'([{string.punctuation}“”¨«»®´·º½¾¿¡§£₤‘’])')
def tokenize(s):
if isinstance(s, str):
return ' '.join(re_tok.sub(r' \1 ', s.lower()).split())
else:
return ' '

Việc chuyển văn bản thành vector thường có 2 hướng: bag of word, term frequency–inverse document frequency để chuyển về sparse vector hoặc doc2vec để chuyển về dense vector. Tôi đã sử dụng doc2vec cho bài toán phân loại văn bản và thấy không hiệu quả lắm, vì vậy hướng khả thi là dùng TF-IDF.

Với TF-IDF, ngoài word level thì còn cần thêm char level vì cùng một từ có rất nhiều cách viết. Ví dụ như từ F*ck.

Đ̶â̶y̶ ̶c̶ũ̶n̶g̶ ̶l̶à̶ ̶l̶ý̶ ̶d̶o̶ ̶v̶ì̶ ̶s̶a̶o̶ ̶f̶a̶s̶t̶t̶e̶x̶t̶ ̶k̶h̶ô̶n̶g̶ ̶h̶i̶ệ̶u̶ ̶q̶u̶ả̶ ̶t̶r̶o̶n̶g̶ ̶c̶u̶ộ̶c̶ ̶t̶h̶i̶ ̶n̶à̶y̶.̶

Một số cách làm khác tôi cũng đã thử đó là stemming và lemmatization hoặc thêm một số meta feature như độ dài văn bản, số lương từ khác nhau trong văn bản, đếm số lượng từ "nhạy cảm" trong câu... Tuy nhiên những cách này không giúp tăng đáng kể vị trí của tôi trên bảng xếp hạng.

Mô hình đầu tiên tôi nghĩ tới đó là logistic regression. Thật bất ngờ là mô hình đơn giản này lại cho kết quả rất tốt khi kết hợp với TF-IDF word và char level n-gram.

Essemble method: Hãy chơi theo cách của bạn

Chia sẻ một chút là thông thường thì mỗi sáng tôi sẽ đến công ty sớm, uống cafe, đọc tin tức rồi làm việc. Nhưng từ khi thi đấu trên kaggle thì mỗi buổi sáng của tôi không còn bình yên nữa. Việc đầu tiên tôi làm khi đến công ty là nhìn thứ hạng của mình tụt thê thảm chỉ sau một đêm và kèm đó là cảm giác muốn tìm ra những cách mởi để cải thiện vị trí của mình.

Một trong những lý do khiến cho bạn có thể tụt hạng là vì những kernel được chia sẻ trong cuộc thi. Bất kì ai cũng có thể sử dụng lại kernel này để submit kết quả, và nếu log loss của họ thấp hơn thì bạn sẽ bị tụt xuống.

Rất nhiều các kernel trên kaggle sử dụng essemble method bằng cách tính trung bình cộng các kết quả từ các kernel khác như cái của Howard hoặc trung bình có trọng số của Ivan. Cách này phổ biến đến mức Vitaly Kuznetsov đã nói ở NIPS 2014:

This is how you win ML competitions: you take other peoples’ work and ensemble them together!

Tôi cũng đã thử cách này nhưng thay vì tính trung bình cộng, tôi sử dụng trung bình nhân. Kết quả cho ra tốt hơn nhiều so với việc chạy chỉ một model.

log loss 0.045 và thứ hạng tốt nhất của tôi. Tất nhiên là càng về sau thì nó càng tụt thảm hại :D.

Tuy nhiên tôi không ủng hộ cách làm này lắm vì nó mang nhiều tính cầu may. Thật khó để hiểu tại sao Ivan lại chọn trọng số như bên dưới và cũng thật khó để tìm ra các con số khác có thể giúp giảm log loss trên bảng xếp hạng.

(2*p_nbsvm + 3*p_lstm + 4*p_eaf) / 9

Essemble method ngoài phương pháp cầu may như trên thì còn có các phương pháp khác khá hữu ích trong thực tế đó là stacking và blending. Tôi sẽ nói rõ hơn về nó ở phần sau của bài viết.

xgboost: Võ lâm chí tôn

xgboost là một model thuộc tree-base method. Cá nhân tôi biết đến model này khi nó được giới thiệu là giành chiến thắng trong các cuộc thi của kaggle. Tôi không có nhiều kinh nghiệm chạy mô hình này nên chủ yếu đọc và làm theo document của thư viện. Kết quả của mô hình sau tinh chỉnh rơi vào khoảng 0.05 - 0.06 chứ không giúp tôi cải thiện vị trí của mình.

Theo tôi biết thêm thì những người chiến thắng còn essemble các boosting model này chứ không chỉ chạy riêng một model là được.

blending: cocktail thập cẩm

Như đã nói ở phần trước, essemble method ngoài essemble result thì còn 2 cách khác khá hữu ích trong thực tế đó là stacking và blending. Ý tưởng chung của stacking và blending là chia training set thành 2 phần, phần 1 cho training với một nhóm các model, phần 2 sử dụng một model phân loại khác với traning set là tổng hợp các dự đoán của các model của bộ 1.

Tuy khá giống nhau về cách làm, stacking có khác biệt đó là các model "nhìn" thấy toàn bộ training set còn ở blending thì model ở tập nào sẽ chỉ "nhìn" thấy dữ liệu ở tập đó. Mặc dù blending khiến cho training set bị giảm đi về tổng thể, cá nhân tôi thấy nó hợp lý hơn trong thực tế vì không vi phạm giả định về việc để cho model "nhìn" thấy trước kết quả.

Tôi cũng đã có thử blending với TF-IDF word, char n-gram features và base classifiers là logistic, SVM, naive bayes tuy nhiên log loss không thấp hơn 0.045. Có lẽ lần sau tôi nên thử với model hạng nặng như xgboost và lightGBM xem thế nào :D.

CNN & RNN: How "deep" is your model

Lưu ý một chút đó là tiêu đề được tôi mượn lại từ bài "how deep is your love" của Bee Gees chứ không phải của Calvin Harris & Disciples. Tôi thường thích mấy thứ nhẹ nhàng :D.

Trong xu thế nhà nhà nói về deep learning như hiện nay thì thật là thiếu sót khi không thử với các mô hình như CNN hay RNN. Đầu tiên là việc chuẩn bị dữ liệu để đưa vào mô hình sẽ hơi khác một chút so với phần trước. Tư tưởng chung vẫn là đưa một câu về dạng cố định nhưng ở phần trước là ở dạng dense hay sparse vector, còn ở phần này là ma trận với mỗi hàng là một đơn vị từ (word, sub word hoặc char) được biểu diễn bởi một vector. Có 2 hướng để lựa chọn số chiều và giá trị của embedding vector: Cách một là sử dụng sử dụng pre-trained word embeddings như của glove hay fasttext. Cách thứ 2 là để cho mô hình tối ưu các giá trị này tùy theo training set.

Đã có một số người sử dụng CNN , DPCNN hay RNN cho kết quả khá tốt. Tôi cũng đã thử CNN với word và subword level nhưng vì chưa có kinh nghiệm nên mô hình bị overfit khá nặng. Có lẽ tôi cần học nhiều hơn để sử dụng hiệu quả hơn :D.

Data leakage: Chơi tiếp hay nghỉ?

Data leakage là việc rò rỉ thông tin khiến cho các mô hình có thể dự đoán tốt hơn training set và test set. Người chơi trên kaggle hoàn toàn có thể sử dụng điều này khi thi đấu trên kaggle và nó hoàn toàn hợp lệ.

Data leakage cũng xuất hiện trong cuộc thi này. Một phần test set của cuộc thi trùng với bộ dữ liệu đã có từ trước. Vì thể lệ của cuộc thi là người chơi được quyền dùng dữ liệu bên ngoài nên điều này sẽ khiến cho cuộc thi không còn "vui" nữa.

Những ngày sau đó phần vì tôi bận, phần vì đợi mãi không thấy ban tổ chức thông báo bộ test set mới nên tôi quyết định không tham gia tiếp.

Nguồn: youtube image

Sau tất cả

Tôi sẽ tổng hợp lại một số điều mà mình học được ở lần đầu tiên tham gia cuộc thi trên kaggle:

  • Thi thố trên kaggle rất vui. Nếu biết vui như vậy thì tôi đã tham gia thi kaggle từ lâu rồi. Cảm giác submit, chờ kết quả rồi thấy mình thăng hạng thực sự rất tuyệt.
  • Bạn không cần giỏi như "con nhà người ta" mới có thể thi. Thực tế kaggle có rất nhiều người ở mọi trình độ. Mục discussion cũng có nhiều câu hỏi từ cơ bản như làm thế nào để submit kết quả tới những câu hỏi khó hơn. Vì vậy nếu bạn không biết thì có thể hỏi hoặc làm theo những kernel được upload để học hỏi thêm. Mọi người ở kaggle đều rất nhiệt tình giúp đỡ.
  • Bạn sẽ học được rất nhiều điều mới. Tôi thường xuyên theo dõi mục discussion và kernel của cuộc thi. Từ code và comment ở các mục này sẽ ra được nhiều hướng tiếp cận mới để giúp thăng hạng.
  • Làm tốt feature engineering. Khi đọc các tut trên mạng bạn có thể sẽ bị sa đà quá nhiều vào model engineering mà quên đi feature engineering. Cá nhân tôi cho rằng dữ liệu là điều quan trọng nhất. Model thì ai cũng biết và làm được cả, nhưng dữ liệu chuẩn và sạch thì không phải ai cũng có. Vì vậy hãy tập trung vào dữ liệu trước khi nghĩ tới các mô hình phân tích.
  • Nên bắt đầu từ những mô hình đơn giản trước. Hãy bắt đầu bằng những mô hình đơn giản như logistic trước khi dùng các mô hình mạnh hơn như xgboost hay CNN. Như bên trên bạn cũng thấy rằng mô hình logistic cho ra kết quả tốt không kém gì các mô hình phân loại mạnh cả.
  • Thử, thử và thử. Một trong những điều tôi học được từ khóa học của Andrew Ng đó là ML là quá trình thực nghiệm. Bạn có ý tưởng, bạn code, rồi thử, nếu ý tưởng không hiệu quả thì bạn thử lại một ý tưởng khác, cứ như vậy cho tới khi bạn có được kết quả tốt. Theo tôi không có công thức chung nào cho việc lựa chọn tham số tối ưu cho mô hình, tất cả chỉ là thử thử và thử.
Nguồn: medium

Tôi có đưa một số code của mình lên github. Nó còn khá lộn xộn và nhiều lỗi nhưng nếu thích bạn có thể xem qua.