TadaoYamaokaの日記

山岡忠夫 Home で公開しているプログラムの開発ネタを中心に書いていきます。

TF-IDFを使ってFAQに回答する

とある理由からBotについて調べています。

最近はAIを使ったBotもありますが、古典的な方法として、あらかじめ質問と回答を用意しておき、ユーザが入力した質問と類似度の高い質問を選んで回答する方法があります。

TF-IDFがその代表的なアルゴリズムになります。

ここでは、TF-IDFをPythonで実装する方法を示します。

scikit-learnにあるTfidfVectorizerクラスを利用することで、簡単に実装できます。
日本語を扱う場合は、あらかじめ形態素解析を行う必要があります。
形態素解析にはMeCab(mecab-python3)を使用します。

fit_transformで、形態素解析した質問文のセットを使用してモデルを学習します。
モデルを使用して質問文をベクトル化した結果が出力されます。

transformで学習したモデルを使用してユーザの入力文をベクトル化し、scikit-learnのcosine_similarity関数で入力文のベクトルと用意した質問文のベクトルのコサイン類似度を計算します。
最もコサイン類似度が高い質問を選択し、その回答を表示します。

コード例

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
import MeCab

import argparse

parser = argparse.ArgumentParser(description="convert csv")
parser.add_argument("input", type=str, help="faq tsv file")
parser.add_argument("--dictionary", "-d", type=str, help="mecab dictionary")
parser.add_argument("--stop_words", "-s", type=str, help="stop words list")
args = parser.parse_args()

mecab = MeCab.Tagger("-Owakati" + ("" if not args.dictionary else " -d " + args.dictionary))

questions = []
answers = []
for line in open(args.input, "r", encoding="utf-8"):
    cols = line.strip().split('\t')
    questions.append(mecab.parse(cols[0]).strip())
    answers.append(cols[1])

stop_words = []
if args.stop_words:
    for line in open(args.stop_words, "r", encoding="utf-8"):
        stop_words.append(line.strip())

vectorizer = TfidfVectorizer(token_pattern="(?u)\\b\\w+\\b", stop_words=stop_words)
vecs = vectorizer.fit_transform(questions)

#for k,v in vectorizer.vocabulary_.items():
#    print(k, v)

while True:
    line = input("> ")
    if not line:
        break

    index = np.argmax(cosine_similarity(vectorizer.transform([mecab.parse(line)]), vecs))
    print(questions[index])
    print()
    print(answers[index])
    print()

TfidfVectorizerはデフォルトでは一文字の単語はトークンとして認識しませんが、日本語では一文字の単語(「雪」など)があるので、オプションで1文字もトークンとして認識するようにしています。

実行方法

>python tf_idf.py ..\data\faq.tsv -d H:\src\mecab-ipadic-neologd\build\mecab-ipadic-2.7.0-20070801-neologd-20170420 -s ..\data\stop_words.txt

引数にタブ区切りの質問と回答のテキストデータを指定し、オプションでMeCabの辞書とストップワードを指定しています。
ストップワードには、助詞や助動詞、連体詞、記号などを登録しておきます。

実行例

気象庁のよくある質問集で試した結果です。

> みぞれとは何ですか?
「 みぞれ 」 と は 何 です か ?

みぞれとは、雨と雪が混じったものをいいます。上空から雪が降ってくる途中、地上近くの気温が高いと、雪がとけて雨になります。一部分とけずに雪のまま落ちてくると、みぞれになります。みぞれは、観測分類上は雪に含めます。

> 台風とハリケーンの違いは?
台風 と ハリケーン と サイクロン の 違い は 何 です か ?

台風は、東経180度より西の北西太平洋および南シナ海に存在する熱帯低気圧のうち、最大風速が約17m/s以上になったものを指します。ハ リケーンは、北大西洋、カリブ海、メキシコ湾および西経180度より東の北東太平洋に存在する熱帯低気圧のうち、最大風速が約33m/s以上 になったものを指します。サイクロンは、ベンガル湾やアラビア海などの北インド洋に存在する熱帯低気圧のうち、最大風速が約17m/s以上に なったものを指します。このように、それぞれの名称を付している最大風速の基準には違いはありますが、台風もハリケーンもサイクロンもそれぞれの地域に存在する熱帯低気圧を強さによって分類している用語の1つということになります。なお、サイクロンは熱帯低気圧と温帯低気圧の区別をせず、広く低気圧一般を指す用語としても用いられることがあります。

> 異常気象について
「 異常気象 」 の 定義 は ある の です か ?

一般には、過去に経験した現象から大きく外れた現象のことを言います。大雨や暴風等の激しい数時間の気象から、数か月も続く干ばつ、極端な冷夏・暖冬まで含みます。また、気象災害も異常気象に含む場合があります。気象庁では、気温や降水量などの異常を判断する場合、原則として 「ある場所(地域)・ある時期(週、月、季節)において30年に1回以下で発生する現象」を異常気象としています。


文章における単語の重要度を見ているので、全文検索よりは精度が高くなりますが、古典的な方法であり類似語や質問の意味は解釈しません。
FAQに回答する場合では、これでも十分実用的です。