【翻訳】scikit-learn 0.18 チュートリアル テキストデータの操作

http://scikit-learn.org/0.18/tutorial/text_analytics/working_with_text_data.html をgoogle翻訳した
[scikit-learn 0.18 チュートリアル 目次](http://qiita.com/nazoking@github/items/5160c11d1a5b3fe8f34c) / [前のチュートリアル](http://qiita.com/nazoking@github/items/44df68d5a372517205f9)


----

# テキストデータの操作

このガイドの目的は、1つの実用的なタスクに関する主要な `scikit-learn` ツールのいくつかを探究することです：20の異なるトピックに関するテキストドキュメント（ニュースグループの投稿）の分析。
このセクションでは、以下の方法を説明します。

  - ファイルの内容とカテゴリをロードする
  - 機械学習に適した特徴ベクトルを抽出する
  - カテゴリ化を行う線形モデルを訓練する
  - グリッド探索戦略を使用して、特徴抽出コンポーネントと分類器の両方の良好な構成を見つける

## チュートリアルセットアップ

このチュートリアルを始めるには、最初にscikit-learnと必要なすべての依存関係をインストールする必要があります。
詳細およびシステムごとの指示については、[インストール手順](http://scikit-learn.org/0.18/install.html#installation-instructions)のページを参照してください。
このチュートリアルのソースは、scikit-learnフォルダ内にあります：

```
scikit-learn/doc/tutorial/text_analytics/
```

チュートリアルフォルダには、次のフォルダが含まれている必要があります。

  - `*.rstファイル` - sphinxで書かれたチュートリアル文書のソース
  - `data` - チュートリアルで使用されたデータセットを入れるためのフォルダ
  - `skeletons` - 練習問題の不完全なサンプルスクリプト
  - `solutions` - 演習の解決策

ハードドライブ上のどこかに `sklearn_tut_workspace` という新しいフォルダに `skeltons` をコピーしましょう。オリジナルのスケルトンを元のままにして、このエクササイズではコピーしたファイルを編集します：

```
% cp -r skeletons work_directory/sklearn_tut_workspace
```

機械学習にはデータが必要です。各 `$TUTORIAL_HOME/data` サブフォルダに移動し、そこから `fetch_data.py` スクリプトを実行します（最初に読み込んだ後に）。
例えば：

```
% cd $TUTORIAL_HOME/data/languages
% less fetch_data.py
% python fetch_data.py
```

## 20のニュースグループデータセットを読み込む

データセットは「20個のニュースグループ」と呼ばれます。[ウェブサイト](http://people.csail.mit.edu/jrennie/20Newsgroups/) から引用された公式の記述はここにあります：

> 20のニュースグループデータセットは、20のニュースグループにまたがって（ほぼ）均等に分割された約20,000のニュースグループ文書の集合です。われわれの知る限りでは、もともと彼の論文「Newsweeder：ネットニュースをフィルタリングすることを学ぶ」のためにケン・ラングによって収集されましたが、このコレクションには明示的に言及していません。 20のニュースグループのコレクションは、テキスト分類やテキストクラスタリングなどの機械学習技術のテキストアプリケーションの実験用の一般的なデータセットになっています。

以下では、scikit-learnの20のニュースグループに組み込みのデータセットローダーを使用します。また、Webサイトから手動でデータセットをダウンロードし、[sklearn.datasets.load_files](http://scikit-learn.org/0.18/modules/generated/sklearn.datasets.load_files.html#sklearn.datasets.load_files) 関数を使用して、圧縮されていないアーカイブフォルダの `20news-bydate-train` サブフォルダを指定することもできます。
この最初の例の実行時間を短縮するために、データセットで使用可能な20のうち4つのカテゴリのみを使用して部分的なデータセットを作成します。

```python
>>> categories = ['alt.atheism', 'soc.religion.christian',
...               'comp.graphics', 'sci.med']
```

これらのカテゴリに一致するファイルのリストを次のように読み込むことができます：

```python
>>> from sklearn.datasets import fetch_20newsgroups
>>> twenty_train = fetch_20newsgroups(subset='train',
...     categories=categories, shuffle=True, random_state=42)
```

返されるデータセットは、scikit-learn の "bunch"です。フィールドを持つ単純なホルダーオブジェクトで、利便性のためにPythonの `dict` キーや `object` の属性としてアクセスすることができます。たとえば、target_namesには、要求されたカテゴリ名のリストが格納されます。

```python
>>> twenty_train.target_names
['alt.atheism', 'comp.graphics', 'sci.med', 'soc.religion.christian']
```

ファイル自体は、データ属性のメモリにロードされます。参照のため、ファイル名も利用できます：

```python
>>> len(twenty_train.data)
2257
>>> len(twenty_train.filenames)
2257
```

では、読み込まれたファイルの最初の行を出力しましょう：

```pycon
>>> print("\n".join(twenty_train.data[0].split("\n")[:3]))
From: sd345@city.ac.uk (Michael Collier)
Subject: Converting images to HP LaserJet III?
Nntp-Posting-Host: hampton

>>> print(twenty_train.target_names[twenty_train.target[0]])
comp.graphics
```

教師あり学習アルゴリズムでは、トレーニングセット内の各ドキュメントのカテゴリラベルが必要です。この場合、カテゴリはニュースグループの名前であり、個々の文書を保持するフォルダの名前でもあります。
スピードとスペース効率の理由から、scikit-learnは `target_names` リスト内のカテゴリ名のインデックスに対応する整数の配列として `target` 属性をロードします。各サンプルのカテゴリ整数IDは、 `target` 属性に格納されます。

```python
>>> twenty_train.target[:10]
array([1, 1, 3, 3, 3, 3, 3, 2, 2, 2])
```

次のようにカテゴリ名を取得することが可能です：

```python
>>> for t in twenty_train.target[:10]:
...     print(twenty_train.target_names[t])
...
comp.graphics
comp.graphics
soc.religion.christian
soc.religion.christian
soc.religion.christian
soc.religion.christian
soc.religion.christian
sci.med
sci.med
sci.med
```

サンプルがランダムにシャッフルされていることに気付くでしょう（固定乱数シードで）。完全なデータセットをトレーニングする前に、最初のサンプルのみを利用してモデルを素早くトレーニングし、最初のアイデアの結果を試すのに便利です。

## テキストファイルからの特徴の抽出

テキスト文書の機械学習を行うためには、まずテキストコンテンツを数値の特徴ベクトルに変換する必要があります。

### Bag of Words

これを行う最も直感的な方法は、Bag of Words表現です：

  1. 訓練集合の任意の文書中に存在する各単語に固定整数idを割り当てる（例えば、単語から整数インデックスへの辞書を構築することによって）。
  2. 各文書 `#i` について、各単語 `w` の出現回数をカウントし、それを特徴量 `#j` の値として `X[i, j]` に格納する。ここで、 `j` は辞書内の単語 `w` のインデックスである

Bags of words表現は、`n_features` がコーパス内の別個の単語の数であることを意味する。この数字は、典型的には100,000を超える。
`n_samples == 10000` の場合、`X`を `numpy` の浮動小数点数配列として格納するには、今日のコンピュータでもなかなか苦しい 10000 x 100000 x 4bytes = **4GBのRAM** が必要です。
幸運なことに、 **Xのほとんどの値は0** になります。これは、指定されたドキュメントでは数千もの異なる単語が使用されるためです。この理由から、Bag of Wordsは **高次元の疎なデータセット** であると言えます。特徴ベクトルの非ゼロ部分のみをメモリに記憶することによって、多くのメモリを節約することができる。
`scipy.sparse` 行列はまさにこれを行うデータ構造であり、scikit-learnはこれらの構造の組み込みサポートを持っています。

### scikit-learnでテキストをトークン化する

ストップワードのテキスト前処理、トークン化、およびフィルタリングは、フィーチャの辞書を構築してドキュメントをフィーチャベクターに変換できる上位レベルのコンポーネントに含まれています。

```python
>>> from sklearn.feature_extraction.text import CountVectorizer
>>> count_vect = CountVectorizer()
>>> X_train_counts = count_vect.fit_transform(twenty_train.data)
>>> X_train_counts.shape
(2257, 35788)
```

[CountVectorizer](http://scikit-learn.org/0.18/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html#sklearn.feature_extraction.text.CountVectorizer) は、N-gram の単語または連続する文字のカウントをサポートします。一旦適合されると、ベクトル化器は特徴指標の辞書を構築します：

```python
>>> count_vect.vocabulary_.get(u'algorithm')
4690
```

ボキャブラリ内の単語のインデックス値は、トレーニングコーパス全体の頻度にリンクしています。

## 回数から頻度へ

単語数カウントは良いスタートですが、同じトピックについて話していても、ドキュメントが長いほど平均カウント値が高くなります。
これらの潜在的な不一致を回避するには、ドキュメント内の各単語の出現回数を文書内の単語の総数で除算するだけで十分です。これらの新しい機能は、Term Frequenciesの `tf` と呼ばれます。
tfの別の改良は、コーパスの多くの文書に出現する単語の重みを小さくすることです。コーパスに稀に出てくる単語よりも有益ではない。
このダウンスケーリングは、「Term Frequency times Inverse Document Frequency」のtf-idfと呼ばれます。
**tf** と **tf-idf** は、次のように計算できます。

```python
>> from sklearn.feature_extraction.text import TfidfTransformer
>>> tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)
>>> X_train_tf = tf_transformer.transform(X_train_counts)
>>> X_train_tf.shape
(2257, 35788)
```

上記の例のコードでは、最初に `fit(...)` メソッドを使用して、データに推定値をフィットさせ、次にcount-matrixをtf-idf表現に変換する `transform(...)` メソッドを使用します。これらの2つのステップを組み合わせて、冗長な処理をスキップすることで、同じ最終結果をより迅速に達成することができます。これは、以下に示すように、前のセクションの注記で説明したように、 `fit_transform(..)` メソッドを使用して行います。

```python
>>> tfidf_transformer = TfidfTransformer()
>>> X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
>>> X_train_tfidf.shape
(2257, 35788)
```

## 分類器のトレーニング

さて特徴量を手に入れたので、分類器を訓練して、投稿のカテゴリを予測することができます。[ナイーブベイズ](http://scikit-learn.org/0.18/modules/naive_bayes.html#naive-bayes)分類器から始めましょう。これは、このタスクのための素晴らしいベースラインを提供します。 scikit-learnには、この分類器のいくつかの変種が含まれています。単語数に最も適したものは多項式の変形です：

```python
>>> from sklearn.naive_bayes import MultinomialNB
>>> clf = MultinomialNB().fit(X_train_tfidf, twenty_train.target)
```

新しい文書の結果を予測するには、以前とほぼ同じ特徴抽出チェーンを使用して特徴量を抽出する必要があります。 違いは、すでにトレーニングセットにfitしているので、変換器の `fit_transform` の代わりに `transform` を呼び出すことです。

```python
>>> docs_new = ['God is love', 'OpenGL on the GPU is fast']
>>> X_new_counts = count_vect.transform(docs_new)
>>> X_new_tfidf = tfidf_transformer.transform(X_new_counts)

>>> predicted = clf.predict(X_new_tfidf)

>>> for doc, category in zip(docs_new, predicted):
...     print('%r => %s' % (doc, twenty_train.target_names[category]))
...
'God is love' => soc.religion.christian
'OpenGL on the GPU is fast' => comp.graphics
```

## パイプラインの構築

「ベクトル化器 => 変換器 => 分類器」といった一連の処理を使いやすくするために、scikit-learnは、複合分類器のように動作する `Pipeline` クラスを提供します。

```python
>>> from sklearn.pipeline import Pipeline
>>> text_clf = Pipeline([('vect', CountVectorizer()),
...                      ('tfidf', TfidfTransformer()),
...                      ('clf', MultinomialNB()),
... ])
```

名前 `vect`、`tfidf` および `clf`（分類器の略）は任意です。以下では、グリッド検索のセクションでその使用方法を見ていきます。次のコマンドを使用してモデルを訓練することができます。

```pyython
>>> text_clf = text_clf.fit(twenty_train.data, twenty_train.target)
```

## テストセットのパフォーマンスの評価

モデルの予測精度を評価することも同様に簡単です。

```python
>>> twenty_test = fetch_20newsgroups(subset='test',
...     categories=categories, shuffle=True, random_state=42)
>>> docs_test = twenty_test.data
>>> predicted = text_clf.predict(docs_test)
>>> np.mean(predicted == twenty_test.target)            
0.834...
```

私たちは、83.4％の精度を達成しました。（ナイーブベイズよりも少し遅いですが）最良のテキスト分類アルゴリズムの1つと広く認識されている [線形サポートベクターマシン（SVM）](http://scikit-learn.org/0.18/modules/svm.html#svm) でうまくいくかどうかを見てみましょう。異なる分類器オブジェクトをパイプラインに差し込むだけで、学習者を変更できます：

```python
>>> from sklearn.linear_model import SGDClassifier
>>> text_clf = Pipeline([('vect', CountVectorizer()),
...                      ('tfidf', TfidfTransformer()),
...                      ('clf', SGDClassifier(loss='hinge', penalty='l2',
...                                            alpha=1e-3, n_iter=5, random_state=42)),
... ])
>>> _ = text_clf.fit(twenty_train.data, twenty_train.target)
>>> predicted = text_clf.predict(docs_test)
>>> np.mean(predicted == twenty_test.target)            
0.912...
```

scikit-learnはさらに結果のより詳細なパフォーマンス分析のためのユーティリティを提供します：

```python
>>> from sklearn import metrics
>>> print(metrics.classification_report(twenty_test.target, predicted,
...     target_names=twenty_test.target_names))
...                                         
                        precision    recall  f1-score   support

           alt.atheism       0.95      0.81      0.87       319
         comp.graphics       0.88      0.97      0.92       389
               sci.med       0.94      0.90      0.92       396
soc.religion.christian       0.90      0.95      0.93       398

           avg / total       0.92      0.91      0.91      1502


>>> metrics.confusion_matrix(twenty_test.target, predicted)
array([[258,  11,  15,  35],
       [  4, 379,   3,   3],
       [  5,  33, 355,   3],
       [  5,  10,   4, 379]])
```

予想通り、混乱のマトリックスは、無神論(atheism) とクリスチャンに関するニュースグループからの投稿が、コンピュータグラフィックスよりも互いにしばしば混同されていることを示しています。

## グリッド検索によるパラメータチューニング

`TfidfTransformer` で `use_idf` などのパラメータがすでに発生しています。分類器は多くのパラメータも持つ傾向があります。 `MultinomialNB` には平滑化パラメータ `alpha` が含まれており、`SGDClassifier`にはペナルティパラメータ`alpha`と目的関数内に設定可能な損失とペナルティ項があります（モジュールのマニュアルを参照するか、Pythonの `help`関数を使用してこれらの説明を参照してください）。
チェインのさまざまなコンポーネントのパラメータを微調整する代わりに、可能な値のグリッド上で最良のパラメータを網羅的に検索することができます。idfの有無、線形SVMに対してペナルティパラメータ(alpha)0.01または0.001、ベクタライザの分割方法を、単語またはバイグラムのいずれか、で試行します。

```python
>>> from sklearn.model_selection import GridSearchCV
>>> parameters = {'vect__ngram_range': [(1, 1), (1, 2)],
...               'tfidf__use_idf': (True, False),
...               'clf__alpha': (1e-2, 1e-3),
... }
```

明らかに、このような徹底的な検索は高価になる可能性があります。複数のCPUコアがあれば、グリッドサーチャーに `n_jobs` パラメーターを渡して、8つのパラメーターの組み合わせを並行して試してもらうように指示できます。このパラメータに `-1` という値を指定すると、インストールされているコアの数が検出され、それらのすべてが使用されます。

```python
>>> gs_clf = GridSearchCV(text_clf, parameters, n_jobs=-1)
```

グリッド検索インスタンスは、通常のScikit学習モデルのように動作します。トレーニングデータのより小さなサブセットで検索を実行して計算を高速化しましょう：

```python
>>> gs_clf = gs_clf.fit(twenty_train.data[:400], twenty_train.target[:400])
```

`GridSearchCV` オブジェクトを `fit` した結果は、 `predict` が使用できる分類器です。

```python
>>> twenty_train.target_names[gs_clf.predict(['God is love'])[0]]
'soc.religion.christian'
```

オブジェクトの `best_score_` および `best_params_` 属性には、そのスコアに対応する最良平均スコアおよびパラメータ設定が格納されます。

```python
>>> gs_clf.best_score_                                  
0.900...
>>> for param_name in sorted(parameters.keys()):
...     print("%s: %r" % (param_name, gs_clf.best_params_[param_name]))
...
clf__alpha: 0.001
tfidf__use_idf: True
vect__ngram_range: (1, 1)
```

検索の詳細については、`gs_clf.cv_results_` を参照してください。
`cv_results_`パラメータは、後の検査のために `DataFrame` としてpandasに簡単にインポートできます。

## エクセサイズ

エクセサイズを行うには、 `skeletons` フォルダの内容を `workspace` という名前の新しいフォルダとしてコピーします。

```
% cp -r skeletons workspace
```

その後、元のエクササイズ指示が失われることなく、ワークスペースの内容を編集することができます。
次に、ipythonシェルを起動し、work-in-progressスクリプトを実行します。

```
[1] %run workspace/exercise_XX_script.py arg1 arg2 arg3
```

例外がトリガされた場合、 `%debug` を使用して検死デバッグセッションを開始します。
実装を改良し、エクセサイズが解決されるまで繰り返します。
各演習では、スケルトンファイルに必要なすべてのインポートステートメント、データを読み込むための定型コード、モデルの予測精度を評価するためのサンプルコードが用意されています。

### エクセサイズ1：言語の識別

  - カスタムプリプロセッサと `CharNGramAnalyzer` を使用して、Wikipediaの記事のデータをトレーニングセットとして使用して、テキスト分類パイプラインを作成します。
  - いくつかのテストセットのパフォーマンスを評価します。

ipythonコマンドライン：

```
%run workspace/exercise_01_language_train_model.py data/languages/paragraphs/
```

### エクセサイズ2：映画レビューの感情分析

  - ムービーレビューを肯定的または否定的な方法で分類するためのテキスト分類パイプラインを作成します。
  - グリッド検索を使用して、適切なパラメータセットを見つけます。
  - ホールドアウトされたテストセットのパフォーマンスを評価します。

ipythonコマンドライン：

```
%run workspace/exercise_02_sentiment.py data/movie_reviews/txt_sentoken/
```

### エクセサイズ3：CLIテキスト分類ユーティリティ

  - 前のエクセサイズの結果と標準ライブラリの `cPickle` モジュールを使用して、stdinから供給されるテキストの言語を検出し、テキストが英語で書かれている場合は極性（正または負）を推定するコマンドラインユーティリティを記述します。
  - ボーナスポイント：ユーティリティがその予測のための信頼水準を与えることができるかどうか。

## 次は

このチュートリアルを完了したら、 scikit を直感的に理解するのを助けるいくつかの提案があります：

  - [CountVectorizer](http://scikit-learn.org/0.18/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html#sklearn.feature_extraction.text.CountVectorizer) の `analyzer` と `トークンの正規化` を試してみてください
  - ラベルがない場合は、[クラスタリング](http://scikit-learn.org/0.18/auto_examples/text/document_clustering.html#sphx-glr-auto-examples-text-document-clustering-py) を使用してみてください。
  - 1つのドキュメントに複数のラベル（カテゴリなど）がある場合、[Multiclassセクションとmultilabelセクション](http://qiita.com/nazoking@github/items/9decf45d106accc6afe1) を見てください。
  - [潜在意味解析](https://en.wikipedia.org/wiki/Latent_semantic_analysis) に [Truncated SVD](http://scikit-learn.org/0.18/modules/decomposition.html#lsa) を使用してみてください。
  - [Out-of-Core Classification](http://scikit-learn.org/0.18/auto_examples/applications/plot_out_of_core_classification.html#sphx-glr-auto-examples-applications-plot-out-of-core-classification-py) を使用して、コンピュータのメインメモリに収まらないデータから学習する方法を見てください。
  - [Hashing Vectorizer](http://qiita.com/nazoking@github/items/b270288fa38aed0a71bf#4239-hashingvectorizer%E3%81%A7%E3%82%A2%E3%82%A6%E3%83%88%E3%82%AA%E3%83%96%E3%82%B3%E3%82%A2%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B) を、CountVectorizerのメモリに代わるものとして見てみましょう。


----

[チュートリアル 目次](http://qiita.com/nazoking@github/items/5160c11d1a5b3fe8f34c) / [次のチュートリアル](http://qiita.com/nazoking@github/items/de71ad4e50ab79d998e2)

©2010 - 2016、scikit-learn developers（BSDライセンス）。