言語処理100本ノック(2020)-51: 特徴量抽出

[言語処理100本ノック 2020 (Rev2)](https://nlp100.github.io/ja/)の[「第6章: 機械学習」](https://nlp100.github.io/ja/ch06.html)の[51本目「特徴量抽出」](https://nlp100.github.io/ja/ch06.html#51-%E7%89%B9%E5%BE%B4%E9%87%8F%E6%8A%BD%E5%87%BA)記録です。
[「第9章: RNN, CNN」](https://nlp100.github.io/ja/ch09.html)の前提だったためやりました。あまり熱心にやっておらず、以前やった[「2015年版言語処理言語処理100本ノック-71(StanfordNLP使用):ストップワード」](https://qiita.com/FukuharaYohei/items/60719ddaa47474a9d670)のコピペがほとんどです。今回はspacyを使うなどいくつか変更しています。
[記事「まとめ: 言語処理100本ノックで学べることと成果」](https://qiita.com/FukuharaYohei/items/6f0af3c4e270d8af2d99)に[言語処理100本ノック 2015](http://www.cl.ecei.tohoku.ac.jp/nlp100/)についてはまとめていますが、追加で差分の[言語処理100本ノック 2020 (Rev2)](https://nlp100.github.io/ja/)についても更新します。

# 参考リンク

|リンク|備考|
|:--|:--|
|[51.特徴量抽出.ipynb](https://github.com/YoheiFukuhara/nlp100-2020/blob/main/06.%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92/51_%E7%89%B9%E5%BE%B4%E9%87%8F%E6%8A%BD%E5%87%BA.ipynb)|回答プログラムのGitHubリンク|
|[まとめ: 言語処理100本ノックで学べることと成果](https://qiita.com/FukuharaYohei/items/6f0af3c4e270d8af2d99)|言語処理100本ノックまとめ記事|
|[言語処理言語処理100本ノック-71(StanfordNLP使用):ストップワード](https://qiita.com/FukuharaYohei/items/60719ddaa47474a9d670)|2015年版100本ノックの素性抽出処理|

# 環境
後々GPUを使わないと厳しいので、Goolge Colaboratory使いました。Pythonやそのパッケージでより新しいバージョンありますが、新機能使っていないので、プリインストールされているものをそのまま使っています。

|種類|バージョン|内容|
|:--|:--|:--|
|Python|3.7.12|Google Colaboratoryのバージョン|
|google|2.0.3|Google Drive連携|
|pandas|1.1.5|ファイル読込や行列処理|
|spacy|2.2.4|形態素解析|

# [第6章: 機械学習](https://nlp100.github.io/ja/ch06.html)
## 学習内容
> 『文書分類器を機械学習で構築します．さらに，機械学習手法の評価方法を学びます．

## ノック内容
> 本章では，Fabio Gasparetti氏が公開している[News Aggregator Data Set](https://archive.ics.uci.edu/ml/datasets/News+Aggregator)を用い，ニュース記事の見出しを「ビジネス」「科学技術」「エンターテイメント」「健康」のカテゴリに分類するタスク（カテゴリ分類）に取り組む．

### [51.特徴量抽出](https://nlp100.github.io/ja/ch06.html#51-%E7%89%B9%E5%BE%B4%E9%87%8F%E6%8A%BD%E5%87%BA)
>学習データ，検証データ，評価データから特徴量を抽出し，それぞれtrain.feature.txt，valid.feature.txt，test.feature.txtというファイル名で保存せよ． なお，カテゴリ分類に有用そうな特徴量は各自で自由に設計せよ．記事の見出しを単語列に変換したものが最低限のベースラインとなるであろう．

# 回答
## 回答結果
訓練データの先頭10行です。Linuxの`head`コマンドで出力しています。

```:train.feature.txt(先頭10行)
title	category
REFILE UPDATE car sale up for sixth month as economy recover	b
Amazon plan to fight FTC over Mobile App purchase	t
kid still get codeine in emergency room despite Risky side effect STUDY	m
what on earth happen between Solange and Jay after the Met Gala	e
NATO Missile Defense be flight test over Hawaii	b
NYT Publisher Sulzberger say Abramson Firing drive by conduct	b
Argentina Kicillof say have process June debt payment	b
the Perfect and the good on Network Neutrality	t
Scarlett Johansson and Lupita Nyong'o land role in Jungle Book Adaptation	e
```

## 回答プログラム [51.特徴量抽出.ipynb](https://github.com/YoheiFukuhara/nlp100-2020/blob/main/06.%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92/51_%E7%89%B9%E5%BE%B4%E9%87%8F%E6%8A%BD%E5%87%BA.ipynb)
GitHubには確認用コードも含めていますが、ここには必要なものだけ載せています。

```python
import re

import pandas as pd
import spacy
from google.colab import drive

drive.mount('/content/drive')

BASE_PATH = '/content/drive/MyDrive/ColabNotebooks/ML/NLP100_2020/06.MachineLearning/'
nlp = spacy.load('en_core_web_sm')

reg_sym = re.compile(r'^[!-/:-@[-`{-~]|[!-/:-@[-`{-~]$')
reg_dit = re.compile('[0-9]')

# RNN・CNN用なので、そんなに除去しない
EXC_POS = {'PUNCT',   # 句読点
           'X',       # その他
           'SYM',     # 記号
           'SPACE' }  # SPACE

def is_stopword(token):
    lemma = reg_sym.sub('', token.lemma_)
    return True if lemma == '' \
                 or token.pos_ in EXC_POS \
                 or len(lemma) == 1 \
                 or reg_dit.search(lemma) \
                 else False

def preproc_text(text):
    doc = nlp(text)
    return ' '.join([reg_sym.sub('', token.lemma_) for token in doc if is_stopword(token) is False])
    
def change_text(type_):
    df = pd.read_table(BASE_PATH+type_+'.txt')
    print('---'+type_+'---')
    df.info()
    df['title'] = df['title'].map(preproc_text)
    df.to_csv(BASE_PATH+type_+'.feature.txt', sep='\t', index=False)

change_text('train')
change_text('valid')
change_text('test')
```

## 回答解説
回答の補足説明です。

### 形態素解析
[「言語処理100本ノック-71(StanfordNLP使用):ストップワード」](https://qiita.com/FukuharaYohei/items/60719ddaa47474a9d670)では[StanfordNLP](https://stanfordnlp.github.io/stanfordnlp/)を形態素解析に使っていましたが、今回は[spacy](https://spacy.io/)を使いました。たいした理由はなく、最近仕事で`spacy`使うので、少しでも慣れるためにと使いました。両者を比較して、精度などを理由として選定したわけではありません。
使い方もたいして変わりません。最初にロードします。

```python:
nlp = spacy.load('en_core_web_sm')
```

で、テキストを渡すだけです。

```python:
def preproc_text(text):
    doc = nlp(text)
```

[形態素解析の属性一覧](https://spacy.io/api/token#attributes)です。裏でGinzaを使った場合も同じ属性になり、その辺の統一性がMeCabよりいいですね。
今回はレンマを意味する`lemma_`と[Universal POS tags](https://universaldependencies.org/u/pos/)を意味する`pos_`を使っています。最初は`nltk`パッケージを使ってStemmingもしていましたが、[「84.単語ベクトルの導入」](https://nlp100.github.io/ja/ch09.html#84-%E5%8D%98%E8%AA%9E%E3%83%99%E3%82%AF%E3%83%88%E3%83%AB%E3%81%AE%E5%B0%8E%E5%85%A5)で学習済みのEmbeddingへと変換できないことに気づき、やめました。
また、除去する[Universal POS tags](https://universaldependencies.org/u/pos/)も最初は多かったのですが、除去しすぎてタイトルがなくなるものもできてきて、除去対象を少なくしました。まぁ、CNNやRNNなのでそんなに除去しなくても、とも思いました。
今回、あまりこの辺りは細かい調整をしていません。
ちなみに、レンマ処理では人物の固有名詞は`PRON`に変換されていました。例えばトランプ大統領だったら政治トピックなど、重要な単語もありそうで失敗したかも。後で気づきました。