65.9K
CodeProject 正在变化。 阅读更多。
Home

TensorFlow - 使用 TensorFlowSharp 创建 C# 应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (13投票s)

2019年8月7日

CPOL

8分钟阅读

viewsIcon

78974

如何使用 TensorFlowSharp 创建 C# 应用程序

目录

引言

在模式识别领域,深度神经网络在过去五年中取得了显著的地位。这在很大程度上归因于廉价硬件、编程库和标记数据的可用性。如果训练得当,深度神经网络或卷积神经网络 (CNN) 可以产生出色的结果。Google 的 TensorFlow 是实现这些复杂算法的最流行的库之一。

在本文中,我将演示如何训练一个 CNN 模型来识别 MNIST 数据库中的手写数字。随后,我将介绍一个 C# 控制台应用程序,该应用程序将使用训练好的模型来实际分类 MNIST 数据集中的测试图像。本文的目标是演示如何最大限度地利用 Python 进行模型训练,并利用 .NET 构建一个消费训练模型的假设性最终用户应用程序。

顶部

关于 TensorFlow

TensorFlow 原生库

///
///https://tensorflowcn.cn/install/lang_c
///The windows native implementation is downloadable as a single ZIP 
///and structured as follows
///

include
--------
        |
        |
        |
        --c_api.h
        |
        |
lib
--------
        |
        |
        --tensorflow.dll
        |
        |
        --tensorflow.lib

Python 和 C# 的 TensorFlow 绑定

