AI應用開發實戰 - 定制化視覺服務的使用


AI應用開發實戰 - 定制化視覺服務的使用

本篇教程的目標是學會使用定制化視覺服務,並能在UWP應用中集成定制化視覺服務模型。

前一篇:AI應用開發實戰 - 手寫識別應用入門

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

聯系我們
OpenmindChina@microsoft.com

零、定制化視覺服務簡介

有的時候,在構建應用的過程中,在缺少強大計算資源與高性能算法的情況下,我們不一定需要自己從零開始訓練模型。我們需要用的一些輪子,已經有人給我們造好了。

就比如:

微軟提供的定制化視覺服務。

在機器學習應用中,任何情況下都需要一個或大或小的模型。而怎么得到這個模型是其中最復雜的部分。定制化視覺服務相當於在雲端提供了一個生成模型的方法,把模型相關的復雜的算法都簡化了。同時,它不僅能夠讓用戶自己管理訓練數據,定義自己的分類問題,而且支持一鍵訓練,一鍵導出模型;不僅能導出適配所有主流框架的模型,而且可以生成REST接口,讓程序通過接口獲取圖片分類的結果。這樣給用戶提供了多種集成模型的方法和選擇,盡可能滿足用戶的各種需求,這也正是定制化視覺服務的強大之處。同時,通過定制化服務來生成模型,需要的數據量可以非常少,訓練過程相對來說也很快。使用上也是非常的方便。

本篇教程,就教大家如何使用定制化視覺服務。

定制化視覺服務官方地址 :https://customvision.ai/

一、准備微軟賬號

使用該服務需要准備微軟賬號,可以直接在定制化視覺服務官方地址上創建。

二、創建定制化視覺服務

截圖 操作
進入官方網站,點擊SIGN IN,目前定制化視覺服務提供了免費試用版,可以體驗定制化視覺服務。
登錄后,然后界面會提示要求同意一些條約。
條約的大致內容就是,個人必須在微軟要求的規則下使用微軟提供的這項服務。請勾選agree
此時,界面會提示注冊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平台-大規模計算資源調度系統

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

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


免責聲明!

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



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