ONNX 파일 .Net framework C# 으로 활용하기

개요

요즘 기계 학습(Machine Learning, ML)을 이용한 프로그램이나 앱이 많이 등장하고 있습니다. 기계 학습의 결과를 .Net framework C# 환경에서 연동하는 방법을 알아보도록 하겠습니다.

ONNX

ONNX 는 (Open Neural Network Exchange) 를 줄인 것으로 여러가지 학습 플랫폼의 결과를 서로 사용할 수 있게 하는 일종의 프레임워크 같은 개념입니다.

ONNX 웹 사이트
ONNX 웹 사이트

기계 학습의 결과를 ONNX 파일로 저장하는 방법은 이 글의 범위를 넘어가게 됩니다. 좋은 글이 많이 있으므로 그것을 참조하시면 됩니다.

이 글에서는 직접 기계학습으로 저장한 것이나 다른 곳에서 구한 ONNX 파일을 사용하는 것으로 하겠습니다.

Netron

직접 기계 학습의 결과를 저장했다면 어떤 값을 입력했고 출력 되는 값을 이미 알고 있습니다. 그러나 다른 곳에서 전달 받았거나 구한 파일만 있다면 그것을 정확히 알 수 없습니다. 이때 사용하는 프로그램이 Netron 입니다.

웹사이트에 방문하여 파일을 올리면 구조를 시각화해서 보여주므로 매우 편리합니다. 파일을 올리는 것이 꺼려지는 분들은 Github 에서 설치본을 받아 설치한 후 사용하시면 됩니다.

필자는 타이타닉 생존자 데이터를 가지고 기계 학습을 통해서 예측하는 모델로 export 한 파일을 사용하였습니다.

ONNX 파일을 Netron 으로 열어보면 다음과 같습니다.

ONNX 파일을 Netron에서 열어 본 결과
ONNX 파일을 Netron에서 열어 본 결과

모델이 간단하여 복잡하지 않습니다. 기계 학습을 위해 입력한 값이 float 형으로 8개 이고 출력 값이 확률과 레이블(목표값)임을 알 수 있습니다.

C# 코드

위에 언급한 ONNX 파일을 이용하여 예측 값을 가져오는 코드는 다음과 같습니다.

using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
...
string ONNX_MODEL_PATH = "D:\\yourPaht\\your.onnx";

InferenceSession session = new InferenceSession(ONNX_MODEL_PATH);
DenseTensor<float> denseTensor;

float[,] predictInput = new float[1, 8];
predictInput[0, 0] = 3f;
predictInput[0, 1] = 1f;
predictInput[0, 2] = 16.0f;
predictInput[0, 3] = 2f;
predictInput[0, 4] = 0f;
predictInput[0, 5] = 18.000f;
predictInput[0, 6] = 7f;
predictInput[0, 7] = 3f;

denseTensor = predictInput.ToTensor();
var inputMetaData = session.InputMetadata;
var outputMetaData = session.OutputMetadata;

var modelInput = new List<NamedOnnxValue>();

foreach (var name in inputMetaData.Keys)
{
    modelInput.Add(NamedOnnxValue.CreateFromTensor<float>(name, denseTensor));
}

try
{
    var results = session.Run(modelInput);
    var inferenceResult = results.ToList()[0];
    var inferenceResultValue = inferenceResult.Value;

    var modelOutput = results.ToList()[0].AsTensor<long>().ToArray<long>()[0].ToString();

    Console.WriteLine("Predict Survived : " + modelOutput.ToString());
    Console.ReadLine();
}
catch (Exception error)
{
    Console.WriteLine(error.Message);
    Console.ReadLine();
}

9~28 행은 입력 값으로 8개의 항목을 입력하는 코드입니다.

32~39 행은 입력 데이터를 적용해서 그 결과를 출력하는 부분 입니다.

위 결과를 수행해 보면 다음과 같습니다(1이 생존).

예측 결과
예측 결과

입력 값의 항목을 변경하면 예측한 값이 달라집니다.

이번 글에서는 ONNX 파일을 .Net framework C# 환경에서 사용하는 방법을 알아보았습니다.

벽돌깨기 게임

개요

예전에 즐겼던 벽돌깨기 게임을 만들어 보면 좋겠다는 생각을 오래전부터 하고 있었습니다. 간단하게 기획하고 실제로 만들어 보기로 했습니다.

개발 과정

개발을 시작했을 때 어렵지 않게 구현을 할 수 있을 것이라고 생각했습니다. 일단 벽돌, 공, 막대 이 세가지 요소를 배치하고 기본적인 로직을 구현했습니다.

메인화면

그 다음 화면의 좌우, 위쪽에 벽을 만들고 공이 벽에 부딪치도록 했습니다.

첫 번째 문제

큰 문제 없이 동작할 것으로 생각하고 테스트 해보니 막대, 벽돌, 벽에 부딪칠 때 어떤 경우에 공이 튕겨 나오지 않고 벽을 따라 이동하는 경우가 있었습니다. 가만히 생각해 보니 입사각이 아주 작은 경우 그렇게 되는 것이 실제 물리 법칙에 맞았습니다. 그러나 게임에서는 진행이 불가능해져서 해결해야 할 문제였습니다.

이리저리 변경해 보다가 문득 이전에 오락실에 있었던 게임은 어떻게 동작했는지 확인해 보았습니다. 실제 물리 법칙을 따르지 않는 것을 확인했습니다.

공이 투사체이므로 ProjectileMovement 를 이용해서 구현했습니다. 공에 원래 없었는데 부자연스러운 동작을 보여 추가했습니다.

두 번째 문제

기본적인 동작은 완료되어 다양한 모양의 스테이지를 추가하는 작업을 진행했습니다. 처음에는 간단하게 코드에 추가해서 테스트 해 보았습니다. 실제 기기에 올려보니 세로 방향이라서 벽돌을 길게 배치할 필요가 있었습니다.

코드에 스테이지 정보가 있으면 나중에 수정하기가 너무 어려울 것 같아서 다른 방법을 알아보게 되었습니다.

이리저리 찾아보니 DataTable 이라는 클래스가 있었습니다. 별도의 파일에 게임에 필요한 정보를 저장해 놓고 그것을 사용할 수 있도록 해 줍니다.

스테이지 정보를 별도의 텍스트 파일로 만들고 그 정보를 읽어서 구현하는 방법으로 진행했습니다.

게임 화면

처음에 생각했던 것 보다는 개발하는데 어려웠습니다. 간단해 보이는데 그 안에서 동작하고 판단하고 처리하는 부분은 쉽지 않았습니다.

다시 한번 쉬운 것은 없고 초기에 문제가 될 것 같은 것을 미리미리 정리해 두는 것이 중요하다고 느꼈습니다.

여기에서 다운로드 받으실 수 있습니다. 광고가 포함되어 있습니다.