データナード

機械学習と自然言語処理についての備忘録 (旧ナード戦隊データマン)

keras-laserの修正: ミニバッチ戦略の変更

ミニバッチとは、訓練イテレーションごとに全データの中から部分集合を抽出することです。ミニバッチ戦略は、そのミニバッチの生成方法に対する戦略です。

概要

問題: 文ベクトルに0.0が大量に含まれる上に、0.0を任意の負数で置き換えても、置き換えたあとの負数がmax_poolingで選ばれる。

仮説: 出力や入力に含まれるパディングのための0が作用した結果として起こる。

解決策: ミニバッチ戦略の変更。

適用したミニバッチ戦略

  1. 出力長ごとにデータをまとめる。
  2. まとめたデータをバッチサイズごとに切り出す。
  3. 切り出したデータを、入力長の長い順にソートする。
  4. 入力Embeddingにmask_zero=Trueをつける。

Note: ソート時には、入出力の整合性を保ったままソートを行う必要があります。このために、argsortによってソート後のインデックスのみを取得することができます。

コードの主要部分

def data_generate_phase2_grp(gen_p1, max_bs=1000, bs=50):
    while True:
        count = 0
        data = {}
        for i, (s1ids, s2ids, lid) in enumerate(gen_p1):
            y = s2ids[1:]
            ylen = len(s2ids[1:])
            if ylen not in data:
                data[ylen] = [[[], [], []], []]
            data[ylen][1].append(y)
            data[ylen][0][0].append(s1ids)
            data[ylen][0][1].append(s2ids[:-1])
            data[ylen][0][2].append([lid for _ in range(len(s2ids[:-1]))])
            count += 1

            if count == max_bs:
                for ylen, bch in data.items():
                    if any(len(x) <= 0 for x in bch):
                        continue
                    X1, X2, X3 = bch[0]
                    Y = bch[1]
                    assert len(X1) == len(X2)
                    assert len(X1) == len(X3)
                    assert len(X1) == len(Y)
                    spl_size = len(X1)//bs
                    if spl_size <= 0:
                        spl_size = 1
                    X1_bs = np.array_split(X1, spl_size)
                    X2_bs = np.array_split(X2, spl_size)
                    X3_bs = np.array_split(X3, spl_size)
                    Y_bs = np.array_split(np.array(Y), spl_size)
                    assert len(X1_bs) == len(X2_bs)
                    assert len(X1_bs) == len(X3_bs)
                    assert len(X1_bs) == len(Y_bs)
                    for X1_b, X2_b, X3_b, Y_b in zip(X1_bs, X2_bs, X3_bs,
                                                     Y_bs):
                        inds = np.argsort([-len(x) for x in X1_b])
                        yield [
                            tf.keras.preprocessing.sequence.pad_sequences(
                                X1_b[inds], padding='post'),
                            X2_b[inds], X3_b[inds]], np.array(Y_b[inds])
                count = 0
                data = {}

# https://github.com/sugiyamath/keras-laser/blob/master/scripts/model_scripts/data_generator.py

問題は解決できたのか

  • ベクトル内から0.0が消えた。
  • 0.0を置換しても、置換した値 (十分小さい負数) が出てくることがなくなった。

この手法についての考慮点

  • ランダムサンプリングではないため、偏りが生じる可能性がある。
  • この手法は精度面というより、むしろ速度面を改善する手法だが、コード中のmax_bsとbsを適切に設定しなければ速度面でのメリットが得られない可能性がある。

次の課題

  • 文ベクトルが実際にSTSやXNLIなどのタスクで機能する必要がある。
  • 訓練のepoch数を増やしても問題が生じないことを確認する必要がある。

参考