Foundation Models は追加学習できるらしい

Foundation Models について、セッションメモや実践についていくつかまとめてきた。

これらを通して筆者は、Foundation Models のコンセプトは Apple の用意したモデルを受動的に使うもので、追加学習する方法は提供されていないものだと理解していた。実際、Foundation Models の API には、外部情報と連携する tool calling の仕組みはあっても、モデルをファインチューニングする手段は提供されていない。

それが、先日参加した WWDC25 Recap イベントにて、近くの方から「できる」という情報をいただいて驚いた。その後、Apple の Technology Evangelist の方にも確認したところ、やはりできるとのこと。以下のページにその手順がまとまっていると教えてくださった。

Foundation Models adapter training – Apple Intelligence – Apple Developer

Adapters are an advanced technique that *adapt* a large language model (LLM) with new skills or domains. With the adapter training toolkit, you can train adapters to specialize the on-device system LLM’s abilities, and then use your adapter with the Foundation Models framework.

トレーニングには Adapter training toolkit なるものを用いるらしい。ローカルマシンと Python コードでトレーニングし、別途 Entitlement をリクエストする必要がある。また、マシンスペックもメモリ32GB以上とそれなり。事前学習させたモデルをアプリに組み込むので、例えばユーザー生成コンテンツをもとに Foundation Models のレスポンスを生成することはできないだろう。

Foundation Models は OS と共にバージョンアップされ続けるが、聞いた話ではその都度学習がリセットされ、その度に再学習させ直す必要があるとのこと。


ちなみに、このアダプターについてはセッション内に言及あったようで、それをいちからビデオを見直して探すのは大変なので、これまで見たセッションの transcript を溜めていたフォルダから Cursor に探させた。その答えが以下。

「Meet the Foundation Models framework」セッション

  • 該当箇所: 3:47〜3:57付近
  • 内容要約:
  • 「特定のユースケース(例:コンテンツタグ付け)向けに、モデルの能力を最大化するためのspecialized adapters(専用アダプター)を提供している」と明記。
  • さらに「今後もモデルを継続的に改善していく」と述べており、アダプターを使った拡張性やカスタマイズ性に触れています。
  • 原文抜粋:

> For certain common use cases, such as content tagging, we also provide specialized adapters that maximize the model’s capability in specific domains.

ついでに ChatGPT o3 に解説させた。

https://chatgpt.com/share/686d278b-cf78-8005-a273-061117f3d216

Foundation Models でオセロを作ってみたが、、

これの続き。型安全に構造化したデータを出力できる特徴を活かして何かできないか?と選んだテーマがオセロだったが、結果的にはゲームが成立しなかった。そもそもFoundation Modelsで一番最初に作るモノとしては適切でなかったかもしれない笑

紆余曲折経て、最終的に以下のようなデータモデルになった。

@Generable(description: "Represents the current state of an Othello game.")
struct OthelloGame {
    @Generable(description: "Represents the coordinates of a disc. Expressed as 'x:y' in string format (e.g., a:5).")
    struct DiscCoordinate {
        @Generable(description: "The X coordinate of the disc (a...h).")
        enum XCoordinate: String {
            case a, b, c, d, e, f, g, h
        }
        @Generable(description: "The Y coordinate of the disc (1...8).")
        enum YCoordinate: String {
            case `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`
        }
        
        @Guide(description: "The X position of the disc.")
        var x: XCoordinate
        @Guide(description: "The Y position of the disc.")
        var y: YCoordinate
    }
    
    @Guide(description: "Coordinates occupied by the white player.")
    var whiteDiscs: [DiscCoordinate] = [.init(.d, .`4`), .init(.e, .`5`)]
    
    @Guide(description: "Coordinates occupied by the black player.")
    var blackDiscs: [DiscCoordinate] = [.init(.e, .`4`), .init(.d, .`5`)]
    
    @Guide(description: "A message shown to the opponent based on the current state of the game when you make a move.")
    var message: String
}

うまくいかなかったというのは具体的には、初期状態の盤からゲームを進めるたびに最新の盤を返すよう instruction に指示していたがそれが不完全だったり、あり得ない場所に石を置いたりするといった感じだ。

ちなみにChatGPTだと次のように、間違うことはあれど一定成立するので、広義にLLMがオセロをできない、というわけではない。

たかだか2時間程度の試行錯誤なので、もっと頑張れば精度上げられたかもしれないが、大前提として以下のような向き不向きがあると思いやめた。

現状の Foundation Models 自体がオセロゲームを正確に扱えない

構造化した出力だけでなく、ChatGPT と同様に自然言語的なやりとりでも試みてみたが、初手から出力精度は ChatGPT 4o にはるかに下がる傾向があり、オンデバイスという特徴であったり、Foundation Models 自体の学習内容からして、オセロ自体に向いていないのではと考えている。

構造化させすぎることでコンテクスト上限を容易に超えてしまう

ゲームの向き不向き以外にも、数ターンでコンテクストの上限を迎える問題があった。検証してはいないのであくまで仮説だが、上述のように @Generable を構造化した分、レスポンスに要するトークンは増大すると推測している(データモデルを表す文字列が長くなるため)。もちろんゲームの特徴上、本来は会話のように過去の履歴を残す必要はなく、最後の盤とターンだけを引き継いだセッションを再生成すれば良いだろうが。


