【TensorFlow】TFlearnを利用した日本語のテキスト分類

ディープラーニングが流行っているので、TFLearnを利用して日本語のテキスト分類をしてみた。
TFLearnはTensorFlow[^1]をScikit-learnライク[^2]に使えるライブラリのことで、Githubに[サンプルコード](https://github.com/tensorflow/tensorflow/tree/master/tensorflow/examples/skflow)が幾つか掲載されている。

# インストール
TensorFlowの中にTFLearnは組み込まれているので、TensorFlowをインストールすればTFLearnも使えるようになる。
TensorFlowのインストール方法は[ここ](https://www.tensorflow.org/versions/master/get_started/os_setup.html)に載っている。conda経由でインストールするのが一番手軽で、最新版を使いたい場合はソースからインストールする必要がある。

# 前処理
一般的なテキスト分類ではKWの出現回数などで数値化したものを入力ファイルとする場合が多いが、TFLearnの場合はKWインデックスで数値化したものを入力ファイルとする。
具体的には、KWインデックスが、

```text
1: ふわふわ
2: オムライス
3: たっぷり
4: がっつり
```

だった場合、

```text
ふわふわオムライス
たっぷりがっつりオムライス
```

というテキストは、

```text
1,2,0
3,4,2
```

という3次元のベクトルになる。
ここで、「0」はベクトルの次元数を揃えるため（padding用）のインデックス番号。

実際の入力ファイルでは、1次元目の数値はクラスのラベル（二値分類の時は「1」もしくは「2」）になる。なお、ベクトルの次元数は自分で決める必要がある。

```text
1,1,2,3,4,5,6,1,7,2,...
1,37,38,21,39,2,1,40,41,37,...
2,61,38,62,4,63,50,64,1,65,...
2,81,82,50,16,21,5,67,31,23,...
...
```

# TFlearnで学習＆予測
二値分類での畳み込みニューラルネットワーク（CNN）のソースコード。リカレントニューラルネットワーク（RNN）を使いたい場合は、`cnn_model`の部分のコードを`rnn_model`の[コード](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/skflow/text_classification.py)に置き換えるだけ。

```py3
# -*- coding: UTF-8 -*-
import numpy as np
from sklearn import metrics
import pandas

import tensorflow as tf
from tensorflow.contrib import learn

from sklearn.cross_validation import train_test_split

EMBEDDING_SIZE = 20
N_FILTERS = 10
WINDOW_SIZE = 20
FILTER_SHAPE1 = [WINDOW_SIZE, EMBEDDING_SIZE]
FILTER_SHAPE2 = [WINDOW_SIZE, N_FILTERS]
POOLING_WINDOW = 4
POOLING_STRIDE = 2
N_WORDS = １００００ + 1 # 対象KW数（１０,０００に仮設定）に1（padding用のインデックス番号分）を加える

# 元のサンプルコードほぼそのまま
def cnn_model(x, y):
  """2 layer Convolutional network to predict from sequence of words
  to a class."""
  # Convert indexes of words into embeddings.
  # This creates embeddings matrix of [n_words, EMBEDDING_SIZE] and then
  # maps word indexes of the sequence into [batch_size, sequence_length,
  # EMBEDDING_SIZE].
  y = tf.one_hot(y, 15, 1, 0)

  word_vectors = learn.ops.categorical_variable(x, n_classes=N_WORDS,
      embedding_size=EMBEDDING_SIZE, name='words')
  word_vectors = tf.expand_dims(word_vectors, 3)
  with tf.variable_scope('CNN_Layer1'):
    # Apply Convolution filtering on input sequence.
    conv1 = tf.contrib.layers.convolution2d(word_vectors, N_FILTERS,
                                            FILTER_SHAPE1, padding='VALID')
    # Add a RELU for non linearity.
    conv1 = tf.nn.relu(conv1)
    # Max pooling across output of Convolution+Relu.
    pool1 = tf.nn.max_pool(conv1, ksize=[1, POOLING_WINDOW, 1, 1],
        strides=[1, POOLING_STRIDE, 1, 1], padding='SAME')
    # Transpose matrix so that n_filters from convolution becomes width.
    pool1 = tf.transpose(pool1, [0, 1, 3, 2])
  with tf.variable_scope('CNN_Layer2'):
    # Second level of convolution filtering.
    conv2 = tf.contrib.layers.convolution2d(pool1, N_FILTERS,
                                            FILTER_SHAPE2, padding='VALID')
    # Max across each filter to get useful features for classification.
    pool2 = tf.squeeze(tf.reduce_max(conv2, 1), squeeze_dims=[1])

  # Apply regular WX + B and classification.
  prediction, loss = learn.models.logistic_regression(pool2, y)

  train_op = tf.contrib.layers.optimize_loss(
      loss, tf.contrib.framework.get_global_step(),
      optimizer='Adam', learning_rate=0.01)

  return {'class': tf.argmax(prediction, 1), 'prob': prediction}, loss, train_op


def main(unused_argv):
  # Prepare training and testing data
  data = np.loadtxt('./input_file.csv', delimiter=',', dtype=int) # 作成した入力ファイルを読み込む
  labels   = np.array(data[:, 0:1])
  features = np.array(data[:, 1:])
  x_train, x_test, y_train, y_test = train_test_split(features, labels.ravel(), test_size=0.2, random_state=42)

  # Build model
  classifier = learn.Estimator(model_fn=cnn_model)

  # Train and predict
  classifier.fit(x_train, y_train, steps=200)
  y_predicted = classifier.predict(x_test)

  # Result
  true_pos = 0
  true_neg = 0
  false_pos = 0
  false_neg = 0
  for i, predicted in enumerate(y_predicted['class']):
    if predicted == 1 and y_test[i] == 1:
      true_pos += 1
    elif predicted == 2 and y_test[i] == 1:
      true_neg += 1
    elif predicted == 1 and y_test[i] == 2:
      false_pos += 1
    else:
      false_neg += 1
  print(true_pos, true_neg, false_pos, false_neg)


if __name__ == '__main__':
  tf.app.run()
```

# 雑感
難しそうなイメージがあるディープラーニングだが、TensorFlowのTFLearnを使えば手軽に試すことができる。ただ、CNNの層の数も含め、ハイパーパラメータの数が非常に多いので、適切なパラメータを探索する必要がある。
テキスト処理において、ディープラーニングのみが圧倒的な精度に達するケースはあるのだろうか…。

[^1]: Googleが開発したディープラーニングのソフトウェア。オープンソースで公開されている。
[^2]: classifier.fit(...)で学習、classifier.predict(...)で予測。