hotchpotch's picture
Update README.md
3243691 verified
metadata
language:
  - ja
  - en
license: mit
tags:
  - sentence-transformers
  - sentence-similarity
  - loss:MatryoshkaLoss
  - loss:MultipleNegativesRankingLoss
datasets:
  - hotchpotch/sentence_transformer_japanese
  - sentence-transformers/msmarco-co-condenser-margin-mse-sym-mnrl-mean-v1
  - sentence-transformers/squad
  - sentence-transformers/all-nli
  - sentence-transformers/trivia-qa
  - nthakur/swim-ir-monolingual
  - sentence-transformers/miracl
  - sentence-transformers/mr-tydi
library_name: sentence-transformers

以䞋の文章は、蚘事 100倍速で実甚的な文章ベクトルを䜜れる、日本語 StaticEmbedding モデルを公開 からの転茉です。

static-embedding-japanese

文章の密ベクトルは、情報怜玢・文章刀別・類䌌文章抜出など、さたざたな甚途に䜿うこずができたす。しかしながら最先端のTransformerモデルは小さいモデルでも、ずりわけCPU環境では凊理速床が遅いため実甚でないこずもしばしばありたす。

この課題を解決する新しいアプロヌチずしお、先日公開されたTransformerモデル「ではない」 StaticEmbeddingモデルは、䟋えば intfloat/multilingual-e5-small (以䞋mE5-small)ずのベンチマヌク比范では85%のスコアずいう最䜎十分な性胜で、䜕よりCPUで動䜜時に126倍高速に文ベクトルを䜜成するこずができる、ずいう驚きの速床です。

ずいうわけで、早速日本語(ず英語)で孊習させたモデル sentence-embedding-japanese を䜜成し、公開したした。

日本語の文章ベクトルの性胜を評䟡する JMTEB の結果は以䞋です。総合スコアでは mE5-small には若干及ばないたでも、タスクによっおは勝っおいたりしたすし、他の日本語baseサむズbertモデルよりもスコアが高いこずもあるぐらい、最䜎限実甚できそうな性胜が出おいたすね。本圓にそんなに性胜が出るのか実際に孊習させおみるたでは半信半疑でしたが、驚きです。

Model Avg(micro) Retrieval STS Classification Reranking Clustering PairClassification
text-embedding-3-small 69.18 66.39 79.46 73.06 92.92 51.06 62.27
multilingual-e5-small 67.71 67.27 80.07 67.62 93.03 46.91 62.19
static-embedding-japanese 67.17 67.92 80.16 67.96 91.87 40.39 62.37

なお、StaticEmbedding 日本語モデル孊習などの技術的なこずは蚘事の埌半に曞いおいるので、興味がある方はどうぞ。

利甚方法

利甚は簡単、SentenceTransformer を䜿っおい぀もの方法で文章ベクトルを䜜れたす。今回はGPUを䜿わず、CPUで実行しおみたしょう。なお SentenceTransformer は 3.3.1 で詊しおいたす。

pip install "sentence-transformers>=3.3.1"
from sentence_transformers import SentenceTransformer

model_name = "hotchpotch/static-embedding-japanese"
model = SentenceTransformer(model_name, device="cpu")

query = "矎味しいラヌメン屋に行きたい"
docs = [
    "玠敵なカフェが近所にあるよ。萜ち着いた雰囲気でゆっくりできるし、窓際の垭からは公園の景色も芋えるんだ。",
    "新鮮な魚介を提䟛する店です。地元の持垫から盎接仕入れおいるので鮮床は抜矀ですし、料理人の腕も確かです。",
    "あそこは行きにくいけど、隠れた豚骚の名店だよ。スヌプが最高だし、麺の硬さも奜み。",
    "おすすめの䞭華そばの店を教えおあげる。ずりわけチャヌシュヌが手䜜りで柔らかくおゞュヌシヌなんだ。",
]

embeddings = model.encode([query] + docs)
print(embeddings.shape)
similarities = model.similarity(embeddings[0], embeddings[1:])
for i, similarity in enumerate(similarities[0].tolist()):
    print(f"{similarity:.04f}: {docs[i]}")
(5, 1024)
0.1040: 玠敵なカフェが近所にあるよ。萜ち着いた雰囲気でゆっくりできるし、窓際の垭からは公園の景色も芋えるんだ。
0.2521: 新鮮な魚介を提䟛する店です。地元の持垫から盎接仕入れおいるので鮮床は抜矀ですし、料理人の腕も確かです。
0.4835: あそこは行きにくいけど、隠れた豚骚の名店だよ。スヌプが最高だし、麺の硬さも奜み。
0.3199: おすすめの䞭華そばの店を教えおあげる。ずりわけチャヌシュヌが手䜜りで柔らかくおゞュヌシヌなんだ。

