<?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>Mon, 24 Aug 2026 11:40:01 GMT</lastBuildDate>
        <atom:link href="https://secon.dev/other_feed" rel="self" type="application/rss+xml"/>
        <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>
        <item>
            <title><![CDATA[100倍速で実用的な文章ベクトルを作れる、日本語 StaticEmbedding モデルを公開]]></title>
            <description><![CDATA[<p>文章の密ベクトルは、情報検索・文章判別・類似文章抽出など、さまざまな用途に使うことができます。しかしながら最先端のTransformerモデルは小さいモデルでも、とりわけCPU環境では処理速度が遅いため実用でないこともしばしばあります。</p>
<p>この課題を解決する新しいアプローチとして、先日公開されたTransformerモデル「ではない」 <a href="https://huggingface.co/blog/static-embeddings">StaticEmbeddingモデル</a>は、例えば <a href="https://huggingface.co/intfloat/multilingual-e5-small">intfloat/multilingual-e5-small</a> (以下mE5-small)とのベンチマーク比較では85%のスコアという最低十分な性能で、何よりCPUで動作時に126倍高速に文ベクトルを作成することができる、という驚きの速度です。</p>
<p>というわけで、早速日本語(と英語)で学習させたモデル sentence-embedding-japanese を作成し、公開しました。</p>
<ul>
<li><a href="https://huggingface.co/hotchpotch/static-embedding-japanese">https://huggingface.co/hotchpotch/static-embedding-japanese</a></li>
</ul>
<p>日本語の文章ベクトルの性能を評価する <a href="https://github.com/sbintuitions/JMTEB">JMTEB</a> の結果は以下です。総合スコアでは mE5-small には若干及ばないまでも、タスクによっては勝っていたりしますし、<a href="https://github.com/sbintuitions/JMTEB/blob/main/leaderboard.md">他の日本語baseサイズbertモデルよりもスコアが高いこともある</a>ぐらい、最低限実用できそうな性能が出ていますね。本当にそんなに性能が出るのか実際に学習させてみるまでは半信半疑でしたが、驚きです。</p>
<table>
<thead>
<tr>
<th>Model</th>
<th>Avg(micro)</th>
<th>Retrieval</th>
<th>STS</th>
<th>Classification</th>
<th>Reranking</th>
<th>Clustering</th>
<th>PairClassification</th>
</tr>
</thead>
<tbody>
<tr>
<td>text-embedding-3-small</td>
<td>69.18</td>
<td>66.39</td>
<td>79.46</td>
<td>73.06</td>
<td>92.92</td>
<td>51.06</td>
<td>62.27</td>
</tr>
<tr>
<td>multilingual-e5-small</td>
<td>67.71</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><strong>static-embedding-japanese</strong></td>
<td>67.17</td>
<td><strong>67.92</strong></td>
<td><strong>80.16</strong></td>
<td><strong>67.96</strong></td>
<td>91.87</td>
<td>40.39</td>
<td><strong>62.37</strong></td>
</tr>
</tbody>
</table>
<p>なお、StaticEmbedding 日本語モデル学習などの技術的なことは記事の後半に書いているので、興味がある方はどうぞ。</p>
<h2>利用方法</h2>
<p>利用は簡単、SentenceTransformer を使っていつもの方法で文章ベクトルを作れます。今回はGPUを使わず、CPUで実行してみましょう。なお SentenceTransformer は 3.3.1 で試しています。</p>
<pre><code>pip install "sentence-transformers>=3.3.1"
</code></pre>
<pre><code class="hljs language-python"><span class="hljs-keyword">from</span> sentence_transformers <span class="hljs-keyword">import</span> SentenceTransformer

model_name = <span class="hljs-string">"hotchpotch/static-embedding-japanese"</span>
model = SentenceTransformer(model_name, device=<span class="hljs-string">"cpu"</span>)

query = <span class="hljs-string">"美味しいラーメン屋に行きたい"</span>
docs = [
    <span class="hljs-string">"素敵なカフェが近所にあるよ。落ち着いた雰囲気でゆっくりできるし、窓際の席からは公園の景色も見えるんだ。"</span>,
    <span class="hljs-string">"新鮮な魚介を提供する店です。地元の漁師から直接仕入れているので鮮度は抜群ですし、料理人の腕も確かです。"</span>,
    <span class="hljs-string">"あそこは行きにくいけど、隠れた豚骨の名店だよ。スープが最高だし、麺の硬さも好み。"</span>,
    <span class="hljs-string">"おすすめの中華そばの店を教えてあげる。とりわけチャーシューが手作りで柔らかくてジューシーなんだ。"</span>,
]

embeddings = model.encode([query] + docs)
<span class="hljs-built_in">print</span>(embeddings.shape)
similarities = model.similarity(embeddings[<span class="hljs-number">0</span>], embeddings[<span class="hljs-number">1</span>:])
<span class="hljs-keyword">for</span> i, similarity <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(similarities[<span class="hljs-number">0</span>].tolist()):
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"<span class="hljs-subst">{similarity:<span class="hljs-number">.04</span>f}</span>: <span class="hljs-subst">{docs[i]}</span>"</span>)</code></pre>
<pre><code>(5, 1024)
0.1040: 素敵なカフェが近所にあるよ。落ち着いた雰囲気でゆっくりできるし、窓際の席からは公園の景色も見えるんだ。
0.2521: 新鮮な魚介を提供する店です。地元の漁師から直接仕入れているので鮮度は抜群ですし、料理人の腕も確かです。
0.4835: あそこは行きにくいけど、隠れた豚骨の名店だよ。スープが最高だし、麺の硬さも好み。
0.3199: おすすめの中華そばの店を教えてあげる。とりわけチャーシューが手作りで柔らかくてジューシーなんだ。
</code></pre>
<p>このように、queryにマッチする文章のスコアが高くなるように計算できてますね。この例文では、例えばBM25ではqueryに含まれる「ラーメン」のような直接的な単語が文章に出ていないため、うまくマッチさせることが難しいでしょう。</p>
<p>続いて、類似文章タスクの例です。</p>
<pre><code class="hljs language-python">sentences = [
    <span class="hljs-string">"明日の午後から雨が降るみたいです。"</span>,
    <span class="hljs-string">"来週の日曜日は天気が良いそうだ。"</span>,
    <span class="hljs-string">"あしたの昼過ぎから傘が必要になりそう。"</span>,
    <span class="hljs-string">"週末は晴れるという予報が出ています。"</span>,
]

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

<span class="hljs-built_in">print</span>(similarities)

<span class="hljs-comment"># 一つ目の文章と、その他の文章の類似度を表示</span>
<span class="hljs-keyword">for</span> i, similarity <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(similarities[<span class="hljs-number">0</span>].tolist()):
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"<span class="hljs-subst">{similarity:<span class="hljs-number">.04</span>f}</span>: <span class="hljs-subst">{sentences[i]}</span>"</span>)</code></pre>
<pre><code>tensor([[1.0000, 0.2814, 0.3620, 0.2818],
        [0.2814, 1.0000, 0.2007, 0.5372],
        [0.3620, 0.2007, 1.0000, 0.1299],
        [0.2818, 0.5372, 0.1299, 1.0000]])
