8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【文書検索において高速(200~800x)・高性能な独自の静的埋め込み機構】SSE: Stable Static Embedding Technical Report

8
Posted at

:blue_heart: :yellow_heart: :green_heart: :purple_heart: :heart: :blue_heart: :yellow_heart: :green_heart: :purple_heart: :heart: :blue_heart: :yellow_heart: :green_heart: :purple_heart: :heart: :blue_heart: :yellow_heart: :green_heart: :purple_heart: :heart: :blue_heart: :yellow_heart: :green_heart: :purple_heart: :heart: :blue_heart: :yellow_heart: :green_heart:

はじめに

桜の花が細い枝に咲き、澄んだ空にリボンを結ぶ今日この頃、みなさまいかがお過ごしでしょうか?

六花 牡丹(りっか ぼたん)と申します :heart_exclamation:

本記事は、

SSE (Stable Static Embedding): Unlocking the Potential of Static Embeddings, A Dynamic Tanh Normalization Approach without Speed Penalty
(SSE:静的埋め込みの可能性を解き放つ、速度ペナルティのないDynamic Tanh normalization アプローチ)

の日本語での解説記事になります。

拙筆ではございますが、この記事が皆様の役に立つことを祈っています。

研究進捗や日常についてお話していますので、もしよろしければフォローなどよろしくお願いいたします。

X(Twitter) アカウント

要約

静的埋め込みモデルは、シンプルなアーキテクチャにより高速な推論を可能としますが、それ故に、表現力を高めるのは困難であることが知られています。コーパスの規模が拡大し続ける中、埋め込みモデルに対する高い効率性と高精度性の両立は依然として重要とされています。
そこで本記事では、SSE (Stable Static Embedding) というシンプルな手法を提案します。SSE は Separable DyT (Dynamic Tanh normalization) というオリジナルのモジュールを使用しています。
SSE はパラメータ数が半分ながら、従来のアプローチよりも高い検索性能を達成します。16Mパラメータという規模ながら、SSE はNanoBEIR English mean nDCG@10で 0.512というスコアを達成しました。
Separable DyT を活用することで、SSE は勾配の流れを効果的に制御し、次元間の不均衡や過学習を抑制します。
この手法は静的埋め込みモデルに対する新たな視点を提供するものと考えています。

1 主な成果

  1. 学習を安定させ、埋め込み空間の構造を改善するシンプルな正規化機構である Separable DyTを提案しました

  2. パラメータ効率の高い静的埋め込みである SSE を導入し、NanoBEIR における実験を通じて、高速な推論と低い計算コストを維持しつつ実用的な検索性能を実現することを示しました

figure2_en.png

図 1 | (a) NanoBEIR Englishにおける検索性能(nDCG@10)(b) mean nDCG@10 vs 推論速度 (QPS: Query Per Second) (Intel® Core™ Ultra 7 265K (3.90 GHz)、バッチサイズ:32、データセット:TREC-COVID 及び Quora)

figure1_ja.png

図 2 | (a) NanoBEIR Japaneseにおける検索性能(nDCG@10)(b) mean nDCG@10 vs 推論速度 (QPS: Query Per Second) (Intel® Core™ Ultra 7 265K (3.90 GHz)、バッチサイズ:32、データセット:Miracl)

2 研究背景

高密度なベクトル表現は、情報検索や検索拡張言語システムの基盤となっています。Transformer ベースの文脈埋め込みモデルは優れた性能を発揮しますが、推論時の計算コストが大きく、レイテンシに敏感な大規模アプリケーションではボトルネックになります。一方で静的埋め込みモデルは構造が単純であるため、高速かつメモリ消費も少なく、大規模検索や推薦・リアルタイム検索システムに適しています。こうした背景から、速度は得られるものの精度が不足しやすく、逆に高精度を求めると計算負荷が増加するというトレードオフがあります。このトレードオフの解消には、表現品質を向上させつつ余分な計算負荷をかけない新しい手法が必要です。

単語表現の発展は、Word2Vec(Mikolov et al., 2013) で分散型埋め込みが確立されて以来大きく進化しています。その後、 GloVe(Pennington et al., 2014) はグローバル共起統計を取り入れ学習の安定性を高め、FastText(Bojanowski et al., 2017)はサブワード情報で希少語への耐性を向上させました。これらは固定トークン表現と軽量な合成機能により、大規模システムで実用化できるようになりました。

最近では、 static-similarity-mrl-multilingual-v1static-retrieval-mrl-en-v1(sentence-transformers, 2025)が登場し、静的埋め込みが文脈エンコーダーより400倍高速でありながら実用的な検索性能を発揮できることを示されました (Aarsen, 2025)

しかし、コーパスやRAGパイプラインが急速に拡大する中で、さらに高速かつ高精度な埋め込みモデルへの需要は続いています。静的埋め込みと文脈エンコーダーのギャップを縮めることが不可欠です。重要なのは、静的埋め込みは表現力には限界があり、それが進歩を妨げています。固定トークン表現と軽量な合成機能に頼るため、複雑な意味関係を捉えるのは困難です。具体的には埋め込み空間で「異方性(anisotropy)」が問題になっており、次元ごとの分散が不均一になる方向バイアスが生じ、特徴間の表現バランスが崩れます。この現象は訓練中の勾配不安定性と関係し、表現容量の偏った発展を招き汎化性能を低下させます。
Matryoshka Embeddings(Kusupati et al., 2024) など最近の試みも主に圧縮手法に焦点を当てており、学習プロセス中のこの核心的な表現不均衡には十分対処できていません。

本記事では、静的埋め込みモデルの性能向上を図るシンプルなフレームワーク「SSE(Stable Static Embedding)」を提案します。SSE は勾配の流れを安定させ、学習中に次元間の不均衡を抑える「Separable DyT(Dynamic Tanh normalization)」(軽量な正規化機構である DyT(Zhu et al., 2025) の派生)を使用しています。Separable DyT は埋め込み活性化のスケールと飽和を動的に制御し、過学習を抑えつつ埋め込み空間の一様性を高めます。その結果、モデルの複雑さを増すことなく、より汎化性の高い表現が得られます。

実験では、SSE が従来の静的埋め込み手法を上回りつつもパラメータ数はコンパクトであることを示します。16MパラメータでNanoBEIR English mean nDCG@10において 0.512 を達成し、ベースラインを上回っています。また、同等の性能を持つ従来手法と比べてパラメータ数が半分に抑えられ、推論速度は約2倍となります。

3 手法

3.1 構造

