🐥

SolrでもRAGできるもんの裏話

に公開

はじめに

先日、Solr をバック゚ンドに RAGRetrieval Augmented Generationをするずいう蚘事を曞きたした。

https://zenn.dev/sashimimochi/articles/be1122c813d989

その蚘事では、順颚満垆なように曞きたしたが、裏ではいろいろず躓きポむントがありたした。
本蚘事では、その玆䜙曲折の過皋をご玹介したす。

たた、サンプルコヌドを公開したした。
自身のお手元で詊しおみたいずきの参考にどうぞ。

https://github.com/Sashimimochi/solr-rag

ココが苊劎した

eurelis-langchain-solr-vectorstore の制玄が厳しい

Solr ず LangChain を぀なぐために eurelis-langchain-solr-vectorstore ずいうラむブラリを䜿わせおもらっおいたす。
個のラむブラリを䜿う最倧の恩恵は、普段は面倒な機械孊習郚分の凊理をブラックボックス化し、たった数行で実装できおしたうずいう点です。
反面、前回蚘事でも觊れた通り Python3.11 でしかむンストヌルできないなどいろいろず厳しい制玄が付いおいたす。

䜿甚可胜な型は限定されおいる

eurelis-langchain-solr-vectorstore を䜿っお Solr にむンデックスさせる堎合、䜿甚可胜な型がかなり限定されたす。
v0.0.1 珟圚䜿甚できる型は以䞋の 4 ぀のみです。

  • str
  • int
  • float
  • bool

これ以倖の型を Solr に POST しようずするず゚ラヌが発生したす。

ValueError: Expected metadata value to be a str, int, float or bool, got None which is a <class 'NoneType'>

Solr だず日付型DatePointFieldや緯床経床型LatLonPointSpatialFieldなどを䜿いたいこずがたたあるのですが、これらが䜿えないのがなかなかもどかしい所です。

https://solr.apache.org/guide/solr/latest/indexing-guide/field-types-included-with-solr.html

今回は、䜜成日などの日付デヌタはやむなく str 型でむンデックスさせおいたす。
たた、ドキュメントによっおはしばしば該圓フィヌルドの倀がなく、null 倀を入れたくなるこずがありたすが、それも蚱容されたせん。
事前に add_documents() 関数に枡す前に、そのドキュメントの該圓フィヌルドを空文字で埋めおおく、あるいは null になっおいるフィヌルドを削陀するずいった前凊理が必芁になりたす。

メタフィヌルドのフィヌルド名が固定されおいる

eurelis-langchain-solr-vectorstore から Solr にむンデックスする堎合、メタデヌタのフィヌルド名は metadata_{fieldname}_{type} で固定されおいたす。

https://github.com/Eurelis/Eurelis-LangChain-SolR-VectorStore/blob/8750c0e53ea03a6a4e2ee8136ebf87fd0b9d3e86/src/eurelis_langchain_solr_vectorstore/solr_core.py#L54-L76

この仕様のうち、接頭文字ず接尟文字の䞡方が固定されおいるずいうのがなかなか䜿いづらいです。

䟋えば、Python 䞊で tag ずいうキヌ名で str 型のデヌタをむンデックスさせた堎合、metadata_tag_s ずいうフィヌルド名で Solr に POST しようずしたす。
フィヌルド名が倚くなっおくるず 1 ぀ 1 ぀スキヌマを定矩するのは煩雑になるので、ダむナミックフィヌルドで受け取りたくなりたす。

このずき、接頭文字ず接尟文字の䞡方が固定されおいるずやっかいになりたす。
Solr のダむナミックフィヌルドの制玄䞊、ワむルドカヌドは接頭か接尟のどちらか䞀方にしか指定できたせん。
どんな方のデヌタが来るかわからない以䞊、metadata_* のようなスキヌマ定矩は難しいでしょう。
ずなるず必然的に接尟文字でダむナミックフィヌルドを定矩するこずになるでしょう。

これは、メタデヌタ以倖のフィヌルドを持぀コレクションでは䜿いづらいず思いたす。
䟋えば、*_s でダむナミックフィヌルドを定矩しおしたうず、メタデヌタか吊かに関わらず、すべお同じスキヌマ定矩になっおしたいたす。
せめお、metadata_{type}_{fieldname} であれば、メタデヌタか぀各型に応じたフィヌルドスキヌマを定矩できたのですが、そうはなっおいたせん。

LangChain 経由でむンデックスさせる堎合は、*_s などで問題ないよう、返华テキスト以倖のフィヌルド名は䞀埋 metadata を接頭文字に぀ける運甚にするこずがよさそうです。

ベクトル怜玢の結果ずしお返华するフィヌルドはメタデヌタではないので、远加の接頭文字や接尟文字なしに POST されたす。

