目錄
介紹
關于TensorFlow
TensorFlow本機庫
Python和C#的TensorFlow綁定
背景
什麼是MNIST?為何選擇MNIST?
概述
示例圖檔
圖檔分布
深度學習
感覺
單感覺器
多層感覺器
卷積神經網絡
TensorflowSharp——使用來自C#應用程式的Tensorflow
使用Tensorflow和GPU
概述
GPU/NVDIA-什麼為我工作?
使用TensorFlow和Python訓練CNN模型
CNN架構
用于訓練的圖像檔案
1-Python腳本(MnistImageLoader.py)
2-加載訓練圖像(TrainMnistFromFolder.py)
3-建立CNN模型(TrainMnistFromFolder.py)
4-訓練模型(TrainMnistFromFolder.py)
5-儲存模型(FreezeKerasToTF.py)
6-結果
C#控制台應用程式
概述
1-建立控制台應用程式
2-加載訓練模型檔案
3 Utils.ImageToTensorGrayScale
4- Utis.Quantized
5-結果
使用代碼
Github存儲庫
解決方案結構
1-PythonTrainer
2-MNISTPng
3-ConsoleAppTester
興趣點
- 從Github下載下傳源代碼
介紹
在模式識别領域,深度神經網絡在過去的5年中獲得了突出地位。這主要歸功于更便宜的硬體,程式設計庫和标簽資料的可用性。如果訓練得當,深度神經網絡或卷積神經網絡(CNN)可以産生驚人的效果。來自Google的TensorFlow是一個非常受歡迎的庫,它們實作了一些複雜的算法。
在本文中,我将示範如何訓練CNN模型以識别MNIST資料庫中的手寫數字。接下來是C#控制台應用程式,它将使用經過訓練的模型來實際分類來自MNIST資料集的測試圖像。本文的目的是示範如何充分利用Python來訓練模型,以及如何使用.NET來建構一個假設的最終使用者應用程式來使用經過訓練的模型。
關于TensorFlow
TensorFlow本機庫
///
///https://www.tensorflow.org/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訓練CNN模型,使用手寫數字的MNIST資料集。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/)
示例圖檔
圖檔分布
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神經元的設計向前推進了一步。
單感覺器
藍色圓圈表示a.x + b.y + c = 0形式的直線方程。
給定兩類點X和O,它們是線性可分的,你可以找到一條直線來劃分這兩個類。如果你将類X中的點的坐标輸入方程a.x + b.y + 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();
}
}
使用Tensorflow和GPU
概述
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)
----------------------
用于訓練的圖像檔案
可以從scikit-learn包中輕松通路MNIST資料集。但是,在本教程中,我示範了如何從磁盤加載圖像。各個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_images和ReShapeData。
#
#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 [email protected]"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%。
使用代碼
Github存儲庫
- https://github.com/sdg002/MNISTpng
解決方案結構
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加載訓練模型
興趣點
- Andrew Ng教程
- MNIST首頁