SSEの中核となるのは、各埋め込み次元ごとのスケールに応じた勾配の流れを調整する、軽量な正規化モジュール Separable DyT です。Separable DyTは埋め込みベクトルに直接作用し、計算コストをほとんど増やさずに、EmbeddingBagの正規化層として組み込むことができます。

入力となる埋め込みベクトル $ \mathbf{x} \in \mathbb{R}^E $ に対して、SSEは各次元独立にSeparable DyTを適用し、正規化された表現 $ \mathbf{y} \in \mathbb{R}^E $ を生成します。その結果、ノイズが多く大きな成分を持つ次元からの学習信号を減衰させつつ、安定した情報を有する次元については勾配流を維持します。これは明示的なハイパーパラメータなしで、表現空間の汎化性能を高める暗黙的な正則化を可能にします。

\begin{aligned}
& \text{------------------------------------------------------------------------------} \\
& \textbf{Algorithm 1: SSE(Stable Static Embedding)} \\
& \text{------------------------------------------------------------------------------} \\
& \textbf{Input: } x: (B, S) \\
& \textbf{Output: } y: (B, E) \\
& \quad 1:\quad x_T: (B, S) \leftarrow \mathrm{Tonenizer}(x) \\
& \quad 2:\quad x_S: (B, E) \leftarrow \mathrm{EmbeddingBag}(x_T) \\
& \quad 3:\quad y: (B, E) \leftarrow \mathrm{Separable Dynamic Tanh normalization}(x_S) \\
& \quad 4:\quad \textbf{return } y \\
& \text{------------------------------------------------------------------------------} \\
\end{aligned}

sse_architecture.png

図 3 | SSE (Stable Static Embedding) のアーキテクチャ

3.2 Separable DyT(Dynamic Tanh normalization)

各埋め込み次元 $ x_k $ について、Separable DyT は出力を以下のように計算されます。

$$
y_k = \gamma_k \tanh(\alpha_k x_k + \beta_k),
$$

ここで $ \alpha_k, , \beta_k, , \gamma_k $ はそれぞれスケーリング、シフト、および出力振幅を制御する学習可能パラメータです。

\begin{aligned}
& \text{------------------------------------------------------------------------------} \\
& \textbf{Algorithm 2: Separable Dynamic Tanh normalization} \\
& \text{------------------------------------------------------------------------------} \\
& \textbf{Input: } x: (B, E) \\
& \textbf{Output: } y: (B, E) \\
& \textbf{Parameters: } \alpha, \beta, \gamma: (E) \\
& \quad 1:\quad x \leftarrow \alpha \cdot x + \beta \\
& \quad 2:\quad y: (B, E) \leftarrow \gamma \cdot \mathrm{Tanh}(x) \\
& \quad 3:\quad \textbf{return } y \\
& \text{------------------------------------------------------------------------------} \\
\end{aligned}

入力次元 $ x_k $ に関する微分は以下のようになります:

$$
\frac{\partial y_k}{\partial x_k} = \gamma_k \alpha_k , \mathrm{sech}^2(\alpha_k x_k + \beta_k).
$$

この形式は強度依存型のゲートを導入します。勾配の大きさは双曲線正接関数の二乗で調整されます。

飽和した次元 $ |x_k| > 1 $ の場合、
$$
|\alpha_k x_k + \beta_k| \gg 1,
$$
は指数関数的な減衰をもたらします。
$$
\mathrm{sech}^2(z) \sim 4 e^{-2|z|}.
$$
これにより勾配が抑制され、
$$
\frac{\partial y_k}{\partial x_k} \rightarrow 0.
$$
となります。

対して、非飽和の次元 $ |x_k| \ll 1 $ の場合、
$$
\mathrm{sech}^2(z) \approx 1,
$$
ほぼ一定の勾配を維持します。
$$
\frac{\partial y_k}{\partial x_k} \approx \gamma_k \alpha_k.
$$

この強度依存型のゲートは、ノイズが多く大きな成分を持つ次元からの学習信号を減衰させつつ、安定した情報を有する次元については勾配流を維持します。これは明示的なハイパーパラメータなしで、表現空間の汎化性能を高める暗黙的な正則化を可能にします。

3.3 強度依存型のゲートによる暗黙的な正則化

Separable DyT の重要な特性は、追加のハイパーパラメータを導入することなく暗黙的な正則化として機能する点です。強度依存型のゲート機構は最適化中に不安定な特徴方向を選択的に抑制することで、以下の効果をもたらします。

  • 埋め込み空間内の次元間不均衡を削減
  • 勾配の爆発と過剰増幅を防ぐ
  • 極端な活性化を減衰させることで過学習を軽減
  • 表現の一様性および異方性を改善

標準的な正規化技術(例:レイヤー正規化や L2 正規化)とは異なり、Separable DyT は埋め込み表現をグローバルに再スケーリングしません。代わりに、各次元が独立の動的範囲と感度を持てるようにする次元ごとの適応的調整を行います。

3.4 静的埋め込みモデルへの統合

Separable DyT は、埋め込み表現を固定次元の表現に集約する EmbeddingBag レイヤーの出力に適用されます。
$ \mathbf{E} \in \mathbb{R}^{V \times E} $ を埋め込み行列とし、文はトークンの集合 $ {t_1, \dots, t_n} $ で表されるとします。EmbeddingBag レイヤーは集約された表現を計算します。

$$
\mathbf{z} = \mathrm{EmbeddingBag}(t_1, \dots, t_n)
$$

ここで、集約は通常、選択された埋め込みに対する平均プーリングによって行われます。

集約された表現に対して Separable DyT が適用されます。

$$
\mathbf{s} = \mathrm{SeparableDyT}(\mathbf{z})
$$

ここで $ \mathbf{s} \in \mathbb{R}^E $ は最終的な文埋め込みを表します。

Separable DyT が集約されたベクトルに対して要素ごとに作用し、次元ごとにわずかなパラメータしか導入しないため、既存の静的埋め込みアーキテクチャに簡便に統合され、その構造的シンプル性を損なうことなく機能します。

3.5 計算効率

SSE は静的埋め込みモデルの主要な利点である極めて高速な推論を維持しています。EmbeddingBag は埋め込み表現の効率的な集約を行い、逐次計算や深い文脈エンコーディングの必要性を回避します。

Separable DyT 変換は、要素ごとの積と Tanh activationから構成されており、どちらも計算コストが低く高度に並列化可能です。Separable DyT による追加パラメータの数は埋め込み次元に比例して増加しますが、EmbeddingBagと比較すると無視できるレベルです。

