文章を探す時にベクトル検索を行う際の課題と解決策のメモ
大きな背景: GPTのchatbotが微妙
最近は、GPTのpromptにオリジナル文書を載せて回答させるchatbotが流行ってます。
文章検索は、GPTのembedベクトル計算機能を使うのが一般的で、llammaindex などのライブラリにも内蔵されています
個人的には、SBERTやローカルLLMでも、大丈夫だと思っています
しかし、実際に運用してみると、思ったように動かないという問題に直面します
技術的な背景
簡単に、ベクトル検索の特徴について記します。cojiさんに教えて貰った記事から抜粋(翻訳)していきます
この記事面白かったです!https://t.co/JZuCuBJ6TY
— 溝口 浩二 coji💿 (@techtalkjp) May 19, 2023
ベクトル検索の利点
![](https://assets.st-note.com/img/1684539282573-YdEArfOwHy.png?width=1200)
キーワード検索の利点
![](https://assets.st-note.com/img/1684539382370-AMjCX8oBnD.png?width=1200)
ベクトル検索は、意味にフォーカスした検索が可能で、従来のキーワード検索は、単語を拾うのが得意、という感じです。
ベクトル検索で個人的に困った点
ベクトル検索では、目的の文書になかなか辿り着けないという問題に直面しました。特に困ったのは以下のケース
言語を跨ぐケース
ベクトル検索の本来の趣旨からすると、日本語のクエリで英語の文章を見つけられるはずです
しかし、実際に運用すると、殆ど辿り着けませんでした。
意味としては関係の薄い、日本語の文章がヒットすることが多かったです
とりあえずの解決策として、DeepLのAPIで翻訳を噛ませることにしました
これで、概ねOKです。 (ただしDeepLは専門用語の翻訳をミスるので、100%満足という訳ではないです)
固有名詞や専門用語を検索するケース
Embedding計算の問題点の一つは、未学習の単語に対してvectorを生成する能力が低いということです
なので、固有名詞や専門用語の検索をかけても、基本的にヒットしないことが多かったです
上手く行かない実例
以下に、J-Stageというプラットフォームで公開されている、高分子論文集をデータベースに設定し、XX先生の研究業績を探させるというタスクの結果を示します。Embed計算はSBERTで行いました。
![](https://assets.st-note.com/img/1684539882715-Y8eaKQqZo8.png?width=1200)
本来は、XX先生の論文がヒットすべきなんですが、全く関係のない文献ばかりが引っかかるという、残念な結果に終わりました。
ベクトル検索の問題点
いくつかの技術的な問題点が存在するようです。基本的には、2つの問題があると考えています。
一つ目は先述の通り、特にマニアックな単語に対して、ベクトル計算の精度が低いという点です。
二つ目は、クエリ文字列と文献の中身が、質的に異なるという点です。
「猫の名前を知る」という実例で考えてみます。
クエリ文字列
猫の名前は?
検索したい文章
吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
太字部分は、ベクトル情報として、「クエリ文字列」と「検索したい文章」の類似度が高いと期待される箇所です。しかし、検索したい文章には、「どこで生れたか…」という、質問文とは関係性の低いテキストが続きます。なので、ベクトルとしての類似度が下がります。もし代わりに、「犬の名前は太郎です」という短文の文献がデータベース中にあった場合、こちらが引っかかる可能性が出てきそうです。
解決策1: promptの回答を検索する(私の用途ではNG)
ベクトル検索の強みを生かす解決策として、質問そのものではなく、(文献情報を与えない条件下での)GPTの回答を検索クエリにかけるというアプローチがあるようです。
GPT-3.5に「猫の名前は?」と聞くと、以下のような答えが返ってきます。
![](https://assets.st-note.com/img/1684540808324-3iGYqDreCh.png?width=1200)
「猫の名前は?」でデータベース検索するよりも、「私は仮想的な存在であり、直接猫の名前を知ることはできません。猫の名前は飼い主が選ぶものであり、様々な名前が存在します。」というプレ回答をクエリにかけるというアプローチになります。
確かに、文章全体としての類似度は、オリジナルの質問よりも高いような気がします。
猫の名前は?
吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。
私は仮想的な存在であり、直接猫の名前を知ることはできません。猫の名前は飼い主が選ぶものであり、様々な名前が存在します。
しかしこの手法の問題点は、やはり、マニアックな事項に弱いということです。
例えば筆者のような泡沫研究者について質問しても、まともな回答は返って来ません。この回答をデータベースと照合する価値はほぼゼロです。
![](https://assets.st-note.com/img/1684541066250-TVvBgFDgy7.png?width=1200)
解決策2: キーワード検索を併用する
ベクトル検索には限界があるので、十分な蓄積のあるキーワード検索を併用
するのが2023年5月時点での、現実的なアプローチであるように思います。
ただし、どのようにハイブリッドするかという問題が出てきます。
このあたり、どうしようかと迷っていたところ、LLM Meetup Tokyo #2で相談したら、色々とアドレスを貰えたという次第です。
LLM Meetup Tokyo #2、来週 5月18日(木) 19:00 〜@メルカリで実施します。
— 溝口 浩二 coji💿 (@techtalkjp) May 11, 2023
参加エントリー開始しました。来週月曜日 12:00 までお受け付けして、事務局にて選考・その後参加者へのご連絡を行います。キャパが厳しく申し訳ありません。
どうぞよろしくおねがいします!https://t.co/1XAsbGgNB0
アイデア
両者の結果を線形結合する
キーワード検索を併用する
embedやキーワード検索で出てくる各文献と、質問の類似度をGPTに判断して貰う
単語やフレーズを区切ってembed計算し、類似度を計算する
cross encoder re rankingする
いくつかのアイデアについて詳しく考えていきます。
線形結合は真っ先に思い付きますが、微妙なようです
![](https://assets.st-note.com/img/1684541587959-7GsCgTHgQp.png?width=1200)
![](https://assets.st-note.com/img/1684541611493-tAbHH2yhdZ.png?width=1200)
embedやキーワード検索で出てくる各文献と、質問の類似度をGPTに判断して貰う
両情報をプロンプトに載せた上で、「関係性を1,2,3,4,5の五段階で出力しろ」(or Yes/No出力)みたいな命令を出せば、うまくrankingできそうです。検索時間と金銭コストが増えるのが欠点です
cross encoder re rankingする
どうやらこちらの方法が、Qdrant的には一押しのようです。
まとめ(by GPT-4)
GPTのチャットボットとベクトル検索は一部問題点があり、特にマニアックな単語の検索精度が低下する。
言語を跨ぐ検索や固有名詞、専門用語の検索はベクトル検索では難易度が高い。
GPTの回答を検索クエリとして利用するアプローチやハイブリッド検索の提案がありますが、マニアックな情報には対応力が低い。
おまけ: cross encoder re rankingとは何なのか?
こちら、現在勉強中のことなので、メモ書きになります。間違っているかもしれません。
以下のメカニズムだと解釈しました。
queryに対して、ベクトル検索とキーワード検索の結果をピックアップ
1で出てきた候補とqueryの関連性を、SBERTなどのcross-encoderで計算
関連順に文献をソート
以下、cojiさんに教えてもらったサイトの日本語訳の抜粋です。確かに、精度が上がってます。
![](https://assets.st-note.com/img/1684542414702-30juXi1nfp.png?width=1200)
![](https://assets.st-note.com/img/1684542459543-3ba9Kh4H2j.png?width=1200)
![](https://assets.st-note.com/img/1684542517775-RHtCoBf97d.png?width=1200)
以下、GPT-4とお勉強していきます
Q. cross encoder re rankingとはどういうことか
A. Cross-encoder re-rankingは、情報検索や推薦システムなどのコンテキストでしばしば使用される手法の1つです。
大まかに言うと、この手法は、一連の可能性のある出力候補(たとえば、検索結果の一覧や推奨アイテムのリストなど)を評価し、最も関連性の高いものを最初に提示するために使用されます。
具体的には、"cross-encoder"は2つの異なる入力(通常はクエリとドキュメントまたはアイテム)を1つの連続的な表現にエンコードするモデルです。これに対し、"bi-encoder"はそれぞれの入力を個別にエンコードし、それらのベクトル表現の間の類似度を計算します。
Cross-encoder re-rankingのプロセスは以下のようなステップで行われます:
初期検索フェーズ:最初に、bi-encoderを使用して大量の文書またはアイテムから関連する候補を取得します。このフェーズでは、各ドキュメントとクエリが個別にエンコードされ、それらの類似度が計算されます。結果として得られる上位Nの文書が次のフェーズへ進みます。
リランキングフェーズ:次に、cross-encoderを使用して、各候補とクエリの関連性をより正確に評価します。このフェーズでは、クエリと各候補ドキュメントが1つのモデルに入力され、それらの関連性スコアが直接出力されます。これにより、リストは新たにランク付けされ、最も関連性の高い項目が最初に表示されます。
このアプロチは、検索や推奨などのタスクでより精度の高い結果を得るのに役立つことがあります。しかし、その一方で、cross-encoderの使用は計算コストが高いため、bi-encoderでフィルタリングを行う初期フェーズが必要となります。
Q. Cross-encoder re-rankingではSBERTなどが利用されているようだが、未学習の単語に対して正しく処理できるのか?
Q. SBERT(Sentence-BERT)は、BERTモデルを改良したもので、2つの文章の意味的な類似性を効率的に計算できるように設計されています。
Cross-encoder re-rankingにおいては、クエリとドキュメントのペアをエンコードし、その関連性を評価するのに使用されます。
それに対して、未学習の単語(すなわち、モデルの訓練データに含まれなかった単語)については、SBERTや他のTransformerベースのモデルは、その単語を知らないという意味では「完全に」理解することはできません。しかし、これらのモデルはサブワードトークン化(例えば、BERTはWordPieceトークン化を使用します)を採用しているため、未知の単語をそのサブワードの部分に分解することができます。これにより、モデルはその未知の単語の一部について何らかの意味的理解を得ることができる可能性があります。
しかし、これはあくまで暫定的な解決策であり、その単語が特定の文脈でどのように使用されるか、またはその単語が持つ固有の意味についての深い理解をモデルが得ることはできません。それゆえ、未学習の単語が検索クエリやドキュメントに頻繁に出現する場合、それはリランキングの精度に影響を及ぼす可能性があります。
したがって、モデルが未学習の単語に適切に対応するためには、その単語を含む新しいデータを用いてモデルを定期的に再訓練することが最良の方法と言えるでしょう。
所感
まだ勉強中なのですが、
ベクトル表現での計算が、実質的に、二回入っているのが気になります。
ベクトル表現で質問との類似度計算
キーワード検索で質問との類似度計算
ベクトル表現で、1,2の結果と質問との類似度計算
疑問点
速度を十分に担保できるシステムなら、始めから3だけをやれば良いような気がしないでもない
専門用語の問題をクリアできているのか?
最終結果が、ベクトル表現の方に引っ張られるかも?
これ以上は、試してみないと分からなそうです。
少し立て込んでますが、引き続き検討していく予定です。
この記事が気に入ったらサポートをしてみませんか?