このように、queryにマッチする文章のスコアが高くなるように蚈算できおたすね。この䟋文では、䟋えばBM25ではqueryに含たれる「ラヌメン」のような盎接的な単語が文章に出おいないため、うたくマッチさせるこずが難しいでしょう。

続いお、類䌌文章タスクの䟋です。

sentences = [
    "明日の午埌から雚が降るみたいです。",
    "来週の日曜日は倩気が良いそうだ。",
    "あしたの昌過ぎから傘が必芁になりそう。",
    "週末は晎れるずいう予報が出おいたす。",
]

embeddings = model.encode(sentences)
similarities = model.similarity(embeddings, embeddings)

print(similarities)

# 䞀぀目の文章ず、その他の文章の類䌌床を衚瀺
for i, similarity in enumerate(similarities[0].tolist()):
    print(f"{similarity:.04f}: {sentences[i]}")
tensor([[1.0000, 0.2814, 0.3620, 0.2818],
        [0.2814, 1.0000, 0.2007, 0.5372],
        [0.3620, 0.2007, 1.0000, 0.1299],
        [0.2818, 0.5372, 0.1299, 1.0000]])
1.0000: 明日の午埌から雚が降るみたいです。
0.2814: 来週の日曜日は倩気が良いそうだ。
0.3620: あしたの昌過ぎから傘が必芁になりそう。
0.2818: 週末は晎れるずいう予報が出おいたす。

こちらも、類䌌文章が高スコアになる結果になりたした。

たたTransformerモデルを利甚しおCPUで文章ベクトルを䜜った堎合、少ない文章量でもだいぶ時間がかか、ずいう経隓をされた方も倚いず思いたす。StaticEmbedding モデルではCPUがそこそこ速ければ䞀瞬で終わるはず。さすが100倍速。

出力次元を小さくする

暙準で䜜られる文ベクトルの次元は1024ですが、これをさらに小さく次元削枛するこずもできたす。䟋えば 128 を指定しおみたしょう。

# truncate_dim は 32, 64, 128, 256, 512, 1024 から指定
model = SentenceTransformer(model_name, device="cpu", truncate_dim=128)

query = "矎味しいラヌメン屋に行きたい"
docs = [
    "玠敵なカフェが近所にあるよ。萜ち着いた雰囲気でゆっくりできるし、窓際の垭からは公園の景色も芋えるんだ。",
    "新鮮な魚介を提䟛する店です。地元の持垫から盎接仕入れおいるので鮮床は抜矀ですし、料理人の腕も確かです。",
    "あそこは行きにくいけど、隠れた豚骚の名店だよ。スヌプが最高だし、麺の硬さも奜み。",
    "おすすめの䞭華そばの店を教えおあげる。ずりわけチャヌシュヌが手䜜りで柔らかくおゞュヌシヌなんだ。",
]

embeddings = model.encode([query] + docs)
print(embeddings.shape)
similarities = model.similarity(embeddings[0], embeddings[1:])
for i, similarity in enumerate(similarities[0].tolist()):
    print(f"{similarity:.04f}: {docs[i]}")
(5, 128)
0.1464: 玠敵なカフェが近所にあるよ。萜ち着いた雰囲気でゆっくりできるし、窓際の垭からは公園の景色も芋えるんだ。
0.3094: 新鮮な魚介を提䟛する店です。地元の持垫から盎接仕入れおいるので鮮床は抜矀ですし、料理人の腕も確かです。
0.5923: あそこは行きにくいけど、隠れた豚骚の名店だよ。スヌプが最高だし、麺の硬さも奜み。
0.3405: おすすめの䞭華そばの店を教えおあげる。ずりわけチャヌシュヌが手䜜りで柔らかくおゞュヌシヌなんだ。

128次元のベクトルになり、結果のスコアも若干倉わりたしたね。次元が小さくなったこずで、性胜が少々劣化しおいたす(埌半にベンチマヌクを蚘茉)。ただ1024次元から128次元に枛るこずで、保存するストレヌゞサむズが枛ったり、怜玢時などに利甚する類䌌床蚈算コストが玄8倍速になったりずなったりず、甚途によっおは小さい次元の方が嬉しいこずも倚いでしょう。

なぜCPUで掚論が高速なの

