自然言語処理ならHugging FaceのTransformersでしょ

# 自然言語処理を勉強しよう
目的としてはBERTやその派生モデルを使って様々な分析を行い、新たな知見を得ることである。
そもそも機械に私たちの言葉を理解させようとすることが興味深すぎる。

BERTを使った推論までやると長すぎるので、今回の記事ではその前段階であるHuggingface社のもつライブラリ「Transformers」にある日本語対応したトークナイザーを使ってBERTを使える段階までの道のりを書く。
ちなみにHugginfaceはTransformer以外にもAIモデルや、データセットであったり機械学習用の大量の知恵が詰まっているプラットフォームであり、これからお世話になりそうな予感がプンプンする。

## 事前に必要な知識
BERTモデルを使った処理には以下の二つのステップがある。
1.トークナイザを用いて文章をトークン化、bertに入力できる形にする
2.そのデータをbertに入力して出力を得る

トークナイザーとは自然言語をトークン化して、入力を機械が使いやすいように処理するものである。
今回の記事ではステップ１の流れを紹介する。
## 環境構築

最初の難関。
普段使うM1macだと必要なライブラリであるtorchやtransformersが動かない。

結果的にcolabで動いたため、macで開発を進める。
しかし無料版で使うことを考えると研究のような大きな分析には不向きな環境であるため、今後はローカルでできるようにしたい。
そしてwindowsで動くことは確認できたので以降はこっちでできたらいいな。

## Transformersを使ってみる
### 必要なものを準備
トークン化をするために必要なライブラリをインストール。
fugashi以降は日本語対応するためのものである。

```python
pip install scikit-learn transformers fugashi fugashi[unidic-lite] ipadic datasets
```


以下colabで書いたコード
TransformersにあるBERTとそのトークナイザーを持ってきている。
今回は東北大学様が作成なさった日本語対応のモデルとトークナイザーを使わせていただいた。

```python
from transformers import BertJapaneseTokenizer

model_name = 'cl-tohoku/bert-base-japanese-whole-word-masking'
tokenizer = BertJapaneseTokenizer.from_pretrained(model_name)

tokenizer.tokenize('阪神タイガース38年ぶりの日本一おめでとう！')
```
するとunidic-liteをインストールしろと怒られたのでインストール
```python
pip install fugashi[unidic-lite]
```

そして再び実行すると
```
['阪神', 'タイガース', '38', '年', 'ぶり', 'の', '日本', '一','お', 'め', '##で',
'##とう', '!']
```

ちゃんとトークンに分割された。
'##で'のように言葉の前に##がついているのはサブワードと言って単語をさらに分割した時にできるもの。例えば「東京タワー」は一つの単語だけどさらに分割もできる、それによって「'東京','##タワー'」になる感じ。

<br>
現時点ではただトークンで分割しただけ。
bertで扱えるようにするにはより適した形にする必要がある。
以下の作業で単語それぞれをIDにする。
学習された32000の語彙全てにIDが割り振られている。

```python
text = 'ラーメンは美味しい'
encoding = tokenizer(
    text, max_length=12, padding='max_length', truncation=True
)
print('# encoding:')
print(encoding)

tokens = tokenizer.convert_ids_to_tokens(encoding['input_ids'])
print('# tokens:')
print(tokens)

# 以下出力結果
# encoding:
# encoding:
{'input_ids': [2, 9714, 9, 18178, 485, 3, 0, 0, 0, 0, 0, 0], 
'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
'attention_mask': [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]}
# tokens:
['[CLS]', 'ラーメン', 'は', '美味', '##しい', '[SEP]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']
```

input_idsには各単語に割り振られたIDが格納。
token_type_idsは異なる分野セグメントを割り振る時に使われる。
attention_maskは使うトークンを1とし、使わないトークンを0とする。
実際にそこには[PAD]がはいっている。
これは、max_lengthにおいてID列の長さを12と設定したにも関わらず、文書が短く余白ができてしまったために無理やり帳尻合わせのためにトークンをぶち込んだという感じである。

<br>
以下のようにリストにまとめて処理も可能。

```python
text_list = ['リンゴと桃はどちらも美味しい','新時代はこの未来だ']
tokenizer(text_list, padding='longest')


# 以下出力結果
{'input_ids': 
    [[2, 13871, 13, 4889, 9, 3788, 28, 18178, 485, 3], 
    [2, 147, 315, 9, 70, 5143, 75, 3, 0, 0]], 
'token_type_ids':
    [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
'attention_mask': 
    [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
    [1, 1, 1, 1, 1, 1, 1, 1, 0, 0]]
}

```

そして、最終的にtransformersのbertに入力を与えるにはpytorchの多次元配列を扱う型torch.tensorにする必要がある。

```python
tokenizer(
    text_list,
    max_length=10,
    padding='max_length',
    truncation=True,
    return_tensors='pt'
)

# 以下出力結果
{'input_ids': tensor([[    2, 13871,    13,  4889,     9,  3788,    28, 18178,   485,     3],
    [    2,   147,   315,     9,    70,  5143,    75,     3,     0,     0]]),
'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],[1, 1, 1, 1, 1, 1, 1, 1, 0, 0]])}
```
tensor()となっているので準備OK。
BERTはこれを使って処理を行う。


## 今後
簡単な分類問題とかを解いてみたい。
その後ファインチューニングをして推論を行い、比較をする。
BERTは使うのはまだGPTの１を使ってんのって感じだからこれから色々調べて扱ってみる。

## 謝辞
参考にさせていただいたのはストックマーク株式会社様の『BERTによる自然言語処理入門』という本です。
非常にわかりやすい本をありがとうございます。