したがって、SSE は静的埋め込みモデルの定数時間推論特性を維持しつつ、埋め込み空間の安定性と表現力を改善します。

4 実験

4.1 学習

対照学習と Matryoshka Loss を組み合わせ、埋め込みモデルの学習を行いました。これは Train 400x faster Static Embedding Models with Sentence Transformers(Aarsen, 2025) に記載されている手法を踏襲しています。Matryoshka Loss の次元は 32、64、128、256、512 で設定し、これにより異なる埋め込みサイズにおいても検索性能を維持できます。

bf16 を用いた混合精度計算を有効にした AdamW optimizerを使用しました。学習率は 0.1 に設定し、cosine scheduleとwarm-up ratioは 0.1 を採用しています。batch size 512 と gradient accumulation 8 によりglobal batch sizeは 4096 となります。学習は1 epoch (num_train_epochs=1) 行い、各ステップでモデルを評価しました。また、対照学習における重複ペアによる過学習を防ぐため、no_duplicates を適用しています。

非デフォルトのハイパーパラメータは下記の通りです。

表 1 | 学習時のハイパーパラメータ

Parameter Value
Optimizer AdamW (beta2: 0.9999, epsilon: 1e-10)
Learning Rate 0.1
LR Scheduler Cosine Decay
Warmup Ratio 0.1
Batch Size (per device) 512
Gradient Accumulation Steps 8
Training Epochs 1
Precision BF16 (bf16: True)
Evaluation Strategy Steps
Dataloader Workers 4
Batch Sampler no_duplicates

4.2 データセット

様々な検索およびSTSタスクでの汎化性を確保するために、多様な 15 のデータセット で訓練を行いました。これらのデータセットは質問応答(QA)、自然言語推論(NLI)、情報検索(IR)のドメインをカバーしています。

表 2 | 訓練データセット

Dataset Domain Ratio
squad Question Answering ~2%
trivia_qa Question Answering ~2%
allnli Natural Language Inference ~2%
pubmedqa Scientific QA ~2%
hotpotqa Multi-hop QA ~2%
miracl Multilingual IR ~2%
mr_tydi Multilingual IR ~2%
msmarco Web Search IR ~5%
msmarco_10m Large-scale IR ~45%
msmarco_hard Hard Negative Mining ~2%
mldr Long Document Retrieval ~2%
s2orc Scientific Text ~14%
swim_ir Semantic Web IR ~2%
paq Question Answering ~14%
nq Natural Questions ~2%

4.3 モデル

SSEと以下の2つのベースラインを比較しました。

  • Static Embedding (no DyT): DyTレイヤーなしの静的埋め込みモデル
  • Static Embedding + DyT: 先行研究で報告された通常のDynamic Tanh normalizationを組み込んだ静的埋め込みモデル
\begin{aligned}
& \text{------------------------------------------------------------------------------} \\
& \textbf{Algorithm 3: Dynamic Tanh normalization} \\
& \text{------------------------------------------------------------------------------} \\
& \textbf{Input: } x: (B, E) \\
& \textbf{Output: } y: (B, E) \\
& \textbf{Parameters: } \alpha: (1) \\
& \qquad \qquad \qquad \; \beta, \gamma: (E) \\
& \quad 1:\quad x \leftarrow \mathrm{Tanh}(\alpha \cdot x)  \\
& \quad 2:\quad y: (B, E) \leftarrow \gamma \cdot x + \beta\\
& \quad 3:\quad \textbf{return } y \\
& \text{------------------------------------------------------------------------------} \\
\end{aligned}

4.4 学習結果

図4は、3つのモデルにおける損失と勾配を示しています。

損失の収束(図4(a)):3つのモデルはいずれもほぼ同様の損失の推移を示し、約19から急速に低下して2〜3の範囲で安定します。この収束の様子から、観測される性能の違いは、損失の低下速度や最終的な損失値の差によるものではないことがわかります。

勾配の挙動(図4(b)):勾配の大きさを見ると、明確な違いが現れます。Static Embedding を用いたベースライン(紫色)は、学習全体を通して一貫して低い値を保っており、勾配消失や特徴の過度な抑制といった問題の可能性が示唆されます。一方、SSE(ピンク色)は初期に大きくばらついた後、およそ 0.2 程度の適度な水準に落ち着きます。この挙動は、大きな入力による活性化関数の飽和を防ぐために、勾配の流れが適切に調整されていることを示しています。情報量の多い次元でゼロでない勾配を保つことで、SSEは学習中も継続的にパラメータ更新を行うことができ、ベースラインで見られるような特徴学習の早期停滞を避けています。

dyt_comparison_figure.png

図4 | (a) 損失と (b) 勾配ノルムのトレーニングステップごとの比較

5 評価

5.1 NanoBEIR mean nDCG@10の推移

図5は、ベースラインと比較したSSEの学習過程と最終的な性能を示しています。結果から、SSEは学習のほとんどのステップで2つベースラインモデルを安定して上回っており、特に後半(ステップ1k以降)でその差が顕著に現れています。

  • 収束性:ピンクの線が示すように、SSEは平均nDCG@10で約0.5124の最大値に到達し、紫の線で示される従来の静的埋め込みベースライン(約0.5068で頭打ち)を上回っています。これは検索精度において約1.3%の改善に相当します
  • 次元分離の重要性:一方で、通常のDyTを用いたモデル(シアンの線)は、ベースラインを下回り、0.497から0.503の間で推移しています。この結果は、非分離的な正規化を適用すると、有用な特徴が抑えられたり、次元ごとの不安定さに十分対応できない可能性を示唆しています。これに対して、SSEのSeparable DyTは各次元ごとに勾配を適応的に調整することで、これらの問題を効果的に解消しています

dyt_comparison_ndcg_figure.png

図5 | トレーニングステップごとの NanoBEIR mean nDCG@10

表3 | NanoBEIR English nDCG@10 の比較。

Model NanoArguAna nDCG@10 NanoClimateFEVER nDCG@10 NanoDBPedia nDCG@10 NanoFEVER nDCG@10 NanoFiQA2018 nDCG@10 NanoHotpotQA nDCG@10 NanoMSMARCO nDCG@10 NanoNFCorpus nDCG@10 NanoNQ nDCG@10 NanoQuoraRetrieval nDCG@10 NanoSCIDOCS nDCG@10 NanoSciFact nDCG@10 NanoTouche2020 nDCG@10 Mean nDCG@10
SSE(Static Embedding + Separable DyT) 0.4105 0.2998 0.5493 0.6808 0.3744 0.7021 0.4132 0.2982 0.4652 0.9094 0.3381 0.6176 0.6029 0.5124
Static Embedding + DyT 0.3615 0.2717 0.5632 0.6716 0.3393 0.6765 0.4367 0.3142 0.4674 0.9094 0.3267 0.6129 0.5816 0.5025
Static Embedding (No DyT) 0.3884 0.3005 0.5552 0.7125 0.3573 0.6783 0.4219 0.2955 0.4638 0.8979 0.3264 0.6076 0.5834 0.5068

