テキストデータの前処理（ベクトル化、TF-IDF)

## テキストのベクトル化

テキストデータに機械学習アルゴリズムを適用するには、単語の羅列である生データから数値的な特徴ベクトルに変換する必要がある。

Bag-of-words・・・文法や語順は無視して、文章を単語の集合と捉える考え方。

ここではgensimというトピックモデル用のライブラリを用いる。
公式のチュートリアルに記載の練習用の短い9つの文からなるコーパスを使用。

```py3
from gensim import corpora

documents = ["Human machine interface for lab abc computer applications",
             "A survey of user opinion of computer system response time",
             "The EPS user interface management system",
             "System and human system engineering testing of EPS",              
             "Relation of user perceived response time to error measurement",
             "The generation of random binary unordered trees",
             "The intersection graph of paths in trees",
             "Graph minors IV Widths of trees and well quasi ordering",
             "Graph minors A survey"]
```

a, the, for, of, andなどありふれた単語はBag-of-wordsでは意味を持たないので、stop wordとして除外する。
また、表記ぶれや単語が文頭に存在することの影響をなくすため、全て小文字に変換する。

```py3
# remove common words and tokenize
stoplist = set('for a of the and to in'.split())
texts = [[word for word in document.lower().split() if word not in stoplist]
         for document in documents]
```

コーパス中で一度しか現れない単語もあまり情報を持たないので除外する。

```py3
# remove words that appear only once
from collections import defaultdict
frequency = defaultdict(int)
for text in texts:
    for token in text:
        frequency[token] += 1

texts = [[token for token in text if frequency[token] > 1] for text in texts]

from pprint import pprint  # pretty-printer
pprint(texts)
```

結果がこちら。

```
[['human', 'interface', 'computer'],
 ['survey', 'user', 'computer', 'system', 'response', 'time'],
 ['eps', 'user', 'interface', 'system'],
 ['system', 'human', 'system', 'eps'],
 ['user', 'response', 'time'],
 ['trees'],
 ['graph', 'trees'],
 ['graph', 'minors', 'trees'],
 ['graph', 'minors', 'survey']]
```

ここまでで空白で単語を分割してトークン化（tokenize）した。
次に各単語にidを与え、出現回数を数える(count)。
[gensim.corpora.dictionary.Dictionary](https://radimrehurek.com/gensim/corpora/dictionary.html#) classはトークンとidのマッピングを表すディクショナリー。

```py3
dictionary = corpora.Dictionary(texts)

print(dictionary)
print(dictionary.token2id)
```

```
Dictionary(12 unique tokens: ['human', 'interface', 'computer', 'survey', 'user']...)
{'human': 0, 'interface': 1, 'computer': 2, 'survey': 3, 
'user': 4, 'system': 5, 'response': 6, 'time': 7, 
'eps': 8, 'trees': 9, 'graph': 10, 'minors': 11}
```

コーパス中のユニークな単語数は12個。各文章はそれぞれの単語のカウントを含む12次元のベクトルで表される。
新しい文章をvectorizeするにはdoc2bowを使う。

```py3
new_doc = "Human computer interaction"
new_vec = dictionary.doc2bow(new_doc.lower().split())
print(new_vec)  # the word "interaction" does not appear in the dictionary and is ignored
```

```
[(0, 1), (2, 1)]
```

結果は [(word_id, word_count), ...]の形で返される。
この場合はid=0のhumanを1つ、id=2のinteractionを1つ含む。
出現回数が0の単語はスキップされる。また、"interaction"はdictionaryに含まれないので無視される。

元の文章をベクトル化する。

```py3
corpus = [dictionary.doc2bow(text) for text in texts]
for c in corpus:
    print(c)
```

```
[(0, 1), (1, 1), (2, 1)]
[(2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)]
[(1, 1), (4, 1), (5, 1), (8, 1)]
[(0, 1), (5, 2), (8, 1)]
[(4, 1), (6, 1), (7, 1)]
[(9, 1)]
[(9, 1), (10, 1)]
[(9, 1), (10, 1), (11, 1)]
[(3, 1), (10, 1), (11, 1)]
```

## TF-IDF
| 用語 | 意味 |
|:-:|:-:|
| TF (term frequency)  | 単語の出現頻度  |
| IDF (inverse document frequency)  | 逆文書出現頻度  |


コーパス中で出現回数が極端に多い単語はあまり有用な情報を持たない可能性がある。単語のカウントデータ(TF)を直接分類器に与えると、頻度は少ないがより意味のある単語がこれらの単語に埋もれてしまう。そこでTFに加え、IDFで特徴量を重み付けする。

IDFは下記のように定義される。
$n_d$は全文書数、$df(d,t)$は単語 _t_ を含む文書数。単語 _t_ を含む文書が少ないほどIDFは大きくなる（稀な単語の重みを大きくする）。

```math
idf(t)=log\frac{1+n_d}{1+df(d,t)}+1
```


gensimでTF-IDFによる変換をする。

```py3
from gensim import models
# step 1 -- initialize a model
tfidf = models.TfidfModel(corpus)

# step 2 -- use the model to transform vectors
corpus_tfidf = tfidf[corpus]
for doc in corpus_tfidf:
    print(doc)
```

```
[(1, 0.5710059809418182), (4, 0.4170757362022777), (5, 0.4170757362022777), (8, 0.5710059809418182)]
[(0, 0.49182558987264147), (5, 0.7184811607083769), (8, 0.49182558987264147)]
[(4, 0.45889394536615247), (6, 0.6282580468670046), (7, 0.6282580468670046)]
[(9, 1.0)]
[(9, 0.7071067811865475), (10, 0.7071067811865475)]
[(9, 0.5080429008916749), (10, 0.5080429008916749), (11, 0.695546419520037)]
[(3, 0.6282580468670046), (10, 0.45889394536615247), (11, 0.6282580468670046)]
```

ここではtrainingに用いたコーパス自体をTF-IDFにより変換したが、もちろんどのような文章ベクトルでも（同じベクトル空間に属していれば）変換できる。