Categories
程式開發

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器


本文最初發表在Towards Data Science 博客,經原作者Vinay Kudari 授權,InfoQ 中文站翻譯並分享。

本文作者帶你演示如何使用Fastai——基於PyTorch 的高級深度學習庫,用15 行代碼來創建一個口罩分類器。

Fastai 是一個基於PyTorch 的高級深度學習庫。 Jeremy Howard 最近推出了該庫的最新版本,同時還推出了非常方便、適合初學者的一本書和一門課程。我對它的抽象程度相當驚訝,它能幫助你在短短幾分鐘內就創建出最先進的模型,而無需擔心背後的數學問題。

本文是為初學者寫的,就算你沒有多少編程經驗,你也能按照這篇文章做出來。讀完本文後,你就可以從頭開始寫一段代碼,可以識別你最喜歡的超級英雄,甚至可以通過動物發出的聲音來識別它。

下面是口罩分類器的結果快照,我使用了少量的訓練數據、幾行代碼,以及在一個GCP 集群上幾分鐘的訓練。點擊這裡可以免費設置你自己的FastAI GPU VM。

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器 1

為了實現這一結果,我們首先需要在圖像中找到人臉,也就是定位,然後對每個人臉進行分類,並根據它所述的類別(綠色:
with_mask;紅色:no_mask;黃色:mask_worn_inproperly)繪製出彩色邊框。現在,讓我們來了解多類圖像分類問題。

該項目的代碼可以在這裡找到。

我會在寫一些代碼的同時,解釋深度學習和計算機視覺的一些基本概念。我強烈建議你在Jupyter Notebook 上逐行運行代碼,因為我們理解了抽象函數背後的思想。

處理數據

該庫分為幾個模塊,主要有表格、文本和視覺。由於我們今天的問題將涉及到視覺,因此,讓我們從vision 庫導入我們需要的all 函數:

In [1]: from fastai.vision.all import *

就像我們如何通過觀察圖像來學習識別物體一樣,計算機也需要數據來識別圖像。為了檢測出口罩,我整理了一個從Kaggle 和其他來源收集的帶有標籤的數據集,你可以從這裡下載。

我們存儲數據集所在的路徑。 Path 返回一個apathlib 對象,它可以非常容易地用於執行某些文件操作。

In [2]: DATASET_PATH = Path('/home/kavi/datasets')

在訓練模型(即教會模型識別圖像的算法)之前,我們首先需要告訴它一些事情:

  • 預期的輸入和輸出是什麼?問題域是什麼?
  • 數據位於何處,以及如何標記?
  • 我們需要保留多少數據來評估模型的性能?
  • 我們需要轉換數據嗎?如果需要,又該如何轉換?

Fastai 有一個名為DataBlock 的函數,它超級靈活,可以輸入上述問題並準備一個模板:

In [3]: mask_datablock = DataBlock(
           get_items=get_image_files,
           get_y=parent_label,
           blocks=(ImageBlock, CategoryBlock),
           item_tfms=RandomResizedCrop(224, min_scale=0.3),
           splitter=RandomSplitter(valid_pct=0.2, seed=100),
           batch_tfms=aug_transforms(mult=2)
        )
  • get_image_files 函數遞歸地獲取給定路徑中的所有圖像文件文職並返回它們,這樣我們就可以告訴Fastai 在哪裡get_items。
  • 在我們的數據集中,我將圖像放在根據類別命名的單獨文件夾中,parent_label 函數根據路徑返回文件的父目錄。

例如:

parent_label('/home/kavi/datasets/with_mask/1.jpg') => 'with_mask'

你可以根據數據的標籤方式來編寫自己的函數:

  • 知道輸入圖像和目標標籤的文件路徑後,我們需要根據問題的類型對數據進行預處理。圖像的預處理步驟示例包括使用Pillow 從文件路徑創建圖像並將其轉換為張量。

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器 2

圖像在電腦中是如何表示的?

每張圖像都是一個像素密度矩陣。每個值的範圍為0~255。0 是各通道最黑的,255 是各通道最亮的。