5.2 Matryoshka

SSEフレームワークの有効性を検証するため、NanoBEIRベンチマークで、異なる埋め込み次元(32〜512)における検索性能を評価しました。SSEは、通常の静的埋め込みおよび参照モデル(static-retrieval-mrl-en-v1)と比較して、計算効率を保ちながら一貫して高い検索性能を示すことが確認されました。

図6に示すように、埋め込み次元の増加に伴い、SSEモデル(ピンク線)はすべてのベースラインに対して明確な優位性を示します。参照モデル(static-retrieval-mrl-en-v1)は次元32においてnDCG@10が0.3532(SSEは0.3448)となり、初期段階ではSSEを上回りますが、次元64以降では逆転します。次元64では、SSEは0.4275を記録し、参照モデルを約2.4%上回ります。さらに次元が大きくなるにつれてこの差は拡大し、次元512ではSSEが0.5124に達し、参照モデル(0.4957)および「Static Embedding + DyT」ベースライン(0.5025)を上回ります。これらの結果から、SSEは埋め込み次元の増加に応じて効果的にスケールし、追加された表現能力を従来手法よりも効率的に活用できることが分かります。

SSEと非分離型のDynamic Tanh正規化を用いたモデル(「Static Embedding + DyT」)と比較すると、すべての次元においてSSEが一貫して上回る結果となりました(例:dim 512では0.5124 vs 0.5025)。この性能向上は、前述仮説を支持しています。すなわち、各次元ごとに独立して正規化を行うことで、より精緻なスケール適応型の勾配制御が可能になるという点です。具体的には、Separable DyTは極端に大きな値を持つ次元を抑えつつ、有用な情報を保持し、従来の一括的な正規化よりも安定した埋め込み構造を実現します。

最後に、SSEは効率と精度のバランスにも優れています。計算コストが低い小規模な次元(64や128など)でも、高次元モデルに匹敵する性能を維持します。これは、Separable DyTが限られた次元に情報を効果的に集約できることを示しています。SSEはリソース制約のある環境でも高精度を実現しつつ、次元の増加に対しても安定して性能を伸ばせる、実用性の高い検索手法といえます。

dim_scores_ref_figure.png

図6 | NanoBEIR English mean nDCG@10 と Matryoshka Embedding Truncation

表4 | NanoBEIR English mean nDCG@10 と Matryoshka Embedding Truncation

モデル 32 64 128 256 512 1024
SSE (Static Embedding + Separable DyT) 0.3448 0.4275 0.4659 0.4969 0.5124 -
Static Embedding + DyT 0.3338 0.4134 0.4622 0.4919 0.5025 -
Static Embedding (no DyT) 0.3367 0.4161 0.4625 0.4912 0.5068 -
static-retrieval-mrl-en-v1 (For reference) 0.3532 0.4176 0.4622 0.4819 0.4957 0.5031

5.3 パフォーマンス(英語検索タスク)

英語検索タスクに関して精度と速度の評価を行いました(図7)。図から、Transformerモデルが最も高い絶対精度(nDCG@10>0.60)を達成している一方で、低い推論速度(QPS < 10,000)となっていることが分かります。一方で、多くの静的埋め込み手法は nDCG@10<0.50となっています。

SSEモデル(stable-static-embedding-fast-retrieval-mrl-en)は、mean nDCG@10 が 0.50 を上回りつつ、スループットも 50,000 QPS を超えています。特筆すべき点として、他のStatic Embeddingアプローチと比較して 2.16倍の高速化を実現しながら、高い精度を維持しています。このような効率向上は、Separable DyTによって導入された強度適応的な勾配の流れが埋め込み次元の飽和を防ぎ、高次元の静的表現においても豊かな情報を保てていることを示唆しています。

figure2-2-2_en.png

図 7 | mean nDCG@10 vs 推論速度 (QPS: Query Per Second) (Intel® Core™ Ultra 7 265K (3.90 GHz)、バッチサイズ:32、データセット:TREC-COVID 及び Quora)

figure2-1-2_en.png

図 8 | NanoBEIR Englishにおける検索性能(nDCG@10)

5.4 パフォーマンス(日本語検索タスク)

図9は、日本語検索ベンチマークにおける性能分布を示しています。Transformerベースのモデルは高精度・低スループットの領域(左側)に位置する一方、従来の静的埋め込みは高速であるにもかかわらず、性能の低い領域に集中していることがわかります。

SSEモデル(stable-static-embedding-fast-retrieval-mrl-ja)は、このギャップを効果的に埋め、右上領域に位置しています。具体的には、mean nDCG@10が 0.45 を超えつつ、約 60,000 QPS のスループットを達成しています。この結果は、SSEが従来の静的埋め込みを大きく上回るだけでなく、同程度の精度においてベースラインと比較して 1.46倍の高速化 を実現していることを示しています。

figure1-2-2_ja.png

図 9 | mean nDCG@10 vs 推論速度 (QPS: Query Per Second) (Intel® Core™ Ultra 7 265K (3.90 GHz)、バッチサイズ:32、データセット:Miracl)

figure1-1-2_ja.png

図 10 | NanoBEIR Japaneseにおける検索性能(nDCG@10)

5.5 スペクトル分析

学習された埋め込み行列に対してPCAによるスペクトル分析を行うことで、SSEが高い性能を示す背景にある表現幾何の特徴が明らかになります。図11に示すように、ベースラインであるStatic Embeddingでは、正規化された固有値が512次元全体にわたって緩やかに減衰しており、意味的な分散が多くの方向に広く分布し、有効ランクが比較的高い状態にあることが示唆されます。

一方、Standard DyTでは、PC ≈ 480付近でスペクトルが急激に落ち込む“崖”のような挙動が見られ、末尾の次元で分散が突然失われています。これは、高次元の分散が急激に抑制される不安定な圧縮が起きており、埋め込み空間の幾何構造を乱している可能性を示しています。

