Microsoft MVP성태의 닷넷 이야기
닷넷: 2247. C# - tensorflow 연동 (MNIST 예제) [링크 복사], [링크+제목 복사],
조회: 1608
글쓴 사람
정성태 (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

비밀번호

댓글 작성자
 




... [31]  32  33  34  35  36  37  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12882정성태12/18/20217303개발 환경 구성: 619. Windows Server에서 WSL을 위한 리눅스 배포본을 설치하는 방법
12881정성태12/17/20217754개발 환경 구성: 618. WSL Ubuntu 20.04에서 파이썬을 위한 uwsgi 설치 방법 (2)
12880정성태12/16/20217680VS.NET IDE: 170. Visual Studio에서 .NET Core/5+ 역어셈블 소스코드 확인하는 방법
12879정성태12/16/202113935오류 유형: 774. Windows Server 2022 + docker desktop 설치 시 WSL 2로 선택한 경우 "Failed to deploy distro docker-desktop to ..." 오류 발생
12878정성태12/15/20218940개발 환경 구성: 617. 윈도우 WSL 환경에서 같은 종류의 리눅스를 다중으로 설치하는 방법
12877정성태12/15/20217618스크립트: 36. 파이썬 - pymysql 기본 예제 코드
12876정성태12/14/20217417개발 환경 구성: 616. Custom Sources를 이용한 Azure Monitor Metric 만들기
12875정성태12/13/20217029스크립트: 35. python - time.sleep(...) 호출 시 hang이 걸리는 듯한 문제
12874정성태12/13/20217105오류 유형: 773. shell script 실행 시 "$'\r': command not found" 오류
12873정성태12/12/20218207오류 유형: 772. 리눅스 - PATH에 등록했는데도 "command not found"가 나온다면?
12872정성태12/12/20218098개발 환경 구성: 615. GoLang과 Python 빌드가 모두 가능한 docker 이미지 만들기
12871정성태12/12/20218221오류 유형: 771. docker: Error response from daemon: OCI runtime create failed
12870정성태12/9/20216687개발 환경 구성: 614. 파이썬 - PyPI 패키지 만들기 (4) package_data 옵션
12869정성태12/8/20219026개발 환경 구성: 613. git clone 실행 시 fingerprint 묻는 단계를 생략하는 방법
12868정성태12/7/20217682오류 유형: 770. twine 업로드 시 "HTTPError: 400 Bad Request ..." 오류 [1]
12867정성태12/7/20217269개발 환경 구성: 612. 파이썬 - PyPI 패키지 만들기 (3) entry_points 옵션
12866정성태12/7/202114606오류 유형: 769. "docker build ..." 시 "failed to solve with frontend dockerfile.v0: failed to read dockerfile ..." 오류
12865정성태12/6/20217393개발 환경 구성: 611. 파이썬 - PyPI 패키지 만들기 (2) long_description, cmdclass 옵션
12864정성태12/6/20215738Linux: 46. WSL 환경에서 find 명령을 사용해 파일을 찾는 방법
12863정성태12/4/20217691개발 환경 구성: 610. 파이썬 - PyPI 패키지 만들기
12862정성태12/3/20216348오류 유형: 768. Golang - 빌드 시 "cmd/go: unsupported GOOS/GOARCH pair linux /amd64" 오류
12861정성태12/3/20218696개발 환경 구성: 609. 파이썬 - "Windows embeddable package"로 개발 환경 구성하는 방법
12860정성태12/1/20216710오류 유형: 767. SQL Server - 127.0.0.1로 접속하는 경우 "Access is denied"가 발생한다면?
12859정성태12/1/202112889개발 환경 구성: 608. Hyper-V 가상 머신에 Console 모드로 로그인하는 방법
12858정성태11/30/202110228개발 환경 구성: 607. 로컬의 USB 장치를 원격 머신에 제공하는 방법 - usbip-win
12857정성태11/24/20217546개발 환경 구성: 606. WSL Ubuntu 20.04에서 파이썬을 위한 uwsgi 설치 방법
... [31]  32  33  34  35  36  37  38  39  40  41  42  43  44  45  ...