子供が生まれてから、子供の成長と自分との関係性の変化を日記に残したいと思い、iPhone に一日を記録させて夜に下書きを置くアプリを作りました。文字起こしと「この声は自分か」の判定は端末内、要約だけ Apple のクラウド(PCC)、記録は外に出しません。
3ヶ月ほど作る中で、Apple のドキュメントに書いていないことを実機で測るしかなかった 5 つです。
1. バックグラウンドでは CPU のタスクが遅いコアで動く。Neural Engine は影響を受けない
フォアグラウンドなら 33 秒で終わる処理が、バックグラウンド(BGContinuedProcessingTask)では 5 分たっても終わりませんでした。Apple のフォーラムにも「バックグラウンドで 4〜5 倍遅い、原因不明」という投稿があります。私も「バックグラウンドでは Neural Engine が遅くなる」と思っていましたが、測ってみると Neural Engine は 2 倍以内で、遅いのは CPU で動く処理でした。同じタスク(日本語 10 分の文字起こし)を条件を変えて測った結果です(iPhone 16、iOS 27、2 回の中央値)。
| 計算ユニット | フォアグラウンド | バックグラウンド(継続処理) | バックグラウンド(録音中) |
|---|---|---|---|
| Neural Engine | 2.2 秒 | 4.3 秒(2 倍) | 2.4 秒 |
| CPU のみ | 8.4 秒 | 102〜174 秒(12〜21 倍) | 7.3 秒 |
表の読み方: フォアグラウンドで処理すれば Neural Engine で 2.2 秒、CPU のみで 8.4 秒。バックグラウンドの継続処理に回すと、Neural Engine は 4.3 秒で済むのに、CPU のみだと 102〜174 秒かかります。ただし録音中(マイクを開いている間)のバックグラウンドなら、どちらもフォアグラウンドと同じ速さです。
なぜ CPU だけ遅いのか。プロセスの CPU 時間と消費電力量を task_info で読むと、CPU のみの処理はフォアグラウンドでは経過時間 8.7 秒に対して CPU 時間 7.9 秒、バックグラウンドでは経過時間 112 秒に対して CPU 時間 86 秒で、消費電力量は 4.6 分の 1 でした。つまり、バックグラウンドでも CPU 時間はほぼ経過時間ぶん割り当てられているのに、同じタスクを終えるのに 11 倍の CPU 時間が要っています。「CPU の割り当てを絞られて待たされている」のではなく、「省電力の遅いコアで低いクロックで動いている」が最もよく合う説明です(Instruments でコアの配置を直接見てはいないので推定です)。
音声を最長 20 秒のセグメントに分けて処理しているので、セグメント 1 個あたりの時間も見ました。バックグラウンドでは最初のセグメントから遅く(1 個 1.7 秒、フォアグラウンドは 0.14 秒)、途中で 1 個 8 秒前後に切り替わる区間もあります。Apple のフォーラムには「フォアグラウンドでも、負荷が 30 秒続くとスレッドが効率コアに移される」という別の報告がありますが、今回は最初から遅いので、その現象とは違います。
教訓は 3 つ。バックグラウンドで遅いなら、モデルではなく CPU で動く処理(前処理、クラスタリング、後処理)を疑う。Core ML を .background 優先度のタスクから呼ばない(フォアグラウンドでも 3.6 倍遅くなり、一方 CPU の処理は優先度で変わらない)。マイクを開いている間はバックグラウンドでもフォアグラウンドと同じ速さなので、重い処理は録音中に終わらせる。アプリをそう直して、録音後にやっていた笑い声の再検出 167 秒が 0 秒になりました。
2. 読み上げ音声のベンチマークで選ぶと、会話の文字起こしでは順位が逆になる
読み上げ音声のベンチマーク(FLEURS 日本語)では文字誤り率が Apple の音声認識 4.7%、Parakeet 5.3% で、Apple の方が良い。ところが放送の会話 258 分では Parakeet 12.7%、Apple 23.0% で逆転します。Apple は何も出力しない区間があり、誰も言っていない「ありがとうございます」を補うこともあります。読み上げの数字で選んでいたら、乗り換えて失敗していました。
もう一つ注意点があります。IBM の Granite を比較に加えたとき、音声の前後に 0.5 秒の無音を足すか足さないかだけで、文字誤り率が 3 ポイント動きました。条件をそろえると Granite と Parakeet は同じ精度で、実際の録音では Parakeet の方が句を落とさないので、アプリは Parakeet のままです。
3. 文字誤り率が同じでも、要約の間違いは 3 倍違う
同じ会話を 2 つの音声認識で文字起こしし、それぞれから要約を作ってどちらか伏せて比べると、間違いを含む話題が 9/32 と 3/29 でした。文字誤り率は同じです。ユーザーが読むのは要約なので、評価の物差しを間違えていました。要約のプロンプトも逆効果があり、「本文にある数字だけ使え」と縛ると、聞き間違いの数字をそのまま写して(「2、3 社」が「23 社」になる)間違いが増えます。音声認識が返す単語ごとの確信度は、正しくても間違っていても 0.95 前後で、重み付けには使えません。
4. Apple の PCC に本番のリクエストが「不適切」と拒否される率
Apple のガードレールが普通のリクエストを拒否しすぎることは、端末内モデルについては開発者フォーラムで何度も報告されています。クラウド側(PCC)を本番で使ったときの率は見当たらなかったので、自分の iPhone の本番ログ 41 日分を数えました。
| リクエスト | 失敗 | 「不適切」で拒否 | レート制限 | 1 日の上限 |
|---|---|---|---|---|
| 614 回 | 130 回 | 65 回(10.6%) | 59 回(9.6%) | 0 回 |
拒否されるのは普通の日記の下書きと会話の要約で、技術の打ち合わせの録音も拒否されます。拒否のレスポンスは 7.6〜12.7 秒かかり、成功(1〜3 秒)より長い。ストリーミングで返し始めてからフィルタが止めるからです。レート制限は 1 分間に 8〜10 回で当たり、朝の一括生成が毎日これに当たっていました。1 日の上限もあり、開発機では 1,158 回呼んだ日に 689 回が上限のエラーで失敗しました。正直に言うと、resetDate を返す API を読んでおらず、上限が iCloud アカウント単位で他のアプリと共有だということも知りませんでした。
5. 何を与えても「自分のコピー」は 62〜67% しか一致しない。人を見分けるのは話し方
LLM に自分の代わりをさせて、自分と同じ選択をするか測りました。与えた材料は、自己紹介の一行、日記 2 万トークン、価値観についての回答 31 問、面接 12 問。どれを与えても自分の選択との一致率は 0.62〜0.67 で、一行の自己紹介がいちばん高く(0.72)、自分の回答を足すとむしろ下がりました。
一方で、話し方は相手によってはっきり変わります。発話の間(ま)は同僚と 1.6 秒、妻と 2.2 秒、娘と 2.8 秒、上司と 5.3 秒。一文の長さは家族には 20 字、同僚には 32 字。研究でも同じ方向の結果が出ています。声の抑揚だけで知り合いを識別できること、文章の真似は家族でも見抜けないこと、遺族は内容の正しさより口調で「その人らしい」と判断すること。つまり、その人らしさは「何を言うか」より「どう言うか」にあります。
だからアプリは、整形後の文章だけでなく、話したままの言葉と、声の数値(高さ、速さ、間、笑い)と、誰と話していたかを、自分の発話の区間だけ残すようにしました。相手の声は残さず、音声そのものは 24 時間で消します。
まだ無いもの
iOS と watchOS だけ。アプリは非公開で、計測アプリとそのレポートは GitHub で公開しています(yamannyama/granite-speech-coreml)。日本語が主で英語は未測定。機能ごとの拒否率と一日の電池消費は記録中。日記と書き出しは無料、毎朝の一筆、会話の記録、絵と選曲が Pro(月 $3.99)。
バックグラウンド処理、PCC の本番運用、HealthKit の癖、音声認識の比べ方、うまくいかなかったこと、何でも聞いてください。