1.0000: 明日の午後から雨が降るみたいです。
0.2814: 来週の日曜日は天気が良いそうだ。
0.3620: あしたの昼過ぎから傘が必要になりそう。
0.2818: 週末は晴れるという予報が出ています。
</code></pre>
<p>こちらも、類似文章が高スコアになる結果になりました。</p>
<p>またTransformerモデルを利用してCPUで文章ベクトルを作った場合、少ない文章量でもだいぶ時間がかか、という経験をされた方も多いと思います。StaticEmbedding モデルではCPUがそこそこ速ければ一瞬で終わるはず。さすが100倍速。</p>
<h3>出力次元を小さくする</h3>
<p>標準で作られる文ベクトルの次元は1024ですが、これをさらに小さく次元削減することもできます。例えば 128 を指定してみましょう。</p>
<pre><code class="hljs language-python"><span class="hljs-comment"># truncate_dim は 32, 64, 128, 256, 512, 1024 から指定</span>
model = SentenceTransformer(model_name, device=<span class="hljs-string">"cpu"</span>, truncate_dim=<span class="hljs-number">128</span>)

query = <span class="hljs-string">"美味しいラーメン屋に行きたい"</span>
docs = [
    <span class="hljs-string">"素敵なカフェが近所にあるよ。落ち着いた雰囲気でゆっくりできるし、窓際の席からは公園の景色も見えるんだ。"</span>,
    <span class="hljs-string">"新鮮な魚介を提供する店です。地元の漁師から直接仕入れているので鮮度は抜群ですし、料理人の腕も確かです。"</span>,
    <span class="hljs-string">"あそこは行きにくいけど、隠れた豚骨の名店だよ。スープが最高だし、麺の硬さも好み。"</span>,
    <span class="hljs-string">"おすすめの中華そばの店を教えてあげる。とりわけチャーシューが手作りで柔らかくてジューシーなんだ。"</span>,
]

