CODE

【解説】初心者でもわかるチャットボットの作り方【アニメキャラとの会話も夢じゃない】

更新日:

今回はプログラミング初心者向けに、簡単なチャットボットの作り方を解説していきます。

という僕も初心者なんですがね。

まあ、あれですよ。大学の授業でそういう課題があったんです。

チャットボットとは

チャットボットとは、人間と会話するコンピュータです。

最近では宅急便のラインでの問い合わせなんかも、チャットボットになっています。

つまるところ、人間が何かしら入力するとコンピュータが返答してくれる、プログラムです。

この「チャットボット」には大きく分けて2種類あります。

ルール型と機械学習型です。

ルール型は「こういう文章に対してはこう返答する」みたいな感じで、あらかじめ解答を用意しておくタイプです。
人工無脳なんて呼ばれたりする。

機械学習型はその名の通り、学習しながら解答をします。

一時期話題になった、りんなは機械学習型です。膨大な会話データからそれっぽい返事をするやつです。

とまあ、ルール型と機械学習型があるわけですが、なんとなく機械学習型の方が高性能そうですよね?

用途にもよりますが、一般的には機械学習型の方が自然?な会話ができます。(家電の音声操作ではルール型で十分だったり)

でもごめんね。今から紹介するのはルール型です。

僕も初心者なんです。機械学習は環境が色々と面倒なんです。

プログラムの説明

というわけでプログラムの説明です。

このチャットボットがルール型だというのはさっき言った通りです。

入力した文章はまず形態素解析によって要素ごとに区切られます。

そしてあらかじめ用意しておいた、質問と回答のデータベースの中から、一番似ているものを選んで、その解答を出力するという感じです。

例えば、「今日の天気は?」という文章を入力すると、今日、の、天気、は、という感じで分けられます。

そして、分割した要素と一番被っている要素が多いデータベースを参照する、という仕組みです。
もしデータベース(質問:解答)が

明日の天気は?:自分で調べろや、アホ。
こんにちは:イェ―――――――――イ!!!!
好きな食べ物は?:ガソリンです。もちろんハイオクですよ。

こんな感じなら「今日の天気は?」という質問に対して、「明日の天気は?」が一番近い文章になるので、「自分で調べろや、アホ。」と出力されるわけです。

この例からも分かるように、データベースに無い質問をされると詰みます。変な解答を返します。
そこがデータベース型の欠点ですね。

環境構築

このプログラムはC#で書きます。

なので、開発にはVisual Studioを使います。

Visual Studioの導入方法はこちらの記事をどうぞ!

今回はC#なので.NETデスクトップ開発が必要です。

新しいプロジェクトの作成をして、テンプレートはコンソールアプリ(.NET Framework)を使います!

そしたら、MeCabという形態素解析するライブラリを導入していきます。

上のタブの、
ツール→NuGetパッケージマネージャー→ソリューションのNuGetパッケージの管理を選択します。

そしたら、参照で「mecab」を検索します。そしてチェックボックスにチェックをつけて、NMeCabをインストールします。

同意は適当にしといてください。これでMeCabがインストールできたはず。

コード解説

はあ、ここまで長かった。ではコードを解説していきます。

以下のusingディレクティブを使います。

using System;
using System.Collections.Generic;
using System.IO;
using NMeCab;

プラグラムの最初につけておいてください。

public static void Main(string[] args)
        {
            while (true)
            {
                var words = Console.ReadLine();
                Recog(words);
            }
        }

ここで言うwordsは入力です。Recogはメソッドです。次で解説するよ。

まあ、入力に対して返答する、というプログラムを1セットとして無限ループします。

static void Recog(string words)
        {
            var tagger = NMeCab.MeCabTagger.Create();
            var db = new QADatabase(@"E:\daigo\Desktop\Programing\C#\QAlist.txt", tagger);

            var sentence = ToArray(tagger.ParseToNode(words));
            string answer = db.NearestAnswer(sentence);
            Console.WriteLine(answer);
        }

これがRecogの中身です。

