8.3K Views
December 13, 23
スライド概要
2023/8/23〜25に開催された CEDEC 2023 の講演スライドです。
講師:大前 広樹(ユニティ・テクノロジーズ・ジャパン株式会社)
リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。
推論ライブラリを実装する ユニティ・テクノロジーズ・ジャパン株式会社 大前 広樹
2 *ただし、周囲の受講者にご配慮下さい
3 AI
4 AI SUGOI “‘X{)$(O’&Q 3重耐性 49連勝 3点振り $@%#” Babu Babu 2022年までは・・・
5 AI AI AI AI AI 2023年
AIは世界的なトレンドになり 世界を急激に変え始めている 技術者としても そろそろ避けて通れない雰囲気…
でもなんか気付いたらメッチャ 色々技術が積み上がってて何を どうすれば良いのか・・・
あ、▼▼さん! 今度の企画なんですけど、 ○○にAI使ってやれませんかね …って言われても どうすりゃいいんだよ〜
AIコワイ あれ・・こわくない・・・? 本セッションの目標
• AIとは 構成 • AI(機械学習の基本) • モデルを実機で使うためには(ONNX) • 実機で推論モデルを動かすためには Unity Sentisを実例に
11 AIって何?
12 input なんかこういう図だけ見たことあるけど・・・ こういう「ニューラルネット」をつくって「学習」させると なんかの役に立つらしい? でも効率的ではないとも聞くなぁ ていうか、そもそも・・・ output
13 人の顔を認識したり・・・ 質問にイイ感じに回答したり・・・ なんでこれが出来るようになるん?
AI(機械学習)の基本
台東区で2LDKの部屋 借りようとおもったら だいたいイクラくらい? それだったら 月○○万円 くらいっすね そもそも機械学習(AI)で 解決したい問題は、 これはコンちゃん? なすちゃん? プログラミング それは 『なすちゃん』 (アルゴリズム)だけでは っすね 解決しづらい問題 これ数字の何って 描いてあんの? それは 『5』 っすね
識別的モデル 入力内容から答えを予想したり、 ・・・ 分類したりする 線形回帰 2値分類 多値分類 数字認識 生成的モデル 画像や文章や音声など、 ・・・ 入力されたデータ以上の (あるいは異なる形式の) 出力を生成する 画像生成 文章生成 音声生成 解決したい問題に合わせて「モデル」を設計していく 画像認識
予測計算 学習データ 学習データをもらって正解の値 を目指して計算する 正解データ 最適化 損失計算 「勾配」を使って、予測計算の ためのパラメーターを調整する 正解データとどのくらい離れて いるかを計算する 勾配計算 損失から「勾配」をつくる 勾配降下法を使った識別モデルのプログラムは、4つの要素で出来ている
入力データをみて か 2値分類 か? を分類したい
この辺で線を引いて・・・ こっち側にいるのは で・・・ こっち側にいるのは かな・・・ AとBで分類したいデータが ある 予め用意したデータじゃなくて も、入力したら か 分かるようになりたい。 か 今取ったデータは かな? かな?
この直線を引く一次関数 Yp = W * X + B = 適切なWとBの値が 2値分類の 予測計算 見つけられたら、 正確に分類出来る これを見つける作業が『学習』
損失計算 Yp 予測結果と正解データの2つの値がどれだけズレてるか (損失)を計算する。 損失はできるだけ0に近づけたいので、単純な差分を使う 今のパラメータで 予想してみたで〜 よりも差が際立ちやすい手法を使った方が学習に役立つ Y 正解データ 正解はこれでした i.e. loss = (Y-Yp)²
勾配計算 損失から「パラメータを次に調整すると 今回の損失の値は こんな感じでした きはどうしたらいい?」という足がかりに 勾配値に反映しとくな! なる「勾配値」を作る
最適化(パラメータ調整) 更新された勾配値でパラメータをそれぞれ更新する。 パラメータ W W この時にどのくらい反映するか=「学習率」 とっても簡単な最適化例: B B W -= 学習率 x Wの勾配値 B -= 学習率 x Bの勾配値 そしてまた予測計算へ!
この4つのプロセスを繰り返すことでだんだん 最適なパラメーターが見つかる 最適と思われるパラメータを獲得したモデルを 「学習済みモデル」と呼ぶ モデルを保存してスマホやPC、サーバーなどで使う!
input output 入力テンソル この謎のグラフは… 予測関数 出力テンソル 実は「予測関数」のこと!
より複雑なモデルでは 『特徴表現学習』という部分が増えて、 元データの特徴をより多角的に学習で きるようにモデルを作ったりする (でも、基本の構成は同じ!) 入力画像 畳み込み処理を何回か行うことで 畳み込みを行った結果 元画像から学べる特徴量を増やす 小さくなった画像を1階 のテンソルに変換して、 多値分類を行う 複雑な複合材構造の衝撃検出と特性評価のための畳み込みニューラルネットワーク A Convolutional Neural Network for Impact Detection and Characterization of Complex Composite Structures, Iuliana Tabian, Hailing Fu and Zahra Sharif Khodaei, 2019
0階のテンソル スカラー値 1.0 1階のテンソル ベクトル [1.0, 0.0, 0.0] 機械学習のモデルで 扱う入出力はすべて 「テンソル」 2階のテンソル 行列 ・ ・ ・ [ 1.0, 0.0, 0.0, 0.0 0.0, 1.0, 0.0, 0.0 0.0, 0.0, 1.0, 0.0 0.0, 0.0, 0.0, 1.0 ]
手書き数字の画像(28 x 28) [784] の 入力テンソル テンソルに変換 [784] 数値予測 出力テンソル [10] の [10] どの数字かの確率を 保持したテンソル 関数は基本同じ階のテンソルを扱うので、 事情に合わせて変形して使う
https://projector.tensorflow.org/ 文章や単語なども ベクトルに変換してしまう ことで扱えるようになる Word2Vec 10K
テンソルに対する計算処理は、大抵のライブラリで 部品化されている 線形関数 活性化 線形関数 活性化 関数 線形関数 関数 入力テンソル 予測モデル これらのコンポーネントを 「レイヤー」 と呼ぶ 活性化 関数 出力テンソル
学習したものを 実機に持っていく
● さまざまな推論用のシステムが サポートする共通フォーマット ● モデル構造や、モデルに紐付くデータや メタデータなどを効率的に持っていける ● 一般的に必要な要件を定義しながらも、 拡張性にも考慮されている ● ONNXは実行環境とは独立した 仕様となっている
モデルデータは好みの環境で作って ONNX形式で出力する
34 数多くのオペレーター が定義されている
35 Operator 線形関数 活性化 線形関数 関数 入力テンソル 活性化 関数 出力テンソル PyTorchで描いた 予測関数 ONNXモデルを グラフ化したもの
Unity Sentis ● ONNXベースのモデルを Unityが対応している さまざまなプラットフォームで 実行するライブラリ
Safe Harbor State ●Unity Sentisは現在まだ実験的機能 (Experimental) です。 ここで解説された実装方式や方針は 今後予告なく変わることがあります。 ●本セッション内容は 「推論ライブラリっていうのはこういうことを してるのね」ということを理解するための 補助として参照してください。
数多くのONNX オペレーターを 実装 推論ライブラリを実装する=(ONNXの)オペレーターを実装する という作業
推論ライブラリ実行時の流れ 準備(Editor) 実行(Runtime) Layer model.onnx Layer Layer Layer Model Asset モデルデータの 実行環境向けに インポート 最適化 モデルの モデルを実行する 入力データの準備 ロード Workerを作成 (テンソル化) モデルの実行をスケジュール 推論結果の受取(非同期) 推論ライブラリの役割は ● モデルに保存されたグラフから、実行するオペレーション(レイヤー)群を構築する ● 構築されたレイヤー群を使って、定型のオペレーションを効率良く実行する
Sentisでの モデルの 最適化 ModelOptimizer model.onnx Model Asset •意味のないレイヤーや重複しているレイヤーなどを見つけて整理 •レイヤーやサブグラフの中に同様に機能するもっと簡素な 別のレイヤーに置き換えられるものがあれば置き換え •推論時にデータの読み取りが必要なレイヤーは CPUで動作するよう設定 •etc
ONNX Model Model Asset
Sentisでモデルを実行 する Layer モデルの モデルを実行する 入力データの準備 ロード Workerを作成 (テンソル化) Layer Layer Layer モデルの実行をスケジュール ModelAsset modelAsset = Resources.Load(“model.onnx”) as ModelAsset; Model runtimeModel = ModelLoader.Load(modelAsset); 推論結果の受取(非同期)
Sentisでモデルを実行 する Layer モデルの モデルを実行する 入力データの準備 ロード Workerを作成 (テンソル化) Layer Layer Layer モデルの実行をスケジュール 推論結果の受取(非同期) IWorker worker = WorkerFactory.CreateWorker (BackendType.GPUCompute, runtimeModel); CPUで動かすか GPUで動かすかなどを指定する
Sentisでモデルを実行 する Layer モデルの モデルを実行する 入力データの準備 ロード Workerを作成 (テンソル化) Layer Layer Layer モデルの実行をスケジュール TensorFloat inputTensor = TextureConverter.ToTensor(inputTexture); テクスチャデータなど、ゲームでよく使うデータ構造を テンソルに変換してくれるコンバータも用意されている 推論結果の受取(非同期)
Sentisでモデルを実行 する Layer モデルの モデルを実行する 入力データの準備 ロード Workerを作成 (テンソル化) worker.Execute(inputTensor); 非同期で実行 Layer Layer Layer モデルの実行をスケジュール 推論結果の受取(非同期)
Sentisでモデルを実行 する Layer モデルの モデルを実行する 入力データの準備 ロード Workerを作成 (テンソル化) Layer Layer Layer モデルの実行をスケジュール TensorFloat outputTensor = worker.PeekOutput() as TensorFloat; outputTensor.AsyncReadbackRequest(callback); // callback => Action<bool> 推論結果の受取(非同期)
Sentisでモデルを実行 する Layer モデルの モデルを実行する 入力データの準備 ロード Workerを作成 (テンソル化) Layer Layer Layer モデルの実行をスケジュール 推論結果の受取(同期) TensorFloat outputTensor = worker.PeekOutput() as TensorFloat; 推論結果を同期待ちしたい場合は outputTensor.MakeReadable(); float[] results = outputTensor.ToReadOnlyArray(); MakeReadable()を呼ぶと 終わるまでブロックする
CPU BurstとJob Systemで CPU実行 Sentisの バックエンド タイプ さまざまなプラットフォームで利用可能 にするため、レイヤー/Opの実装は1つ GPU Compute GPU Command Bu er Compute Shaderで コマンドバッファに書き込み GPU実行 任意のタイミングでGPU実行 GPU Pixel Pixel Shaderで ではない ff GPU実行
フィルタ GPU Pixel 実装の工夫 テンソルはRender Textureを フィルタ 使用して実装するが、 テクスチャデータの次元に沿っては格 納しない 畳み込み結果 テクスチャフェッチ回数が多いConv(畳み込み) レイヤー等に有利な形でテンソルをテクスチャに格納
GPU Backend Sentisの Workerは生成時に実際の処理を行う”Backend” オブジェクトが接続される。 処理構造 Worker 参照 *講演後編集 Model
TensorData (GPU) 転送 Tensor TensorData (CPU) *講演後編集 Backend テンソルは使用時にはBackendと 同じメモリ空間にいる必要があるので、 テンソルのデータは内部的には TensorDataという別の形で持っており、 かつデバイス間を移動(アップロード) する機能を持つ GPU
TensorData (GPU) 生成 Job Tensor GPU Operation実装 Backend Job Job Job Job … Job Thread Worker Job発行 Workerを動かすと、 ジョブを使えるバックエンドでは Modelの構成に沿ってJobを発行 Execute Model *講演後編集
TensorData (GPU) 生成 Job Tensor Job Job Job Job … Job Thread Worker 各Jobが BackendのOperationを呼び出して 処理を実行 Execute Model *講演後編集 GPU Operation実装 Backend
Y ReLU関数の 実装例 X 活性化関数のひとつ public interface IOps : IDisposable { … TensorFloat Relu(TensorFloat x); … } *講演後編集 (0, 0) インターフェイス
CPU(Burst) の場合 public virtual TensorFloat Relu(TensorFloat X) { var O = NewOutputTensorFloat(X.shape); if (O.shape.HasZeroDims()) return O; C# (Job発行側) var job = new ReluJob(); job.ScheduleXO(Pin(X), Pin(O, uploadCache: false), O.shape.length, 1024); return O; } float Apply(float v) { return 0.5f * (v + abs(v)); } public void Execute(int threadIdx) { float v = Xptr[threadIdx]; Optr[threadIdx] = Apply(v); } *講演後編集 C# (Burst Job)
GPU Compute の場合
public override TensorFloat Relu(TensorFloat X)
{
var O = NewOutputTensorFloat(X.shape);
if (O.shape.HasZeroDims())
return O;
C# (CPU)
var fn = new ComputeFunc("Relu");
fn.ScheduleXO(Pin(X), Pin(O, uploadCache: false), O.shape.length);
return O;
}
#ifdef RELU
[numthreads(64, 1, 1)]
void Relu(uint3 dispatchThreadID : SV_DispatchThreadID)
{
uint threadIdx = unrolledDispatchArgs.x * dispatchThreadID.y + dispatchThreadID.x;
if(threadIdx >= unrolledDispatchArgs.y)
return;
{
float v = Xptr[threadIdx];
Optr[threadIdx] = Apply(v);
}
}
#endif
*講演後編集
Compute Shader
GPU Pixel の場合
public override TensorFloat Relu(TensorFloat X)
{
return Activation(X, "Relu");
}
C# (CPU)
TensorFloat Activation(TensorFloat X, string kernelName,
float alpha = 0f, float beta = 0f)
{
var O = NewOutputTensorFloat(X.shape);
if (O.shape.HasZeroDims())
return O;
var func = new PixelFunc("Hidden/Sentis/Activation");
var pinX = PinBlockAny(X);
var pinO = PinAsSame(O, pinX, uploadCache: false);
func.SetFloat(k_ID_Alpha, alpha);
func.SetFloat(k_ID_Beta, beta);
func.SetTensor(k_TensorPropertiesX, pinX);
func.SetTensorBlockStride(k_TensorPropertiesO, pinO);
func.EnableKeyword(kernelName);
func.Dispatch(pinO);
return O;
}
*講演後編集
Pixel Shader
float4 frag(v2f i, UNITY_VPOS_TYPE screenPos :
VPOS) : SV_Target
{
…
#ifdef Relu
v = 0.5f * (v + abs(v));
#endif
…
return v
}
Special Thanks Lein.@キノピオPro 氏 ( twitter.com/lears_VR ) みどりの森( https://mido0021.booth.pm/ )
Q&A