Microsoft MVP성태의 닷넷 이야기
닷넷: 2247. C# - tensorflow 연동 (MNIST 예제) [링크 복사], [링크+제목 복사],
조회: 9649
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)

C# - tensorflow 연동 (MNIST 예제)

요즘 접하기 쉬운 예제로 MNIST 손글씨 인식을 C#에서 tensorflow와 연동해 만들어 보겠습니다. 여기서 중요한 것은, Model을 구해야 하는 것인데요 ^^ 그 부분은 그냥 파이썬 환경에서 자유롭게 코딩해 구하기만 하면 됩니다.

예를 들어, 아래의 MNIST 예제는 my_mnist_model.keras 파일로 모델을 저장하고 있습니다.

// 케라스 창시자에게 배우는 딥러닝
// https://github.com/gilbutITbook/080315/blob/main/chapter02_mathematical-building-blocks.ipynb

import setuptools.dist
from tensorflow.keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

from tensorflow import keras
from tensorflow.keras import layers

model = keras.Sequential([
    layers.Dense(512, activation='relu'),
    layers.Dense(10, activation='softmax')
    ])

model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['accuracy'])

train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255

model.fit(train_images, train_labels, epochs=5, batch_size=128)

test_loss, test_acc = model.evaluate(test_images, test_labels)
print(f'{test_acc}')

# https://www.tensorflow.org/tutorials/keras/save_and_load?hl=ko
model.save('my_mnist_model.keras')

my_mnist_model.keras 파일의 크기는 3MB 정도 됩니다. 이렇게 구한 Model 파일은 C# 프로젝트에 추가/배포해, 실행 시 C#에서 Python.NET을 이용해 저 Model 파일을 로드해 활용할 것입니다.




자, 그럼 본격적으로 위에서 만든 MNIST 필기체 인식 Model을 C#에서 Python과 연동해 볼까요? ^^

이를 위해, 모델을 이용한 predict 코드를 호출하는 파이썬 코드를 다음과 같이 만들어 줍니다.

# mnist_predict.py

import setuptools.dist
import tensorflow as tf
import numpy as np

model = tf.keras.models.load_model('my_mnist_model.keras')

def predict(img):
    imgs = np.expand_dims(img, axis=0)
    predictions = model.predict(imgs, verbose=0)
    predict_number = np.argmax(predictions[0])
    return (predict_number.item(), predictions[0][predict_number].item())

위의 predict 함수는 model.predict 호출 시 해당 이미지로 판정되는 숫자와 그 확률을 반환합니다.

그럼, 이제 Python.NET을 이용한 C# 코드에서는 이를 호출하는 코드만 다음과 같이 작성해 주면 됩니다.

using Python.Runtime;

namespace ConsoleApp3;

internal class Program
{
    static void Main(string[] args)
    {
        Runtime.PythonDLL = @".\python\python312.dll";

        PythonEngine.Initialize();

        using (_ = Py.GIL())
        {
            DisableTensorflowLog();

            dynamic npModule = Py.Import("numpy");

            {
                dynamic sys = Py.Import("sys");
                string dirPath = Path.GetDirectoryName(typeof(Program).Assembly.Location) ?? Environment.CurrentDirectory;
                sys.path.append(dirPath);
            }

            float[]? testImgArray = // ... 28x28 크기의 이미지 데이터 ...;
            dynamic npArray = npModule.array(testImgArray);

            {
                var pyFile = Py.Import(Path.GetFileNameWithoutExtension("mnist_predict"));

                dynamic results = pyFile.InvokeMethod("predict", npArray);

                int expected = results[0];
                double percentage = results[1];

                Console.WriteLine($"{expected}: {percentage:P0}");
            }
        }

        PythonEngine.Shutdown();
    }
}

만약 testImgArray에 7과 비슷한 숫자의 이미지를 담고 있는 28x28 크기의 버퍼가 있다면 위의 프로그램을 실행 시 "7: 100%"와 유사한 출력이 나옵니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다.)

만약 지난 글에 설명한 대로 CopyToOutputDirectory 설정을 했다면, 위의 예제를 실행했을 때 "C:\temp\ConsoleApp3\net8.0" 디렉터리에 출력이 모였을 것입니다. 해당 출력 파일만 다른 컴퓨터에 그대로 복사하면 (당연히 별도의 파이썬 설치 없이) 정상적으로 실행까지 됩니다.

한 가지 문제점이라면, 위의 경우 net8.0 출력에 있는 전체 바이너리의 크기가 (python + tensorflow까지 포함하므로) 1.6GB 정도, 압축하면 480MB 정도 됩니다. 만약 대상 컴퓨터에 파이썬 tensorflow 환경이 설치돼 있다면 이 용량을 없앨 수 있지만 그렇지 않은 경우라면... 뭔가 있어 보이는 ^^ 응용 프로그램의 크기를 자랑합니다.




참고로, 위의 코드를 Windows 10+ 환경에서 Python 3.12.0 버전으로 실행하면 load_model 시에 다음과 같은 오류가 발생합니다.