embeddings = model.encode([query] + docs)
<span class="hljs-built_in">print</span>(embeddings.shape)
similarities = model.similarity(embeddings[<span class="hljs-number">0</span>], embeddings[<span class="hljs-number">1</span>:])
<span class="hljs-keyword">for</span> i, similarity <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(similarities[<span class="hljs-number">0</span>].tolist()):
    <span class="hljs-built_in">print</span>(<span class="hljs-string">f"<span class="hljs-subst">{similarity:<span class="hljs-number">.04</span>f}</span>: <span class="hljs-subst">{docs[i]}</span>"</span>)</code></pre>
<pre><code>(5, 128)
0.1464: 素敵なカフェが近所にあるよ。落ち着いた雰囲気でゆっくりできるし、窓際の席からは公園の景色も見えるんだ。
0.3094: 新鮮な魚介を提供する店です。地元の漁師から直接仕入れているので鮮度は抜群ですし、料理人の腕も確かです。
0.5923: あそこは行きにくいけど、隠れた豚骨の名店だよ。スープが最高だし、麺の硬さも好み。
0.3405: おすすめの中華そばの店を教えてあげる。とりわけチャーシューが手作りで柔らかくてジューシーなんだ。
</code></pre>
<p>128次元のベクトルになり、結果のスコアも若干変わりましたね。次元が小さくなったことで、性能が少々劣化しています(後半にベンチマークを記載)。ただ1024次元から128次元に減ることで、保存するストレージサイズが減ったり、検索時などに利用する類似度計算コストが約8倍速になったりとなったりと、用途によっては小さい次元の方が嬉しいことも多いでしょう。</p>
<h2>なぜCPUで推論が高速なの？</h2>
<p>StaticEmbedding はTransformerモデルではありません。つまりTrasformerの特徴である "Attention Is All You Need" なアテンションの計算が一切ないのです。文章に出てくる単語トークンを1024次元のテーブルに保存して、文ベクトル作成時にはそれの平均をとっているだけです。なお、アテンションがないので、文脈の理解などはしていません。</p>
<p>また内部実装では PyTorch の nn.EmbeddingBag を使って、全てを連結したトークンとオフセットを渡して処理することで、PyTorch の最適化で高速なCPU並列処理とメモリアクセスがされているようです。</p>
<p><img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/static-embeddings/similarity_speed.png" alt=""></p>
<p><a href="https://huggingface.co/blog/static-embeddings#multilingual-similarity-4">元記事の速度評価結果によると</a>CPUではmE5-smallと比べて126倍速らしいですね。</p>
<h2>評価結果</h2>
<p>JMTEBでの全ての評価結果は<a href="https://huggingface.co/hotchpotch/static-embedding-japanese/blob/main/JMTEB/summary.json">こちらJSONファイルに記載</a>しています。<a href="https://github.com/sbintuitions/JMTEB/blob/main/leaderboard.md">JMTEB Leaderboard</a>で他のモデルと見比べると、相対的な差がわかるでしょう。JMTEBの全体の評価結果はモデルサイズを考えると、すこぶる良好です。なお、JMTEB のmr-tidy タスクは700万文章のベクトル化を行うので処理に時間がかなりかかる(モデルにもよりますがRTX4090で1~4時間ほど)と思います。これもStaticEmbeddingsでは非常に速く、RTX4090では約4分で処理終えることができました。</p>
<h3>情報検索でBM25の置き換えができそうか?</h3>
<p>JMTEBの中の情報検索タスクの<a href="https://huggingface.co/hotchpotch/static-embedding-japanese/blob/main/JMTEB/summary.json#L21-L39">Retrievalの結果</a>を見てみましょう。StaticEmbedding では mr-tidy の項目が著しく悪いですね。mr-tidyは他のタスクに比べて文章量が圧倒的に多く(700万文章)、つまる所大量の文章を検索するようなタスクでは結果が悪い可能性がありそうです。文脈を無視したた単純なトークンの平均なので、増えれば増えるほど似た平均の文章が出てくるとすると、そういう結果にもなり得そうですね。</p>
<p>ので、大量の文章の場合、BM25よりもだいぶ性能が悪い可能性がありそうです。ただ、少ない文章で、ずばりの単語マッチが少ない場合は、BM25よりも良好な結果になることが多そうですね。</p>
<p>なお情報検索タスクの jaqket の結果が他のモデルに対してやたら良いのは、jaqket の問題を含む JQaRa (dev, unused)を学習しているからといっても、高すぎる感じで謎です。test の情報リークはしていないとは思うのですが…。</p>
<h3>クラスタリング結果が悪い</h3>
<p>こちらも詳細は追っかけていませんが、スコア的には他のモデルよりもだいぶ悪い結果ですね。クラス分類タスクは悪くないので不思議です。埋め込み空間がマトリョーシカ表現学習で作られた影響もあるのでしょうか。</p>
<h2>JQaRA, JaCWIR でのリランキングタスク評価</h2>
<p><a href="https://huggingface.co/datasets/hotchpotch/JQaRA">JQaRA</a> の結果はこちら。</p>
<table>
<thead>
<tr>
<th align="left">model_names</th>
<th align="right">ndcg@10</th>
<th align="right">mrr@10</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><a href="https://huggingface.co/hotchpotch/static-embedding-japanese">static-embedding-japanese</a></td>
<td align="right">0.4704</td>
<td align="right">0.6814</td>
</tr>
<tr>
<td align="left">bm25</td>
<td align="right">0.458</td>
<td align="right">0.702</td>
</tr>
<tr>
<td align="left"><a href="https://huggingface.co/intfloat/multilingual-e5-small">multilingual-e5-small</a></td>
<td align="right">0.4917</td>
<td align="right">0.7291</td>
</tr>
</tbody>
</table>
<p><a href="https://huggingface.co/datasets/hotchpotch/JaCWIR">JaCWIR</a> の結果はこちら。</p>
<table>
<thead>
<tr>
<th align="left">model_names</th>
<th align="right">map@10</th>
<th align="right">hits@10</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><a href="https://huggingface.co/hotchpotch/static-embedding-japanese">static-embedding-japanese</a></td>
<td align="right">0.7642</td>
<td align="right">0.9266</td>
</tr>
<tr>
<td align="left">bm25</td>
<td align="right">0.8408</td>
<td align="right">0.9528</td>
</tr>
<tr>
<td align="left"><a href="https://huggingface.co/intfloat/multilingual-e5-small">multilingual-e5-small</a></td>
<td align="right">0.869</td>
<td align="right">0.97</td>
</tr>
</tbody>
</table>
<p>JQaRa 評価は BM25 よりは若干良く、mE5-small よりは若干低い、JaCWIR は BM25, mE5よりだいぶ低い感じの結果になりました。</p>
<p>JaCWIR はqueryから探しあてる文章が、Web文章のタイトルと概要文なので、いわゆる「綺麗な」文章ではないケースも多いです。transformerモデルはノイズに強いので、単純なトークン平均のStaticEmbeddingではスコアに差がつけられるのも納得ですね。BM25は特徴的な単語が出現した文章にマッチするので、JaCWIR でもノイズとなるような文章上の単語はクエリにそもそもマッチしないため、Transformer モデルと競争力のある結構良い結果を残しています。</p>
<p>この結果から、StaticEmbedding は Transformer / BM25 に比べ、ノイズを多く含む文章の場合はスコアが悪い可能性があります。</p>
<h2>出力次元の削減</h2>
<p>StaticEmbedding で出力される次元は、学習次第ですが今回作成したものは1024次元とそこそこのサイズです。次元数が大きいと、推論後のタスク(クラスタリングや情報検索など)に計算コストがかかってしまいます。しかしながら、学習時にマトリョーシカ表現学習(<a href="https://arxiv.org/abs/2205.13147">Matryoshka Representation Learning(MRL)</a>)をしているため、1024次元をさらに小さな次元へと簡単に次元削減ができます。</p>
<p>MRLは、学習時に先頭のベクトルほど重要な次元を持ってくることで、例えば1024次元でも先頭の32,64,128,256...次元だけを使って後ろを切り捨てるだけで、ある程度良好な結果を示しています。</p>
<p><img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/static-embeddings/nano_beir_matryoshka.png" alt=""></p>
<p>このグラフ参照元の<a href="https://huggingface.co/blog/static-embeddings#matryoshka-evaluation">StaticEmbedding の記事</a>によると、128次元で91.87%, 256次元で95.79%, 512次元で98.53%の性能を維持しているようです。精度にそこまでシビアではないが、その後の計算コストを下げたい場合、ガッと次元削減して使う、という用途にも使えそうですね。</p>
<h3>StaticEmbdding 日本語モデルでの次元削減結果</h3>
<p>JMTEB では、出力時にモデルのパラメータを制御できるため、truncate_dim オプションを渡すことで、次元削減した結果のベンチマークも簡単に計測できます。素晴らしいですね。というわけで、StaticEmbdding 日本語モデルでも、次元削減した結果でベンチマークをとってみました。</p>
<table>
<thead>
<tr>
<th>次元数</th>
<th>Avg(micro)</th>
<th>スコア割合(%)</th>
<th>Retrieval</th>
<th>STS</th>
<th>Classification</th>
<th>Reranking</th>
<th>Clustering</th>
<th>PairClassification</th>
</tr>
</thead>
<tbody>
<tr>
<td>1024</td>
<td>67.17</td>
<td>100.00</td>
<td>67.92</td>
<td>80.16</td>
<td>67.96</td>
<td>91.87</td>
<td>40.39</td>
<td>62.37</td>
</tr>
<tr>
<td>512</td>
<td>66.57</td>
<td>99.10</td>
<td>67.63</td>
<td>80.11</td>
<td>65.66</td>
<td>91.54</td>
<td>41.25</td>
<td>62.37</td>
</tr>
<tr>
<td>256</td>
<td>65.94</td>
<td>98.17</td>
<td>66.99</td>
<td>79.93</td>
<td>63.53</td>
<td>91.73</td>
<td>42.55</td>
<td>62.37</td>
</tr>
<tr>
<td>128</td>
<td>64.25</td>
<td>95.65</td>
<td>64.87</td>
<td>79.56</td>
<td>60.52</td>
<td>91.62</td>
<td>41.81</td>
<td>62.33</td>
</tr>
<tr>
<td>64</td>
<td>61.79</td>
<td>91.98</td>
<td>61.15</td>
<td>78.34</td>
<td>58.23</td>
<td>91.50</td>
<td>39.11</td>
<td>62.35</td>
</tr>
<tr>
<td>32</td>
<td>57.93</td>
<td>86.24</td>
<td>53.35</td>
<td>76.51</td>
<td>55.95</td>
<td>91.15</td>
<td>38.20</td>
<td>62.37</td>
</tr>
</tbody>
</table>
<p><del>スコアの変化を見ると、512次元へと次元削減した場合はやたらRetrieval, Classification,Reranking の性能が悪くなります。むしろ256次元まで次元削減してしまった方が良好な結果に。256次元では、スコア的には次元削減する前のモデルの98.93%なんですが、これはクラスタリングの結果がなぜか1024次元よりも良くなってしまったためですね。</del></p>
<p>512次元でのスコア計測が間違っていたので修正しました。マトリョーシカ表現学習がうまく反映され、次元数を削ると若干のスコア低下が見られますが、次元数が減ったためその後のコストが抑えられそうですね。</p>
<p>クラスタリングタスクにおいては128次元まで次元削減しても1024次元よりもスコアが高い、という本来情報量を削らない方がスコアが良いくなりそうなのに、クラスタリングタスクのみは逆にスコアが上がってしまう興味深い結果となりました…。マトリョーシカ表現学習では、先頭の次元の方が全体的な特徴を踏まえているので、クラスタリング用途には(クラスタリングのアルゴリズムにもよると思いますが)、特徴的な前の方の次元のみで後ろの次元を使わない方が良質な結果が得られる、ということなのかもしれません。</p>
<p>というわけで、static-embedding-japanese モデルで次元削減する時は、512,256,128次元あたりが性能と次元削減のバランスが取れてそうですね。</p>
<h2>StaticEmbedding モデルを作ってみて</h2>
<p>正直、単純なトークンのembeddingsの平均でそんなに性能出るのか半信半疑だったのですが、実際に学習させてみてシンプルなアーキテクチャなのに性能の高さにびっくりしました。Transformer 全盛のこの時代に、古き良き単語埋め込みの活用モデルで、実世界で利活用できそうなモデルの出現に驚きを隠せません。</p>
<p>CPUでの推論速度が速い文ベクトル作成モデルは、ローカルCPU環境で大量の文章の変換などはもとより、エッジデバイスだったりネットワークが遅い(リモートの推論サーバを叩けない)環境だったり、色々と活用できそうですね。</p>
<hr>
<h1>StaticEmbedding 日本語モデル学習のテクニカルノート</h1>
<h2>なぜうまく学習できるのか</h2>
<p>StaticEmbedding は非常にシンプルで、文章をトークナイズしたIDで単語の埋め込みベクトルが格納されているEmbeddingBagテーブルからN次元(今回は1024次元)のベクトルを取得し、その平均を取るだけです。</p>
<p>これまで、単語埋め込みベクトルといえば、word2vec や GloVe のように Skip-gram や CBOW を用いて単語の周辺を学習してきました。しかし、StaticEmbedding では文章全体を用いて学習しています。また、対照学習を使って大量の様々な文章を巨大バッチで学習しており、良い単語の埋め込み表現の学習に成功しています。</p>
<p>対照学習は、基本的に正例以外全てを負例として学習するため、例えばバッチサイズ2048なら1の正例に対して2047の負例を2048通り、つまり2048x2047で約400万の比較を学習します。そのため、元の単語空間に対して適切な重みを更新しながら、学習を進めることができるのです。</p>
<h2>学習データセット</h2>
<p>日本語モデル学習にあたり、対照学習で利用できるデータセットとして、以下を作成し使用しました。</p>
<ul>
<li><a href="https://huggingface.co/datasets/hotchpotch/sentence_transformer_japanese">hotchpotch/sentence_transformer_japanese</a>
<ul>
<li><a href="https://sbert.net/docs/sentence_transformer/loss_overview.html">SentenceTransformer で学習しやすいカラム名と構造</a>に整えたものです。
<ul>
<li><code>(anchor, positive)</code>, <code>(anchor, positive, negative)</code>, <code>(anchor, positive, negative_1, ..., negative_n)</code> といった構造になっています。</li>
</ul>
</li>
<li>以下のデータセットを基に hotchpotch/sentence_transformer_japanese を作成しました。毎度ながらデータセットの作者の方々・とりわけ hpprc 氏に感謝です。
<ul>
<li><a href="https://huggingface.co/datasets/hpprc/emb">https://huggingface.co/datasets/hpprc/emb</a>
<ul>
<li><a href="https://huggingface.co/datasets/hotchpotch/hpprc_emb-scores">https://huggingface.co/datasets/hotchpotch/hpprc_emb-scores</a> のリランカースコアを使用し、positive(>=0.7) / negative(&#x3C;=0.3) のフィルタリングを行いました。</li>
</ul>
</li>
<li><a href="https://huggingface.co/datasets/hpprc/llmjp-kaken">https://huggingface.co/datasets/hpprc/llmjp-kaken</a></li>
<li><a href="https://huggingface.co/datasets/hpprc/msmarco-ja">https://huggingface.co/datasets/hpprc/msmarco-ja</a>
<ul>
<li><a href="https://huggingface.co/datasets/hotchpotch/msmarco-ja-hard-negatives">https://huggingface.co/datasets/hotchpotch/msmarco-ja-hard-negatives</a> のリランカースコアを用いて、positive(>=0.7) / negative(&#x3C;=0.3) のフィルタリングを行いました。</li>
</ul>
</li>
<li><a href="https://huggingface.co/datasets/hpprc/mqa-ja">https://huggingface.co/datasets/hpprc/mqa-ja</a></li>
<li><a href="https://huggingface.co/datasets/hpprc/llmjp-warp-html">https://huggingface.co/datasets/hpprc/llmjp-warp-html</a></li>
</ul>
</li>
</ul>
</li>
<li>上記の作成したデータセットの中で、以下を使用しました。なお、情報検索を強化したかったため、情報検索に適したデータセットのデータはオーギュメンテーションで件数を多めに学習させています。
<ul>
<li>httprc_auto-wiki-nli-triplet</li>
<li>httprc_auto-wiki-qa</li>
<li>httprc_auto-wiki-qa-nemotron</li>
<li>httprc_auto-wiki-qa-pair</li>
<li>httprc_baobab-wiki-retrieval</li>
<li>httprc_janli-triplet</li>
<li>httprc_jaquad</li>
<li>httprc_jqara</li>
<li>httprc_jsnli-triplet</li>
<li>httprc_jsquad</li>
<li>httprc_miracl</li>
<li>httprc_mkqa</li>
<li>httprc_mkqa-triplet</li>
<li>httprc_mr-tydi</li>
<li>httprc_nu-mnli-triplet</li>
<li>httprc_nu-snli-triplet</li>
<li>httprc_quiz-no-mori</li>
<li>httprc_quiz-works</li>
<li>httprc_snow-triplet</li>
<li>httprc_llmjp-kaken</li>
<li>httprc_llmjp_warp_html</li>
<li>httprc_mqa_ja</li>
<li>httprc_msmarco_ja</li>
</ul>
</li>
<li>英語データセットには、以下のデータセットを利用しています。
<ul>
<li><a href="https://huggingface.co/datasets/sentence-transformers/msmarco-co-condenser-margin-mse-sym-mnrl-mean-v1">sentence-transformers/msmarco-co-condenser-margin-mse-sym-mnrl-mean-v1</a></li>
<li><a href="https://huggingface.co/datasets/sentence-transformers/squad">sentence-transformers/squad</a></li>
<li><a href="https://huggingface.co/datasets/sentence-transformers/all-nli">sentence-transformers/all-nli</a></li>
<li><a href="https://huggingface.co/datasets/sentence-transformers/trivia-qa">sentence-transformers/trivia-qa</a></li>
<li><a href="https://huggingface.co/datasets/nthakur/swim-ir-monolingual">nthakur/swim-ir-monolingual</a></li>
<li><a href="https://huggingface.co/datasets/sentence-transformers/miracl">sentence-transformers/miracl</a></li>
<li><a href="https://huggingface.co/datasets/sentence-transformers/mr-tydi">sentence-transformers/mr-tydi</a></li>
</ul>
</li>
</ul>
<h2>日本語トークナイザ</h2>
<p>StaticEmbedding を学習するためには、HuggingFace のトークナイザライブラリの tokenizer.json 形式で処理可能なトークナイザを使うと簡単そうだったので、 <a href="https://huggingface.co/hotchpotch/xlm-roberta-japanese-tokenizer">hotchpotch/xlm-roberta-japanese-tokenizer</a> というトークナイザを作成しました。語彙数は 32,768 です。</p>
<p>このトークナイザは、wikipedia 日本語~~、wikipedia 英語(サンプリング)、cc-100(日本語, サンプリング)~~(訂正:作成コードを確認したところ、wikipedia日本語のみを利用していました)のデータを unidic で分割し、sentencepiece unigram で学習したものです。XLM-Roberta 形式の日本語トークナイザとしても機能します。今回はこのトークナイザを利用しました。</p>
<h2>ハイパーパラメータ</h2>
<p><a href="https://huggingface.co/blog/static-embeddings#english-retrieval-2">大元の学習コード</a>との変更点やメモは以下の通りです。</p>
<ul>
<li>batch_size を大元の 2048 から 6072 に設定しました。
<ul>
<li>対照学習で巨大なバッチを処理するとき、同一バッチ内にポジティブとネガティブが含まれると学習に悪影響を与える可能性があります。これを防ぐために <a href="https://sbert.net/docs/package_reference/sentence_transformer/sampler.html">BatchSamplers.NO_DUPLICATES</a> オプションがあります。しかし、バッチサイズが巨大だと同一バッチに含めないためのサンプリング処理に時間がかかることがあります。</li>
<li>今回は <code>BatchSamplers.NO_DUPLICATES</code> を指定し、RTX4090 の 24GB に収まる 6072 に設定しました。バッチサイズはさらに大きい方が結果が良い可能性があります。</li>
</ul>
</li>
<li>epoch数を1から2に変更しました
<ul>
<li>1よりも2の方が良い結果になりました。ただし、データサイズがもっと大きければ、1の方が良い可能性があります。</li>
</ul>
</li>
<li>スケジューラ
<ul>
<li>標準のlinearから、経験則でより良いと感じるcosineに変更しました。</li>
</ul>
</li>
<li>オプティマイザ
<ul>
<li>標準のAdamW のままです。adafactorに変更した場合、収束が悪くなりました。</li>
</ul>
</li>
<li>learning_rate
<ul>
<li>2e-1 のままです。値が巨大すぎるのではないかと疑問に思いましたが、低くすると結果が悪化しました。</li>
</ul>
</li>
<li>dataloader_prefetch_factor=4</li>
<li>dataloader_num_workers=15
<ul>
<li>トークナイズとバッチサンプラのサンプリングに時間がかかるため、大きめに設定しました。</li>
</ul>
</li>
</ul>
<h2>学習リソース</h2>
<ul>
<li>CPU
<ul>
<li>Ryzen9 7950X</li>
</ul>
</li>
<li>GPU
<ul>
<li>RTX4090</li>
</ul>
</li>
<li>memory
<ul>
<li>64GB</li>
</ul>
</li>
</ul>
<p>このマシンリソースで、フルスクラッチ学習にかかった時間は約4時間でした。GPUのコア負荷は非常に小さく、他のtransformerモデルでは学習時に90%前後で張り付くのに対して、StaticEmbeddingではほとんど0%でした。これは、巨大なバッチをGPUメモリに転送する時間が大半を占めているためかと思われます。そのため、GPUメモリの帯域幅が速くなれば、学習速度がさらに向上する可能性があります。</p>
<h2>さらなる性能向上へ</h2>
<p>今回利用したトークナイザはStaticEmbedding向けに特化したものではないため、より適したトークナイザを使用すれば性能が向上する可能性があります。バッチサイズをさらに巨大化することで、学習の安定性が向上し、性能向上が見込めるかもしれません。</p>
<p>また、さまざまなドメインや合成データセットを利用するなど、より幅広い文章リソースを学習に組み込むことで、さらなる性能向上が期待できます。</p>
<h2>大元の学習コード</h2>
<p>学習に使用したコードは、以下で MIT ライセンスで公開しています。スクリプトを実行すれば再現できる、はず...!</p>
<ul>
<li><a href="https://huggingface.co/hotchpotch/static-embedding-japanese/blob/main/trainer.py">https://huggingface.co/hotchpotch/static-embedding-japanese/blob/main/trainer.py</a></li>
</ul>
<h2>ライセンス</h2>
<p>static-embedding-japanese はモデル重み・学習コードを MIT ライセンスで公開しています。</p>]]></description>
            <link>https://secon.dev/entry/2025/01/21/060000-static-embedding-japanese</link>
            <guid isPermaLink="false">/entry/2025/01/21/060000-static-embedding-japanese</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Mon, 20 Jan 2025 21:00:00 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[振り返り2024年]]></title>
            <description><![CDATA[<p>今年も年の瀬。2024年を振り返る。</p>
<p><img src="https://i.imgur.com/0zZSHkA.jpeg" alt="2025は巳年"></p>
<hr>
<h2>生活</h2>
<p>2024はマイペースで過ごすことができた。家を建てていたので打ち合わせや様子を見にいくことが多く、長期海外旅行などは行けなかったけど、海外にも国内にもちらほら旅行したし、まぁまぁの活動。</p>
<p>健康面では人生での体重最高を更新してしまったので、ダイエットも兼ねてパーソナルジムへ行き、-5kgで一昔前の体重へ。本当はあと5kgぐらい痩せて、筋肉量も増やしたいが、この辺は週一のジム通いだけでなく、ちゃんと運動しないと筋肉はつかなさそう。</p>
<p>そして家がついにたった！今年ギリギリの引っ越しで暮らし始めてまだ6日目ほどだが、今のところ大変快適に過ごしている。薪ストーブも今のところ寝ている時以外は常時焚いていて暖かくて最高だ。</p>
<h2>仕事</h2>
<p>仕事では今年から始まったAI関連プロダクトのPdM・データサイエンティスト(主に情報検索分野)としてだいぶ好きにやらせてもらい、自分にない知見を持ったチームメンバーとも関われ、楽しく充実した一年。結構良いプロダクトができたのではななろうか。プロダクトのサイレントリリースはしていて、来年は多分広く使われるようになるんじゃないかなぁ。というか広く使われるようにしていきたい。</p>
<h2>技術・趣味</h2>
<p>2023年後半から情報検索が断然面白くなってきて、仕事でもやり始めたのけど、趣味ではデータセット作ったり、リランカーや検索モデルを作って公開したりと、色々と役立つ物のアウトプットもある程度はできたかなーと思っている。LLMの学習はマシンリソース的に難しいのだけど、何かを行うことに足りないワンピースを埋めるようなモデルは作れるし、日本語関連は公開する人が少ないから特定用途に特化すればNo1モデルも作れる感じだしで楽しい。</p>
<p>情報検索・NLP・機械学習分野はやればやるほど知識が増えていく感じが続いて、面白すぎるじゃんという感じで、まだまだやりたいこと知りたいことがたくさんあるので、技術分野ではこの辺を抑えつつ、趣味で色々作ったり、この辺の知見を仕事に活かしたりと、2025も色々やっていきたい。</p>
<p>が、ちょっと仕事よりの技術にプライベートでは時間を使いすぎで(朝晩は大体趣味のモデル作りに勤しんでいた)、プライベートではもっとあれこれ広く楽しんだ方が人生が豊かになりそうな気もしていて、2025はこの辺のバランスも気をかけたい。</p>
<h2>総括</h2>
<p>振り返るとあっという間の一年で、結構色々やったような、まだまだ全然やれてないような、そんな年だった。2023年に崩したマイペースは、取り戻したと言えそうで、自分にちょうど良いバランスであれこれできた気がする。</p>
<p>そして毎度のことだが、さまざまなことをサポートしてくれる妻に感謝だ。というわけで、2025年もみなさんよろしくお願いいたします。</p>
<ul>
<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/2024/12/31/100000-furikaeri-2024</link>
            <guid isPermaLink="false">/entry/2024/12/31/100000-furikaeri-2024</guid>
            <dc:creator><![CDATA[secondlife / @hotchpotch / Yuichi Tateno]]></dc:creator>
            <pubDate>Tue, 31 Dec 2024 01:00:00 GMT</pubDate>
        </item>
    </channel>
</rss>