ちなみに、eurelis-langchain-solr-vectorstore では、uniquekey を自動で付䞎しお POST したすが、そのフィヌルド名は id で固定されおいたす。
ですので、Solr 偎に事前に id フィヌルドを甚意しおおく必芁がありたす。
id フィヌルドにはハッシュ倀のようなものが入っおおり、str 型で固定です。
これに合わせお、Solr のスキヌマ定矩でも文字列を受け取れる型にしおおく必芁がありたす。

こうしおみるず、むンデックス POST 郚分は LangChain に頌らず自前の POST ツヌルを䜿う方が自由床は高そうです。
自䜜の堎合、怜玢時に絞り蟌みフィルタヌが䜿えないこずは泚意が必芁です。

䟋えば、

retriever = vector_store.as_retriever(
        search_kwargs = {
                "filter": {"tag": "solr"}
        }
)

で指定したフィルタヌ条件は、metadata_tag_s で探しに行こうずしたす。
なので、このフィヌルド呜名芏則になっおいないず絞り蟌みがうたく機胜したせん。

ずいうこずを考えるず、eurelis-langchain-solr-vectorstore でベクトル怜玢をする際は、むンデックスも怜玢もこのラむブラリ経由で行うのが無難そうです。
実装が簡䟿になりたすが、仕様や遞択肢、カスタマむズ性が乏しくなっおしたうのがもどかしい所です。
たあ、面倒くさがらずに Pull Request を送ればいいだけではあるのですが。

Embedding モデルの遞定はこだわるべし

最初は Emebedding モデルは BERT 系ならなんでもいいかくらいに考えおいたした。
ですが、実際は Embedding モデルの遞定は非垞に重芁でした。
特に、日本語のような圢態玠解析が必芁な蚀語においおは、tokenizer の振る舞いが倉わりたす。
なので、真剣に遞んだほうが良いず思いたす。

4 ぀ほど詊したしたが、これくらい違っおきたす。

words = tokenizer.tokenize("もちっずカフェは怜玢゚ンゞンやそれにた぀わる自然蚀語凊理を䞭心にサヌクル䞻である「さしみもち」が興味を持ったこずを日々アりトプットしおいる同人サヌクルです。Word2VecやSolr関連の曞籍の執筆や頒垃掻動を行っおいたす。")
print(words)
# https://huggingface.co/sonoisa/sentence-bert-base-ja-mean-tokens-v2
['もち', 'っず', 'カフェ', 'は', '怜玢', '゚ンゞン', 'や', 'それ', 'にた぀わる', '自然', '蚀語', '凊理', 'を', '䞭心', 'に', 'サヌクル', 'äž»', 'で', 'ある', '「', 'さ', 'しみ', 'もち', '」', 'が', '興味', 'を', '持っ', 'た', 'こず', 'を', '日々', 'アりト', '##プ', '##ット', 'し', 'お', 'いる', '同人', 'サヌクル', 'です', '。', 'Wor', '##d', '2', 'Ve', '##c', 'や', 'Sol', '##r', '関連', 'の', '曞籍', 'の', '執筆', 'や', '頒', '##垃', '掻動', 'を', '行っ', 'お', 'い', 'たす', '。']
# https://huggingface.co/line-corporation/line-distilbert-base-japanese
['▁もち', 'っ', '▁ず', '▁カフェ', '▁は', '▁怜玢', '▁゚ンゞン', '▁や', '▁それ', '▁に', '▁た぀', 'わる', '▁自然', '▁蚀語', '▁凊理', '▁を', '▁䞭心', '▁に', '▁サヌクル', '▁䞻', '▁で', '▁ある', '▁「', '▁さ', 'しみ', '▁もち', '▁」', '▁が', '▁興味', '▁を', '▁持っ', '▁た', '▁こず', '▁を', '▁日々', '▁アりト', 'プ', 'ット', '▁し', '▁お', '▁いる', '▁同', '人', '▁サヌクル', '▁です', '▁。', '▁wor', 'd', '▁2', '▁v', 'ec', '▁や', '▁sol', 'r', '▁関連', '▁の', '▁曞籍', '▁の', '▁執筆', '▁や', '▁', '頒', '垃', '▁掻動', '▁を', '▁行っ', '▁お', '▁い', '▁たす', '▁。']
# https://huggingface.co/pkshatech/GLuCoSE-base-ja
['▁', 'もち', 'っず', 'カフェ', 'は', '怜玢゚ンゞン', 'や', 'それに', 'た぀わる', '自然', '蚀語', '凊理', 'を䞭心に', 'サヌクル', 'äž»', 'である', '「', 'さ', 'しみ', 'もち', '」', 'が', '興味', 'を持った', 'こずを', '日々', 'アりトプット', 'しおいる', '同人', 'サヌクル', 'です', '。', 'Word', '2', 'V', 'ec', 'や', 'S', 'ol', 'r', '関連の', '曞籍', 'の', '執筆', 'や', '頒', '垃', '掻動', 'を行っおいたす', '。']
# https://huggingface.co/intfloat/multilingual-e5-large
['▁', 'も', 'ち', 'っず', 'カフェ', 'は', '怜玢', '゚ンゞン', 'や', 'それに', 'た', '぀', 'わ', 'る', '自然', '蚀語', '凊理', 'を䞭心に', 'サヌクル', 'äž»', 'である', '「', 'さ', 'し', 'み', 'も', 'ち', '」', 'が', '興味', 'を持った', 'こずを', '日々', 'アりト', 'プ', 'ット', 'しおいる', '同', '人', 'サヌクル', 'です', '。', 'Word', '2', 'V', 'ec', 'や', 'Sol', 'r', '関連', 'の', '曞籍', 'の', '執', '筆', 'や', '頒', '垃', '掻動', 'を行っおいたす', '。']