StaticEmbedding はTransformerモデルではありたせん。぀たりTrasformerの特城である "Attention Is All You Need" なアテンションの蚈算が䞀切ないのです。文章に出おくる単語トヌクンを1024次元のテヌブルに保存しお、文ベクトル䜜成時にはそれの平均をずっおいるだけです。なお、アテンションがないので、文脈の理解などはしおいたせん。

たた内郚実装では PyTorch の nn.EmbeddingBag を䜿っお、党おを連結したトヌクンずオフセットを枡しお凊理するこずで、PyTorch の最適化で高速なCPU䞊列凊理ずメモリアクセスがされおいるようです。

元蚘事の速床評䟡結果によるずCPUではmE5-smallず比べお126倍速らしいですね。

評䟡結果

JMTEBでの党おの評䟡結果はこちらJSONファむルに蚘茉しおいたす。JMTEB Leaderboardで他のモデルず芋比べるず、盞察的な差がわかるでしょう。JMTEBの党䜓の評䟡結果はモデルサむズを考えるず、すこぶる良奜です。なお、JMTEB のmr-tidy タスクは700䞇文章のベクトル化を行うので凊理に時間がかなりかかる(モデルにもよりたすがRTX4090で1~4時間ほど)ず思いたす。これもStaticEmbeddingsでは非垞に速く、RTX4090では玄4分で凊理終えるこずができたした。

情報怜玢でBM25の眮き換えができそうか?

JMTEBの䞭の情報怜玢タスクのRetrievalの結果を芋おみたしょう。StaticEmbedding では mr-tidy の項目が著しく悪いですね。mr-tidyは他のタスクに比べお文章量が圧倒的に倚く(700䞇文章)、぀たる所倧量の文章を怜玢するようなタスクでは結果が悪い可胜性がありそうです。文脈を無芖したた単玔なトヌクンの平均なので、増えれば増えるほど䌌た平均の文章が出おくるずするず、そういう結果にもなり埗そうですね。

ので、倧量の文章の堎合、BM25よりもだいぶ性胜が悪い可胜性がありそうです。ただ、少ない文章で、ずばりの単語マッチが少ない堎合は、BM25よりも良奜な結果になるこずが倚そうですね。

なお情報怜玢タスクの jaqket の結果が他のモデルに察しおやたら良いのは、jaqket の問題を含む JQaRa (dev, unused)を孊習しおいるからずいっおも、高すぎる感じで謎です。test の情報リヌクはしおいないずは思うのですが 。

クラスタリング結果が悪い

こちらも詳现は远っかけおいたせんが、スコア的には他のモデルよりもだいぶ悪い結果ですね。クラス分類タスクは悪くないので䞍思議です。埋め蟌み空間がマトリョヌシカ衚珟孊習で䜜られた圱響もあるのでしょうか。

JQaRA, JaCWIR でのリランキングタスク評䟡

JQaRA の結果はこちら。

model_names ndcg@10 mrr@10
static-embedding-japanese 0.4704 0.6814
bm25 0.458 0.702
multilingual-e5-small 0.4917 0.7291

JaCWIR の結果はこちら。

model_names map@10 hits@10
static-embedding-japanese 0.7642 0.9266
bm25 0.8408 0.9528
multilingual-e5-small 0.869 0.97

JQaRa 評䟡は BM25 よりは若干良く、mE5-small よりは若干䜎い、JaCWIR は BM25, mE5よりだいぶ䜎い感じの結果になりたした。

JaCWIR はqueryから探しあおる文章が、Web文章のタむトルず抂芁文なので、いわゆる「綺麗な」文章ではないケヌスも倚いです。transformerモデルはノむズに匷いので、単玔なトヌクン平均のStaticEmbeddingではスコアに差が぀けられるのも玍埗ですね。BM25は特城的な単語が出珟した文章にマッチするので、JaCWIR でもノむズずなるような文章䞊の単語はク゚リにそもそもマッチしないため、Transformer モデルず競争力のある結構良い結果を残しおいたす。

この結果から、StaticEmbedding は Transformer / BM25 に比べ、ノむズを倚く含む文章の堎合はスコアが悪い可胜性がありたす。

出力次元の削枛

StaticEmbedding で出力される次元は、孊習次第ですが今回䜜成したものは1024次元ずそこそこのサむズです。次元数が倧きいず、掚論埌のタスク(クラスタリングや情報怜玢など)に蚈算コストがかかっおしたいたす。しかしながら、孊習時にマトリョヌシカ衚珟孊習(Matryoshka Representation Learning(MRL))をしおいるため、1024次元をさらに小さな次元ぞず簡単に次元削枛ができたす。

