NLP(自然言語処理)訓練データと検証データを分ける
前回の投稿,メモリに乗らない大きなデータを分割して訓練するで,ジェネレーター関数を用いる方法を学びました。ここでは,訓練データと検証データを分ける方法を学習します。
訓練データと検証データ
本来,機械学習は訓練されたモデルを用いて,未知のデータに対する適切な予測結果を生成することが目的です。そのため,一般的には用意されたデータの一部を訓練に使用し,残りのデータを検証のために使用します。
検証用データはモデルにとって未知のデータであり,訓練されたモデルがそれをどの程度正確に予測できるかをval_loss
で示します。
検証データの設定
テキストの前処理やジェネレーター関数については,前回の記事を参照してください。
train_val_rate = 0.8
ここでは,単純な方法で訓練データと検証データを分けます。読み込んだテキストの最初の80パーセントを訓練データに使用し,残りの20パーセントを検証データに使用します。
train_start = 0
train_end = round(len(texts) * train_val_rate)
テキストは6672行の文で構成され,およそ640KBです。round()
は小数点以下を切り捨てて値を整数にします。したがって,round(len(texts) * train_val_rate)=5337
です。テキストの0から5337行を訓練データにします。
val_start = train_end + 1
val_end = len(texts)
一方で,検証データは,テキストの5338から6672行までです。
model.fit(
train_generator(train_start, train_end),
steps_per_epoch=(train_end - train_start) // batch_size,
validation_data=train_generator(val_start, val_end),
validation_steps=(val_end - val_start) // batch_size,
epochs=100,
verbose=1)
訓練を行います。train_generator(train_start, train_end)
は0から5337行を訓練データとして呼び出します。
また,validation_data=train_generator(val_start, val_end)
は5338から6672行を検証データとして呼び出します。
batch_size=100
なので,(train_end - train_start) // batch_size = (5337-0)//100 = 53
です。これはfit()
がジェネレーター関数を53回呼び出すことを意味しています。
一方で,validation_steps=(val_end - val_start) // batch_size = (6672-5338)//100 = 13
です。ジェネレーター関数は13回呼び出されます。
訓練を行います。
Epoch 100/100
53/53 - 5s - loss: 3.2926 - accuracy: 0.4244 - val_loss: 7.3059 - val_accuracy: 0.2865
新しい値であるval_loss
とval_accuracy
が表示されました。val_loss
は検証データの損失を表し,val_accuracy
は検証データの精度を表します。
一方で,loss
は訓練データの損失を表し,accuracy
は訓練データの精度を表します。これは訓練データをそのまま用いて予測を行った場合の精度です。
残念ながら,検証データの精度はおよそ28パーセントであり,未知のデータを予測する性能は低いことが分かります。
全体のコードを示します。
import numpy as np
import sys
import io
import os
import stanza
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
from tensorflow import keras
from keras.models import Sequential
from keras.layers import Dense, LSTM
from keras.optimizers import Adam
from keras.utils import np_utils
from keras.preprocessing import sequence
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import TimeseriesGenerator
#read the text
with io.open('articles_u.txt', encoding='utf-8') as f:
text = f.read()
texts = text.replace('eos', 'eos\n').splitlines()
#make the dictionary
tokenizer = Tokenizer()
tokenizer.fit_on_texts(texts)
char_indices = tokenizer.word_index
#make the inverted dictionary
indices_char = dict([(value, key) for (key, value) in char_indices.items()])
np.save('voa_char_indices', char_indices)
np.save('voa_indices_char', indices_char)
#vectorization
texts = tokenizer.texts_to_sequences(texts)
texts = sequence.pad_sequences(texts, maxlen=30, padding="pre", truncating="post")
#make dataset
batch_size = 100
seq_length = 5
def train_generator(start, end):
while True:
for step in range((end - start) // batch_size):
x = []
y = []
for line in range(batch_size):
dataset = TimeseriesGenerator(
texts[start+step*batch_size+line],
texts[start+step*batch_size+line],
length=seq_length,
batch_size=1)
for batch in dataset:
X, Y = batch
x.extend(X[0])
y.extend(Y)
x = np.reshape(x,(25*batch_size,seq_length,1))
x = x / float(len(char_indices)+1)
y = np_utils.to_categorical(y, len(char_indices)+1)
yield x, y
#build the model
print('build the model....')
model = Sequential()
model.add(LSTM(128, input_shape=(seq_length, 3)))
model.add(Dense(len(char_indices)+1, activation='softmax'))
optimizer = Adam(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy'])
#training
train_val_rate = 0.8
train_start = 0
train_end = round(len(texts) * train_val_rate)
val_start = train_end + 1
val_end = len(texts)
model.fit(
train_generator(train_start, train_end),
steps_per_epoch=(train_end - train_start) // batch_size,
validation_data=train_generator(val_start, val_end),
validation_steps=(val_end - val_start) // batch_size,
epochs=100,
verbose=2)
#callbacks=[early_stopping, reduce_lr])
#save the model
model.save('u_model.h5')
SNSでシェア