ベクトル検索エンジンでChatGPTのコンテキストを作る方法
はじめに
ChatGPTの問題に、2021年までのデータでしか学習しておらず、最新の情報に答えることができないことがあります。例えば昨日の東京の天気を教えてと、小学生でも分かる質問にすら答えることができません。また、インターネットに公開していない情報、例えば企業の内部情報や個人のプライベートな情報にも無力です。人間もそうですが、人工知能と言えど知らないことは知らないのです。
その問題の解決策の一つにファインチューンがあります。自社データを持ち込み学習モデルの微調整を行う方法です。つい先日、Azure版のgpt-3.5 turbo
のファインチューンがサポートされました。ただこの方法はお金もかかりますし、Azure環境を利用するなど敷居が高いです。
そこで本ブログでは、ベクトル検索エンジンを併用した、お手軽で低コストにChatGPTを賢くする方法をご紹介したいと思います。
この方法ではプロンプトが4096トークン
まで使用できることに着目し、最大限に活用します。
ざっくり説明すると、あらかじめ自社データをベクトル検索エンジンに保存しておきます。ユーザが入力したプロンプトに類似する文章をベクトル検索で抽出します。抽出した文章をオリジナルのプロンプトと一緒にくっつけてChatGPTに渡します。つまり、コンテキストを作ることで精度を上げるわけです。
これはOpenAIでも紹介されている公式な方法になります。
この記事では自然言語処理では定番のlivedoorニュースコーパスを使用し、ベクトル検索エンジンとChatGPTをどのように組み合わせるのか説明したいと思います。
まず先に結果を見せちゃいましょう
『週刊誌で交際が報じられた芸能人を教えてください。』 と質問(プロンプト)してみました。
まずは改良していないデフォ版のChatGPT API
の結果です。
田中みな実と中尾明慶、石原さとみと山下智久、広瀬すずと山崎賢人、橋本環奈と佐野勇斗、綾瀬はるかと松山ケンイチ、木村拓哉と工藤静香、浜崎あゆみと渡辺直美、長澤まさみと村上信五、井上真央と松本潤、北川景子とDAIGO、石田ゆり子と小栗旬、綾野剛と星野源、福山雅治と吹石一恵、嵐の櫻井翔と相葉雅紀、AKB48の指原莉乃と渡辺麻友、乃木坂46の生田絵梨花と山下美月などが挙げられます。
『綾野剛と星野源』や『生田絵梨花と山下美月』など、まさかのBL・百合展開になっています。これは、英語でhallucinationと呼ばれる現象で、ChatGPTが現実には存在しない、架空の商品名やお店をでっち上げてしまう症状です。
さて次は、改良版のChatGPT APIの結果です。
田中みな実さんとHIROさん、橋本環奈さんと山崎賢人さん、石原さとみさんと星野源さん、広瀬すずさんと竹内涼真さん、綾瀬はるかさんと松山ケンイチさん、などが報じられています。ただし、報道内容は常に真実とは限りませんので、ご注意ください。
交際というか共演者のようですが、BL・百合展開は回避できたようです。どうも、『交際』の解釈が違ったようです。熱愛、不倫などの用語を使用すると結果は変わります。別の例もあとでご紹介します。
ベクトル検索エンジンとは?
ベクトル検索とはセマンティック検索とも呼ばれ、ドキュメント全体の意味を考慮して検索する手法です。ドキュメントをベクトルで表現し、同じ方向性(コサイン類似度)であれば、それらのドキュメントには類似性があるとみなします。ドキュメントをベクトルで表現することをembedding
と言います。
ベクトル検索エンジン自体にはドキュメントをベクトル化する機能はありません。特に海外のベクトル検索エンジンは日本語に対応していません。GiNZAなどを使用して別途ベクトル化しておく必要があります。
ベクトル化やベクトル検索自体はChatGPTの話とは関係がないためここでは説明しません。詳細はこちらの記事をご覧ください。
ChatGPTとの結合
さて、いよいよ本題に入ります。ChatGPTと併用する方法を説明します。
全体のおまかな流れは以下のようになります。
- ライブドア記事をベクトル検索エンジンQdrantに登録する
- プロンプトをベクトル化する。GiNZAを使用してベクトル化します。
- ベクトル検索エンジンQdrantを使用してライブドアの類似記事を検索する
- プロンプトを再構築し、ChatGPT APIを呼び出す
1番目は先ほどの私のブログを参照してください。
2 プロンプトをベクトル化
ユーザが入力したプロンプトをベクトル化します。もちろん、ライブドア記事のベクトル化と
全く同じ方法でベクトル化する必要があります。
import os
from typing import List
import spacy
import openai
from qdrant_client import QdrantClient
from qdrant_client.http.models import ScoredPoint
# GiNZAの辞書(spaCy)をロードします。
nlp: spacy.Language = spacy.load('ja_ginza', exclude=["tagger", "parser", "ner", "lemmatizer", "textcat", "custom"])
openai.api_key = os.getenv("OPENAI_API_KEY")
collection_name = "livedoor"
# ベクトル検索エンジンQdrantクライアント
qdrant_server = QdrantClient(host='localhost', port=6333)
def get_vector(text: str) -> List[float]:
doc: spacy.tokens.doc.Doc = nlp(text) # ベクトル化します
return doc.vector
3 類似記事の検索
先ほどベクトル化したプロンプトを使用して類似記事をベクトル検索エンジンから取得します。注意点として、4096トークンの制約があるため6件ほどしか類似記事をねじ込めません。
def search_similar_docs(query_vector: List[float]) -> List[str]:
hits = qdrant_server.search( # Qdrantを呼び出します。検索します。
collection_name=collection_name,
query_vector=query_vector, # プロンプトのベクトル値
query_filter=None,
with_payload=True,
limit=6 # 類似記事を6件取得します
)
docs = []
for hit in hits: # 類似記事がリストで返ります
h: ScoredPoint = hit
print(h.payload.get('body')) # ペイロード部分に記事本文が登録してある
docs.append(h.payload.get('body'))
return docs
4 プロンプトを再構築
プロンプトを再構築します。取得した類似記事の文章をプロンプトにねじ込みます。
system : "Answer the question as truthfully as possible using the provided text below, and if you're unsure of the answer, say Sorry, 'I don't know'"
user : (ここにプロンプトと似ているライブドアニュースをねじ込む)
user : (ここにユーザの実際の質問)
system
でアニメのキャラ設定のようなことができます。ここでは以下のキャラ設定を英語でしています。日本語よりも英語で指示を与えた方が精度が上がります。
『なるべく正直に、ねじ込んだ類似記事を使用して答えてください。自信がない場合は、知らないと答えてください。』
実際のソースコードになります。比較のためaugment(ブール値)
で、改良版の有効/無効の切り替えができるようにしました。
def chatgpt_query(query: str, docs: List[str], augment=True) -> str:
"""Query the chatgpt model using OpenAI API."""
messages = []
if augment: # 改良版フラグ
# キャラ設定
messages.append({"role": "system",
"content": "Answer the question as truthfully as possible using the provided text below, and if you're unsure of the answer, say Sorry, 'I don't know'"})
for doc in docs: # 検索した類似記事をねじ込む
messages.append({"role": "user", "content": doc})
# 実際にユーザが入力したプロンプト
messages.append({"role": "user", "content": query})
# ChatGPT APIを呼ぶ
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=messages,
temperature=0.0, # 1.0に近づくほどランダムになる
)
return response['choices'][0]['message']['content']
以上で完成です。
さあ、実行してみましょう
いくつかの実行例をお見せします。改良版(augment=True)が上です。
踊る大捜査線の『恩田すみれ』を演じた女優は、深津絵里さんです。
---
女優の名前は、上戸彩(うえとあや)さんです。
正解は、深津絵里さんです。昔はやった刑事ドラマです。ライブドアニュースが2012年の記事コーパスであるためです。日本のAIを発展させたいのであればデジタル庁さん、なんとかしてください。
11月4日付け日刊スポーツによると、
日本ハム編成トップの山田GMが事前に巨人側に菅野の指名はしないと申し入れていたが、
それでも指名に踏み切ったことに、菅野サイドの不信感は募っていったため、
巨人に入団することを諦め、1年間の浪人を決断したとされています。
---
甲子園での活躍が評価され、プロ野球選手としての道を歩むことを目指していた菅野投手ですが、
高校卒業後にプロ入りするためのドラフト指名を受けることができませんでした。
そのため、大学進学を考えたものの、野球を続けるためには浪人することを決断しました。
菅野投手は、浪人してまでプロ野球選手としての道を歩むことを決めた理由として、
自分自身の野球力を高めるために時間をかけたかったということが挙げられます。
また、プロ野球選手としての将来を見据え、
より高いレベルの野球を経験することが必要だと考えたため、浪人を選択したとも言われています。
巨人の菅野投手は、2011年のドラフトで日ハムに指名されたものの、幼少期からの夢であった、伯父である原辰徳の巨人軍のユニフォームを着ることを希望し、浪人を選択したと言われています。しかし、デフォ版のChatGPTの回答の妄想ぶりが半端ないですね。キャラ設定をプロ野球ニュースなどにすれば良いかもしれません。
U-23日本代表×U-23ニュージーランド代表の一戦は、後半26分、
徳永悠平が放ったミドルシュートのこぼれ球を杉本健勇が押し込み、先制点を挙げるも、
後半ロスタイムも残り1分、日本はパスミスからまさかの失点。勝利を目前に痛いドローに終わった。
---
2021年6月12日に行われたキリンチャレンジカップの日本対ニュージーランドの試合は、
日本が1-0で勝利しました。得点者は、後半に出場したMF南野拓実選手でした。
改良版の回答は2012年のキリンチャレンジカップのことを言っています。
1−1(前半0−0)ドロー。得点 杉本健勇(後半26分)
くどいですがライブドアニュースが2012年の記事であるため、改良版はそちらに調整されています。
NVIDIAのGPU戦略の中心となる新アーキテクチャーは、Keplerアーキテクチャーです。
Keplerアーキテクチャーは、従来のFermiアーキテクチャーに比べて消費電力が大幅に削減され、
新しいアンチエイリアス機能によって画像のなめらかさが向上しています。
また、自動クロックアップ機能「GPU Boost」も搭載され、高性能かつ高機能なGPUとなっています。
---
NVIDIAのGPU戦略の中心となる新アーキテクチャーは、Ampereアーキテクチャーです。
Ampereアーキテクチャーは、高度なAI処理やデータセンター向けの高性能コンピューティングに
最適化されており、従来のGPUアーキテクチャーよりも高速で効率的な処理を実現します。
また、Ampereアーキテクチャーは、NVIDIAの新しいGPU製品であるGeForce RTX 30シリーズや
NVIDIA A100 Tensor Core GPUなどに採用されています。
改良版は2012年の、デフォ版は2021年でのアーキテクチャーになります。2023年時点の最新アーキテクチャーはAda Lovelace
です。
補足
日本語は英語に比べるとトークン数が増えてしまうようです。
キリンチャレンジカップの試合結果について教えてください。日本とニュージーランドの試合です。
Please tell me about the game results of the Kirin Challenge Cup. It's a game between Japan and New Zealand.
トークンのカウントはこちらのページからできます。tiktokenというライブラリもあります。英語の場合は、100 tokens ~= 75 words
だそうです。
OpenAI APIの課金は、プロンプトとChatGPTの回答のトークン数の従量課金になります。したがって、英語で聞いた方が安く済みます。この記事の執筆にあたりバカスカ使いましたが1ドルほどで収まりました。安いですね。
考察と改良
記事の要約
現状では4096トークンの制約があるため、プロンプトにねじ込める記事の量に限りがあります。OpenAIを使用して要約した記事をベクトル化したり、使用するライブドア文章の選別をするなどが必要です。全く同じ記事を排除するなど。
フィルター検索
ベクトル検索と併用してフィルター検索を使用するとより精度の高い類似文書の抽出ができます。Qdrantでもタグなどのメタデータで絞り込み検索が可能です。
レスポンス
トライアル版のOpenAI APIが原因かもしれませんがレスポンスが遅いです。特にデフォ版の場合は応答が返ってこない場合があります。プロンプトを長く(トークン数を増す)と少し改善するようです。
Bing Chat
私はBing Chat
を使えるのですが、とにかくChatGPTよりも圧倒的な精度で驚きです。裏の仕組みとしてBing検索を併用しているためでしょう。Google検索やツイート検索を併用するのも良いでしょう。
以上です。
Discussion