TorchTextとRandom Forestで文書分類

## はじめに
　[PyTorchのtochtextで文書分類](https://qiita.com/ryuji0123/items/916eb304e7d0f0a5dcb2)の続きです。前回は`torchtext`のチュートリアルに沿って実装を解説しました。今回は`torchtext`のデータを使って`random forest`での学習を行う方法を解説します。

## 復習
　今回は`tf-idf`形式にデータを加工した上で`random forest`を`fit`させます。普通に`sklearn`の[TfidfVectorizer](https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html#sklearn-feature-extraction-text-tfidfvectorizer)を使えばいいように思えますが、`torchtext`のデータをそのまま`TfidfVectorizer`に放り込むことはできません。理由は`torchtext`から取り出せるデータの構造が`TfidfVectorizer`の入力として可能なものではないからです。`torchtext`では`N-gram`形式でデータを保持しています。一方`TfidfVectorizer`には`raw text`つまり原文を入力する必要があります。実装においてはこのギャップを最初に埋めます。

## 開発環境
`Google Colab`

## 実装
### 0. pip install, importなど
　前回と同様に`import`等を行います。`mount`する理由は後述します。

```python
# mount
from google.colab import drive
drive.mount('/content/gdrive/')
%cd 'gdrive/My Drive/Colab Notebooks/'
%ls

#install PyTorch
!pip install torch 
!pip install torchtext==0.5
%matplotlib inline

#import modules
from torchtext.datasets import text_classification
import csv
import os
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import GridSearchCV
```

### 1. データの用意
　データを用意していきます。今回自前で処理を書く必要があるのが`train_corpus`変数の定義以降の部分です。なお`Google Colab`の場合、csvファイルからデータを直接得るために`mount`が必要です。　

```python

if not os.path.isdir('./.data'):
  os.mkdir('./.data')
SAMPLE_NUMBER = 100
K = 5
train_dataset, test_dataset = text_classification.DATASETS['AG_NEWS'](
    root='./.data', ngrams=NGRAMS, vocab=None
)

train_corpus = []
train_labels = []
with open('./.data/ag_news_csv/train.csv', 'r') as f:
  train_csv = csv.reader(f)
  for row in train_csv:
    train_labels.append(row[0])
    train_corpus.append(row[-1])

test_corpus = []
test_labels = []
with open('./.data/ag_news_csv/test.csv', 'r') as f:
  train_csv = csv.reader(f)
  for row in train_csv:
    test_labels.append(row[0])
    test_corpus.append(row[-1])
```

### 2. tf-idf作成
　`train`と`test`で使用する関数が違うので注意してください。`fit_transform`を使用すると`vectorizer`で保持している行列が変更されます。使用するデータが異なる場合、新規で作成した`tf-idf matrix`は異なります。従って、`fit_transform`を再度使用すると`score`計算においてエラーが発生します。そのため、既存の行列構造を保持しながら変換を行う`transform`関数を`test`データに対して適応しています。

```python
# create tf-idf
vectorizer = TfidfVectorizer()
train_matrix = vectorizer.fit_transform(train_corpus)
test_matrix = vectorizer.transform(test_corpus)
```
### 3. Random Forest
　`tf-idf`を用意できたので実際に`fit`させます。そのままでもいいですが、せっかくなので[grid-search](http://satopoooon.hatenablog.com/entry/2017/12/24/213920)を使ってモデルを選択します。ちなみに今回は処理時間削減のために一部のみを`fit`に利用しています。学習用サンプル数1万程度でも0.8以上の精度は出るようです。

```python
# randomforest
forest = RandomForestClassifier()

# grid-searchをしないならそのままfit
# forest.fit(train_matrix[:SAMPLE_NUMBER], train_labels[:SAMPLE_NUMBER])

# grid-search
param_grid = {"n_estimators":[10,20,30,40,50,60,70,80,90,100]}
tree_grid = GridSearchCV(
    estimator=forest,
    param_grid=param_grid,
    scoring='accuracy',
    cv=K
)
tree_grid.fit(train_matrix[:SAMPLE_NUMBER], train_labels[:SAMPLE_NUMBER])
tree_grid_best = tree_grid.best_estimator_
# check classifier's performance
print("Best Model Parameter: ",tree_grid.best_params_)
print("Best Model Score    : ",tree_grid.best_score_)
```

## 終わりに
　今後も`torchtext.text_classification`関係で実装を公開していこうと思います。