AIアプリの未来はデバイス上にある:React Native ExecuTorchでAIモデルを実行する方法
開発 • React Native • 2025年10月2日 • 14分で読める
Norbert Klockiewicz ゲスト著者
Maciej Rys ゲスト著者
ユーザーのデータを安全かつプライベートに保ちながら、ExpoアプリにAIモデルを統合する方法を学びましょう。
AIアプリの人気が急上昇しており、現在数千人の開発者がそれらを構築しています。しかし、多くの開発者は、モデルプロバイダーのAPIにデータを送信したり、アクセス料金を支払ったりすることなく、AI統合が可能であることを認識していません。
最近まで、デバイス上AIは単なる実験的なものでしたが、モデルがより小さく効率的になり、デバイスの処理能力が向上するにつれて、この技術は誰でもアクセスできるようになりました。
デバイス上AIとは何か?
10年以上前(2011年)、Siriはモバイル AIの最初の例の一つとして登場しました。数年後(2014年)、「Hey Siri」コマンドでSiriを起動できるようになり、これは最初のデバイス上AI機能の一つでした。
画期的な出来事は2017年、A11 Bionicプロセッサー(iPhone X)にNeural Engineが導入されたことで、毎秒6000億回の専用AI処理能力を提供しました。それ以来、技術は単純な音声コマンドを超えて発展し始めました。
iPhone 16 Proの現世代NPUは毎秒35兆回の演算が可能で、高度な言語モデルとリアルタイム画像生成を可能にし、すべて完全にデバイス上で実行されます。
しかし、ハードウェア機能は基盤に過ぎません。デバイス上AIは、モバイルアプリケーションについての考え方を変革する利点を提供します:
プライバシー・バイ・デザイン
ユーザーデータはデバイスから離れることがありません。機密文書の要約、個人写真の分析、機密情報の処理など、デバイス上AIは完全なプライバシーを保証します。
無料
継続的なAPIコストと予測不可能なクラウド費用を排除します。クラウドコンピューティングで月に数百ドルかかるモデルが、ユーザーのデバイス上で限界費用ゼロで無期限に実行されます。
汎用的な信頼性
デバイス上AIはどこでも動作します:機内モード、遠隔地、ネットワーク障害時でも。この信頼性により、クラウド依存ソリューションでは不可能な使用例が可能になります。
即座の応答
ネットワークの往復がないため、ユーザーは自然で即座に感じられる1秒未満の応答を体験できます。
しかし、デバイス上AIの実装の現実的な側面は重要な課題を提示します。モバイルデバイス上でAIモデルを直接実行するには、ExecuTorchのような推論エンジンの統合が必要です。これらは複雑なビルドプロセスを持つ大規模プロジェクトで、ドメイン固有の知識が必要です。
ここで、react-native-executorchのようなライブラリが、これらの複雑さを開発者から抽象化することでギャップを埋めます。
react-native-executorchの紹介
react-native-executorchは、Software Mansionによって作成されたライブラリです。その使命は、React Native開発者が機械学習の専門知識や複雑なインフラストラクチャ設定なしにAI機能を実装できるようにすることです。
ライブラリは完全な開発エコシステムを提供します:
- Hugging Faceでホストされた事前エクスポートモデル、即座に使用可能
- 前処理と後処理を抽象化する直感的なAPI
- 既存のReact Nativeワークフローとのシームレスな統合
技術的な詳細
内部的に、react-native-executorchはExecuTorchを活用しています。これは、InstagramとFacebookアプリケーションでデバイス上AI機能を支えるMetaの推論エンジンです。
ExecuTorchは、エッジデバイスでのAI展開のためのエンドツーエンドソリューションを提供し、いくつかの主要な利点があります:
PyTorchエコシステム統合
開発者は馴染みのあるPyTorch環境を使用してモデルを構築・訓練し、馴染みのある環境を離れることなく、モバイル開発用に直接エクスポートできます。
クロスプラットフォーム汎用性
スマートフォンを超えて、ExecuTorchはエッジコンピューティングスペクトラム全体をサポートします—マイクロコントローラー、スマートウォッチ、AR/VRヘッドセット、IoTデバイス。
最適化されたパフォーマンス
ExecuTorchは、Neural Engine加速のためのiOS上のCoreML、クロスプラットフォームGPUコンピューティングのためのVulkan、専用モバイルGPUバックエンドなど、様々なバックエンドのサポートでハードウェア利用を最大化します。これにより、デバイス機能に関係なく、モデルが最高効率で実行されることを保証します。
![図1:react-native-executorchとExecuTorch間の通信フロー]()
デモアプリの構築に入る前に、部屋の中の象に対処する必要があります:デバイス上AIは万能なソリューションではありません。利点は魅力的ですが、制限は現実的であり、慎重に考慮されなければユーザー体験に大きな影響を与える可能性があります。
デバイス上AIが問題となる場合
リソース消費
AI推論は計算集約的です。ユーザーはAI集約的なタスク中により速いバッテリー消耗を経験し、長時間の処理中にデバイスが著しく温かくなる可能性があります。現代のチップは驚くほど効率的ですが、物理法則に縛られています。より多くの計算はより多くのエネルギー消費を意味します。
ハードウェア制約
フラッグシップデバイスでも限界があります。ほとんどのスマートフォンは6-12GBのRAMを提供し、アプリで利用可能なのはその一部のみです。20GB以上を必要とする大規模言語モデルは、最適化の努力に関係なく、ローカルで実行することは単純に不可能です。
ストレージと配布の課題
AIモデルは数十メガバイトから数ギガバイトまで実質的になる可能性があります。これは配布のジレンマを作成します:アプリにモデルをバンドルする(ダウンロードサイズを膨張させ、アプリストアの制限に達する可能性)か、初回ユーザー体験に影響する複雑なダウンロード戦略を実装するかです。
パフォーマンスの格差
API応答時間はデバイス間で比較的一貫していますが、デバイス上パフォーマンスは劇的に変化します。モデルはフラッグシップ電話で毎秒50トークンでテキストを生成するかもしれませんが、ミッドレンジデバイスでは毎秒5トークンで苦労し、ユーザーベース全体で一貫性のない体験を作成します。
軽減戦略
量子化と最適化
量子化などの技術により、許容可能な精度を維持しながらモデルサイズを50-75%削減できます。重みの精度を32ビットから8ビットまたは4ビット表現に削減することで、モデルはより高速でメモリ効率的になります。追加戦略には、モデルプルーニング、知識蒸留、動的ローディングアプローチが含まれます。
ハイブリッドアーキテクチャ
スマートアプリケーションは、パフォーマンス、プライバシー、リソース制約のバランスを取りながら、シンプルで頻繁なタスクにデバイス機能を使用し、複雑な操作にクラウド処理を活用します。
デバイス上AIを選択する場合
すべてのデバイス層で一貫したパフォーマンスが必要な場合、最先端の大規模モデルへのアクセスが必要な場合、または最小限のアプリフットプリントが必要な場合は、デバイス上AIを避けてください。
プライバシーが最重要である場合、オフライン機能が必要な場合、またはリアルタイムインタラクションのために超低遅延が必要な場合は、デバイス上AIを選択してください。
重要なのは、技術的アプローチを製品要件に合わせることです—すべてのAI機能がローカルで実行される必要はなく、すべての機能がクラウドで実行されるべきでもありません。
実世界アプリの構築:デバイス上AIによる音声転写
デバイス上AIの利点と制限の両方を理解したところで、実用的なものを構築しましょう。Siriの話で始めたので、独自の音声アシスタント基盤を作成するのが適切です:完全にデバイス上処理で動作するリアルタイム音声転写アプリです。
電話のマイクロフォンを通じて音声をキャプチャし、OpenAIのWhisperモデルを使用して完全にローカルで転写するアプリを構築します。これは現代の音声アシスタントの重要なコンポーネントを実証します:音声データを外部サーバーに送信することなく音声をテキストに変換することです。
依存関係の設定
ベアなExpoアプリから始めて、コア依存関係をインストールします:
yarn add react-native-executorch
yarn add react-native-audio-api
ライブラリ概要
- react-native-executorch - MetaのExecuTorchランタイムへのブリッジ、デバイス上AI推論を可能にします
- react-native-audio-api - Web Audio API仕様に基づくReact Native用の高性能オーディオエンジン
権限の設定
音声録音には明示的なユーザー権限が必要です。app.jsonでreact-native-audio-api Expoプラグインを使用して設定します:
{
"plugins": [
[
"react-native-audio-api",
{
"iosBackgroundMode": true,
"iosMicrophonePermission": "This app requires access to the microphone to record audio.",
"androidPermissions": [
"android.permission.MODIFY_AUDIO_SETTINGS",
"android.permission.FOREGROUND_SERVICE",
"android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
],
"androidForegroundService": true,
"androidFSTypes": ["mediaPlayback"]
}
]
]
}
この設定を追加した後、ネイティブ変更を適用するために開発ビルドを再構築してください。
音声キャプチャの実装
音声認識の基盤は高品質な音声キャプチャです。音声認識用に最適化された特定のパラメータでオーディオレコーダーを設定します:
import { AudioManager, AudioRecorder } from 'react-native-audio-api';
function App() {
const [recorder] = useState(() => new AudioRecorder({
sampleRate: 16000,
bufferLengthInSamples: 1600,
}));
useEffect(() => {
AudioManager.setAudioSessionOptions({
iosCategory: 'playAndRecord',
iosMode: 'spokenAudio',
iosOptions: ['allowBluetooth', 'defaultToSpeaker'],
});
AudioManager.requestRecordingPermissions();
}, []);
}
設定詳細
- 16kHzサンプルレート:Whisperのネイティブ入力フォーマット、処理オーバーヘッドを削減
- 100msバッファチャンク:品質を維持しながら応答性の高いフィードバックを提供
- SpokenAudioモード:ノイズ抑制を備えた音声コンテンツ用のiOS最適化
Whisperモデルの読み込み
モバイル展開のバランスの取れた選択であるWhisper Tiny EnglishでuseSpeechToTextフックを使用します:
import { useSpeechToText, WHISPER_TINY_EN } from 'react-native-executorch';
const model = useSpeechToText({
model: WHISPER_TINY_EN,
});
モデルアーキテクチャ
選択されたモデルは、自動的にダウンロードされる3つのコンポーネントで構成されています:
- エンコーダー:音声特徴を処理(~33MB)
- デコーダー:テキストトークンを生成(~118MB)
- トークナイザー:テキストエンコーディング/デコーディングを処理(~3MB)
合計ダウンロード:初回使用時約150MB、後続セッション用にローカルキャッシュ
代替モデル:多言語サポートには、複数言語をサポートするWHISPER_TINYを使用
ユーザー体験を向上させるためにダウンロード進行状況を表示:
return (
<View style={styles.container}>
{!model.isReady ? (
<>
<Text>Loading Whisper model...</Text>
<Text>{Math.round(model.downloadProgress * 100)}%</Text>
</>
) : (
...
)}
</View>
);
リアルタイム転写の実装
Whisperの30秒音声処理制限により、無期限の長さの連続音声ストリームには特別な処理が必要です。私たちのストリーミング実装は、whisper-streamingから適応された重複音声チャンクを使用して、文の途中でのカットを防ぎます。このアプローチにより、音声セグメント全体で一貫した転写が保証されます。
アプリで使用しましょう:
const handleStartStreaming = async () => {
recorder.onAudioReady(async ({ buffer }) => {
const bufferArray = Array.from(buffer.getChannelData(0));
model.streamInsert(bufferArray);
});
recorder.start();
try {
await model.stream();
} catch (error) {
console.error('Transcription error:', error);
handleStopStreaming();
}
};
const handleStopStreaming = () => {