Closed13

高スループット・低遅延でEmbeddings・Rerankなどを提供できる「Infinity」を試す

kun432kun432

GitHubレポジトリのREADMEからポイントをピックアップ

Infinity ♾️

Infinityは、テキスト埋め込み、再ランキングモデル、clip、clapおよびcolpali向けの高スループット・低遅延RESTAPIです。InfinityはMITライセンスの下で開発されています。

なぜInfinity

  • HuggingFaceの任意のモデルをデプロイ: HuggingFaceから任意のembedding、reranking、clipおよびsentence-transformerモデルをデプロイ
  • 高速推論バックエンド: 推論サーバーはPyTorchoptimum (ONNX/TensorRT)CTranslate2上に構築されており、FlashAttentionを使用してNVIDIA CUDAAMD ROCMCPUAWS INF2APPLE MPSアクセラレータを最大限に活用します。Infinityは動的バッチ処理とワーカースレッドでの専用トークン化を使用します。
  • マルチモーダルおよびマルチモデル: 複数のモデルを組み合わせて使用できます。Infinityがそれらを調整します。
  • テスト済みの実装: ユニットテストとエンドツーエンドテスト済み。Infinityによる埋め込みは正確に埋め込まれます。APIユーザーは無限に、そしてそれ以上に埋め込みを作成できます。
  • 使いやすさ: FastAPI上に構築。Infinity CLI v2は環境変数または引数を介してすべての引数を起動できます。OpenAIのAPIの仕様に準拠したOpenAPI。https://michaelfeil.github.io/infinityでドキュメントを確認して、始め方を学んでください。

Infinityがサポートするタスクとモデル

Infinityはembeddings、reranking、および関連するRAGタスクの機能をほとんどサポートすることを目指しています。以下のInfinityはGithub CIで15+のアーキテクチャとすべての以下のケースをテストしています。

テキスト埋め込み

テキスト埋め込みはテキスト文字列の関連性を測定します。埋め込みは検索、クラスタリング、レコメンデーションのために使用されます。
OpenAIのテキスト埋め込みのプライベートデプロイバージョンと考えてください。https://platform.openai.com/docs/guides/embeddings

テスト済みの埋め込みモデル:

その他のモデル:

リランキング

クエリと文書のリストが与えられると、リランキングはクエリに対して最も意味的に関連のある文書を、最も少ないものまでインデックス付けします。
https://docs.cohere.com/reference/rerankのローカルにデプロイされたバージョンのようなものです

テスト済みの再ランキングモデル:

その他の再ランキングモデル:

マルチモーダルおよびクロスモーダル - 画像および音声埋め込み

画像<->テキストまたは画像<->音声検索を可能にする特殊な埋め込みモデル。
通常、これらのモデルはテキスト<->テキスト、テキスト<->その他、およびその他<->その他の検索を可能にし、クロスモーダルに移行するとトレードオフのある精度を提供します。

画像<->テキストモデルは、例えばフォトギャラリー検索に使用でき、ユーザーはキーワードを入力して写真を検索したり、写真を使用して関連画像を見つけたりできます。
音声<->テキストモデルはあまり一般的ではなく、テキスト説明に基づいて音楽曲を見つけたり、関連する音楽曲を見つけたりするために使用できます。

テスト済みの画像<->テキストモデル:

テスト済みの音声<->テキストモデル:

  • LAION製Clapモデル
  • 限られた数のオープンソース組織がこれらのモデルをトレーニングしています
    • 注:音声データのサンプリングレートはモデルと一致する必要があります *

サポートされていないもの:

  • nomic-ai/nomic-embed-vision-v1.5のような単純なビジョンモデル

ColBertスタイルのlate-interaction埋め込み

ColBert埋め込みは特別なプーリング方法を実行せず、生のトークン埋め込みを返します。
トークン埋め込みはVectorDB(Qdrant / Vespa)でMaxSimメトリックでスコアリングされます

RestAPI経由で使用する場合、後期インタラクション埋め込みはbase64エンコーディングを介して最適に転送できます。
サンプルノートブック:https://colab.research.google.com/drive/14FqLc0N_z92_VgL_zygWV5pJZkaskyK7?usp=sharing

テスト済みのcolbertモデル:

ColPali-スタイルのlate-interaction画像<->テキスト埋め込み

ColBertと同様の使用法ですが、テキストのみではなく画像<->テキスト間のスキャンに使用します。

RestAPI経由で使用する場合、後期インタラクション埋め込みはbase64エンコーディングを介して最適に転送できます。
サンプルノートブック:https://colab.research.google.com/drive/14FqLc0N_z92_VgL_zygWV5pJZkaskyK7?usp=sharing

テスト済みのColPali/ColQwenモデル:

テキスト分類

BERTスタイルのマルチラベルテキスト分類。異なるカテゴリに分類します。

