32
Help us understand the problem. What are the problem?

posted at

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

はじめに

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

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

BERTとは1

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

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

Screen Shot 2021-09-28 at 16.34.04.png

事前学習

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

Mask Language Model

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

MaskLanguageModelの例
['[CLS]', '私', 'は', '[MASK]', 'に', '所属', 'しています', '[SEP]']

[MASK]された単語を文脈から予測します。
[MASK]=公安9課

Screen Shot 2021-09-28 at 16.35.23.png

Next Sentence Prediction

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

Screen Shot 2021-09-28 at 20.09.06.png

形態素解析

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

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

分かち書き

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

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

転移学習(ファインチューニング)

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

実装

HuggingFaceとは

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

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

データセット

もしくは、

インストール
git clone https://github.com/huggingface/transformers
cd transformers
pip install .
pip install -r requirements.txt
!pip install fugashi[unidic-lite] ipadic datasets
モデル学習
!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/
リプライ関数
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)を搭載し、自ら「思考」し自律的に行動することができる戦車

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

組み込み

Screen Shot 2021-09-26 at 13.49.41.png

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

口調変換

類似文章検索

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

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

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

となります。

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)
あなたの名前は?思考戦車のタチコマ
0.7184281945228577   私はタチコマです。
0.7169045209884644   初めまして!ボクは、タチコマ!
0.6292034983634949   私は犬のことがとても好きです。

口調変換

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

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

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

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

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

確率算出のための関数
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


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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
32
Help us understand the problem. What are the problem?