これに対してSSEでは、PC ≈ 430付近からより早い段階で減衰が始まるものの、その変化は滑らかで、その後の次元では固有値が速やかにゼロへと近づいていきます。この挙動は、SSEが暗黙的に低ランク正則化のような働きをし、意味的な分散をよりコンパクトな部分空間に集中させつつ、ノイズが支配的な方向を抑制していることを示唆しています。このように制御された圧縮により、コサイン類似度や距離構造の安定性が向上し、その結果として下流タスクにおける検索性能の改善につながっていると考えられます。

pca_eigenvalue_figure2.png

図 11 | NanoBEIR English DatasetsにおけるPCAスペクトル(a)線形スケール(b)対数スケール

6 議論

6.1 適用範囲と限界

SSEの有効性は、現時点では静的なテキスト埋め込みの文脈において実験的に確認されている点に注意が必要です。本研究の枠組みでは、Separable DyT はルックアップテーブルを通じて取得される埋め込みベクトル(例:単語IDやアイテムID)に対して作用しています。本手法が他の表現形式にも適用可能かどうかは、依然として未解明の課題です。

6.2 今後の研究課題

SSEをより広い研究範囲へと拡張することは、有望な研究方向であると考えています。具体的には、以下のような領域があると考えられます。

  • 動的・文脈依存埋め込み:今後の研究では、Separable DyT を Transformer ベースのアーキテクチャ(例:LayerNormやRMSNormの代替または補完)に組み込むことが検証されると考えられます。これは、入力文脈に応じて隠れ状態が動的に変化する深層ネットワークにおいて、学習の安定化に寄与するかどうかを検証する目的です
  • クロスモーダル:強度依存型ゲートの原理はテキストデータに限定されるものではありません。今後の研究では、画像パッチの埋め込みなど高次元ベクトルの安定性が重要となる他のモダリティへの適用も有望な研究方針となります
  • 最適化手法:Separable DyT を用いたモデルがさまざまな最適化手法(例:MuonやSGDなど)の下で、本研究と同様の結果を呈するのかは未検証事項です

7 公開モデル

SSE_Logo_white.png

7.1 SSE

RikkaBotan/stable-static-embedding-fast-retrieval-mrl-en

  • SSE for Retrieval MRL English version

表5 | NanoBEIR English Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.5124 0.5640 0.4317
NanoClimateFEVER 0.2998 0.3611 0.2344
NanoDBPedia 0.5493 0.7492 0.4247
NanoFEVER 0.6808 0.6318 0.6105
NanoFiQA2018 0.3744 0.4197 0.3162
NanoHotpotQA 0.7021 0.7679 0.6273
NanoMSMARCO 0.4132 0.3537 0.3733
NanoNFCorpus 0.2982 0.4889 0.1091
NanoNQ 0.4652 0.3992 0.4028
NanoQuoraRetrieval 0.9094 0.9122 0.8847
NanoSCIDOCS 0.3381 0.5509 0.2604
NanoArguAna 0.4105 0.3193 0.3325
NanoSciFact 0.6176 0.5933 0.5824
NanoTouche2020 0.6029 0.7852 0.4539

RikkaBotan/stable-static-embedding-fast-retrieval-mrl-ja

  • SSE for Retrieval MRL Japanese version

表6 | NanoBEIR Japanese Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.4507 0.5090 0.3695
NanoClimateFEVER 0.3110 0.4208 0.2347
NanoDBPedia 0.5596 0.7652 0.4000
NanoFEVER 0.5611 0.5003 0.4923
NanoFiQA2018 0.3247 0.3731 0.2692
NanoHotpotQA 0.4795 0.5758 0.4182
NanoMSMARCO 0.3845 0.3191 0.3335
NanoNFCorpus 0.2736 0.4544 0.1014
NanoNQ 0.4218 0.3658 0.3572
NanoQuoraRetrieval 0.7786 0.7750 0.7428
NanoSCIDOCS 0.3026 0.4850 0.2192
NanoArguAna 0.3521 0.2686 0.2793
NanoSciFact 0.6372 0.6100 0.5990
NanoTouche2020 0.4731 0.7036 0.3572

RikkaBotan/stable-static-embedding-fast-retrieval-mrl-bilingual-ja-en

  • SSE for Retrieval MRL Bilingual version (English & Japanese)

表7 | NanoBEIR English Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.5073 0.5563 0.4207
NanoClimateFEVER 0.3239 0.4045 0.2612
NanoDBPedia 0.5647 0.7321 0.4262
NanoFEVER 0.6450 0.5790 0.5514
NanoFiQA2018 0.3374 0.3838 0.2766
NanoHotpotQA 0.6897 0.7505 0.6177
NanoMSMARCO 0.4463 0.3621 0.3740
NanoNFCorpus 0.2844 0.4456 0.1071
NanoNQ 0.4851 0.4217 0.4186
NanoQuoraRetrieval 0.8554 0.8540 0.8202
NanoSCIDOCS 0.3376 0.5482 0.2566
NanoArguAna 0.3941 0.3154 0.3279
NanoSciFact 0.6185 0.5977 0.5881
NanoTouche2020 0.6123 0.8369 0.4432

表8 | NanoBEIR Japanese Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.4511 0.5141 0.3772
NanoClimateFEVER 0.2979 0.4005 0.2353
NanoDBPedia 0.5429 0.7633 0.4059
NanoFEVER 0.5133 0.4643 0.4661
NanoFiQA2018 0.3174 0.3669 0.2619
NanoHotpotQA 0.5000 0.5672 0.4234
NanoMSMARCO 0.4372 0.3865 0.4022
NanoNFCorpus 0.2866 0.5185 0.1177
NanoNQ 0.3987 0.3500 0.3527
NanoQuoraRetrieval 0.7944 0.8100 0.7685
NanoSCIDOCS 0.3153 0.5127 0.2322
NanoArguAna 0.3721 0.2873 0.2990
NanoSciFact 0.6216 0.5904 0.5804
NanoTouche2020 0.4662 0.6656 0.3589

7.2 SSE(量子化シリーズ)

RikkaBotan/quantized-stable-static-embedding-fast-retrieval-mrl-en

  • Quantized SSE for Retrieval MRL English version

表9 | NanoBEIR English Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.5110 0.5645 0.4312
NanoClimateFEVER 0.3127 0.3822 0.2439
NanoDBPedia 0.5472 0.7440 0.4252
NanoFEVER 0.6870 0.6402 0.6191
NanoFiQA2018 0.3750 0.4155 0.3129
NanoHotpotQA 0.6927 0.7572 0.6205
NanoMSMARCO 0.4105 0.3504 0.3694
NanoNFCorpus 0.3063 0.4989 0.1148
NanoNQ 0.4523 0.3884 0.3941
NanoQuoraRetrieval 0.9147 0.9222 0.8944
NanoSCIDOCS 0.3345 0.5562 0.2622
NanoArguAna 0.4154 0.3151 0.3257
NanoSciFact 0.5972 0.5774 0.5703
NanoTouche2020 0.5979 0.7910 0.4526