Tensorflow 实现为 C/C++ 动态链接库。平台特定的二进制文件包含在一个 ZIP 文件中。各种语言的绑定在此库之上提供。这些是调用原生库的特定于语言的包装器。Python 可能是建立在原生 TensorFlow 实现之上的最通用的编程层之一。TensorFlowSharp 是 TensorFlow 的 .NET 包装器。

                        TensorFlow(C/C++)
                        ----------------
                            |
                            |
        ------------------------------------------------
        |                                               |
        |                                               |
        |                                               |
      Python                                        TensorFlowSharp(C#)
      ------                                        -------------------
  (train model)                             (use model in client application)

顶部

背景

  • Python - 我使用 Python 和 MNIST 手写数字数据集训练了一个 CNN 模型。基本的 Python 知识是必不可少的。我使用了 **Visual Studio Code (1.36.1)** 进行 Python 脚本编写。您可以使用任何适合您的 Python 编辑器。
  • 我使用 **Visual Studio 2017** 来开发简单的控制台应用程序,该应用程序消费训练好的模型并对测试图像进行分类。
  • 在本文中,我使用了 TensorFlow 的 **GPU** 版本来提高学习速度。您需要一台 **GPU** 启用的台式机。提醒读者,使用 GPU 进行编码需要安装额外的 **CUDA** 库和驱动程序。本文还假设读者熟悉 **深度卷积神经网络** 的基本原理。

顶部

MNIST 是什么?为什么是 MNIST?

概述

MNIST 数据库是手写数字 (0-9) 的集合。它包含 60,000 个训练图像和 10,000 个测试图像。每个图像的宽度为 28 像素,高度为 28 像素,并且所有图像都是灰度的。在机器学习和计算机视觉领域,MNIST 已成为测试任何新范例的实际标准。(参考: http://yann.lecun.com/exdb/mnist/)

示例图片

图片分布

  0 1 2 3 4 5 6 7 8 9
培训 5923 6742 5985 6131 5842 5421 5918 6265 5851 5949
测试 980 1135 1032 1010 982 892 958 1028 974 1009

顶部

深度学习

感知器

在 20 世纪 40 年代和 50 年代,一个非常基础的数学神经元概念开始成型。研究人员(McCulloch、Pitts 和 Rosenblatt)从生物神经元的工作原理中汲取灵感。神经元是神经系统的构建块。人类大脑平均有数十亿个神经元,它们通过突触相互连接。他们设想单个神经元可以像一个直线分类器一样工作。通过树突流入的电信号代表一个真实世界的信号(向量),输出信号代表一个二元的(开/关)分类状态。Frank Rosenblatt (1962) 在他的著作《神经动力学原理》(1962 年出版,第“完全线性感知器”节) 中,通过提出线性感知器的设计,将 McCulloch 和 Pitts 神经元的设计向前推进了一步。

顶部

单层感知器

蓝色的圆圈代表方程 ax+by+c=0 的直线。

给定两个线性可分的点类 X 和 O,您可以找到一条直线来分隔这两个类。如果您将类 X 中点的坐标输入方程 ax+by+c,然后对类 O 中的所有点执行相同操作,您会发现类 X 中的所有点产生正值,而类 O 中的所有点产生负值(反之亦然)。符号的变化可能取决于常数 a、b 和 c。但是,这是构成感知器作为线性分类器的首要原理。

顶部

多层感知器

如果我们无法找到一条直线来分离类 X 和 O,就像著名的 XOR 问题一样,那么我们可以串联多个线性分类器。

顶部

卷积神经网络

深度学习通过结合特征提取和超平面发现,将多层感知器向前推进了一步。特征由滤波器层提取。有关此主题的详细论述,请读者参考 Andrew Ng 的教程。

顶部

TensorFlowSharp - 从 C# 应用程序使用 TensorFlow

TensorFlowSharp 是 TensorFlow 非托管原生库的 .NET 包装器。这是 Miguel de lcaza 开创性工作的成果。TensorFlowSharp 可以消费使用 Python 训练的 CNN 模型,这为创建令人兴奋的最终用户应用程序打开了可能性。

        nuget install TensorFlowSharp
        ///
        ///Skeletal code using TensorFlowSharp
        ///
        byte[] buffer = System.IO.File.ReadAllBytes(modelfile);
        using (var graph = new TensorFlow.TFGraph())
        {
            graph.Import(buffer);
            using (var session = new TensorFlow.TFSession(graph))
            {   
                /*
                1)pick a test image
                2)Created tensor object using this image
                */
                var runner = session.GetRunner();
                runner.AddInput(...,tensor,)
                runner.Fetch(...)
                var output = runner.Run();
            }
        }    

顶部

使用 GPU 的 TensorFlow

概述

                                Python script
                                --------------
                                        |
                                        |
                                        |

                              TensorFlow GPU package
                              ----------------------
                                        |
                                        |
                                        |
                                      cuDNN
                                      -----
                                        |
                                        |
                                        |
                                  CUDA Toolkit
                                  --------------
                                        |
                                        |
                                        |
                                     Drivers
                                     -------
                                        |
                                        |
                                        |
                                       GPU
                                       ---

GPU/NVDIA - 我是如何成功的?

在使用 TensorFlow 进行训练时,您可以选择使用 CPU 包或 GPU 包。GPU 更受青睐,因为训练速度明显更快。您需要正确版本的 NVIDIA 驱动程序和 CUDA 库。经验法则是,NVIDIA 驱动程序的版本应与 TensorFlow 的当前版本匹配。在撰写本文时,我使用了 Python 包 TensorFlow-GPU 1.14.0。我需要提醒读者,我在安装驱动程序并使 TensorFlow GPU 生效方面的经验并不顺利。

  • 更新 NVIDIA 驱动程序的版本。我没有通过 NVIDIA 网站安装。我通过 Windows 设备管理器用户界面更新了显卡。24.21.14.1131 版本对我有效。
  • 安装 CUDA Toolkit 10.0 版本
  • 安装 cuDNN SDK 版本 7.6.2。我选择了 Windows 10 版本。我将 cudnn64_7.dll 复制到 %ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v10.0\bin
  • Python 包 Tensorflow 1.14
  • Python 包 Keras 2.2.4
  • Python 包 Numpy 1.16.1

顶部

使用 TensorFlow 和 Python 训练 CNN 模型

CNN 架构

                               
                                 Input layer (28X28,1 channel)
                                 -----------------------------
                                            |
                                            |
                                            |           
                                 Convolution layer (5X5,20,RELU)
                                --------------------------------
                                            |
                                            |
                                            |           
                                Max Pool layer (2X2,stride=2)
                               ------------------------------
                                            |                              
                                            |                              
                                            |     
                                Convolution layer (5X5,50,RELU)
                               --------------------------------
                                            |                              
                                            |                              
                                            |                              
                                Max Pool layer (2X2,stride=2)
                                -----------------------------
                                            |                              
                                            |                              
                                            |     
                                         Flatten
                                        ---------
                                            |                              
                                            |                              
                                            |     
                                Dense layer (500 nodes,RELU)
                                ----------------------------
                                            |                              
                                            |                              
                                            |     
                                Dense layer (10 nodes,RELU)
                                ----------------------------
                                            |                              
                                            |                              
                                            |     
                                Output layer(Softmax)
                                ----------------------    

顶部

用于训练的图像文件

MNIST 数据集可以从 scikit-learn 包中方便地访问。但是,在本教程中,我演示了如何从磁盘加载图像。单独的 PNG 文件在随附的项目 MNISpng.csproj 中提供。Python 脚本 MnistImageLoader.py 将遍历目录结构并构建训练/测试图像列表。每个 PNG 文件的父文件夹将提供训练标签 (0-9)。

        MNIST
        -----
                |
                |
                training.zip
                -----------
                |    |
                |    |
                |    |--(folders 0 to 9)
                |           |
                |           |
                |           |_0
                |           |
                |           |
                |           |_1
                |           |
                |           |
                |           |_2
                |           .
                |           .
                |           ._9
                |
                |
                testing.zip
                -----------
                     |
                     |
                     |--(folders 0 to 9)    

顶部

1-Python 脚本 (MnistImageLoader.py)

#
#Load images and labels. Returns a tuple of image data,label
#
def load_images(path_in):
        filenames = glob.glob(path_in)
        images=[] 
        labels=[] #labels for each training file
        filenames = glob.glob(path_in)
        for filename in filenames:
                #get the parent folder from the full path of the 
                #file /mnist/blah/training/3/34348.png
                fulldir=os.path.dirname(filename)
                parentfolder=os.path.basename(fulldir)
                imagelabel=int(parentfolder)
                labels.append(imagelabel)
                img = get_im(filename)
                images.append(img)
        return images,labels

#
#The output from load_images() is further refined
#
def ReShapeData(data,target,numclasses):
        data_out = np.array(data, dtype=np.uint8)
        target_out = np.array(target, dtype=np.uint8)
        data_out = data_out.reshape(data_out.shape[0],  28,28)
        data_out = data_out[:, :, :, np.newaxis]
        data_out = data_out.astype('float32')
        data_out /= 255
        target_out = np_utils.to_categorical(target_out, numclasses)
        return data_out,target_out

顶部

2-加载训练图像 (TrainMnistFromFolder.py)

主 Python 脚本 TrainMnistFromFolder.py 将调用 load_imagesReShapeData 函数。

#
#Load training images
#
from MnistImageLoader import load_images,ReShapeData

print("Loading training images")
(train_data, train_target)=load_images(mnist_train_path_full)
(train_data1,train_target1)=ReShapeData(train_data,train_target,nb_classes)
print('Shape:', train_data1.shape)
print(train_data1.shape[0], ' train images were loaded')    

顶部

3-创建 CNN 模型 (TrainMnistFromFolder.py)

# 
# Create a sequential model
#
model = Sequential()
# Add the first convolution layer
model.add(Convolution2D(
    name="conv1",
    filters = 20,
    kernel_size = (5, 5),
    padding = "same",
    input_shape = (28, 28, 1)))
# Add a ReLU activation function
model.add(Activation(
    activation = "relu"))
# Add a pooling layer
model.add(MaxPooling2D(
    name="maxpool1",
    pool_size = (2, 2),
    strides =  (2, 2)))
# Add the second convolution layer
model.add(Convolution2D(
    name="conv2",
    filters = 50,
    kernel_size = (5, 5),
    padding = "same"))
# Add a ReLU activation function
model.add(Activation(
    activation = "relu"))
# Add a second pooling layer
model.add(MaxPooling2D(
    name="maxpool2",
    pool_size = (2, 2),
    strides = (2, 2)))
# Flatten the network
model.add(Flatten())
# Add a fully-connected hidden layer
model.add(Dense(500))
# Add a ReLU activation function
model.add(Activation(activation = "relu"))
# Add a fully-connected output layer - the output layer nodes 
# should match the count of image classes
model.add(Dense(nb_classes,name="outputlayer")) 
# Add a softmax activation function
model.add(Activation("softmax"))
#
#Display Summary
#
model.summary()

# Compile the network
model.compile(
    loss = "categorical_crossentropy", 
    optimizer = SGD(lr = 0.01),
    metrics = ["accuracy"])
print("Compilation complete");

顶部

4-训练模型 (TrainMnistFromFolder.py)

#
# Train the model 
#
total_epochs=20
start = time.time()
model.fit(
    train_data1, 
    train_target1, 
    batch_size = 128, 
    epochs = total_epochs,
	  verbose = 1)
print("Train complete");
#
#Test the model
#
print("Testing on test data")
(loss, accuracy) = model.evaluate(
    test_data1, 
    test_target1,
    batch_size = 128, 
    verbose = 1)

# Print the model's accuracy
print("Accuracy="+ str(accuracy))

顶部

5-保存模型 (FreezeKerasToTF.py)

训练完成后,必须将模型保存为原始 TensorFlow 格式 (.pb)。文件 FreezeKerasToTF.py 中的 freeze_session 函数为我们完成了这个任务。保存的模型包含网络布局和权重。

#
#Saving using Freeze approach 
#https://stackoverflow.com/questions/45466020/how-to-export-keras-h5-to-tensorflow-pb
#

frozen_graph = freeze_session(K.get_session(),
                              output_names=[out.op.name for out in model.outputs])
tf.train.write_graph(frozen_graph, "Out", "Mnist_model.pb", as_text=False)

6-结果

顶部

C# 控制台应用程序

概述

        -----------------------
        1)Load trained model file
        -----------------------
                |
                |
        -----------------
        2)Load test images
        -----------------
                |
                |
        -----------------------------------
        3)Evaluate the test image using CNN
        -----------------------------------    

