TPTブログ

テックポート株式会社のブログです。 技術情報や製品・サービス情報、 また未経験社員がデータサイエンティストを 目指す奮闘記など、更新していきます。

▲Kaggleやってみよう【Movie Reviews:映画レビューの感情分析】中篇

f:id:TBT_matsu:20200421115711p:plain
こんにちは!
テービーテックの村松です。

本日は、こちらの続きをやっていこうかと思います。
ds-blog.tbtech.co.jp

前回は、前処理の単語にばらして綺麗に整えるところまで行いました。
《予定している前処理》
・余分な要素の排除
・小さな単位(文字・単語など)にバラバラにする←ここまで
・文字・単語のベクトル化(文字を数字にする)

今日は文字のベクトル化からやっていきましょう。

先にtargetデータの作成
##教師データの抜出し
target = train.Sentiment.values
#5クラス分類用にto-categoricalを使用してOne-Hot
y_target = to_categorical(target)
num_classes = y_target.shape[1]
データの分割
from sklearn.model_selection import train_test_split
x_train, x_val, y_train, y_val =train_test_split(train_sentences,y_target,test_size=0.2,stratify=y_target)
len(x_train), len(x_val), len(y_train), len(y_val)
##結果
(124848, 31212, 124848, 31212)
単語の辞書の作成

レビュー内で使われている単語のダブリを無くして使用されている単語の一覧を作ります。
ついでに単語を多く使っているフレーズの単語数を数えます。

unique_words = set()
len_max = 0
for sent in tqdm(x_train):
  unique_words.update(sent)
  if(len_max<len(sent)):
    len_max = len(sent)
print(len(list(unique_words)))
print(len_max)
##結果
13734
48

x_train内で使われている単語は13,734種類、
一番単語が多いフレーズには48個の単語が使用されていることがわかりました。
これらを使って単語を数値化していきます。

単語を数値に変換

例として1フレーズ現在どんな感じか見ておきます。

x_train[0]
##結果
['a', 'gobbler']

これがどんなふうに数値に変換されていくか確認していきましょう。

from keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer(num_words=len(list(unique_words)))
tokenizer.fit_on_texts(list(x_train))
from keras.preprocessing import sequence
x_train = tokenizer.texts_to_sequences(x_train)
x_val = tokenizer.texts_to_sequences(x_val)
x_test = tokenizer.texts_to_sequences(test_sentences)
x_train[0]
##結果
[2, 8442]

辞書を元に単語ごとに数字が割り振られ、フレーズの単語を割り振られた数値に変換していきます。
今回は'a'は2に、'gobbler’は8422に変換されました。

さて、単語を数値に落とし込むことはできましたが、このままではまだ足りません。
LSTMは入力を同じ長さにしなくてはいけないとのことなので、一番使用単語の多いフレーズに合わせてパディングしていきます。

from keras.preprocessing import sequence
x_train = sequence.pad_sequences(x_train, maxlen=len_max)
x_val = sequence.pad_sequences(x_val, maxlen=len_max)
x_test = sequence.pad_sequences(x_test, maxlen=len_max)
x_train[0]
##結果
array([   0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
          0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
          0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
          0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
          0,    0,    2, 8442], dtype=int32)

ひとまず、以上が前処理となります。
次は整えたデータを使って学習をしていきましょう。

モデルの作成

#モジュールのインポート
from keras.layers import Dense,Dropout,Embedding,LSTM
from keras.callbacks import EarlyStopping
from keras.losses import categorical_crossentropy
from keras.optimizers import Adam
from keras.models import Sequential

過学習防止のためにこちらも用意しておきます。

early_stopping = EarlyStopping(min_delta = 0.001, mode = 'max', monitor='val_acc', patience = 2)
callback = [early_stopping]

モデルの作成。

#KerasのLSTMを使用
model=Sequential()
model.add(Embedding(len(list(unique_words)),300,input_length=len_max))
model.add(LSTM(128,dropout=0.5, recurrent_dropout=0.5,return_sequences=True))
model.add(LSTM(64,dropout=0.5, recurrent_dropout=0.5,return_sequences=False))
model.add(Dense(100,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes,activation='softmax'))
model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.005),metrics=['accuracy'])
model.summary()
##結果
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, 48, 300)           4120200   
_________________________________________________________________
lstm_1 (LSTM)                (None, 48, 128)           219648    
_________________________________________________________________
lstm_2 (LSTM)                (None, 64)                49408     
_________________________________________________________________
dense_1 (Dense)              (None, 100)               6500      
_________________________________________________________________
dropout_1 (Dropout)          (None, 100)               0         
_________________________________________________________________
dense_2 (Dense)              (None, 5)                 505       
=================================================================
Total params: 4,396,261
Trainable params: 4,396,261
Non-trainable params: 0
_________________________________________________________________

学習

history=model.fit(x_train, y_train, 
                  validation_data=(x_val, y_val), 
                  epochs=6, 
                  batch_size=256, 
                  verbose=1, 
                  callbacks=callback)
##結果
Train on 124848 samples, validate on 31212 samples
Epoch 1/6
124848/124848 [==============================] - 378s 3ms/step - loss: 0.7713 - accuracy: 0.6835 - val_loss: 0.8131 - val_accuracy: 0.6645
Epoch 2/6
124848/124848 [==============================] - 380s 3ms/step - loss: 0.7285 - accuracy: 0.6992 - val_loss: 0.8164 - val_accuracy: 0.6686
Epoch 3/6
124848/124848 [==============================] - 375s 3ms/step - loss: 0.7047 - accuracy: 0.7080 - val_loss: 0.8330 - val_accuracy: 0.6625
Epoch 4/6
124848/124848 [==============================] - 375s 3ms/step - loss: 0.6869 - accuracy: 0.7145 - val_loss: 0.8360 - val_accuracy: 0.6633
Epoch 5/6
124848/124848 [==============================] - 377s 3ms/step - loss: 0.6741 - accuracy: 0.7197 - val_loss: 0.8377 - val_accuracy: 0.6687
Epoch 6/6
124848/124848 [==============================] - 380s 3ms/step - loss: 0.6651 - accuracy: 0.7239 - val_loss: 0.8585 - val_accuracy: 0.6665

結果の可視化

#エポック数のカウント
epoch_count = range(1, len(history.history['loss']) + 1)

#精度と学習曲線
import matplotlib.pyplot as plt
plt.plot(epoch_count, history.history['loss'], 'r--')
plt.plot(epoch_count, history.history['val_loss'], 'b-')
plt.legend(['Training Loss', 'Validation Loss'])
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()

f:id:TBT_matsu:20200422115439p:plain
ううむ・・・まだ改良の余地がありそうですね。

ここで終わるのももったいないので、精度を上げるための工夫を凝らしていきましょうか。
私にしては長く3部構成になりますが、最後までお付き合いいただければ幸いです。