<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title><![CDATA[A Day in the Life - 記事]]></title>
        <description><![CDATA[Yuichi Tateno (セコン)による、日記以外の記事一覧]]></description>
        <link>https://secon.dev</link>
        <generator>RSS for Node</generator>
        <lastBuildDate>Sun, 04 Oct 2026 10:39:30 GMT</lastBuildDate>
        <atom:link href="https://secon.dev/other_feed" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[ブラウザ上でも動く、超小型な System One Decision モデル bekko-system-one を公開]]></title>
            <description><![CDATA[<p>TypeSafe AI の System One Decision モデル、<a href="https://docs.typesafe.ai/concepts/system-one">Jev</a> が、汎用性能が非常に高く、いわゆる classification task で解ける課題なら LLM よりも圧倒的に安価・高速で話題ですね。</p>
<p>そこで今回、小型なモデルだとどれぐらいの性能が出せるのだろうと、System One Decision モデル の <a href="https://github.com/hotchpotch/bekko-system-one">bekko-system-one-v0</a>（以下 BS1）シリーズを制作してみました。汎化性能は Jev の方が圧倒的に上ですが、S1MB（System One Mosaic Benchmark）（注：著者作）では、小さなモデルながら、ほかの大きなサイズのモデル並みの結果が得られています。とりわけ、学習しているドメインのタスクでは、なかなかの精度で利用が可能です。</p>
<ul>
<li>🤗 <a href="https://huggingface.co/collections/hotchpotch/bekko-system-one-v0-6abc47eab9c4fe4ec83d9e30">bekko-system-one collection</a></li>
</ul>
<p>なお対応言語は、英語のみとなっています...。</p>
<h2>ブラウザ上で実行可能</h2>
<p>本モデル(英語のみ対応)は <a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-17m">17M</a>、<a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-68m">68M</a>、<a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-400m">400M</a> パラメーターの3つのサイズのモデルを公開しており、とりわけ 17M や 68M の小型モデルは、ブラウザ上にモデルをロードし、CPU での推論も十分な速度で可能です。</p>
<ul>
<li><a href="https://huggingface.co/spaces/hotchpotch/bekko-system-one-in-browser">ブラウザ上でモデルを動かし、推論するデモ</a></li>
</ul>
<p><img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20260930-bekko-s1-browser.webp" alt="bekko-system-one のブラウザデモ：17M モデルを CPU で実行した結果"></p>
<p>注意: なおこのデモは、BS1 モデルで解きやすいタスクを恣意的に選んでいるため、ぱっと見の性能は良さそうですが、Jev のような「幅広いタスクで高性能」なわけではないので、ご注意ください。</p>
<p>また、17M パラメーターというのは、総パラメーター数が約17Mで、そのうち埋め込みを除くパラメーター数は、たったの約4Mです（ここでは Active Parameters - AP と呼びます）。ブラウザ実行用に、token の lookup table だけを INT8 にしたものを ONNX へと変換しており、モデルファイル単体のサイズは、たったの約29MBとなります。</p>
<h2>評価ベンチマーク</h2>
<p>ブラウザで動いたとして、実際のモデルとしての性能はどんな感じでしょうか。というわけで評価をしてみました。<a href="https://huggingface.co/spaces/hotchpotch/S1MB-leaderboard">S1MB - System One Mosaic Benchmark</a> という、私作のベンチマークを使っています。このベンチマークは、後述のデータセットのうち、test set のデータを中心に、原則100ケースを抽出した、137個(Noul 59, Choice 57, Score 21)のベンチマークで構成されており、Noul、Choice、Score の3軸で評価しています。</p>
<p>S1MB については、<a href="https://huggingface.co/blog/hotchpotch/system-one-mosaic-benchmark/">S1MB記事</a>で書いているので、詳細は割愛しますが、総パラメーター数500M以下に絞ると以下のような結果です。BS1 も、超小型としては、かなり健闘しているのではないでしょうか。なお比較のために Jev 1.13 も載せています。</p>
<table>
<thead>
<tr>
<th>Model</th>
<th align="right">Avg</th>
<th align="right">Noul</th>
<th align="right">Choice</th>
<th align="right">Score</th>
<th align="right">Total Params</th>
<th align="right">Active Params</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://docs.typesafe.ai/models">Jev 1.13</a></td>
<td align="right">59.59</td>
<td align="right">64.63</td>
<td align="right">67.22</td>
<td align="right">46.92</td>
<td align="right">—</td>
<td align="right">—</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-400m">bekko-system-one-v0-400m</a></td>
<td align="right">50.60</td>
<td align="right">51.24</td>
<td align="right">61.32</td>
<td align="right">39.25</td>
<td align="right">395M</td>
<td align="right">343M</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-68m">bekko-system-one-v0-68m</a></td>
<td align="right">40.46</td>
<td align="right">42.91</td>
<td align="right">51.62</td>
<td align="right">26.85</td>
<td align="right">68M</td>
<td align="right">42M</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-17m">bekko-system-one-v0-17m</a></td>
<td align="right">27.57</td>
<td align="right">31.43</td>
<td align="right">35.57</td>
<td align="right">15.70</td>
<td align="right">17M</td>
<td align="right">4M</td>
</tr>
<tr>
<td><a href="https://huggingface.co/wfzyx/von">von</a></td>
<td align="right">16.21</td>
<td align="right">20.15</td>
<td align="right">23.99</td>
<td align="right">4.48</td>
<td align="right">395M</td>
<td align="right">343M</td>
</tr>
<tr>
<td><a href="https://huggingface.co/convaiinnovations/laya-typed-decisions">laya-typed-decisions</a></td>
<td align="right">15.00</td>
<td align="right">20.06</td>
<td align="right">18.93</td>
<td align="right">5.99</td>
<td align="right">421M</td>
<td align="right">370M</td>
</tr>
<tr>
<td><a href="https://huggingface.co/convaiinnovations/laya">laya</a></td>
<td align="right">13.36</td>
<td align="right">20.19</td>
<td align="right">16.31</td>
<td align="right">3.58</td>
<td align="right">421M</td>
<td align="right">370M</td>
</tr>
<tr>
<td><a href="https://huggingface.co/convaiinnovations/laya-multilingual">laya-multilingual</a></td>
<td align="right">9.28</td>
<td align="right">14.01</td>
<td align="right">13.03</td>
<td align="right">0.79</td>
<td align="right">322M</td>
<td align="right">125M</td>
</tr>
</tbody>
</table>
<p>AvgはNoul・Choice・Scoreの調整済みスコアを等重みで平均した値です。各スコアは高いほど良く、正解率そのものではありません。</p>
<p>しかしながら、S1MB に含まれる「汎化性能を計測する」ベンチマーク(GPT-6-Astra で生成した合成データセット)では、BS1 モデルは圧倒的に性能が悪いです。これは「特定ドメインを学習したデータセットなら、ある程度の性能は出るが、自由な指示や未知のドメインについては弱い」と言えるでしょう。なので、今回公開した BS1 も「汎化性能を持った System One Decision モデルに達していない」ことから version 0 として、<code>-v0</code> suffix をつけています。</p>
<p>全体評価と同じモデルについて、汎化性能の6ベンチマークに絞った結果です。Avg順に並べています。</p>
<table>
<thead>
<tr>
<th>Model</th>
<th align="right">Avg</th>
<th align="right">General Noul</th>
<th align="right">General Choice</th>
<th align="right">General Score</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://docs.typesafe.ai/models">Jev 1.13</a></td>
<td align="right">96.27</td>
<td align="right">99.00</td>
<td align="right">98.68</td>
<td align="right">91.14</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-400m">bekko-system-one-v0-400m</a></td>
<td align="right">54.48</td>
<td align="right">52.00</td>
<td align="right">80.30</td>
<td align="right">31.14</td>
</tr>
<tr>
<td><a href="https://huggingface.co/wfzyx/von">von</a></td>
<td align="right">43.19</td>
<td align="right">41.00</td>
<td align="right">65.89</td>
<td align="right">22.67</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-68m">bekko-system-one-v0-68m</a></td>
<td align="right">32.48</td>
<td align="right">31.00</td>
<td align="right">57.52</td>
<td align="right">8.90</td>
</tr>
<tr>
<td><a href="https://huggingface.co/convaiinnovations/laya-typed-decisions">laya-typed-decisions</a></td>
<td align="right">31.82</td>
<td align="right">22.00</td>
<td align="right">58.80</td>
<td align="right">14.67</td>
</tr>
<tr>
<td><a href="https://huggingface.co/convaiinnovations/laya">laya</a></td>
<td align="right">26.50</td>
<td align="right">9.00</td>
<td align="right">54.83</td>
<td align="right">15.67</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-17m">bekko-system-one-v0-17m</a></td>
<td align="right">18.96</td>
<td align="right">15.00</td>
<td align="right">39.11</td>
<td align="right">2.76</td>
</tr>
<tr>
<td><a href="https://huggingface.co/convaiinnovations/laya-multilingual">laya-multilingual</a></td>
<td align="right">14.47</td>
<td align="right">6.00</td>
<td align="right">36.67</td>
<td align="right">0.75</td>
</tr>
</tbody>
</table>
<p>なお、Jev はドメイン特化性能（特別にそのドメインを学習したわけではないのに、すごいですね）も、汎化性能も、特筆したスコアですね。とりわけ汎化性能のスコアについては、ほぼほぼ満点と言えるでしょう。つまり、汎化性能が高い System One Decision モデルを名乗るには、最低限この汎化性能ベンチマークで、Jev 並みのスコア（つまりほぼ満点）を出せないと、あまり意味がないと思っています。</p>
<h2>モデルサイズと推論速度</h2>
<p>BS1 モデルで、特筆すべきことはモデルサイズと推論速度です。GPU（RTX 5090）を使ったS1MBの26,269件の評価処理は、17Mで約13.33秒、68Mで約34.04秒、400Mで約132.97秒（約2分13秒）でした。17Mでは、モデルロードと各ベンチマークのデータ読み込みなどを含む計測区間の合計でも約21.38秒です。冒頭のデモのように、ブラウザ上でも現実的に動きます。</p>
<p>なおJevはAPI経由となり、通信待ちなどを含む各ベンチマークの評価時間の合計は約58分37秒でした。APIの応答時間はタイミングによって前後するでしょう。ここで示した値は評価処理の計測値で、純粋なモデル計算だけの時間ではありません。また、17Mの約21.38秒もwarmupなどを除く区間の合算で、コマンド開始から終了までの時間ではありません。</p>
<h2>技術アーキテクチャでの取り組み</h2>
<p>Bekko System One（以下 BS1）で工夫したのは、複数の選択肢を判定するときに、共通する入力の処理を使い回すことです。その方法の前に、まず一般的な encoder model で、Jev のような処理をする、よくある方法の二つを解説しましょう。</p>
<h3>cross encoder</h3>
<p>通常の双方向 encoder を使う cross encoder では、以下のように、共通する入力と選択肢を組み合わせて処理します。特殊トークンは模式的な表記です。</p>
<pre><code class="hljs language-text">[CLS] {state} {instruction} [SEP] {criteria A} [SEP]
[CLS] {state} {instruction} [SEP] {criteria B} [SEP]
[CLS] {state} {instruction} [SEP] {criteria C} [SEP]</code></pre>
<p>先頭の CLS トークンの表現や、トークンの表現を平均したベクトル（mean pooling）を、学習済みの head に渡してスコアを求めます。情報検索の reranker でも広く使われている方式です。</p>
<p>入力と選択肢がお互いを参照できるため、細かな対応関係を捉えられます。しかしながら、state が長いと、同じ文章を選択肢の数だけ処理することになります。前半の表現も後半の選択肢によって変わるため、前半が同じ文字列でも、途中の計算結果をそのまま使い回せないのです。</p>
<p>なお情報検索の reranker では、前半に配置する検索クエリが短いことがほとんどのため、共有による計算量削減の効果が小さく、cross encoder でも良いケースが多いですね。</p>
<h3>listwise encoder</h3>
<p>それなら、選択肢をまとめて一回で処理しちゃおう、という方法もあります。全候補を連結する listwise 方式では、例えば以下のように入力します。</p>
<pre><code class="hljs language-text">{state} {instruction} [SEP]
{criteria A} [SEP] {criteria B} [SEP] {criteria C} [SEP]</code></pre>
<p>候補ごとの表現や候補前後の特殊トークンを pooling するなどして、スコアを求めます。共通部分を一度で処理でき、選択肢同士も参照できるのが利点です。</p>
<p>一方、候補が多いと入力全体が長くなります。全トークン間で attention を計算する場合、その計算量は長さの二乗に比例するため、多数の候補をまとめると負担が増えます。cross encoder より常に遅いということではなく、文章の長さや候補数によって向き不向きがあります。</p>
<h3>BS1 の方法 - 共有 prefix</h3>
<p>BS1 では、state と instruction を一つの共有部分（prefix）として処理し、各層の Key/Value、いわゆる K/V を保存して使い回します。</p>
<pre><code class="hljs language-text">{state} {instruction} → 各層の共有 K/V
                          ├─ {criteria A} → score A
                          ├─ {criteria B} → score B
                          └─ {criteria C} → score C</code></pre>
<p>普通の cross encoder と何が違うの？と思われるかもしれませんが、ここでは参照できる範囲を変えています。共有 prefix は選択肢を参照せず、各選択肢は共有 prefix と自分自身だけを参照します。これなら、選択肢が変わっても共有部分の計算結果は変わらないため、一度計算した K/V を再利用できます。</p>
<p>LLM の KV cache と同じように K/V を保存しますが、次のトークンを一つずつ生成するわけではありません。共有部分を処理した後、各選択肢を並列に処理し、候補側の表現を mean pooling して head からスコアを求めます。</p>
<p>候補側だけを pooling していますが、候補の文章しか見ていないわけではありません。各候補は各層で共有部分を参照するため、state と instruction を踏まえた表現になっています。一方、共有部分の先頭に置いた CLS トークンは候補を参照できないので、その表現だけでは候補ごとの判定はできません。</p>
<p>これによって、長い state と instruction を選択肢ごとに処理し直す必要がなくなります。ただし、通常の cross encoder と同じ計算を、そのまま高速化したものではありません。通常の cross encoder では、候補に応じて入力側の表現も変わり、その情報を候補側が再び参照できます。BS1 では、この相互のやり取りを制限する代わりに、共有部分の計算を使い回しています。</p>
<p>そのぶん、細かな対応関係を捉える自由度は通常の cross encoder の方が高いと考えられます。ただ、実際の精度差はタスクや学習条件にもよるでしょう。</p>
<p>なお、state と instruction はまとめて双方向に処理しています。state だけを独立してキャッシュしているわけではないため、instruction を変えた場合は共有部分を再計算します。</p>
<p>また、今回は、criteria 同士が参照できるような attention は含めてません。本来、例えば Choice のようなケースですと、他の criteria を参照した上で回答した方が精度がありそうですが、短時間で試しただけでは、精度が上がる学習ができなかったです。なおこれは無理だ、と言っているわけではなく、私が試した方法でうまくいかなかっただけの可能性も十分あるでしょう。</p>
<h2>大元の base model</h2>
<p>大元の base model としては、<a href="https://huggingface.co/answerdotai/ModernBERT-base">ModernBERT</a> アーキテクチャの英語高性能モデルである <a href="https://arxiv.org/abs/2507.11412">Ettin-encoder</a> を、さらに教師 reranker モデルを用い cross encoder として学習した <a href="https://huggingface.co/cross-encoder/ettin-reranker-17m-v1">Ettin-reranker</a> を利用しています。</p>
<p>ModernBERT などの MLM、いわゆる pretrained model の状態では、検索クエリとドキュメントの関連度を直接スコア化するようには学習していません。情報検索向けデータを学習した Dense Embedding モデルや、cross encoder モデルでは、文章とドキュメントの関連性を理解しているため、今回はすでにそれらを学習済みの Ettin-reranker を使っています。小型ながら、英語 rerank 性能は非常に高いです。今回の BS1 では、Ettin-reranker を base model として用い、追加学習を行いました。</p>
<p>以下は Ettin-encoder、Ettin-reranker を base model とし、各データセットに上限を適用した学習データの全量を、同じ条件で学習させた対照実験の結果です。68Mではreranker初期化が総合CE・各型のaccuracyで良く、17Mでは総合CEは僅差で、Noul・Choiceはreranker、Scoreはencoderが良い結果でした。</p>
<table>
<thead>
<tr>
<th>サイズ</th>
<th>初期化モデル</th>
<th align="right">総合CE ↓</th>
<th align="right">Noul accuracy ↑</th>
<th align="right">Choice accuracy ↑</th>
<th align="right">Score accuracy ↑</th>
</tr>
</thead>
<tbody>
<tr>
<td>17M</td>
<td>Ettin-encoder</td>
<td align="right">0.8694</td>
<td align="right">68.71%</td>
<td align="right">60.14%</td>
<td align="right">56.57%</td>
</tr>
<tr>
<td>17M</td>
<td>Ettin-reranker</td>
<td align="right">0.8713</td>
<td align="right">69.88%</td>
<td align="right">60.77%</td>
<td align="right">53.88%</td>
</tr>
<tr>
<td>68M</td>
<td>Ettin-encoder</td>
<td align="right">0.7625</td>
<td align="right">72.87%</td>
<td align="right">69.78%</td>
<td align="right">58.16%</td>
</tr>
<tr>
<td>68M</td>
<td>Ettin-reranker</td>
<td align="right">0.7180</td>
<td align="right">77.01%</td>
<td align="right">71.13%</td>
<td align="right">61.34%</td>
</tr>
</tbody>
</table>
<p>CEは小さいほど良く、accuracyは大きいほど良い指標です。データセットごとに集計し、二つの入力順序の結果を平均しています。前掲の調整済みスコアとは指標が異なります。各条件1 seedでの比較です。</p>
<h2>データセット</h2>
<p>データセットでは <a href="https://huggingface.co/datasets/hotchpotch/bekko-system-one-dataset-v0">hotchpotch/bekko-system-one-dataset-v0</a> を利用しています。このデータセットは、既存の NLP や情報検索関連のデータセットを、Noul、Choice、Score の3形式として学習しやすいように変換した 153 個の学習データセットで、training set は約659万ケースとなっています。一部は合成データセットも追加してあります。なお、このデータセットの test set は S1MB のベンチマークにも転用しているので、同じドメインの training set で学習した BS1 は S1MB の一部ベンチマークのスコアが上がりやすくなっています。</p>
<p>BS1 では、このデータセットを用いて学習を行いました。学習には RTX 5090 を用い、17M の学習では、たった2時間で学習を終えました。なお 68M では5.5時間、400M では約25時間でした。</p>
<h2>学習のハイパーパラメーター</h2>
<p>学習のハイパーパラメーターやデータセットの件数などは、細かくは解説しませんが、<a href="https://arxiv.org/abs/2607.25180v1">bekko-embedding 論文</a>（私作の、超小型マルチリンガル dense retrieval model の論文です）で書かれている知見を主に、いくつかパラメーターを試し、その範囲で良い結果が得られた値を使っています。</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>17M</th>
<th>68M</th>
<th>400M</th>
</tr>
</thead>
<tbody>
<tr>
<td>Optimizer</td>
<td>AdamW</td>
<td>AdamW</td>
<td>AdamW</td>
</tr>
<tr>
<td>Batch size</td>
<td>512</td>
<td>512</td>
<td>512</td>
</tr>
<tr>
<td>Encoder learning rate</td>
<td>1e-4</td>
<td>3e-5</td>
<td>1e-5</td>
</tr>
<tr>
<td>Head learning rate</td>
<td>5e-4</td>
<td>2e-4</td>
<td>2e-4</td>
</tr>
<tr>
<td>Weight decay</td>
<td>0.01</td>
<td>0.01</td>
<td>0.01</td>
</tr>
<tr>
<td>Warmup ratio</td>
<td>10%</td>
<td>10%</td>
<td>10%</td>
</tr>
<tr>
<td>Learning-rate schedule</td>
<td>Cosine</td>
<td>Cosine</td>
<td>Cosine</td>
</tr>
<tr>
<td>Query / candidate上限</td>
<td>4,096 / 2,048 tokens</td>
<td>4,096 / 2,048 tokens</td>
<td>4,096 / 2,048 tokens</td>
</tr>
</tbody>
</table>
<p>学習用の実装も公開しているので、この実装を使うことで、同様のモデルを学習可能です。興味がある方は、ご利用ください。</p>
<ul>
<li><a href="https://github.com/hotchpotch/bekko-system-one">bekko-system-one：学習・推論コード</a></li>
</ul>
<h2>超小型の System One Decision モデルは実現可能なのか？</h2>
<p>将来は、S1MB の汎化性能ベンチマークでも Jev 並みのスコアに達する、アクティブなパラメーターサイズが100M以下といった、超小型なモデルも可能である、と思っています。それを達成するための、最も重要なことは、学習に必要な高品質な合成データセットです。</p>
<p>TypeSafe AI CEO の Diogo Almeida 氏も「<a href="https://x.com/CompleteSkeptic/status/2100617775823966680">100% of our data is synthetic (but not the type of crap that is just spit out from an LLM obviously)</a>」と言っているように、Noul、Choice、Score で、ありとあらゆる質問と適切なスコアの合成データセットが用意できれば、超小型モデルも実現可能だと思っています。どうしても「モデルアーキテクチャ」に目線が行きがちですが、高品質で齟齬がない多様な合成データセットをどのように作り、それをどのように学習するか、が肝心でしょう。</p>
<p>実際に、私作のマルチリンガル情報検索モデルである <a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a8m">bekko-embedding</a> シリーズでは、10億以上のデータ（うち1.6億件が合成データセット）を、巨大バッチで学習させることにより、アクティブパラメーターサイズが 8M、25M という超小型サイズのモデルで数倍〜数十倍の情報検索モデルに匹敵する情報検索モデルが作れています。</p>
<p>続いての重要なことは、強力な base model の登場です。実用的なテキスト生成能力を持つ LLM（decoder）を超小型にするのは難しいため、文字列生成にこだわらなければ、もっと小型で性能が良い base model が作れるでしょう。ModernBERT はその例だと思いますが、現状のさらに発展し続けている LLM のモデルアーキテクチャを使い、さらに小型モデルでより良いアーキテクチャと、大量の多様なデータを学習させた base model があれば、その継続タスクの学習に効いてくるでしょう。ただし、合成データセットの方が重要度は非常に高いとは思っています。</p>
<h2>終わりに</h2>
<p>TypeSafe AI の System One Decision モデル、Jev の登場により、汎化性能が高い Classification model の有益性が証明されました。TypeSafe AI の素晴らしい点は、中途半端な汎化性能で出すのでなく、フロンティア LLM に並ぶ性能の超汎化性能を持つモデルを作り上げ、出したことでしょう。</p>
<p>Classification で解決できる課題は、思った以上に広いです。BERT 以降、分類やスコア付けなどの NLP タスクでは Transformer encoder が広く使われてきました。</p>
<p>本記事では、学習したタスクなら、ブラウザ上でも動くほど小さなモデルである程度の精度を出せる、bekko-system-one-v0 を作ったお話でした。学習済みのタスクで小型モデルを試したり、独自データで追加学習したりするための出発点として、モデル・データセット・学習コードを公開しています。</p>
<p>幅広い未知のタスクへの対応は、今後の課題です。前項で述べたとおり、大量の良質なデータセットや、小型モデルアーキテクチャの進化により、十分な汎化性能を持った System One Decision モデルは、きっと開発されるはずです。</p>
<p>この bekko-system-one-v0 が、何らかの価値につながれば幸いです。</p>
<h1>参考URL</h1>
<h2>bekko-system-one</h2>
<ul>
<li><a href="https://huggingface.co/collections/hotchpotch/bekko-system-one-v0-6abc47eab9c4fe4ec83d9e30">bekko-system-one collection</a></li>
<li><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-17m">bekko-system-one-v0-17m</a></li>
<li><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-68m">bekko-system-one-v0-68m</a></li>
<li><a href="https://huggingface.co/hotchpotch/bekko-system-one-v0-400m">bekko-system-one-v0-400m</a></li>
<li><a href="https://huggingface.co/spaces/hotchpotch/bekko-system-one-in-browser">ブラウザデモ</a></li>
<li><a href="https://github.com/hotchpotch/bekko-system-one">学習・推論コード</a></li>
<li><a href="https://huggingface.co/datasets/hotchpotch/bekko-system-one-dataset-v0">学習データセット</a></li>
</ul>
<h2>S1MB</h2>
<ul>
<li><a href="https://github.com/hotchpotch/S1MB">評価コード</a></li>
<li><a href="https://huggingface.co/datasets/hotchpotch/s1mb-dataset">評価データセット</a></li>
<li><a href="https://huggingface.co/datasets/hotchpotch/s1mb-result">評価結果リポジトリ</a></li>
<li><a href="https://huggingface.co/spaces/hotchpotch/S1MB-leaderboard">Leaderboard</a></li>
<li><a href="https://huggingface.co/blog/hotchpotch/system-one-mosaic-benchmark/">S1MB の紹介記事</a></li>
</ul>
<h2>base model・関連モデル・資料</h2>
<ul>
<li><a href="https://docs.typesafe.ai/concepts/system-one">TypeSafe AI — System One / Jev</a></li>
<li><a href="https://docs.typesafe.ai/models">TypeSafe AI — モデル一覧</a></li>
<li><a href="https://x.com/CompleteSkeptic/status/2100617775823966680">Diogo Almeida 氏の合成データについての投稿</a></li>
<li><a href="https://github.com/NandhaKishorM/laya">Laya：コード</a> / <a href="https://huggingface.co/convaiinnovations/laya">モデル</a></li>
<li><a href="https://huggingface.co/answerdotai/ModernBERT-base">ModernBERT-base</a></li>
<li><a href="https://arxiv.org/abs/2507.11412">Ettin：Seq vs Seq 論文</a></li>
<li><a href="https://huggingface.co/cross-encoder/ettin-reranker-17m-v1">Ettin-reranker-17m-v1</a></li>
<li><a href="https://huggingface.co/cross-encoder/ettin-reranker-68m-v1">Ettin-reranker-68m-v1</a></li>
<li><a href="https://huggingface.co/cross-encoder/ettin-reranker-400m-v1">Ettin-reranker-400m-v1</a></li>
<li><a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a8m">bekko-embedding-v1-a8m</a></li>
<li><a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a25m">bekko-embedding-v1-a25m</a></li>
<li><a href="https://arxiv.org/abs/2607.25180v1">Bekko Embedding 論文</a></li>
<li><a href="https://www.sbert.net/examples/cross_encoder/applications/README.html">Sentence Transformers — Cross-Encoders</a></li>
</ul>]]></description>
            <link>https://secon.dev/entry/2026/09/30/100000-bekko-system-one-v0-release</link>
            <guid isPermaLink="false">/entry/2026/09/30/100000-bekko-system-one-v0-release</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Jev を用い rerank・関連度でフィルターできるライブラリ jev-reranker を公開]]></title>
            <description><![CDATA[<p>TypeSafe.AI の Jev を用いて、検索結果の rerank はもとより、検索に関連しない不要な情報を削除する relevance filter の機能も持った jev-reranker というライブラリを公開しました。</p>
<ul>
<li><a href="https://github.com/hotchpotch/jev-reranker/">https://github.com/hotchpotch/jev-reranker/</a></li>
</ul>
<p>RAG などで検索を利用している場合、検索結果には引っかかるが、回答生成には不要なデータが返ってくることが多くあります。この場合、LLM に全て不要な結果も含め渡すと、処理速度やトークンコストが余計にかかってしまい、場合によってはハルシネーションを起こしかねません。</p>
<p>また全く適切な検索結果がなかった場合は、LLM に渡す前に処理を切り上げたり、別の検索クエリを検討したりできるので、閾値によって不要なデータを削ることができる、スクリーニング用途にも使える relevance rerank filter は単純な並べ替えの rerank よりも重要になるケースが多々あります。本記事の後半でも述べますが、<a href="https://huggingface.co/datasets/hakari-bench/NanoBEIR-en">NanoHotpotQA</a> では、検索候補の約92%を削減しながら、nDCG@10で0.975を達成しています。</p>
<p><img src="https://raw.githubusercontent.com/hotchpotch/jev-reranker/main/assets/relevance_filter_performance.webp" alt="jev-reranker relevance filtering and performance comparison"></p>
<p>イメージとしてはこんな感じで、関連しないドキュメントはスコアがかなり低く出るので、閾値を使った足切りがしやすいですね。</p>
<h2>relevance rerank filter としての利用例</h2>
<p>以下のように簡単に利用ができます。</p>
<pre><code class="hljs language-sh">uv add jev-reranker</code></pre>
<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> jev_reranker <span class="hljs-keyword">import</span> JevReranker

query = <span class="hljs-string">"How long do I have to return an online order to ACME Shop?"</span>
documents = [
    <span class="hljs-string">"ACME Shop accepts online returns within 30 days of delivery."</span>,
    <span class="hljs-string">"For ACME Shop online orders, submit your return request within 30 days of receiving the item."</span>,
    <span class="hljs-string">"ACME Shop in-store purchases can be returned within 14 days of purchase."</span>,
    <span class="hljs-string">"ACME Shop products come with a one-year repair warranty."</span>,
    <span class="hljs-string">"FooBar Shop accepts online returns within 60 days of delivery."</span>,
]

reranker = JevReranker(api_key=<span class="hljs-string">"YOUR-TYPESAFE-API-KEY..."</span>)
response = reranker.relevance_rerank(query, documents, threshold=<span class="hljs-number">0.2</span>)

<span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> response[<span class="hljs-string">"results"</span>]:
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"<span class="hljs-subst">{item[<span class="hljs-string">'score'</span>]:<span class="hljs-number">.2</span>f}</span>  <span class="hljs-subst">{item[<span class="hljs-string">'text'</span>]}</span>"</span>)</code></pre>
<p>結果の出力例は以下です。後半の3つはなんとなく似ていますが、実際は紛らわしい回答なので、閾値未満のため削除されてます。</p>
<pre><code class="hljs language-text">0.98  ACME Shop accepts online returns within 30 days of delivery.
0.97  For ACME Shop online orders, submit your return request within 30 days of receiving the item.</code></pre>
<h2>relevance rerank と rerank は何が違うの？</h2>
<p>ここが Jev の面白いところで、異なるのは<a href="https://github.com/hotchpotch/jev-reranker/blob/main/src/jev_reranker/instructions.py">インストラクション（指示文）</a>のみです。query に強く関連があるかないかだけで評価すると、ソートに適した rerank になりますし、RAG の検索後処理で利用したいような「質問の回答に関連が低いものは極端に低スコア」のような指示文を入れれば、relevance rerank filter として使うことができます。これも Jev の汎化性能の高さゆえですね。</p>
<h2>Jev の評価結果</h2>
<p>今回は、マルチホップ QA データセットである、HotpotQA の情報検索用にサンプリングされたデータセットの NanoHotpotQA に対して、以下のような評価を行いました。</p>
<ul>
<li>NanoHotpotQA を BM25 と dense（<code>harrier-oss-v1-270m</code>）検索を用いて、Hybrid 検索を行い、Top-100 を抽出。もし Top-100 に正解がない場合は 101 件目に正解を挿入</li>
<li>その結果に対して、nDCG@10 の結果を取得</li>
</ul>
<p>つまり query 50 に対し、各々検索結果上位の 100（or 101）件に対して rerank するようなベンチマークタスクです。結果は以下です。</p>
<table>
<thead>
<tr>
<th>方式</th>
<th align="right">閾値</th>
<th align="right">nDCG@10</th>
<th align="right">残留文書 / query</th>
<th align="right">文書削減率</th>
</tr>
</thead>
<tbody>
<tr>
<td>Hybrid search</td>
<td align="right">—</td>
<td align="right">0.833</td>
<td align="right">100件</td>
<td align="right">0%</td>
</tr>
<tr>
<td><code>rerank</code></td>
<td align="right">0.0</td>
<td align="right">0.969</td>
<td align="right">100件</td>
<td align="right">0%</td>
</tr>
<tr>
<td><code>relevance_rerank</code></td>
<td align="right">0.2</td>
<td align="right"><strong>0.975</strong></td>
<td align="right">7.62件</td>
<td align="right"><strong>92.38%</strong></td>
</tr>
</tbody>
</table>
<p>評価方法と再現手順は、<a href="https://github.com/hotchpotch/jev-reranker/blob/main/docs/eval.md">評価スクリプトの利用ガイド</a> にまとめています。今回の評価では NanoBEIR-en の NanoHotpotQA を使い、候補数を制限せず、50クエリそれぞれの hybrid 検索結果100件を対象にしました。採点方式は listwise、閾値は <code>rerank</code> が <code>0.0</code>、<code>relevance_rerank</code> が <code>0.2</code> です。同じスクリプトで Hybrid search の nDCG@10 も確認できます。</p>
<p>特筆すべきは relevance rerank filter の文書の削減率です。これは、閾値（0.2）未満のスコアのものを全て関連しないとして削除した時の削除率で、1 query あたり、100件の検索結果を平均約7.6件にフィルターで効率よく削除・スクリーニングができています。検索ドキュメントのうち、質問に対する関連性が低いと判定された文書約92%も、この段階で削除ができてるのです。</p>
<p>さらに削除した結果の nDCG@10 も <code>0.975</code> と非常に高く、削除したからといって検索結果が劣化しているわけではありません。</p>
<p>RAG のパイプラインで、query に対して検索結果のドキュメント 100 件を取得して、全て後の LLM に渡すと、前述の通り非常に高コストです。しかしながら、この relevance filter を使うことで、1 query あたり平均約7.6件しか渡さずに済んで、かつその中に正解が含まれています。そのため、低い性能の LLM でもハルシネーションを起こす可能性がグッと低くなるでしょう。</p>
<p>このように、jev-reranker の、とりわけ relevance rerank filter を使うことで、query に関係がない検索結果を大幅に削減することが可能なのです。</p>
<p>また、この閾値も柔軟に変更が可能です。例えば見逃すと損害が大きいシステムでは <code>&#x3C;= 0.05</code> などにできますし、検索に関連しなそうなら大胆に結果を削除して良いなら、<code>&#x3C;= 0.5</code> なども考えられるでしょう。また前述の通り、インストラクション文章を変えれば、自分自身のシステムにあった relevance filter を作成することが可能なのです。これが Jev が注目されている、汎化性能の高さですね。</p>
<p>なお、この結果は、<code>jev-1.13.0</code> の結果に基づいています。Jev のモデルが更新されると、結果が異なる可能性が高いです。</p>
<p>余談ですが、高性能 reranker としてよく使われている、reranker に特化した cross-encoder である <a href="https://huggingface.co/BAAI/bge-reranker-v2-m3">bge-reranker-v2-m3</a> 並みの rerank 性能も、手元では確認できているので、気になる方は評価してみてはいかがでしょうか。</p>
<hr>
<h2>なぜ jev-reranker ライブラリを使うのか？</h2>
<p>実際のところ、Jev の API をそのまま叩いても十分機能するのですが、この jev-reranker は情報検索の実利用で便利な機能を最初から実装しています。</p>
<h3>listwise or pointwise（pairwise）</h3>
<p>Jev では、rerank する際に 2 つの手段が考えられます。1 つは <code>[query, doc1, doc2, ...]</code> など 1 つのコンテキストに全て埋め込む listwise の手法、もう 1 つは <code>[[query, doc1], [query, doc2], ...]</code> と、query と doc の pair を毎回リクエストする pointwise の手法、2 パターンがあります。</p>
<p>jev-reranker ではこの 2 つの方法、両方に対応しています。比較してみたところ、基本的に listwise の方が高速かつ精度が良いので、デフォルトは listwise にしていますが、pointwise の方が良いタスクもあるかもしれません。</p>
<p>また、Jev は現状最大で 32k token しか含めないため、とりわけ listwise の 1 req に全てを詰め込む方法では、max token になってしまいがちです。そのため、適切に文字列長 or token 長で分割してリクエストを送るようにしています。本当は Jev tokenizer が公開されていれば、事前に 32k token を超えていないかが観測できるのですが、残念ながらまだライブラリや API 共に token の長さを事前に測る方法は、公開されていないようです。</p>
<h3>並列性</h3>
<p>毎度 1 つ 1 つを直列で叩くと非常に遅いため、最初からある程度並列リクエストを叩きます。そのため、高速な処理が可能です。もちろん、設定で並列数は変えられますし、レートリミットでエラーになった場合は時間をおいて retry 等々の再開・例外処理も実装されています。</p>
<p>そのため、jev-reranker を使うことで、rerank、relevance filter に適したインストラクションプロンプトはもとより、これらの便利機能も利用可能なので、よかったら使ってみてくださいね。</p>
<ul>
<li><a href="https://github.com/hotchpotch/jev-reranker/">https://github.com/hotchpotch/jev-reranker/</a></li>
</ul>
<h2>Jev の感想というお気持ちというか</h2>
<p>Jev は、巷の評判の通り、LLM では遅く、かといってなんらかの特化 model を学習するにはコストが高すぎる、適切にモデルを作成できる人材がいない、ような用途の隙間にピッタリとハマる、汎化性能が高い structured decision モデルです。</p>
<p>また、汎化性能の高さ、というのも魅力です。検索結果はコンテキストによって優先すべき結果が異なるため、それを instruction により制御できることは、今後重宝されていくでしょう。情報検索タスクの一部である reranker や relevance filter としても、現時点でも性能の高さが窺い知れますね。</p>
<p>そんなわけで、Jev の出現は「今までになかった、隙間を埋める汎化モデル」として非常に喜ばしいですね。また Jev のようなモデルが流行ることによって、別の企業・研究機関なども同様の方向性のモデルを出してくる流れになることは間違い無いでしょうから、その点でも Jev の登場は嬉しいですね。今後の AI 関連技術の発展が見えるような、ワクワクするモデルの登場でした。</p>]]></description>
            <link>https://secon.dev/entry/2026/09/20/100000-jev-reranker</link>
            <guid isPermaLink="false">/entry/2026/09/20/100000-jev-reranker</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Sun, 20 Sep 2026 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[openai-api-server-via-codex を golang に書き換え]]></title>
            <description><![CDATA[<p>以前作った codex サブスクリプション経由で OpenAI 互換APIの変換プロキシ的なツールである <a href="https://github.com/hotchpotch/openai-api-server-via-codex">openai-api-server-via-codex</a>、個人的に便利に使っている。リリース当初はほぼ反響がなかったんだけど、最近 github star がじわじわ増え出して(と言っても現在30だけど…)、PRもいくつかもらったりした。</p>
<p>リリース当初、考えもなくとりあえず python で作ってしまったのだけど、どう考えてもパフォーマンスが悪いし python である理由もないので、golang で書き換える。エーアイ氏に書き換えていただく。これで速攻立ち上がるようになり、利用メモリも10M以下となったので、常時立ち上げっぱなしでも気にならない感じになった。起動自体は互換性を考慮し、uvx 経由で golang バイナリが立ち上がるようにしている。</p>
<img width="70%" src="https://raw.githubusercontent.com/hotchpotch/openai-api-server-via-codex/main/docs/assets/quick-start.png" alt="Start the Go server with uvx, then call the OpenAI-compatible Responses API" />
<hr>
<p>貰ったPR、たまたまかもしれないが、どれも丁寧に作られている。AI Slop 的な PR ではなく、PR がマージしやすいように色々配慮されて作らていてびっくり。コーディングエージェントの性能も上がったので、その辺のやり方もこなれてきている感じがするね。人間が気をかけることが減って、ありがたい限りだね。</p>]]></description>
            <link>https://secon.dev/entry/2026/08/13/200000-openai-api-server-go-rewrite</link>
            <guid isPermaLink="false">/entry/2026/08/13/200000-openai-api-server-go-rewrite</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Thu, 13 Aug 2026 11:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Spotify で再生中アートワークを Pixoo 64 で表示]]></title>
            <description><![CDATA[<p><a href="https://divoom.com/products/pixoo-64/">Divoom Pixoo 64</a>という、64x64 ピクセルの解像度が荒い matrix 液晶のデバイスを購入した。<a href="https://tenderlovemaking.com/2026/01/01/pixoo64-ruby-client/">tenderlove氏の記事</a>を読んで、プログラムから制御できそうで面白い、と思ったのがきっかけ。</p>
<p>やりたかったことは、聴いている音楽(Spotifyを愛用してる)のアートワークを部屋のどこかに表示したかったのだ。だいたいPCかスマフォから、部屋のスピーカで音楽を再生しているのだけど、思った以上にアートワークを見る機会がなく、ランダムに流していると、今流れている曲はなんだろう、と知りたい欲求もちょくちょくあって、どこかで常時表示されていてほしい。</p>
<p>というわけでエーアイ氏に実装していただいた。正確には7ヶ月ぐらい前に作って日々使っているのだけど、ちょっと手直しして公開。Pixoo の API ドキュメントはお世辞にも人間が読みやすい・使いやすいとは言えないのだけど、エーアイ氏なら読み解いてくれて実装してくれて便利。叩けるAPI自体は便利なものが幅広く実装されていて、ドキュメントはとりあえず公開されているだけで十分とも感じる。</p>
<ul>
<li><a href="https://github.com/hotchpotch/pixoo-spotify">https://github.com/hotchpotch/pixoo-spotify</a></li>
</ul>
<p>こんな感じで表示される👇</p>
<img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20260810-pixoo-spotify-example.gif" width="128" height="128" />
<p>64x64 ピクセルしかないので、ピクセルアートっぽくなって満足。kawaii。曲タイル取るなどは、美咲ゴシック8pt をオーバーレイで重ね、gif を作り、それを表示している感じ。小さなピクセルフォント様々(ありがたや〜)。プログラムは余っている raspi で動かしている。spotify API は pooling なので反応が若干遅い。イベントで受け取れるといいんだけどねぇ。</p>]]></description>
            <link>https://secon.dev/entry/2026/08/10/200000-pixoo-spotify-release</link>
            <guid isPermaLink="false">/entry/2026/08/10/200000-pixoo-spotify-release</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Mon, 10 Aug 2026 11:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[あるいちにち]]></title>
            <description><![CDATA[<p><img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20260806-a-day-with-ai-agent.webp" alt="ある夜"></p>
<p>つい、やらなくてもいいようなことを、たくさんやってしまった一日。AI Agent により、以前ははやればできるが時間かかるのでやらない・やるべきでないと判断していた開発が雑指示で出来てしまう。それらは本来優先度が高くないのだけど、10分ぐらいで終わるなら、とついやってしまう。設計・実装的には無駄なコードが増えがち・チーム共有の他人への認知負荷も発生するのでなので、マイナスですらある。</p>
<p>今までは数時間〜数日かかった開発が物の数分〜数十分出てきちゃうので、成果が出たと勘違いしてしまうが、むしろマイナス。本末転倒である。もっと考えを広げたり深めたりする時間に使う分だが、AI Agent は毎度ガチャが発生して射倖心を煽る(短時間で良いものが作れるかどうかはギャンブルみがある)からなぁ。issue, PR をこなすと成果が出た気になれてしまうのも良くない。</p>
<hr>
<p>以前某社にソフトウェアエンジニアとして入社した時、目標設定の際に、開発はしなくて良いから、目標を徹底的に考えて。この目標は小さな積み重ねではなく、到達が非常に難しいが、達成した時に大きな価値につながるものね、というお題をこなしていた時期が1ヶ月ぐらいあった。</p>
<p>目標承認も、上司の部長承認、その後社長承認を経て、初めて開発に取り掛かれる、というものだった。とりわけ社長承認が「うーん、本当？なんか違うんじゃないかなー」という禅問答のような内容で、まぁ解も特になかった(と思う)のだけど、エンジニアだけど手を止めて開発せずに、徹底的に考える、みたいなプロセスには非常に価値があった。何も価値を出せていない焦り（何かを開発すると、とりあえずは価値が出せている感じなり安心できる。新参者は新しい組織で居場所が作れてないから尚更）はあって、入社早々転職を考えたりもしたけど、今振り返るとありがたい思考プロセスであった。サラリーマンなので給料はもらえてたしね、ラッキー。</p>
<hr>
<p>個人的には、今世に出ている AI を使ったプロダクトの成功は非常に難しいのでは、と思っている。技術進化が速く、あっという間に時代遅れになるから、プロダクトリリースし、そこから進化し続けない限りは失敗する。ほとんどのプロダクトはスタートが今見えているゴールであるし、ユーザが定着するとそれはそれで破壊的変更を入れることが難しい(裏側の見えない部分を徹底的に良くする、みたいな方向になる)ので、AI Agent でガット作っても、ガットの作り直しはリリースしてからは出来にくい。</p>
<p>またAIを使わないプロダクトは、AIによって代替されるなら、今作っても意味がないので、プロダクトを成功させる難易度が、プロダクト実装は簡単になったが、むしろ世の中の流れ的には、非常に難しくなっている。</p>
<hr>
<p>AI における開発生産性向上も、結局 AI を使ったベストな開発手法なんてしょっちゅう変わっていっているので、あんまり追いかけてもなーと思っている。プロダクトを作るのが難しい今、開発生産性が上がっても事業価値は上がらないから、なら別に組織としては上げなくても良いのでは、とも思っている。時間が経てば、ある程度汎用的なAIとの協調開発手法が生み出されていくわけだし。</p>
<p>例えば今流行りのハーネスも、AIと協調開発しやすい環境を整えましょうやっていき〜〜という感じで、今今AIにおける開発生産性を上げたいならやりましょう、となるのだが、将来 AI ready cloud みたいな完成系が出たタイミングで移行する、でも良いのでは。</p>
<p>流行りのループなんかも、プロダクトにおけるループゴールの設定を考えるには、結果組織が何をゴールにするべきか、そのゴールを提供するための判断材料は、そのためにはコンテキストが必要だからさまざまな方法、例えば Claude Tag を用いて slack でのフロー情報を集めたりして、組織最高のコンテキストメモリを作ってくぞ、みたいな話になったりしがちだが、そのコンテキストメモリの作り方も今後変わっていくだろうし、いやいやそんなみんながデジタルな場所に情報は出さないでしょ、もっと別のところから変えていくべきでは、などとも思ったりする。</p>
<p>組織の価値提供・向上のボトルネックは開発やデジタル作業から別のところに移るのは間違いないので、ではそのボトルネックは自分の組織ではどこになって、どういうふうに変えていくと良いのでは、といった解を考え、変えていくことに時間をかけるべきなんじゃないかなぁ。言うが易しだけどね。</p>
<hr>
<p>あれこれと無駄なことをやり過ぎてしまった1日を自省しつつ、なにもやらないこと自体が価値になったりもするんじゃないかなー、となにもしなかった1ヶ月間を思い出したりしていたのでした。</p>]]></description>
            <link>https://secon.dev/entry/2026/08/06/200000-a-day-with-ai-agent</link>
            <guid isPermaLink="false">/entry/2026/08/06/200000-a-day-with-ai-agent</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Thu, 06 Aug 2026 11:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[超小型な多言語ベクトル検索モデル Bekko Embedding を公開]]></title>
            <description><![CDATA[<p>昨今、ベクトル(文章埋め込み)モデルは性能向上とともにサイズも巨大になってきました。総パラメータ数が100M〜600Mの多言語モデルでは、<a href="https://huggingface.co/google/embeddinggemma-300m">EmbeddingGemma</a>、<a href="https://huggingface.co/ibm-granite/granite-embedding-311m-multilingual-r2">Granite Embedding R2 311M</a>、<a href="https://huggingface.co/Alibaba-NLP/gte-multilingual-base">gte-multilingual-base</a>、<a href="https://huggingface.co/BAAI/bge-m3">BGE-M3</a>、<a href="https://huggingface.co/Snowflake/snowflake-arctic-embed-l-v2.0">Arctic Embed</a> などがあります。さらに大きなモデルとしてLLMを基盤にした <a href="https://huggingface.co/nvidia/Nemotron-3-Embed-8B-BF16">Nemotron-3-Embed-8B</a>、<a href="https://huggingface.co/Qwen/Qwen3-Embedding-8B">Qwen3-Embedding-8B</a>、<a href="https://huggingface.co/perplexity-ai/pplx-embed-v1-4b">pplx-embed-v1-4b</a> など、数Bのモデルも出てきました。十分な速度で動かせるGPU環境があるなら、こうした大きな高性能モデルを使うと良いですね。</p>
<p>ただ、自分が興味を持ったのは巨大な高性能化とは反対の、どこまで小型にできるかというアプローチです。実際の検索に使える性能を残したまま、どこまで小さくできるのか。そこで作ってみたのが Bekko Embedding です。 <a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a8m">bekko-embedding-v1-a8m</a> と <a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a25m">bekko-embedding-v1-a25m</a> の2モデルでMITライセンスで公開しています。以下では短く bekko-a8m、bekko-a25m と表記します。</p>
<p>小さい方(a8m)のモデルは、<strong>Active Parameters（AP）がわずか 7.67M</strong>です。APとはトークン埋め込み参照テーブルを除いた、Transformer演算で使う重みの部分のパラメータです。検索性能も公式の <a href="https://huggingface.co/spaces/mteb/leaderboard">MMTEB Multilingual v2</a> に含まれる18の検索タスクでは平均スコアをa8m では56.2を記録し、APが約3〜40倍ある <a href="https://huggingface.co/intfloat/multilingual-e5-small">multilingual-e5</a> 系の全モデルと BGE-M3 を上回りました。またよりパラメータが大きい bekko-a25m はより良いスコアを出しています。</p>
<p><img src="https://storage.googleapis.com/secons-site-images/other/huggingface/bekko/blog/01_frontier.webp?v=2" alt="Active Parametersあたりの検索性能"></p>
<table>
<thead>
<tr>
<th></th>
<th align="right"><a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a8m">bekko-embedding-v1-a8m</a></th>
<th align="right"><a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a25m">bekko-embedding-v1-a25m</a></th>
</tr>
</thead>
<tbody>
<tr>
<td>Active Parameters</td>
<td align="right">7.67M</td>
<td align="right">24.93M</td>
</tr>
<tr>
<td>総パラメータ数</td>
<td align="right">106M</td>
<td align="right">123M</td>
</tr>
<tr>
<td>出力次元</td>
<td align="right">384 (→ 256 / 128 / 64)</td>
<td align="right">384 (→ 256 / 128 / 64)</td>
</tr>
<tr>
<td>最大入力長</td>
<td align="right">8,192トークン</td>
<td align="right">8,192トークン</td>
</tr>
<tr>
<td>MMTEB Multilingual v2、検索18タスク</td>
<td align="right">56.2</td>
<td align="right">57.5</td>
</tr>
<tr>
<td>MMTEB Multilingual v2、全131タスク</td>
<td align="right">56.7</td>
<td align="right">58.3</td>
</tr>
<tr>
<td>CPUスループット (Ryzen 9 7950X、OpenVINO)</td>
<td align="right">364 docs/s</td>
<td align="right">134 docs/s</td>
</tr>
<tr>
<td>軽量 ONNX ファイル</td>
<td align="right">124 MiB</td>
<td align="right">190 MiB</td>
</tr>
<tr>
<td>query / document prefix</td>
<td align="right">なし</td>
<td align="right">なし</td>
</tr>
</tbody>
</table>
<p>インストールせずに試すこともできます。<a href="https://huggingface.co/spaces/hotchpotch/bekko-embedding-web">bekko-embedding-web</a> のデモはモデルをブラウザに読み込み、そのままブラウザ上で実行します。</p>
<h2>小型モデルを作った理由</h2>
<p>コーディング・AIエージェントの台頭により、PCの使い方が変わりました。LLMの推論はAPIの先にあるフロンティアモデルが行うため、その部分においては手元のマシン性能は以前ほど気になりません。一方、エージェントが情報を探すための処理は、今も手元のマシンで動かします。ドキュメントの埋め込み(ベクトルに変換)、インデクス作成、コンテキストの探索等々です。</p>
<p>手元のマシンにGPUがないことも多く、低スペックなマシン(Raspberry Piなど)で動かすこともあります。その際 multilingual-e5-large で1万件の文書を埋め込むと、高速なデスクトップCPUでも約8分、Raspberry Pi 5 では2時間近くかかります。しかもクエリの埋め込みはインデックス作成時に一度計算すれば終わりではなく、検索のたびにも推論が必要です。</p>
<p>小さな埋め込みモデル、たとえばAPが30M未満のモデルの選択肢はかなり少ないです。自分が調べた範囲では、オープンなライセンスで利用でき、文脈を考慮する多言語埋め込みモデルのうち、Bekko以外で30M未満かつ検索に使える性能だったのは2つだけでした。2020年に蒸留された MiniLM を初期値にした <a href="https://huggingface.co/intfloat/multilingual-e5-small">multilingual-e5-small</a> と、2026年に公開されたAP 28.3Mの <a href="https://huggingface.co/ibm-granite/granite-embedding-97m-multilingual-r2">granite-embedding-97m-r2</a> です。</p>
<p>なおModel2Vec などの <a href="https://huggingface.co/blog/static-embeddings">静的埋め込み Static Embeddings</a> はさらに小さくできます。ただし、Transformers のような文脈を理解するモデルではなく、性能も低くなります。</p>
<h2>モデルパラメータと演算量</h2>
<p>多言語埋め込みモデルの演算量は、総パラメータ数だけでは見積もれません。モデルが118Mパラメータと書かれていると、そのパラメータすべてを使って計算するように思えます。しかし、多言語エンコーダでは、その大半が多言語のためのトークン埋め込みテーブルです。Bekkoの場合はこの埋め込みテーブルが256,000トークンありますが、推論時にはこのテーブルを参照するだけです。トークンごとに1つを取り出すため行列積はなく、このルックアップテーブルから読み込むだけです。</p>
<p>各トークンで実際に行列積を行うのは、Transformerブロックに含まれるパラメータです。<a href="https://arxiv.org/abs/2001.08361">スケーリング則で用いられるパラメータの数え方</a>にならい、ここではこれらを <strong>Active Parameters</strong>(AP) と呼びます。総パラメータ数よりもより推論コストを考える際に重要な指標となります。</p>
<p>この Active Parameters (AP) という言葉は別の意味でも使われるため、補足します。ここでいう AP は、ルータがトークンごとに異なるエキスパートを選び、全重みの一部だけを動かす Mixture-of-Experts(MoE) の文脈のAPではありません。Bekko は MoE ではない通常のエンコーダで、7.67Mのパラメータすべてを推論時に使います。パラメータ数から除いているのは、埋め込みのルックアップだけです。</p>
<p>たとえば、multilingual-e5-small は総パラメータ数118Mのうち、APが21.6Mです。bekko-a8m は総パラメータ数106M、AP 7.67Mです。この差が推論時間に表れます。なおこのAPだけで速度が決まるわけではありませんが、小さいほど基本的に推論が高速です。</p>
<h2>Bekko Embedding</h2>
<p>Bekko は、22層の <a href="https://huggingface.co/jhu-clsp/mmBERT-small">mmBERT-small</a> エンコーダを4層(a8m)と13層(a25m)にそれぞれ pruning し、これらの2モデルを約11億の多言語ペアで学習しています。</p>
<ul>
<li>100以上の言語を含むデータで、言語横断検索ができるように学習しました。翻訳を挟まず、日本語のクエリから英語の文書を検索できます。後述する言語別評価では、そのうち14言語を扱います。</li>
<li>どちらのモデルも8,192トークンまで入力できます。</li>
<li>出力は384次元です。Matryoshka学習により、256 / 128 / 64次元へ切り詰めて使えます。bekko-a25m を256次元にすると、検索性能は HAKARI-Bench Overall が相対で約1.3%、128次元では約6.2%低下します。</li>
<li>モデルの重みはMITライセンスです。</li>
<li>学習データも、<a href="https://huggingface.co/datasets/hotchpotch/bekko-embedding-v1-unsupervised">第一段階のコーパス</a>と<a href="https://huggingface.co/datasets/hotchpotch/bekko-embedding-v1-hard-negatives">マイニングしたhard negatives</a>を公開しています。</li>
</ul>
<h2>ベンチマーク結果</h2>
<p>下の図には6つの列があります。最初の2列は、同じ <a href="https://huggingface.co/spaces/mteb/leaderboard">MMTEB Multilingual v2</a> 評価を異なる切り口で集計したものです。ALLは全131タスクの平均、Retはそのうち18の検索タスクだけの平均です。比較モデルのスコアには公式に公開されているスナップショットを使い、Bekkoも同じタスク集合と集計方法で評価しました。</p>
<p>残る4列は、すべて検索タスクのベンチマーク評価です。<a href="https://huggingface.co/collections/zeta-alpha-ai/nanobeir-66e1a0af21dfd93e620cd9f6">Multilingual NanoBEIR</a> は、英語版のオリジナル NanoBEIR に、コミュニティが公開した13の翻訳版を加えたものです。これらの翻訳版は私が作ったものではありませんが、他の Nano-set は後述の HAKARI-Bench 用に私が作った物です。NanoRTEB は、実運用に近い検索データセットを集めた <a href="https://huggingface.co/blog/rteb">RTEB</a> の小型版です。NanoCoIR は<a href="https://huggingface.co/CoIR-Retrieval">コード検索</a>、NanoLongEmbed は<a href="https://huggingface.co/datasets/dwzhu/LongEmbed">長文入力</a>を扱います。この4つは、私が作った <a href="https://huggingface.co/spaces/hakari-bench/leaderboard">HAKARI-Bench</a>（<a href="https://arxiv.org/abs/2606.22778">arXiv</a>）で同じ設定を使って評価しました。語彙一致のベースラインとして、BM25も含めています。</p>
<p><img src="https://storage.googleapis.com/secons-site-images/other/huggingface/bekko/blog/02_benchmarks.webp?v=2" alt="12モデルとBM25を6つのベンチマークで比較"></p>
<p>MMTEBの2列を並べると、順位がかなり違います。特に分かりやすいのが granite-embedding-97m-r2 で、18の検索タスクでは60.3と比較中4位ですが、全131タスクでは51.9と最下位です。検索タスクを集中して学習すると、そのほかのタスクで性能を落とすことがあります。Bekkoも検索向けに学習したモデルですが、全タスクの平均も悪くはありません。</p>
<p>bekko-a8m は6つの列すべてで multilingual-e5-small を上回り、multilingual-e5-small のAPは2.8倍と約3倍の大きさです。bekko-a25m は、APが約12分の1でありながら、MMTEBの検索18タスクでは multilingual-e5-large と BGE-M3 を上回ります。ただしMMTEB全131タスクでは、どちらのモデルも bekko-a25m より高いスコアです。</p>
<p>18の検索タスクでは、5モデルが bekko-a25m を上回ります。<a href="https://huggingface.co/microsoft/harrier-oss-v1-270m">harrier-oss-v1-270m</a>、granite-embedding-311m-r2、EmbeddingGemma-300M、granite-embedding-97m-r2、snowflake-arctic-embed-l-v2.0 です。全131タスクでは4モデルが上回ります。harrier-oss-v1-270m と EmbeddingGemma-300M に加え、BGE-M3 と <a href="https://huggingface.co/intfloat/multilingual-e5-large">multilingual-e5-large</a> です。gte-multilingual-base は同点でした。</p>
<p>ここで bekko-a25m を上回ったモデルは、granite-embedding-97m-r2 を除き、bekko-a25m の4〜13倍のAPを持ちます。検索精度を最優先し、十分な計算資源があるなら、どれも優れたモデルと言えるでしょう。</p>
<p>コード検索では、bekko-a25m は78.6で、multilingual-e5-large と BGE-M3 を上回る一方、84.7の EmbeddingGemma-300M と81.4の granite-embedding-311m-r2 には届きません。長文入力の結果は、もう少し意外でした。長い文書の中から1つの箇所を探す場合、語彙一致は強く、BM25が82.2で全モデルを上回ります。しかし12の密ベクトルモデルの中では、bekko-a25m が70.6で1位、bekko-a8m が3位でした。</p>
<h2>言語別の結果</h2>
<p>平均値だけを見ると、特定の言語でモデルの性能が崩れていても気づけません。以下は、Multilingual NanoBEIR の14言語を個別に見た結果です。</p>
<p><img src="https://storage.googleapis.com/secons-site-images/other/huggingface/bekko/blog/03_languages.webp?v=2" alt="Multilingual NanoBEIR 14言語の言語別評価結果"></p>
<p>bekko-a25m は、14言語すべてで multilingual-e5-small と BM25 を上回りました。BGE-M3 との差が最も大きい言語でも2.7ポイントです。14言語の中ではタイ語、アラビア語、セルビア語が弱く、まだ改善の余地があります。</p>
<h2>推論速度</h2>
<p>APはスループットを大まかに見積もる目安になります。Raspberry Pi 5 から RTX 5090 まで、4つの環境で6モデルを同じ条件で測定しました。</p>
<p><img src="https://storage.googleapis.com/secons-site-images/other/huggingface/bekko/blog/04_speed_devices.webp" alt="4つの環境すべてで最も高速"></p>
<p>bekko-a8m は、どの環境でも6モデル中もっとも高速でした。実際のインデックス作成で、Ryzen 9 7590X CPUで10万文書を埋め込みベクトルに変換すると、bekko-a8m なら約5分ですが、multilingual-e5-large なら約80分かかります。</p>
<p>自分がより面白いと思ったのは、Raspberry Pi 5 の結果です。1秒あたり33文書はGPUに比べれば圧倒的に遅いですが、1万文書のインデックスなら約5分で作成可能です。これぐらいの速度なら、用途によっては実用的でしょう。同じ Raspberry Pi 5 で multilingual-e5-large を使うと、2時間近くかかります。</p>
<p>図の2つのCPUパネルでは、それぞれのモデルが公開している PyTorch のバックエンドに OpenVINO を使う版を使っています。6モデルとも、CPUでは OpenVINO が最速だったためです。一部のモデルだけOpenVINO を使わずに動かすと公平な比較になりません。同等の OpenVINO 版がないモデルに、ここでの数値をそのまま当てはめることはできません。Bekko は両サイズとも OpenVINO 版を公開しています。</p>
<p>APだけですべての速度差を説明できるわけではありません。multilingual-e5-small のAPは bekko-a25m の87%ですが、CPUでは1.7倍高速です。granite-embedding-97m-r2 はAPが14%多いものの、速度はほぼ変わりません。</p>
<p>自分は、アーキテクチャの違いが効いているのではないかと思っています。<a href="https://arxiv.org/abs/2412.13663">ModernBERT</a> のアーキテクチャでは、unpadding、可変長 FlashAttention カーネル、global attention と local attention の相互配置など、GPUカーネルを意識した高速化を行っています。一方、素直な XLM-R 系エンコーダアーキテクチャは、CPUバックエンドに載せやすそうです。原因を切り分けるところまではできていないので、ここは実際の実装を真に確認した結果ではなく自分の解釈です。ただこうした違いがあっても、bekko-a8m は十分に小さく、今回の比較では最速でした。</p>
<h2>124 MiB の配布サイズとブラウザ実行</h2>
<p>総パラメータ数を大きくしている語彙埋め込みテーブルは、ダウンロードサイズも大きくします。このテーブルは静的なルックアップなので、ONNX 版と OpenVINO 版では行ごとの int8 量子化で保存し、Transformer の重みは fp32 のままにしました。リリース時の検証では、PyTorch 版とのコサイン類似度は最小でも0.9994でした。</p>
<p>これにより bekko-a8m は124 MiB、bekko-a25m は190 MiBになり、ブラウザへ配れるサイズになりました。なおTransformer の重み自体をさらに小さくfloatから int8 にしサイズ削減もできるのですが、精度劣化が激しいため行なっておりません。</p>
<table>
<thead>
<tr>
<th></th>
<th align="right">bekko-a8m</th>
<th align="right">bekko-a25m</th>
</tr>
</thead>
<tbody>
<tr>
<td>ブラウザ、WebGPU</td>
<td align="right">187 docs/s</td>
<td align="right">89 docs/s</td>
</tr>
<tr>
<td>ブラウザ、WASM (CPU)</td>
<td align="right">51 docs/s</td>
<td align="right">16 docs/s</td>
</tr>
<tr>
<td>ネイティブ、Apple GPU (MPS)</td>
<td align="right">592 docs/s</td>
<td align="right">351 docs/s</td>
</tr>
</tbody>
</table>
<p>上の速度図と同じ Natural Questions の入力を使っています。最後の行はブラウザでの結果ではなく、比較用に同じモデルをネイティブ実行した値です。</p>
<p>ブラウザデモは <a href="https://huggingface.co/spaces/hotchpotch/bekko-embedding-web">huggingface.co/spaces/hotchpotch/bekko-embedding-web</a> で試せます。埋め込みの計算はブラウザ内で行うため、入力した文章をサーバーへ送ることはありません。</p>
<h2>ほかのモデルを選んだ方がよい場合</h2>
<ul>
<li>日本語のみを扱いたい場合、ruri-v3 シリーズは小さく Bekko より高性能です。<a href="https://huggingface.co/cl-nagoya/ruri-v3-30m">ruri-v3-30m</a>は AP 10M、出力次元が 256 と小型ながら必要十分な性能のことも多いでしょう</li>
<li>計算資源に余裕があり、この比較で最も高い検索精度を求める場合は、harrier-oss-v1-270m、granite-embedding-311m-r2、EmbeddingGemma-300M の方が高いスコアです。</li>
<li>検索以外にも広く使える文章埋め込みモデルが必要な場合、Bekko の分類とクラスタリング性能はは中位から下位です。検索ではない目的なら、他のモデルの方が適していることも多いでしょう。</li>
</ul>
<h2>Bekko Embedding の使い方</h2>
<p>どちらのモデルを試すか迷ったら、CPUでのインデックス作成やブラウザ検索には a8m、Apple MPS やGPUが使え速度より検索性能を優先するなら a25m がよいでしょう。</p>
<p>PyTorchでそのまま動かすほか、CPUでは OpenVINO、ブラウザでは Transformers.js を使えます。3つとも同じモデルIDで動き、prefixも不要です。実行環境ごとにAPIが異なります。</p>
<pre><code class="hljs language-bash">pip install -U <span class="hljs-string">"sentence-transformers>=5.0"</span> <span class="hljs-string">"transformers>=5.12"</span></code></pre>
<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> sentence_transformers <span class="hljs-keyword">import</span> SentenceTransformer, util

model = SentenceTransformer(<span class="hljs-string">"hotchpotch/bekko-embedding-v1-a8m"</span>)

query = <span class="hljs-string">"What are the characteristics of sushi?"</span>
docs = [
    <span class="hljs-string">"A warm noodle soup served in broth with sliced toppings."</span>,
    <span class="hljs-string">"天ぷらは魚や野菜に衣をつけて揚げた料理です。"</span>,
    <span class="hljs-string">"Une fine crepe garnie de sucre, de beurre ou de fruits."</span>,
    <span class="hljs-string">"A Japanese dish made with vinegared rice, often shaped with seafood, vegetables, or egg."</span>,
]

scores = util.cos_sim(
    model.encode(query, normalize_embeddings=<span class="hljs-literal">True</span>),
    model.encode(docs, normalize_embeddings=<span class="hljs-literal">True</span>),
)[<span class="hljs-number">0</span>]
<span class="hljs-built_in">print</span>(<span class="hljs-string">"best doc:"</span>, docs[<span class="hljs-built_in">int</span>(scores.argmax())])</code></pre>
<p>prefix やタスク指示は不要で、クエリと文書はどちらも同じ <code>encode</code> 呼び出しで処理します。上の条件では、a8m の OpenVINO 実装は PyTorch より x86 で約2.8倍、Raspberry Pi 5 で約1.7倍高速です。</p>
<details>
<summary><strong>CPU で OpenVINO を使う</strong></summary>
<pre><code class="hljs language-bash">pip install -U \
  <span class="hljs-string">"sentence-transformers[openvino]>=5.0"</span> \
  <span class="hljs-string">"transformers>=4.57,&#x3C;5"</span></code></pre>
<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> sentence_transformers <span class="hljs-keyword">import</span> SentenceTransformer, util

model = SentenceTransformer(
    <span class="hljs-string">"hotchpotch/bekko-embedding-v1-a8m"</span>,
    backend=<span class="hljs-string">"openvino"</span>,
    device=<span class="hljs-string">"cpu"</span>,
    model_kwargs={<span class="hljs-string">"file_name"</span>: <span class="hljs-string">"openvino/openvino_model.xml"</span>, <span class="hljs-string">"device"</span>: <span class="hljs-string">"CPU"</span>},
)

query = <span class="hljs-string">"What are the characteristics of sushi?"</span>
docs = [
    <span class="hljs-string">"A warm noodle soup served in broth with sliced toppings."</span>,
    <span class="hljs-string">"A Japanese dish made with vinegared rice, often shaped with seafood, vegetables, or egg."</span>,
]

scores = util.cos_sim(
    model.encode(query, normalize_embeddings=<span class="hljs-literal">True</span>),
    model.encode(docs, normalize_embeddings=<span class="hljs-literal">True</span>),
)[<span class="hljs-number">0</span>]
<span class="hljs-built_in">print</span>(<span class="hljs-string">"best doc:"</span>, docs[<span class="hljs-built_in">int</span>(scores.argmax())])</code></pre>
<p>OpenVINO 連携は Transformers を5.1未満に固定するため、先ほどの <code>transformers>=5.12</code> と同じ環境には入れられません。別環境を用意するか、両方とも Transformers 4.57 を使ってください。通常の PyTorch の例も4.57で動きます。</p>
</details>
<p>Transformers.js でも、同じモデルをブラウザ内で動かせます。対応しているブラウザでは WebGPU を使います。</p>
<details>
<summary><strong>ブラウザで Transformers.js を使う</strong></summary>
<pre><code class="hljs language-bash">npm install @huggingface/transformers</code></pre>
<pre><code class="hljs language-js"><span class="hljs-keyword">import</span> { pipeline } <span class="hljs-keyword">from</span> <span class="hljs-string">"@huggingface/transformers"</span>;

<span class="hljs-comment">// Browser: use WebGPU when available, otherwise fall back to WASM.</span>
<span class="hljs-comment">// Node.js: replace this line with `const device = "cpu";`.</span>
<span class="hljs-keyword">const</span> device = navigator.gpu ? <span class="hljs-string">"webgpu"</span> : <span class="hljs-string">"wasm"</span>;

<span class="hljs-keyword">const</span> extractor = <span class="hljs-keyword">await</span> pipeline(
  <span class="hljs-string">"feature-extraction"</span>,
  <span class="hljs-string">"hotchpotch/bekko-embedding-v1-a8m"</span>,
  { device, <span class="hljs-attr">dtype</span>: <span class="hljs-string">"fp32"</span> },
);

<span class="hljs-keyword">const</span> embedding = <span class="hljs-keyword">await</span> extractor(<span class="hljs-string">"What are the characteristics of sushi?"</span>, {
  <span class="hljs-attr">pooling</span>: <span class="hljs-string">"mean"</span>,
  <span class="hljs-attr">normalize</span>: <span class="hljs-literal">true</span>,
});

<span class="hljs-built_in">console</span>.log(embedding.dims); <span class="hljs-comment">// [1, 384]</span></code></pre>
</details>
<h2>リソースへのリンク</h2>
<ul>
<li>モデル：<a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a8m">bekko-embedding-v1-a8m</a> と <a href="https://huggingface.co/hotchpotch/bekko-embedding-v1-a25m">bekko-embedding-v1-a25m</a></li>
<li>ブラウザデモ：<a href="https://huggingface.co/spaces/hotchpotch/bekko-embedding-web">bekko-embedding-web</a></li>
<li>学習データ：<a href="https://huggingface.co/datasets/hotchpotch/bekko-embedding-v1-unsupervised">第一段階のコーパス</a> と <a href="https://huggingface.co/datasets/hotchpotch/bekko-embedding-v1-hard-negatives">hard negatives</a></li>
</ul>
<h2>モデルの学習方法</h2>
<p>使い方を中心にした記事なので、学習については簡単に。mmBERT-small の22層を4層と13層に削り、先頭側の層と、深い位置にある global attention の層を1つ残しました。削った後のチェックポイントを、そのまま基盤モデルとして使っています。</p>
<p>そこから2段階の対照学習を行いました。第一段階では、公開しているコーパスの約11億多言語ペア(合成データセットなども多様して作成)を使っています。性質の異なる2系統の LLM 合成クエリも含まれます。続いて、8,192トークンの長文も含む hard negative を使ってファインチューニングしました。どちらの段階でも教師モデルや蒸留は使っていません。</p>
<p>両モデルとも、RTX PRO 6000 Blackwell Max-Q 1枚のみで学習しました。所要時間は a8m が約3日、a25m が約8日です。一般的な規模の多言語埋め込みモデルは GPU クラスターで学習され、BGE-M3 の論文では最大96基の GPU を使ったと報告されています。APを小さくすると推論コストが下がり、学習もワークステーション GPU 1枚で行えます。RTX5090でも学習時間は増えますが、学習可能です。大量のGPUが必要ないことも、自分がこのサイズのモデルに取り組む大きな理由の1つです。</p>
<p>今回のモデルは、かなり多くの研究や実装を参考にしています。基盤モデルには mmBERT と ModernBERT、学習方法には E5、GTE、BGE-M3、Qwen3 Embedding、Ruri、実装には sentence-transformers を使いました。これだけ小さなモデルで、どうやって多言語検索の性能を保ったのか。残す層を選ぶ実験、損失、約11億ペアの収集・合成方法、詳しい評価結果をまとめた野良論文は以下で公開しています。</p>
<ul>
<li>arXiv: <a href="https://arxiv.org/abs/2607.25180">Bekko Embedding: Parameter-Efficient Multilingual Retrieval with Ultra-Compact Encoders</a></li>
<li>日本語版: <a href="https://storage.googleapis.com/secons-site-images/other/huggingface/bekko/blog/bekko_embedding_paper_ja.pdf">Bekko Embedding: 超小型エンコーダによるパラメータ効率の高い多言語検索</a></li>
</ul>
<p>小さな多言語検索モデルを探している方に、Bekko Embedding が一つの選択肢になれば幸いです。</p>]]></description>
            <link>https://secon.dev/entry/2026/07/29/080000-bekko-embedding</link>
            <guid isPermaLink="false">/entry/2026/07/29/080000-bekko-embedding</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Tue, 28 Jul 2026 23:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[herdr で cdd]]></title>
            <description><![CDATA[<p>screen, tmux 時代から、<a href="https://secon.dev/entry/20080218/1203303528/">cdd というコマンドを作り</a>愛用してきた。cdd とは、例えば <code>window:3</code> で <code>~/foo/bar</code> のディレクトリで作業している時に <code>cdd 3</code> とすると <code>cd ~/foo/bar</code> される、というだけのものなのだが、しょっちゅう使っているぐらい重宝してきた。<code>cdd</code> というコマンド名も打ちやすくて好き。というわけで、cdd も herdr で動くように実装した。</p>
<ul>
<li><a href="https://gist.github.com/hotchpotch/8fbca5605af16c386acb34bc74f8c1ae">https://gist.github.com/hotchpotch/8fbca5605af16c386acb34bc74f8c1ae</a></li>
</ul>
<p>herdr は以下の三つの状態を検討する必要がある。</p>
<ul>
<li>現在の workspace の tab, pane の dir</li>
<li>別の workspace の tab, pane の dir</li>
<li>agent が作業している dir</li>
</ul>
<p>これらを考慮し、zsh 決め打ちで、以下のような仕様で作った。bash その他用もエーアイに伝えれば同様な実装はすぐ作れると思う。</p>
<ul>
<li><code>cdd</code> のみを実行すると、 すべての作業ディレクトリ一覧を fzf でフィルターし移動が可能</li>
<li><code>cdd 3</code> などで、現在の workspace の tab id 3 に移動</li>
<li><code>cdd a3</code> で、agent list の3番目の agent の cwd に移動</li>
<li><code>cdd w3:p2</code> などで、workspace id 3 の pane id 2 に移動。<code>w3:p2</code> などは herdr オフィシャルでの表記と一緒。</li>
<li><code>cdd w3:t2</code> など(w3, tab 2)も。</li>
<li>適当に tab を打つと補完リストが出てきて選択も可能</li>
<li>なお <code>jq</code> と <code>fzf</code> に依存しているので、これらのコマンドが必要</li>
</ul>]]></description>
            <link>https://secon.dev/entry/2026/07/10/080000-herdr-cdd</link>
            <guid isPermaLink="false">/entry/2026/07/10/080000-herdr-cdd</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Thu, 09 Jul 2026 23:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[herdr で tmux-fingers のようなことができる plugin を作った]]></title>
            <description><![CDATA[<p>最近話題のターミナルマルチプレクサ herdr、使ってみたらあっという間に手に馴染む。センスがいいね。</p>
<p>今まで使っていた tmux では、<a href="https://github.com/Morantron/tmux-fingers">tmux-fingers</a>という、画面上に表示されているコピーしたくなるような文字列(url, SHAs, UUID などなど)を短いキーでコピーできるものが便利でよく使っていたのだけど、これがなかったので、herdr pluginとして実装してみた。</p>
<ul>
<li><a href="https://github.com/hotchpotch/herdr-tiny-fingers">https://github.com/hotchpotch/herdr-tiny-fingers</a></li>
</ul>
<pre><code>herdr plugin install hotchpotch/herdr-tiny-fingers
</code></pre>
<pre><code>[[keys.command]]
key = "prefix+f"
type = "plugin_action"
command = "hotchpotch.herdr-tiny-fingers.open"
description = "fingers mode"
</code></pre>
<p>などすれば使えるので、tmux-fingers 使いの方はどうぞ。</p>
<hr>
<p>例によってエーアイに herdr の repos を見せて実装してもらったんだけど、herdr は plugin 開発もめちゃ楽な感じであった。エーアイによって上手く開発されているプロジェクトは、エーアイが開発・デバッグしやすいように作られていているのだなぁ、と改めて感じる。</p>]]></description>
            <link>https://secon.dev/entry/2026/07/09/080000-herdr-tiny-fingers</link>
            <guid isPermaLink="false">/entry/2026/07/09/080000-herdr-tiny-fingers</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Wed, 08 Jul 2026 23:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[macOS Tahoe で動くネイティブAPI経由での翻訳 cli を作った]]></title>
            <description><![CDATA[<p>macOS ちょっと前から、そういえば Apple インテリジェンスなるものが使えるなぁ、ただなんかただ邪魔なUIが出るだけだが（自分のユースケースでは全く使わない）、程度に思っていたのだけど、macOS のネイティブAPIとして呼び出せるようなので、それを使って翻訳 cli アプリを作ってみた。</p>
<ul>
<li><a href="https://github.com/hotchpotch/mac-translate-cli">https://github.com/hotchpotch/mac-translate-cli</a></li>
</ul>
<pre><code>brew tap hotchpotch/mac-translate-cli https://github.com/hotchpotch/mac-translate-cli
brew install hotchpotch/mac-translate-cli/trn
</code></pre>
<p>で入れて</p>
<pre><code>trn --to ja "Hello world!"
#=> こんにちは、世界！
echo "Hello world!" | trn --to ja
#=> こんにちは、世界！
trn --to ja -q low "Hello world!" # デフォルト
trn --to ja -q high "Hello world!" # ニューラルネットワーク翻訳ぽい、高品質…？
</code></pre>
<p>みたいに使える。なお macOS Tahoe 26.4 以降に対応。最初、Apple Intelligence models(ニューラルネットワークを使った翻訳なのか?) API をデフォルトとして使っていたんだけど、<a href="https://github.com/hotchpotch/mac-translate-cli/blob/main/translation-quality-check.md">これが遅い割に、あんまり品質が高くない</a>。長文ならコンテキストの空気を読むのかな、と思いきや en &#x3C;-> ja に関しては試した感じ別に良くない。今までの統計確率ベース翻訳モデルの方が、10倍以上速くてLLMから見たクオリティはあまり変わらず。high は自然な文章、な気もするが、場合によってはうまく翻訳ができてないケースもありそう。なのでデフォルト品質は low (今までの翻訳API)とした。</p>
<p>音声をテキストに変換する STT は <a href="https://kanary.download/ja">kanary.app</a> 経由で使った限り、変換品質そこそこで1時間の音声を1分で文字起こしできて爆走だったため、翻訳(highのApple Intelligence model)でも爆速を期待したんだけど、翻訳の方は次のモデルに期待かな。</p>]]></description>
            <link>https://secon.dev/entry/2026/06/08/080000-mac-translate-cli-trn</link>
            <guid isPermaLink="false">/entry/2026/06/08/080000-mac-translate-cli-trn</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Sun, 07 Jun 2026 23:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Codex 認証を使った OpenAI API 互換サーバーの実装と利用]]></title>
            <description><![CDATA[<p>ローカル環境の Codex の認証情報を使い、OpenAI API の Responses API や Chat Completions API として叩ける互換サーバーを実装してみました。Codex の利用方法の一つである、ChatGPT サブスクリプションでの定額利用範囲でも使えるのが嬉しいですね。Codex で認証済みの環境なら、<code>uvx</code> コマンド1つで互換サーバを起動できます。</p>
<ul>
<li><a href="https://github.com/hotchpotch/openai-api-server-via-codex">https://github.com/hotchpotch/openai-api-server-via-codex</a></li>
</ul>
<pre><code class="hljs language-bash">uvx openai-api-server-via-codex</code></pre>
<p>これでローカルにサーバーが立ち上がります。デフォルトでは <code>http://127.0.0.1:18080</code> で待ち受け、OpenAI API を叩けるクライアントから、<code>/v1/responses</code> や <code>/v1/chat/completions</code> のエンドポイントが利用できるようになります。ライブラリ経由で API を叩きたいときに便利ですね。</p>
<p>例えば、起動した環境に適当な OpenAI API に対応している GUI クライアントを使ってみるとこんな感じです。ChatGTP Pro サブスクリプションでは Codex から今現在(2026年5月7日)、<code>gpt-5.3-codex-spark</code> が使えるので、以下のスクリーンショットでは試しに使ってみています。</p>
<img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20260506-openai-api-server-via-codex.webp" style="max-width: 640px" />
<hr>
<h2>どのように実現しているの?</h2>
<p>この方法は、OpenClaw が Codex を経由で処理するときに利用しているエージェント実行エンジンの一つ、<a href="https://github.com/badlogic/pi-mono">Pi</a>でも行われている方法で、認証は Codex の情報を利用しつつ、<code>https://chatgpt.com/backend-api/codex</code> の Codex 用 API エンドポイントを叩いています。</p>
<p>この API エンドポイントを勝手に使って良いかは不明だったのですが、Simon Willison 氏の記事 <a href="https://simonwillison.net/2026/Apr/23/gpt-5-5/">A pelican for GPT-5.5 via the semi-official Codex backdoor API</a> を読むと、どうやら利用してもよさそう(semi-official とは言い得て妙ですね)な雰囲気なので作ってみました。</p>
<h2>注意事項</h2>
<p>このプロジェクトは OpenAI 公式のAPI ではありません。あくまで、自身が利用権限を持つ Codex の認証情報を使い、ローカルなどの開発環境で OpenAI API 互換の形に変換するものです。次のような用途に利用すると、OpenAI の利用規約に抵触する場合があります。</p>
<ul>
<li>ChatGPT / Codex の利用制限を回避する目的で使う</li>
<li>自分以外の人に API として提供する</li>
<li>サブスクリプションを再販・共有する</li>
<li>公開 API や第三者向けサービスのバックエンドとして使う</li>
<li>Codex の認証情報やトークンを他人と共有する</li>
</ul>
<p>また、Codex backend の仕様は予告なく変更される可能性があります。動作していたリクエスト形式やモデル名が突然使えなくなることもあり得ます。</p>
<h2>Codex, ClaudeCode サブスクの2社のスタンスの違い</h2>
<p><a href="https://www.theverge.com/ai-artificial-intelligence/907074/anthropic-openclaw-claude-subscription-ban">ClaudeCode が、OpenClaw などでのサブスクション契約での利用禁止</a>が、2026年4月に通達されましたが、反対に Codex では、<a href="https://developers.openai.com/codex/app-server">Codex App Server</a>や<a href="https://developers.openai.com/codex/sdk">Codex SDK</a>が公開されるなど、少なくとも今今はサブスクでも幅広く使ってもらうための仕組みを提供し始めており、両者のスタンスが反対で興味深いですね。</p>
<p>Anthropic 社はClaude向けの推論リソースが現状カツカツな感じがするので厳しく制限していることに対し、OpenAI 社はリソースが余っているのか、割と大盤振る舞いな雰囲気を感じています。ただ、OpenAI 社もリソースが厳しくなる・競争が無くなると将来的には厳しくなる・サブスクが無くなる可能性も十分にあり得ると思うので、今後どうなることやら、未来は読めませんね。</p>]]></description>
            <link>https://secon.dev/entry/2026/05/07/080000-openai-api-server-via-codex</link>
            <guid isPermaLink="false">/entry/2026/05/07/080000-openai-api-server-via-codex</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Wed, 06 May 2026 23:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[SQLiteやDuckDBで日本語全文検索をVaporettoを組み込んで実現する]]></title>
            <description><![CDATA[<p>各種エージェントの台頭により、サーバレスに動作しローカルファイルとして保存でき、永続化可能な組み込みデータベースのSQLiteやDuckDBへの注目を感じる昨今ですね。これらDBでの全文検索(FTS)の日本語対応ってどんなものなのだろうかと調べると、<a href="https://www.sqlite.org/fts5.html">trigram での検索</a>はできるものの、日本語語彙に特化した検索は標準できないようでした。</p>
<p><a href="https://github.com/lindera/lindera">Lindera</a>を使うアプローチもあるようですが、今回は Rust で実装されている軽量高速なトークナイザの <a href="https://github.com/daac-tools/vaporetto">Vaporetto</a> を組み込んで動く拡張機能を作ってみました。</p>
<ul>
<li>SQLite + Vaporetto
<ul>
<li><a href="https://github.com/hotchpotch/sqlite-vaporetto">https://github.com/hotchpotch/sqlite-vaporetto</a></li>
</ul>
</li>
<li>DuckDB + Vaporetto
<ul>
<li><a href="https://github.com/hotchpotch/duckdb-vaporetto">https://github.com/hotchpotch/duckdb-vaporetto</a></li>
</ul>
</li>
</ul>
<p>Vaporetto は、<a href="https://www.anlp.jp/proceedings/annual_meeting/2022/pdf_dir/D2-5.pdf">点予測法</a>で文字境界を線形分類モデルで判定するため、辞書なしモデル(辞書ありモデルもある)で利用可能なため、サイズを小さく保つこともできるので用途によっては便利そうですね。ので、Web ブラウザー上で完結する、DuckDB + Vaporetto の組み合わせで bm25 関連度スコアでソートする全文検索を行える技術デモを作ってみました。ただ対象テキストの件数が少ないと、全文検索(件数が増えても高速に検索が可能)やbm25(単語の出現回数や文章長を加味)の嬉しさが少ないのですが…。</p>
<ul>
<li><a href="https://duckdb-vaporetto-wasm-demo.surge.sh/">https://duckdb-vaporetto-wasm-demo.surge.sh/</a></li>
</ul>
<p>また例えば、この blog(secon.dev) の記事検索を SQLite + FTS5 + vaporetto の組み合わせで試しに作ってみたところ、約2700記事の bm25 検索で、大体3ms前後の速度で検索が可能になりました。</p>
<p><img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20260427-secon-dev-sqlite-search.webp" alt="secon. dev検索例"></p>
<p>SQLite, DuckDB などでカジュアルに日本語全文検索ができるようになるので、使い所がハマれば便利そうな気がしています。</p>
<hr>
<p>追記: 形態素解析の第一人者である、Kudo さんよりコメントをいただく、ありがたい。全文検索では点予測による単語分割は、一貫性の無さが不向きと。なるほど〜。</p>
<blockquote>
<p>点予測の単語分割は全文検索には不向きです。特に辞書なしだと、文脈依存分割が避けられず検索漏れのリスクが増えます。拙書の形態素解析本に解説あります。</p>
</blockquote>
<ul>
<li><a href="https://x.com/taku910/status/2048676651517768163">https://x.com/taku910/status/2048676651517768163</a></li>
</ul>
<blockquote>
<p>文脈依存性とは、例えば「形態素解析」というフレーズの分割が、その前後文脈に左右されず、一意に決まることを指します。クエリの分割が文書中で再現されることが重要であり、Unigram言語モデルはこの条件を満たします。精度は犠牲になるものの、一貫性が保証されます。</p>
</blockquote>
<ul>
<li><a href="https://x.com/taku910/status/2048695518176665637">https://x.com/taku910/status/2048695518176665637</a></li>
</ul>]]></description>
            <link>https://secon.dev/entry/2026/04/27/080000-sqlite-duckdb-vaporetto</link>
            <guid isPermaLink="false">/entry/2026/04/27/080000-sqlite-duckdb-vaporetto</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Sun, 26 Apr 2026 23:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[RTX5090 2台構成の機械学習用PCを自作する]]></title>
            <description><![CDATA[<p>私は小さな、パラメータサイズが100M以下ぐらいのtransformerモデルの学習が好きで、しょっちゅう学習を回している。今までも RTX3090,4090,5090 の自作PCを作成し、利用してきた。</p>
<p>ただ、もうちょっと学習速度が欲しいことがあったり、また複数GPU環境での学習の知見を得たい為、今回 RTX5090 x2 構成の自作PCを作ってみた。その際、最近はコンシューマ向けGPUからNVLinkが廃止され、かつ電力消費も上がったため意外と RTX5090 2枚を使った事例がなく少々調べるのに苦労したため、まとめてみた。なおこの自作例は2025年末時点の例である。</p>
<div class="photos photos-small"><span itemscope="" itemtype="http://schema.org/Photograph"><a href="https://storage.googleapis.com/secons-site-images/photo/large/20260118_L1001763.webp"><img src="https://storage.googleapis.com/secons-site-images/photo/medium/20260118_L1001763.webp" class="photo" itemprop="image" /></a></span></div>
<h2>電源</h2>
<p>RTX5090 x2 で苦労する点といえば、まず電源である。RTX5090のTBPは最大575Wで、それが2機。CPUその他諸々の消費電力を考えると、最低1600W電源以上は欲しい。しかしながら、日本のご家庭用コンセントの100Vでは最大1500Wとなっているし、調べる限り一般的なPC用電源は100Vでは1300Wまでしか販売されていない。</p>
<p>なので、1300W電源までは多彩なラインナップがあるのだが、それ以上の電源になると極端に少なくなる。それ以上の電源は、電源の入力コネクタの口が一般的なPC電源のC13ではなく、C19という形状に変わる。そこに200V電源を供給することで1300W以上の出力を得ることができる。</p>
<p>というわけで、電源を繋げるべくNEMA規格の20A250V対応の壁コンセント(パナソニックのWF2520Bを選んだ)を工事して設置し、そこから200V20A供給(最大4000W)を可能とした。電源ケーブルはNEMA L6-20P → IEC 60320 C19のものが必要になるので、Schneider Electric AP8753J Power Cord, Locking C19 to L6‑20P を利用。なお、このコンセントにはブレーカーから単独で給電している。</p>
<p>また電源は評判が良さそうな1650WのASRockのTaichi TC-1650Tを選択した。GPUに大きな電力を供給する12V-2x6コネクタの安全性を考慮したATX3.1に対応している。またこの電源は、標準で100V用の電源ケーブル（C19-C20）に挿せる電源ケーブルがついてくる。これを使うと1300Wまでだが、このケーブル自体が市場にほぼ出回っていないので、テスト起動などで活用できて好感度高し。</p>
<p><ins>追記:電源 x2 が設置できるケースを使い、1300W電源x2を各々100V電源に繋ぐ、という方法もあるようだ</ins></p>
<h2>GPU</h2>
<p>RTX5090は膨大な発熱処理のため、空冷モデルではPCIスロット3〜4個分を占める厚さのものがほとんどだ。この厚さのGPUを2枚使う場合は、ライザーケーブルを使って物理的に離して設置しないと、ケースやマザーボードでぶつかって装着できないという問題が起きることが多いだろう。</p>
<p>このため選択肢として</p>
<ul>
<li>3スロット以下の空冷モデルを使う（隙間なくみっちり挿すことになり熱が心配だが）</li>
<li>2枚のGPUとも簡易水冷にする</li>
<li>1枚を空冷、1枚を簡易水冷にする</li>
<li>ライザーケーブルを使ってどうにかする</li>
</ul>
<p>あたりが考えられる。すでに自分は厚さ約3.5スロットのRTX5090を保持していたため、1枚を簡易水冷、もう1枚を空冷とし、以下の2つのGPUの選択となった。なお、もしRTX5090を1枚も持っていなければ、価格が少々高くなるが、GPUを2枚とも簡易水冷とし、CPUを空冷としたほうが、ケース内部の配置に余裕が出て楽になるし、GPU温度もさらに下げることが可能なので、そちらにしたと思う。</p>
<p>なお今回使ったGPUは以下。</p>
<ul>
<li>MSI GeForce RTX 5090 32G VENTUS 3X OC
<ul>
<li>約3.5スロットの厚みがある空冷（元々持っていた）</li>
</ul>
</li>
<li>MSI GeForce RTX 5090 32G SUPRIM LIQUID SOC
<ul>
<li>厚さ2スロット強の空冷 + 120x360の水冷ラジエータ</li>
</ul>
</li>
</ul>
<p>また、お金に余裕があれば、RTX 6000 Pro (RTX5090と同じBlackwellアーキテクチャでメモリが96GB)や、性能は少々落ちるが消費電力がかなり下がって300WのRTX PRO 6000 Blackwell Max-Q を選ぶという手もある。Max-Qは排熱の考慮もだいぶ減りそうなので、設置が楽そうだ。</p>
<h2>マザーボード</h2>
<p>マザーボードに求める要件は、PCIe 5.0 x8 の速度で二枚刺さること、上部に刺すGPU1(水冷)と、下部にさすGPU2(空冷)の間が2レーンあって問題ないことである。実際に海外で RTX5090x2 の組み立て済みPCとして販売されている実績を評価し、Ryzen が乗るASUS ProArt X870E-CREATOR WiFi AMD AM5 X870E ATXを選択した。</p>
<p>なおオンボードWiFi 7チップが載っているが、Linuxのカーネルドライバーが現状無さそうなので、オンボードWiFi で繋ごうと思ってる人は注意が必要かもしれない。自分の用途では無線は使わず有線LAN接続なので特に困っていない。</p>
<h2>ケース</h2>
<p>ケースは、下部に3.5レーン分の厚さがあるGPUを差した時、問題なくある程度の空間があり、簡易水冷のラジエータを2機(CPUとGPU1)を問題なく設置できるもので検討した結果、CORSAIR 7000D AIRFLOWとなった。通常のケースより一回り大きいが、ケース内空間が大きいことも冷却においてはメリットが大きい。PC内部が見れるガラスパネルは不要なのだが、いざ作ってみたら結構かっこ良くて満足。</p>
<h2>エアフロー（空気の流れ）</h2>
<p>ケース内で最大1650W程度の電力消費が発生し、その時発生する熱は相当なものなので、良い形で空気を循環させねばならない。</p>
<p>簡易水冷はCPUとGPU1、空冷はGPU2で使うので、どのように外気を取り込み・排出すると良いのかを考える必要がある。PCの冷却ファンは、表裏をひっくり返すことで、吸気・排気を簡単に変更することができる。AIに壁打ちしながらベストを考えた結果、以下のエアフローになった。ただこの辺は素人なので、もっと良い配置があるかもしれない。</p>
<ul>
<li>フロント・吸気
<ul>
<li>140mm ファンx2(ケース付属、本当は140mmをもう一つ追加したほうが良い)</li>
<li>GPU2(空冷)に当たる位置に配置</li>
</ul>
</li>
<li>サイド（ケース横）・吸気
<ul>
<li>GPU1の水冷・120mm x3</li>
</ul>
</li>
<li>トップ・排気
<ul>
<li>CPUの水冷・120mm x3</li>
</ul>
</li>
<li>リア・排気
<ul>
<li>ケース付属・140mm x1</li>
</ul>
</li>
</ul>
<p>この辺までが、組み立てる上であまり情報がなかったので苦労したところだ。続いて以下は好みで選べば良いと思うが、機械学習マシン視点からコメント入りで組み立てたパーツを紹介する。</p>
<h2>CPU</h2>
<p>16コア32スレッドのAMD Ryzen9 9950X。9950X3Dも出ていたが、ゲームをしない環境では誤差程度の性能差であり、差額も2万円ほど9950Xの方が安かったので9950Xを。データ加工などの処理は並列で行うことがほとんどなため、CPUコアはあればあるほど良いが、これ以上のコア数になるとThreadripperになってしまうため、16コアで。</p>
<h2>RAM</h2>
<p>せっかくなので上限の192GBを、と思っていたが、AI関連データセンタによるメモリ需要急増のため、2025年9月と比べると4-5倍ぐらいの値段で高止まりしていて、いくらなんでも高すぎるので DDR5-5600 32x2=64GBで。本当はECCにしたかったがこちらも高すぎるので…。自分の使い方の場合、容量は64GBだと時々swapにアクセスが発生する程度・かつswapがNVMe書き込み読み込みともに結構速いものを使っていることもあり、RAM はもっとあったら嬉しいが、64GBでも困ったことがほとんどない。</p>
<p>今回、中国のAcclamatorというブランドのメモリが、他のブランドの同容量のメモリの60%ぐらいの価格で売っていた(今はそこそこ高くなってしまったようだが)ので、DDR5 5600MHz 32GBx2 を購入。5600MHzでmemtest86やstresstest-cliで12時間ほど負荷をかけてみたが、とりわけエラーなく使えている。長期の耐久性や暑さ本番の夏(現在は冬で寒い)はわからない。GPU学習時にRAM速度の影響はほとんどないので、さらに安定すべく4800Mhzにクロックを落とし使っている。推論時にCPUオフロードする場合など、RAM速度も重要になるケースもあるのだけど、自分はやる予定がないので。</p>
<p><ins>追記: 結局メモリ不足を感じ、もう32GBx2を増やして合計128GBへ。</sin></p>
<h2>ストレージ・NVMe</h2>
<p>学習に使うデータは、雑にやるとデータがシャッフルされしてランダムアクセスが発生するため(たとえばHuggingFace Transformers も標準では学習時必ず shuffle する)。そのため容量が巨大なNVMe(SSD)を。容量はあるだけ良い。</p>
<ul>
<li>Sandisk SN850X NVMe SSD WDS800T2X0E 8TB
<ul>
<li>PCIe のCPU直結レーン。8TB でも足りない（データを削除しながら使っている）ので本当はもっと容量が欲しい。</li>
</ul>
</li>
<li>Samsung 980 Pro 2TB
<ul>
<li>これは余っていたので追加</li>
<li>チップセット共有レーン</li>
</ul>
</li>
</ul>
<h2>ストレージ・HDD</h2>
<p>生ダウンロードデータの一時的なファイル置き場として 14TB のHDDを使っている。ランダムアクセスが発生する用途には遅すぎて使えないのだけど、たとえば実運用面では、 HuggingFace datasets ライブラリはまず環境変数 <code>HF_HUB_CACHE</code> にデータをダウンロードするのだが、実際にライブラリがロードする際は、parquet ファイルから arrow 形式に変換されるため、後者の方が NVMe でアクセスできれば良いので、<code>HF_HUB_CACHE</code> のディレクトリだけHDDに指定することで、切り分けて使えている。</p>
<ul>
<li>TOSHIBA MG07ACA14TE 14TB</li>
</ul>
<h2>CPUクーラー</h2>
<p>120x3 のラジエータの簡易水冷モデルなら特にこだわりがなかったので、CORSAIR NAUTILUS 360 RS LCDを利用。CPUクーラーの表面に液晶があるモデルで、液晶にCPU温度表示できるのいいじゃん、と購入してからこの制御はUSB経由で行われるため、Linuxからだと制御が難しい(OSSでできるが、サクッと温度表示などは難しそう)ことの気づく。ので、今ならLCD無しモデルを買ったかな…。</p>
<h2>組み立て</h2>
<p>ケースや電源や空冷が重い(筋肉痛)、自分のミスでしょっちゅうファンの前後ろを間違える、ラジエータの上下を間違える、など以外は特に困らず組み立てられ、一発で起動し問題無く動いている。</p>
<h2>OS</h2>
<p>使い慣れている Ubuntu Server 24 LTS で。ssh で繋ぐだけなので、GUIは一切使っていない。</p>
<h1>RTX5090 x2 PCを作ってみての感想</h1>
<p>実際に作ってから1ヶ月ほど経ったが、冬場だからかもしれないが、GPU二枚をフルで使っても特に問題なく安定して動いている。良かったところとしては、PCIe がボトルネックにならない場合、例えばMLMでのbi-encoderモデルの学習速度が RTX5090x1 に比べ x1.8程度の速度になり、だいぶ速くなった。また、推論も水平処理ができる場合、例えば vLLM に Qwen3-8B モデルを載せて1000万件処理する、みたいな処理もほぼ2倍速で処理できるので便利だ。</p>
<p>CUDAは環境変数 CUDA_VISIBLE_DEVICES でプログラムから見えるGPUを簡単に切り替えできるので、たとえばGPU2を使いたい場合は CUDA_VISIBLE_DEVICES=1 とするだけで、プログラムには一切手を入れずに1枚のGPUとして認識させることできるのも便利。簡単にGPUを切り替えつつ使うことができている。</p>
<p>また、マルチGPU周りの知見を得るという点でも、今までずっと1GPUしか使ってこなかったため、複数GPU環境で学習・推論の方法や考え方を知ることができて勉強になり、こちらの点でも良かった。</p>
<p>ただ、PCIe 5.0 x8 の速度がボトルネックに感じることが割とあって、例えば PyTorch DDP では学習stepごとに GPU間のデータを同期する All-Reduce が発生するが、学習方法によっては非常に時間がかかる。巨大バッチでのコントラスト学習とかね。すると GPU SMの idle 時間がグッと増えて、速度向上がせいぜい1.2倍程度、場合によっては1 GPUの方が速いみたいなケースすらある。</p>
<p>B200、H200 などのデータセンター向けGPUではNVLinkを使うと、構成によっては GPU間で 数百GB/s〜TB/sの速度が出るため、高速な転送速度が確保できるが、PCIe 5.0 x8 では実効速度は 約20-30GB/s ほどのため、NVLink に比べると圧倒的に遅かったりする。高いGPUはよくできているなぁ(B200 x8 なマシン1つで8000万円程度かな…)。</p>
<p>というわけで、作ってみての満足度は高い。パーツを購入したタイミングも、メモリは高くなっていたが、2026年1月中旬はさらにストレージもメモリもGPU(RTX5090)も高くなってしまったので、まだマシだったタイミングであった。AI需要・そして円安もあり色々と高くなってしまったなぁ…。</p>]]></description>
            <link>https://secon.dev/entry/2026/01/19/100000-rtx5090x2-pc</link>
            <guid isPermaLink="false">/entry/2026/01/19/100000-rtx5090x2-pc</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Mon, 19 Jan 2026 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[振り返り2025年]]></title>
            <description><![CDATA[<h1>生活</h1>
<h2>子供</h2>
<p>子供がうまれた。人生で一番大きな変化は子供が生まれた時、とはよく聞くが、当事者になってみると全くその通りだと思う。何もかも子供主体な考え方に変わる。それにしても子供はかわいい。かわいすぎる。子供を育てられる幸せを噛み締める。リモートワークで、しょっちゅう子の顔を見れる、ありがたさもある。</p>
<p>産まれる前は妻が持病関連で体を悪くし、長期入院になったり、出産後もどうなるかわからずだいぶ不安だったが、その後の回復は良好で、問題無く日常生活を過ごせている。健康のありがたさよ。私は(四|五)十肩、以外はだいたい元気。</p>
<div class="photos photos-small"><span itemscope="" itemtype="http://schema.org/Photograph"><a href="https://storage.googleapis.com/secons-site-images/photo/large/20251231_L1001845.webp"><img src="https://storage.googleapis.com/secons-site-images/photo/medium/20251231_L1001845.webp" class="photo" itemprop="image" /></a></span></div>
<h2>家</h2>
<p>昨年建てて、昨年末から新しく住み出した家、すこぶる快適。冬はそれなりに寒いし、周りが農地なので夏は虫が多いが、それらをさっ引いても大変暮らしやすい。子も増えたことだし、建てて良かった。</p>
<h2>車</h2>
<p>テスラのモデルY ジュニパー ロングレンジをお迎え入れした。BEVのエンジンがない快適さ(静か、あっという間に速度が上がる)もさることながら、ソフトウェア・UX周辺、ほんとよくできている。ほとんどの車が、過去の車の延長線上なUXだが、新興の会社は過去を踏襲する必要がないので、新しい体験設計ができ、それをひしひしと感じる。車移動（というのは田舎において移動手段のほぼ全てある）が非常に楽に・快適になった。</p>
<p>現在のオートパイロット運転アシストでさえもかなり快適なのに、将来はFSD(完全自動運転、という名の、いい感じの自動運転支援)が日本でも使えるようになるだろうから、そちらもとても楽しみである。</p>
<h1>技術</h1>
<p>引き続き、情報検索周りを主に、技術的なあれこれやプロダクトづくりをやっていた。コーディングエージェントが台頭した年で、ほぼ全ての技術作業はエーアイさんにやってもらっているが、打ち手が増えて、できることが大きく広がったと感じる。通常のソフトウェア開発に限らず、例えば情報検索モデル開発関連も、エーアイが全部やってくれ、今までだったら手を動かす時間がかかりすぎていて大変だったことも、エーアイYOLO、ですんで最高だ。</p>
<p>エーアイが代替できない・代替しない技術をちゃんとやる、というのが正解プロダクト不定の時代の仕込みとしては大事だと思っているのだけど、その技術的なところを色々やれているので、楽しいね。バイアスがかかってるので、自分がそう思ってるだけで、簡単に代替される可能性はもちろんあるのだけど。</p>
<p>仕事周りでも今年はメインで作っていたエーアイプロダクトが世に出て、世の中的にも会社的にも一定の評価をいただけたようで、ありがたい限り。チーム開発のやりやすさの賜物でもあるので、関係者各位ありがとうございます。来年もまたチーム、エーアイさんの手を大いに借りて、技術的なことをやりつつも、新しいプロダクトを作っていきたい。</p>
<hr>
<p>2025年はいろいろあったが、子が産まれたに尽きる。毎度のことだが、さまざまなことをサポートしてくれる妻に感謝だ。というわけで、皆様2026年もよろしくお願いいたします。</p>
<div class="photos photos-small"><span itemscope="" itemtype="http://schema.org/Photograph"><a href="https://storage.googleapis.com/secons-site-images/photo/large/20251231_L1001851.webp"><img src="https://storage.googleapis.com/secons-site-images/photo/medium/20251231_L1001851.webp" class="photo" itemprop="image" /></a></span></div>
<hr>
<ul>
<li><a href="https://secon.dev/entry/2024/12/31/100000-furikaeri-2024/">振り返り2024年</a></li>
<li><a href="https://secon.dev/entry/2024/01/08/070000/">振り返り2023年</a></li>
<li><a href="https://secon.dev/entry/2022/12/31/070000/">振り返り2022年</a></li>
<li><a href="https://secon.dev/entry/2021/12/31/070000/">振り返り2021年</a></li>
<li><a href="https://secon.dev/entry/2020/12/31/080000-hurikaeri-2020/">2020年の振り返り</a></li>
</ul>]]></description>
            <link>https://secon.dev/entry/2025/12/31/100000-furikaeri-2025</link>
            <guid isPermaLink="false">/entry/2025/12/31/100000-furikaeri-2025</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Wed, 31 Dec 2025 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[LLMに渡す前に関連しない文を削除するモデル OpenProvence を公開]]></title>
            <description><![CDATA[<p>昨今、LLMが回答するための「良い知識」を作るために、検索を行い情報を集め、さらに足りない知識を補うために多方面のさまざまな検索クエリを作り検索結果から必要な情報だけを抽出したり…といったことを、再起的に行っています。AI Agent、DeepResearch、Context Engineering と 2025年の流行の技術では、このような検索を裏側で行うことがしばしばあり、筋が良い情報をいかに検索で取得できるかが鍵になることも多いでしょう。</p>
<p>しかしながら、大量に検索を行うと「検索結果」の情報も同時に増加していきます。そのため、本当に必要な情報の抽出をLLMが間違えたり、ハルシネーションが起きたり、入力情報の増加により処理が遅くなったり、LLM利用費用が増加したりと、大量の検索が難しかったりもします。</p>
<p>そこで、検索結果をLLMに渡す前に、関連しない情報は削除しちゃおう、ついでに関連度スコアもつけちゃおう、というアプローチが <a href="https://arxiv.org/abs/2501.16214">Provence</a> です。このアプローチでは、検索でヒットした文章のうち、関連しない部分を削除することが可能です。実際にデルの性能を測定したところ、長文の質問・回答データセットを用いた評価(MLDR + LLM eval)では、80-95%ほど文章を削除できました。10000文字の文章なら500-2000字程度にLLMに渡す前に減らせる、ということですね。かなりの入力データの削減が期待できますね。短い文章に分割されたような短文が多いデータセットでも、ドメインによりますが30〜70%の文の削除が行われています。</p>
<p><img src="https://storage.googleapis.com/secons-site-images/other/open_provence/carbon_xsmall_ja.png" alt="短文での削除例"></p>
<p>ただ研究開発として公開されている Provence 実装やモデルは非商用で、日本語のデータセットも公開されていなかったので、今回 <a href="https://github.com/hotchpotch/open_provence/">OpenProvence</a> というプロジェクトを作成し、で学習推論などのソースコードやモデルの重みなどを "オープン" なライセンスで公開しました。日本語データセットも作成して公開しています(データセットは大元のライセンスがあるため、オープンなライセンスではないものが多いですが)。</p>
<hr>
<h2>OpenProvence の試し方</h2>
<p>以下の URL に huggingface spaces (CPU) 環境で動くデモを用意したのでお試しください。デモのサンプルにあるWikipediaの情報検索のページ情報をもとに「ベクトル検索は？」をクエリに文削除を実行すると、約5000文字の記事が400文字に削減され、かつベクトル検索についての情報のみが残った形で出力さると思います。</p>
<ul>
<li>🤗 <a href="https://huggingface.co/spaces/hotchpotch/open_provence_demo">https://huggingface.co/spaces/hotchpotch/open_provence_demo</a></li>
</ul>
<p>またデモは以下の手順でローカルマシンでも手軽に動かせます。最近の MacBook でしたら、かなり高速に推論することも可能でしょう。</p>
<pre><code class="hljs language-bash">git <span class="hljs-built_in">clone</span> https://huggingface.co/spaces/hotchpotch/open_provence_demo
<span class="hljs-built_in">cd</span> open_provence_demo
uv sync
uv run python app.py</code></pre>
<h3>python からの利用方法</h3>
<p>python からは以下の感じで利用できます。小型のxsmallモデルならCPU環境でも推論可能です。また GPU 環境(NVIDIA + flash attention2)では、即座にで推論が完了し、文章の削除が行われるでしょう。本番検索環境に組み込んでも、問題ない速度で処理できると思っています。</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> transformers <span class="hljs-keyword">import</span> AutoModel

<span class="hljs-comment"># 利用モデルに合わせて変更</span>
model_name = <span class="hljs-string">"hotchpotch/open-provence-reranker-xsmall-v1"</span>
provence = AutoModel.from_pretrained(model_name, trust_remote_code=<span class="hljs-literal">True</span>)

question:<span class="hljs-built_in">str</span> = <span class="hljs-string">"日本の首都について"</span>
context:<span class="hljs-built_in">str</span> = <span class="hljs-string">"""
今日は学校に行き、さまざまなことを学んだり、友達と学食でたらふく食べた。
日本の首都は東京で、東京は日本の政治、経済、文化の中心地らしい。この都市は約1,400万人の人口を抱える世界有数の大都市らしい。
夜は飲み会に誘われたが、参加せずに帰宅した、今月そんなにお金が残ってないからなぁ、残念だ。
"""</span>

result = provence.process(question, context, threshold=<span class="hljs-number">0.1</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"Reranking Score: <span class="hljs-subst">{result[<span class="hljs-string">'reranking_score'</span>]:<span class="hljs-number">.4</span>f}</span>"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"Compression Rate: <span class="hljs-subst">{result[<span class="hljs-string">'compression_rate'</span>]:<span class="hljs-number">.1</span>f}</span>%"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"Pruned Context:\n<span class="hljs-subst">{result[<span class="hljs-string">'pruned_context'</span>]}</span>"</span>)

<span class="hljs-comment"># 出力例:</span>
<span class="hljs-comment"># Reranking Score: 0.7043</span>
<span class="hljs-comment"># Compression Rate: 62.5%</span>
<span class="hljs-comment"># Pruned Context:</span>
<span class="hljs-comment"># 日本の首都は東京で、東京は日本の政治、経済、文化の中心地らしい。</span>
<span class="hljs-comment"># この都市は約1,400万人の人口を抱える世界有数の大都市らしい。</span></code></pre>
<h2>コーディングエージェントの活用</h2>
<p>OpenProvence は、推論・学習モデル実装、評価実装、データセット作成実装など、私は一行もコードを書かない縛りで、全ての実装はコーディングエージェント(Claude Code, Codex) によって行いました。かなり修正の指示は必要でしたが、隙間時間に進めたプロジェクトとしてはなかなかのものが、コーディングエージェントを活用することで出来上がったのかな、と思っています。出来上がった成果物のコードを見ると、もっとシンプルなコードにすることはできそうですが、現状のLLMが理解しやすい形・LLMが修正しやすい形だと、これぐらいの冗長なコードが丁度良いのかもしれません。</p>
<p>適切な指示と開発指針、AIが自身で開発、改善し続けられる環境等を作り続けることで、プロダクションレベルの品質のソフトウェアをAIと協調しながら作成する、<a href="https://simonwillison.net/2025/Oct/7/vibe-engineering/">Vibe engineering</a> という言葉も生まれました。</p>
<p>開発指針やユニットテスト、CI、コードレビュー環境といった通常のコーディングエージェントを用いたソフトウェア開発に加え、モデル学習時の学習を短時間で行える最小ベースラインと評価データの用意(これが意図せず変化するとバグ)、データセットについての詳細な説明等々を用意することで、ある程度の規模の機械学習モデル・プロジェクトも開発できることを実感しています。</p>
<h2>おわりに</h2>
<p>OpenProvence のような、質問と関連しない文章を削除するアプローチは、とりわけ巨大な文章を処理するようなプロダクトととても相性が良いでしょう。</p>
<p>2024年はRAGが話題でしたが、2025年のAI Agent、DeepResearch、Context Engineering のような流行を先取りし、技術的に重要なポイントを研究開発した Naver Labs Europe の Provence チームの先見の明(Provenceは2025年1月公開!)に驚きと感謝を。</p>
<p>昨今、LLMを活用するプロダクトでは、裏側で情報検索を活用することで価値を高められ、情報検索技術は引き続きとても面白いです。このプロジェクトが少しでもプロダクトや研究で活用していただけたら幸いです。</p>
<ul>
<li><a href="https://github.com/hotchpotch/open_provence/">https://github.com/hotchpotch/open_provence/</a></li>
<li><a href="https://huggingface.co/collections/hotchpotch/openprovence">https://huggingface.co/collections/hotchpotch/openprovence</a></li>
</ul>]]></description>
            <link>https://secon.dev/entry/2025/10/31/100000-open-provence-release</link>
            <guid isPermaLink="false">/entry/2025/10/31/100000-open-provence-release</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Fri, 31 Oct 2025 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Embedding Gemma 300M 文章ベクトルの日本語性能を JMTEB で測る]]></title>
            <description><![CDATA[<p>Google が先日 EmbeddingGemma <a href="https://huggingface.co/google/embeddinggemma-300m">google/embeddinggemma-300m</a> という文章ベクトルモデルをリリースしましたね。MTEB(Multilingual v2)においては、かなりの成績、というわけで日本語性能もちゃんと測るべく、JMTEB(v1)でベンチマークを取ってみました。</p>
<p>結論から言うと日本語においては、EmbeddingGemma はとても性能が低かったです。</p>
<h2>JMTEB v1 ベンチマーク評価</h2>
<table>
<thead>
<tr>
<th>モデル</th>
<th>params</th>
<th>avg</th>
<th>Retrieval</th>
<th>STS</th>
<th>Classification</th>
<th>Reranking</th>
<th>Clustering</th>
<th>PairClass</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://huggingface.co/google/embeddinggemma-300m">google/embeddinggemma-300m</a></td>
<td>308M</td>
<td><strong>58.10</strong></td>
<td>42.18</td>
<td>73.36</td>
<td>63.23</td>
<td>91.55</td>
<td>45.87</td>
<td>62.42</td>
</tr>
<tr>
<td><a href="https://huggingface.co/intfloat/multilingual-e5-small">intfloat/multilingual-e5-small</a></td>
<td>118M</td>
<td><strong>69.52</strong></td>
<td>67.27</td>
<td>80.07</td>
<td>67.62</td>
<td>93.03</td>
<td>46.91</td>
<td>62.19</td>
</tr>
<tr>
<td><a href="https://huggingface.co/intfloat/multilingual-e5-large">intfloat/multilingual-e5-large</a></td>
<td>560M</td>
<td><strong>71.65</strong></td>
<td>70.98</td>
<td>79.70</td>
<td>72.89</td>
<td>92.96</td>
<td>51.24</td>
<td>62.15</td>
</tr>
<tr>
<td><a href="https://huggingface.co/cl-nagoya/ruri-v3-30m">cl-nagoya/ruri-v3-30m</a></td>
<td>37M</td>
<td><strong>74.51</strong></td>
<td>78.08</td>
<td>82.48</td>
<td>74.80</td>
<td>93.00</td>
<td>52.12</td>
<td>62.40</td>
</tr>
<tr>
<td><a href="https://huggingface.co/cl-nagoya/ruri-v3-310m">cl-nagoya/ruri-v3-310m</a></td>
<td>315M</td>
<td><strong>77.24</strong></td>
<td>81.89</td>
<td>81.22</td>
<td>78.66</td>
<td>93.43</td>
<td>55.69</td>
<td>62.60</td>
</tr>
</tbody>
</table>
<p><em>注：MTEB v1の16の日本語タスクのマイクロ平均（単純平均）で算出</em></p>
<p>なお、JMTEBの設定は<a href="https://github.com/hotchpotch/JMTEB/compare/e152a7a351d0550466a...f23d9a737f70c57f8#diff-7c452491cda037603553f833680a105d21981861f8bd037a91779d824f641e45">こちら</a>で、各種 prefix などはつけているはずです。また結果の json である <a href="https://gist.github.com/hotchpotch/62eb81698b66e3fea5a15816521f695d">summary.json はこちら(gist)</a>。再現方法も gist に記載してます。私の測定結果がおかしかったら教えてください。</p>
<p><ins>2025/10/03追記: <a href="https://huggingface.co/google/embeddinggemma-300m/discussions/3#68baf8490495f751dd1a654b">transformersのバグの影響</a>で、最新版だと ruri-base 並に性能が向上するとのこと。LM8(@ShengzheLi) さん、情報ありがとうございます!</ins></p>
<h2>JQaRA / JaCWIR</h2>
<p>JMTEB v1 でのスコアが低すぎるので、別途 <a href="https://github.com/hotchpotch/JQaRA">JQaRA</a> / <a href="https://github.com/hotchpotch/JaCWIR">JaCWIR</a> でも評価してみましたが、やはりかなり低い結果となりました。</p>
<table>
<thead>
<tr>
<th>モデル</th>
<th>JQaRA (nDCG@10)</th>
<th>JQaRA (MRR@10)</th>
<th>JaCWIR (MAP@10)</th>
<th>JaCWIR (HIT_RATE@10)</th>
</tr>
</thead>
<tbody>
<tr>
<td>google/embeddinggemma-300m</td>
<td>0.261</td>
<td>0.457</td>
<td>0.730</td>
<td>0.904</td>
</tr>
<tr>
<td>intfloat/multilingual-e5-small</td>
<td>0.492</td>
<td>0.729</td>
<td>0.869</td>
<td>0.970</td>
</tr>
<tr>
<td>intfloat/multilingual-e5-large</td>
<td>0.554</td>
<td>0.799</td>
<td>0.876</td>
<td>0.973</td>
</tr>
</tbody>
</table>
<h1>MTEB 高性能 ≠ 日本語高性能</h1>
<p>先日評価した Qwen3 Embedding (<a href="https://secon.dev/entry/2025/06/11/100000-qwen3-embedding-jmteb/">Qwen3 Embedding 文章ベクトルの日本語性能を JMTEB で測る</a>)もですが、最近の MTEB 高評価マルチリンガル embedding モデルは、日本語性能が低いことが多いですね。なんでだろうと、MTEB Leaderboard の <code>Language-specific</code> の <code>Japanese</code> を見ると、Qwen3 Embeddings, Embedding Gemma 共に Pair Classification しか日本語結果では載っていないので、ほぼ参考になりません。マルチリンガル性能とは…。</p>
<p>またこの Qwen3 Embedding, Embedding Gemma の2モデルは decoder モデルベースのアーキテクチャです。embeddinggemma-300m の中身を見ると、<a href="https://huggingface.co/google/embeddinggemma-300m/tree/main">埋め込み用の HEAD (pooling + 2層dense) を mean pooling して使っています</a>ね。</p>
<p>decoder + 小さなパラメータサイズの場合、少なくとも日本語における性能は他の encoder マルチリンガルモデルよりだいぶ低い結果でした。この辺は、そもそも日本語を embeddings タスクでほとんど学習させてないからなのか、それとも大元の小さな decoder の時点で日本語汎化性能が低いのか…。</p>]]></description>
            <link>https://secon.dev/entry/2025/09/18/100000-embedding-gemma300m</link>
            <guid isPermaLink="false">/entry/2025/09/18/100000-embedding-gemma300m</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Thu, 18 Sep 2025 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[JFWIR - Japanese FineWeb Information Retrieval: 日本語FineWebを用いた巨大な情報検索用データセットを公開]]></title>
            <description><![CDATA[<p>日本語の情報検索（Information Retrieval, IR）分野において、これまで多くのデータセットがWikipediaを中心に構築されてきました。しかし、実際のWebにはWikipediaのような「綺麗に整形された文章」だけでなく、ブログ、ニュース、フォーラムなど、多様な文体やノイズを含む文章が存在します。</p>
<p>今回公開した <strong>JFWIR (Japanese FineWeb Information Retrieval)</strong> は、この課題に取り組むために作成した約6,400万件の大規模な日本語情報検索に活用できるデータセットです。このデータセットは、高品質な教育的コンテンツを含むWebクロールデータ「<a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese">fineweb-2-edu-japanese</a>」を基に構築されています。</p>
<ul>
<li><a href="https://huggingface.co/datasets/hotchpotch/JFWIR">https://huggingface.co/datasets/hotchpotch/JFWIR</a></li>
</ul>
<h2>JFWIRの特徴</h2>
<h3>1. 大規模かつ多様性の高いデータセット</h3>
<p>JFWIRは以下の特徴を持つデータセットです：</p>
<ul>
<li><strong>6,400万件以上の文書-クエリペア</strong>: 各文書に対して7種類の異なるタイプのクエリ（keywords, synonym_keywords, query, alt_query, title, faq, summary）を生成</li>
<li><strong>実際のWeb文章</strong>: Wikipedia以外の教育的価値の高いWebコンテンツを収録</li>
<li><strong>ハードネガティブ付き</strong>: 効果的な学習のための類似しているネガティブ文書</li>
</ul>
<h3>2. ベンチマーク評価結果</h3>
<p>JFWIRを使用して学習させたリランキングモデルの性能を、主要な日本語情報検索ベンチマークで評価しました。以下の4つのベンチマークで比較を行いました：</p>
<table>
<thead>
<tr>
<th>ベンチマーク</th>
<th>JFWIRなし</th>
<th>JFWIR 1000万件利用</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://huggingface.co/datasets/SkelterLabsInc/JQaRA">JQaRA</a></td>
<td>0.7621</td>
<td>0.7633</td>
</tr>
<tr>
<td><a href="https://huggingface.co/datasets/miracl/miracl">MIRACL(ja)</a></td>
<td>0.8332</td>
<td>0.8385</td>
</tr>
<tr>
<td><a href="https://techblog.yahoo.co.jp/entry/2022122030379907/">jsquad</a></td>
<td>0.9801</td>
<td>0.9821</td>
</tr>
<tr>
<td><a href="https://huggingface.co/datasets/hotchpotch/JaCWIR">JaCWIR</a></td>
<td>0.9339</td>
<td><strong>0.9586</strong></td>
</tr>
</tbody>
</table>
<p>特に、Web文章を対象とするJaCWIRでは0.9339から0.9586への改善が見られました。</p>
<h2>使い方</h2>
<p>JFWIRはHugging Face Datasetsから簡単に利用できます。以下に基本的な使用例を示します：</p>
<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> datasets <span class="hljs-keyword">import</span> load_dataset

<span class="hljs-comment"># メインデータセットの読み込み</span>
train_ds = load_dataset(<span class="hljs-string">"hotchpotch/JFWIR"</span>, split=<span class="hljs-string">"train"</span>, name=<span class="hljs-string">"small_tokens_cleaned"</span>)

<span class="hljs-comment"># サンプルデータの確認</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">3</span>):
    sample = train_ds[i]
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Query: <span class="hljs-subst">{sample[<span class="hljs-string">'query'</span>]}</span>"</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Document: <span class="hljs-subst">{sample[<span class="hljs-string">'text'</span>][:<span class="hljs-number">100</span>]}</span>..."</span>)

<span class="hljs-comment"># ハードネガティブ付きデータセットの読み込み</span>
hard_negatives_ds = load_dataset(<span class="hljs-string">"hotchpotch/JFWIR"</span>, split=<span class="hljs-string">"train"</span>, name=<span class="hljs-string">"hard_negatives"</span>)

<span class="hljs-comment"># ハードネガティブの使用例</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">3</span>):
    hn_sample = hard_negatives_ds[i]
    pos_id = hn_sample[<span class="hljs-string">'pos_id'</span>]
    pos_doc = train_ds[pos_id]
    
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Query: <span class="hljs-subst">{pos_doc[<span class="hljs-string">'query'</span>]}</span>"</span>)
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Positive (score: <span class="hljs-subst">{hn_sample[<span class="hljs-string">'pos_score'</span>]:<span class="hljs-number">.3</span>f}</span>): <span class="hljs-subst">{pos_doc[<span class="hljs-string">'text'</span>][:<span class="hljs-number">100</span>]}</span>..."</span>)
    
    <span class="hljs-comment"># ネガティブ文書をスコア順にソート</span>
    neg_pairs = <span class="hljs-built_in">list</span>(<span class="hljs-built_in">zip</span>(hn_sample[<span class="hljs-string">'neg_ids'</span>], hn_sample[<span class="hljs-string">'neg_scores'</span>]))
    neg_pairs.sort(key=<span class="hljs-keyword">lambda</span> x: x[<span class="hljs-number">1</span>])
    
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Negatives (lowest scores):"</span>)
    <span class="hljs-keyword">for</span> neg_id, score <span class="hljs-keyword">in</span> neg_pairs[:<span class="hljs-number">2</span>]:
        <span class="hljs-built_in">print</span>(<span class="hljs-string">f"  Score <span class="hljs-subst">{score:<span class="hljs-number">.3</span>f}</span>: <span class="hljs-subst">{train_ds[neg_id][<span class="hljs-string">'text'</span>][:<span class="hljs-number">80</span>]}</span>..."</span>)</code></pre>
<h2>データセットの作成プロセス</h2>
<h3>1. 高品質な日本語Web文章の収集</h3>
<p>まず、大規模なWebクロールデータセット「<a href="https://huggingface.co/datasets/HuggingFaceFW/fineweb-2">FineWeb-2</a>」から、教育的価値の高い日本語コンテンツを抽出して「<a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese">fineweb-2-edu-japanese</a>」を作成しました。さらに、Web文章特有のノイズを除去し、適切な文章長に調整した「small_tokens_cleaned」サブセットを作成しました。</p>
<h3>2. 多様なクエリの生成</h3>
<p>6,400万件のデータセットに対してクエリを生成するため、軽量なクエリ生成モデル「<a href="https://secon.dev/entry/2025/05/07/100000-query-crafter-japanese/">query-crafter-japanese</a>」を使用しました。多様性を確保するため、以下の3つのモデルを組み合わせて使用しています：</p>
<ul>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-Qwen3-1.7B">hotchpotch/query-crafter-japanese-Qwen3-1.7B</a></li>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-Qwen3-4B">hotchpotch/query-crafter-japanese-Qwen3-4B</a></li>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-sarashina2.2-3b-instruct-v0.1">hotchpotch/query-crafter-japanese-sarashina2.2-3b-instruct-v0.1</a></li>
</ul>
<p>各文書に対して7種類のクエリタイプ（keywords, synonym_keywords, query, alt_query, title, faq, summary）を生成することで、多角的な検索ニーズに対応できるデータセットを構築しました。</p>
<h3>3. ハードネガティブの作成</h3>
<p>情報検索モデルの性能を向上させるため、ハードネガティブ（クエリに類似しているが正解ではない文書）を含むデータセットも作成しました：</p>
<ol>
<li><strong>埋め込みモデルによる類似文書検索</strong>: <a href="https://huggingface.co/cl-nagoya/ruri-v3-30m">ruri-v3-30m</a>モデルを使用して6,400万件の文書をベクトル化し、各文書に対して類似度の高い文書を検索</li>
<li><strong>適切なネガティブの選定</strong>: 類似度top10-50とtop50-200からランダムサンプリング</li>
<li><strong>リランカースコアの付与</strong>: <a href="https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2">japanese-reranker-xsmall-v2</a>を使用してスコアリングしています。たとえば正例として不適切なもの（スコア&#x3C;0.6など）や負例として不適切なもの（スコア>0.4など）を除外して利用することで、より適切な正例・負例を選択できます。</li>
</ol>
<h2>今後の展望</h2>
<p>JFWIRは、日本語情報検索分野の発展に貢献することを目的として公開されました。しかしながら、<a href="https://secon.dev/entry/2025/05/07/100000-query-crafter-japanese/">query-crafter-japanese</a> は文章からのある程度単純なクエリ生成にとどまり、もっと多様な価値のある質問文を作成することで、より様々な情報検索精度の向上が可能になると思っております。</p>
<h2>まとめ</h2>
<p>JFWIRは、Wikipediaに偏重していた従来の日本語IRデータセットとは異なるアプローチとして、実際のWeb文章を対象とした情報検索データセットです。約6,400万件のデータ、7種類のクエリタイプ、対照学習用のハードネガティブなど、情報検索システムの開発に活用いただける要素を含んでおります。</p>
<p>データセットはHugging Faceで公開されており、ODC-byライセンスの下で自由に利用できます。日本語情報検索分野の発展に、このデータセットが少しでも貢献できれば幸いです。</p>
<hr>
<h2>関連リンク</h2>
<h3>データセット</h3>
<ul>
<li><strong>JFWIRデータセット</strong>: <code>hotchpotch/JFWIR</code> (Hugging Face Datasets)</li>
<li><strong>fineweb-2-edu-japanese</strong>: <a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese">https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese</a></li>
<li><strong>FineWeb-2</strong>: <a href="https://huggingface.co/datasets/HuggingFaceFW/fineweb-2">https://huggingface.co/datasets/HuggingFaceFW/fineweb-2</a></li>
</ul>
<h3>モデル</h3>
<ul>
<li><strong>ruri-v3-30m（埋め込みモデル）</strong>: <a href="https://huggingface.co/cl-nagoya/ruri-v3-30m">https://huggingface.co/cl-nagoya/ruri-v3-30m</a></li>
<li><strong>ruri-v3-pt-70m（事前学習モデル）</strong>: <a href="https://huggingface.co/cl-nagoya/ruri-v3-pt-70m">https://huggingface.co/cl-nagoya/ruri-v3-pt-70m</a></li>
<li><strong>japanese-reranker-xsmall-v2</strong>: <a href="https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2">https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2</a></li>
<li><strong>query-crafter-japanese（クエリ生成モデル）</strong>:
<ul>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-Qwen3-1.7B">hotchpotch/query-crafter-japanese-Qwen3-1.7B</a></li>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-Qwen3-4B">hotchpotch/query-crafter-japanese-Qwen3-4B</a></li>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-sarashina2.2-3b-instruct-v0.1">hotchpotch/query-crafter-japanese-sarashina2.2-3b-instruct-v0.1</a></li>
</ul>
</li>
</ul>
<h3>記事・論文</h3>
<ul>
<li><a href="https://secon.dev/entry/2025/05/07/100000-query-crafter-japanese/">query-crafter-japanese記事</a></li>
<li><a href="https://techblog.yahoo.co.jp/entry/2022122030379907/">日本語言語理解ベンチマークJGLUEの構築 〜 自然言語処理モデルの評価用データセットを公開しました</a></li>
<li><a href="https://arxiv.org/abs/2210.09984">MIRACL: A Multilingual Retrieval Dataset Covering 18 Diverse Languages</a></li>
<li><a href="https://arxiv.org/abs/2406.17557">FineWeb-Edu: The Finest Collection of Educational Content the Web Has to Offer</a></li>
<li><a href="https://arxiv.org/abs/2409.07737">Ruri: Japanese General Text Embeddings</a></li>
</ul>
<p><strong>作者</strong>: <a href="https://secon.dev/">Yuichi Tateno (@hotchpotch)</a></p>
<h2>ライセンス</h2>
<p>本データセットは、元の FineWeb2 と同様に <strong>Open Data Commons Attribution License (ODC-By) v1.0</strong> の下で公開します。また、使用にあたっては <a href="https://commoncrawl.org/terms-of-use">CommonCrawlの利用規約</a> も適用されます</p>
<h2>Citation Information</h2>
<p>JFWIRデータセットを研究や開発に使用される場合は、以下の引用情報をご利用ください</p>
<pre><code>@misc{tateno2025jfwir,
  author = {Yuichi Tateno},
  title = {JFWIR: Japanese FineWeb Information Retrieval Dataset},
  year = {2025},
  url = {https://huggingface.co/datasets/hotchpotch/JFWIR},
  note = {A large-scale Japanese information retrieval dataset with 60+ million document-query pairs}
}
</code></pre>]]></description>
            <link>https://secon.dev/entry/2025/06/19/100000-jfwir-japanese-fineweb-ir</link>
            <guid isPermaLink="false">/entry/2025/06/19/100000-jfwir-japanese-fineweb-ir</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Thu, 19 Jun 2025 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Qwen3 Embedding 文章ベクトルの日本語性能を JMTEB で測る]]></title>
            <description><![CDATA[<p>オープンウェイトな高性能マルチリンガル embedding, reranker モデル、<a href="https://qwenlm.github.io/blog/qwen3-embedding/">Qwen3 Embedding</a> シリーズがリリースされましたね。モデルサイズも 8B, 4B, 0.6B とあり文章ベクトルの作成・リランキングで高性能で、<a href="https://huggingface.co/spaces/mteb/leaderboard">Multilingual MTEB leaderboard</a>ではトップの性能となっています。</p>
<p>ただ、マルチリンガルモデルはあまり日本語が重視されない傾向にあるので、<a href="https://github.com/sbintuitions/JMTEB">JMTEB: Japanese Massive Text Embedding Benchmark</a>で Qwen3-Embedding-0.6B の性能を計測してみました。なお、jsick, jsts がエラーになったため、STSタスクは除いてあります。</p>
<h2>JMTEB 計測結果</h2>
<table>
<thead>
<tr>
<th align="left">Model</th>
<th align="center">Retrieval</th>
<th align="center">STS</th>
<th align="center">Classification</th>
<th align="center">Reranking</th>
<th align="center">Clustering</th>
<th align="left">PairClassification</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><strong>Qwen3-Embedding-0.6B</strong></td>
<td align="center">72.81</td>
<td align="center">--</td>
<td align="center">66.09</td>
<td align="center">93.10</td>
<td align="center">48.84</td>
<td align="left">62.42</td>
</tr>
<tr>
<td align="left">ruri-v3-310m</td>
<td align="center"><strong>81.89</strong></td>
<td align="center">81.22</td>
<td align="center"><strong>78.66</strong></td>
<td align="center">93.43</td>
<td align="center"><strong>55.69</strong></td>
<td align="left"><strong>62.60</strong></td>
</tr>
<tr>
<td align="left">ruri-v3-130m</td>
<td align="center"><strong>81.89</strong></td>
<td align="center">79.25</td>
<td align="center">77.16</td>
<td align="center">93.31</td>
<td align="center">55.36</td>
<td align="left">62.26</td>
</tr>
<tr>
<td align="left">ruri-v3-70m</td>
<td align="center">79.96</td>
<td align="center">79.82</td>
<td align="center">76.97</td>
<td align="center">93.27</td>
<td align="center">52.70</td>
<td align="left">61.75</td>
</tr>
<tr>
<td align="left">PLaMo-Embedding-1B</td>
<td align="center">79.94</td>
<td align="center"><strong>83.14</strong></td>
<td align="center">77.20</td>
<td align="center">93.57</td>
<td align="center">53.47</td>
<td align="left">62.37</td>
</tr>
<tr>
<td align="left">ruri-v3-30m</td>
<td align="center">78.08</td>
<td align="center">82.48</td>
<td align="center">74.80</td>
<td align="center">93.00</td>
<td align="center">52.12</td>
<td align="left">62.40</td>
</tr>
<tr>
<td align="left">sbintuitions/sarashina-embedding-v1-1b</td>
<td align="center">77.61</td>
<td align="center">82.71</td>
<td align="center">78.37</td>
<td align="center"><strong>93.74</strong></td>
<td align="center">53.86</td>
<td align="left">62.00</td>
</tr>
<tr>
<td align="left">jinaai/jina-embeddings-v3</td>
<td align="center">75.22</td>
<td align="center">80.05</td>
<td align="center">76.39</td>
<td align="center">92.71</td>
<td align="center">51.46</td>
<td align="left">62.37</td>
</tr>
<tr>
<td align="left">OpenAI/text-embedding-3-large</td>
<td align="center">74.48</td>
<td align="center">82.52</td>
<td align="center">77.58</td>
<td align="center">93.58</td>
<td align="center">53.32</td>
<td align="left">62.35</td>
</tr>
<tr>
<td align="left">pkshatech/GLuCoSE-base-ja-v2</td>
<td align="center">73.36</td>
<td align="center">82.96</td>
<td align="center">74.21</td>
<td align="center">93.01</td>
<td align="center">48.65</td>
<td align="left">62.37</td>
</tr>
<tr>
<td align="left">pkshatech/RoSEtta-base-ja</td>
<td align="center">73.21</td>
<td align="center">81.39</td>
<td align="center">72.41</td>
<td align="center">92.69</td>
<td align="center">53.23</td>
<td align="left">61.74</td>
</tr>
<tr>
<td align="left">intfloat/multilingual-e5-large</td>
<td align="center">70.98</td>
<td align="center">79.70</td>
<td align="center">72.89</td>
<td align="center">92.96</td>
<td align="center">51.24</td>
<td align="left">62.15</td>
</tr>
<tr>
<td align="left">OpenAI/text-embedding-3-small</td>
<td align="center">66.39</td>
<td align="center">79.46</td>
<td align="center">73.06</td>
<td align="center">92.92</td>
<td align="center">51.06</td>
<td align="left">62.27</td>
</tr>
</tbody>
</table>
<p>結果はこちらです。日本語のタスクがあまり学習されていないからなのか、日本語の結果は振るわない結果でした。ruri-v3 シリーズはモデルサイズも小さく、かつ日本語では圧倒的に高性能ですね。</p>
<p>なお Retrieval, Reranking タスクでは、Query の prefix に <code>Instruct: Given a web search query, retrieve relevant passages that answer the query\nQuery:</code> を追加しています。</p>
<p>また計測に使った、JMTEB用の設定(jsonnet)や結果の summary.json、実行コマンドは以下においてあります。Qwen3-Embedding-0.6B の性能が低すぎる気もするので、何か間違っていたら教えてください。</p>
<ul>
<li><a href="https://gist.github.com/hotchpotch/f6be186010e70d6eb6e46447cea258f9">https://gist.github.com/hotchpotch/f6be186010e70d6eb6e46447cea258f9</a></li>
</ul>
<h2>おまけ: Qwen3 Embedding 論文を読む</h2>
<p><a href="https://arxiv.org/abs/2506.05176">Qwen3 Embedding: Advancing Text Embedding and Reranking Through Foundation Models</a>が公開されたのでざっくり読んでみました。とりわけ合成データセット作成あたりが個人的に面白かったです。</p>
<p>以下は私の興味範囲のメモ書きです。</p>
<ul>
<li>LLM2Vec のような decoder → encoder ではなく、casual attention をそのまま利用</li>
<li>Embedding モデルは最終層の<code>[EOS]</code> トークンの hidden state から最終埋め込みを取得
<ul>
<li>Query は Instruction + Query で作成。Doc はそのまま。</li>
<li>InfoNCE を改良したスコア(単純な対照学習ではなく、ハードネガティブを複数含めたり、類似度のポジネガを調整した偽陰性の調整など)</li>
</ul>
</li>
<li>Reranking は chat template をそのまま使って、"yes", "no" トークンの確率で、関連性スコアとして計算
<ul>
<li>decoder model のラベル分類の解き方(該当ラベルトークンの確率を見る)のアプローチをママ適用</li>
<li>SFT で学習できる</li>
</ul>
</li>
<li>1st stage で Qwen3-32B で作った合成データセットをもとに学習
<ul>
<li>情報検索, 対訳マイニング(Bitext Mining), 意味的類似性, 分類 の4つのタイプを作成</li>
<li>情報検索の合成データセットの場合、詳細な設定を作り、それをもとにQwen3の事前訓練コーパスの文章からクエリを生成</li>
</ul>
</li>
<li>2nd stage で 700万の既存データセット(MS Marco, MIRACLなどなど)と、1st stage のコサイン類似度でフィルタリングした1200万件のデータをもとに学習</li>
<li>最後に多様性考慮のモデルマージ
<ul>
<li>詳細は記されてないので推察だが、2nd stage の複数のチェックポイントは、タスク特化学習させたもの、特定言語にフォーカスして学習させたもの、などが考えられそう</li>
<li>モデルマージは適当にマージして、ベンチマーク走らせると結果が向上することを、少ないコンピューティングリソースで観測できるので、たくさんチェックポイントがあるならいろいろ試した方が良さそう。</li>
</ul>
</li>
</ul>]]></description>
            <link>https://secon.dev/entry/2025/06/11/100000-qwen3-embedding-jmteb</link>
            <guid isPermaLink="false">/entry/2025/06/11/100000-qwen3-embedding-jmteb</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Wed, 11 Jun 2025 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[とても小さく速く実用的な日本語リランカー japanese-reranker-tiny,xsmall,small,base の v2 を公開]]></title>
            <description><![CDATA[<p>とても小さな日本語のリランカーモデル <a href="https://huggingface.co/hotchpotch/japanese-reranker-tiny-v2">japanese-reranker-tiny-v2</a> と <a href="https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2">japanese-reranker-xsmall-v2</a> を公開しました。情報検索システムにおいて、リランカーは検索結果の精度を高める役割を担いますが、モデルサイズと計算コストが実用における課題でした。</p>
<p>🆕 2025-07-10 まぁまぁ小さなリランカー <a href="https://huggingface.co/hotchpotch/japanese-reranker-small-v2">japanese-reranker-small-v2</a> と <a href="https://huggingface.co/hotchpotch/japanese-reranker-base-v2">japanese-reranker-base-v2</a> も追加しました。</p>
<p>本モデルは最小限のレイヤー数とパラメータ数で作成されており、CPUやAppleシリコン環境でも実用的な速度で動作します。これにより、高価なGPUリソースなしでもRAGシステムの精度向上が可能になり、エッジデバイスでの展開や低レイテンシが要求される本番環境で活用できるでしょう。性能評価では、大型モデルと比較しても競争力のあるスコアを出しています。</p>
<ul>
<li><a href="https://huggingface.co/hotchpotch/japanese-reranker-tiny-v2">https://huggingface.co/hotchpotch/japanese-reranker-tiny-v2</a></li>
<li><a href="https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2">https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2</a></li>
<li><a href="https://huggingface.co/hotchpotch/japanese-reranker-small-v2">https://huggingface.co/hotchpotch/japanese-reranker-small-v2</a></li>
<li><a href="https://huggingface.co/hotchpotch/japanese-reranker-base-v2">https://huggingface.co/hotchpotch/japanese-reranker-base-v2</a></li>
</ul>
<p><img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20250508-japanese-reranker-v2-scores.png" alt="Reranker Benchmark"></p>
<h1>リランカーとは何か、そして小さなリランカーの重要性</h1>
<p>リランカーとは、検索システムにおいて、質問（クエリ）と文書の関連性を評価し、最も関連性の高い順に並べ替える（ランキング）するモデルです。従来の文ベクトル（埋め込み）検索だけでは捉えきれない複雑な関連性を評価できる点が強みです。特にCrossEncoderと呼ばれるアーキテクチャを用いることで、質問と文書を一つのペアとして入力し、より細かなニュアンスや文脈的理解を実現します。</p>
<ul>
<li><a href="https://secon.dev/entry/2024/04/02/070000-japanese-reranker-release/">日本語最高性能のRerankerをリリース / そもそも Reranker とは?</a></li>
</ul>
<p>小さなリランカーモデルが重要な理由はいくつかあります。まず、リランカーは質問と候補文書のすべての組み合わせを評価する必要があるため、計算量が非常に多くなります。例えば100件の候補文書をリランクする場合、100回のモデル推論が必要です。そのため、モデルが小さいほど処理速度が向上し、レイテンシが低減します。</p>
<p>また、小型モデルは限られたリソース環境での実行も可能です。CPUのみの環境やエッジデバイス、モバイルデバイスでも現実的な速度で動作でき、RAG（検索拡張生成）システムの実用性を大きく高めます。同時に、クラウド等のサーバ環境ではGPUメモリ使用量の削減により、GPUリソースの共有が可能となりコスト効率が大幅に向上します。</p>
<ul>
<li><a href="https://speakerdeck.com/hotchpotch/ask-nikkei-ragjian-suo-ji-shu-noshen-ceng">Ask! NIKKEI RAG検索技術の深層</a></li>
</ul>
<p>このように、小型リランカーは速度、コスト、リソース効率の面で大きなメリットをもたらし、実用的なRAGシステム構築において大切な役割を果たすでしょう。</p>
<h2>ベンチマーク性能</h2>
<p>ベンチマーク結果は以下です。小さなな tiny, xsmall v2 の性能はモデルサイズを考えるとかなり高く、大きいモデルとしては <a href="https://huggingface.co/cl-nagoya/ruri-v3-reranker-310m">ruri-v3-reranker-310m</a> が圧倒的ですね。これらの高性能なモデルは、ベースがどれも高性能な ModernBert になったことも、性能向上に寄与しているでしょう。</p>
<p>なお、日本語モデルはどれもJQaRA(クイズ形式)の傾向を学んでおり、bge-reranker-v2-m3 は不利になります。これはリランカーが適切にドメイン課題を学習すれば、だいぶスコアが上がることの例でもあります。</p>
<table>
<thead>
<tr>
<th>モデル名</th>
<th>avg</th>
<th>JQaRA</th>
<th>JaCWIR</th>
<th>MIRACL</th>
<th>JSQuAD</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-tiny-v2">japanese-reranker-tiny-v2</a></td>
<td>0.8138</td>
<td>0.6455</td>
<td>0.9287</td>
<td>0.7201</td>
<td>0.9608</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2">japanese-reranker-xsmall-v2</a></td>
<td>0.8699</td>
<td>0.7403</td>
<td>0.9409</td>
<td>0.8206</td>
<td>0.9776</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-small-v2">japanese-reranker-small-v2</a></td>
<td>0.8856</td>
<td>0.7633</td>
<td>0.9586</td>
<td>0.8385</td>
<td>0.9821</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-base-v2">japanese-reranker-base-v2</a></td>
<td>0.8930</td>
<td>0.7845</td>
<td>0.9603</td>
<td>0.8425</td>
<td>0.9845</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-xsmall-v1">japanese-reranker-cross-encoder-xsmall-v1</a></td>
<td>0.8131</td>
<td>0.6136</td>
<td>0.9376</td>
<td>0.7411</td>
<td>0.9602</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-small-v1">japanese-reranker-cross-encoder-small-v1</a></td>
<td>0.8254</td>
<td>0.6247</td>
<td>0.9390</td>
<td>0.7776</td>
<td>0.9604</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-base-v1">japanese-reranker-cross-encoder-base-v1</a></td>
<td>0.8484</td>
<td>0.6711</td>
<td>0.9337</td>
<td>0.8180</td>
<td>0.9708</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-large-v1">japanese-reranker-cross-encoder-large-v1</a></td>
<td>0.8661</td>
<td>0.7099</td>
<td>0.9364</td>
<td>0.8406</td>
<td>0.9773</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-bge-reranker-v2-m3-v1">japanese-bge-reranker-v2-m3-v1</a></td>
<td>0.8584</td>
<td>0.6918</td>
<td>0.9372</td>
<td>0.8423</td>
<td>0.9624</td>
</tr>
<tr>
<td><a href="https://huggingface.co/BAAI/bge-reranker-v2-m3">bge-reranker-v2-m3</a></td>
<td>0.8512</td>
<td>0.6730</td>
<td>0.9343</td>
<td>0.8374</td>
<td>0.9599</td>
</tr>
<tr>
<td><a href="https://huggingface.co/cl-nagoya/ruri-v3-reranker-310m">ruri-v3-reranker-310m</a></td>
<td>0.9171</td>
<td>0.8688</td>
<td>0.9506</td>
<td>0.8670</td>
<td>0.9820</td>
</tr>
</tbody>
</table>
<h2>推論速度</h2>
<p>こちらは、HuggingFace transformers ライブラリを使った、約15万ペアをリランキングした推論速度結果(トークナイズ時間は除いていて、純粋なモデルでの推論時間)です。MPS(Appleシリコン),CPUの計測にはM4 Maxを、GPUにはRTX5090を用い、かつ ModernBert 系列モデルでは GPU 処理時に flash-attention2 を使っています。</p>
<p>japanese-reranker-tiny-v2, xsmall-v2 は速度面で圧倒的ですね。ruri-v3-reranker-310m もモデルサイズを考えるとかなり速く、これらは flash-attention2 が効いているからでしょう。なお、<a href="https://github.com/huggingface/text-embeddings-inference/">text-embeddings-inference</a>等を使うことで、他のモデルも flash-attention2 を使うことができ、その場合はこの評価以上の速度が出ると思います。</p>
<table>
<thead>
<tr>
<th>モデル名</th>
<th>レイヤー数</th>
<th>隠れ層サイズ</th>
<th>速度(GPU)</th>
<th>速度(MPS)</th>
<th>速度(CPU)</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-tiny-v2">japanese-reranker-tiny-v2</a></td>
<td>3</td>
<td>256</td>
<td>2.1s</td>
<td>82s</td>
<td>702s</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2">japanese-reranker-xsmall-v2</a></td>
<td>10</td>
<td>256</td>
<td>6.5s</td>
<td>303s</td>
<td>2300s</td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-small-v2">japanese-reranker-small-v2</a></td>
<td>13</td>
<td>384</td>
<td>15.2s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-base-v2">japanese-reranker-base-v2</a></td>
<td>19</td>
<td>512</td>
<td>32.5s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-xsmall-v1">japanese-reranker-cross-encoder-xsmall-v1</a></td>
<td>6</td>
<td>384</td>
<td>20.5s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-small-v1">japanese-reranker-cross-encoder-small-v1</a></td>
<td>12</td>
<td>384</td>
<td>40.3s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-base-v1">japanese-reranker-cross-encoder-base-v1</a></td>
<td>12</td>
<td>768</td>
<td>96.8s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-large-v1">japanese-reranker-cross-encoder-large-v1</a></td>
<td>24</td>
<td>1024</td>
<td>312.2s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/hotchpotch/japanese-bge-reranker-v2-m3-v1">japanese-bge-reranker-v2-m3-v1</a></td>
<td>24</td>
<td>1024</td>
<td>310.6s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/BAAI/bge-reranker-v2-m3">bge-reranker-v2-m3</a></td>
<td>24</td>
<td>1024</td>
<td>310.7s</td>
<td></td>
<td></td>
</tr>
<tr>
<td><a href="https://huggingface.co/cl-nagoya/ruri-v3-reranker-310m">ruri-v3-reranker-310m</a></td>
<td>25</td>
<td>768</td>
<td>81.4s</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<p>なお、推論速度のベンチマークに用いた<a href="https://gist.github.com/hotchpotch/bab03c7d4399aa13beb2702600ad9371">スクリプトはこちら</a>です。</p>
<p>また CPU用に onnx に変換したモデルも公開しているため、例えばラズパイ環境などで、onnx + arm向け量子化モデルを使うことで、実際にエッジ環境でも動くでしょう。</p>
<h1>モデル作成の簡易テクニカルレポート</h1>
<p>japanese-reranker-tiny-v2, xsmall-v2, small-v2, base-v2 の学習データ元として、<a href="https://huggingface.co/hotchpotch/japanese-splade-v2">hotchpotch/japanese-splade-v2</a> 学習で用いたデータセット + ハードネガティブ + 若干の独自データを用いて学習させています。v1と比べて大幅に性能が上がったのは、ModernBert ベースで事前対象学習を行った <a href="https://huggingface.co/cl-nagoya/ruri-v3-pt-30m">ruri-v3-pt-30m</a>を用いてることと、v1よりも数倍のデータセットを用いたこと、またハードネガティブでの良質なデータの抽出(各種rerankerのスコアを用い、正しい・正しくないでフィルタリング)を行ったことも大きいでしょう。</p>
<p>また、Tiny モデルのモデルパラメータ抽出元として、<a href="https://huggingface.co/sbintuitions/modernbert-ja-30m">sbintuitions/modernbert-ja-30m</a>と<a href="https://huggingface.co/cl-nagoya/ruri-v3-pt-30m">cl-nagoya/ruri-v3-pt-30m</a>を利用・評価しました。ModernBert アーキテクチャは、グローバルアテンションとローカルアテンションのレイヤーを交互に含みます。例えば modernbert-ja-30m モデルは10層のレイヤーで、<code>[0,3,6,9]</code>層がグローバルアテンションで、それ以外がローカルアテンションとなっています。</p>
<p>最初は全てグローバルアテンションの方が良いだろうと思ったのですが、3,6,9層を含むと基本悪くなり、また出力層に近い層を含むと、こちらも結果が悪くなりました。以下のグラフは同じデータセットで学習したrerankerのrerank評価結果です。出力層に近い6,9などを含むとだいぶ悪くなり学習早期で止めたので、以下の結果には含めてません。また、layer 0 のみは流石に全く性能が出ませんでした。</p>
<table>
<thead>
<tr>
<th>name</th>
<th>JQaRA</th>
<th>miracl</th>
<th>jsquad</th>
<th>JaCWIR</th>
</tr>
</thead>
<tbody>
<tr>
<td>modernbert-ja-30m + full layers</td>
<td>0.7261</td>
<td>0.8095</td>
<td>0.9752</td>
<td>0.9420</td>
</tr>
<tr>
<td>modernbert-ja-30m + layer 0,2,4</td>
<td>0.6455</td>
<td>0.7185</td>
<td>0.9588</td>
<td>0.9265</td>
</tr>
<tr>
<td>modernbert-ja-30m + layer 0,2</td>
<td>0.6171</td>
<td>0.6784</td>
<td>0.9516</td>
<td>0.9155</td>
</tr>
<tr>
<td>modernbert-ja-30m + layer 0</td>
<td>0.2515</td>
<td>0.4416</td>
<td>0.3172</td>
<td>0.0738</td>
</tr>
<tr>
<td>ruri-v3-pt-30m + full layers (= xsmall-v2)</td>
<td>0.7403</td>
<td>0.8206</td>
<td>0.9776</td>
<td>0.9409</td>
</tr>
<tr>
<td>ruri-v3-pt-30m + layer 0,2,4  (= tiny-v2)</td>
<td>0.6455</td>
<td>0.7201</td>
<td>0.9608</td>
<td>0.9287</td>
</tr>
<tr>
<td>ruri-v3-pt-30m + layer 0,1,3</td>
<td>0.6405</td>
<td>0.7124</td>
<td>0.9552</td>
<td>0.9211</td>
</tr>
<tr>
<td>ruri-v3-pt-30m + layer 0,3</td>
<td>0.6177</td>
<td>0.6619</td>
<td>0.9482</td>
<td>0.9076</td>
</tr>
</tbody>
</table>
<p>この中から、最も良質な結果になった <code>ruri-v3-pt-30m</code> を xsmall として、tiny モデルとしては <code>ruri-v3-pt-30m + layer 0,2,4</code> を公開しました。また、small-v2 と base-v2 は <a href="https://huggingface.co/cl-nagoya/ruri-v3-pt-70m">ruri-v3-pt-70m</a> と <a href="https://huggingface.co/cl-nagoya/ruri-v3-pt-130m">ruri-v3-pt-130m</a> をベースにそれぞれ作成されています。なお、モデルマージすると性能は少々上がりますが、今回は行っていません。</p>
<h1>おわりに</h1>
<p>本エントリーでは、非常に小型軽量で実用的な日本語リランカーモデル<a href="https://huggingface.co/hotchpotch/japanese-reranker-tiny-v2">japanese-reranker-tiny-v2</a>、<a href="https://huggingface.co/hotchpotch/japanese-reranker-xsmall-v2">japanese-reranker-xsmall-v2</a>、<a href="https://huggingface.co/hotchpotch/japanese-reranker-small-v2">japanese-reranker-small-v2</a>、<a href="https://huggingface.co/hotchpotch/japanese-reranker-base-v2">japanese-reranker-base-v2</a> についての紹介をしました。これらのモデルのうちtinyやxsmallは、CPUやAppleシリコンといった環境でも実用的な速度で動作し、高価なGPUリソースを必要とせずにとも、とりわけローカルなRAGシステムなどの検索精度の向上に寄与します。またGPU上で動かすことで、高速なレスポンスも実現可能です。</p>
<p>近年の高性能な ModernBert 等の Encoder モデルの登場により、より高性能な実用的な性能を持つモデルの開発を後押ししています。本記事が、日本語処理技術のさらなる発展に貢献できれば幸いです。</p>]]></description>
            <link>https://secon.dev/entry/2025/05/08/100000-japanese-reranker-v2</link>
            <guid isPermaLink="false">/entry/2025/05/08/100000-japanese-reranker-v2</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Thu, 08 May 2025 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[情報検索のための質問文作成モデル query-crafter-japanese を公開]]></title>
            <description><![CDATA[<p>情報検索で利用する、ベクトル検索・リランカーなどのニューラルネットワークモデルの学習には、質問文と回答文がペアで必要です。回答文章はなんでも良い(もちろん質が高い文章や、独自ドメインのデータなどが高品質なモデル作成につながるのですが)のですが、学習にはその回答に関連がある質問文が必要になってきます。最近のLLMの性能向上はめざましく、回答文からLLMを通して自動作成した質問文を作成することで、そのペアを学習に利用することができます。これらのLLMが自動作成するデータセットは、合成データセットとも呼ばれています。</p>
<p>しかし、合成データセットを作成して広く公開したい場合、OpenAIやGeminiなどの商用LLMでは、利用規約によってライセンスの問題が発生します。また、大量の文章を処理したい場合は時間・費用もかなりかかります。</p>
<p>そのため、1.7B〜4B という小型サイズのモデルで高速に動作しながらも、DeepSeek-R1で生成した質問文と同レベルの情報検索用の質問文（クエリ文）を自動作成でき、さらに出力ライセンスに制限がないquery-crafter-japanese モデルを作成しApache 2.0ライセンスで公開しました。</p>
<ul>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-Qwen3-1.7B">query-crafter-japanese-Qwen3-1.7B</a>
<ul>
<li>⭐️:👆速度・性能の面でおすすめです</li>
</ul>
</li>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-Qwen3-4B">query-crafter-japanese-Qwen3-4B</a></li>
<li><a href="https://huggingface.co/hotchpotch/query-crafter-japanese-sarashina2.2-3b-instruct-v0.1">query-crafter-japanese-sarashina2.2-3b-instruct-v0.1</a></li>
</ul>
<hr>
<p><img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20250507-query-crafter-japanese-desc.jpg" alt="query-crafter-japanese-desc"></p>
<hr>
<p>query-crafter は7つのカテゴリーを生成できます。</p>
<ul>
<li>keywords: スペース区切りのキーワード</li>
<li>synonym_keywords: 類義語をもちいた特徴的なキーワード</li>
<li>query: 文章の内容に基づいた質問文</li>
<li>alt_query: BM25でマッチしない表現を使った質問文</li>
<li>title: 文章全体を表現するタイトル</li>
<li>faq: 文章をFAQの回答とした場合の質問文</li>
<li>summary: 文章の短い要約</li>
</ul>
<p>では、以下の文章を用いて、各々のカテゴリーに対する質問文を作成してみましょう。</p>
<ul>
<li><a href="https://gist.github.com/hotchpotch/8b9c9c43e6aacc14b4b47801de063d64">query-crafter-japanese-example.py</a></li>
</ul>
<pre><code>夕方、開発合宿の成果発表会。私以外は、AI関連のちゃんとしたテーマに取り組んで、クオリティも高く、いやー面白い。I氏はエンジニアでもないのに、Figmaプラグインを作ったり、vercelにデプロイしてたり(ほぼcursorが書いた)して、AIによって大きく幅が広がる一例を間近に見る。私は何かのテーマに取り組んだわけではなく、Vibe Cording を一度もしたことがなかったので、cursor でコードをいかに触らず・見ずに作れるかを試した。

毎年のこの日記を要約してdiscordなどに投稿するツール（以前も作ったものの仕様を書いて新機能などを追加）を作成したり、この日記のタイトルがないものに自動でタイトルをつけたりするツールを作成する。Vibe Cording は思った通りの感じで、なるほど便利。

コードは見ずにブラックボックス的な開発（出力成果物だけをみる）をしたので、出来上がったコードを後で見ると本番運用前提のコードでは全くないが、書き殴りのツールを作るには十分。また自分が指示するのは仕様のみで、仕様書も随時アップデートされるようにしてるので、機能を変えたくなったら仕様変更・追加するだけでいいし、楽で良いね。
</code></pre>
<p><code>query-crafter-japanese-Qwen3-1.7B</code> を用いてカテゴリーごとに質問文を生成した結果はこちらです。keywords, query, title, summary あたりは特色が分かりやすく出ていますが、synonym_keywords は完璧な類義語でないことも多かったり、alt_query, faq は query とそれほど変わらなかったりすることもあります。</p>
<pre><code>keywords: Vibe Cording ブラックボックス開発 仕様変更
synonym_keywords: AI活用開発プロジェクト 発表会 仕様変更追加
query: 開発合宿で作成したツールの具体的な機能は？
alt_query: 開発者向けツール開発でコード見ない開発手法の利点は？
title: AI活用で拓く開発の新領域：Vibe Cordingとブラックボックス開発の可能性
faq: 開発合宿で実現した新機能や成果は？
summary: AI活用の開発成果発表会で、Vibe Cordingや日記ツール開発、コード見ずに開発を実施
</code></pre>
<p>また動作速度も vllm + RTX5090 環境で、入力トークンが <code>48,000 toks/s</code>、出力トークンが <code>2200 toks/s</code> で動作します。〜1000文字程度の文章1万件から質問文1万件を生成した場合、100秒弱で作成できます。対象文章が1億件あったとしても、約140時間程度で全てを処理することができます。</p>
<p>なお、DeepSeek-R1 を夜間ディスカウント時間帯(input: 1M toks 0.135USD, output: 1M toks 0.55USD)で実際に10万件の文章を並列100のAPIリクエストで処理した場合、約7時間と40USD程度の費用がかかりました。もし、DeepSeek-R1 APIで1億件を処理した場合、約7,000時間(実際には夜間ディスカウント時間を狙うと、そのタイミングでしか処理できないので、もっと時間がかかります。また並列リクエストの最大数はDeepSeekサイドのリソースによって変動します)と、40,000USDほどの費用が発生するでしょう。</p>
<p>このように、query-crafter は、特に大量の文章から質問文を作成したい場合、処理速度的にも費用的にも大きなメリットがあります。</p>
<h2>query-crafter-japanese モデルの学習</h2>
<p>学習には、出力結果利用に制限がない DeepSeek-R1 を使い <a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese">fineweb-2-edu-japanese</a>のデータをもとに、質問文となる教師データを、合成データセットとして作成しました。</p>
<p>例えば <code>title</code> については '文章全体をうまく表現した、タイトルを考え作成しなさい。考えたタイトルは30文字以内で出力すること。出力は厳密な JSON 形式で <code>{"query": "タイトル"}</code> とする。他に一切余計な出力はしないこと。' といった指示文を用いて作成しています。</p>
<ul>
<li><a href="https://huggingface.co/datasets/hotchpotch/japanese-query-crafter-reasoning-80k">https://huggingface.co/datasets/hotchpotch/japanese-query-crafter-reasoning-80k</a></li>
</ul>
<p>続いてこのデータを教師データとし、SFT(Supervised Fine-tuning)で、Qwen3-4B, Qwen3-1.7B, sarashina2.2-3b-instruct-v0.1, TinySwallow-1.5B-Instruct を学習させました。</p>
<p>SFT時に使ったフォーマットはシンプルに</p>
<pre><code>{
  "system": "{category名}",
  "user": "{text}",
  "assistant": "{query}",
}
</code></pre>
<p>といった内容です。systemプロンプトに <code>title</code> などの指示カテゴリを、user 入力文に文章テキストを、そしてmodelの出力に <code>query</code> を設定しています。何かの用途に特化したSFTの場合、冗長なプロンプトを書く必要はなく、短い指示(今回は各種カテゴリー)のみで、うまく学習できます。</p>
<h2>query-crafter-japanese モデルの評価</h2>
<p>query-crafter の評価は、<a href="https://huggingface.co/datasets/hotchpotch/japanese-query-crafter-reasoning-80k/viewer/default/test">japanese-query-crafter-reasoning-80k の test</a>データを用いました。このデータのtextを元に、各種SFTで学習させたquery-crafterモデルを使って質問文を作成します。</p>
<p>そしてこれらの質問文とテキストをペアに、リランカー<a href="https://huggingface.co/BAAI/bge-reranker-v2-m3">BAAI/bge-reranker-v2-m3</a>で評価させたスコアの結果が以下です。このリランカーは、文章とテキストの関連性が高いと1.0になり、関連性がないと0.0となります。そのため、質問文とテキストが関連しているかどうかの目安になります。</p>
<table>
<thead>
<tr>
<th>モデル</th>
<th>平均</th>
<th>標準偏差</th>
</tr>
</thead>
<tbody>
<tr>
<td>query-crafter-jp-Qwen3-1.7B</td>
<td>0.8701</td>
<td>0.2592</td>
</tr>
<tr>
<td>query-crafter-jp-Qwen3-4B</td>
<td>0.8712</td>
<td>0.2652</td>
</tr>
<tr>
<td>query-crafter-jp-TinySwallow-1.5B</td>
<td>0.7526</td>
<td>0.3611</td>
</tr>
<tr>
<td>query-crafter-jp-sarashina2.2-3b</td>
<td>0.8670</td>
<td>0.2646</td>
</tr>
<tr>
<td>deepseek-r1</td>
<td>0.8507</td>
<td>0.2875</td>
</tr>
</tbody>
</table>
<p>パーセンタイルをプロットしたグラフは以下です。</p>
<p><img src="https://storage.googleapis.com/secons-site-images/other/blog_images/20250507-query-crafter-jp-percentile.jpg" alt="query-crafter-jp-percentile"></p>
<p>結果、TinySwallow-1.5B 以外は、ほとんどのケースでDeepSeek-R1以上のスコアとなりました。特に、Qwen3-1.7B は日本語に特化しているわけではないマルチリンガルモデルですが、SFTするとQwen3-4Bとほとんどスコアが変わらず、性能の高さは驚くべきものです。そのため、特にこだわりがなければ、query-crafter-japanese-Qwen3-1.7B を利用するとよいでしょう。</p>
<p>なお、DeepSeek-R1 他よりスコアが低いからといって必ずしもDeepSeek-R1の質問文の質が悪いというわけではなく、リランカーでも判別が難しいような「正しく難しい質問文」を作成しているケースもあります。TinySwallow-1.5B はちょこちょこ全く関連がない質問文を作成してしまうケースがあり、他のモデルよりスコアが低くなりました。TinySwallow-1.5B-Instruct は <a href="https://arxiv.org/abs/2501.16937">TAID</a> でモデル蒸留されているため、その後の SFT には不向きなのかもしれません。</p>
<h2>おわりに</h2>
<p>大量の質問文章を作りたい場合において、速度的にも費用的にも大きなメリットがある、<code>query-crafter-japanese</code> モデルを作成し公開しました。高性能かつ出力結果に制限がない DeepSeek-R1 の登場以降、様々な方法でデータセットの作成・公開・それを教師データとして利用したモデルの作成がしやすくなりました。また、Qwen などの小型サイズのモデルといった、ライセンスが使いやすいオープンウェイトなLLMの登場・性能進化により、ファインチューンした用途特化の小型モデルも作成・公開しやすくなり、幅広い応用が可能になってきたことを実感しています。もし半年前なら、このモデルを個人で作成することはリソース的にも不可能だったでしょう。</p>
<p>このモデルが、質問文を作りたい方の助けになれば幸いです。</p>]]></description>
            <link>https://secon.dev/entry/2025/05/07/100000-query-crafter-japanese</link>
            <guid isPermaLink="false">/entry/2025/05/07/100000-query-crafter-japanese</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Wed, 07 May 2025 01:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[🍷 FineWeb2 Edu Japanese - 高品質な教育向け日本語データセット]]></title>
            <description><![CDATA[<p>🍷 FineWeb2 Edu Japanese: 高品質な教育向け日本語データセットを、公開しました。</p>
<ul>
<li><a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese">https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese</a></li>
</ul>
<p>以下の内容は、上記ページの日本語訳です。</p>
<hr>
<p><img src="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese/resolve/main/assets/fw2.png" alt="FineWeb2 Edu Japanese image"></p>
<p>本データセットは、FineWeb2 の日本語データ（376M件）のうち、教育向けコンテンツと判断した120M件（約89.3Bトークン）の文章をフィルタしたものです。以下のサブセットも提供しています。</p>
<ul>
<li><strong>default</strong>: 約120M件（1.2億件）のデータ・約89.3Bトークン</li>
<li><strong>sample_10BT</strong>: default からランダムサンプリングした約10Bトークンのデータ</li>
<li><strong>small_tokens</strong>: トークン数が512以下の短い文章のみから構成されるデータ</li>
<li><strong>small_tokens_cleaned</strong>: small_tokens から Web 特有のテキストノイズを除去したデータ</li>
</ul>
<h2>データセット作成の背景</h2>
<p><a href="https://huggingface.co/datasets/HuggingFaceFW/fineweb">FineWeb</a>（英語のみ）は、Webデータの重複除去と高品質テキスト抽出を目的として作成されました。さらに、教育向けに質の高いテキストを抽出した <a href="https://huggingface.co/datasets/HuggingFaceFW/fineweb-edu">FineWeb-Edu</a> により、より少ないトークン数でも効率的な学習が実現可能となっています。</p>
<p>2024年12月に公開された <a href="https://huggingface.co/datasets/HuggingFaceFW/fineweb-2">FineWeb2</a> は多言語対応（日本語を含む）の高品質データセットですが、2025年2月現在、教育向けに価値が高い「Edu」データセットは未公開です。そこで、本プロジェクトでは <a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese">FineWeb2 Edu Japanese データセット</a> を作成し、公開しました。</p>
<h2>教育的データのフィルタリング</h2>
<p>本データセットの構築には、FineWeb2 日本語データから、教育向け文章を判定するためのモデル <a href="https://huggingface.co/hotchpotch/fineweb-2-edu-japanese-classifier">fineweb-2-edu-japanese-classifier</a> を利用してフィルタリングしました。判定モデルのスコアリングの教師データには、DeepSeek-API (deepseek-chat) によって評価された <a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese-scores">fineweb-2-edu-japanese-scores</a> を使っています。なお、本データセットでは、スコアが2.5以上の文章のみを抽出しており、そのスコアは <code>score</code> カラムに記載しています。</p>
<h2>トークンカウントの付与</h2>
<p><a href="https://huggingface.co/sbintuitions/modernbert-ja-130m">ModernBERT-Ja-130M</a> のトークナイザを用いてカウントしたトークン数が <code>token_count</code> カラムとして付与されています。</p>
<h2>Web特有のノイズ除去</h2>
<p>FineWeb2 の日本語データには、Web特有のボイラープレートや不要なノイズが含まれることがあります。例えば、以下のような文章が含まれます。</p>
<pre><code>この文章は90日以上更新の無いサイトに表示されています。
ログイン ログアウト

本当に必要な文章以外にも、さまざまなノイズが含まれていることがあります。例えば、この文章もその一例です。本来不要なテキストが入ってしまうことがこのようにあるでしょう。

今なら50%オフ！クリックしてリンク先の商品を表示

とりわけ文章長が短い場合、文章のほとんどがノイズを含む可能性があります。それらを取り除くことで、より高品質の文章を抽出できないかと考えています。

前のページ  次のページ
</code></pre>
<p>このような不要なテキストを取り除くためのモデル、<a href="https://huggingface.co/hotchpotch/fineweb-2-japanese-text-cleaner">fineweb-2-japanese-text-cleaner</a> を開発しました。ノイズ判定の教師データとしては、<a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-japanese-noise-spans">fineweb-2-japanese-noise-spans</a> を利用しています。この教師データは<a href="https://huggingface.co/cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese">cyberagent/DeepSeek-R1-Distill-Qwen-32B-Japanese</a> を活用して作られました。</p>
<p>このモデルにより、以下のようにノイズ箇所が検出されます。</p>
<pre><code>[NOISE]この文章は90日以上更新の無いサイトに表示されています。[/NOISE]
[NOISE]ログイン[/NOISE] [NOISE]ログアウト[/NOISE]

本当に必要な文章以外にも、さまざまなノイズが含まれていることがあります。例えば、この文章もその一例です。本来不要なテキストが入ってしまうことがこのようにあるでしょう。
[NOISE]
今なら50%オフ！クリックしてリンク先の商品を表示[/NOISE]

とりわけ文章長が短い場合、文章のほとんどがノイズを含む可能性があります。それらを取り除くことで、より高品質の文章を抽出できないかと考えています。

[NOISE]前のページ[/NOISE]  [NOISE]次のページ[/NOISE]
</code></pre>
<p>本データセットに含まれる<code>small_tokens_cleaned</code> サブセットは、<code>small_tokens</code> からさらに <a href="https://huggingface.co/hotchpotch/fineweb-2-japanese-text-cleaner">fineweb-2-japanese-text-cleaner</a> モデルを適用し、ノイズを除去したデータとなります。なお、モデルを使ってノイズ検出をした生データは <a href="https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese-noise-detect-raw">fineweb-2-edu-japanese-noise-detect-raw</a> で公開しています。</p>
<p>なおノイズ検出は完璧ではないため、場合によっては正しい文章の一部が誤って除外されている可能性がありますのでご注意ください。</p>
<h2>注意事項</h2>
<p>本データセット「FineWeb2 Edu Japanese」と、Eduフィルタリングを実施していない大元の「FineWeb2」データセットとの比較実験は行っておりません。そのため、実際のLLM学習においてどの程度の効果差が生じるかは未検証です。</p>
<p>また、教育向けテキストかどうかの分類精度も完璧ではなく、一部教育向けではないテキストも含まれます。</p>
<h2>ライセンス</h2>
<p>本データセットは、元の FineWeb2 と同様に <strong>Open Data Commons Attribution License (ODC-By) v1.0</strong> の下で公開します。また、使用にあたっては <a href="https://commoncrawl.org/terms-of-use">CommonCrawlの利用規約</a> も適用されます。</p>
<h2>Citation Information</h2>
<pre><code>@software{yuichi2025fineweb-2-edu-japanese,
  author = {Yuichi Tateno},
  title = {FineWeb2 Edu Japanese},
  month = feb,
  year = 2025,
  url = {https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese/}
}
</code></pre>]]></description>
            <link>https://secon.dev/entry/2025/02/20/100000-fineweb-2-edu-japanese</link>
            <guid isPermaLink="false">/entry/2025/02/20/100000-fineweb-2-edu-japanese</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Thu, 20 Feb 2025 01:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>