Mnist CPU
https://github.com/keijiro/MnistBarracuda
using System.Linq;
using UnityEngine;
using Unity.Barracuda;
using UI = UnityEngine.UI;
sealed class MnistTest : MonoBehaviour
{
public NNModel _model;
public Texture2D _image;
public UI.RawImage _imageView;
public UI.Text _textView;
void Start()
{
// Convert the input image into a 1x28x28x1 tensor.
using var input = new Tensor(1, 28, 28, 1);
for (var y = 0; y < 28; y++)
{
for (var x = 0; x < 28; x++)
{
var tx = x * _image.width / 28;
var ty = y * _image.height / 28;
input[0, 27 - y, x, 0] = _image.GetPixel(tx, ty).grayscale;
}
}
// Run the MNIST model.
using var worker =
ModelLoader.Load(_model).CreateWorker(WorkerFactory.Device.CPU);
worker.Execute(input);
// Inspect the output tensor.
var output = worker.PeekOutput();
var scores = Enumerable.Range(0, 10).
Select(i => output[0, 0, 0, i]).SoftMax().ToArray();
// Show the results on the UI.
_imageView.texture = _image;
_textView.text = Enumerable.Range(0, 10).
Select(i => $"{i}: {scores[i]:0.00}").
Aggregate((t, s) => t + "\n" + s);
}
}
Mnist GPU
https://github.com/keijiro/MnistBarracudaGpu
using UnityEngine;
using Unity.Barracuda;
sealed class MnistTest : MonoBehaviour
{
public NNModel _model;
public ComputeShader _preprocess;
public ComputeShader _postprocess;
public Texture2D _sourceImage;
public Renderer _previewRenderer;
public Renderer _labelRenderer;
ComputeBuffer _scores;
void Start()
{
// Prepare the input tensor buffer.
var shape = new TensorShape(1, 28, 28, 1);
var data = new ComputeTensorData
(shape, "Input", ComputeInfo.ChannelsOrder.NHWC);
using var input = new Tensor(shape, data);
// Invoke the preprocessing compute kernel.
_preprocess.SetTexture(0, "Input", _sourceImage);
_preprocess.SetBuffer(0, "Output", data.buffer);
_preprocess.Dispatch(0, 28 / 4, 28 / 4, 1);
// Run the MNIST model.
using var worker =
ModelLoader.Load(_model).CreateWorker(WorkerFactory.Device.GPU);
worker.Execute(input);
// Get access to the tensor data on the GPU.
var output = worker.PeekOutput().tensorOnDevice as ComputeTensorData;
// Invoke the postprocessing compute kernel.
_scores = new ComputeBuffer(10, sizeof(float));
_postprocess.SetBuffer(0, "Input", output.buffer);
_postprocess.SetBuffer(0, "Output", _scores);
_postprocess.Dispatch(0, 1, 1, 1);
// Output display
_previewRenderer.material.mainTexture = _sourceImage;
_labelRenderer.material.SetBuffer("_Scores", _scores);
}
void OnDestroy()
=> _scores?.Dispose();
}
Barracuda Backend 구성
이 표는 다양한 구현 방식 및 기술을 간단히 설명한 내용으로
각 열은 구현 방식과 그에 대한 설명을 나타냅니다. 아래는 표의 항목에 대한 설명입니다:
CSharpRef
- 설명: C# 레퍼런스 구현 (C# Reference Implementation)
- 특징: 기본적인 C# 구현으로 성능보다는 코드의 참조 및 이해를 목적으로 작성된 버전입니다.
CSharp
- 설명: C# Implementation (IL2CPP)
- 특징: IL2CPP(Intermediate Language to C++)를 활용하여 변환된 C# 코드의 구현입니다. 성능이 개선된 C# 버전으로, 더 빠른 실행 속도를 제공합니다.
CSharpBurst
- 설명: Burst (HPC#) Implementation
- 특징: HPC#(고성능 C#)로 작성된 Burst 컴파일러를 사용하는 구현입니다. 주로 높은 성능과 효율적인 실행을 목적으로 사용됩니다.
ComputeRef
- 설명: GPU Compute Reference Implementation
- 특징: GPU 기반의 연산을 참조하는 레퍼런스 구현입니다. GPU의 병렬 처리를 활용하지만 최적화는 기본적으로 제공되지 않습니다.
Compute
- 설명: GPU Compute Implementation
- 특징: GPU 연산을 활용하여 최적화된 성능을 제공하는 구현입니다. 병렬 처리를 통해 빠른 처리 속도를 목표로 합니다.
ComputePrecompiled
- 설명: GPU Compute + CPU Optimization
- 특징: GPU 연산에 더해 CPU 최적화도 포함된 구현입니다. 하이브리드 방식으로 GPU와 CPU의 성능을 조화롭게 활용합니다.
이 표는 성능과 구현 방식에 따라 다른 기술 및 최적화 수준을 구분하여 설명한 것으로, 특정 프로젝트에서 요구되는 성능 및 플랫폼에 따라 적절한 방식을 선택하도록 도와줍니다.