テスト済みモデル:

kun432kun432

Getting Started

https://github.com/michaelfeil/infinity/?tab=readme-ov-file#getting-started

今回はローカルのMacで試す。利用方法はざっと見た感じ、以下がある様子。

  • Infinity CLI
  • Docker
  • Python API
  • Pythonクライアント

とりあえずCLIからまずは初めてみる。

CLIのインストール

CLIはPythonパッケージをインストールする。ということで、uvで環境を作成。

uv init -p 3.12.9 infinity-work && cd infinity-work

CLIのパッケージをインストール

uv add "infinity-emb[all]"
出力
 + infinity-emb==0.0.76

これでinfinity_embというコマンドが使えるようになる。まずはUsage。

uv run infinity_emb v2 --help
出力
INFO     2025-04-18 02:36:38,754 datasets INFO: PyTorch version 2.6.0           config.py:54
         available.

 Usage: infinity_emb v2 [OPTIONS]

 Infinity API ♾️  cli v2. MIT License. Copyright (c) 2023-now Michael Feil
 Multiple Model CLI Playbook:
 - 1. cli options can be overloaded i.e. `v2 --model-id model/id1 --model-id model/id2
 --batch-size 8 --batch-size 4`
 - 2. or adapt the defaults by setting ENV Variables separated by `;`:
 INFINITY_MODEL_ID="model/id1;model/id2;" && INFINITY_BATCH_SIZE="8;4;"
 - 3. single items are broadcasted to `--model-id` length, making `v2 --model-id model/id1
 --model-id/id2 --batch-size 8` both models have batch-size 8.

