テキストデータをNLPのコーパスとして活用するための事前処理


### 概要
[web上から引っ張ってきたWikipediaのデータ](https://qiita.com/atsushieee/items/7b64b605de7d1646bf41)を元に、データ解析を行うための事前処理を行う。
具体的には以下の2点を導出する。


- **単語の出現頻度（TF）**

データ解析に使用する単語の選別時に使用。 TF に応じて、トレーニングデータに含めるか否か決める。

テキスト内にある単語すべてを解析対象とすると、解析時間のコストが増大する。
そこで TF が低い単語を解析対象から外すことで、時間コストの問題を低減する。

以下、単語の出現頻度 (TF) の一部サンプル。

| 単語ID | 単語 | テキスト内の出現頻度 |
| :---: | :---: | :---: |
| 30 | 使用 | 51 |
| 31 | 世紀 | 102 |
| 32 | 滞る | 6 |

<br>
- **テキストのインデックス化**

TF 割り出し時に各単語に付けた インデックスによるテキスト管理を行うことで、[one-hot encoding](https://www.kaggle.com/dansbecker/using-categorical-data-with-one-hot-encoding?)を使ったデータ解析を行いやすくする。


```
単語ID変換 【前】 のテキストサンプル
日本語は、主に日本国内や日本人同士の間で使用されている言語である。

単語ID変換 【後】
315, 8, 117, 26, 324, 484, 135, 1079, 797, 16, 235, 19, 30, 11, 22, 58, 59, 69, 13, 14

（単語ID 315: 日本語、単語ID 8: は）
```


## 手順

### 1. 準備

[githubのコード](https://github.com/atsushieee/word_embedding_test)をダウンロードし、 `requirements.txt`に記載の python ライブラリをインストール。

```yaml:使用環境
python3.7.2
```

```bash:ライブラリのインストール
pip install -r requirements.txt
```

### 2. 単語の出現頻度（TF）を求め、DBに格納

##### 2-1. 形態素解析を用い、テキストデータを単語に分割。分割した単語の出現頻度（TF）を求める

コーパス（ex.wikipedia）のデータを文単位に分割。
`MeCab`の形態素解析にかけて、さらに単語単位に分割。
単語の出現頻度は `Counter` クラスを用いて導出。

以下は、サンプル文に対しての処理の流れを記載

```python
sample_sentence = '日本語は、主に日本国内や日本人同士の間で使用されている言語である。'

# 日本語の表層形でなく、基本形を使用するために、パーサーとしてOchasenを使用
tagger = MeCab.Tagger('-Ochasen')
# サンプル文を形態素解析した単語群にパース
node = tagger.parseToNode(sample_sentence)
words = []
while node:
    #  node.featureで、該当単語に対して以下の特徴抽出ができる
    # [品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音]
    # 表層形でなく、基本形(原形)を採用
    word = node.feature.split(",")[6]
    # 日本語の表現だけを抽出
    if re.search(r'[ぁ-んァ-ヶ一-龥]+', word):
        words.append(word)
    node = node.next

words_frequency = Counter(words_list)
```

コーパスデータ内の全文に同様の処理を行い、 `Counter`クラス同士の加算演算をすることで、データ全体の単語出現頻度(TF)を算出。

##### 2-2. DB（sqlite）の準備。単語の出現頻度（TF）をDBに格納

今後の処理を行いやすくするために、算出したデータをDBに格納。
以下、格納する `words`テープルの定義一覧

| 物理名 | 論理名 | 型 | 主キー | AUTO INCREMENT |
| :---: | :---: | :---: | :---: | :---: |
| id | インデックス | INTEGER | ○ | ○ |
| word | 単語 | STRING |  |  |
| frequency | 出現回数 | STRING |  |  |

```python:wordsテーブルへの格納
# 以下<db_file_path>はsqliteのdbファイル格納場所
conn = sqlite3.connect('<db_file_path>')
cur = self.conn.cursor()

insert_word_sql = 'INSERT INTO words (word, frequency) values (?,?)'
inserted_info = []
# words テーブルに格納するデータを配列に格納し、一括でinsert
for word, frequency in dict(words_frequency).items():
    inserted_info.append((word, frequency))
cur.executemany(insert_word_sql, inserted_info)
```


### 3. テキスト内にある各単語をインデックス番号に変換し、DBに格納

##### 3-1. テキスト内の各単語をインデックスに変換できるよう、dictionaryを作成

手順2で格納したテーブルデータから、 全単語のリストを抽出。
それを元に `key:単語`と `value:単語インデックス`のdictionaryを作成。

```python
# words テーブルから全データを抽出
cur.execute('SELECT * FROM words')
words_info = cur.fetchall()

word_stoi = {}
for word_info in words_info:
    word_stoi[word_info[1]] = word_info[0]
```
上で求めたdictionaryを使って、テキストデータを単語インデックスの配列に整形

```python
words_indices = []
while node:
    word = node.feature.split(",")[6]
    # 日本語の表現だけを抽出
    if re.search(r'[ぁ-んァ-ヶ一-龥]+', word):
        words_indices.append(word_stoi[word])
    node = node.next
```

##### 3-2. DBへの格納

元のテキストデータと単語をインデックス変換した配列のデータをDBに格納する
以下、格納する `sentences`テープルの定義一覧

| 物理名 | 論理名 | 型 | 主キー | AUTO INCREMENT |
| :---: | :---: | :---: | :---: | :---: |
| id | インデックス | INTEGER | ○ | ○ |
| word_sentence | テキストデータ | STRING |  |  |
| index_sentence | 構成単語のID配列| STRING |  |  |

```python
insert_sentence_sql = '''
    INSERT INTO sentences (
        word_sentence, index_sentence
    ) values (?,?)
'''
inserted_info = [sample_sentence, str(words_indices)]
cur.execute(insert_sentence_sql, inserted_info)
```

## 結論

NLP処理を行うための前準備として、テキストデータの処理を行った。
またDBに格納することで、今後の処理を行いやすくした。

ここでは、サンプル文に対するデータ処理を掲載したが、wikipedia全データに対する処理の流れは、[jupyter notebook](https://github.com/atsushieee/word_embedding_test/blob/master/jupyter_notebook/preprocess.ipynb)に掲載。
全コードは、[github](https://github.com/atsushieee/word_embedding_test)参照。


## 参考
- [Mecab](https://taku910.github.io/mecab/)
- [One-Hot Encoding](https://www.kaggle.com/dansbecker/using-categorical-data-with-one-hot-encoding?)