初めに
日本語と英語の翻訳モデルである CAT-Translate-1.4b を、Windows上のC#アプリケーションから動かしたくなりました。
最終的には.NET 10とWindows MLを使う予定ですが、Windows MLから直接Safetensorsのモデルを読むことはできません。そこで、まずONNX Runtime GenAIで扱える形式へ変換します。
この記事では、Pythonをモデル変換と検証に限定して使い、uvで環境を管理します。
今回確認した結果は次のとおりです。
- CAT-Translate-1.4bをONNX Runtime GenAI形式へ変換できた
- FP32 CPU、INT4 CPU、FP16 DirectMLの3種類を作成できた
- 変換後のモデルをPythonから検証できた
- tokenizerは元モデルと変換後モデルで形式が異なり、追加対応が必要だった
開発環境
| 項目 | バージョン / 構成 |
|---|---|
| OS | Windows |
| Python | 3.13 |
| Python環境 | uv |
| PyTorch | 2.13.0 |
| Transformers | 5.15.0 |
| ONNX Runtime GenAI | 0.15.2 |
| Tokenizers | 0.22.2 |
| ONNX IR | 1.0.0 |
依存関係はプロジェクトのpyproject.tomlで管理しています。
[project] name = "cat-translate-cs-tools" version = "0.1.0" requires-python = ">=3.13,<3.14" dependencies = [ "huggingface-hub>=0.30", "onnx-ir>=1.0.0", "onnxruntime-genai>=0.11,<0.16", "tokenizers>=0.22.2", "torch>=2.13.0", "transformers>=5.15.0", ] [tool.uv] package = false
環境の作成と依存パッケージのインストールはuv syncで行います。
uv sync
ONNXへ変換する
Model Builderを使う
今回は、ONNX Runtime GenAIに含まれているModel Builderを使いました。
通常のONNXモデルでは、入力テンソルを渡して出力テンソルを受け取るだけで済む場合があります。しかし、CAT-Translate-1.4bのような生成モデルでは、KV cacheや生成ループなども必要です。
Model Builderを使うと、ONNXモデルだけではなく、ONNX Runtime GenAIが利用するgenai_config.jsonやtokenizer関連ファイルを含むモデルフォルダーを作成できます。
INT4 CPUモデルを作成する
まず、CPU向けのINT4モデルを作成します。
uv run python -m onnxruntime_genai.models.builder ` -m cyberagent/CAT-Translate-1.4b ` -o artifacts/cat-translate-1.4b-int4-cpu ` -e cpu ` -p int4 ` -c artifacts/hf-cache
-mはHugging Faceのモデル名、-oは出力先、-eはExecution Provider、-pは重みの形式です。-cにはHugging Faceのキャッシュディレクトリを指定しています。
FP32 CPUモデルを作成する
品質確認用として、FP32のCPUモデルも作成しました。
uv run python -m onnxruntime_genai.models.builder ` -m cyberagent/CAT-Translate-1.4b ` -o artifacts/cat-translate-1.4b-fp32-cpu ` -e cpu ` -p fp32 ` -c artifacts/hf-cache
FP16 DirectMLモデルを作成する
DirectMLでの実行候補として、FP16モデルも作成しました。
uv run python -m onnxruntime_genai.models.builder ` -m cyberagent/CAT-Translate-1.4b ` -o artifacts/cat-translate-1.4b-fp16-dml ` -e dml ` -p fp16 ` -c artifacts/hf-cache
今回の変換では、以下の3種類のモデルフォルダーを作成できました。
| 出力先 | 実行対象 | 形式 |
|---|---|---|
artifacts/cat-translate-1.4b-fp32-cpu |
CPU | FP32 |
artifacts/cat-translate-1.4b-int4-cpu |
CPU | INT4 |
artifacts/cat-translate-1.4b-fp16-dml |
DirectML | FP16 |
出力フォルダーには、主に次のようなファイルが含まれます。
cat-translate-1.4b-fp32-cpu/ ├── genai_config.json ├── model.onnx ├── model.onnx.data ├── tokenizer.json ├── tokenizer.model ├── tokenizer_config.json └── chat_template.jinja
実際のファイル分割は変換形式やModel Builderのバージョンによって異なります。C#から読み込むときは、少なくともgenai_config.json、ONNXモデル本体、tokenizer.modelが必要になります。
変換したモデルを検証する
まず、Pythonから変換後のモデルを読み込みます。
今回の検証スクリプトでは、生成されたtokenizer.jsonをtokenizers.Tokenizerで読み込み、入力IDを作ってからONNX Runtime GenAIへ渡しています。
uv run python scripts/validate_manual_tokenizer.py ` --model artifacts/cat-translate-1.4b-int4-cpu ` --source-language Japanese ` --target-language English ` --text "🐈はとてもかわいいの。おててがまるくてふわふわなの。"
プロンプトは次の形にしています。
<|user|>Translate the following Japanese text into English. 🐈はとてもかわいいの。おててがまるくてふわふわなの。</s><|assistant|>
この入力に対して、Python側では次のような出力を確認できました。
🐈 is very cute. Its hands are round and fluffy.
この段階では、ONNXへの変換と生成モデルとしてのロードは成功しています。
Transformersの非推奨警告に対応する
変換前の検証コードでは、次のような警告が表示されました。
[transformers] `torch_dtype` is deprecated! Use `dtype` instead!
現在のTransformersでは、pipelineなどへモデルのデータ型を渡す場合、torch_dtypeではなくdtypeを使います。
from transformers import pipeline import torch translator = pipeline( "text-generation", model=model, tokenizer=tokenizer, dtype=torch.bfloat16, )
古い引数を残したままにせず、最新のAPIに合わせてdtypeへ変更しました。
tokenizerで詰まったこと
元モデルと変換後モデルで形式が違う
CAT-Translate-1.4bの元モデルに含まれるtokenizerはSentencePiece Unigramです。
一方、Model Builderが出力したtokenizer.jsonはBPE形式になっていました。
| tokenizer | モデル形式 |
|---|---|
元モデルのtokenizer.json |
Unigram |
Model Builder出力のtokenizer.json |
BPE |
そのため、元モデルのtokenizer.jsonを変換後のフォルダーへコピーして、ONNX Runtime GenAIのTokenizerで読み込もうとするとエラーになります。
type must be object, but is array
これは、Unigramの語彙が配列形式で保存されているのに対し、ORT側がBPE形式のオブジェクトを期待しているためです。
C#では元のSentencePiece tokenizerを使う
この問題への対応は、記事2で詳しく説明します。方針としては、C#側で元のtokenizer.modelをMicrosoft.ML.Tokenizers.SentencePieceTokenizerに読み込ませます。
その後、C#側で次の処理を行います。
- CAT-Translateのプロンプトを組み立てる
- 元のSentencePiece tokenizerで入力IDへ変換する
Generator.AppendTokensでONNX Runtime GenAIへ渡す- SentencePiece tokenizerで生成結果をデコードする
つまり、ONNXモデルの生成部分はONNX Runtime GenAIで動かし、tokenizerだけは元モデルのものを使う構成です。
まとめ
CAT-Translate-1.4bをONNX Runtime GenAIのモデル形式へ変換できました。
今回作成したモデルは次の3種類です。
- FP32 CPU
- INT4 CPU
- FP16 DirectML
ONNXモデルの変換自体は問題なく完了しました。一方で、tokenizerは元モデルがSentencePiece Unigram、変換後がBPEとなるため、C#側では元のtokenizer.modelを使う必要があります。
次の記事では、今回作成したモデルを.NET 10とWindows MLを使ってC#から実行します。