Phi-4-mini-reasoningをCoreMLで試す

iOSアプリ開発を試したくて色々やっていたところ、LLMを使った機能を実装したいと思ったので、
どんなやり方がいいかと探していたところCoreMLを使用して実行できるらしいとGemini君がいうので試してみたメモ。

# ステップ

1. Hugging Faceからモデルをダウンロードする
1. ダウンロードしたモデルをCoreML形式への変換
1. XCodeプロジェクトへのモデルの適用

# ステップ0 軽量のモデルを探す

アプリと一緒に配布し、マシン上で動作しなくてはならないので、十分に小さくてしっかり動作しそうなモデルを探します。
Microsoftが出している、軽量な推論モデル Phi-4-miniの新しいモデルが出ていたので、こちらで試してみようと思う。

[参考記事: Microsoftが小規模推論モデル「Phi-4-reasoning」「Phi-4-reasoning-plus」「Phi-4-mini-reasoning」を発表 - Gigazine](https://gigazine.net/news/20250501-microsoft-phi-4-reasoning/)

# ステップ1 モデルのダウンロード

モデルのダウンロードは、Huggingface CLIを使うのが楽そう（昔GitでCloneしたりしたときはあまりにもあまりにもだった）ので、Huggingface CLIを使う。

```bash
# 適当にフォルダを切って
> mkdir huggingface-models
> mkdir -
# venvを作っておく
> python3 -m venv .venv
> source .venv/bin/activate
# CLIのインストール
> pip install huggingface-hub 
# ダウンロード (namespace/modelの形式で指定), --local-dirは特定のフォルダを指定する。ない場合は自動作成
> huggingface-cli download microsoft/Phi-4-mini-reasoning --local-dir Phi-4-mini-reasoning
```

結構爆速で終わった。

# ダウンロードしたモデルをCoreML形式への変換

CoreML形式に変換できなければ実行できないので、変換する。

Gemini君が提案してくれたのは以下の２つの方法
1. coremltoolsを使用する
1. Hugging Face optimum を使用する (対応モデルの場合、より簡単)

まずは、Hugging Face Optimumの方法を試して見ることにする。

```bash
## 必要なツール群をインストール
> pip install "optimum[exporters]" coremltools
...

note: This error originates from a subprocess, and is likely not a problem with pip.
  ERROR: Failed building wheel for onnx
Successfully built coremltools
Failed to build onnx
```

なんか色々エラーがでたので確認。

1. pipのアップグレード（25.1.1に） -> だめ
1. cmake, protobufのアップグレード -> だめ
1. Pythonのバージョンを下げる(3.10) -> 成功！

```bash
## 変換のコマンドを実行
> optimum-cli export coreml --model Phi-4-mini-reasoning/ --task text-generation-with-past Phi-4-mini-reasoning_coreml/
```

onnx形式ではないと怒られた。optimumではこの形式は対応していなかったらしい。
onnx形式を経由し、CoreMLに変換する方法を提案されたので試してみる。

:::note info
ONNXは Open Neural Network Exchange という形式で、機械学習系のツール間でモデルを共有・利用可能にするためのオープンフォーマットのことらしいです。
:::

onnxへの変換も相変わらず失敗する。`rope_scalling`の`short_factor`リストが64個なければならないところ、なぜか48個しかないというエラーである。

<details><summary>config.json(抜粋)</summary>

    "short_factor": [
      1,
      1.118320672,
      1.250641126,
      1.398617824,
      1.564103225,
      1.74916897,
      1.956131817,
      2.187582649,
      2.446418898,
      2.735880826,
      3.059592084,
      3.421605075,
      3.826451687,
      4.279200023,
      4.785517845,
      5.351743533,
      5.984965424,
      6.693110555,
      7.485043894,
      8.370679318,
      9.36110372,
      10.4687158,
      11.70738129,
      13.09260651,
      14.64173252,
      16.37415215,
      18.31155283,
      20.47818807,
      22.90118105,
      25.61086418,
      28.64115884,
      32.03,
      32.1,
      32.13,
      32.23,
      32.6,
      32.61,
      32.64,
      32.66,
      32.7,
      32.71,
      32.93,
      32.97,
      33.28,
      33.49,
      33.5,
      44.16,
      47.77 <- 48個しかない
    ],

</details>

## 諦めてモデルを変える

新しいモデルだったので何か問題があったのだろう。ということで、[Phi-4-mini-instructのonnxなモデル](https://huggingface.co/microsoft/Phi-4-mini-instruct-onnx)が公開されていたので、そこからCoreMLへの変換を試してみようと思う。

```bash
# まずはモデルのダウンロード
> huggingface-cli download microsoft/Phi-4-mini-instruct-onnx --local-dir Phi-4-mini-instruct-onnx
> 
```

<details><summary>変換のためのコード</summary>

```python
# onnx_to_coreml.py
import coremltools as ct
import os

# --- 設定項目 ---
# 1. ダウンロードした ONNX モデルファイルへのパスを指定してください
#    例: "onnx_models/phi4_instruct/model.onnx" や "downloaded/phi4.onnx" など
onnx_model_path = "Phi-4-mini-instruct-onnx/model.onnx"  # <--- 必ず変更してください

# 2. 出力する Core ML モデルのファイル名を指定してください
coreml_model_path = "Phi4MiniInstruct.mlmodel" # <--- 必要であれば変更

# 3. (オプション) 最低限サポートする OS バージョンを指定
#    これが低いほど古いOSでも動きますが、モデル形式が古くなる場合があります
#    (例: ct.target.iOS16, ct.target.iOS15, ct.target.iOS14)
#    指定しない場合、比較的新しいOSバージョンがデフォルトになります (例: iOS15 -> ML Program形式)
min_deployment_target = ct.target.iOS17 # 最新の機能を使いたい場合は iOS16 や iOS17

# 4. (オプション) 計算に使用するユニットを指定
#    ct.ComputeUnit.ALL (CPU + GPU + Neural Engine) - 通常はこれが推奨
#    ct.ComputeUnit.CPU_ONLY
#    ct.ComputeUnit.CPU_AND_GPU
compute_units_config = ct.ComputeUnit.ALL
# --- 設定終了 ---

# ONNXファイルの存在確認
if not os.path.exists(onnx_model_path):
    print(f"エラー: 指定されたONNXモデルファイルが見つかりません: {onnx_model_path}")
    print("onnx_model_path の設定を確認してください。")
else:
    print(f"ONNXモデル '{os.path.basename(onnx_model_path)}' を Core ML に変換します...")
    print(f"出力ファイル名: {coreml_model_path}")
    print(f"ターゲットOS: {min_deployment_target}")
    print(f"Compute Units: {compute_units_config}")

    try:
        # ONNX モデルを Core ML モデルに変換
        # 通常、ONNXからの変換では inputs や outputs を明示的に指定する必要はありません。
        # coremltools が自動的に推測します。(必要な場合のみ指定)
        mlmodel = ct.convert(
            onnx_model_path,
            # convert_to="mlprogram", # iOS15以降は ML Program 形式が推奨 (target 指定で自動選択されることも多い)
            minimum_deployment_target=min_deployment_target,
            compute_units=compute_units_config,
            # # --- 必要に応じて入力/出力の指定を追加 ---
            # # 例: 入力形状を可変長にする場合など
            # inputs=[ct.TensorType(name="input_ids", shape=(1, ct.RangeDim(1, 2048)))],
            # # 例: 出力名を指定する場合 (通常はONNXモデルの出力名が使われる)
            # outputs=[ct.TensorType(name="logits")],
            # # ------------------------------------
        )

        # 変換された Core ML モデルを保存
        mlmodel.save(coreml_model_path)
        print("-" * 50)
        print(f"成功: Core MLモデルを '{coreml_model_path}' に保存しました。")
        print("-" * 50)

    except Exception as e:
        print(f"\nエラー: ONNXからCore MLへの変換中に問題が発生しました。")
        print(f"エラー詳細: {e}")
        # 詳細なトレースバックが必要な場合は下のコメントを解除
        # import traceback
        # traceback.print_exc()
```
        
</details>

こちらでも、また別のエラーが出た。
`Unable to determine the type of the model, i.e. the source framework. Please provide the value of argument "source", from one of ["tensorflow", "pytorch", "milinternal"]. (...)`
ようやくすると、インプットになるモデルの出自（フレームワーク）が特定できないから変換できないと言っている。

## 最終手段。 Phi-3

Phi3であれば検証過程でうまくいくことがわかっているので、CoreMLモデルに変換して試してみる。
[microsoft/Phi-3-mini-4k-instruct-onnx](https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-onnx)

onnxモデルがあったので、こちらの`cpu_and_mobile`を使う。

onnxのcpu_and_mobileを変換しようとしたところ、これまでの方法では全くうまくいかなかったので、Hugging faceが用意している[exporters](https://github.com/huggingface/exporters)を試す

結局色々やったけど、うまくいかなかったので今日はここまで。