RikkaBotan/quantized-stable-static-embedding-fast-retrieval-mrl-ja

  • Quantized SSE for Retrieval MRL Japanese version

表10 | NanoBEIR Japanese Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.4477 0.5088 0.3675
NanoClimateFEVER 0.3152 0.4258 0.2417
NanoDBPedia 0.5554 0.7767 0.3962
NanoFEVER 0.5536 0.4907 0.4827
NanoFiQA2018 0.3160 0.3614 0.2653
NanoHotpotQA 0.4722 0.5669 0.4136
NanoMSMARCO 0.3929 0.3237 0.3371
NanoNFCorpus 0.2686 0.4584 0.0962
NanoNQ 0.4170 0.3607 0.3571
NanoQuoraRetrieval 0.7768 0.7750 0.7393
NanoSCIDOCS 0.2939 0.4774 0.2197
NanoArguAna 0.3471 0.2617 0.2727
NanoSciFact 0.6387 0.6127 0.6001
NanoTouche2020 0.4732 0.7240 0.3560

RikkaBotan/quantized-stable-static-embedding-fast-retrieval-mrl-bilingual-ja-en

  • Quantized SSE for Retrieval MRL Bilingual version (English & Japanese)

表11 | NanoBEIR English Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.5049 0.5526 0.4197
NanoClimateFEVER 0.3166 0.3874 0.2511
NanoDBPedia 0.5604 0.7321 0.4244
NanoFEVER 0.6511 0.5871 0.5595
NanoFiQA2018 0.3179 0.3541 0.2617
NanoHotpotQA 0.6840 0.7459 0.6191
NanoMSMARCO 0.4417 0.3616 0.3748
NanoNFCorpus 0.2939 0.4535 0.1202
NanoNQ 0.4952 0.4287 0.4251
NanoQuoraRetrieval 0.8528 0.8533 0.8190
NanoSCIDOCS 0.3335 0.5460 0.2551
NanoArguAna 0.3978 0.3202 0.3326
NanoSciFact 0.6076 0.5842 0.5733
NanoTouche2020 0.6105 0.8298 0.4406

表12 | NanoBEIR Japanese Evaluation

Dataset nDCG@10 MRR@10 MAP@100
NanoBEIR Mean 0.4493 0.5083 0.3744
NanoClimateFEVER 0.2883 0.3860 0.2218
NanoDBPedia 0.5458 0.7632 0.4048
NanoFEVER 0.4956 0.4403 0.4421
NanoFiQA2018 0.3224 0.3667 0.2640
NanoHotpotQA 0.4866 0.5444 0.4117
NanoMSMARCO 0.4578 0.4085 0.4226
NanoNFCorpus 0.2731 0.4844 0.1138
NanoNQ 0.3944 0.3406 0.3436
NanoQuoraRetrieval 0.8003 0.8179 0.7766
NanoSCIDOCS 0.3156 0.5133 0.2325
NanoArguAna 0.3635 0.2758 0.2871
NanoSciFact 0.6341 0.6020 0.5903
NanoTouche2020 0.4628 0.6646 0.3566

8 実装

8.1 Modeling

PyTorchを使用してSSEをsentence-transformersフレームワーク内で実装しました。モデルはライブラリのInputModuleから継承しており、入力処理および推論ワークフローとの完全な互換性を確保しています。

SSE モデリング
"""
coding = utf-8
Copyright 2026 Rikka Botan. All rights reserved
Licensed under "MIT License"
Stable Static Embedding official PyTorch implementation
"""

from __future__ import annotations
import os
from pathlib import Path
from safetensors.torch import save_file as save_safetensors_file
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from typing import Dict
from dataclasses import dataclass
from tokenizers import Tokenizer
from transformers import PreTrainedTokenizerFast
from sentence_transformers.models.InputModule import InputModule


class SeparableDyT(nn.Module):
    def __init__(
        self,
        hidden_dim: int,
        alpha_init: float = 0.5
    ):
        super().__init__()
        self.alpha = nn.Parameter(alpha_init*torch.ones(hidden_dim))
        self.beta = nn.Parameter(torch.ones(hidden_dim))
        self.bias = nn.Parameter(torch.zeros(hidden_dim))
    
    def forward(
        self,
        x: torch.Tensor
    ) -> torch.Tensor:
        x = self.beta * F.tanh(self.alpha * x + self.bias)
        return x