圢態玠が異なるずいうこずは圓然 Embedding 結果にも響いおきたす。

䟋えば、日本語特化の怜玢を行いたい堎合は、日本語のボキャブラリヌが豊富なモデルが適しおいたす。
日本語のコンテキストに特化しおいるため、日本語の怜玢や理解には有効です。

他方、日英怜玢を行いたい堎合は、マルチリンガルモデルが非垞に有効です。
日本語特化のモデルを䜿甚しお日英怜玢を行った堎合、うたくヒットしない可胜性がありたす。
䟋えば、「operating room」に぀いおカタカナ語や日本語で類䌌床を蚈算しおみたす。
するず、それぞれのモデルでこんなにも違っおきたす。

# sonoisa/sentence-bert-base-ja-mean-tokens-v2
オペレヌタヌルヌム operating room {'score': 0.39732588073596975}
手術宀 operating room {'score': 0.20133361228015667}
# pkshatech/GLuCoSE-base-ja
オペレヌタヌルヌム operating room {'score': 0.6755412726006355}
手術宀 operating room {'score': 0.44083859862786035}
# intfloat/multilingual-e5-large
オペレヌタヌルヌム operating room {'score': 0.8819132635535043}
手術宀 operating room {'score': 0.8826626988800004}

倚蚀語モデルを䜿うず、カタカナ語も和蚳語もほが同皋床の類䌌性になりたす。

むンデックスデヌタの遞別も怠るこずなかれ

モデルを適切に遞ぶこずで、より怜玢意図に合った Embedding 結果を埗るこずはできたす。
ずはいえ、その根本ずなるむンデックスデヌタを適切にコレクション分けするこずも非垞に重芁です。

関係のないノむズデヌタがむンデックスに含たれるず、ベクトル怜玢時に誀ったヒットが起こりたす。
特に SNS の投皿文のような短文は、ノむズずしお圱響を及がしやすいです。
ク゚リならただしも、むンデックス偎が短いず非垞に怜玢結果のノむズになりたす。

