自然言語処理(BERT)を用いたチャットボット

# はじめに

「強化学習と自然言語処理を用いたチャットボット」を作る前工程として、「自然言語処理(BERT)を用いたチャットボット」を作ります。

レポジトリはこちらです。

https://github.com/sugiyama404/chat_bot_bert

# BERTとは[^1]

BERTは、google検索やDeeplなどで使われています。とても、高精度で使用しやすいです。

BERTの学習では、事前学習とファインチューニングの二つに分かれます。事前学習で言語の基礎を学習し、ファインチューニングで、個別のタスクに合うように調整します。

![Screen Shot 2021-09-28 at 16.34.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/f26031d0-da3e-4c57-50fa-8cf600b14662.png)

## 事前学習
事前学習は、その言語がどうゆう構造なのか、単語の意味はどういう意味なのかなど言語の基礎を理解させます。具体的には、TransformerがMask Language ModelとNext Sentence Predictionで文章から、文脈を双方向に学習します。

### Mask Language Model
　文章から、特定の単語を15%ランダムに選び、[MASK]トークンに置き換えます。

```console:MaskLanguageModelの例
['[CLS]', '私', 'は', '[MASK]', 'に', '所属', 'しています', '[SEP]']
```
[MASK]された単語を文脈から予測します。
[MASK]＝公安9課


![Screen Shot 2021-09-28 at 16.35.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/b6d7bc97-3315-7437-4386-b3b2b430294f.png)

### Next Sentence Prediction
二つの文章に関係があるかを判定します。後ろの文章を50%の確率で、無関係な文章と置き換えます。後ろの文章が意味的に正しければIsNext、正しくなければNotNextの判定をします。

![Screen Shot 2021-09-28 at 20.09.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/2c56eab6-e946-578a-3024-bccffa200798.png)


### 形態素解析

形態素解析とは、日本語を意味を持つ最小単位に分割することです。日本語は、英語と違い、スペースで区切られていないので、形態素解析が必要です。
今回は、 MeCab を Python から使用する際のラッパーライブラリであるfugashiを使います。

```console:形態素解析の例
私 / は / 公安9課 / に / 所属 / して / います
初めまして / ボク / は / 、 / タチコマ
```

### 分かち書き

文節ごとに空白で区切ることを言います。

```console:分かち書きの例
私  は  公安9課  に  所属  して  います
初めまして  ボク  は  、  タチコマ
```

## 転移学習（ファインチューニング）
事前学習で得られたパラメータを初期値として、ラベル付きのデータでファインチューニングを行います。

## 実装
### HuggingFaceとは

https://github.com/huggingface/transformers/tree/master/examples#squad

HuggingFaceとは、自然言語処理に特化したディープラーニングのフレームワークです。

コーディングをする際に以下のサイトを参考にしました。

https://note.com/npaka/n/na8721fdc3e24

### データセット
https://nlp.ist.i.kyoto-u.ac.jp/index.php?Driving%20domain%20QA%20datasets

もしくは、

```python:インストール
git clone https://github.com/huggingface/transformers
cd transformers
pip install .
pip install -r requirements.txt
!pip install fugashi[unidic-lite] ipadic datasets
```

```python:モデル学習
!python ./examples/legacy/question-answering/run_squad.py \
    --model_type=bert \
    --model_name_or_path=cl-tohoku/bert-base-japanese-whole-word-masking \
    --do_train \
    --do_eval \
    --train_file=DDQA-1.0_RC-QA_train.json \
    --predict_file=DDQA-1.0_RC-QA_dev.json \
    --per_gpu_train_batch_size 12 \
    --learning_rate 3e-5 \
    --num_train_epochs 10 \
    --max_seq_length 384 \
    --doc_stride 128 \
    --overwrite_output_dir \
    --output_dir output/
```

```python:リプライ関数
from transformers import BertJapaneseTokenizer, AutoModelForQuestionAnswering
import torch

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

def reply(context, question):

    inputs = tokenizer.encode_plus(question, context, add_special_tokens=True, return_tensors="pt")
    input_ids = inputs["input_ids"].tolist()[0]
    output = model(**inputs)
    answer_start = torch.argmax(output.start_logits)  
    answer_end = torch.argmax(output.end_logits) + 1 
    answer = tokenizer.convert_tokens_to_string(tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end]))
    answer = answer.replace(' ', '')
    return answer
```