class SSE(InputModule):
    """
    Stable Static Embedding (SSE)
    StaticEmbedding-compatible Sentence-Transformers module
    """

    def __init__(
        self,
        tokenizer: Tokenizer | PreTrainedTokenizerFast,
        vocab_size: int,
        hidden_dim: int = 1024,
        **kwargs,
    ):
        super().__init__()

        if isinstance(tokenizer, PreTrainedTokenizerFast):
            tokenizer = tokenizer._tokenizer
        elif not isinstance(tokenizer, Tokenizer):
            raise ValueError("Tokenizer must be a fast (Rust) tokenizer")

        self.tokenizer: Tokenizer = tokenizer
        self.tokenizer.no_padding()

        self.embedding = nn.EmbeddingBag(vocab_size, hidden_dim)
        self.dyt = SeparableDyT(hidden_dim)

        self.embedding_dim = hidden_dim

        # For model card compatibility
        self.base_model = kwargs.get("base_model", None)

    # Tokenization (StaticEmbedding-compatible)
    def tokenize(
        self,
        texts: list[str],
        **kwargs
    ) -> dict[str, torch.Tensor]:
        encodings = self.tokenizer.encode_batch(texts, add_special_tokens=False)
        encodings_ids = [encoding.ids for encoding in encodings]

        offsets = torch.from_numpy(
            np.cumsum(
                [0] + [len(token_ids) for token_ids in encodings_ids[:-1]]
            )
        )
        input_ids = torch.tensor(
            [token_id for token_ids in encodings_ids for token_id in token_ids],
            dtype=torch.long
        )
        return {
            "input_ids": input_ids,
            "offsets": offsets
        }

    # Forward
    def forward(
        self,
        features: Dict[str, torch.Tensor],
        **kwargs,
    ) -> Dict[str, torch.Tensor]:
        x = self.embedding(features["input_ids"], features["offsets"])
        x = self.dyt(x)
        features["sentence_embedding"] = x
        return features

    # Required APIs
    def get_sentence_embedding_dimension(self) -> int:
        return self.embedding_dim

    @property
    def max_seq_length(self) -> int:
        return torch.inf
    
    def save(
        self,
        output_path: str,
        *args,
        safe_serialization: bool = True,
        **kwargs,
    ) -> None:
        os.makedirs(output_path, exist_ok=True)

        if safe_serialization:
            save_safetensors_file(
                self.state_dict(),
                os.path.join(output_path, "model.safetensors"),
            )
        else:
            torch.save(
                self.state_dict(),
                os.path.join(output_path, "pytorch_model.bin"),
            )

        self.tokenizer.save(
            str(Path(output_path) / "tokenizer.json")
        )

    @classmethod
    def load(
        cls,
        model_name_or_path: str,
        **kwargs,
    ):
        allowed_keys = {
            "cache_dir",
            "local_files_only",
            "force_download",
        }
        filtered_kwargs = {
            k: v for k, v in kwargs.items() if k in allowed_keys
        }
    
        tokenizer_path = cls.load_file_path(
            model_name_or_path,
            filename="tokenizer.json",
            **filtered_kwargs,
        )
        tokenizer = Tokenizer.from_file(tokenizer_path)
    
        weights = cls.load_torch_weights(
            model_name_or_path=model_name_or_path,
            **filtered_kwargs,
        )
    
        hidden_dim = weights["embedding.weight"].size(1)
        vocab_size = weights["embedding.weight"].size(0)
    
        model = cls(
            tokenizer=tokenizer,
            vocab_size=vocab_size,
            hidden_dim=hidden_dim,
        )
    
        model.load_state_dict(weights)
        return model
SSE Configs
@dataclass
class SSESforzandoConfig:
    hidden_dim: int = 512
    vocab_size: int = 30522


@dataclass
class SSEForzandoConfig:
    hidden_dim: int = 384
    vocab_size: int = 30522

8.2 推論

以下のシンプルなコードで実行できます。

"""
coding = utf-8
Copyright 2026 Rikka Botan. All rights reserved
Licensed under "MIT License"
Stable Static Embedding inference implementation
"""

import torch
from sentence_transformers import SentenceTransformer

# load (remote code enabled)
model = SentenceTransformer(
    "RikkaBotan/stable-static-embedding-fast-retrieval-mrl-en",
    trust_remote_code=True,
    device="cuda" if torch.cuda.is_available() else "cpu",
    truncate_dim=256,
)

# inference
query = "What is Stable Static Embedding?"
sentences = [
    "SSE: Stable Static embedding works without attention.",
    "Stable Static Embedding is a fast embedding method designed for retrieval tasks.",
    "Static embeddings are often compared with transformer-based sentence encoders.",
    "I cooked pasta last night while listening to jazz music.",
    "Large language models are commonly trained using next-token prediction objectives.",
    "Instruction tuning improves the ability of LLMs to follow human-written prompts.",
]


with torch.no_grad():
    embeddings = model.encode(
        [query] + sentences,
        convert_to_tensor=True,
        normalize_embeddings=True,
        batch_size=32
    )

print("embeddings shape:", embeddings.shape)

# cosine similarity
similarities = model.similarity(embeddings[0], embeddings[1:])
for i, similarity in enumerate(similarities[0].tolist()):
    print(f"{similarity:.05f}: {sentences[i]}")

8.3 Quantized Modeling

リソースの利用効率を最適化するために、4bit量子化モデリング(SSEQ)を行いました。データサイズの大幅な圧縮を実現し、元のモデルに匹敵する検索性能を維持しました。

SSEQ モデリング
"""
coding = utf-8
Copyright 2026 Rikka Botan. All rights reserved
Licensed under "MIT License"
Stable Static Embedding official PyTorch implementation
"""

from __future__ import annotations
import os
from pathlib import Path
from safetensors.torch import save_file as save_safetensors_file
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from typing import Dict
from dataclasses import dataclass
from tokenizers import Tokenizer
from transformers import PreTrainedTokenizerFast
from sentence_transformers.models.InputModule import InputModule
from safetensors.torch import load_file


def quantize_q4(weight: torch.Tensor):
    """
    weight: (vocab, dim)
    returns: packed uint8 + scale + zero
    """
    w = weight.detach().cpu().numpy().astype(np.float32)

    scales = np.max(np.abs(w), axis=1, keepdims=True) + 1e-8
    w_norm = w / scales

    q = np.clip(np.round((w_norm + 1) * 7.5), 0, 15).astype(np.uint8)

    # pack 2x4bit -> uint8
    packed = (q[:, 0::2] << 4) | q[:, 1::2]

    return {
        "packed": packed,
        "scales": scales.astype(np.float32),
    }


def dequantize_q4(packed: np.ndarray, scales: np.ndarray):
    hi = (packed >> 4) & 0xF
    lo = packed & 0xF

    q = np.empty((packed.shape[0], packed.shape[1]*2), dtype=np.uint8)
    q[:, 0::2] = hi
    q[:, 1::2] = lo

    w = (q.astype(np.float32) / 7.5) - 1.0
    w = w * scales
    return torch.from_numpy(w)


class SeparableDyT(nn.Module):
    def __init__(
        self,
        hidden_dim: int,
        alpha_init: float = 0.5
    ):
        super().__init__()
        self.alpha = nn.Parameter(alpha_init*torch.ones(hidden_dim))
        self.beta = nn.Parameter(torch.ones(hidden_dim))
        self.bias = nn.Parameter(torch.zeros(hidden_dim))
    
    def forward(
        self,
        x: torch.Tensor
    ) -> torch.Tensor:
        x = self.beta * F.tanh(self.alpha * x + self.bias)
        return x


