AI應用開發實戰(轉)


 

 

AI應用開發實戰 - 從零開始配置環境

與本篇配套的視頻教程請訪問:https://www.bilibili.com/video/av24421492/

建議和反饋,請發送到
https://github.com/Microsoft/vs-tools-for-ai/issues

聯系我們
OpenmindChina@microsoft.com

零、前提條件

  • 一台能聯網的電腦,使用win10 64位操作系統
  • 請確保鼠標、鍵盤、顯示器都是好的

一、Windows下開發環境搭建

本教材主要參考了如下資源:

官方github教程:https://github.com/microsoft/vs-tools-for-ai

斗魚tv教程:https://v.douyu.com/show/V6Aw87OBmXZvYGkg

本教程分為五步:

  • 安裝VS:難度一星
  • 安裝python:難度一星
  • 安裝CUDA和cuDNN:這是本教程最繁瑣的一步,這一步直接拉高本教程的平均難度。
  • 配置機器學習環境:這是本教程最簡單的一步,為了方便用戶配置環境,微軟提供了一鍵安裝工具!沒錯,一鍵安裝工具!業界良心阿!
  • 安裝VS Tools For AI插件:難度一星

note:本教程對各個軟件需要使用的版本都做出了明確說明,請安裝指定的版本

請放輕松,接下來的傻瓜教程不需要動腦子,你甚至可以打開手機邊刷微博邊配置環境

0.安裝Git

訪問 https://git-scm.com/download/win

選擇64-bit Git for Windows Setup下載

雙擊.exe開始安裝

選擇好自己的安裝路徑,一路next,直到Adjusting your PATH environment

請選擇Use Git from the Windows Command Prompt

這一步就已經將Git添加到環境變量中了,然后就可以直接在命令行里使用Git啦。

然后繼續next,直到安裝結束

1.安裝VS

訪問 https://www.visualstudio.com/zh-hans/products/
產品中點擊Visual Studio 2017

選擇Community版本下載

打開Visual Studio Installer進行如下的配置:

僅選擇.NET桌面開發Python開發即可

僅選擇.NET桌面開發Python開發即可

僅選擇.NET桌面開發Python開發即可

note:請自行決定Visual Studio的安裝路徑

等待數分鍾,時長視網絡狀況而定,這個時候你可以去泡一杯茶,或者聽一首歌,如果你的網絡不是很好,那你可以去看集美劇或者別的什么,等待安裝結束。

note:坐 和 放寬

2.安裝python

注意!!!如果你已經安裝了VS2017帶Python開發的環境,就不需要再裝一遍python了。打開vs2017, 點擊Tools->Python->Python Environments,應該可以看到Python 3.6已經安裝,在下面有個folder,大概是“c:\Program Files(x86)\Microsoft Visual Studio\Shared\Python36_64”,把這個字符串copy下來。然后打開Settings->Home->About->System info,在彈出的窗口中選擇Advanced system settings->Advanced->Environment Variables->System variables->Path->Edit->New,把剛才的python環境變量字符串paste進來。如果有多個python環境,建議把一些舊的版本卸載先,保證你的機器沒有那么多垃圾。
點擊OK后,再打開一個command窗口,輸入Python,就可以正常使用了。

訪問 https://www.python.org/downloads/

選擇版本3.5.43.6.5 ,Windows x86-64 executable installer下載。

打開安裝包,在安裝前,請選擇Add Python 3.X to PATH,隨后按照默認選項安裝即可。

點選后,程序將自動將Python加入環境變量,這樣避免在安裝后手動配置環境變量。

安裝結束后,請進行如下操作驗證python是否安裝成功

1.同時按下 win 與 R,在彈出的輸入框里輸入cmd 2.在彈出的窗口中輸入 python 3.輸入exit()退出 4.輸入python -m pip install -U pip以更新pip到最新版本

note: pip是一個用來管理python包的工具

自此,你已經完成了python的安裝,在朝着AI技術大牛的路上又前進了一步!

note:請伸出大拇指給自己一個贊👍

3.安裝CUDA與cuDNN

如果你的電腦裝有Nvidia的顯卡,請進行這一步配置,否則請跳過。

首先通過操作系統更新,升級顯卡驅動到最新版。

3.1 安裝CUDA

打開 https://developer.nvidia.com/cuda-toolkit-archive

選擇CUDA 9.0 進行安裝。

點擊后,選擇如下的配置:

note:請選擇local版本下載,一旦失敗還可以重新再來;如果使用network版本,一旦失敗,需要重新下載1.4GB的安裝包

打開安裝包,進行安裝,請自行配置CUDA的安裝路徑,並手動將CUDA庫添加至PATH環境變量中。

note:在Windows中,CUDA的默認安裝路徑是:

"C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin"

3.2 安裝cuDNN

note:打起精神!這是操作最復雜的一步!

訪問 https://developer.nvidia.com/rdp/cudnn-archive 找到我們需要的cuDNN版本:

cuDNN v7.0.5 (Dec 5, 2017), for CUDA 9.0

cuDNN v7.0.5 Library for Windows 10

點擊鏈接,等待着你的並不是文件下載,而是:

↑這就是本教程里最麻煩的一步:在下載cuDNN之前需要注冊Nvidia會員並驗證郵箱。不過還好可以微信登錄,省掉一些步驟。

一番令人窒息的操作之后,我們終於得到了cuDNN,我們把文件解壓,取出這個路徑的cudnn64_7.dll,復制到CUDA的bin目錄下即可。默認的地址是:

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin

note:到這里,我們已經完成了本教程最復雜的一步了

4.安裝機器學習的軟件及依賴