1-创建控制台应用程序

  • 使用 .NET Framework (64 位,4.6.1 或更高版本) 创建一个新的控制台应用程序
  • 将 NUGET 包引用添加到 TensorFlowSharp

顶部

2-加载训练好的模型文件

        ///
        ///Skeletal code using TensorFlowSharp
        ///
        var modelfile=@"c:\\MyTensorFlowModel.pb";//Produced by training
        byte[] buffer = System.IO.File.ReadAllBytes(modelfile);
        using (var graph = new TensorFlow.TFGraph())
        {
            graph.Import(buffer);
            using (var session = new TensorFlow.TFSession(graph))
            {   
                var file="test.png";
                var runner = session.GetRunner();
                var tensor = Utils.ImageToTensorGrayScale(file);
                runner.AddInput(graph["conv1_input"][0], tensor);
                runner.Fetch(graph["activation_4/Softmax"][0]);

                var output = runner.Run();
                var vecResults = output[0].GetValue();
                float[,] results = (float[,])vecResults;
                ///
                /// Evaluate the results
                ///
                int[] quantized = Utils.Quantized(results);
            }
        }    

顶部

3-Utils.ImageToTensorGrayScale

此函数将加载一个 MNIST 图片文件并创建一个 TFTensor

        public static TensorFlow.TFTensor ImageToTensorGrayScale(string file)
        {
            using (System.Drawing.Bitmap image = 
                  (System.Drawing.Bitmap)System.Drawing.Image.FromFile(file))
            {
                var matrix = new float[1, image.Size.Height, image.Size.Width, 1];
                for (var iy = 0; iy < image.Size.Height; iy++)
                {
                    for (int ix = 0, index = iy * image.Size.Width; 
                         ix < image.Size.Width; ix++, index++)
                    {
                        System.Drawing.Color pixel = image.GetPixel(ix, iy);
                        matrix[0, iy, ix, 0] = pixel.B / 255.0f;
                    }
                }
                TensorFlow.TFTensor tensor = matrix;
                return tensor;
            }
        }    

