【デュエマ】曖昧な質問で裁定検索ができるデモサイト作った
この記事の要約
・こんな感じに曖昧な文章でも関連したデュエマの裁定を検索できるデモサイトを作った。
・このページで使える。
・デモでしかないので速度・精度・動作は保証しないし新しい裁定にも対応しない。突然消す可能性もあり。お遊び程度のものと思ってもらえれば。
はじめに
こういった文はqiitaとかに投げた方が良いんだろうけど、デュエマプレイヤーにはあまり馴染みがない気がするのでnoteに書く次第。
前に灼熱ドロン・ゴーの裁定に関する記事を書いたが、その時のモチベーションの一つに「もっと公式のルールや裁定を引用して語ってほしい!」というのがあった。
灼熱ドロン・ゴーの裁定が正確に定まる前は、twitterを見る限りはかなりの人が二次情報を参照して議論しており、これはあまり良くないのではと感じていた。
とはいえ事細かにルールや裁定を引用するのが大変なのも事実である。その原因はいくつか存在しているが以下のようなものが挙げられるだろう。
この内上2つは自分1人ではどうしようもないことだが、最後の項目は技術で解消可能なのでどうにかできる。ので試しにやってみた。
問題の整理
「『よくある質問』ページが検索しにくい」とは言うが、「検索しにくさ」にも色々ある。
現状存在する「『よくある質問』ページの検索しにくさ」とは以下のようなものだと思う。
で、1.については最近定期的にカード検索の改善をしてくれている運営なのでいずれやってくれそうではある。2.については、自分の現状興味ある分野に関連するし実装されなさそうなのでやってみたい。
ということで2.を実装してみる。
実装
※一応「情報解析のための利用」の要件を満たす範囲でやってるのでこれに当てはまる、という理解でやってます。
基本的に以下を参考。
実装といってもデータベースに加工したデータを突っ込んで検索するだけなので大したことではないし、爆速で作ったので雑なところが多いのだがざっくり書いてく。
実装はpython3.10で行う。
要件の実現のために、今回はLangChainのVectorStoreを利用する。
VectorStoreはドキュメントを文字列ではなくベクトル化して保存し、関連するドキュメントの高速検索を実現するための機能を提供している。これにより、入力された文との完全一致や部分一致ではなく、関連度での検索を実現する。
つまるところ「入力された内容に近い文章」を探せるということだ。
データベースにはいろんなサービスが利用できるが、とりあえず目に付いたもので使い方がすぐ分かったQdrantを利用する。ChromaなどはLangChainと相互にドキュメントを置いてたりするので、こっちのほうが良いかもしれない。
まずはデータがなければどうしようもないので「よくある質問」のデータを取得する。ここは取得できればなんでもよいし本旨ではないので省略。
次にVectorStoreを利用する準備。以下のライブラリを入れる。
pip install langchain
pip install qdrant-client
pip install sentence-transformers
次にデータベースを作成する。
この際のembeddingの処理に、OpenAIのAPIを使ってOpenAIEmbeddings()を利用することは可能だが、今後似たようなものを作る可能性があることを考えると料金が発生するAPIを組み込みたくないので、huggingfaceのモデルを使ってembeddingを行う。データの保存方法は色々あるが、記事に倣いつつ扱いやすさを考慮してローカルに指定。
import glob
import json
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Qdrant
docs = []
# QAをjsonに保存しているので読み込み
json_pathes = glob.glob("rules/*.json")
for json_path in json_pathes:
with open(json_path, encoding = "utf-8") as f:
loaded_json = json.load(f)
for value in loaded_json.values():
# QA冒頭のQとAを削除
q_text = value["q_text"].lstrip("Q")
a_text = value["a_text"].lstrip("A")
source = value["answer_url"]
text_splitter = CharacterTextSplitter(chunk_size=150, chunk_overlap=15)
doc = text_splitter.create_documents(texts=[q_text], metadatas=[{"source": "Q:" + q_text + "\n" + "A:" + a_text + "\n" + source }])
print(doc)
docs.extend(doc)
embeddings = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-base")
# db作成
db = Qdrant.from_documents(docs, embeddings, path="local_qdrant", collection_name="qa_data")
今回はLLMを使ったような、「質問と回答」全体を元に文意を考慮した検索するのではなく、入力された質問との関連度の高い質問を検索したいので、回答部分をデータに含めると(カードゲーム特有の固有名詞の多さなどもあり)逆に精度が落ちると考え、質問部分のみをデータベースに入力している。
また検索結果にはQAの全体とURLを含めたいため、metadataのsourceにQA全体とURLを渡している。
注意として、モデルの選定や処理時のパラメータ設定は一番気を払ったほうが良い。元々oshizo/sbert-jsnli-luke-japanese-base-liteを使ってテストしていたが全く良い結果が出ず困っていたところ、intfloat/multilingual-e5-largeに変更したら一気に精度が良くなった。まあembeddingは「どのように文章を理解するか」という処理そのものなので、ここを重要視するのは当たり前ではある…。
パラメータは主にchunk_sizeの設定になるが、利用するモデルやサービスによって必要なサイズが変わってくる。また、サイズによって検索の精度も変わってくるのでどれが適切かを探る必要がある。一応自分はいくらか試してchunk_size=150に設定したが、別の最適解が存在するかもしれない。
データベースが作成できたらあとは検索するだけである。検索のコードは以下(データベースは一度作成したらそれを使い回せば良いので上記のコードとは別にする)。
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Qdrant
from qdrant_client import QdrantClient
embeddings = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-base")
client = QdrantClient(
path="./local_qdrant", prefer_grpc=True
)
db = Qdrant(client=client, embeddings=embeddings, collection_name="qa_data")
query = "芸魔龍王アメイジンの出た時の効果は、後から出たクリーチャーも影響しますか"
# kは出力する結果の数
docs = db.similarity_search_with_score(query=query, k=10)
for i in docs:
doc, score = i
print({"score": score, "content": doc.page_content, "metadata": doc.metadata} )
このコードにある「芸魔龍王アメイジンの出た時の効果は、後から出たクリーチャーも影響しますか」という文章は「よくある質問」には存在しないのだが、この検索結果(doc.metadataの部分のみ)は以下。
いい感じなので良し!
というか「『アメイジン』という単語は入ってないが同じルールに基づく裁定」もちゃんと拾ってるのでかなり良い。デモとしては十分だろう。
ということで、huggingfaceで使えるようにちょっとだけ修正して終わり。
雑感
・元々はRetrievalQAを使ってLLMに回答してもらうことで文意を考慮して回答してもらおうと思っていたが、回答が遅いし動作は重いし思ったより適切に答えが返ってこないし…という感じで面倒だったので、関連度で検索できればいいやと とりあえずこの形で実装した。
簡単な質問ならこれだけで思った以上にちゃんと関連した裁定を探せるし、そもそも固有名詞がいくつも存在する細切れのQA集というデータセットで調整してどこまでうまい返しができるのか?というのもあった。
・カードや総合ルールのデータ、よくある質問のQA全体のテキスト込みでいい感じにチューニングしてLLMに答えさせればまた別の結果は得られるだろうが、めんどくさいので機械学習好きで強いGPUを持ってるDMPに任せたい。
・カードゲームの裁定のテキストは固有名詞が非常に多いので、embeddingの際の処理が特に大事というのを身にしみて感じた。あらゆる学習に言える。
・ひとまずテキスト関連はこのくらいにして、次は遊戯王のニューロンに搭載されているようなカード認識機能を作りたい。かなり前に簡素なものは作ったが(下みたいなの)、今ならより良い精度で作れそう。
・サイトの使い方として、基本的に「よくある質問」の文章は敬体で書かれているので、ですますで質問を入力したほうが精度は高い。また複雑な質問だと関連回答が出にくいので、表示数を増やしたり、固有名詞はなるべく正式名称を入れるようにしたりして検索してもらえれば。ただし出ないときは出ない。デモなのであしからず。
データベースでも機械学習でもなんでもやるんでタカラトミー雇ってください!!
この記事が気に入ったらサポートをしてみませんか?