這一步雖然是整個教程最簡單的一步,甚至比把大象關進冰箱更簡單。

你只需要:

win + R ,打開cmd,在命令行中輸入:

cd c:\ //選擇一個你喜歡的路徑 md AI //在這里創建一個AI目錄 cd AI //打開這個目錄 //克隆倉庫到本地 git clone https://github.com/Microsoft/samples-for-ai.git cd samples-for-ai //打開這個目錄 cd installer //還有這個目錄 python.exe install.py //開始安裝

然后刷會微博,等待安裝結束即可。

成功之后是這樣的:

或者你覺得自己不怕麻煩,那么請訪問:https://github.com/Microsoft/vs-tools-for-ai/blob/master/docs/zh-hans/docs/prepare-localmachine.md

根據教程按步安裝,相信我,你會回來選擇一鍵安裝的。

note:就差一步啦!成功就在眼前!

5.安裝tools for ai插件

打開Visual Studio,選擇工具->擴展和更新->選擇“聯機”->搜索“AI”
就像這樣:

等待下載完成之后,關閉Visual Studio,沒錯,關閉Visual Studio,系統將自動安裝AI插件。

安裝完畢后再次打開Visual Studio,你將在界面上看到這樣的內容:

那么恭喜你!安裝成功!

note:千里之行始於足下,恭喜你成功地完成了環境的搭建,接下來就已經可以使用Visual Studio Tools For AI進行開發了😀

二、離線模型的訓練


6.14日更新
GitHub上的samples-for-ai進行了一定的更新,目前MNIST文件夾下只有一個mnist.py文件,

下述步驟中,請使用最新的mnist.py文件


在進行完環境搭建后,我們馬上就可以開始訓練第一個模型了,我們選擇tensorflow下的MNIST作為第一個例子。

MNIST的介紹請參考這個鏈接 https://www.tensorflow.org/versions/r1.1/get_started/mnist/beginners

首先我們打開這個路徑:C:\AI\samples-for-ai\examples\tensorflow,如果你在別的目錄下克隆了目錄,那么請打開你對應的目錄。然后雙擊TensorflowExamples.sln
就像這樣:

note:如果存在多個Python環境,你需要為Visual Studio的AI項目設置默認的Python環境。

例如,手動安裝的Python 3.5與Visual Studio 2017 Python開發負載自動安裝了64位的Python 3.6

如果要為Visual Studio設置全局的默認Python環境,請打開工具->Python -> Python環境。然后,選擇自己需要的Python版本,點擊將此作為新項目的默認環境

然后在解決方案資源管理器中,選擇MNIST,單擊右鍵,選擇設為啟動項目

然后選擇MNIST中的mnist.py,單擊右鍵,選擇在不調試的情況下啟動

然后程序就開始運行了,就像這樣:

等待一段時間之后,模型就訓練好了!這個時候打開MNIST所在的文件夾,MNIST下是否多了三個文件夾?分別是inputoutput還有export,這三個文件夾分別存儲了訓練模型的輸入文件、訓練時的檢查點文件,還有最終導出的模型文件

檢查點文件:

模型文件:

可能存在的問題

GPU ran out of memory

方法一:
修改convolutional.py第45行或第47行的BATCH_SIZEEVAL_BATCH_SIZE為一個更小的數字。具體修改哪一個,需要視你在程序運行的哪個部分得到了ERROR決定。

方法二:
不使用GPU訓練,在項目MNIST上單擊右鍵,選擇屬性(R)

修改環境變量為CUDA_VISIBLE_DEVICES=" "

 

AI應用開發實戰 - 手寫識別應用入門

手寫體識別的應用已經非常流行了,如輸入法,圖片中的文字識別等。但對於大多數開發人員來說,如何實現這樣的一個應用,還是會感覺無從下手。本文從簡單的MNIST訓練出來的模型開始,和大家一起入門手寫體識別。

在本教程結束后,會得到一個能用的AI應用,也許是你的第一個AI應用。雖然離實際使用還有較大的距離(具體差距在文章后面會分析),但會讓你對AI應用有一個初步的認識,有能力逐步搭建出能夠實際應用的模型。

建議和反饋,請發送到
https://github.com/Microsoft/vs-tools-for-ai/issues

聯系我們
OpenmindChina@microsoft.com

准備工作

一、 思路

通過上一篇文章搭建環境的介紹后,就能得到一個能識別單個手寫數字的模型了,並且識別的准確度會在98%,甚至99%以上了。那么我們要怎么使用這個模型來搭建應用呢?

大致的步驟如下:

  1. 實現簡單的界面,將用戶用鼠標或者觸屏的輸入變成圖片
  2. 將生成的模型包裝起來,成為有公開數據接口的類。
  3. 將輸入的圖片進行規范化,成為數據接口能夠使用的格式。
  4. 最后通過模型來推理(inference)出圖片應該是哪個數字,並顯示出來。

是不是很簡單?

二、動手

步驟一:獲取手寫的數字

提問:那我們要怎么獲取手寫的數字呢?

回答:我們可以寫一個簡單的WinForm畫圖程序,讓我們可以用鼠標手寫數字,然后把圖片保存下來。

首先,我們打開Visual Studio,選擇文件->新建->項目

在彈出的窗口里選擇Visual C#->Windows窗體應用,項目名稱不妨叫做DrawDigit,解決方案名稱不妨叫做MnistForm,點擊確定。

此時,Visual Studio也自動彈出了一個窗口的設計圖。

在DrawDigit項目上點擊右鍵,選擇屬性,在生成一欄將平台目標從Any CPU改為x64

否則,DrawDigit(首選32位)與它引用的MnistForm(64位)的編譯平台不一致會引發System.BadImageFormatException的異常。