顶部

4-Utis.Quantized

此函数会将 TF 结果转换为一个包含 10 个元素的数组。第 0 个元素代表数字 0 的概率,第 9 个元素代表数字 9 的概率。

        //Silly repetitions here! I was running out of time.
        internal static int[] Quantized(float[,] results)
        {
            int[] q = new int[]
            {
                results[0,0]>0.5?1:0,
                results[0,1]>0.5?1:0,
                results[0,2]>0.5?1:0,
                results[0,3]>0.5?1:0,
                results[0,4]>0.5?1:0,
                results[0,5]>0.5?1:0,
                results[0,6]>0.5?1:0,
                results[0,7]>0.5?1:0,
                results[0,8]>0.5?1:0,
                results[0,9]>0.5?1:0,
            };
            return q;
        }    

顶部

5-结果

在迭代完所有 10,000 个测试图像并通过 MNIST 对每个图像进行分类后,我们得到了 98.5% 的预测成功率。有 150 张图像被错误分类。根据 MNIST 主页,最先进的基准测试成功率超过 99.5%。

顶部

Using the Code

Github 存储库

解决方案结构

        Solution
        --------
                |
                |
                MNISTPng (ZIP of individual PNG train and test files)
                ------------------------------------------------------
                |
                |
                PythonTrainer (Python script to train using TensorFlow)
                -------------------------------------------------------
                |
                |
                ConsoleAppTester (C# console app using TensorFlowSharp)
                -------------------------------------------------------    

1-PythonTrainer

用于训练 CNN 模型的 Python 脚本

  • TrainMnistFromFolder.py - 加载和训练图像的最外层 Python 脚本
  • MnistImageLoader.py - 用于将 PNG 转换为 Tensor
  • FreezeKerasToTF.py - 用于将训练好的模型保存为 .PB 文件

2-MNISTPng

训练和测试图像的 ZIP 文件

  • testing.zip - 10,000 个单独的测试文件,组织在 10 个目录中
  • training.zip - 50,000 个单独的训练文件,组织在 10 个目录中

3-ConsoleAppTester

C# EXE,将使用 TensorFlowSharp 加载训练好的模型。

顶部

关注点

顶部

历史

  • 2019 年 8 月 7 日:初始版本
© . All rights reserved.