前回はIrisデータセットを使ってTensorFlowのTutorialをやってみました。
www.pytry3g.com
今回もTensorFlowのTutorialを参考にしながら、テキスト分類をしてみます。
今回やるテキスト分類の目的は与えられたTweetがサッカー日本代表に関するものなのか、正しく分類することです。データは自分で集めました。
モデルの生成と学習にはTensorFlowの高レベルAPIのtf.kerasを使います。
環境
※MeCabをWindowsにインストールするには
https://www.pytry3g.com/entry/2018/04/24/143934#Windows
テキストの前処理のところでJapaneseTextEncoderを使います。
詳細はこちら。
関連リンク
Classify movie reviews: binary classification
ロシアW杯 日本代表について雑談ができる対話エージェントを作る(データ収集編)①
日本語のテキストコーパスから辞書を作るライブラリを作りたい
データの用意
まずデータを集めます。データの集め方はこの記事と同じ方法で集めます。
今回は与えられたTweetがサッカー日本代表についてのものなのかを分類することが目的なので、データを集める際にサッカー日本代表に関するキーワードを含むツイートを(positive)に、キーワードを含まないツイートを(negative)に設定します。
集めたデータは以下のようにサッカー日本代表に関するものはpositive.txtに、そうではないものはnegative.txtに改行区切りで書き込んでいます。
positive.txt
このメンツなら原口だな。
コロンビアってサッカーつよいんですか
そうなんか今日日本と試合するんしょ?
negative.txt
串カツよこせえええええええ
今は串カツとかより地震怖い😢
さらさら金髪マン裏山。
datasets.py
集めたデータは全部で742です。
この集めたデータから学習用データとテストデータを作り、JapaneseTextEncoderを使って辞書を作ります。
import codecs
from sklearn.model_selection import train_test_split
from text_encoder import JapaneseTextEncoder
def load_data():
dataset = []
with codecs.open("positive.txt", "r", "utf-8") as f:
positive = f.read().splitlines()
with codecs.open("negative.txt", "r", "utf-8") as f:
negative = f.read().splitlines()
for sentence in positive:
dataset.append((sentence, 1))
for sentence in negative:
dataset.append((sentence, 0))
sentences, labels = list(zip(*dataset))
sentences, labels = list(sentences), list(labels)
encoder = JapaneseTextEncoder(
sentences,
min_occurrences=1,
append_eos=False
)
dataset = [encoder.encode(sentence) for sentence in sentences]
train_x, test_x, train_t, test_t = train_test_split(dataset, labels, test_size=0.1)
train_data, test_data = (train_x, train_t), (test_x, test_t)
return train_data, test_data, encoder
データの中身
>>> train_x[0]
[3624, 62, 14, 97, 83, 52, 3625, 98, 844, 844, 3626, 2775, 13, 14, 253, 3101, 102, 14, 1236, 32, 836, 523, 5, 3627, 3345, 74, 75, 102, 3628, 423, 105, 2403, 108, 50, 14, 872, 1092, 13, 855, 3628, 423, 105, 63, 14, 223, 3347, 12, 2247, 236, 294, 140, 14]
JapaneseTextEncoderのdecodeを使えば、テキストに変換できます。
>>> encoder.decode(train_x[0])
帰らな。分かってるけど立ち上がれない・・😭💦帰ります。これ書いたら。けーじくんと林先生を見たらほんの少しだけ落ち着きました。ありがとうございます✨ほんの少しだけって。本当に落ち着いて💦大丈夫やから。
ベクトル変換
用意したデータは整数が集まったリストになっています。ニューラルネットワークにはtensor(ベクトル?)を渡す必要があるので、データをベクトルに変換します。
やり方としては主に2つの方法があります。
- データをone hot vectorに変換する方法。これはベクトルの値を0もしくは1で表現するものです。例えば、[2, 3]というリストがあった場合、n次元のベクトルの要素はインデックス2と3が1の値となり、それ以外はすべて0の値となります(※nはデータセットの単語の数)。このやり方だと、データセットの単語の数が増えれば増えるほど次元が大きくなり、学習に時間がかかりメモリに高負荷がかかってしまいます。
- もう1つのやり方はpaddingです。この方法を使うと1の方法と比べて次元を小さくすることができます。
今回はTutorialに従って、方法2でベクトル変換します。
import datasets
import numpy as np
import tensorflow as tf
from tensorflow import keras
from reserved_tokens import PADDING_INDEX
(train_x, train_t), (test_x, test_t), encoder = datasets.load_data()
train_x = keras.preprocessing.sequence.pad_sequences(train_x,
value=PADDING_INDEX,
padding="post",
maxlen=128)
test_x = keras.preprocessing.sequence.pad_sequences(test_x,
value=PADDING_INDEX,
padding="post",
maxlen=128)
このプログラムはまず必要なライブラリをインポートします。(※reserved_tokensについてはこちら。)
学習
モデルを作る
n_vocab = len(encoder.vocab)
model = keras.Sequential()
model.add(keras.layers.Embedding(n_vocab, 16))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation=tf.nn.relu))
model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))
学習の設定
model.compile(optimizer=tf.train.AdamOptimizer(),
loss="binary_crossentropy",
metrics=["accuracy"])
学習する
x_val, y_val = train_x[1000:], train_t[1000:]
partial_x_train, partial_y_train = train_x[:1000], train_t[:1000]
history = model.fit(
partial_x_train,
partial_y_train,
epochs=40,
batch_size=50,
validation_data=(x_val, y_val),
verbose=1
)
結果
results = model.evaluate(test_x, test_t)
print(results)
結果はこんな感じ。accuracyは73%となりました。
[0.5745982763751242, 0.7315436277613544]
考察
学習結果をみるとうまくいきませんでした。
うまくいかなかった原因として考えられるのは
- 学習データが少ない
- もう少し前処理をする、出てくる単語を全て辞書に登録していますが、あまり出てこない単語は取り除くとか、、、
- 意味のなさそうな単語を取り除く、例えば、助詞や助動詞
import datasets
import numpy as np
import tensorflow as tf
from tensorflow import keras
from reserved_tokens import PADDING_INDEX
(train_x, train_t), (test_x, test_t), encoder = datasets.load_data()
train_x = keras.preprocessing.sequence.pad_sequences(train_x,
value=PADDING_INDEX,
padding="post",
maxlen=128)
test_x = keras.preprocessing.sequence.pad_sequences(test_x,
value=PADDING_INDEX,
padding="post",
maxlen=128)
n_vocab = len(encoder.vocab)
model = keras.Sequential()
model.add(keras.layers.Embedding(n_vocab, 16))
model.add(keras.layers.GlobalAveragePooling1D())
model.add(keras.layers.Dense(16, activation=tf.nn.relu))
model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))
model.compile(optimizer=tf.train.AdamOptimizer(),
loss="binary_crossentropy",
metrics=["accuracy"])
x_val, y_val = train_x[1000:], train_t[1000:]
partial_x_train, partial_y_train = train_x[:1000], train_t[:1000]
history = model.fit(
partial_x_train,
partial_y_train,
epochs=40,
batch_size=50,
validation_data=(x_val, y_val),
verbose=1
)
results = model.evaluate(test_x, test_t)
print(results)
history_dict = history.history
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
plt.clf()
acc_values = history_dict['acc']
val_acc_values = history_dict['val_acc']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()