COTOHA 類似度算出APIを使って、FAQ検索システムを構築してみた


本文書について

COTOHA APIの類似度算出を使って、FAQ検索システムを構築してみた

COTOHA APIの類似度算出について

COTOHAの類似度算出APIは2つの文章の類似性を数値化し出力してくれる。
少し使ってみたが、思いの外精度良く文間の類似度を算出してくれた。

入出力例

import os
import json
import requests
import pickle

CLIENT_ID = 'XXX'
CLIENT_SECRET = 'XXX'
API_BASE_URL = 'https://api.ce-cotoha.com/api/dev/nlp/'
ACCESS_TOKEN_PUBLISH_URL = 'https://api.ce-cotoha.com/v1/oauth/accesstokens'

def get_access_token():
    headers = {'Content-Type': 'application/json',
               'charset': 'UTF-8',}
    data = {'grantType':'client_credentials',
            'clientId':CLIENT_ID,
            'clientSecret':CLIENT_SECRET}
    data = json.dumps(data)
    response = requests.post(ACCESS_TOKEN_PUBLISH_URL, headers=headers, data=data)
    response = json.loads(response.text)
    return response['access_token']

if not os.path.isfile('./ACCESS_TOKEN.pickle'):
    ACCESS_TOKEN = get_access_token()
    with open('ACCESS_TOKEN.pickle', mode='wb') as f:
        pickle.dump(ACCESS_TOKEN, f)
with open('ACCESS_TOKEN.pickle', mode='rb') as f:
    ACCESS_TOKEN = pickle.load(f)

def similarity(s1,s2):
    global ACCESS_TOKEN
    headers = {'Content-Type': 'application/json',
               'charset': 'UTF-8',
               'Authorization': 'Bearer '+ACCESS_TOKEN}
    data = {'s1':s1,
            's2':s2}
    data= json.dumps(data)
    response = requests.post(API_BASE_URL+'v1/similarity', headers=headers, data=data)
    response = json.loads(response.text)
    if response['status'] == 99998:
        ACCESS_TOKEN = get_access_token()
        with open('ACCESS_TOKEN.pickle', mode='wb') as f:
            pickle.dump(ACCESS_TOKEN, f)
        return similarity(a,b)
    return response['result']['score']

print(similarity('近くのレストランはどこですか','この辺りの定食屋はどこにありますか'))
print(similarity('この辺りの定食屋はどこにありますか','本日のおすすめ定食'))
0.91079295 #'近くのレストランはどこですか':'この辺りの定食屋はどこにありますか'
0.46422136 #'この辺りの定食屋はどこにありますか':'本日のおすすめ定食'

上記の例だと、同じ定食という単語を持つ文同士よりも、レストラン定食屋という意味は似ているが異なる単語を持つ文同士のほうが、類似度が高い結果となっている。
単語の表層だけでなく、意味情報まで類似度算出に利用しているように見受けられる。

FAQ検索システムの構築

本記事では、類似度算出APIの出力結果を利用して、簡単なFAQ検索システムを構築してみる。
題材としては、qiitaのFAQデータのすべての質問テキストを用いることとし、
新たな質問を入れた場合に、FAQ中の似ている質問をスコア順で出力させる。

動作環境

python3.6.2

FAQ検索システム

class FAQ:
    def __init__(self):
        self.questions = list()
        self._set_questions()

    # FAQはテキストデータから読み込み
    def _set_questions(self):
        with open('faq_texts.txt', mode='r') as fin:
            for line in fin:
                line = line.strip()
                self.questions.append(line)

    # 似ているQを返す
    def extract_similar_questions(self,text):
        question_scores = dict()
        for q in self.questions:
            score = similarity(q,text)
            question_scores[q] = score
        return [k for k,v in sorted(question_scores.items(), key=lambda x: x[1], reverse=True)]

使ってみる

「英語に変えたい」

faq = FAQ()
result = faq.extract_similar_questions('英語に変えたい')
for i,q in enumerate(result[:3]):
    print(i+1,q)
1 言語の変更について
2 投稿を非公開にするには
3 Qiitaの投稿をMarkdown表示にする方法

キーワードとして一致していないにもかかわらず、英語言語変えたい変更が類似していることを読み取った出力結果になっている。

「良い記事を書きたい」

faq = FAQ()
result = faq.extract_similar_questions('良い記事を書きたい')
for i,q in enumerate(result[:3]):
    print(i+1,q)
1 良い記事を書くためのガイドライン
2 良い投稿記事にいいねしよう
3 記事を投稿する

ガイドライン、読んでおきます。ありがとうございます。

「いいねを増やしたい」

faq = FAQ()
result = faq.extract_similar_questions('いいねを増やしたい')
for i,q in enumerate(result[:3]):
    print(i+1,q)
1 絵文字の利用について
2 タグやユーザーをフォローしよう
3 Twitter・Github連携を解除してしまった際の再ログイン方法

まとめ

COTOHAの類似度算出APIについて、出力をいくつか見てみたが意味等の解析もできており、悪くないと感じた
一方で、FAQ検索に使おうとすると、無料環境の制限(1000回/日?)に簡単に引っかかってしまう
実用的に利用するとなると、Enterprise版の検討が必要そうだ