╭─ Options ────────────────────────────────────────────────────────────────────────────────╮
│ --model-id                                     TEXT                 Huggingface model    │
│                                                                     repo id. Subset of   │
│                                                                     possible models:     │
│                                                                     https://huggingface… │
│                                                                     [env var:            │
│                                                                     `INFINITY_MODEL_ID`] │
│                                                                     [default:            │
│                                                                     michaelfeil/bge-sma… │
│ --served-model-name                            TEXT                 the nickname for the │
│                                                                     API, under which the │
│                                                                     model_id can be      │
│                                                                     selected             │
│                                                                     [env var:            │
│                                                                     `INFINITY_SERVED_MO… │
│ --batch-size                                   INTEGER              maximum batch size   │
│                                                                     for inference        │
│                                                                     [env var:            │
│                                                                     `INFINITY_BATCH_SIZ… │
│                                                                     [default: 32]        │
│ --revision                                     TEXT                 huggingface  model   │
│                                                                     repo revision.       │
│                                                                     [env var:            │
│                                                                     `INFINITY_REVISION`] │
│ --trust-remote-code     --no-trust-remote-…                         if potential remote  │
│                                                                     modeling code from   │
│                                                                     huggingface repo is  │
│                                                                     trusted.             │
│                                                                     [env var:            │
│                                                                     `INFINITY_TRUST_REM… │
│                                                                     [default:            │
│                                                                     trust-remote-code]   │
│ --engine                                       [torch|ctranslate2|  Which backend to     │
│                                                optimum|neuron|debu  use. `torch` uses    │
│                                                gengine]             Pytorch GPU/CPU,     │
│                                                                     optimum uses ONNX on │
│                                                                     GPU/CPU/NVIDIA-Tens… │
│                                                                     `CTranslate2` uses   │
│                                                                     torch+ctranslate2 on │
│                                                                     CPU/GPU.             │
│                                                                     [env var:            │
│                                                                     `INFINITY_ENGINE`]   │
│                                                                     [default: torch]     │
│ --model-warmup          --no-model-warmup                           if model should be   │
│                                                                     warmed up after      │
│                                                                     startup, and before  │
│                                                                     ready.               │
│                                                                     [env var:            │
│                                                                     `INFINITY_MODEL_WAR… │
│                                                                     [default:            │
│                                                                     model-warmup]        │
│ --vector-disk-cache     --no-vector-disk-c…                         If                   │
│                                                                     hash(request)/resul… │
│                                                                     should be cached to  │
│                                                                     SQLite for latency   │
│                                                                     improvement.         │
│                                                                     [env var:            │
│                                                                     `INFINITY_VECTOR_DI… │
│                                                                     [default:            │
│                                                                     vector-disk-cache]   │
│ --device                                       [cpu|cuda|mps|tenso  device to use for    │
│                                                rrt|auto]            computing the model  │
│                                                                     forward pass.        │
│                                                                     [env var:            │
│                                                                     `INFINITY_DEVICE`]   │
│                                                                     [default: auto]      │
│ --device-id                                    TEXT                 device id defines    │
│                                                                     the model placement. │
│                                                                     e.g. `0,1` will      │
│                                                                     place the model on   │
│                                                                     MPS/CUDA/GPU 0 and 1 │
│                                                                     each                 │
│                                                                     [env var:            │
│                                                                     `INFINITY_DEVICE_ID… │
│ --lengths-via-token…    --no-lengths-via-t…                         if True, returned    │
│                                                                     tokens is based on   │
│                                                                     actual tokenizer     │
│                                                                     count. If false,     │
│                                                                     uses len(input) as   │
│                                                                     proxy.               │
│                                                                     [env var:            │
│                                                                     `INFINITY_LENGTHS_V… │
│                                                                     [default:            │
│                                                                     lengths-via-tokeniz… │
│ --dtype                                        [float32|float16|bf  dtype for the model  │
│                                                loat16|int8|fp8|aut  weights.             │
│                                                o]                   [env var:            │
│                                                                     `INFINITY_DTYPE`]    │
│                                                                     [default: auto]      │
│ --embedding-dtype                              [float32|int8|uint8  dtype post-forward   │
│                                                |binary|ubinary]     pass. If !=          │
│                                                                     `float32`, using     │
│                                                                     Post-Forward Static  │
│                                                                     quantization.        │
│                                                                     [env var:            │
│                                                                     `INFINITY_EMBEDDING… │
│                                                                     [default: float32]   │
│ --pooling-method                               [mean|cls|auto]      overwrite the        │
│                                                                     pooling method if    │
│                                                                     inferred             │
│                                                                     incorrectly.         │
│                                                                     [env var:            │
│                                                                     `INFINITY_POOLING_M… │
│                                                                     [default: auto]      │
│ --compile               --no-compile                                Enable usage of      │
│                                                                     `torch.compile(dyna… │
│                                                                     if engine relies on  │
│                                                                     it.                  │
│                                                                     [env var:            │
│                                                                     `INFINITY_COMPILE`]  │
│                                                                     [default: compile]   │
│ --bettertransformer     --no-bettertransfo…                         Enables varlen       │
│                                                                     flash-attention-2    │
│                                                                     via the              │
│                                                                     `BetterTransformer`  │
│                                                                     implementation. If   │
│                                                                     available for this   │
│                                                                     model.               │
│                                                                     [env var:            │
│                                                                     `INFINITY_BETTERTRA… │
│                                                                     [default:            │
│                                                                     bettertransformer]   │
│ --preload-only          --no-preload-only                           If true, only        │
│                                                                     downloads models and │
│                                                                     verifies setup, then │
│                                                                     exit. Recommended    │
│                                                                     for pre-caching the  │
│                                                                     download in a        │
│                                                                     Dockerfile.          │
│                                                                     [env var:            │
│                                                                     `INFINITY_PRELOAD_O… │
│                                                                     [default:            │
│                                                                     no-preload-only]     │
│ --host                                         TEXT                 host for the FastAPI │
│                                                                     uvicorn server       │
│                                                                     [env var:            │
│                                                                     `INFINITY_HOST`]     │
│                                                                     [default: 0.0.0.0]   │
│ --port                                         INTEGER              port for the FastAPI │
│                                                                     uvicorn server       │
│                                                                     [env var:            │
│                                                                     `INFINITY_PORT`]     │
│                                                                     [default: 7997]      │
│ --url-prefix                                   TEXT                 prefix for all       │
│                                                                     routes of the        │
│                                                                     FastAPI uvicorn      │
│                                                                     server. Useful if    │
│                                                                     you run behind a     │
│                                                                     proxy / cascaded     │
│                                                                     API.                 │
│                                                                     [env var:            │
│                                                                     `INFINITY_URL_PREFI… │
│ --redirect-slash                               TEXT                 where to redirect    │
│                                                                     `/` requests to.     │
│                                                                     [env var:            │
│                                                                     `INFINITY_REDIRECT_… │
│                                                                     [default: /docs]     │
│ --log-level                                    [critical|error|war  console log level.   │
│                                                ning|info|debug|tra  [env var:            │
│                                                ce]                  `INFINITY_LOG_LEVEL… │
│                                                                     [default: info]      │
│ --permissive-cors       --no-permissive-co…                         whether to allow     │
│                                                                     permissive cors.     │
│                                                                     [env var:            │
│                                                                     `INFINITY_PERMISSIV… │
│                                                                     [default:            │
│                                                                     no-permissive-cors]  │
│ --api-key                                      TEXT                 api_key used for     │
│                                                                     authentication       │
│                                                                     headers.             │
│                                                                     [env var:            │
│                                                                     `INFINITY_API_KEY`]  │
│ --proxy-root-path                              TEXT                 Proxy prefix for the │
│                                                                     application. See:    │
│                                                                     https://fastapi.tia… │
│                                                                     [env var:            │
│                                                                     `INFINITY_PROXY_ROO… │
│ --help                                                              Show this message    │
│                                                                     and exit.            │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
kun432kun432

APIサーバの起動

では試しに intfloat/multilingual-e5-small を使ってEmbeddingのAPIを建ててみる

https://huggingface.co/intfloat/multilingual-e5-small

uv run infinity_emb v2 --model-id intfloat/multilingual-e5-small

以下のように起動してくるが、多分裏でモデルのダウンロード+ロードをしていると思うのでちょっと時間がかかる。

出力
INFO     2025-04-18 02:45:16,447 datasets INFO: PyTorch version 2.6.0 available.                       config.py:54
INFO:     Started server process [89325]
INFO:     Waiting for application startup.
INFO     2025-04-18 02:45:17,099 infinity_emb INFO: Creating 1engines:                        infinity_server.py:84
         engines=['intfloat/multilingual-e5-small']
INFO     2025-04-18 02:45:17,100 infinity_emb INFO: Anonymized telemetry can be disabled via        telemetry.py:30
         environment variable `DO_NOT_TRACK=1`.
INFO     2025-04-18 02:45:17,102 infinity_emb INFO: model=`intfloat/multilingual-e5-small`       select_model.py:64
         selected, using engine=`torch` and device=`None`
INFO     2025-04-18 02:45:17,765 sentence_transformers.SentenceTransformer INFO: Load    SentenceTransformer.py:218
         pretrained SentenceTransformer: intfloat/multilingual-e5-small

しばらく待ってると、とりあえずAPIが起動したみたい。Warningにもある通り、Apple Siliconだと本来の性能は出ないかも。

出力
WARNING  2025-04-18 02:46:18,352 infinity_emb WARNING: BetterTransformer is not available for    acceleration.py:56
         MPS device. Continue without bettertransformer modeling code.
INFO     2025-04-18 02:46:20,837 infinity_emb INFO: Getting timings for batch_size=32 and avg    select_model.py:97
         tokens per sentence=2
                 0.37     ms tokenization
                 12.12    ms inference
                 0.06     ms post-processing
                 12.55    ms total
         embeddings/sec: 2549.07
INFO     2025-04-18 02:46:22,211 infinity_emb INFO: Getting timings for batch_size=32 and avg   select_model.py:103
         tokens per sentence=512
                 11.23    ms tokenization
                 472.62   ms inference
                 0.11     ms post-processing
                 483.96   ms total
         embeddings/sec: 66.12
INFO     2025-04-18 02:46:22,212 infinity_emb INFO: model warmed up, between 66.12-2549.07      select_model.py:104
         embeddings/sec at batch_size=32
INFO     2025-04-18 02:46:22,213 infinity_emb INFO: creating batching engine                   batch_handler.py:456
INFO     2025-04-18 02:46:22,214 infinity_emb INFO: ready to batch requests.                   batch_handler.py:525
INFO     2025-04-18 02:46:22,214 infinity_emb INFO:                                           infinity_server.py:98

         ♾️  Infinity - Embedding Inference Server
         MIT License; Copyright (c) 2023-now
         Infinity OSS-Project: github.com/michaelfeil.infinity
         Maintained by @michaelfeil @wirthual
         Version 0.0.76

         Open the Docs via Swagger UI:
         http://0.0.0.0:7997/docs

         Access all deployed models via 'GET':
         curl http://0.0.0.0:7997/models

         Visit the docs for more information:
         https://michaelfeil.github.io/infinity


INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:7997 (Press CTRL+C to quit)

APIの利用

とりあえずcurlでモデルの一覧を取得してみる。

curl http://127.0.0.1:7997/models | jq -r .
出力
{
  "data": [
    {
      "id": "intfloat/multilingual-e5-small",
      "stats": {
        "queue_fraction": 0.0,
        "queue_absolute": 0,
        "results_pending": 0,
        "batch_size": 32
      },
      "object": "model",
      "owned_by": "infinity",
      "created": 1744912188,
      "backend": "torch",
      "capabilities": [
        "embed"
      ]
    }
  ],
  "object": "list"
}

http://localhost:7997/docsで確認する限りはOpenAI互換っぽい。

テキストのEmbeddingsを生成してみる。

curl -X POST http://127.0.0.1:7997/embeddings \
    -H "Content-Type: application/json" \
    -d '{
      "input": ["おはようございます"]
    }' | jq -r .
出力
{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "embedding": [
        0.05275781825184822,
        -0.022957203909754753,
        -0.06776507198810577,
        -0.05877435579895973,
        0.08515320718288422,
        (snip)
        -0.0194106325507164,
        0.08340593427419662,
        0.07239586114883423,
        0.04821839928627014,
        0.03272527828812599
      ],
      "index": 0
    }
  ],
  "model": "intfloat/multilingual-e5-small",
  "usage": {
    "prompt_tokens": 9,
    "total_tokens": 9
  },
  "id": "infinity-8c03815c-0b5a-4c42-9845-a46e2df1a96a",
  "created": 1744912650
}

複数のモデルを同時にホストできるらしいので、出たばっかりの以下の2モデルで試してみる。

https://huggingface.co/cl-nagoya/ruri-v3-30m

https://huggingface.co/pfnet/plamo-embedding-1b

CLIを止めて、以下のようにして実行。

uv run infinity_emb v2 \
    --model-id pfnet/plamo-embedding-1b\
    --model-id cl-nagoya/ruri-v3-30m
出力
INFO     2025-04-18 03:08:22,492 infinity_emb INFO: Creating 2engines:                        infinity_server.py:84
         engines=['pfnet/plamo-embedding-1b', 'cl-nagoya/ruri-v3-30m']
INFO     2025-04-18 03:08:22,492 infinity_emb INFO: Anonymized telemetry can be disabled via        telemetry.py:30
         environment variable `DO_NOT_TRACK=1`.
INFO     2025-04-18 03:08:22,494 infinity_emb INFO: model=`pfnet/plamo-embedding-1b` selected,   select_model.py:64
         using engine=`torch` and device=`None`
INFO     2025-04-18 03:08:23,667 sentence_transformers.SentenceTransformer INFO: Load    SentenceTransformer.py:218
         pretrained SentenceTransformer: pfnet/plamo-embedding-1b
WARNING  2025-04-18 03:08:23,860 sentence_transformers.SentenceTransformer WARNING: No  SentenceTransformer.py:1524
         sentence-transformers model found with name pfnet/plamo-embedding-1b. Creating
         a new one with mean pooling.
(snip)
ModuleNotFoundError: No module named 'sentencepiece'

ERROR:    Application startup failed. Exiting.

pfnet/plamo-embedding-1b の方は sentencepiece が必要みたい。ということで追加。

uv add sentencepiece

が、残念。

出力
WARNING  2025-04-18 03:10:45,420 sentence_transformers.SentenceTransformer WARNING: No  SentenceTransformer.py:1524
         sentence-transformers model found with name pfnet/plamo-embedding-1b. Creating
         a new one with mean pooling.
Asking to truncate to max_length but no maximum length is provided and the model has no predefined maximum length. Default to no truncation.
(snip)
TypeError: 'NoneType' object is not iterable

ERROR:    Application startup failed. Exiting.

とりあえず cl-nagoya/ruri-v3-30m の別のサイズで。

uv run infinity_emb v2 \
    --model-id cl-nagoya/ruri-v3-30m \
    --model-id cl-nagoya/ruri-v3-130m

今度はうまく行った。

出力
(snip)
INFO     2025-04-18 03:13:34,908 infinity_emb INFO: Creating 2engines:                        infinity_server.py:84
         engines=['cl-nagoya/ruri-v3-30m', 'cl-nagoya/ruri-v3-130m']
(snip)
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:7997 (Press CTRL+C to quit)

ではcurlで。

curl http://127.0.0.1:7997/models | jq -r .
出力
{
  "data": [
    {
      "id": "cl-nagoya/ruri-v3-30m",
      "stats": {
        "queue_fraction": 0.0,
        "queue_absolute": 0,
        "results_pending": 0,
        "batch_size": 32
      },
      "object": "model",
      "owned_by": "infinity",
      "created": 1744913768,
      "backend": "torch",
      "capabilities": [
        "embed"
      ]
    },
    {
      "id": "cl-nagoya/ruri-v3-130m",
      "stats": {
        "queue_fraction": 0.0,
        "queue_absolute": 0,
        "results_pending": 0,
        "batch_size": 32
      },
      "object": "model",
      "owned_by": "infinity",
      "created": 1744913768,
      "backend": "torch",
      "capabilities": [
        "embed"
      ]
    }
  ],
  "object": "list"
}

それぞれのモデルがリストアップされている。

テキストのEmbeddingsを生成。

curl -X POST http://127.0.0.1:7997/embeddings \
    -H "Content-Type: application/json" \
    -d '{
      "input": ["おはようございます"]
    }' | jq -r .

複数のモデルをホストする場合はモデル名が必要になる。

出力
{
  "error": {
    "message": "Invalid model: Engine for model name `default/not-specified` not found. Available model names are ['cl-nagoya/ruri-v3-30m', 'cl-nagoya/ruri-v3-130m']",
    "type": null,
    "param": null,
    "code": 400
  }
}
curl -X POST http://127.0.0.1:7997/embeddings \
    -H "Content-Type: application/json" \
    -d '{
      "model": "cl-nagoya/ruri-v3-30m",
      "input": ["おはようございます"]
    }' | jq -r .
出力
{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "embedding": [
        -0.045119766145944595,
        -0.003127568867057562,
        -0.049851853400468826,
        0.036303646862506866,
        0.07206321507692337,
        (snip)
        0.03493461757898331,
        -0.0219422560185194,
        -0.06379549950361252,
        0.0026309911627322435,
        -0.0014501155819743872
      ],
      "index": 0
    }
  ],
  "model": "cl-nagoya/ruri-v3-30m",
  "usage": {
    "prompt_tokens": 9,
    "total_tokens": 9
  },
  "id": "infinity-a73c0195-5b63-4804-93a4-cc900e12c9e9",
  "created": 1744914017
}
curl -X POST http://127.0.0.1:7997/embeddings \
    -H "Content-Type: application/json" \
    -d '{
      "model": "cl-nagoya/ruri-v3-130m",
      "input": ["おはようございます"]
    }' | jq -r .
出力
{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "embedding": [
        -0.004650148097425699,
        0.012932839803397655,
        -0.009189369156956673,
        0.018922852352261543,
        -0.008427134715020657,
        (snip)
        -0.008729965426027775,
        0.01514500007033348,
        0.03178422525525093,
        -0.02900286205112934,
        -0.01114029623568058
      ],
      "index": 0
    }
  ],
  "model": "cl-nagoya/ruri-v3-130m",
  "usage": {
    "prompt_tokens": 9,
    "total_tokens": 9
  },
  "id": "infinity-0c07d3de-9260-4e13-b967-04c86dc0d241",
  "created": 1744914025
}
kun432kun432

余談だが、この「Infinity」では動かなかった上記の3モデル、冒頭の「STAPI」でざっと確認した感じだと動いたので、上記モデルで簡単にAPI建てたい場合には一度お試しあれ。

kun432kun432

Embedding以外についても、いくつか試してみる。

リランキング

以下で試してみる。

https://huggingface.co/cl-nagoya/ruri-v3-reranker-310m

リランキングモデルの場合でもCLIの使い方は変わらない。

uv run infinity_emb v2 \
    --model-id cl-nagoya/ruri-v3-reranker-310m

curlはちょっと面倒なので、Pythonで。

import requests
import json

url = "http://127.0.0.1:7997/rerank"

payload = {
    "query": "瑠璃色はどんな色?",
    "documents": [
        (
            "ワシ、タカ、ハゲワシ、ハヤブサ、コンドル、フクロウが代表的である。"
            "これらの猛禽類はリンネ前後の時代(17~18世紀)には鷲類・鷹類・隼類及び"
            "梟類に分類された。ちなみにリンネは狩りをする鳥を単一の目(もく)にまとめ、"
            "vultur(コンドル、ハゲワシ)、falco(ワシ、タカ、ハヤブサなど)、strix"
            "(フクロウ)、lanius(モズ)の4属を含めている。"
        ),
        (
            "瑠璃、または琉璃(るり)は、仏教の七宝の一つ。サンスクリットの vaiḍūrya"
            " またはそのプラークリット形の音訳である。金緑石のこととも、ラピスラズリで"
            "あるともいう[1]。"
        ),
        (
            "瑠璃色(るりいろ)は、紫みを帯びた濃い青。名は、半貴石の瑠璃(ラピスラズリ"
            "、英: lapis lazuli)による。JIS慣用色名では「こい紫みの青」(略号 dp-pB)"
            "と定義している[1][2]。"
        )
    ],
    "return_documents": True,
}

response = requests.post(url, json=payload)

if response.status_code == 200:
    print(json.dumps(response.json(), indent=4, ensure_ascii=False))
else:
    print(f"エラー: {response.status_code}")
    print(response.text)

実行結果

出力
{
    "object": "rerank",
    "results": [
        {
            "relevance_score": 0.9999963045120239,
            "index": 2,
            "document": "瑠璃色(るりいろ)は、紫みを帯びた濃い青。名は、半貴石の瑠璃(ラピスラズリ、英: lapis lazuli)による。JIS慣用色名では「こい紫みの青」(略号 dp-pB)と定義している[1][2]。"
        },
        {
            "relevance_score": 0.08069775253534317,
            "index": 1,
            "document": "瑠璃、または琉璃(るり)は、仏教の七宝の一つ。サンスクリットの vaiḍūrya またはそのプラークリット形の音訳である。金緑石のこととも、ラピスラズリであるともいう[1]。"
        },
        {
            "relevance_score": 0.00035695568658411503,
            "index": 0,
            "document": "ワシ、タカ、ハゲワシ、ハヤブサ、コンドル、フクロウが代表的である。これらの猛禽類はリンネ前後の時代(17~18世紀)には鷲類・鷹類・隼類及び梟類に分類された。ちなみにリンネは狩りをする鳥を単一の目(もく)にまとめ、vultur(コンドル、ハゲワシ)、falco(ワシ、タカ、ハヤブサなど)、strix(フクロウ)、lanius(モズ)の4属を含めている。"
        }
    ],
    "model": "cl-nagoya/ruri-v3-reranker-310m",
    "usage": {
        "prompt_tokens": 390,
        "total_tokens": 390
    },
    "id": "infinity-d81faa3f-aa4a-403a-bcdf-5d617059aeb2",
    "created": 1744970505
}

日本語のリランキングモデルはそんなに多くないけど、いくつか確認した限り以下は動いた。

https://huggingface.co/hotchpotch/japanese-bge-reranker-v2-m3-v1

以下は動かなかった

https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-xsmall-v1

https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-small-v1

https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-base-v1

https://huggingface.co/hotchpotch/japanese-reranker-cross-encoder-large-v1

kun432kun432

マルチ/クロスモーダルのEmbeddingや、ColBert/ColPali、テキスト分類モデルなども同様に使える様子。実際に動くかどうかは試してみないとわからないが、興味があればお試しあれ。

kun432kun432

Docker

公式のオススメはDockerイメージを使う方法の様子。

Dockerイメージは、GPU・CPUなど複数のイメージが用意されているようで、このあたりはドキュメントを確認して、各自の環境やユースケースに合わせて選択されたし・・・

port=7997
model1=cl-nagoya/ruri-v3-30m
model2=cl-nagoya/ruri-v3-reranker-310m
volume=$PWD/data

docker run --rm -it \
    -v $volume:/app/.cache \
    -p $port:$port \
    michaelf34/infinity:latest \
    v2 \
    --model-id $model1 \
    --model-id $model2 \
    --port $port

なのだが、どうもモデルによってはダウンロードが上手くいかないことがあった。例えば上の例だと cl-nagoya/ruri-v3-30m はすぐにダウンロードされるのだが、cl-nagoya/ruri-v3-reranker-310m はなかなかダウンロードされない。ダウンロードされるディレクトリをウォッチしてても全然変更されない。CPUイメージも試してみたけどこちらも上手くいかず・・・

Ubuntuサーバでも試してみたけど、そちらだと問題なかったので、自分のMacの環境に問題があるのかも知れない。

とりあえずUbuntuサーバで起動したDockerコンテナで試してみる。

embedding

embed.py
import requests
import json

url = "http://192.X.X.X:7997/embeddings"

payload = {
    "model": "cl-nagoya/ruri-v3-30m",
    "input": ["瑠璃色はどんな色?"]
}

response = requests.post(url, json=payload)

if response.status_code == 200:
    print(json.dumps(response.json(), indent=4, ensure_ascii=False))
else:
    print(f"エラー: {response.status_code}")
    print(response.text)
出力
{
    "object": "list",
    "data": [
        {
            "object": "embedding",
            "embedding": [
                0.025892337784171104,
                -0.0017613227246329188,
                0.01595025323331356,
                0.011587179265916348,
                0.07839227467775345,
                (snip)
                -0.0017702634213492274,
                -0.021028583869338036,
                -0.015378046780824661,
                -0.023746564984321594,
                0.007545971777290106
            ],
            "index": 0
        }
    ],
    "model": "cl-nagoya/ruri-v3-30m",
    "usage": {
        "prompt_tokens": 9,
        "total_tokens": 9
    },
    "id": "infinity-a8343ced-6ad5-4550-bd33-cb25a4faf0d2",
    "created": 1744977250
}

reranking

import requests
import json

url = "http://rtx4090.local:7997/rerank"

payload = {
    "model": "cl-nagoya/ruri-v3-reranker-310m",
    "query": "瑠璃色はどんな色?",
    "documents": [
        (
            "ワシ、タカ、ハゲワシ、ハヤブサ、コンドル、フクロウが代表的である。"
            "これらの猛禽類はリンネ前後の時代(17~18世紀)には鷲類・鷹類・隼類及び"
            "梟類に分類された。ちなみにリンネは狩りをする鳥を単一の目(もく)にまとめ、"
            "vultur(コンドル、ハゲワシ)、falco(ワシ、タカ、ハヤブサなど)、strix"
            "(フクロウ)、lanius(モズ)の4属を含めている。"
        ),
        (
            "瑠璃、または琉璃(るり)は、仏教の七宝の一つ。サンスクリットの vaiḍūrya"
            " またはそのプラークリット形の音訳である。金緑石のこととも、ラピスラズリで"
            "あるともいう[1]。"
        ),
        (
            "瑠璃色(るりいろ)は、紫みを帯びた濃い青。名は、半貴石の瑠璃(ラピスラズリ"
            "、英: lapis lazuli)による。JIS慣用色名では「こい紫みの青」(略号 dp-pB)"
            "と定義している[1][2]。"
        )
    ],
    "return_documents": True,
}

response = requests.post(url, json=payload)

if response.status_code == 200:
    print(json.dumps(response.json(), indent=4, ensure_ascii=False))
else:
    print(f"エラー: {response.status_code}")
    print(response.text)
出力
{
    "object": "rerank",
    "results": [
        {
            "relevance_score": 0.9999963045120239,
            "index": 2,
            "document": "瑠璃色(るりいろ)は、紫みを帯びた濃い青。名は、半貴石の瑠璃(ラピスラズリ、英: lapis lazuli)による。JIS慣用色名では「こい紫みの青」(略号 dp-pB)と定義している[1][2]。"
        },
        {
            "relevance_score": 0.07807817310094833,
            "index": 1,
            "document": "瑠璃、または琉璃(るり)は、仏教の七宝の一つ。サンスクリットの vaiḍūrya またはそのプラークリット形の音訳である。金緑石のこととも、ラピスラズリであるともいう[1]。"
        },
        {
            "relevance_score": 0.000368297885870561,
            "index": 0,
            "document": "ワシ、タカ、ハゲワシ、ハヤブサ、コンドル、フクロウが代表的である。これらの猛禽類はリンネ前後の時代(17~18世紀)には鷲類・鷹類・隼類及び梟類に分類された。ちなみにリンネは狩りをする鳥を単一の目(もく)にまとめ、vultur(コンドル、ハゲワシ)、falco(ワシ、タカ、ハヤブサなど)、strix(フクロウ)、lanius(モズ)の4属を含めている。"
        }
    ],
    "model": "cl-nagoya/ruri-v3-reranker-310m",
    "usage": {
        "prompt_tokens": 390,
        "total_tokens": 390
    },
    "id": "infinity-97f384c7-f776-4b8d-937f-a08e6d18d84e",
    "created": 1744977256
}

embeddingの方はOpenAI SDKでも試してみる。

from openai import OpenAI

client = OpenAI(
    api_key="dummy",
    base_url="http://192.X.X.X:7997/"
)

response = client.embeddings.create(
    input="瑠璃色はどんな色?",
    model="cl-nagoya/ruri-v3-30m",
)

print(response.data[0].embedding[:5])
出力
[0.025892337784171104, -0.0017613227246329188, 0.01595025323331356, 0.011587179265916348, 0.07839227467775345]
kun432kun432

ローカルダウンロード済みモデルを使うこともできるようなので、それで再度Mac上で試してみた。

まずモデルをダウンロード。

git lfs install
mkdir models && cd models
git clone https://huggingface.co/cl-nagoya/ruri-v3-30m 
git clone https://huggingface.co/cl-nagoya/ruri-v3-reranker-310m
cd ..

コンテナ起動。モデルのディレクトリをマウントして、モデルIDの指定をパスで行えばよい。上のスクリプトにあわせていくつかオプションもつけてみた。あと、起動時にウォームアップが走るのだけど、結構長いのでスキップさせている。

docker run --rm -it \
    -v ./models:/models \
    -p 7997:7997 \
    michaelf34/infinity:latest \
    v2 \
    --model-id "/models/ruri-v3-30m" \
    --served-model-name "cl-nagoya/ruri-v3-30m" \
    --model-id "/models/ruri-v3-reranker-310m" \
    --served-model-name "cl-nagoya/ruri-v3-reranker-310m" \
    --port 7997 \
    --no-model-warmup

今度は起動して、一つ上のスクリプトもきちんと動作した。

kun432kun432

その他

  • Python APIで直接モデルを扱うこともできる。
  • 一応OpenAI互換API(といってもrerankingなどOpenAIが提供していないものもあるが)なので、OpenAIクライアントも使えるし、REST APIでアクセスすればいいのだけど、専用のクライアントライブラリも用意されている様子。
kun432kun432

まとめ

EmbeddingモデルをローカルでAPI立てる場合、以下のようなものがある。

https://zenn.dev/kun432/scraps/7f04e5f8e4b3cb

https://zenn.dev/kun432/scraps/97b23cae49edfc

これらと比較した場合に、Infinityにどのようなメリットがあるか?については、以下のIssueやベンチマークに記載されている。

https://github.com/michaelfeil/infinity/issues/108

https://michaelfeil.eu/infinity/0.0.76/benchmarking/

高速性についてはまあその通りなんだろうと思うけど、CPU推論でも速いってのは強みかも(モデルにもよるだろうけど)。あと、個人的には、rerankingやColPaliなんかも含めて、まとめてホストできるってのは良いなと思ったところ。

ただ、オープンウェイトのモデルはいろいろあって、モデルによって動く・動かない、というかツールによってサポートしている・していないってのは結局ある。動くように直せるならいいんだけどね、自分は流石に直せる気がしないので、いろいろなツールを使い分けることにはなりそう。

このスクラップは6ヶ月前にクローズされました