Traceback (most recent call last):
  File "C:\temp\ConsoleApp3\net8.0\python\test.py", line 36, in <module>
    model2 = tf.keras.models.load_model('my_mnist_model.keras')
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\temp\ConsoleApp3\net8.0\python\Lib\site-packages\keras\src\saving\saving_api.py", line 176, in load_model
    return saving_lib.load_model(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\temp\ConsoleApp3\net8.0\python\Lib\site-packages\keras\src\saving\saving_lib.py", line 152, in load_model
    return _load_model_from_fileobj(
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\temp\ConsoleApp3\net8.0\python\Lib\site-packages\keras\src\saving\saving_lib.py", line 207, in _load_model_from_fileobj
    _raise_loading_failure(error_msgs)
  File "C:\temp\ConsoleApp3\net8.0\python\Lib\site-packages\keras\src\saving\saving_lib.py", line 295, in _raise_loading_failure
    raise ValueError(msg)
ValueError: A total of 1 objects could not be loaded. Example error message for object <keras.src.optimizers.adam.Adam object at 0x000001CBB0BCFBF0>:

The shape of the target variable and the shape of the target value in `variable.assign(value)` must match. variable.shape=(10,), Received: value.shape=(512, 10). Target variable: <KerasVariable shape=(10,), dtype=float32, path=adam/dense_1_bias_momentum>

List of objects that could not be loaded:
[<keras.src.optimizers.adam.Adam object at 0x000001CBB0BCFBF0>]))

3.12.2 이상의 버전에서 하면 오류가 발생하지 않습니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/24/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  72  73  74  [75]  ...
NoWriterDateCnt.TitleFile(s)
12062정성태11/21/201919057디버깅 기술: 133. windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례 - 두 번째 이야기
12061정성태11/20/201919452Windows: 167. CoTaskMemAlloc/CoTaskMemFree과 윈도우 Heap의 관계
12060정성태11/20/201921123디버깅 기술: 132. windbg/Visual Studio - HeapFree x64의 동작 분석
12059정성태11/20/201920385디버깅 기술: 131. windbg/Visual Studio - HeapFree x86의 동작 분석
12058정성태11/19/201920956디버깅 기술: 130. windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례
12057정성태11/18/201916801오류 유형: 579. Visual Studio - Memory 창에서 유효한 주소 영역임에도 "Unable to evaluate the expression." 오류 출력
12056정성태11/18/201922475개발 환경 구성: 464. "Microsoft Visual Studio Installer Projects" 프로젝트로 EXE 서명 및 MSI 파일 서명 방법파일 다운로드1
12055정성태11/17/201916689개발 환경 구성: 463. Visual Studio의 Ctrl + Alt + M, 1 (Memory 1) 등의 단축키가 동작하지 않는 경우
12054정성태11/15/201918332.NET Framework: 869. C# - 일부러 GC Heap을 깨뜨려 GC 수행 시 비정상 종료시키는 예제
12053정성태11/15/201919940Windows: 166. 윈도우 10 - 명령행 창(cmd.exe) 속성에 (DotumChe, GulimChe, GungsuhChe 등의) 한글 폰트가 없는 경우
12052정성태11/15/201918703오류 유형: 578. Azure - 일정(schedule)에 등록한 runbook이 1년 후 실행이 안 되는 문제(Reason - The key used is expired.)
12051정성태11/14/201922285개발 환경 구성: 462. 시작하자마자 비정상 종료하는 프로세스의 메모리 덤프 - procdump [1]
12050정성태11/14/201919912Windows: 165. AcLayers의 API 후킹과 FaultTolerantHeap
12049정성태11/13/201920296.NET Framework: 868. (닷넷 프로세스를 대상으로) 디버거 방식이 아닌 CLR Profiler를 이용해 procdump.exe 기능 구현
12048정성태11/12/201920397Windows: 164. GUID 이름의 볼륨에 해당하는 파티션을 찾는 방법
12047정성태11/12/201922746Windows: 163. 안전하게 eject시킨 USB 장치를 물리적인 재연결 없이 다시 인식시키는 방법
12046정성태10/29/201917257오류 유형: 577. windbg - The call to LoadLibrary(...\sos.dll) failed, Win32 error 0n193
12045정성태10/27/201917183오류 유형: 576. mstest.exe 실행 시 "Visual Studio Enterprise is required to execute the test." 오류 - 두 번째 이야기
12044정성태10/27/201916766오류 유형: 575. mstest.exe - System.Resources.MissingSatelliteAssemblyException: The satellite assembly named "Microsoft.VisualStudio.ProductKeyDialog.resources.dll, ..."
12043정성태10/27/201918321오류 유형: 574. Windows 10 설치 시 오류 - 0xC1900101 - 0x4001E
12042정성태10/26/201918059오류 유형: 573. OneDrive 하위에 위치한 Documents, Desktop 폴더에 대한 권한 변경 시 "Unable to display current owner"
12041정성태10/23/201919015오류 유형: 572. mstest.exe - The load test results database could not be opened.
12040정성태10/23/201919365오류 유형: 571. Unhandled Exception: System.Net.Mail.SmtpException: Transaction failed. The server response was: 5.2.0 STOREDRV.Submission.Exception:SendAsDeniedException.MapiExceptionSendAsDenied
12039정성태10/22/201916862스크립트: 16. cmd.exe의 for 문에서는 ERRORLEVEL이 설정되지 않는 문제
12038정성태10/17/201916944오류 유형: 570. SQL Server 2019 RC1 - SQL Client Connectivity SDK 설치 오류
12037정성태10/15/201924450.NET Framework: 867. C# - Encoding.Default 값을 바꿀 수 있을까요?파일 다운로드1
... 61  62  63  64  65  66  67  68  69  70  71  72  73  74  [75]  ...