NLP(自然言語処理):pad_sequencesとTimeseriesGeneratorの初歩

NLP(自然言語処理):kerasとLSTMを用いて学習と予測を行うで,Kerasとmodel.fit()の使い方を学びました。

ここでは,pad_sequencesとTimeseriesGeneratorについて学びます。

pad_sequences

自然言語処理では入力される文はさまざまな長さになります。しかし,大量のデータを扱うためにはデータの長さがそろっていた方が効率よく処理できます。

そのため,リストに 0 を挿入して大きさをそろえます。

[[1 2 3]
 [1 2 3 4 5 6]
 [1 2 3 4]]

--->

[[1 2 3 0 0 0 0 0 0 0]
 [1 2 3 4 5 6 0 0 0 0]
 [1 2 3 4 0 0 0 0 0 0]]

pad_sequences()を用いてパディングします。

例えば,さまざまな長さの文をベクトル化したリストtextsを,文ごとの長さが30単語であるリストにそろえる場合,次のように書きます。

texts = sequence.pad_sequences(texts, maxlen=30, padding="post", truncating="post")

文章をベクトル化したリストtextsをパディングし,再びtextsに格納します。

texts =
[[1, 407, 813, 319, 432, 1697, 666, 4, 985, 5, 563, 3, 613, 5,
  735, 3, 2599, 1, 525, 4, 526, 3622, 1, 814, 986, 2],
 [1, 117, 338, 18, 2600, 3, 892, 2072, 815, 339, 2],
 [1, 339, 20, 893, 10, 5, 167, 36, 1, 319, 6, 3623, 36, 1, 67, 320, 2]

 ......

]

pad_sequences -->

texts =
[[   1  407  813  319  432 1697  666    4  985    5  563    3  613    5
   735    3 2599    1  525    4  526 3622    1  814  986    2    0    0
     0    0]
 [   1  117  338   18 2600    3  892 2072  815  339    2    0    0    0
     0    0    0    0    0    0    0    0    0    0    0    0    0    0
     0    0]
 [   1  339   20  893   10    5  167   36    1  319    6 3623   36    1
    67  320    2    0    0    0    0    0    0    0    0    0    0    0
     0    0]

 ......

]

maxlen=30はデータの数が30個であるという意味です。単語の数が30個以上の場合,後ろの単語がカットされます。

padding="post"は0をデータの後ろに挿入します。preを指定するとデータの前に0を挿入します。

[1 2 3 4]

padding="pre"  --> [0 0 0 1 2 3 4]

padding="post" --> [1 2 3 4 0 0 0]

truncating="post"は,単語数が30を超えた場合に後ろの方をカットします。

TimeseriesGenerator

TimeseriesGeneratorを用いると,LSTMで必要な時系列データを簡単に用意することができます。

seq_length = 5
for line in range(len(texts)):
    dataset = TimeseriesGenerator(texts[line], texts[line], length=seq_length, batch_size=1)

len(texts)は文章の行数を表します。for文で行ごとに時系列データを作成します。

texts[line]が2つありますが,一つは入力データ,もう一つは答えのデータです。通常は,同じ文から入力と答えの両方を抽出するはずです。seq_length=5はタイムステップの長さです。今回は,5つの連続した単語から次の単語を予測するので,タイムステップの長さは5です。

時系列データと答えがdatasetに格納されます。

texts = [1 2 3 4 5 6 7 8 9 10 ......]

X = [[1 2 3 4 5]      Y = [[6]
     [2 3 4 5 6]           [7]
     [3 4 5 6 7]           [8]
          ......]          ......]

batch_sizeは次のようになります。

texts = [1 2 3 4 5 6 7 8 9 10 ......]

batch_size=1  -->

[[1 2 3 4 5]
 [2 3 4 5 6]
 [3 4 5 6 7] ......]

batch_size=2  -->

[[1 2 3 4 5]
 [3 4 5 6 7]
 [5 6 7 8 9] ......]

batch_size=3  -->

[[1 2 3 4 5]
 [3 4 5 6 7]
 [6 7 8 9 10] ......]
    for batch in dataset:
        X, Y = batch
        x.extend(X[0])
        y.extend(Y)

for文を用いて,datasetから入力Xと答えYを取り出し,リストxyに一つずつ追加します。

Xは2次元のリストなので,X[0]として1次元のリストを追加します。

X = [[1 2 3 4 5]]
X[0]= [1 2 3 4 5]

結果,xはタイムステップが並んだ一次元のリストになります。

x = [1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 ......]
x = np.reshape(x,(int(len(x)/5),5,1))

LSTMの入力値は(バッチサイズ,タイムステップ,入力次数)の形状をとります。

np.reshapeでxを3階のテンソルに変換します。

x = [[[1]
      [2]
      [3]
      [4]
      [5]]
     [[2]
      [3]
      [4]
      [5]
      [6]]
     [[3]
      [4]
      [5]
      [6]
      [7]] ......]

これで,LSTMに入力できる形状になりました。