LoginSignup
26
45

More than 3 years have passed since last update.

機械学習のための日本語前処理

Last updated at Posted at 2020-04-29

はじめに

機械学習を使ったチャットボットの仕組みを理解するために、テキストを訓練データとする簡単なニューラルネットワークを作成した際の備忘録。

目的

英文テキストで作成したルールベース型チャットボットを、日本語テキストにも適用して動作させること。日本語テキストを前処理し、それをニューラルネットワークへ通せることを確認する。訓練データとして、Niantic社の"Pokemon GO"に関連したサポートページをWebスクレイピングしたものを使用した。

Nianticサポートページ

使用しているCSVファイル(GitHub)

マルチクラス分類

予め用意された応答文を入力にあわせて返す「ルールベース型」を参考に、"Intents"(意図)を識別して予測するマルチクラス分類の部分までを形にした。

「生成型」ではなく、入力情報から関連した「よくある質問(FAQ)」を予測するものであるため、”RNN”ではなく通常のニューラルネットワーク層でモデルを作成。

環境

Jupyter notebookは使わずに、仮想環境を構築。

  • macOS Mojave 10.14.6
  • Python 3.6.0
  • TensorFlow 1.9.0
  • MeCab(日本語の形態素解析ツール)

MeCabインストールの参考ページ

コード

訓練データの読み込みと、日本語テキストの前処理

wakatigaki.py
import MeCab
import csv
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.sequence import pad_sequences

def create_tokenizer() :
# CSVファイルを読み込む
    text_list = []
    with open("pgo_train_texts.csv", "r") as csvfile :
        texts = csv.reader(csvfile)

        for text in texts :
            text_list.append(text)

# MeCabを使い、日本語テキストを分かち書きする。
        wakati_list = []
        label_list = []
        for label, text in text_list :
            text = text.lower()

            wakati = MeCab.Tagger("-O wakati")
            text_wakati = wakati.parse(text).strip()
            wakati_list.append(text_wakati)
            label_list.append(label)

# 文章のうち最大のものの要素数を調べる。
# トークナイザーで使用するテキストデータのリストを作成。
        max_len = -1
        split_list = []
        sentences = []
        for text in wakati_list :
            text = text.split()
            split_list.extend(text)
            sentences.append(text)

            if len(text) > max_len :
                max_len = len(text)
        print("Max length of texts: ", max_len)
        vocab_size = len(set(split_list))
        print("Vocabularay size: ", vocab_size)
        label_size = len(set(label_list))

# Tokenizerを使い、単語にインデックス1から番号を割り当てる。
# 辞書も作成。
        tokenizer = tf.keras.preprocessing.text.Tokenizer(oov_token="<oov>")
        tokenizer.fit_on_texts(split_list)
        word_index = tokenizer.word_index
        print("Dictionary size: ", len(word_index))
        sequences = tokenizer.texts_to_sequences(sentences)

# 教師あり学習に使うラベルデータも、Tokenizerを使い番号をふる。
        label_tokenizer = tf.keras.preprocessing.text.Tokenizer()
        label_tokenizer.fit_on_texts(label_list)
        label_index = label_tokenizer.word_index
        label_sequences = label_tokenizer.texts_to_sequences(label_list)

# Tokenizerは1から番号をわりあてるのに対し、実際のラベルは0番からインデックスを開始するため−1する。
        label_seq = []
        for label in label_sequences :
            l = label[0] - 1
            label_seq.append(l)

# to_categorical() を使い、モデルに渡す実際のラベルデータであるOne-Hotベクトルを作成。
        one_hot_y = tf.keras.utils.to_categorical(label_seq)

# 訓練データのサイズを揃えるため、短いテキストにもっとも長いテキストデータに合わせて0を追加する。
        padded = pad_sequences(sequences, maxlen=max_len, padding="post", truncating="post")
        print("padded sequences: ", padded)

        reverse_index = dict()
        for intent, i in label_index.items() :
            reverse_index[i] = intent

    return padded, one_hot_y, word_index, reverse_index, tokenizer, max_len, vocab_size

TensorFlowを用いてモデル作成

model.py
import tensorflow as tf

def model(training, label, vocab_size) :
    model = tf.keras.models.Sequential([
        tf.keras.layers.Embedding(input_dim=vocab_size, output_dim=16, input_length=len(training[0])),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(30, activation="relu"),
        tf.keras.layers.Dense(len(label[0]), activation="softmax")
    ])

    model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
    model.fit(x=training, y=label, epochs=100)

    model.summary()

    return model
  • 最初にEmbeddingレイヤーを使い、単語同士の関係をベクトルで捉えられるようにする。
  • Flatten()を挟み、Fully connectedであるDenseレイヤーへ渡せるようにEmbedding Matrixを平坦化する。
    • 代わりにAveragePooling1D()を使うと、ニューラルネットワークのパラメータ数を少なくでき、計算コストを減らせる。
  • 識別したい"Intents(意図)”の数はラベルの種類と同一であるため、One-Hotベクトルのインデックス0番の要素数に合わせる。
  • 出力層の活性化関数は、マルチクラス分類に対応する”softmax”を選択。
  • モデルのコンパイルでは、マルチクラス分類用のロス計算方法を設定。
  • 学習アルゴリズムに”Adam”を使用。

入力画面の作成

chat.py

import MeCab
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.sequence import pad_sequences

# コンソールで受けつけたテキストをモデルが処理できるように整える。
def prepro_wakati(input, tokenizer, max_len) :
    sentence = []

    input = input.lower()
    wakati = MeCab.Tagger("-O wakati")
    text_wakati = wakati.parse(input).strip()
    sentence.append(text_wakati)
    print(sentence)

    seq = tokenizer.texts_to_sequences(sentence)
    seq = list(seq)
    padded = pad_sequences(seq, maxlen=max_len, padding="post", truncating="post")
    print(padded)

    return padded

def chat(model, tokenizer, label_index, max_len) :
    print("Start talking with the bot (type quit to stop): ")
    while True :
        input_text = input("You: ")
        if input_text.lower() == "quit" :
            break

        x = prepro_wakati(input_text, tokenizer, max_len)
        results = model.predict(x, batch_size=1)
        print("results: ", results)
        results_index = np.argmax(results)
        print("Predicted index: ", results_index)

        intent = label_index[results_index + 1]

        print("Type of intent: ", intent)

コンソール画面。
入力されたテキストを訓練済みモデルにわたし、9つある”Intents”のうちどれに当てはまるかを予測する。

  • Intentsの種類
    • お問い合わせ
    • 不具合
    • スタートガイド
    • アクセサリー
    • バトル
    • イベント
    • 相棒
    • セキュリティ
    • ショップ

実行

定義した関数を呼び出して実行。

ex.py
import wakatigaki
import model
import chat

padded, one_hot_y, word_index, label_index, tokenizer, max_len, vocab_size = wakatigaki.create_tokenizer()

model = model.model(padded, one_hot_y, vocab_size)

chat.chat(model, tokenizer, label_index, max_len)

訓練結果

スクリーンショット 2020-04-29 16.39.32.png

"results: "のなかの数値は、各カテゴリーの該当する確率を表す。

「ポケモンの捕まえ方」という入力に対し、「スタートガイド」を予測。つづいて「ポケコインとアイテム」に対し「ショップ」を予測。どちらも適切なカテゴリーを予測できた。

26
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
45