こうしたことからなんとなく掴めたことがふたつある。まずは、Foundation Models の得意分野としては、過度な構造化が不要な自然言語を軸としたユースケースで(素直に)活用するのが良いと思っている。また、構造化するしないを置いておいても、省トークンの工夫として型の命名などに気を付ける必要があるかもしれない。

次に、いきなり @Generable といったデータモデルを設計実装する前に、まず自然言語で対話してみて、ユースケースを満たす性能を有しているか、事前検証してみるのが良いと思った。Playgournd でも良いし、チャットアプリ作ってでも良い。Foundation Models が LLM をアプリに組み込みやすくしてくれているおかげで、チャットレベルであれば30分もかからず実装することができる。

Foundation Models framework を触ってみたが、、

以前紹介した Foundation Models framework を触ってみたが、シミューレタ起動すると例外が発生した。

let session = LanguageModelSession()
let response = try await session.respond(to: "What's a good name for a trip to Japan? Reply only with a title")
print("response: \(response)")
Passing along InferenceError::inferenceFailed::Error Domain=com.apple.UnifiedAssetFramework Code=5000 "There are no underlying assets (neither atomic instance nor asset roots) for consistency token for asset set com.apple.modelcatalog" UserInfo={NSLocalizedFailureReason=There are no underlying assets (neither atomic instance nor asset roots) for consistency token for asset set com.apple.modelcatalog} in response to ExecuteRequest

調べてみると、

Your app runs on iOS / iPadOS / visionOS / macOS 26, with Apple Intelligence enabled.

If you use a simulator, be sure that your Mac is on macOS 26, with Apple Intelligence enabled.

Apple Intelligence is available for your system language and region. If not sure, set the system language of your device to English and the region to United States.

Always check the availability when using Apple Foundation Models, as demonstrated in the Apple sample.

https://developer.apple.com/forums/thread/787445

なるほど、macOS は Sequoia 15.5 のままだったので、M2 搭載 Mac かつ Apple Intelligence 有効でも、動作要件を満たせていないのだった。(どこかのセッションで触れてた気がする)

というわけで、iPadOS 26 にアップデートした M1 搭載 iPad でビルドしたら、無事結果を得ることができた。

response: Optional(FoundationModels.LanguageModelSession.Response<Swift.String>(userPrompt: "What\'s a good name for a trip to Japan? Reply only with a title", duration: 5.951339542, content: "\"Samurai Sojourn in the Rising Sun\"", transcriptEntries: ArraySlice([(Response) "Samurai Sojourn in the Rising Sun"])))

Foundation Models のネタとして、何ができるだろうと考えていたのだが、オセロゲームの実装を閃いた。もちろんオセロ自体、ChatGPTと対戦することもできるだろうが、構造化した結果を保証する Foundation Models の強みが活かせそうだと思ったのと、ストリーム方式の結果出力に同期して状態更新を行ってみる、良いサンプルにもなりそうだからだ。

しかしChatGPT相手のオセロってどんな感じなんだろうと、ひとつ試してみたが、出力結果がブレたり、ルールガン無視したり、打てる手を否定してきたりとなかなかカオスだった笑

https://chatgpt.com/share/68594389-f13c-8005-bb90-78b4f628e1ae

これに比べて、Foundation Models framework のモデルがどれくらい精度高い/低いのか気になる。

iOS:マイク入力のオーディオレベルを取得する

AVAudioRecorder をセットアップする。

import AVFAudio

let recorder = try AVAudioRecorder(url: fileURL, settings: [
    // 例
    AVFormatIDKey : kAudioFormatLinearPCM,
    AVSampleRateKey : 44100,
    AVNumberOfChannelsKey : 2,
])
// メータリングを有効
recorder.isMeteringEnabled = true

最新の値を取得する。

// recorderからの最大・平均入力値をリフレッシュ(最新値の取得時に必ず実行)
recorder.updateMeters()

// 0番チャンネルの最大・平均入力それぞれの値を取得(-160〜0のFloat型)
let peak = recorder.peakPower(forChannel: 0)
let average = recorder.averagePower(forChannel: 0)
print("Peak: \(peak), Average: \(average)")

これを繰り返し実行することで、以下のようにリアルタイムな値を取得できる。

 Peak: -38.29653 dB, Average: -49.813396 dB
 Peak: -38.29653 dB, Average: -45.75203 dB
 Peak: -36.77163 dB, Average: -47.29889 dB
 Peak: -36.77163 dB, Average: -46.575954 dB
 Peak: -36.77163 dB, Average: -46.979954 dB
 Peak: -22.248798 dB, Average: -22.248798 dB
 Peak: -17.44955 dB, Average: -19.18833 dB
 Peak: -13.695625 dB, Average: -19.736965 dB
 Peak: -12.488851 dB, Average: -12.488851 dB
 Peak: -12.488851 dB, Average: -12.966704 dB

recorder.isMeteringEnabled を必ず true に指定しておく必要がある点に注意。デフォルトでは false のため、最小値を示す-160しか返ってこない。