彩色圖像是一個三層矩陣/三價張量。每層由紅(Red)、綠(Green)、藍(Blue)強度組成,而黑白圖像則是一維矩陣。

我們將TransformBlock 類型的元組(input_transformation, output_transformation) 傳遞給blocks。在我們的問題中,需要預測圖像的類別,因此傳遞(ImageBlock, CategoryBlock)。如果假設你想根據一個人的圖片來預測他的年齡,你需要傳遞(ImageBlock, RegressionBlock)。

  • 深度學習模型在所有圖像大小相同的情況下效果最好,而且當分辨率較低時,模型學習的速度更快。我們將通過item_tfms 傳遞一個合適的resize 函數來告訴如何調整圖像的大小,這個函數將被應用到每張圖像上。

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器 3

使用ResizeMethod.Squish、ResizeMethod.Pad 和ResizeMethod.Crop 方法調整圖像大小。

Fastai 提供了各種調整大小的方法:裁剪、填充或壓縮。它們中每個方法都有一些問題。有時候,我們甚至可能會丟失一些關鍵信息,就像在中心裁剪之後的第三張圖像中一樣。

我們可以使用RandomResizedCrop 來解決這個問題。在這裡,我們隨機選擇圖片的一部分,並對模型進行幾個輪數(epoch)的訓練(即一次完整地遍歷數據集中所有圖片),每個輪數涵蓋了每張圖片的大部分區域。 min_scale 決定了每次選擇圖像的最小值。

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器 4

在訓練數據集上進行模型正確率評估會導致評分出現偏差,可能會導致對未見過的數據的性能較差。我們需要告訴DataBlock API 留出一部分經過預處理的數據來評估模型的性能。算法看到數據稱為訓練數據,保留的數據稱為驗證數據。通常,數據集將定義驗證集,但在我們的示例中,我們並沒有驗證集,因此,需要將split 函數傳遞給splitter。

Fastai 有幾個split 函數,讓我們用RandomSplitter 來解決今天的問題,valid_pct 將確定需要保留的訓練數據部分,並且seed 將確保始終保留相同的隨機圖像。

  • 擁有多樣化的數據集對於任何深度學習模型的性能都至關重要。那麼,如果你沒有足夠的數據量,該怎麼辦呢?我們在現有數據的基礎上生成新的數據,這個過程稱為“數據增強”(Data augmentation)。

數據增強(Data augmentation)指的是創建輸入數據的隨機變化,使得它們看起來不同,但實際上並不改變數據的含義。
-快速手冊

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器 5

使用aug_transforms() 對單個泰迪熊圖像進行增強。

上面的圖片都是有一張泰迪熊的照片生成的。在現實世界中,我們經常需要對看不見的數據進行預測,如果模型只是記住訓練數據,那麼它的性能會很差(過擬合),而應該理解數據。事實證明,在很多情況下,數據增強對提高模型的性能很有幫助。

在Fastai 中,我們有一個預定義的函數aug_transforms,它執行一些默認的圖像轉換,比如翻轉、改變亮度、傾斜等等。我們將這個函數傳遞給batch_tfms,值得注意的是,這些轉換是在GPU 上執行的(如果可用的話)。

DataBlock 保存將在數據集上執行的列表指令。這將充當創建DataLoader 的藍圖,該DataLoader 採用我們的DataSet 路徑,按照DataBlock 對象的定義對圖像應用預處理轉換,並將其加載到GPU 中。數據被加載之後,batch_tfms 數據將應用於該批。默認批大小為64,你可以通過bs=n 傳遞給數據讀取器函數,根據你的GPU 內存增加或減少批大小。

提示:!nvidia-smi 命令可以在Jupyter Notebook 隨時執行,以了解GPU 使用的詳細信息。你可以重新啟動內核以釋放內存。

In[10]: dls = mask_datablock.dataloaders(DATASET_PATH)

dls 是一個DataLoader 對象,它包含訓練數據和驗證數據。你可以使用dls.show_batch() 查看轉換後的數據。

訓練口罩分類器模型

