langchianlogo

LangChain入門 – 8)Embed 埋め込み

LangChain入門8回目です。Embed 埋め込みについて説明します。OpenAIなどの生成AIでは文字や文章をベクトルといった数値列に変換して、いろいろな処理を行っています。その内部では難しい数式が駆使されているのですが、ここではその雰囲気を感じていただけるよう説明を進めてゆきます。

本記事はFuture Coders独自教材からの抜粋です。

LangChain入門 – 8)Embed 埋め込み 目次

ベクトル表現

ベクトルと聞くと難しく感じるかもしれませんが、単なる数値の羅列のことです。2次元の座標は(x, y)、3次元の座標は(x, y, z)という形式で表現します。これらもベクトルにほかなりません。ベクトルの値から座標が求まります。

Embeddingとは言葉をこのようなベクトルで表現することです。もちろん、単語の数は膨大なのでx, y, zというような単純な値で表現することはできません。OpenAIでは1500もの次元で単語や文章を管理しています。確認してみましょう。

from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()

apple_vec = embeddings.embed_query("apple")
print(len(apple_vec))

doc_vec = embeddings.embed_documents(["hello, I am Taro", "How are you?"])
print(len(doc_vec[0]), len(doc_vec[1]))

embed_queryは単語を、embed_documentsは文章をベクトルに変換します。上記のプログラムを実行すると、以下のように出力されます。単語も文章も1536次元のベクトルとして表現されていることがわかります。

1536
1536 1536

実際のベクトルの値は以下のようになっていました。このような値が1536個も並んでいます。

[0.007714149542152882, -0.023108163848519325, -0.007467296905815601, -0.02778465300798416, -0.004645632114261389, 0.012966628186404705, -0.021860186010599136, -0.008523277938365936, 0.01891166716814041, -0.02984175831079483, -0.002809664700180292, 0.020118502900004387,...
]

似たような意味をもつ言葉であれば、似たような数値を持っていることになります。とはいえ、1536個の値を比較するのも大変です。どのくらい類似度があるかという数値で表現するための関数cosine_similarityが用意されています。

cosine_similarityは2つのベクトルを比較して、それらが1に近いほど類似度が高いということを意味します。

以下はざっくりとしたイメージでの説明です。cosineとは三角関数のCOS(コサイン)です。左の円の中心から矢印を引いた時に、矢印と円の交差したところからX軸に線を引いたところの値がCOSの値になります。オレンジ色の太い線です。

cos類似度
LangChain入門 – 8)Embed 埋め込み 5

2つのベクトルでこのCOSの値を求めます。似たような値の時はCOSの値は大きくなりますが、向きや大きさが違うときは小さくなります。このようなCOSの特徴をつかって、2つの値の類似度を計算して似た値を検索することができます。

apple(リンゴ), melon(メロン), cough(咳), covid(コロナ)の4つの単語の関連性をしらべてみました。

from langchain_openai import OpenAIEmbeddings
from langchain_community.utils.math import cosine_similarity
embeddings = OpenAIEmbeddings()

melon_vec = [embeddings.embed_query("melon")]
apple_vec = [embeddings.embed_query("apple")]
covid_vec = [embeddings.embed_query("covid")]
cough_vec = [embeddings.embed_query("cough")]


melon_apple = cosine_similarity(melon_vec, apple_vec)
melon_covid = cosine_similarity(melon_vec, covid_vec)
melon_cough = cosine_similarity(melon_vec, cough_vec)
apple_covid = cosine_similarity(apple_vec, covid_vec)
apple_cough = cosine_similarity(apple_vec, cough_vec)
cough_covid = cosine_similarity(cough_vec, covid_vec)

print(f"melon_apple:{melon_apple}")
print(f"melon_covid:{melon_covid}")
print(f"melon_cough:{melon_cough}")
print(f"apple_covid:{apple_covid}")
print(f"apple_cough:{apple_cough}")
print(f"cough_covid:{cough_covid}")

print(cosine_similarity(cough_vec, cough_vec))
melon_apple:[[0.89474356]]
melon_covid:[[0.7703209]]
melon_cough:[[0.78576505]]
apple_covid:[[0.7771166]]
apple_cough:[[0.7967254]]
cough_covid:[[0.83582836]]

図にすると以下のようになります。

類似度の相関
LangChain入門 – 8)Embed 埋め込み 6

同じ果物どうし、病気関連どうしの単語の類似度が高いことがわかります。

ベクトルストア

ベクトル化された情報は何らかの入れ物(データベース)に保存して、そこから似たようなものを速やかに検索できることが重要です。このようなベクトルに特化したデータベースやクラウドサービスがいろいろと提供されています。

  • https://www.pinecone.io/ = クラウド上のベクトルデータベースサービス
  • FAISS = Facebookが開発したオープンソースのベクトルデータベース

https://www.pinecone.io/

ここでは、FAISSを使って類似単語の検索をしてみます。

from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
words = ["リンゴ", "ゴリラ", "ランドセル", "ルビー", "ビール", 
         "カレーライス", "学生", "ジョギング", "勉強", "掃除", "宿題"]
embeddings = OpenAIEmbeddings()
faiss = FAISS.from_texts(words, embeddings)
faiss.save_local("content")

上記のプログラムを実行するとcontentというフォルダが作成され、その中にindex.faiss, index.pklというファイルが作成されます。上に列挙した単語がベクトルとして格納されています。

では、このデータベースに単語を問い合わせるアプリをStreamlitを使って作成してみます。

import streamlit as st
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
embeddings = OpenAIEmbeddings()

st.title("類似単語検索")
query = st.text_input("任意のテキスト")
if st.button("検索"):
    db = FAISS.load_local("content", embeddings)
    r = db.similarity_search(query)
    st.write(r)

FAISS.load_localからローカルのフォルダを読み込んでデータベースを作成します。そこに対して、similarity_searchを実行することで類似した単語を取得して表示しています。

類似語検索アプリ
LangChain入門 – 8)Embed 埋め込み 7

単語数が少なく実感が持ちにくいかもしれません。単語を増やしてみるとより実感が持てるかもしれません。

直接ベクトルの値を操作することはないと思いますが、生成AIの裏ではこのようなベクトルをつかった様々な処理が行われていることを知っておくと、次章以降の説明が理解しやすくなると思います。