然后我們對這個窗口做一些簡單的修改:

首先我們打開VS窗口左側的工具箱,這個窗口程序需要以下三種組件:

  1. PictureBox:用來手寫數字,並且把數字保存成圖片
  2. Label:用來顯示模型的識別結果
  3. Button:用來清理PictureBox的手寫結果

那經過一些簡單的選擇與拖動還有調整大小,這個窗口現在是這樣的:

一些注意事項

  1. 這些組件都可以通過右鍵->查看屬性,在屬性里修改它們的設置
  2. 為了方便把PictureBox里的圖片轉化成Mnist能識別的格式,PictureBox的需要是正方形
  3. 可以給這些控件起上有意義的名稱。
  4. 可以調整一下label控件大小、字體等,讓它更美觀。

經過一些簡單的調整,這個窗口現在是這樣的:

現在來讓我們愉快地給這些組件添加事件!

還是在屬性窗口,我們選擇某個組件,右鍵->查看屬性,點擊閃電符號,給組件綁定對應的事件。每次綁定后,會跳到代碼部分,生成一個空函數。點回設計視圖繼續操作即可。

組件類型 事件
pictureBox1 Mouse下雙擊MouseDownMouseUpMouseMove來生成對應的響應事件函數。
button1 如上,在Action下雙擊Click
Form1 如上,在Behavior下雙擊Load

然后我們開始補全對應的函數體內容。

注意,如果在上面改變了控件的名稱,下面的代碼需要做對應的更改。

廢話少說上代碼!

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Drawing2D;//用於優化繪制的結果 using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using MnistModel; namespace DrawDigit { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private Bitmap digitImage;//用來保存手寫數字 private Point startPoint;//用於繪制線段,作為線段的初始端點坐標 private Mnist model;//用於識別手寫數字 private const int MnistImageSize = 28;//Mnist模型所需的輸入圖片大小 private void Form1_Load(object sender, EventArgs e) { //當窗口加載時,繪制一個白色方框 model = new Mnist(); digitImage = new Bitmap(pictureBox1.Width, pictureBox1.Height); Graphics g = Graphics.FromImage(digitImage); g.Clear(Color.White); pictureBox1.Image = digitImage; } private void clean_click(object sender, EventArgs e) { //當點擊清除時,重新繪制一個白色方框,同時清除label1顯示的文本 digitImage = new Bitmap(pictureBox1.Width, pictureBox1.Height); Graphics g = Graphics.FromImage(digitImage); g.Clear(Color.White); pictureBox1.Image = digitImage; label1.Text = ""; } private void pictureBox1_MouseDown(object sender, MouseEventArgs e) { //當鼠標左鍵被按下時,設置isPainting為true,並記錄下需要繪制的線段的起始坐標 startPoint = (e.Button == MouseButtons.Left) ? e.Location : startPoint; } private void pictureBox1_MouseMove(object sender, MouseEventArgs e) { //當鼠標在移動,且當前處於繪制狀態時,根據鼠標的實時位置與記錄的起始坐標繪制線段,同時更新需要繪制的線段的起始坐標 if (e.Button == MouseButtons.Left) { Graphics g = Graphics.FromImage(digitImage); Pen myPen = new Pen(Color.Black, 40); myPen.StartCap = LineCap.Round; myPen.EndCap = LineCap.Round; g.DrawLine(myPen,startPoint, e.Location); pictureBox1.Image = digitImage; g.Dispose(); startPoint = e.Location; } } private void pictureBox1_MouseUp(object sender, MouseEventArgs e) { //當鼠標左鍵釋放時 //同時開始處理圖片進行推理 //暫時不處理這里的代碼 } } } 

步驟二:把模型包裝成一個類

將模型包裝成一個C#是整個過程中比較麻煩的一步。所幸的是,Tools for AI對此提供了很好的支持。進一步了解,可以看這里

首先,我們在解決方案MnistForm下點擊鼠標右鍵,選擇添加->新建項目,在彈出的窗口里選擇AI Tools->Inference->模型推理類庫,名稱不妨叫做MnistModel,點擊確定,於是我們又多了一個項目,

然后自己配置好這個項目的名稱、位置,點擊確定

然后彈出一個模型推理類庫創建向導,這個時候就需要我們選擇自己之前訓練好的模型了~

首先在模型路徑里選擇保存的模型文件的路徑。這里我們使用在AI應用開發實戰 - 從零開始配置環境博客中訓練並導出的模型

note:模型可在/samples-for-ai/examples/tensorflow/MNIST目錄下找到,其中output文件夾保存了檢查點文件,export文件夾保存了模型文件。

對於TensorFlow,我們可以選擇檢查點的.meta文件,或者是保存的模型的.pb文件

這里我們選擇在AI應用開發實戰 - 從零開始配置環境這篇博客最后生成的export目錄下的檢查點的SavedModel.pb文件,這時程序將自動配置好配置推理接口,見下圖:

類名可以自己定義,因為我們用的是MNIST,那么類名就叫Mnist好了,然后點擊確定。

這樣,在解決方案資源管理器里,在解決方案MnistForm下,就多了一個MnistModel

雙擊Mnist.cs,我們可以看到項目自動把模型進行了封裝,生成了一個公開的infer函數。

然后我們在MnistModel上右擊,再選擇生成,等待一會,這個項目就可以使用了~

步驟三:連接兩個部分

這一步差不多就是這么個感覺:

I have an apple , I have a pen. AH~ , Applepen

首先,我們來給DrawDigit添加引用,讓它能使用MnistModel。在DrawDigit項目的引用上點擊鼠標右鍵,點擊添加引用,在彈出的窗口中選擇MnistModel,點擊確定。

