超シンプルな検索エンジンを作ってみたゾ!


はじめに

以下の記事を眺めていたら、自然言語処理を使って、大量データの中から、自分が求めているデータを抽出できるのって素敵だなーって感じていました。

なので、超シンプルな検索エンジンを作りました。

作った検索エンジンの仕組み

①蓄積した文章群から言語モデル(今回はtfidf)、ベクトルを作成。
②入力文章を言語モデルからベクトル化
③入力文章ベクトルと、蓄積した文章群の一つ一つとcos類似度を計算
④cos類似度が高い順にソートして、出力

ソースコードはこんな感じ

twitterから取得したデータを用いて、言語モデル、ベクトル作成。
※データ取得については、この記事の本質ではないので、今回は割愛します。

#データサイエンティスト、自然言語処理、機械学習を含むtwitterデータを使います。
#search_tweet_listが、取得したデータを保持しているリスト変数です。

#tfidfのために、twitterデータを分かち書きする関数
def wakati_word_list_create(sentences, get_word_class):
    #複数文から、指定の品詞(GET_WORD_CLASS)を抽出した単語リスト
    sentences_word_list = []

    for sentence in sentences:
        #一文から、指定の品詞(GET_WORD_CLASS)を抽出した単語リスト
        one_sentence_word_str = ''
        #形態素解析
        node = mecab.parseToNode(sentence)

        while node:

            #語幹
            word = node.feature.split(",")[-3]
            #品詞
            word_class = node.feature.split(",")[0]  
            #(指定の品詞(GET_WORD_CLASS)である) and (語幹が*のもの(つまり未知語))場合は、単語リストに追加
            if word_class in get_word_class and word != '*': 
                one_sentence_word_str = one_sentence_word_str + " " + word

            node = node.next
        sentences_word_list.append(one_sentence_word_str)
    return sentences_word_list

#twitterデータから、指定品詞の分かち分割形式の単語リストを生成
GET_WORD_CLASS = ['名詞', '形容詞']
wakati_word_list = wakati_word_list_create(search_tweet_list, GET_WORD_CLASS)

#tfidfベクトル化
from sklearn.feature_extraction.text import TfidfVectorizer

tfidf = TfidfVectorizer(use_idf=True, token_pattern=u'(?u)\\b\\w+\\b')
#言語モデル作成
tfidf_model = tfidf.fit(wakati_word_list)
#ベクトル空間作成
vectors = tfidf_model.transform(wakati_word_list).toarray()

tfidfとcos類似度で類似度を計算し、降順ソートして、出力する


import numpy as np

#cos類似度を計算する関数
def cos_sim(v1, v2):
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))

#検索時に何件を表示させるか
OUTPUT_NUMBER = 5

#検索したい文章、キーワード(入力データ)
search_sentence = ["ここに、なに調べたいかを書いてね"]

#wakati_word_list_createは、上のソースコードに書いている関数です。
wakati_input_texts = wakati_word_list_create(search_sentence, GET_WORD_CLASS)

#作った言語モデルはここで使うよ
tfidf_vectors = tfidf_model.transform(wakati_input_texts).toarray()

similars_map = {}

for sentence_id, sentence_vector in enumerate(vectors):
    similars_map[sentence_id] = cos_sim(tfidf_vectors[0], sentence_vector)
sorted_similars_map = sorted(similars_map.items(), key=lambda x: -x[1])

#任意件数(OUTPUT_NUMBER)を表示
print("入力文章:{}".format(search_sentence[0]))
for break_flag, similar_map in enumerate(sorted_similars_map):
    if break_flag == OUTPUT_NUMBER:
        break
    sentence_id = similar_map[0]
    sentence_similar = similar_map[1]
    if np.isnan(sentence_similar):
        if break_flag == 0:
            print("類似データは1件もなかったよ")
        break

    print("==========================================")
    print("類似度 : {}".format(sentence_similar))
    print("{}".format(search_tweet_list[sentence_id]))
    print("")

実行結果

結果①

結果②

ええ感じやでー( *´艸`)
特に結果②は、ちゃんとイベント情報をとれてるわ!素敵!!

もっと良くするには、を考えてみた

これを作ってみて、感じたのは、ベクトル化の手法がかなり大事だなって
ことです。
今回は、tfidfでベクトル化をしましたが、BM25、SCDV、word2vecなどで、全然違うでしょう。さらに、感情評価やネガポジ(極性)を取り込んでベクトル化してみたら、ネガポジの観点を含むような、ちょっと変わった検索エンジンができますね!
なによりも、こういうのには、Bertとか使ってみたいですね!!!どんな精度の検索がされるか、すごく気になります!!!!スペック足りないので、僕の家では作れないですけどね。。。(涙)

最後に

最後まで読んで頂き、ありがとうございました。
今回のソースコード書いていて、もっと、色々なNLPの手法について勉強してみたいと思いました。
次は、LDAとかやってみて、なにか作ってみようかな~!!('◇')ゞ