はじめに
クジラ飛行机、 杉山 陽一、遠藤 俊輔 著「PythonでつくるWebアプリのつくり方」を読み,Pythonを使ったWebアプリ開発の基礎を学ぶことにした。
本記事は,「chapter5 機械学習を使ったWebアプリを作ろう」における,セマンティック検索に関する読書メモである。
本記事を読むことで得られること
本記事を読むことで得られることは,主に以下の内容である。
- Sentence Transformersを用いた文書のEmbedding獲得とその利用法について
04 Embeddingを利用したセマンティック検索ツールを作ろう
要点
セマンティック検索とEmbedding
セマンティック検索は,検索クエリの単語そのものではなく,検索クエリの意味を理解して検索する手法である。
単語の意味を理解させるためには,Embedding(埋め込み表現)と呼ばれる,テキストや画像などのデータを数値ベクトルに変換する技術である。テキストデータを数値ベクトルに置き換えることで,意味的な近さを扱えるようになり,表記ゆれがある場合や異なる単語間で検索をかける場合でも対応できるようになる。
Sentence Transformers
Embeddingを扱うためには,Pythonのライブラリであるsentence-transformersを用いる。
本節の気付き
Sentence Transformersのインストール
本節では,Sentence Transformersのインストールは単にpip install をすればよいと書かれていたが,embedding_search\embedding_test.pyを動かすためには,tf-kerasをインストールする必要があった。
tf-kerasをインストールした後であれば,無事にembedding_search\embedding_test.pyが動作した。

本節の気付き : 単語の埋め込み表現の2次元プロット
本書P278にあった,単語の埋め込み表現の2次元プロットを作成してみた。コード作成には,ChatGPTを用いた。
- サンプルコード
from sentence_transformers import SentenceTransformer import matplotlib.pyplot as plt from sklearn.decomposition import PCA import matplotlib.font_manager as fm import matplotlib # Windows用:MS Gothicを指定して日本語文字化けを防止 jp_font = fm.FontProperties(fname="C:/Windows/Fonts/msgothic.ttc") matplotlib.rcParams['font.family'] = jp_font.get_name() # Embeddingに使用するモデルを指定 model = SentenceTransformer("stsb-xlm-r-multilingual") # 単語リスト words = ["リンゴ", "バナナ", "フルーツ", "犬", "猫", "飛行機", "動物", "コンピューター", "ノートパソコン", "小鳥", "ライオン"] # 単語をベクトルに変換 embeddings = model.encode(words) # 次元削減(PCAで2次元へ) pca = PCA(n_components=2) embeddings_2d = pca.fit_transform(embeddings) # プロット plt.figure(figsize=(10, 8)) for i, word in enumerate(words): x, y = embeddings_2d[i] plt.scatter(x, y) plt.text(x + 0.01, y + 0.01, word, fontsize=12, fontproperties=jp_font) plt.title("単語ベクトルの2次元プロット(PCA)", fontproperties=jp_font) plt.grid(True) plt.show()
- 出力

「フルーツ」・「バナナ」・「リンゴ」の組合せや,「コンピューター」・「ノートパソコン」の組合せが近くに配置されていた。「飛行機」と「猫」が近くにプロットされているのは解せないが,これは次元削減にPCAを使っていたためで,例えばt-SNEなどの非線形な次元削減手法を使えば,これらの単語は離れて配置されるようになるかもしれない。
本節の気付き : 検索アプリ
本書で紹介されていた検索アプリのコードである「search_app.py」について,機能ごとに内容を確認した。
モデル・インデックスの読み込み
このアプリでは,事前に検索対象をインデックスに置き換えている。そして検索アプリでは,Embeddingに用いるモデルと,インデックスを読み込んでいる。
# Embeddingに使用するモデルを指定 --- (※1) model = SentenceTransformer("stsb-xlm-r-multilingual") # Embeddingのインデックスを読み込む --- (※2) with open(INDEX_FILE, "rb") as fp: database = pickle.load(fp)
それぞれファイルサイズが大きいため,検索アプリを稼働させる際には,ファイルの読み込み時間を考慮する必要がある。
検索
検索を行なう際には,
- 検索クエリの文章をEmbeddingに置き換える
- 検索クエリとインデックスの類似度(similarities)を計算する
という順で計算を行なう。
# 検索クエリをEmbeddingに変換 --- (※5) query_embedding = model.encode(query, convert_to_tensor=False) print(query_embedding) # 類似度を計算 --- (※6) index = database["index"] embeddings = np.array([x[2] for x in index]) similarities = util.cos_sim(query_embedding, embeddings)[0] # 類似度が高い順にソート --- (※7) sorted_indices = similarities.argsort(descending=True) # 結果を表示 --- (※8)
検索結果
検索Webアプリにおける検索結果は以下のようになる。

まとめと感想
今回は,「chapter5 機械学習を使ったWebアプリを作ろう」における,セマンティック検索についてまとめた。
Embeddingについてよく例に出されるのはWord2Vecで,学習の手順なども紹介されているが,既存のモデルを使った方が精度が良いと考えられるので,積極的に使用したいと思った。一方で,専門用語が含まれる場合もあるので,セマンティック検索だけではなく,単語辞書やナレッジグラフなどを用いた検索を併用したほうが精度が上がるのではないかと感じた。
検索アプリについては,実施していることは文書の埋め込み表現に関するコサイン類似度を測る,という比較的シンプルなことをしているが,検索対象が多くなると時間がかかりそうだと感じた。効率よくコサイン類似度を計算する方法なども調べてみたい。
本記事を最後まで読んでくださり,どうもありがとうございました。