### チューニング用文章

>私の名前は、思考戦車のタチコマです。私は公安9課に所属しています。
思考戦車とは人工ニューロチップを用いた人工知能（AI）を搭載し、自ら「思考」し自律的に行動することができる戦車


```python
context  = '私の名前は、思考戦車のタチコマです。私は公安9課に所属しています。'\
'思考戦車とは人工ニューロチップを用いた人工知能（AI）を搭載し、'\
'自ら「思考」し自律的に行動することができる戦車'

question = "あなたの名前は？"
answer = reply(context, question)

print("question: " + question)
print("answer: " + answer)

question = "あなたの所属は？"
answer = reply(context, question)

print("question: " + question)
print("answer: " + answer)

question = "思考戦車とは何ですか？"
answer = reply(context, question)

print("question: " + question)
print("answer: " + answer)
```

```console
question: あなたの名前は？
answer: 思考戦車のタチコマ
question: あなたの所属は？
answer: 公安9課
question: 思考戦車とは何ですか？
answer: 人工ニューロチップを用いた人工知能(AI)を搭載し、
自ら「思考」し自律的に行動することができる戦車
```

## 組み込み

![Screen Shot 2021-09-26 at 13.49.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/f27ccca5-a744-19cc-82d7-3b73ea16057a.png)

質問に対して、回答ができていると思います。
ただ、かなり無機質な回答ですね。
そこで、回答文に口調変換を行い、個性付けを行う必要があります。

#口調変換

## 類似文章検索

類似文章検索とは、元の文章を比較して、どれだけ近いかを確率で表します。
つまり、「質問＋bertからの応答」により近い文章を検索することができます。


例えば、
「あなたの名前は？思考戦車のタチコマ」と「私はタチコマです。」
0.7184281945228577

「あなたの名前は？思考戦車のタチコマ」と「私は犬のことがとても好きです。」
0.6292034983634949

となります。

```python
from bert_score import score
def calc_bert_score(cands, refs):
    Precision, Recall, F1 = score(cands, refs, lang="ja", verbose=False)
    return F1.numpy().tolist()

cand = "あなたの名前は？思考戦車のタチコマ"

refs = ["私はタチコマです。","私は犬のことがとても好きです。","初めまして！ボクは、タチコマ！"]
cands = []
for i in refs:
    cands.append(cand)

f1 = calc_bert_score(cands, refs)
data = []
for item, ref in zip(f1, refs):
    data.append([item, ref])
data.sort(reverse=True)

print()
print(cand)
for it,it2 in data:
    print(it,' ',it2)
```

```console
あなたの名前は？思考戦車のタチコマ
0.7184281945228577   私はタチコマです。
0.7169045209884644   初めまして！ボクは、タチコマ！
0.6292034983634949   私は犬のことがとても好きです。
```

## 口調変換

今回は、bertからの返答に以下の操作をすることで、口調変換をし、個性を添加することにします。

(a)bertからの返答＋「だよ」
(b)類似文章検索を用いて、「bertに対する質問＋bertからの返答」に最も近い個性を持った想定文章

この二つを組み合わせることにします。

どの文章が出るかは、下記の確率により、選択したいと思います。

係数の0.5は(a)の出る確率を制御しています。
おおよそ、係数と同じような確率になります。

```python:確率算出のための関数
def change_prob(data):
    prob = []
    for item in data:
        prob.append(item[0])
    leng = len(prob)
    prob[0] = prob[0] * leng * 0.5
    m = nn.Softmax(dim=1)
    input = torch.tensor(prob)
    input = torch.reshape(input, (1, leng))
    output = m(input)
    x_numpy = output.to('cpu').detach().numpy().copy()
    return x_numpy.reshape(leng,)
```

# 組み込み

![Screen Shot 2021-09-27 at 19.28.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731268/89cd15c6-1be0-c1b1-f1b5-f98f1f73b816.png)



[^1]: 'BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding', Jacob Devlin Ming-Wei Chang Kenton Lee Kristina Toutanova, (2019), Google AI Language