[NLP Series #2] Sử dụng Gensim Word2Vec để dạy máy tính “ngửi” văn (phân loại văn bản)

Trong bài này chúng ta sẽ sử dụng một phương pháp Word Embedding mới là Gensim Word2Vec để tạo vector đặc trưng cho các từ và train model “ngửi văn” (phân loại văn bản) cho máy tính nhé.

Bài này là bài thứ 2 trong Series về NLP, bài trước mình đã chia sẻ cách sử dụng TFIDF để làm biểu diễn văn bản tại đây.

Chắc hẳn ngày còn bé anh em đã đọc truyện cười dân gian Ngửi văn chứ? Nếu chưa đọc thì anh em xem ảnh bên dưới mình có trích dẫn lại :

Nói vui vậy còn chúng ta sẽ làm một bài toán là đưa một câu văn bản vào, máy tính phải nhận biết được đây là tin kinh tế, giáo dục hay y học (mình tạm train 3 category này làm sample nha).

Trong bài này các bạn sẽ học được các kỹ thuật:

  • Tiền xử lý văn bản đầu vào: tách câu, xóa dấu câu,…
  • Train model Word2vec và tạo Embedding Vector cho các câu dữ liệu
  • Train model LSTM để classify văn bản

Ưu điểm của model Word2vec gồm:

  • Tốc độ khá nhanh
  • Có biểu diễn được thông tin về hướng của sự tương đồng giữa các từ cũng được lưu lại trong mô hình

Bạn nào cần tìm hiểu sâu hơn về món này thì tham khảo link này nhé!

Go ahead man!

Phần 1 – Chuẩn bị nguyên vật liệu cho bài toán

Đầu tiên cứ phải mã nguồn cho nó tường minh cái đã nhở. Các bạn clone mã nguồn về bằng lệnh git quen thuộc:

git clone https://github.com/thangnch/MiAI_Word2Vec_Demo .Code language: PHP (php)

Đợi chạy xong sẽ thấy thêm thư mục MiAI_Word2Vec_Demo, đó chính là thư mục chứa mã nguồn nhé.

Tiếp theo để chạy được thì các bạn cài đặt các thư viện cần thiết. Chuyển vào thư mục MiAI_Word2Vec_Demo sau đó chạy lệnh:

pip install -r setup.txtCode language: CSS (css)

Sau khi in ra màn hình cả đống chữ thì các bạn sẽ cài đặt thành công các thư viện như tensorflow, keras, gensim…

OK! Giờ để tạm mã nguồn đó, chưa sờ đến vội. Chúng ta cùng đi qua thuật toán, phương pháp triển khai của bài này đã.

Phần 2 – Phương pháp triển khai bài toán

Bài DL nào cũng gồm 02 phase quen thuộc là Train và Test. Bài này cũng không ngoại lệ:

Quá trình train
  • Bước 1: Ta chuẩn bị dữ liệu văn bản của 03 lĩnh vực: Kinh tế, Giáo dục và Y tế. Các bạn nhìn vào thư mục data sẽ thấy rõ các file văn bản đó. Đây là mình trích 1 phần nhỏ dữ liệu để ví dụ, toàn bộ dữ liệu văn bản các bạn có thể tìm trong Thư viện Mì AI: https://miai.vn/thu-vien-mi-ai (các bạn xem video clip để biết cách tải về nha)
  • Bước 2: Ta tiền xử lý văn bản đầu vào, chia thành các câu văn bản riêng biệt.
  • Bước 3: Thực hiện tách từ (tokenizer) và train một model Word2Vec (train từ đầu, ko dùng pretrain) dựa trên dữ liệu đầu vào. Mục đích là tạo ra các vector đặc trưng cho từng word trong văn bản của chúng ta.
  • Bước 4: Nhét dữ liệu input vào mạng LSTM đơn giản để train với các lưu ý:
    • Đầu vào : Lớp Embedding với weights chính là weights của model Word2vec vừa train. Mục đích của lớp này là chuyển từ văn bản đầu vào đã được tokenizer (nói cho đơn giản, thực ra là văn bản đầu vào đã được thay bằng word index) thành một embedding matrix.
    • Đầu ra: Lớp Softmax thần thánh với đầu ra là 3 class chúng ta cần.
Quá trình test:
  • Để đơn giản chúng ta sẽ evaluate trên tập test luôn để xem model của chúng ta predict như thế nào nhé.
  • Các bạn hoàn toàn có thể sửa lại mã nguồn để predict một câu bất kì.

Tạm thế, có gì chưa hiểu các bạn cứ post lên Group trao đổi, chia sẻ: https://facebook.com/groups/miaigroup để cùng giao lưu, hỏi đáp nhé.

Phần 3 – Triển khai thuật toán phân loại văn bản

Bây giờ, chúng ta sẽ cùng nhau điểm qua một số đoạn mã nguồn chính trong chương trình nhé. Các bước ở phần này sẽ bám theo flow đã nêu ra tại phần trên nên các bạn cần đảm bảo hiểm rõ các bước bên trên trước khi bắt đầu.

phân loại văn bản
Bước 1. Load dữ liệu từ các file văn bản trong thư mục data

Đoạn này chúng ta vừa load, vừa tiền xử lý văn bản, vừa tách câu và đồng thời sinh ra labels cho các câu.

def preProcess(sentences):

    text = [re.sub(r'([^\s\w]|_)+', '', sentence) for sentence in sentences if sentence!='']
    text = [sentence.lower().strip() for sentence in text]
    return text

def loadData(data_folder):

    texts = []
    labels = []
    #
    for folder in listdir(data_folder):
        #
        if folder != ".DS_Store":
            print("Load cat: ",folder)
            for file in listdir(data_folder + sep + folder):
                #
                if file!=".DS_Store":
                    print("Load file: ", file)
                    with open(data_folder + sep + folder + sep +  file, 'r', encoding="utf-8") as f:
                        all_of_it = f.read()
                        sentences  = all_of_it.split('.')

                        # Remove garbage
                        sentences = preProcess(sentences)

                        texts = texts + sentences
                        label = [folder for _ in sentences]
                        labels = labels + label
                        del all_of_it, sentences


    return texts, labelsCode language: PHP (php)

Sau 2 đoạn chương trình này chúng ta sẽ có 1 list các câu kèm với nhãn câu đó thuộc thể loại gì. Tiếp nào!

Bước 2. Tokenizer các câu văn bản

Bước này sẽ chuyển các câu văn bản từ dạng list of string về list of numbers. Ví dụ sẽ chuyển câu:

 ['hôm nay chúng ta học xử lý ngôn ngữ tự nhiên']Code language: JSON / JSON with Comments (json)

thành (ví dụ dummy thôi nhé, ko chuẩn đâu haha )

[ 20 1 22 31 34 22 1 12 67 43 22 14...] Code language: JSON / JSON with Comments (json)

def txtTokenizer(texts):
    tokenizer = Tokenizer(num_words=500)
    # fit the tokenizer on our text
    tokenizer.fit_on_texts([text.split() for text in texts])

    # get all words that the tokenizer knows
    word_index = tokenizer.word_index
    #return tokenizer, word_index

    tokenizer, word_index = txtTokenizer(texts)

    # put the tokens in a matrix
    X = tokenizer.texts_to_sequences(texts)
    X = pad_sequences(X)

    # prepare the labels
    y = pd.get_dummies(labels)Code language: PHP (php)

Với nhãn đầu ra thì ta dùng hàm get_dummies của pandas để chuyển từ dạng [‘Kinh tế’,’Giáo dục’,’Kinh tế’] thành dạng one-hot vector.

Rồi, bây giờ tiếp tục sang bước 3!

Bước 3 – Train model Word2vec

Đoạn này khá đơn giản do có thư viện của Gensim rồi. Ta chỉ cần làm một đoạn lệnh đơn giản sau:

word_model = gensim.models.Word2Vec(texts, size=300, min_count=1, iter=10)
word_model.save(data_folder + sep + "word_model.save")Code language: JavaScript (javascript)

Sau khi train xong, ta có thể kiểm tra quá trình train bằng lệnh sau để tìm các từ liên quan với từ ‘cơm’ xem sao:

print(word_model.wv.most_similar('cơm'))Code language: PHP (php)
[('nấu', 0.7218972444534302), ('cháo', 0.6976884603500366), ('nướng', 0.6886948347091675), ('bún', 0.6797002553939819), ('phở', 0.6455461978912354), ('riêu', 0.6107968091964722), ('nát', 0.6087987422943115), ('mì', 0.607367992401123), ('xào', 0.6070189476013184), ('rán', 0.587298572063446)]Code language: JSON / JSON with Comments (json)

Haha, in ra chuẩn phết, ‘nấu’, ‘cháo’…. toàn liên quan đến cơm quá còn gì!

Bước 4 – Train model phân loại văn bản

Rồi, các phần đã xong, bây giờ đơn giản rồi, ghép nối và ném vào model của chúng ta để train thôi. Ở đây mình dùng một model LSTM đơn giản thôi vì thấy cũng khá hiệu quả rồi.

model = Sequential()
    model.add(Embedding(len(word_model.wv.vocab)+1,300,input_length=X.shape[1],weights=[embedding_matrix],trainable=False))
model.add(LSTM(300,return_sequences=False))
model.add(Dense(y.shape[1],activation="softmax"))Code language: PHP (php)
phân loại văn bản

Lượng dữ liệu khá lớn nên sau khi train 1 epochs mình đã đạt được acc và loss khá tốt rồi.

phân loại văn bản

Cuối cùng chúng ta eval trên tập test xem sao:

model.evaluate(X_test,y_test)Code language: CSS (css)

Kết quả đạt accurary 0.77 – ổn rồi các bạn ơi!

Nếu như mình train tiếp, khoảng 5 epochs nữa thì train accuracy lên được 0.95! Wow.

text classify

Và nếu tiếp tục evaluate trên tập test ta sẽ có kết quả là 0.95 accuracy luôn. Quá ổn áp!

text classification

Các bạn có thể tự chạy file train_model.py để kiểm tra kết quả hoặc sửa lại để predict thử 1 vài kết quả trong tập test xem :D. Ngoài ra các bạn có thể thêm bớt dữ liệu, thay đổi kiến trúc mạng để xem kết quả thay đổi ra sao nhé!

Ah còn vấn đề là bài này còn nâng cao được accurary ko ? Câu trả lời là có! Các bạn có thể áp dụng thêm các biện pháp tiền xử lý (bỏ chữ số, xử lý từ viết sai, dính từ…) hoặc thay bộ tokenizer cho phù hợp với tiếng Việt nhé (bật mí là trong các bài sau mình cũng sẽ guide qua phần này).

Còn bây giờ, mình xin tạm dừng bài này ở đây. Hôm nay chúng ta đã học được kha khá về Word2Vec rồi. Trong các bài tới sẽ còn nhiều món như: Doc2Vec, Bert…. Hẹn gặp lại các bạn!

Hãy join cùng cộng đồng Mì AI nhé!

Fanpage: http://facebook.com/miaiblog
Group trao đổi, chia sẻ: https://www.facebook.com/groups/miaigroup
Website: https://miai.vn
Youtube: http://bit.ly/miaiyoutube

Related Post

5 Replies to “[NLP Series #2] Sử dụng Gensim Word2Vec để dạy máy tính “ngửi” văn (phân loại văn bản)”

  1. Ad demo phương pháp Lexicon cho bài toán Tiếng Việt đi AD. Hóng lắm luôn :))

  2. Hi anh Thắng,
    em train xong model và viết một hàm để predict chủ đề của một đoạn text nhập vào như sau:
    def predict(inputStr):
    texts = []
    sentences = inputStr.split(‘.’)
    sentences = preProcess(sentences)
    texts = texts + sentences
    tokenizer, word_index = txtTokenizer(texts)
    X = tokenizer.texts_to_sequences(texts)
    X = pad_sequences(X, maxlen=maxlen)
    prediction = model.predict(X)
    for row in prediction:
    print(row, ‘ => ‘, cats[row.argmax()])

    #load model:
    model = load_model(‘./data/predict_model.save’)
    with open(‘./data/data.pkl’, ‘rb’) as fpt:
    X, y, labels = pickle.load(fpt)
    maxlen = X.shape[1]
    cats = [‘Economy’, ‘Education’, ‘Medical’]

    #sau đó gọi hàm predict một đoạn văn bản trong từng file ở các thư mục Economy, Education, Medical, thì nó trả về category của các câu trong các đoạn văn đó đa số là ‘Education’
    Em đang băn khoăn là hàm predict như trên có sai gì không?
    biến cats gồm các chủ đề sắp xếp alphabe như vậy có đúng không? (trong hàm loadData anh viết thì gọi os.listdir cho nên em nghĩ nó trả về theo thứ tự alphabe)

Leave a Reply

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