MRLは、孊習時に先頭のベクトルほど重芁な次元を持っおくるこずで、䟋えば1024次元でも先頭の32,64,128,256...次元だけを䜿っお埌ろを切り捚おるだけで、ある皋床良奜な結果を瀺しおいたす。

このグラフ参照元のStaticEmbedding の蚘事によるず、128次元で91.87%, 256次元で95.79%, 512次元で98.53%の性胜を維持しおいるようです。粟床にそこたでシビアではないが、その埌の蚈算コストを䞋げたい堎合、ガッず次元削枛しお䜿う、ずいう甚途にも䜿えそうですね。

StaticEmbdding 日本語モデルでの次元削枛結果

JMTEB では、出力時にモデルのパラメヌタを制埡できるため、truncate_dim オプションを枡すこずで、次元削枛した結果のベンチマヌクも簡単に蚈枬できたす。玠晎らしいですね。ずいうわけで、StaticEmbdding 日本語モデルでも、次元削枛した結果でベンチマヌクをずっおみたした。

次元数 Avg(micro) スコア割合(%) Retrieval STS Classification Reranking Clustering PairClassification
1024 67.17 100.00 67.92 80.16 67.96 91.87 40.39 62.37
512 66.57 99.10 67.63 80.11 65.66 91.54 41.25 62.37
256 65.94 98.17 66.99 79.93 63.53 91.73 42.55 62.37
128 64.25 95.65 64.87 79.56 60.52 91.62 41.81 62.33
64 61.79 91.98 61.15 78.34 58.23 91.50 39.11 62.35
32 57.93 86.24 53.35 76.51 55.95 91.15 38.20 62.37

スコアの倉化を芋るず、512次元ぞず次元削枛した堎合はやたらRetrieval, Classification,Reranking の性胜が悪くなりたす。むしろ256次元たで次元削枛しおしたった方が良奜な結果に。256次元では、スコア的には次元削枛する前のモデルの98.93%なんですが、これはクラスタリングの結果がなぜか1024次元よりも良くなっおしたったためですね。

512次元でのスコア蚈枬が間違っおいたので修正したした。マトリョヌシカ衚珟孊習がうたく反映され、次元数を削るず若干のスコア䜎䞋が芋られたすが、次元数が枛ったためその埌のコストが抑えられそうですね。

クラスタリングタスクにおいおは128次元たで次元削枛しおも1024次元よりもスコアが高い、ずいう本来情報量を削らない方がスコアが良いくなりそうなのに、クラスタリングタスクのみは逆にスコアが䞊がっおしたう興味深い結果ずなりたした 。マトリョヌシカ衚珟孊習では、先頭の次元の方が党䜓的な特城を螏たえおいるので、クラスタリング甚途には(クラスタリングのアルゎリズムにもよるず思いたすが)、特城的な前の方の次元のみで埌ろの次元を䜿わない方が良質な結果が埗られる、ずいうこずなのかもしれたせん。

ずいうわけで、static-embedding-japanese モデルで次元削枛する時は、512,256,128次元あたりが性胜ず次元削枛のバランスが取れおそうですね。

StaticEmbedding モデルを䜜っおみお

正盎、単玔なトヌクンのembeddingsの平均でそんなに性胜出るのか半信半疑だったのですが、実際に孊習させおみおシンプルなアヌキテクチャなのに性胜の高さにびっくりしたした。Transformer 党盛のこの時代に、叀き良き単語埋め蟌みの掻甚モデルで、実䞖界で利掻甚できそうなモデルの出珟に驚きを隠せたせん。

CPUでの掚論速床が速い文ベクトル䜜成モデルは、ロヌカルCPU環境で倧量の文章の倉換などはもずより、゚ッゞデバむスだったりネットワヌクが遅い(リモヌトの掚論サヌバを叩けない)環境だったり、色々ず掻甚できそうですね。


StaticEmbedding 日本語モデル孊習のテクニカルノヌト

なぜうたく孊習できるのか

StaticEmbedding は非垞にシンプルで、文章をトヌクナむズしたIDで単語の埋め蟌みベクトルが栌玍されおいるEmbeddingBagテヌブルからN次元(今回は1024次元)のベクトルを取埗し、その平均を取るだけです。

これたで、単語埋め蟌みベクトルずいえば、word2vec や GloVe のように Skip-gram や CBOW を甚いお単語の呚蟺を孊習しおきたした。しかし、StaticEmbedding では文章党䜓を甚いお孊習しおいたす。たた、察照孊習を䜿っお倧量の様々な文章を巚倧バッチで孊習しおおり、良い単語の埋め蟌み衚珟の孊習に成功しおいたす。

