【ITニュース解説】Mean Pooling vs CLS: Watch Transformers.js Turn a Sentence Into an Embedding in Node
2026年10月20日に「Dev.to」が公開したITニュース「Mean Pooling vs CLS: Watch Transformers.js Turn a Sentence Into an Embedding in Node」について初心者にもわかりやすく解説しています。
ITニュース概要
Transformers.jsで文章を埋め込みベクトルに変換する際、複数のトークンをまとめる「プーリング」方法や「正規化」の設定が重要だ。これらの設定がデータ登録時と検索時で異なると、検索精度が著しく低下する。異なる設定の埋め込みは互換性がないため、設定情報を厳密に管理し、ベクトルと共に保存する必要がある。
ITニュース解説
コンピューターが人間の言葉を理解し処理するためには、テキストを数値の形に変換する必要がある。この数値表現を「埋め込み」と呼ぶ。埋め込みは、単語や文の意味を捉えた多次元の数値ベクトルであり、検索拡張生成(RAG)システムのように、大量の文書から関連情報を探し出すAIアプリケーションの基盤となっている。
通常、開発者は「const vec = await embed(text)」のような一行のコードでこの埋め込みを作成し、その中身を深く意識することなく利用する。しかし、この簡潔な関数の中には、埋め込みがどのように生成されるかという重要な「選択」が隠されている。この記事は、その選択がいかに重要であるかを、Transformers.jsライブラリを使ってNode.js環境でテキストから埋め込みを生成する過程を具体的に追いながら解説する。
Transformers.jsのようなライブラリでは、pipeline関数を使ってテキストを埋め込みに変換できる。この際、モデルはまずテキストを「トークン」と呼ばれる小さな単位(単語やサブワード)に分割する。例えば「This is a simple test.」という文は、いくつかのトークンに分けられる。そして、モデルはこれらのトークン一つ一つに対して、それぞれの意味を表す数値ベクトル(「隠れ状態」や「トークン埋め込み」と呼ばれる)を生成する。この段階では、文全体を表す一つのベクトルではなく、複数のトークンベクトルが返される。
モデルから出力されるトークンベクトルを扱うには、いくつかの注意点がある。まず、「トークン化」の過程で、元の単語数よりも多くのトークンが生成される場合がある。これは、モデルが知らない単語をさらに小さなサブワードに分解するためだ。また、テキストの両端には「特殊トークン」が追加されることが多く、特に文の先頭には「[CLS]」という特別なトークンが挿入される。これは、一部のモデルが文全体の意味をこの[CLS]トークンに集約するように学習されているためである。
さらに、複数の文をまとめて処理する際には、全ての文の長さを揃えるために短い文には「パディング」と呼ばれる意味のない情報が追加される。このパディング部分が計算に影響を与えないように、「アテンションマスク」という情報が生成される。アテンションマスクは、どの部分が実際のテキストで、どの部分がパディングであるかをモデルに伝える役割を果たす。
モデルが生成するトークンごとのベクトルは、そのままだと複数のベクトルになるため、通常はこれらをまとめて文全体を表す一つのベクトルに集約する必要がある。この集約プロセスを「プーリング」と呼ぶ。プーリングには主に二つの方法がある。
一つは「平均プーリング(Mean Pooling)」だ。これは、アテンションマスクが示す実際のテキスト部分のトークンベクトルだけを平均して、一つの文ベクトルを生成する方法である。パディング部分を平均に含めてしまうと、短い文の埋め込みが意図せず長さに依存してしまうため、アテンションマスクが非常に重要になる。
もう一つは「CLSプーリング(CLS Pooling)」だ。これは、文の先頭に位置する特殊トークンである[CLS]トークンのベクトルを、そのまま文全体の埋め込みとして利用する方法である。一部のモデルは、この[CLS]トークンが文全体の意味をよく表すように学習されている。
プーリングの後には、「正規化(Normalization)」というステップが適用されることが多い。これは、生成された埋め込みベクトルの「長さ」を1に揃える処理である。正規化は、ベクトルの方向を変えずに長さを調整する。
なぜこれらのプーリングや正規化が重要なのかというと、これらが検索結果に大きな影響を与えるからである。 例えば、埋め込みベクトル間の類似度を測る方法として「コサイン類似度」と「ドット積(内積)」がある。コサイン類似度はベクトルの方向だけを見て類似度を測るため、正規化してもしなくても結果は変わらない。しかし、ドット積はベクトルの方向だけでなく、その長さも考慮して類似度を測る。そのため、正規化せずにドット積を使うと、単にベクトルの長さが長いというだけで、実際の意味的な関連性が低い文書が上位にランクされる可能性が出てくる。多くのベクトルデータベースでは、正規化されたベクトルに対してはドット積を推奨している。これは、正規化によってドット積とコサイン類似度が同じになり、計算効率が良いからである。
最も危険な問題は、埋め込みの生成時と検索時でプーリングや正規化の設定が「不一致」になることである。例えば、文書の埋め込みを平均プーリングと正規化で作成したにもかかわらず、検索クエリの埋め込みをCLSプーリングで作成したり、正規化をせずに作成したりすると、システムは何もエラーを吐かずに動き続ける。しかし、実際には異なる方法で生成されたベクトル同士が比較されるため、検索の精度は「サイレントに」低下してしまう。開発者はこの問題に気づかないまま、誤った検索結果に基づいたAIアプリケーションを運用してしまう可能性がある。
この問題は特に、異なるライブラリやフレームワーク、あるいは異なる言語間で埋め込みを共有する際に発生しやすい。たとえば、特定のモデル(BGEモデルなど)はCLSプーリングを推奨しているにもかかわらず、別のライブラリでそのモデルを使う際に誤って平均プーリングがデフォルトで適用されてしまうといったケースがある。Cloudflare Workers AIのドキュメントでも、CLSプーリングと平均プーリングで生成された埋め込みは互換性がないと明記されている。
このような不一致を防ぐためには、埋め込みを生成する際の設定を、その埋め込みベクトルと一緒にメタデータとして保存することが非常に重要だ。具体的には、使用したモデル名、データ型、プーリング方法、正規化の有無、さらにはクエリプレフィックス(特定のクエリに接頭辞を追加する設定)などの情報をJSON形式でまとめて、埋め込みを保存するデータベースのメタデータとして記録する。そして、検索時にクエリの埋め込みを作成する際の設定と、保存されている埋め込みのメタデータが一致するかを厳密にチェックする仕組みを導入する。もし一致しなければ、エラーを発生させて、不整合を早期に発見できるようにする。
また、モデルのデータ型(例えば、fp32, fp16, q8など)も埋め込みの特性に影響を与えるため、これもメタデータに含めるべきだ。一部のモデルでは、最終トークンや文末トークンの埋め込みを使う「last_tokenプーリング」という方法もあるが、この場合もトークン化の際にパディングがどの位置に追加されるか(先頭か末尾か)によって結果が変わるため、注意が必要となる。
埋め込みは単なる数値のリストではない。その背後にある生成方法、特にプーリングと正規化の選択が、AIアプリケーションの性能と信頼性を大きく左右する。これらの設定の一貫性を保ち、明確に管理することが、堅牢なAIシステムを構築するための鍵となる。