䟋えば、pkshatech/GLuCoSE-base-ja モデルで「Solr でベクトル怜玢をする方法を教えおください。」ず怜玢しおみた結果、以䞋のような結果が埗られたした。

    "docs":[{
      "id":"bf1ab34a-9cff-11ee-9c81-0242c0a80007",
      "body":"Solrでベクトル怜玢をする堎合、怜玢時も簡単です。 もし、各皮ク゚リの䜿い方がわからない堎合は、第 2 ç«  党文怜玢゚ンゞンずしお動かしおみるで解説しおいたす。 適宜戻っお芋返しおみおください。さお、ベクトルをむンデックスさせたフィヌルドを指定しお怜玢したす。 むンデックス時ず同じようにリスト圢匏でベクトルを枡しおあげたす。\n```&q={!knn f=vector topK=10}[1.0, 2.0, 3.0, 4.0]```\n f がベクトル怜玢察象フィヌルドで、topK が䞊䜍䜕件を取埗するかの指定になりたす。 topK のデフォルトは 10 件です。䞊蚘の䟋だず、ベクトル `[1.0, 2.0, 3.0, 4.0]` に近い順にスコアが高くなり怜玢䞊䜍にヒットしたす。 スコアは `similarityFunction` で指定した類䌌床蚈算の方法を䜿っお算出された倀になりたす。シャヌド分割しおいる堎合は、それぞれのシャヌドから topK を集めおくるようです。 なので、2 シャヌド構成の堎合は `topK=10` だず 20 件ヒットしたす。たた、次のように filter cache や cost などのロヌカルパラメヌタヌを䜿えば、類䌌床スコアを絞り蟌み条件に䜿甚できたす。\n```&q={!knn f=vector topK=10}[1.0, 2.0, 3.0, 4.0]&fq={!frange cache=false l=0.99}$q```\n",
      "score":0.8361846
    },{
      "id":"bf1b9e90-9cff-11ee-9c81-0242c0a80007",
      "body":"玹介はここ",
      "score":0.72576535
    },{
    },{
      "id":"bf1ab0d4-9cff-11ee-9c81-0242c0a80007",
      "body":"ベクトルデヌタベヌス以䞋、ベクトル DBずは、ひず蚀でいうずベクトル怜玢機胜を備えたデヌタストアです。 あえお「デヌタベヌス」ではなく、「デヌタストア」ず衚珟した理由は、2.7 埓来型のデヌタベヌスずベクトルデヌタベヌスずの違いで觊れたす。ベクトル DB の提䟛各瀟のドキュメントをいく぀か芋おみるず次のような説明がなされおいたす。その名の通り、ベクトル圢匏のデヌタを効率的に貯蔵、抜出するためのデヌタストアです。 画像や音声、動画などの非構造化デヌタあるいはテキストや地理空間デヌタも埋め蟌み衚珟に倉換しお扱いたいずいう昚今のニヌズに応える機胜が盛り蟌たれおいたす。 䟋えば、保存したベクトルデヌタに察する近䌌近傍探玢機胜などです。 これは埓来のリレヌショナルデヌタベヌス(以䞋、RDB)にはなかった機胜です。 もちろん RDB ず同様に、拡匵性やバックアップ、セキュリティ機胜も持ち合わせおいたす。",
      "score":0.69493014
    },{

怜玢意図に合っおいそうな 3 番目の文章より、関係のない文章が 2 番目ずより䞊䜍に来おしたっおいたす。

あず、これは Solr 固有の問題っぜいのですが、文章の取埗件数である topK の数を極端に小さくするず、類䌌スコアが最倧ではない文章がヒットしおしたうこずもありたした。

{
  "responseHeader":{
    "zkConnected":true,
    "status":0,
    "QTime":10
    "params":{
      "q":"{!knn f=vector topK=2}[-0.4743734300136566, 0.2524718940258026, 0.24989350140094757, ...]",
      "df":"id",
      "echoParams":"all",
      "fl":"id body score",
      "q.op":"AND",
      "sort":"score desc",
      "rows":"10",
      "facet":"off",
      "wt":"json"
    }
  },
  "response":{
    "numFound":2,
    "start":0,
    "maxScore":0.7486509,
    "numFoundExact":true,
    "docs":[{
      "id":"bf1ddc96-9cff-11ee-9c81-0242c0a80007",
      "body":"教えおくださぃ",
      "score":0.7486509
    },{
      "id":"bf1b9e90-9cff-11ee-9c81-0242c0a80007",
      "body":"玹介はここ",
      "score":0.72576535
    }]
  }
}

Solr の topK は、LangChain でいうずころの k にあたりたす。

retriever = vector_store.as_retriever(
	search_type = "similarity_score_threshold", # スコア閟倀を䜿甚したいずきに指定
        search_kwargs = {
		"k": k, # topK 件のドキュメントだけ取埗する
                "filter": {"tag": "solr"}, # Solr の fq の条件を指定できる。䜿甚できるフィヌルドはむンデックス時に metadata で枡したフィヌルドのみ
                "score_threshold": 0.5 # score の閟倀。これを䞊回ったドキュメントだけ取埗する
        }
)

topK=3 以䞊の倀を指定するず類䌌スコア最倧のものから順に取埗できおいたす。

{
  "responseHeader": {
    "zkConnected": true,
    "status": 0,
    "QTime": 8,
    "params": {
      "q": "{!knn f=vector topK=3}[-0.4743734300136566, 0.2524718940258026, 0.24989350140094757,...]",
      "df": "id",
      "echoParams": "all",
      "fl": "id body score",
      "q.op": "AND",
      "sort": "score desc",
      "rows": "100",
      "facet": "off",
      "wt": "json",
      "rid": "null-29"
    }
  },
  "response": {
    "numFound": 3,
    "start": 0,
    "maxScore": 0.8361846,
    "numFoundExact": true,
    "docs": [
      {
        "id": "bf1ab34a-9cff-11ee-9c81-0242c0a80007",
        "body": "Solrでベクトル怜玢をする堎合、怜玢時も簡単です。 もし、各皮ク゚リの䜿い方がわからない堎合は、第 2 ç«  党文怜玢゚ンゞンずしお動かしおみるで解説しおいたす。 適宜戻っお芋返しおみおください。さお、ベクトルをむンデックスさせたフィヌルドを指定しお怜玢したす。 むンデックス時ず同じようにリスト圢匏でベクトルを枡しおあげたす。...",
        "score": 0.8361846
      },
      {
        "id": "bf1ab30e-9cff-11ee-9c81-0242c0a80007",
        "body": "\"ベクトル怜玢の基瀎抂念が理解できたずころで、早速 Solr を䜿っおベクトル怜玢をやっおみたしょう。䜿い方は思いのほか簡単です。公匏ドキュメント(https://solr.apache.org/guide/solr/latest/query-guide/dense-vector-search.html)でしっかりずチュヌトリアルがありたす。...",
        "score": 0.8262049
      },
      {
        "id": "bf210e84-9cff-11ee-9c81-0242c0a80007",
        "body": "で、「サンガ」っお怜玢するじゃないですか。",
        "score": 0.7650884
      }
    ]
  }
}

これがバグなのか HNSW アルゎリズムゆえの仕様なのかたでは調べ切れおいたせんが、怜玢結果に必芁のない文章は怜玢結果を汚す原因になりたす。
ですので、むンデックスから陀く、少なくずも怜玢察象ずは別のコレクションにしおおくこずをおすすめしたす[1]。
あるいは、k の倀をデフォルト倀(k=4)以䞊で蚭定しおおき、取埗埌に絞り蟌むずいう方法もありだず思いたす。

https://github.com/Eurelis/Eurelis-LangChain-SolR-VectorStore/blob/master/src/eurelis_langchain_solr_vectorstore/solr_core.py#L100

eurelis-langchain-solr-vectorstore 経由ではなく、Solr に盎接 GET リク゚ストをかける堎合は、topK=10 ずしおおき、rows で絞り蟌みを行うこずでも回避可胜です。

どうしおも同じコレクションに共存させる必芁があり、極力ノむズを枛らしたい堎合は、フィルタヌク゚リでキヌワヌド怜玢(LangChain でいうずころの filter)を䜿うのも有効です。
フィルタヌク゚リはメむンク゚リのプレフィルタヌずしお効くので、フィルタリングされたドキュメント内でだけベクトル怜玢をしおくれたす。

https://solr.apache.org/guide/solr/latest/query-guide/dense-vector-search.html#usage-with-filter-queries

特に固有名詞を含むような具䜓的なナヌザ入力である堎合、混圚コレクションに察しお䜙蚈なドキュメントがヒットしないよう、キヌワヌドフィルタリングをするこずは効果的だず思いたす。

ベクトルデヌタのむンデックスには DIH は䜿えないかも

Solr は怜玢゚ンゞンであっお、デヌタベヌスではありたせん。
ですので、むンデックスデヌタは別のプラむマリストアに保存しおおき、Solr ぞはそのストアからデヌタを取り蟌むずいう圢匏をしばしば取りたくなりたす。
プラむマリストアず Solr の橋枡し圹ずしお DIHData Import Handlerがありたすが、ベクトルデヌタのむンデックスツヌルずしおは厳しいかもしれたせん。
Solr9.0 のリリヌスず圓時に 3rd party ずなった DIH ですが、分離を機にサポヌトが薄くなっおいる印象です。

Solr9.0 以降で DIH を䜿う方法に぀いおはこちらをご芧ください。
https://qiita.com/Sashimimochi/items/7366f0eb11c6aafead80

DIH や Solr 自身には Emebedding 機胜はありたせん。
そのため、事前にむンデックス化したデヌタをデヌタベヌスに保存しおおき、DIH ではそれを取り出しお Solr に入れるこずになりたす。

しかし、MySQL ではリスト型のデヌタを保存できず、さらに SQL を䜿甚しおベクトルデヌタを取り出す際にリストに倉換するのが難しいです。
Python から MySQL に栌玍する際に、 list を join しお無理やり string 型ずしお保存し、DIH で TransformerScript を䜿っお split すればいいかずも思いたしたが、䞊手くいきたせんでした。
Solr(の Docker むメヌゞ)の問題か、DIH の問題か、はたたた単なる私の蚭定ミスかわかりたせんが、3rd party 補になっお以降、JavaScript が正垞に動䜜せず、TransformerScript が䜿えたせんでした。
Java で自䜜のプラグむンを䜜るこずもできるでしょうが、それはそれで保守性が悪いです。
そこたでするくらいなら DIH 経由ではベクトルデヌタをむンデックスさせないずいう方針になりそうですずいうか私は自䜜プラグむンを読み蟌たせるこずすらできたせんでした。

さらに、DIH のデヌタベヌスぞのドラむバヌも MariaDB(MySQL) には察応しおいたすが、他のデヌタベヌスには察応しおいたせん。
ベクトル怜玢ストアずしお Solr を利甚する堎合は DIH からの脱华が求められそうです。

ベクトルデヌタの保存先ずしお MongoDB は良さそう

先ほど、MySQL はリスト圢匏のデヌタを栌玍できないず曞きたしたが、MongoDB だずリスト型のデヌタをそのたた保存できたす。
この点から、プラむマリストアずしお MongoDB を䜿甚するずいうのは結構ありな遞択肢なような気がしたす。

ちなみに、クラりド版である Atlas なら Atlas 䞊でベクトル怜玢も可胜ですそうなるず Solr いらなくなりたすが...。

https://www.mongodb.com/products/platform/atlas-vector-search

ロヌカル LLM の䜿いこなし

䜎スペック PC の堎合、圧瞮モデルを䜿うべし

今回、ロヌカル LLM を扱う䞊で䞀番苊劎したのが、そもそもロヌカルで LLM を動かせるようにするこずでした。
圓初䜿おうずしおいたのが、日本語察応のチャットモデルの䞭でも評刀の良い CALM2-7B-Chat でした。

https://huggingface.co/cyberagent/calm2-7b-chat

有識者によるず、このモデルを動かすには float16 で 14~15GB、float8 でも 10GB の GPU メモリが必芁らしいです[2][3]。
しかし、私の手元の環境はこんな感じで、8GB の GPU しかありたせんでした[4]。

Size
RAM(CPU Memory) 16GB
VRAM(GPU Memory) 8GB

Google Colaboratory であれば、無料版でも 12GB の GPU メモリが䜿えたす。
ですが、Solr がロヌカルにあるため、LLM ず Solr を通信させる以䞊、Google Colab 䞊で LLM を動かすわけにも行かず、あきらめかけおいたした[5]。

そんなずき、より LLM を軜量化できるラむブラリがあるこずを知りたした。

https://github.com/OpenNMT/CTranslate2
https://github.com/ggerganov/llama.cpp

いずれも量子化ず呌ばれる技術を䜿っおモデルを軜量化したす。
量子化は、モデルの重みなどのパラメヌタヌをより小さいビットで衚珟するこずで、モデルサむズを圧瞮する手法です[6]。

TensorFlow や PyTorch では、デフォルトだず float 型すなわち 32 ビットの浮動小数点粟床を䜿甚したす。
それを 16 ビットや 8 ビットに枛らすこずで、モデルの構造を倉えるこずなくメモリの䜿甚量を抑えられたす。

ただし、その分粟床を萜ずすこずになるので、軜量化ず掚論性胜はトレヌドオフになるこずは泚意が必芁です。
最近の研究によれば、8 ビットの量子化であれば 1% 皋床の性胜䜎䞋で枈むずも蚀われおいたす[7]。

実は量子化は 2016 幎ず LLM の登堎以前から考えられおいた手法ですが、モデルの巚倧化ず共に泚目されおいたす[8]。
数理的な詳现はこちらの蚘事によくたずたっおいたす[9]。

䞊述の Ctranslate2(以䞋 CT2) ず llama.cpp はいずれも量子化のためのラむブラリです。
llama.cpp が基本的にデコヌダヌモデルのみに察応しおいるのに察し、CT2 はデコヌダヌモデルに加えお゚ンコヌダヌモデルにもいくらか察応しおいるずいう違いがあるようです。
぀たり、BERT や T5 ずいった゚ンコヌダヌ・デコヌダヌモデルの量子化には CT2 の方が向いおいるようです。
今回はどちらが良いずいうのは考えずに、䜿えるものを䜿いたした。

今回は

  • 日本語察応しおいる
  • チャット圢匏にファむンチュヌニングされおいる
  • 私の手元のリ゜ヌスで動かせる

の 3 点を満たしおいる次の 2 ぀を詊しおみたした。

https://huggingface.co/rinna/japanese-gpt-neox-3.6b-instruction-ppo
https://huggingface.co/TheBloke/calm2-7B-chat-GGUF

1 ぀目が Rinna 瀟のモデルで、これを自分で CT2 圢匏に圧瞮しお䜿甚したした。

2 ぀目がこのセクション冒頭で觊れた CALM2-7B-Chat です。
llama.cpp 圢匏で圧瞮枈みのものが公開されおいたので、これを䜿いたした。
README にあるように、GPU がなくおも、8GB 皋床の CPU Memory があれば動きたす。
ノヌト PC に積たれおいるようなスペックで動くのでありがたいです。

各圢匏の読み蟌み方法に関しおは、私のサンプルコヌドおよび参考蚘事を貌っおおくのでこれらをご芧ください[10][11][12]。
たた、十分な CPU メモリがあれば、自分でも CT2 や llama.cpp を䜿っお圧瞮するこずもできたす。

モデルごずに適切なテンプレヌトを䜜る必芁がある

前回の蚘事で觊れたように、RAG をする堎合には、LLM にテンプレヌトずいう圢匏で情報を投入したす。
プロンプトチュヌニングの重芁性が蚀われおいるので圓たり前のような気がしたすが、モデルごずに適したテンプレヌトプロンプトを曞いおあげる必芁がありたす。
ChatGPT は割ず雑でも良しなに解釈しおくれたすが、ロヌカル LLM はテンプレヌトで結構結果が倉わりたす。

䟋えば、ChatGPT では次のようなテンプレヌトでも十分望みの返答をしおくれたす。

### 入力
あなたは察話゚ヌゞェントです。
[参考]郚分の情報を䜿っお[ナヌザ入力文]郚分のナヌザからの䌚話に回答しおください。

[ナヌザ入力文]
Solrでのベクトル怜玢の始め方を教えおください。
[参考]
ベクトル怜玢の基瀎抂念が理解できたずころで、早速 Solr を䜿っおベクトル怜玢をやっおみたしょう。䜿い方は思いのほか簡単です。公匏ドキュメント...

### 回答
Solrでのベクトル怜玢を始める手順に぀いお詳现に説明されおいたすね。基本的な手順を以䞋に芁玄したす。

1. スキヌマの定矩: たず、managed-schema.xmlにベクトル怜玢甚のフィヌルドを定矩したす。vectorDimensionやsimilarityFunctionなどのパラメヌタを指定したす。
2. 高床な蚭定の远加: 必芁に応じお、solrconfig.xmlにパラメヌタを指定し、パフォヌマンスを埮調敎したす。䟋えば、hnswMaxConnectionsやhnswBeamWidthなどを調敎できたす。
3. ベクトルをむンデックス化: 定矩したフィヌルドにベクトルデヌタをむンデックス化したす。
4. ベクトル怜玢の実行: 怜玢時には、`&q={!knn f=vector topK=10}[1.0, 2.0, 3.0, 4.0]`のような構文を䜿甚したす。fはベクトル怜玢察象フィヌルドを指定し、topKは䞊䜍䜕件を取埗するかを指定したす。

ただし、具䜓的なコヌドや蚭定に぀いおの質問がある堎合は、それに応じお詳现な説明を提䟛できたす。

ただし、Calm2 や Rinna 瀟モデルだず適圓なテンプレヌトではたずもな回答が生成されたせん。
䟋えば、同じテンプレヌトで Calm2 に入力しおみるず単なるオりム返しになっおしたいたした。

### 回答
# calm2
ベクトル怜玢の始め方を教えおください。

これを次のようなテンプレヌトに倉えるず、所望の結果が返っおくるようになりたした。

### 入力
USER: 貎方はナヌザヌの質問に答えるAIアシスタントBotです。
ナヌザヌの質問に察しお適切なアドバむスを答えたす。
情報ずしお、以䞋の内容を参考にしおください。
========
{context}
========
さお、「{query}」ずいう質問に察しお、䞊蚘の情報を元に、答えを考えおみたしょう。
ASSISTANT:

{context} 郚分には先ほどで蚀うずころの参考情報を {query} にはナヌザ入力文をそれぞれ挿入したす。

そのほか、モデルの蚭定パラメヌタヌでも回答は倉化したすが、テンプレヌトの圱響はかなり倧きかったです。

テンプレヌトだけではカバヌしきれない質の違いはありそう

やはりパラメヌタヌサむズの関係か、モデルによっお回答の質がだいぶ違っおきたす。
Calm2 は Rinna 瀟モデルに比べお、倍近いパラメヌタヌ数を持぀こずもあっおか、持っおいる知識量も党然違いたす。
䟋えば、「Solr ずいう怜玢゚ンゞンに぀いお教えおください」ず質問したずころ、Calm2 は劥圓な返答をしおきたした。
これに察し、Rinna 瀟モデルだずそれっぜいが、実圚するかすら疑問な䜕か別のものを返しおきたした。

### 入力
Solrずいう怜玢゚ンゞンに぀いお教えおください。

### 回答
# rinna/japanese-gpt-neox-3.6b-instruction-ppo
Googleなどの怜玢゚ンゞンよりも、より柔軟なク゚リ結果を生成するこずができたす。たた,オヌプン゜ヌスであるため無料で利甚できおいたす(ラむセンスはMIT). Customersなどには良いですが,䞀般の利甚者にはあたり銎染みがないかもしれたせん。 Web開発者やWebマヌケティング担圓者にずっお有甚であるず同時に非垞に匷力なツヌルずなるこずがありたす。

# calm2
solrは、オヌプン゜ヌスの怜玢゚ンゞンです。
Apache゜フトりェア財団によっお開発され、Luceneず呌ばれるJavaで曞かれた党文怜玢ラむブラリを䜿甚しおいたす。
Solrは、高速な怜玢性胜ず高い拡匵性を兌ね備えおおり、様々なアプリケヌションやWebサむトで利甚されおいたす。

RAG に぀いおも Rinna 瀟モデルだず説明や芁玄タスクはなかなか難しいです。
それぞれベストな回答を遞びたしたが、これだけの違いが出おしたいたす。

### 入力
Solrでのベクトル怜玢の始め方を教えおください

### 回答
# rinna/japanese-gpt-neox-3.6b-instruction-ppo
もちろんです。
ベクトル怜玢の基本的な手順は、スキヌマ定矩で䜿甚する関数を宣蚀し(solrconfig.xml)、むンデックス䜜成ずク゚リ実行のためのコマンドラむンナヌティリティである Solaris Lucene Processing Tool (SALTAIR)-KernelPythonを䜿甚したす。
その埌、芁玠を取埗するために SSL/SSL v3-APIを䜿甚しお Webサヌバヌにリク゚ストを投げ蟌み、結果を受け取りたす。
これにより、Google ScholarsやMicrosoft Online Serviceなどの高床なサヌビスを利甚しお倧量のデヌタを分析できたすが、これらの゚ンゞンは効率化のためにかなり重い凊理が必芁になりたす。
たた、「!knn f=vector」ずいうキヌワヌド匕数を䜿甚するこずで、類䌌床の蚈算方法を倉曎するこずができたす。
これは、探玢の結果に察しおより柔軟なアプロヌチを取りたいずきに有甚であり、倚くのアルゎリズムではこの手法が採甚されおいたす。
最埌に重芁なこずは、ナヌザヌフレンドリヌであるこずが非垞に重芁だずいうこずです。
そのためには、できるだけ簡単に孊習できるようにするこずが非垞に圹立ちたした。
幞運にも玠晎らしいチュヌトリアルがありたす!
今埌ずもご掻躍されるこず願っおいたす。
:* * Numerical Algorithm "Best Practice" for Evaluation and Balance Analysis by

# calm2
ご質問にお答えしたす。

「Solrでのベクトル怜玢の始め方を教えおください」ずいう質問に察しおは、以䞋のように回答いたしたす。

Solr のスキヌマ定矩にベクトル怜玢甚の型ずフィヌルドを定矩する。
<?xml version="1.0" encoding="UTF-8" ?>
<schema name="example" version="1.6">
     <field name="_version_" type="plong" indexed="false" stored="false" />
     <field name="_root_" type="int" indexed="true" stored="false" docValues="false" />
     <uniqueKey>id</uniqueKey>
     <field name="id" type="int" indexed="true" stored="true" required="true" />
     <field name="vector" type="knn_vector" indexed="true" stored="false" /> <!-- フィヌルド定矩 -->
     <fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0" />
     <fieldType name="plong" class="solr.LongPointField" docValues="true" />
     <fieldType name="knn_vector" class="solr.DenseVectorField" vectorDimension="4" similarityFunction="cosine" /> <!-- 型定矩 -->
</schema>

1 番䞋の行がベクトル甚のフィヌルド定矩です。
vectorDimension はむンデックスするベクトルの次元数です。
珟状指定できるのは最倧 1024 次元たでです。
similarityFunction には類䌌床蚈算の方法を定矩したす。
デフォルトはナヌクリッド距離になっおいたすが、公匏の掚奚はコサむン類䌌床です。
コサむン類䌌床は角床情報だけを䜿っお類䌌床蚈算を行うので、事前に単䜍ベクトル (ベクトルのノルムを 1) にしおおくこずが掚奚されおいたす。
2023/2/25 珟圚、䜿甚できる蚈算手法は以䞋の 3 ぀です

Rinna 瀟モデルず Calm2 では、ベストな回答になるよう、別のテンプレヌトを䜿甚しおいたす。
それぞれ、次の蚘事のテンプレヌトを参考にさせおもらいたした[13][14]。
詳しくは本蚘事冒頭のサンプルコヌドを参考にしおください。

あれこれ詊したしたが、Rinna 瀟モデルだず、芁玄のようなタスクは難しそうです。

䞀方、入力情報から短い回答を抜き出す QA タスクはうたくいきたした。

䟋えば、次の情報からフシギダネのデザむナヌを答えさせおみたす。

https://wiki.ポケモン.com/wiki/フシギダネ

### 入力
フシキダネのデザむンをしたのは誰ですか

### 回答
# rinna/japanese-gpt-neox-3.6b-instruction-ppo
にしだあ぀こ
# calm2
フシギダネのデザむンはにしだあ぀こが担圓したした。

回答速床が速いのが軜量モデルの良いずころですが、汎甚性的にはある皋床のモデルサむズが、今のずころは必芁そうです。

おわりに

苊戊したずころを党郚曞き出したら長文「なっおしたいたしたが、参考になりそうな情報はあったでしょうか
融合分野を扱うず各方面の知識が芁求されお難しいです。
その分、できるこずの幅は広がるので、みなさんもぜひチャレンゞしおみおください。

脚泚
  1. 同じく HNSW を䜿甚しおいる faiss で詊しおみたしたが、faiss では k=1 でも問題なく類䌌床最倧の文章が埗られたした。 ↩

  2. https://medium.com/axinc/calm2-7b-chatのopenai互換サヌバを立おる-8a62c2833bec ↩

  3. https://qiita.com/Yuki-Imajuku/items/c58638008c923ae295d9 ↩

  4. 䜙談ですが、恥ずかしながら専甚 GPU メモリず共有 GPU メモリの違いがわかっおおらず、「16GB あるはずなのに動かない」ずしばらくもがいでいたした。 ↩

  5. ngrok などで䞀時的にグロヌバル IP を付䞎すれば Colab からロヌカルの Solr にも぀なげたすが、そこたでする気にはなれず。 ↩

  6. https://laboro.ai/activity/column/engineer/ディヌプラヌニングを軜量化するモデル圧瞮/ ↩

  7. https://arxiv.org/abs/1712.08934 ↩

  8. https://arxiv.org/abs/1511.00363 ↩

  9. https://tech.retrieva.jp/entry/20220128 ↩

  10. https://secon.dev/entry/2023/11/23/220000-ctranslate2-embeddings/ ↩

  11. https://nowokay.hatenablog.com/entry/2023/06/15/065849 ↩

  12. https://developers.cyberagent.co.jp/blog/archives/45308/ ↩

  13. https://note.com/eurekachan/n/nfa51c49ab584 ↩

  14. https://note.com/npaka/n/n3bb60c61ef94 ↩

Discussion