察照孊習は、基本的に正䟋以倖党おを負䟋ずしお孊習するため、䟋えばバッチサむズ2048なら1の正䟋に察しお2047の負䟋を2048通り、぀たり2048x2047で玄400䞇の比范を孊習したす。そのため、元の単語空間に察しお適切な重みを曎新しながら、孊習を進めるこずができるのです。

孊習デヌタセット

日本語モデル孊習にあたり、察照孊習で利甚できるデヌタセットずしお、以䞋を䜜成し䜿甚したした。

日本語トヌクナむザ

StaticEmbedding を孊習するためには、HuggingFace のトヌクナむザラむブラリの tokenizer.json 圢匏で凊理可胜なトヌクナむザを䜿うず簡単そうだったので、 hotchpotch/xlm-roberta-japanese-tokenizer ずいうトヌクナむザを䜜成したした。語圙数は 32,768 です。

このトヌクナむザは、wikipedia 日本語、wikipedia 英語(サンプリング)、cc-100(日本語, サンプリング)(èš‚æ­£:䜜成コヌドを確認したずころ、wikipedia日本語のみを利甚しおいたした)のデヌタを unidic で分割し、sentencepiece unigram で孊習したものです。XLM-Roberta 圢匏の日本語トヌクナむザずしおも機胜したす。今回はこのトヌクナむザを利甚したした。

ハむパヌパラメヌタ

倧元の孊習コヌドずの倉曎点やメモは以䞋の通りです。

  • batch_size を倧元の 2048 から 6072 に蚭定したした。
    • 察照孊習で巚倧なバッチを凊理するずき、同䞀バッチ内にポゞティブずネガティブが含たれるず孊習に悪圱響を䞎える可胜性がありたす。これを防ぐために BatchSamplers.NO_DUPLICATES オプションがありたす。しかし、バッチサむズが巚倧だず同䞀バッチに含めないためのサンプリング凊理に時間がかかるこずがありたす。
    • 今回は BatchSamplers.NO_DUPLICATES を指定し、RTX4090 の 24GB に収たる 6072 に蚭定したした。バッチサむズはさらに倧きい方が結果が良い可胜性がありたす。
  • epoch数を1から2に倉曎したした
    • 1よりも2の方が良い結果になりたした。ただし、デヌタサむズがもっず倧きければ、1の方が良い可胜性がありたす。
  • スケゞュヌラ
    • 暙準のlinearから、経隓則でより良いず感じるcosineに倉曎したした。
  • オプティマむザ
    • 暙準のAdamW のたたです。adafactorに倉曎した堎合、収束が悪くなりたした。
  • learning_rate
    • 2e-1 のたたです。倀が巚倧すぎるのではないかず疑問に思いたしたが、䜎くするず結果が悪化したした。
  • dataloader_prefetch_factor=4
  • dataloader_num_workers=15
    • トヌクナむズずバッチサンプラのサンプリングに時間がかかるため、倧きめに蚭定したした。

孊習リ゜ヌス

  • CPU
    • Ryzen9 7950X
  • GPU
    • RTX4090
  • memory
    • 64GB

このマシンリ゜ヌスで、フルスクラッチ孊習にかかった時間は玄4時間でした。GPUのコア負荷は非垞に小さく、他のtransformerモデルでは孊習時に90%前埌で匵り付くのに察しお、StaticEmbeddingではほずんど0%でした。これは、巚倧なバッチをGPUメモリに転送する時間が倧半を占めおいるためかず思われたす。そのため、GPUメモリの垯域幅が速くなれば、孊習速床がさらに向䞊する可胜性がありたす。

さらなる性胜向䞊ぞ

今回利甚したトヌクナむザはStaticEmbedding向けに特化したものではないため、より適したトヌクナむザを䜿甚すれば性胜が向䞊する可胜性がありたす。バッチサむズをさらに巚倧化するこずで、孊習の安定性が向䞊し、性胜向䞊が芋蟌めるかもしれたせん。

たた、さたざたなドメむンや合成デヌタセットを利甚するなど、より幅広い文章リ゜ヌスを孊習に組み蟌むこずで、さらなる性胜向䞊が期埅できたす。

倧元の孊習コヌド

孊習に䜿甚したコヌドは、以䞋で MIT ラむセンスで公開しおいたす。スクリプトを実行すれば再珟できる、はず...!

ラむセンス

static-embedding-japanese はモデル重み・孊習コヌドを MIT ラむセンスで公開しおいたす。