MNIST
이전 글에 이어서 MNIST를 분석해 보자.
이번 포스트에서는 그려진 텍스처를 MNIST 엔진에 전달하여 글자를 인식하고 결과를 활용하는 법을 분석할 것이다.
MNIST 엔진 사용
사용자가 그린 그림(?)은 drawableTexture에 데이터로 저장되어 있다.
이를 MNIST엔진에 전달하여 데이터를 받아와야 한다.
이 흐름은 Infer에서 담당한다.
void Infer()
{
var probabilityAndIndex = mnist.GetMostLikelyDigitProbability(drawableTexture);
probability = probabilityAndIndex.Item1;
predictedNumber = probabilityAndIndex.Item2;
predictionText.text = predictedNumber.ToString();
if (probabilityText) probabilityText.text = Mathf.Floor(probability * 100) + "%";
}
이 함수는 간단하다.
drawbleTexture를 mnist에게 전달하여 결과를 반환받는다.
그럼 mnist에서 어떻게 모델을 사용하는지 살펴보자.
MNIST 내부 로직
Tensor<float> inputTensor = null;
public (float, int) GetMostLikelyDigitProbability(Texture2D drawableTexture)
{
// Convert the texture into a tensor, it has width=W, height=W, and channels=1:
TextureConverter.ToTensor(drawableTexture, inputTensor, new TextureTransform());
// run the neural network:
engine.Schedule(inputTensor);
// We get a reference to the outputs of the neural network. Make the result from the GPU readable on the CPU
using var probabilities = (engine.PeekOutput(0) as Tensor<float>).ReadbackAndClone();
using var indexOfMaxProba = (engine.PeekOutput(1) as Tensor<int>).ReadbackAndClone();
var predictedNumber = indexOfMaxProba[0];
var probability = probabilities[predictedNumber];
return (probability, predictedNumber);
}
TextureConverter를 통해 전달받은 drawbleTexture를 모델의 입력으로 변환한다.
ToTensor라는 메서드를 사용하여 변경할 텍스처와 변환 결과를 전달할 Tensor, TextureTransform을 매개변수로 전달한다.
Tensor란 쉽게 말해 다차원 배열을 의미하며 이는 GPU 연산에 편리하도록 형태를 바꾼다고 생각하면 된다.
TextureTransform은 텍스처를 변환하는 방법을 지정하는 것이다.
사이즈, 정규화, 채널 순서 변경 등 여러 옵션이 있지만 지금은 기본값으로 변환한다.
Worker engine;
engine.Schedule(inputTensor);
이후, engine을 통해 변환한 Tensor를 전달한다.
engine은 Worker 타입으로 Sentis에서 정의된 클래스이다.
engine에 입력 Tensor를 전달하며 스케줄링(예약)을 하고 Worker인 engine에서는 이를 순서에 맞게 처리한다.
이는 CPU와 GPU의 병렬 처리를 관리하고 성능을 최대화하려는 의도인 것 같다.
결과를 가져오는 방법은 다음과 같다.
// We get a reference to the outputs of the neural network. Make the result from the GPU readable on the CPU
using var probabilities = (engine.PeekOutput(0) as Tensor<float>).ReadbackAndClone();
using var indexOfMaxProba = (engine.PeekOutput(1) as Tensor<int>).ReadbackAndClone();
PeekOutput
신경망 모델의 출력 텐서를 인덱스로 가져오는 메서드
- 인덱스 0은 첫 번째 출력 텐서(확률 분포)
- 인덱스 1은 두 번째 출력 텐서(가장 높은 확률을 가진 인덱스)
신경망 모델은 여러 개의 출력을 가질 수 있는데, 이 경우에는 두 가지 출력을 생성하는 모델이다.
PeekOutput은 이러한 출력들을 참조로 가져온다.
ReadbackAndClone
ReadbackAndClone()은 두 가지 작업을 수행한다.
- Readback: GPU 메모리에 있는 텐서 데이터를 CPU 메모리로 가져옴
- Clone: 데이터의 복사본을 만들어 반환
GPU에서 계산된 결과는 일반적으로 CPU에서 바로 접근할 수 없기 때문에, 이 과정이 필요하다.
GPU 메모리에서 CPU 메모리로 데이터를 복사하는 작업은 상대적으로 비용이 크기 때문에, 필요할 때만 수행한다.
즉, 스케줄링한 입력에 따른 결과를 대기한다고 한다.
여기서 의문이 들었다.
굳이 병렬로 스케줄링 할 필요가 있을까?
이유는 CPU와 GPU를 모두 고려해야 한다는 점에 있었다.
engine에서는 결과가 언제 필요할지 모르기 때문에 대기시간을 최대한 줄이기 위해 이러한 구조로 설계한 것 같다.
var predictedNumber = indexOfMaxProba[0];
var probability = probabilities[predictedNumber];
return (probability, predictedNumber);
이후, 확률이 가장 높은 숫자와 확률을 결과로 반환한다.
정리
모델을 사용하려면 Worker를 통해 스케줄링해야 하며 입력과 결과를 미리 확인하여 포맷을 맞추는 작업이 필요한 것 같다.
추가적으로 다른 예제를 살펴본 뒤 huggingface에서 모델을 받아서 적용하는 것까지 해보자.
MNIST
이전 글에 이어서 MNIST를 분석해 보자.
이번 포스트에서는 그려진 텍스처를 MNIST 엔진에 전달하여 글자를 인식하고 결과를 활용하는 법을 분석할 것이다.
MNIST 엔진 사용
사용자가 그린 그림(?)은 drawableTexture에 데이터로 저장되어 있다.
이를 MNIST엔진에 전달하여 데이터를 받아와야 한다.
이 흐름은 Infer에서 담당한다.
void Infer()
{
var probabilityAndIndex = mnist.GetMostLikelyDigitProbability(drawableTexture);
probability = probabilityAndIndex.Item1;
predictedNumber = probabilityAndIndex.Item2;
predictionText.text = predictedNumber.ToString();
if (probabilityText) probabilityText.text = Mathf.Floor(probability * 100) + "%";
}
이 함수는 간단하다.
drawbleTexture를 mnist에게 전달하여 결과를 반환받는다.
그럼 mnist에서 어떻게 모델을 사용하는지 살펴보자.
MNIST 내부 로직
Tensor<float> inputTensor = null;
public (float, int) GetMostLikelyDigitProbability(Texture2D drawableTexture)
{
// Convert the texture into a tensor, it has width=W, height=W, and channels=1:
TextureConverter.ToTensor(drawableTexture, inputTensor, new TextureTransform());
// run the neural network:
engine.Schedule(inputTensor);
// We get a reference to the outputs of the neural network. Make the result from the GPU readable on the CPU
using var probabilities = (engine.PeekOutput(0) as Tensor<float>).ReadbackAndClone();
using var indexOfMaxProba = (engine.PeekOutput(1) as Tensor<int>).ReadbackAndClone();
var predictedNumber = indexOfMaxProba[0];
var probability = probabilities[predictedNumber];
return (probability, predictedNumber);
}
TextureConverter를 통해 전달받은 drawbleTexture를 모델의 입력으로 변환한다.
ToTensor라는 메서드를 사용하여 변경할 텍스처와 변환 결과를 전달할 Tensor, TextureTransform을 매개변수로 전달한다.
Tensor란 쉽게 말해 다차원 배열을 의미하며 이는 GPU 연산에 편리하도록 형태를 바꾼다고 생각하면 된다.
TextureTransform은 텍스처를 변환하는 방법을 지정하는 것이다.
사이즈, 정규화, 채널 순서 변경 등 여러 옵션이 있지만 지금은 기본값으로 변환한다.
Worker engine;
engine.Schedule(inputTensor);
이후, engine을 통해 변환한 Tensor를 전달한다.
engine은 Worker 타입으로 Sentis에서 정의된 클래스이다.
engine에 입력 Tensor를 전달하며 스케줄링(예약)을 하고 Worker인 engine에서는 이를 순서에 맞게 처리한다.
이는 CPU와 GPU의 병렬 처리를 관리하고 성능을 최대화하려는 의도인 것 같다.
결과를 가져오는 방법은 다음과 같다.
// We get a reference to the outputs of the neural network. Make the result from the GPU readable on the CPU
using var probabilities = (engine.PeekOutput(0) as Tensor<float>).ReadbackAndClone();
using var indexOfMaxProba = (engine.PeekOutput(1) as Tensor<int>).ReadbackAndClone();
PeekOutput
신경망 모델의 출력 텐서를 인덱스로 가져오는 메서드
- 인덱스 0은 첫 번째 출력 텐서(확률 분포)
- 인덱스 1은 두 번째 출력 텐서(가장 높은 확률을 가진 인덱스)
신경망 모델은 여러 개의 출력을 가질 수 있는데, 이 경우에는 두 가지 출력을 생성하는 모델이다.
PeekOutput은 이러한 출력들을 참조로 가져온다.
ReadbackAndClone
ReadbackAndClone()은 두 가지 작업을 수행한다.
- Readback: GPU 메모리에 있는 텐서 데이터를 CPU 메모리로 가져옴
- Clone: 데이터의 복사본을 만들어 반환
GPU에서 계산된 결과는 일반적으로 CPU에서 바로 접근할 수 없기 때문에, 이 과정이 필요하다.
GPU 메모리에서 CPU 메모리로 데이터를 복사하는 작업은 상대적으로 비용이 크기 때문에, 필요할 때만 수행한다.
즉, 스케줄링한 입력에 따른 결과를 대기한다고 한다.
여기서 의문이 들었다.
굳이 병렬로 스케줄링 할 필요가 있을까?
이유는 CPU와 GPU를 모두 고려해야 한다는 점에 있었다.
engine에서는 결과가 언제 필요할지 모르기 때문에 대기시간을 최대한 줄이기 위해 이러한 구조로 설계한 것 같다.
var predictedNumber = indexOfMaxProba[0];
var probability = probabilities[predictedNumber];
return (probability, predictedNumber);
이후, 확률이 가장 높은 숫자와 확률을 결과로 반환한다.
정리
모델을 사용하려면 Worker를 통해 스케줄링해야 하며 입력과 결과를 미리 확인하여 포맷을 맞추는 작업이 필요한 것 같다.
추가적으로 다른 예제를 살펴본 뒤 huggingface에서 모델을 받아서 적용하는 것까지 해보자.