見出し画像

python初心者がチャットボットを作ってみた。その2

1. はじめに


python初心者の私がAidemyを受講し、サポートを受けながらチャットボットを作成した経緯について「心理カウンセラーがチャットボットを作ってみた。」で紹介しました。今回はその続編です。
rinna社による事前学習済の言語モデルGPT-2をベースに、”調子を尋ねる”チャットボット作成のため自作したコーパスでファインチューニングし、、LineBot化まで行いました。
Herokuにデプロイする上でサイズの制限があったのでサイズをmediumからxsmallに変更したため、あまり学習されず会話があまり成り立たなくなったという問題点が出てきました。

唐突な…

また、ボット(今回「アイ」と名付けています)が「死にたい」など不適切な発言をすることがありました。
ですのでこの記事では、引き続きAidemyのサポートを受けながら、なるべく自然な会話をすること、不適切な問題の出現を避けるための工夫についてお伝えします。

2.実行環境


windows 11
Python 3.7.13
Heroku
※なお、基本的なコードやLINE Massaging APIについては前の記事をご覧ください。

3.replaceによる不適切な単語出現の回避


前の記事でお伝えしたように、ボットの発話に記号が含まれないように以下のコードを記載しました。

output = output_1[0][length:50].replace("<","").replace("/","").replace("(笑)","").replace("^","").replace(">","").replace("(","").replace(")","")

これを踏襲し、「死」という単語を空白に置き換えました。

.replace("死","")

かなり地道な作業ですが、今後も会話を重ねて不適切な発言が出現するごとに追加していきます。

4.JSONを使った発話辞書の作成


前の記事でお伝えしたように、元々想定したチャットボットが”調子を尋ねる”もので、会話の最初はボットによる「こんにちは。調子はどうですか?」の発話を想定していました。

print("アイ:こんにちは。調子はどうですか?")

しかしLINEBot化するには適していないので、ユーザーの発話(挨拶)から開始することにしました。
しかし、最初の図のように、「こんにちは」といった挨拶に応えてくれないと、こちらとしては出鼻をくじかれる思いになりますよね…。
ですので、挨拶など会話で出現しやすい発話に対するボットが返答する発話のペアの辞書を新たに作成しました。

そこで、JSONというデータフォーマットを用いました。JSONはJavaScript Object Notationの略です。「「JSON」とは?非エンジニアでもよくわかる解説」を引用しますと、「テキストをベースにした、軽量なデータ交換をおこなうためのフォーマット」です。以下のように、keyvalueのペアを{}で格納することが出来ます。なお、valueは複数記述することが出来ます(その場合はリストになり、[]で囲みます)。

例えば以下のようなデータを作ることが出来ます(「JSON」とは?非エンジニアでもよくわかる解説」を引用)。

{

 “ID”: 1,

 “Name”: “Taro”

 “property”: {

 “age”: 18,

 “birth”: “2001/10/03”

 }

}

これを今回のチャットボットで考えてみると、「ユーザーの発話=key」、「ボットの発話=value」と考えます。
例えばkeyを「こんにちは」としておきます。それに対するvalueも「こんにちは」としておきます。ただ、”こんにちは→こんにちは”だけでは味気ないので色んな挨拶をvalueに記述しておきます。

"こんにちは": [ "こんにちは", "こんにちは、調子はいかがですか?", "こんにちは!" ]

上のような要領で他の挨拶のペアを記述していきます。

  "おはよう": [ "おはようございます", "おはよう、調子はいかが?", "おはよう!" ],
  "こんにちは": [ "こんにちは", "こんにちは、調子はいかがですか?", "こんにちは!" ],
  "こんばんは": [ "こんばんは", "こんばんは、調子はいかがですか?", "こんばんは!" ],
  "おやすみ": [ "はーい、おやすみなさい", "ゆっくり休んでね", "また明日!" ],
  "さようなら": [ "さようなら", "バイバイ", "またね!" ],
  "バイバイ": [ "うん、またね", "バイバイ", "また会いましょう" ]

挨拶以外も考えてみます。例えばユーザーが「死にたい」や「辛い」といった発話をした時に気遣うことが出来るように、「死」や「辛」に対応して以下のようにしてみます。ちなみに「死にたい」や「辛い」とは限らない可能性があるので、あたりさわりのない発話にしてみました。

"死": [ "まずはゆっくりやすみましょう", "どうしたの…?", "もう少し聞かせて…?" ],
"辛": [ "どうしたの…?", "そうなんだ…", "もう少し教えて…?" ]

また、valueはurlでもOKです。例えばユーザーが癒されたい時、癒しの音楽をお勧め出来るように、以下のコードを追加します。ちなみに私はハワイが好きなのでハワイアンミュージック - のんびりできる作業用BGMMusic Hawaiʻi ~リラックス編~としておきます。その他に「おなかがすいた」の場合にはグルメ情報を紹介するようにしました。
余談ですが、アカウント名「Lani-Kai」はハワイオアフ島にあるラニカイビーチから付けました。

"癒": [ "https://youtu.be/AhTt5aVIPsw", "https://www.youtube.com/watch?v=FBOWyhkWLkk&t=1496s"]