模型是一組值,也稱為權重,可用於識別模式。模式識別無處不在,對於生物來說,這是一個認知過程,發生在大腦中,而我們卻沒有意識到它。就像我們小時候通過盯著顏色、物體和字母學會識別一樣。訓練模型就是確定一組正確的權值,它可以解決一個特定的問題,在我們的示例中,是將一幅圖像分類為三類:with_mask、without_mask、mask_weared_incorrect。

如何快速訓練模型?

作為成年人,我們幾乎可以立即學會識別物體,這是因為我們從出生起就一直處於學習模式。最初,我們學會了辨別顏色,然後是簡單的物體,比如球、花之類的。幾年後,我們就能夠識別人和其他復雜的物體。類似的,在機器學習中,我們有預訓練模型,這些模型已經經過訓練來解決類似的問題,並且可以通過修改來解決我們的問題。

將預訓練模型用於不同於最初訓練的任務,稱為遷移學習。
-快速書

resnet34 就是這樣一個模型,它是在ImageNet 數據集上訓練出來的,該數據集包含大約130 萬張圖片,可以將圖片分類為1000 多種類別。在我們的問題中,只有三個類別,因此,我們使用初始層來識別基本的模式,如線、角、簡單的形狀,然後在最後一層進行重新訓練。

In[11]: learn = cnn_learner(dls, resnet34, metrics=error_rate)

Fastai 提供了一個cnn_learner 函數,該函數在計算機視覺模型的訓練中特別有用。其中包括DataLoader 對象dls、預訓練模型resnet34(此處的34 表示有34 層),以及一個度量error_rate(用於計算在驗證數據上分類錯誤的圖像百分比)。

度量是使用驗證集度量模型預測質量的函數。
-快速手冊

遷移學習是如何工作的?

最初,我們用一個或多個具有隨機權重的新層替換我們預訓練模型的最後一層,這部分被稱為頭部。我們使用反向傳播算法更新頭部的權重,有關這部分我將在另一篇文章中闡述。

Fastai 提供了一種方法fine_tune,該方法執行調整預訓練模型的任務,以使用我們整理的數據來解決我們的特定問題。

In[12]: learn.fine_tune(4)

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器 6

fine_tune 函數的輸出。

我們將一個數字傳遞給fine_tune,它告訴你需要訓練多少個輪數(即完全瀏覽數據集的次數)。這是你需要處理的事情,並沒有硬性規定。這取決於你的問題、數據集和你希望花在訓練上的時間。你可以使用不同的輪數次數來運行該函數。

Jeremy 的提示和我的經驗教訓:

  • 訓練大量的輪數可能會導致過擬合,這可能會導致在未見過數據上的性能不佳。如果驗證損失在連續的輪數中持續增加,意味著我們的模型正在記憶訓練數據,就需要停止訓練。

  • 我們可以對模型進行不同分辨率的訓練來提高性能。例如,使用224 × 224 像素進行訓練,然後再使用112 × 112 像素進行訓練。

  • 數據增強在一定程度上有助於與防止過擬合。

使用模型進行推理

現在,我們已經有了經過訓練的口罩分類器,它可以根據一個人的臉部照片來分類一個人是否正確地、或錯誤地佩戴口罩。我們可以導出這個模型,並用它來預測其他類。

In[13]: learn.export()
In[14]: learn.predict('path/to/your/image.jpg')

我們可以使用load_learner(‘path/to/model/export.pkl’) 來加載這個模型。

如何使用fastai v2 在15 行代碼下構建深度學習圖像分類器 7

Web App 的截圖

我接著做了一個REST API,將這個模型公開到互聯網上,並在朋友Vaishnavi 和Jaswanth 的幫助下,製作了一個Web 應用程序,它獲取輸入圖像,並根據人臉所述的類別以及人臉類別的計數繪製邊框。 Web 應用程序已上線,網址為:https://findmask.ml

結語

現在,你可以構建圖像分類器了。你還可以使用這一技術將聲音轉換成譜圖或任何合適的圖像形式來進行分類。

作者介紹:

Vinay Kudari,機器學習工程師、社會企業家。

原文鏈接:

https://towardsdatascience.com/build-any-deep-learning-image-classifier-under-15-lines-of-code-using-fastai-v2-123c81c13b