QADatabaseの中身の@以下は質問文と解答分のデータをtxtファイルにしたものの場所を指定してください。

QADatabaseも後で説明します。

sentenceのところでは、入力した文字列をToArrayメソッドで要素ごとにぶった切っています。

answerではそのsentenceをNearestAnswerで読み込むことで、一番似ているデータベースの回答を返します。

public static string[] ToArray(MeCabNode node)
        {
            var words = new List<string>();
            while (node != null)
            {
                if (node.Stat != MeCabNodeStat.Bos && node.Stat != MeCabNodeStat.Eos)
                {
                    words.Add(node.Surface);
                }
                node = node.Next;
            }
            return words.ToArray();
        }

ここがMeCabのややこしいところです。

文章を形態素で分けたnodeのまま比べればいいじゃないか、と。

僕もそう思ってましたよ!

でもMeCabの形態素解析は配列とかリストで出力されないんですよねー。

ちゃんと説明すると、MeCabのnodeは鎖みたいなイメージです。nodeに次のnodeは何かという情報が含まれているんですねー。

なので、文字列が終了するまでnodeの名前をリストに入れて、node.Nextで次のnodeに移って、を無限ループで繰り返しているわけです。

ちなみに、ifの中のBosとEosは文字列の最初と最後につけられているラベルみたいなものです。これに到達したらループから抜けます。

返す時は配列にして返します。words.ToArray()はこのToArrayメソッドとは関係ないです!

public class QADatabase
        {
            List<string[]> question;
            List<string> answer;
            public QADatabase(string filename, MeCabTagger tagger)
            {
                question = new List<string[]>();
                answer = new List<string>();
                using (var infile = new StreamReader(filename, System.Text.Encoding.GetEncoding("shift_jis")))
                {
                    while (infile.Peek() != -1)
                    {
                        var x = infile.ReadLine().Split(':');

                        question.Add(ToArray(tagger.ParseToNode(x[0])));
                        answer.Add(x[1]);
                    }
                    infile.Close();
                }

            }
            static double similarity(string[] x, string[] y)
            {
                var words = new HashSet<string>();
                foreach (string s in x)
                {
                    words.Add(s);
                }
                int count = 0;
                foreach (string s in y)
                {
                    if (words.Contains(s)) { count++; }
                }
                return (double)count / (Math.Sqrt((double)x.Length * y.Length));
            }
            public string NearestAnswer(string[] q)
            {
                double maxsim = -1;
                int maxind = -1;
                for (int i = 0; i < question.Count; i++)
                {
                    double sim = similarity(question[i], q);
                    if (sim > maxsim)
                    {
                        maxsim = sim;
                        maxind = i;
                    }
                }
                return answer[maxind];
            }
        }

はい、QADatabaseメソッドです。長いね、ごめんね。

最初で、txtファイルを読み込んで質問文と回答文に分けます。

質問文は例によって形態素に分けておきます。入力文と比べるためだよ!

でSimilarityで入力文と出力分を比べます。

まあつまり、文字列のnode数に対して、重複しているnodeがどれだけあるかで判定してます。

その類似度が一番大きいやつの回答をNearestAnswerで返します。

データベースの作り方

このデータベースですべてが決まると言っても過言ではありません。

データが多い方が回答の精度は上がります。

データベースの作り方は、適当なtxtファイルに1行ずつ

[質問]:[回答]

と入力していきます。

このデータベースの回答をアニメキャラのセリフにすれば、アニメキャラと会話しているかのような気分を味わえます。

僕の場合は、ヴァイオレット・エヴァーガーデンの回答を入れまくりました。

まあ、公開したい気持ちはあるんですが、著作権的にまずいのでね。

あなたの好きなキャラを召喚してください。

データ集めは手作業なのでめっちゃ大変です。

まとめ

こんな感じでプログラミングは完成です。

打ち込みじゃなくて、音声入力で音声で返すこともクラウドを使えばできるんですが、色々と面倒なのでまた別の機会に。





-CODE

Copyright© DaigoDiary , 2020 All Rights Reserved Powered by STINGER.