URLは画像形式でも可能です。今回一つのみ示していますが、複数のURLを追加出来ます。
今回はまたしてもハワイ画像です。これで「画面」に対してハワイのフリー画像を送ってくれるようにしました。

"画像": [ "https://cdn.pixabay.com/photo/2014/04/03/00/39/hawaii-309006_960_720.png" ]

作成したらjson形式で保存します。前の記事で作成したフォルダ(「Line_Bot」)内に入れておきます。

前の記事で作成したmain.pyをいくつか修正します。

① 修正前です。

from flask import Flask, request, abort
import os
import logging
from transformers import T5Tokenizer, AutoModelForCausalLM
import string

今回新たにimport reimport randomimport jsonを追加します。

from flask import Flask, request, abort
import os
import re
import random
import json
import logging
from transformers import T5Tokenizer, AutoModelForCausalLM
import string

② 修正前です。

from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

今回は画像ファイルも扱うのでImageMassageImageSendMessageを追加します。

from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,ImageMassage, ImageSendMessage
)

③ 修正前です。

def handle_message(event):
    tex = ["。", "?", "?" , "!", "!", "." ]
    message = event.message.text
    input_ = tokenizer.encode(message, return_tensors="pt")
    output_0 = model.generate(input_, do_sample=True, max_length=40, num_return_sequences=1, 
                         top_p=0.95, top_k=20, bad_words_ids=[[1],[2],[5]], no_repeat_ngram_size=1)
    output_1 = tokenizer.batch_decode(output_0)
    length = len(message) + 4

tex = ["。", "?", "?" , "!", "!", "." ]の下に、以下を追加します。
以下のコードでjsonファイルを開きます。ちなみに今回ファイル名は「main2.json」です。jsonファイルを開き、それをjson_open変数に代入します。

json_open = open('./main2.json', 'r')

次に開いたjsonファイル(json_open)をjson.loadによって辞書型として読み込みます。

data = json.load(json_open)

修正後は以下のようになります。

def handle_message(event):   
    message = event.message.text
    tex = ["。", "?", "?" , "!", "!", "." ]
    json_open = open('./main2.json', 'r')
    data = json.load(json_open)
    input_ = tokenizer.encode(message, return_tensors="pt")
    output_0 = model.generate(input_, do_sample=True, max_length=40, num_return_sequences=1, 
                         top_p=0.95, top_k=20, bad_words_ids=[[1],[2],[5]], no_repeat_ngram_size=1)
    output_1 = tokenizer.batch_decode(output_0)
    length = len(message) + 4

④ 修正前です。

    for t in tex:
        if t in output:
            output = output.split(t)[0] + t
        else:
            output

    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=output))

for構文によってkey, valueの繰り返し処理をします(jsonデータ内の全てのペアを参照していきます)。イメージとしてはデータの中にkeyが見つからなければ次の処理に進み、見つかればばvalueのどれかをランダムで出現させる、という感じです。
まず「画像」のkeyをserachします。画像処理を行うのでImageSendMessageの設定が必要です。「【Python】写真に日付を付けてくれるLINE Bot作った」の「画像の送信」で詳述されています。
次に画像以外のkeyをsearchします。また、「おはよう」や「おはようございます」にも対応出来るように、reモジュールを使って正規表現に対応しておきます。以下が修正後のコードです。

    for t in tex:
        if t in output:
            output = output.split(t)[0] + t
        else:
            output
    for key, value in data.items():
        if re.search(key, message) == None:
            continue
        elif re.search(key, message)[0] == "画像":
            img = random.choice(data[re.search(key, message)[0]])
            line_bot_api.reply_message(
            event.reply_token,
            ImageSendMessage(original_content_url=img, preview_image_url=img))
        elif key == re.search(key, message)[0]:
            output = random.choice(data[re.search(key, message)[0]])
        else:
            output

5.Herokuにデプロイ


前回と同様の手順でHerokuにデプロイします。ちなみに前回で既にherokuでLINEチャンネルアクセストークンとチャンネルシークレットを登録していますので、以下のコードは不要です。

# 以下は不要
heroku config:set LINE_CHANNEL_ACCESS_TOKEN="チャネルアクセストークン" --app アプリ名
heroku config:set LINE_CHANNEL_SECRET="チャネルシークレット" --app アプリ名 

6.完成


まずは挨拶してみました。ようやくあいさつしてくました(喜)!

「ありがとう」、「嬉しい」、「辛い」といった発話に対しては以下のように返してくれました。

続いて、「癒されたい」とコメントするとハワイアンソングのURLを送ってくれました。

「おなかすいた」とコメントすると飯テロのURLを送ってくれました。

「画像ください」と送るとハワイの画像を送ってくれました。なお、私が試したところでは写真のような大きいデータは送ってくれませんでした。

7.終わりに


ちょっとした機能を搭載するだけでも苦労したので、「りんな」等、既存のチャットボットはすごいことを実感しました。
ただ、チャットボットは色んな工夫が出来るのでやりがいがありますね!
引き続き色んな事にトライしてみます。

この記事が気に入ったらサポートをしてみませんか?