Phân tích cảm xúc bình luận với mạng LSTM trong Keras

Hieu Nguyen
4 min readFeb 5, 2020

--

Hướng dẫn sử dụng mạng LSTM trong việc xác định trạng thái tích cực và tiêu cực của một bình luận bằng tiếng anh trên IDMB.

Trước tiên, mình hoàn toàn mù mờ về Python cũng như LSTM. Toàn bộ các kiến thức của mình chỉ ở phần ngọn vì tự tìm hiểu. Bài viết này cũng là sản phẩm của quá trình tự tìm tòi và nghiên cứu được ghi lại nhằm làm thư viện cho các nghiên cứu sau này. Do đó, toàn bộ các đoạn code ở bên dưới có tham khảo tại đây

Bộ dataset mình lấy từ Kaggle là bộ IDMB review gồm 25K đánh giá của người dùng.

Load thư viện

Chúng ta sẽ cần một vài thư viện cơ bản như pandas, numpy, bộ sklearn và keras. Mình không nhắc lại cách cài đặt các thư viện này nữa vì google có hết.

import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
from sklearn.feature_extraction.text import CountVectorizer
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
from keras.models import Sequential
from keras.layers import Dense, Embedding, LSTM, SpatialDropout1D
from sklearn.model_selection import train_test_split
from keras.utils.np_utils import to_categorical
import re

Load dữ liệu

Trong bộ dữ liệu các bạn download về sẽ có nhiều tập tin. Trong đó các bạn để ý tập tin train.csv.

train = pd.read_csv("../input/imdb-sentiments/train.csv")

Tiền xử lý dữ liệu

Mình sẽ đổi toàn bộ dữ liệu train về dạng chữ thường. Đồng thời loại bỏ các ký tự đặc biệt.

train['text'] = train['text'].apply(lambda x: x.lower())
train['text'] = train['text'].apply((lambda x: re.sub('[^a-zA-z0-9\s]','',x)))
for idx,row in train.iterrows():
row[0] = row[0].replace('rt',' ')

Tiếp theo sử dụng Tokenizer để tách từ

max_fatures = 2000
tokenizer = Tokenizer(num_words=max_fatures, split=' ')
tokenizer.fit_on_texts(train['text'].values)
X = tokenizer.texts_to_sequences(train['text'].values)
X = pad_sequences(X)

Xây dựng mạng LSTM đơn giản

lstm_out = 196model = Sequential()
model.add(Embedding(max_fatures, embed_dim,input_length = X.shape[1]))
model.add(SpatialDropout1D(0.4))
model.add(LSTM(lstm_out, dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(2,activation='softmax'))
model.compile(loss = 'categorical_crossentropy', optimizer='adam',metrics = ['accuracy'])
print(model.summary())

Chuẩn bị dữ liệu đầu vào

Ta sử dụng train_test_split để thực hiện chia bộ dữ liệu làm hai phần: train — test với tỉ lệ 33%

Y = pd.get_dummies(train['sentiment']).values
X_train, X_test, Y_train, Y_test = train_test_split(X,Y, test_size = 0.33, random_state = 42)
print(X_train.shape,Y_train.shape)
print(X_test.shape,Y_test.shape)

Huấn luyện

batch_size = 32
model.fit(X_train, Y_train, epochs = 4, batch_size=batch_size, verbose = 2)
Kết quả sau ghi chạy bước huấn luyện

Một vài điểm cần trao đổi thêm:

  • Tham số của mạng LSTM: Nếu các bạn để ý trong hàm model.fit bên trên mình sử dụng batch_size = 32. Việc thay đổi tham số này sẽ cho kết quả như thế nào thì mình sẽ kiểm chứng sau.
  • Thời gian trung bình của một lần epochs là hơn 1900s: Như vậy là rất lâu. Mình cũng cần thêm nghiên cứu để giảm con số này.
  • Tỉ lệ Acc mới chỉ dừng lại ở mức 78% — 85%: Đây là tỉ lệ cần phải cải thiện thêm.

Giả sử bạn có tập huấn luyện gồm 64.000 images, lựa chọn batch size có giá trị là 64 images. Đồng nghĩa: mỗi lần cập nhật trọng số, bạn sử dụng 64 images. Khi đó, bạn mất 64.000/64 = 1000 iterations để có thể duyệt qua hết được tập huấn luyện (để hoàn thành 1 epoch). Các mẫu được lựa chọn ngẫu nhiên trong mỗi lần cập nhật trọng số. Giá trị của epoch và batch size do lập trình viên lựa chọn. — samaku1996vn từ diễn đàn MLCB

Đánh giá lại mô hình LSTM

validation_size = 1500X_validate = X_test[-validation_size:]
Y_validate = Y_test[-validation_size:]
X_test = X_test[:-validation_size]
Y_test = Y_test[:-validation_size]
score,acc = model.evaluate(X_test, Y_test, verbose = 2, batch_size = batch_size)
print("score: %.2f" % (score))
print("acc: %.2f" % (acc))
pos_cnt, neg_cnt, pos_correct, neg_correct = 0, 0, 0, 0
for x in range(len(X_validate)):

result = model.predict(X_validate[x].reshape(1,X_test.shape[1]),batch_size=1,verbose = 2)[0]

if np.argmax(result) == np.argmax(Y_validate[x]):
if np.argmax(Y_validate[x]) == 0:
neg_correct += 1
else:
pos_correct += 1

if np.argmax(Y_validate[x]) == 0:
neg_cnt += 1
else:
pos_cnt += 1
print("pos_acc", pos_correct/pos_cnt*100, "%")
print("neg_acc", neg_correct/neg_cnt*100, "%")

Tổng kết

Bài này chỉ dừng lại ở mức cơ bản mới tiếp cận về LSTM ở góc độ kỹ thuật với ngôn ngữ Python. Còn lý thuyết về LSTM mời các bạn google nhé.

Bài này làm cơ sở để nghiên cứu sâu hơn về LSTM phục vụ các bài toán xử lý ngôn ngữ tự nhiên, có thể là ngôn ngữ tiếng việt trong tương lai và bài toán phân loại URL mà mình đang muốn áp dụng.

--

--

Hieu Nguyen
Hieu Nguyen

Written by Hieu Nguyen

Đang nghiên cứu về Python và Machine Learning. Rất mong được trao đổi thêm với các bạn cùng lĩnh vực

No responses yet