class SSEQ(InputModule):
    """
    Stable Static Embedding (SSE)
    StaticEmbedding-compatible Sentence-Transformers module
    """

    def __init__(
        self,
        tokenizer: Tokenizer | PreTrainedTokenizerFast,
        vocab_size: int,
        hidden_dim: int = 1024,
        **kwargs,
    ):
        super().__init__()

        if isinstance(tokenizer, PreTrainedTokenizerFast):
            tokenizer = tokenizer._tokenizer
        elif not isinstance(tokenizer, Tokenizer):
            raise ValueError("Tokenizer must be a fast (Rust) tokenizer")

        self.tokenizer: Tokenizer = tokenizer
        self.tokenizer.no_padding()

        self.embedding = nn.EmbeddingBag(vocab_size, hidden_dim)
        self.dyt = SeparableDyT(hidden_dim)

        self.embedding_dim = hidden_dim

        # For model card compatibility
        self.base_model = kwargs.get("base_model", None)

    # Tokenization (StaticEmbedding-compatible)
    def tokenize(
        self,
        texts: list[str],
        **kwargs
    ) -> dict[str, torch.Tensor]:
        encodings = self.tokenizer.encode_batch(texts, add_special_tokens=False)
        encodings_ids = [encoding.ids for encoding in encodings]

        offsets = torch.from_numpy(
            np.cumsum(
                [0] + [len(token_ids) for token_ids in encodings_ids[:-1]]
            )
        )
        input_ids = torch.tensor(
            [token_id for token_ids in encodings_ids for token_id in token_ids],
            dtype=torch.long
        )
        return {
            "input_ids": input_ids,
            "offsets": offsets
        }

    # Forward
    def forward(
        self,
        features: Dict[str, torch.Tensor],
        **kwargs,
    ) -> Dict[str, torch.Tensor]:
        x = self.embedding(features["input_ids"], features["offsets"])
        x = self.dyt(x)
        features["sentence_embedding"] = x
        return features

    # Required APIs
    def get_sentence_embedding_dimension(self) -> int:
        return self.embedding_dim

    @property
    def max_seq_length(self) -> int:
        return torch.inf
    
    def save(self, output_path: str):
        os.makedirs(output_path, exist_ok=True)

        state = self.state_dict()

        emb = state["embedding.weight"]
        q = quantize_q4(emb)

        del state["embedding.weight"]

        save_safetensors_file(
            state,
            os.path.join(output_path, "model_rest.safetensors"),
        )

        with open(os.path.join(output_path, "embedding.q4_k_m.bin"), "wb") as f:
            f.write(q["packed"].tobytes())
            f.write(q["scales"].tobytes())

        self.tokenizer.save(
            str(Path(output_path) / "tokenizer.json")
        )
    
    @classmethod
    def load(cls, model_path: str):

        tokenizer = Tokenizer.from_file(
            os.path.join(model_path, "tokenizer.json")
        )

        state = load_file(
            os.path.join(model_path, "model_rest.safetensors"),
            device="cpu"
        )

        # read q4 binary
        bin_path = os.path.join(model_path, "embedding.q4_k_m.bin")
        with open(bin_path, "rb") as f:
            raw = f.read()

        hidden = state["dyt.alpha"].shape[0]
        total_uint8 = len(raw)

        bytes_per_row = hidden // 2 + 4
        vocab = total_uint8 // bytes_per_row

        packed_size = vocab * hidden // 2

        packed = np.frombuffer(raw[:packed_size], dtype=np.uint8)
        scales = np.frombuffer(raw[packed_size:], dtype=np.float32)

        packed = packed.reshape(vocab, hidden // 2)
        scales = scales.reshape(vocab, 1)

        emb = dequantize_q4(packed, scales)

        # rebuild model
        model = cls(
            tokenizer=tokenizer,
            vocab_size=emb.shape[0],
            hidden_dim=emb.shape[1]
        )

        state["embedding.weight"] = emb
        model.load_state_dict(state)

        return model

8.4 量子化

以下のコードを使用することで、重みを量子化できます。作成した重みと SSEQの実装をHugging Faceにアップロードすることで、他のsentence-transformers互換モデルと同じように使用できます。

"""
coding = utf-8
Copyright 2026 Rikka Botan. All rights reserved
Licensed under "MIT License"
Quantization implementation
"""

import os
from tokenizers import Tokenizer
from sentence_transformers import SentenceTransformer
from transformers import AutoTokenizer
from SSE import SSE
from SSE_quantize import SSEQ


def quantize_and_save_sse_from_hf(
    hf_model_name: str,
    output_path: str,
):

    print(f"[1] Loading HF model: {hf_model_name}")

    st_model = SentenceTransformer(hf_model_name)

    sseq_module = None
    for m in st_model.modules():
        if isinstance(m, SSE):
            sseq_module = m
            break

    if sseq_module is None:
        raise ValueError("SSE module not found in the model")

    print("[2] Extract tokenizer")

    # tokenizer
    try:
        hf_tokenizer = AutoTokenizer.from_pretrained(hf_model_name)
        tokenizer = Tokenizer.from_str(hf_tokenizer.backend_tokenizer.to_str())
    except Exception:
        tokenizer = sseq_module.tokenizer

    print("[3] Rebuild SSEQ model")

    # embedding weight
    emb_weight = sseq_module.embedding.weight.detach().cpu()

    model = SSEQ(
        tokenizer=tokenizer,
        vocab_size=emb_weight.shape[0],
        hidden_dim=emb_weight.shape[1],
        base_model=hf_model_name
    )

    state = sseq_module.state_dict()
    model.load_state_dict(state)

    print("[4] Quantize & Save")

    os.makedirs(output_path, exist_ok=True)
    model.save(output_path)

    print(f"[✓] Quantized model saved to: {output_path}")
  • 使用方法
quantize_and_save_sse_from_hf(
    "RikkaBotan/stable-static-embedding-fast-retrieval-mrl-en",
    "./sse-q4")

9 アプリケーション

sse_app.png

謝辞

このモデルの学習のための計算リソースの一部は、Saldraさん、Witnessさん、Lumina Logic Minds社から提供いただきました。貴重なサポートに感謝いたします。

このテーマに関心を持ったきっかけは、Tom Aarsenさんの記事 Train 400x faster Static Embedding Models with Sentence Transformersを読んだことでした。この内容に触発され、静的埋め込みについて検討を進めるようになりました。興味のきっかけを作ってくださったTom Aarsenさんに感謝いたします。

sentence-transformers、python、pytorchを使用させていただきました。 作成・メンテンナンスしてくださっている皆様に感謝いたします。

何よりも、この研究にご興味を持ってくださりありがとうございます。

著者:六花牡丹(りっかぼたん)

おさげとハーフツイン・可愛いお洋服が好きで、基本的にふわふわしている変わり者です。
結構ドジで何もないところで転ぶタイプ。
人工知能に関しては独学のみ。
主な関心分野は自然言語処理・モデルのアーキテクチャ。

最近、白色のワンピースを買いがちで、新しいモードのお洋服に挑戦してみたいなぁと思っています。

RikkaBotan_Logo.png

参照論文・サイト等

SSE_Logo

8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?