然后,由於MNIST的模型的輸入是一個28×28的白字黑底的灰度圖,因此我們首先要對圖片進行一些處理。
首先將圖片轉為28×28的大小。
然后將RGB圖片轉化為灰階圖,將灰階標准化到[-0.5,0.5]區間內,轉換為黑底白字。
最后將圖片用mnist模型要求的格式包裝起來,並傳送給它進行推理。
於是,我們在pictureBox1_MouseUp中添加上這些代碼,並且在文件最初添加上using MnistModel;

        private void pictureBox1_MouseUp(object sender, MouseEventArgs e) { //當鼠標左鍵釋放時 //同時開始處理圖片進行推理 if (e.Button == MouseButtons.Left) { // 復制pictureBox中的圖片並縮放到28*28成為新的圖片(tmpBmp) Bitmap tmpBmp = new Bitmap(digitImage, MinstImageSize, MinstImageSize); //將圖片轉為灰階圖,並將圖片的像素信息保存在list中 var imageData = new List<float>(MnistImageSize * MnistImageSize); for (var y = 0; y < MnistImageSize; y++) { for (var x = 0; x < MnistImageSize; x++) { var color = tmpBmp.GetPixel(x, y); var pixel = (float)(0.5 - (color.R + color.G + color.B) / (3.0 * 255)); imageData.Add(pixel); } } //將圖片信息包裝為mnist模型規定的輸入格式 var batchData = new List<IEnumerable<float>>(); batchData.Add(imageData); //將圖片傳送給mnist模型進行推理 var result = model.Infer(batchData); //將推理結果輸出 label1.Text = result.First().First().ToString(); } } 

最后讓我們嘗試一下運行~

三、效果展示

現在我們就有了一個簡單的小程序,可以識別手寫的數字了。

趕緊試試效果怎么樣~

注意

  1. 路徑中不能有中文字符,否則可能找不到模型。

擴展

嘗試識別多個數字

我們已經支持了單個手寫數字的識別,那能不能支持多個手寫數字的識別呢?同時寫下多個數字,正是現實中更為常見的情形。相比之下,如果只能一次識別一個手寫數字,應用就會有比較大的局限性。
首先,我們可以嘗試在現有的應用里一次寫下兩個數字,看看識別效果(為了更好的展示效果,將筆畫的寬度由40調整為20。這一改動對單個數字的識別並無大的影響):

識別效果不盡人意。
右上角展示的結果准確地反應了模型對我們手寫輸入的推理結果(即result.First().First().ToString()),然而這一結果並不像我們期望的那樣是“42”。
了解MNIST數據集的讀者們可能已經意識到了,這是“理所當然”的。歸根結底,這一問題的症結在於:作為我們AI應用核心的AI模型,本身並不具備識別多個數字的能力——當前案例中我們使用的AI模型是基於MNIST數據集訓練的(訓練過程請回顧我們之前的博客AI應用開發實戰 - 從零開始配置環境),而MNIST數據集只覆蓋了單個的手寫數字;並且,我們並未對筆跡圖形作額外的處理。
結果是在寫下多個數字的情況下,我們實際上在“強行”讓AI模型做超出其適應性范圍的判斷。這屬於AI模型的誤用。其結果自然難以令人滿意。
那么,為了增強應用的可用性,我們能不能改善它、讓它能識別多個數字呢?我們很自然地想到,既然MNIST模型已經能很好地識別單個數字,那我們只需要把多個數字分開,一個一個地讓MNIST模型進行識別就好了。這樣,我們就引入了一個新的子問題,即是“多個手寫數字的分割”。

子問題:分割多個手寫數字

我們注意到本文介紹的應用有一個特點,那就是最終用作輸入的圖形,是用戶當場寫下的,而非通過圖片文件導入的靜態圖片,即我們擁有筆畫產生過程中的全部動態信息,比如筆畫的先后順序,筆畫的重疊關系等等。考慮到這些信息,我們可以設計一種基本的分割規則:在水平面上的投影相重疊的筆畫,我們就認為它們同屬於一個數字。
筆畫和水平方向上投影的關系示意如下圖:

因此書寫時,就要求不同的數字之間盡量隔開。當然為了盡可能處理不經意的重疊,我們還可以為重疊部分相對每一筆畫的位置設定一個閾值,如至少進入筆畫一端的10%以內。
應用這樣的規則后,我們就能比較好的把多個手寫數字分割開,並能利用Visual Studio Tools for AI提供的批量推理功能,一次性對所有分割出的圖形做推理。
多個手寫數字識別的最終效果如圖:

當然,我們對問題的定義還是非常理想化,分割算法也比較簡單。在實際應用中,我們還經常要考慮非二值圖形、噪點、非數字的判別等等。並且對手寫數字的分割可能比我們設定的規則要復雜,因為在現實場景中,水平方向上的重疊可能會影響圖形的涵義。
將兩個手寫數字分割開這一問題,實際上和經典的圖像分割問題非常類似。雖然本文示例中的圖像非常簡單,但仍然可能具有相當復雜的語義需要處理。為此,我們可能需要引入更多的模型,或者擴展現有的模型來正確判斷多個圖形之間的關系。

進階

那么,如果要識別多個連寫的數字,或支持字母該怎么做呢?大家多用用也會發現,如果數字寫得很小,或者沒寫到正中,識別起來正確率也會不高。要解決這些問題,做成真正的產品,就不止這一個模型了。比如在多個數字識別中,可能要根據經驗來切分圖,或者訓練另一個模型來檢測並分割數字。要支持字母,則需要重新訓練一個包含手寫字母的模型,並准備更多的字母的數據。要解決字太小的問題,還要檢測一下字的大小,做合適的放大等等。

我們可以看到,一個訓練出來的模型本身到一個實際的應用之間還有不少的功能要實現。希望我們這一系列的介紹,能夠幫助大家將機器學習的概念帶入到傳統的編程領域中,做出更聰明的產品。

 

 

此時,界面會提示注冊Azure,因為定制化視覺服務實際上是Azure提供的一項雲服務,正式使用這項服務需要有Azure訂閱。
不過我們現在只是免費試用,所以選擇Continue With trial,如果在根據這篇博客流程做完了一個小應用之后,你覺得確實需要使用這項服務,那么你可以去注冊Azure賬號,獲取Azure訂閱。

三、創建定制化視覺服務項目

點擊New Project,填寫項目信息。

這里不妨以一個熊的分類模型作為例子來實踐吧。

填寫好NameDescription,這里Name不妨填寫為BearClassification

隨后選擇ClassificationGeneral(compact),點擊Create

截圖 操作
Project Type一欄,定制化視覺服務提供了識別和分類兩種服務,另外提供了多種識別場景,其中末尾帶有(compact),也即壓縮字樣的三種。
壓縮模型,顧名思義,模型占用的空間更少,運行更快,甚至可以放到手機這種移動設備里。
當然,會有一個小問題就是精確度會受影響。導出模型后,模型文件的使用是沒有任何限制的,而其余的幾種場景只能通過調用API來進行預測,由於當前屬於免費試用,因此這種方式有10000次調用上限。
由於分類服務需要准備用來訓練的數據集,請自行准備幾種不同的熊的照片,將同種的熊放在以這種熊的名字命名的文件夾里,最后再將這些文件夾放在一個data文件夾中。然后點擊Add images
選擇一種熊的全部照片,然后創建對應的標簽,點擊Up load xxx files
在添加了所有的數據集和標簽之后,點擊網頁上方的Train,開始訓練模型。
一小會之后,點擊網頁上方的performance,就可以看到這次訓練的結果了。
這里簡單解釋一下Precision和Recall,這是兩個評估模型好壞的主要指標。
簡單來說,兩個數都是越大越好。在這個項目中,以Brown Bear為例:
Precision就是識別出來的結果的准確率,即在所有被識別為棕熊的圖片中真正有棕熊的圖片所占的比例;而Recall則是測試結果中正確識別為棕熊的圖片占測試集中所有棕熊圖片的比例。
這時再點擊界面右上角的齒輪,可以看到免費用戶每個項目能夠使用的服務額度:
一共可以上傳5000張圖片,創建50個不同標簽,保存10次迭代的結果。
這十次迭代有什么用呢?當需要增刪標簽、給標簽添加或刪除訓練圖片時,這次再訓練,就會花費掉一次迭代。
這些都是當前項目的總數而不是累計值。對於一般的免費用戶,這基本上就相當於你可以隨意使用這項服務了,如果有大量的訓練數據,那么建議您還是訂閱Azure雲服務,Azure秉持着使用多少,收費多少的原則,即使收費,也仍然良心。
然后選擇剛剛訓練好的這次迭代,點擊Export
視覺認知服務一共提供了適用於四種平台的模型導出,對三大操作系統都能支持。
選擇ONNX,這個格式由微軟、臉書、亞馬遜等大廠鼎力支持,點擊Export,等待服務器把模型導出,然后點擊Download,即可下載模型。最后得到了一個.onnx文件,然后就可以使用它來構建應用了。

如果需要上傳大量的圖片數據,那么點擊鼠標的方式肯定不夠方便,微軟同時提供了代碼的支持,詳見官方文檔:

https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/home

四、使用Windows ML構建應用

這次不寫Winform程序,而是搭建一個識別熊的UWP的AI應用,通過這個應用來教大家如何使用Windows ML導入模型。

這部分的代碼已經完成了,請使用git克隆samples-for-ai到本地,UWP項目的代碼在/samples-for-ai/projects/BearClassificationUWPDemo中。

在運行代碼之前,請先安裝開發UWP所需的工作負載,流程如下:

  1. 打開Visual Studio Installer
  2. 在工作負載中勾選Universal Windows Platform development
  3. 在單個組件一欄中下拉到最下方,確認Windows 10 SDK(10.0.17134.0)已被勾選上,這是使用Windows ML開發的核心組件




另外,請將您的操作系統更新到1803版本,否則本程序將不能安裝。

如果您將進行類似的開發,請將UWP項目設置成最低運行目標版本為17134,否則對於版本低於17134的用戶,在運行時會出現:

"Requested Windows Runtime type 'Windows.AI.MachineLearning.Preview.LearningModelPreview' is not registered."

詳見:https://github.com/MicrosoftDocs/windows-uwp/issues/575

安裝需要的時間比較長,可以先看看UWP的視頻教程,做一做頭腦預熱: https://www.bilibili.com/video/av7997007

Visual Studio 和 Windows 更新完畢后,我們打開CustomVisionApp.sln,運行這個程序。

你可以從必應上查找一些熊的圖片,復制圖片的URL,粘貼到輸入框內,然后點擊識別按鈕;或者,點擊瀏覽按鈕,選擇一張本地圖片,點擊確定,你就可以看到識別結果了:




現在來看看這個程序是怎么實現的。

我們來梳理一下這個應用的邏輯,這個應用的邏輯與上一篇博客中的手寫數字識別大體上是一樣的:

  1. 導入模型
  2. 按下按鈕后,通過某種方式獲取要用來識別的圖片
  3. 將圖片交給模型識別
  4. 將圖片與識別結果展示在界面上

1. 文件結構:

文件結構見下圖:

  • Assets文件夾存放了這個項目的資產文件,比如程序圖標等等,在本示例程序中,.onnx文件也存放在其中。
  • Strings文件夾存放了用於本地化與全球化資源文件,這樣可以支持不同的語言。
  • ViewModel文件夾中則存放了本項目的關鍵代碼,整個程序運行的邏輯都在ResultViewModel.cs中
  • BearClassification.cs則是系統自動生成的模型包裝文件
  • MainPage.xaml是程序的UI布局文件

2. 核心代碼一:BearClassification.cs

這部分的代碼是自動生成的,教程詳見鏈接:https://docs.microsoft.com/zh-cn/windows/uwp/machine-learning/

  1. 將.onnx文件添加到UWP項目的Assets文件夾中,隨后將自動生成一個對應的包裝.cs文件,在本例中為BearClassification.cs
  2. 由於目前存在的一些BUG,生成的類名會有亂碼,需要將亂碼替換為別的字符串。
  3. 修改BearClassification.onnx屬性->生成操作,將其改為內容,確保在生成時,能夠調用到這個模型。

生成的文件共有三個類:

  • BearClassificationModelInput:定義了該模型的輸入格式是VideoFrame
  • BearClassificationModelOutput:定義了該模型的輸出為一個list和一個dict,list存儲了所有標簽按照probability降序排列,dict則存儲了標簽與概率的鍵值對
  • BearClassificationModel:定義了該模型的初始化函數與推理函數
// 模型的輸入格式為VideoFrame public sealed class BearClassificationModelInput { public VideoFrame data { get; set; } } // 模型的輸出格式,其中包含了一個列表:classLabel和一個字典:loss // 列表中包含每種熊的標簽,按照概率降序排列 // 字典中則包含了每種熊的標簽和其概率,按照用戶在創建模型時的添加順序排列 public sealed class BearClassificationModelOutput { public IList<string> classLabel { get; set; } public IDictionary<string, float> loss { get; set; } public BearClassificationModelOutput() { this.classLabel = new List<string>(); this.loss = new Dictionary<string, float>(){...} } } // 模型的包裝類,提供了兩個函數 // CreateBearClassificationModel:從.onnx文件中創建模型 // EvaluateAsync:對輸入對象進行評估,並返回結果 public sealed class BearClassificationModel { private LearningModelPreview learningModel; public static async Task<BearClassificationModel> CreateBearClassificationModel(StorageFile file) { ... } public async Task<BearClassificationModelOutput> EvaluateAsync(BearClassificationModelInput input) { ... } }

3. 核心代碼二:ResultViewModel.cs

通過之前的運行可以發現:每次識別圖片,UI中的內容需要進行頻繁地更新,為了簡化更新控件內容的代碼邏輯,這個程序使用UWP開發中常用的MVVM(model-view-viewmodel)這一組合模式開發,使用“綁定”的方式,將UI控件與數據綁定起來,讓數據與界面自動地同步更新,簡化了代碼邏輯,保證了ResultViewModel職責單一。

綁定源(ResultViewMode.cs) 綁定目標(MainPage.xaml)
string BearUrl TextBox InputUriBox
ObservableCollection Results ListView ResultArea
BitmapImage BearImage Image DisplayArea
string Description TextBox DescribeArea
ICommand RecognizeCommand Button RecognizeButton
ICommand BrowseCommand Button BrowseButton

綁定好之后,程序還需要一系列邏輯才能運行,這里就包括:

導入與初始化模型:

在程序一開始,需要調用LoadModel進行模型初始化工作。

private async void LoadModel() { //導入模型文件,實例化模型對象 StorageFile modelFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/BearClassification.onnx")); model = await BearClassificationModel.CreateBearClassificationModel(modelFile); }

圖片推理:

本程序提供了兩種方式訪問圖片資源:

  1. 通過URL訪問網絡圖片
  2. 通過文件選取器訪問本地圖片
private async void EvaluateNetPicAsync() { try { ... //BearClassification要求的輸入格式為VideoFrame //程序需要以stream的形式從URL中讀取數據,生成VideoFrame var response = await new HttpClient().GetAsync(BearUrl); var stream = await response.Content.ReadAsStreamAsync(); BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream.AsRandomAccessStream()); VideoFrame imageFrame = VideoFrame.CreateWithSoftwareBitmap(await decoder.GetSoftwareBitmapAsync()); //將videoframe交給函數進行識別 EvaluateAsync(imageFrame); } catch (Exception ex){ ... } } private async void EvaluateLocalPicAsync() { try { ... // 從文件選取器中獲得文件 StorageFile file = await openPicker.PickSingleFileAsync(); var stream = await file.OpenReadAsync(); ... // 生成videoframe BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream); VideoFrame imageFrame = VideoFrame.CreateWithSoftwareBitmap(await decoder.GetSoftwareBitmapAsync()); // 將videoframe交給函數進行識別 EvaluateAsync(imageFrame); } catch (Exception ex){ ... } } private async void EvaluateAsync(VideoFrame imageFrame) { //將VideoFrame包裝進BearClassificationModelInput中,交給模型識別 //模型的輸出格式為BearClassificationModelOutput //其中包含一個列表,存儲了每種熊的標簽名稱,按照probability降序排列 //和一個字典,存儲了每種熊的標簽,和對應的probability //這里取出輸出中的字典,並對其進行降序排列 var result = await model.EvaluateAsync(new BearClassificationModelInput() { data = imageFrame }); var resultDescend = result.loss.OrderByDescending(p => p.Value).ToDictionary(p => p.Key, o => o.Value).ToList(); //根據結果生成圖片描述 Description = DescribResult(resultDescend.First().Key, resultDescend.First().Value); Results.Clear(); foreach (KeyValuePair<string, float> kvp in resultDescend) { Results.Add(resourceLoader.GetString(kvp.Key) + " : " + kvp.Value.ToString("0.000")); } } 

五、使用其他方法構建應用

同樣,用之前使用Visual Studio Tools for AI提供的推理類庫生成器也能夠構建相似的應用。想看視頻教程的請移步:

【教程】普通程序員一小時入門AI應用——看圖識熊(不含公式,包會)

該教程講解了如何使用模型瀏覽工具Netron

想看圖文教程請繼續往下看:

1. 界面設計

創建Windows窗體應用(.NET Framework)項目,這里給項目起名ClassifyBear。

注意,項目路徑不要包含中文。

在解決方案資源管理器中找到Form1.cs,雙擊,打開界面設計器。從工具箱中向Form中依次拖入控件並調整,最終效果如下圖所示:

左側從上下到依次是:

  • Label控件,將內容改為“輸入要識別的圖片地址:”
  • TextBox控件,可以將控件拉長一些,方便輸入URL
  • Button控件,將內容改為“識別”
  • Lable控件,將label的內容清空,用來顯示識別后的結果。因為label也沒有邊框,所以在界面看不出來。可以將此控件的字體調大一些,能更清楚的顯示推理結果。

右側的控件是一個PictureBox,用來預覽輸入的圖片,同時,我們也從這個控件中取出對應的圖片數據,傳給我們的模型推理類庫去推理。建議將控件屬性的SizeMode更改為StretchImage,並將控件長和寬設置為同樣的值,保持一個正方形的形狀,這樣可以方便我們直觀的了解模型的輸入,因為在前面查看模型信息的時候也看到了,該模型的輸入圖片應是正方形。

2. 查看模型信息

在將模型集成到應用之前,我們先來看一看模型的基本信息,比如模型需要什么樣的輸入和輸出。打開Visual Studio中的AI工具菜單,選擇模型工具下的查看模型,會啟動Netron模型查看工具。該工具默認不隨Tools for AI擴展一起安裝,第一次使用時可以按照提示去下載並安裝。

Netron打開后,點擊Open model選擇打開之前下載的BearModel.onnx文件。然后點擊左上角的漢堡菜單顯示模型的輸入輸出。

上圖中可以看到該模型需要的輸入data是一個float數組,數組中要求依次放置227*227圖片的所有藍色分量、綠色分量和紅色分量,后面程序中調用時要對輸入圖片做相應的處理。

上圖中還可以看到輸出有兩個值,第一個值loss包含所有分類的得分,第二個值classLabel是確定的分類的標簽,這里只需用到第二個輸出即可。

3. 封裝模型推理類庫

由於目前模型推理用到的庫只支持x64,所以這里需要將解決方案平台設置為x64。打開解決方案資源管理器,在解決方案上點右鍵,選擇配置管理器。

在配置管理器對話框中,點開活動解決方案平台下拉框,選擇新建

在新建解決方案平台對話框中,輸入新平台名x64,點擊確定即可

下面添加模型推理類庫,再次打開解決方案資源管理器,在解決方案上點右鍵,選擇添加,然后選擇新建項目。

添加新項目對話框中,將左側目錄樹切換到AI Tools下的Inference,右側選擇模型推理類庫,下方填入項目名稱,這里用Model作為名稱。

確定以后會出現檢查環境的進度條,耐心等待一會就可以出現模型推理類庫創建向導對話框。

點擊模型路徑后面的瀏覽按鈕,選擇前面下載的BearModel.onnx模型文件。

注意,這里會出現幾處錯誤提示,我們需要手動修復一下。首先會看到“發現不支持的張量的數據類型”提示,可以直接點確定。

確定后如果彈出“正在創建項目…”的進度條,一直不消失,這里只需要在類名后面的輸入框內點一下,切換下焦點即可。

然后,我們來手動配置一下模型的相關信息。類名輸入框中填入模型推理類的名字,這里用Bear。然后點擊推理接口右側的添加按鈕,在彈出的編輯接口對話框中,隨便起個方法名,這里用Infer。輸入節點的變量名和張量名填入data,輸出節點的變量名和張量名填入classLabel,字母拼寫要和之前查看模型時看到的拼寫一模一樣。然后一路確定,再耐心等待一會,就可以在解決方案資源管理器看到新建的模型推理類庫了。

還有一處錯誤需要手動修復一下,切換到解決方案資源管理器,在Model項目的Bear目錄下找到Bear.cs雙擊打開,將函數Infer的最后一行

return r0;

替換為

List<List<string>> results = new List<List<string>>(); results.Add(r0); return results;

至此,模型推理類庫封裝完成。相信Tools for AI將來的版本中會修復這些問題,直接選擇模型文件創建模型推理類庫就可以了。

4. 使用模型推理類庫

首先添加對模型推理類庫的引用,切換到解決方案資源管理器,在ClassifyBear項目的引用上點右鍵,選擇添加引用。

在彈出的引用管理器對話框中,選擇項目、解決方案,右側可以看到剛剛創建的模型推理類庫,勾選該項目,點擊確定即可。

在Form1.cs上點右鍵,選擇查看代碼,打開Form1.cs的代碼編輯窗口。

添加兩個成員變量

// 使用Netron查看模型,得到模型的輸入應為227*227大小的圖片 private const int imageSize = 227; // 模型推理類 private Model.Bear model;

回到Form1的設計界面,雙擊Form的標題欄,會自動跳轉到代碼頁面並添加了Form1_Load方法,在其中初始化模型推理對象

private void Form1_Load(object sender, EventArgs e) { // 初始化模型推理對象 model = new Model.Bear(); }

回到Form1的設計界面,雙擊識別按鈕,會自動跳轉到代碼頁面並添加了button1_Click方法,在其中添加以下代碼:

首先,每次點擊識別按鈕時都先將界面上顯示的上一次的結果清除

// 識別之前先重置界面顯示的內容 label1.Text = string.Empty; pictureBox1.Image = null; pictureBox1.Refresh();

然后,讓圖片控件加載圖片

bool isSuccess = false; try { pictureBox1.Load(textBox1.Text); isSuccess = true; } catch (Exception ex) { MessageBox.Show($"讀取圖片時出現錯誤:{ex.Message}"); throw; }

如果加載成功,將圖片數據傳給模型推理類庫來推理。

if (isSuccess) { // 圖片加載成功后,從圖片控件中取出227*227的位圖對象 Bitmap bitmap = new Bitmap(pictureBox1.Image, imageSize, imageSize); float[] imageArray = new float[imageSize * imageSize * 3]; // 按照先行后列的方式依次取出圖片的每個像素值 for (int y = 0; y < imageSize; y++) { for (int x = 0; x < imageSize; x++) { var color = bitmap.GetPixel(x, y); // 使用Netron查看模型的輸入發現 // 需要依次放置227 *227的藍色分量、227*227的綠色分量、227*227的紅色分量 imageArray[y * imageSize + x] = color.B; imageArray[y * imageSize + x + 1* imageSize * imageSize] = color.G; imageArray[y * imageSize + x + 2* imageSize * imageSize] = color.R; } } // 模型推理類庫支持一次推理多張圖片,這里只使用一張圖片 var inputImages = new List<float[]>(); inputImages.Add(imageArray); // 推理結果的第一個First()是取第一張圖片的結果 // 之前定義的輸出只有classLabel,所以第二個First()就是分類的名字 label1.Text = model.Infer(inputImages).First().First(); }

注意,這里的數據轉換一定要按照前面查看的模型的信息來轉換,圖片大小需要長寬都是227像素,並且要依次放置所有的藍色分量、所有的綠色分量、所有的紅色分量,如果順序不正確,不能達到最佳的推理結果。

5. 測試

編譯運行,然后在網上找一張熊的圖片,把地址填到輸入框內,然后點擊識別按鈕,就可以看到識別的結果了。注意,這個URL應該是圖片的URL,而不是包含該圖片的網頁的URL。

六、下一步?

本篇博客我們學會了使用定制化視覺服務與在UWP應用中集成定制化視覺服務模型。這里我提兩個課后習題:(想不到吧)

  1. 當訓練含有多個標簽、大量圖片數據時,如何做到一鍵上傳圖片並訓練?

  2. 如何通過調用REST接口的方式完成對圖片的推理?

提示:請看看定制化視覺服務給我們提供的API,這一題肯定是要寫代碼做的
https://docs.microsoft.com/en-us/azure/cognitive-services/custom-vision-service/home

加油!

七、內容預告

接下來我們將會陸續推出:

  1. 微軟認知服務使用教程
  2. 模型訓練及推理的通常流程及原理
  3. 模型轉換工具的使用
  4. 開放AI平台-大規模計算資源調度系統

請在下方留言,告知我們您最想閱讀哪個教程,我們將優先考慮。

如果您有別的想要了解的內容,也可以在評論區留言。

 

AI應用開發基礎傻瓜書系列的目錄~

寫在前面,為啥要出這個系列的教程呢?

總的說來,我們現在有了很多非常厲害的深度學習框架,比如tensorflow,pytorch,paddlepaddle,caffe2等等等等。然而,我們用這些框架在搭建我們自己的深度學習模型的時候,到底做了一些什么樣的操作呢?我們試圖去閱讀框架的源碼來理解框架到底幫助我們做了些什么,但是……很難!很難!很難!因為深度學習是需要加速啦,分布式計算啦,所以框架做了很多很多的優化,也讓像我們這樣的小白難以理解這些框架的源碼。所以,為了幫助大家更進一步的了解神經網絡模型的具體內容,我們整理了這樣一個系列的教程。

對於這份教程的內容,如果沒有額外的說明,我們通常使用如下表格的命名約定

符號 含義
X 輸入樣本
Y 輸入樣本的標簽
Z 各層運算的結果
A 激活函數結果
大寫字母 矩陣或矢量,如A,W,B
小寫字母 變量,標量,如a,w,b

適用范圍

沒有各種基礎想學習卻無從下手哀聲嘆氣的玩家,請按時跟蹤最新博客,推導數學公式,跑通代碼,並及時提出問題,以求最高療效;

深度學習小白,有直觀的人工智能的認識,強烈的學習欲望和需求,請在博客的基礎上配合代碼食用,效果更佳;

調參師,訓練過模型,調過參數,想了解框架內各層運算過程,給玄學的調參之路添加一點心理保障;

超級高手,提出您寶貴的意見,給廣大初學者指出一條明路!

前期准備

環境:

windows(Linux也行),python(最好用3),anaconda(或者自己裝numpy之類的),tensorflow(嫌麻煩地請看這里《AI應用開發實戰 - 從零開始配置環境》,tools for AI(按照鏈接教程走的就不用管這個了)。

自己:

清醒的頭腦(困了的同學請自覺泡茶),紙和筆(如果像跟着推公式的話),鬧鍾(防止久坐按時起來轉轉),厚厚的衣服(有暖氣的同學請忽略)

目錄

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM