隨著人工智慧的快速發展,電腦視覺技術已成為許多應用領域的核心。本文旨在引導讀者運用機器學習技術構建高效的電腦視覺模型,涵蓋資料集建立、預處理、模型訓練、評估與佈署等關鍵環節。本文以花卉影像分類別為範例,深入淺出地介紹了相關技術和工具,並探討了深度學習模型的優勢以及如何利用 TensorFlow 和 Keras 等框架進行實作。此外,本文也涵蓋了影像資料的視覺化、張量操作以及如何有效地讀取和處理大型資料集。

本文簡介與使用

本文探討電腦視覺領域中的機器學習技術,涵蓋從基礎到進階的主題,旨在幫助讀者建立生產級別的電腦視覺模型。內容包括資料集建立、預處理、模型訓練、監控評估和佈署等關鍵步驟,並介紹了最新的技術趨勢和實用工具。

本文結構安排

  • 第5至9章:逐步講解建立生產級別電腦視覺機器學習模型的標準流程,包括資料集建立(第5章)、預處理(第6章)、訓練(第7章)、監控與評估(第8章)以及佈署(第9章)。這些方法適用於第3和4章討論的各種模型架構和應用場景。
  • 第10章:介紹三個新興趨勢,包括建立端對端的容器化機器學習流程、嘗試無程式碼的影像分類別系統,以及為影像模型預測提供可解釋性。
  • 第11和12章:展示如何利用電腦視覺的基本構建模組解決各種問題,如影像生成、計數、姿態檢測等,並提供這些進階應用場景的實作方案。

本文使用的排版慣例

  • 斜體字:表示新術語、網址、電子郵件地址、檔名和副檔名。
  • 等寬字型:用於程式清單,以及在段落中參照程式元素,如變數或函式名稱、資料型別、環境變數、陳述式和關鍵字。
  • 等寬粗體:在程式碼片段中用於強調,以及顯示應由使用者逐字輸入的命令或其他文字。
  • 等寬斜體:顯示應由使用者提供的值或根據上下文確定的值所取代的文字。

使用程式碼示例

本文提供的補充材料(程式碼示例、練習等)可在GitHub下載。使用程式碼示例時,您可以在程式和檔案中使用它們,無需取得許可,除非您複製了大量的程式碼。

聯絡我們

鳴謝

本文作者感謝以下人員的貢獻:

  • Salem Haykal 和 Filipe Gracio 對每一章的詳細審閱。
  • Vishwesh Ravi Shrimali 和 Sanyam Singhal 對本文結構的建議。
  • Rajesh Thallam、Mike Bernico、Elvin Zhu 等多位專家對相關章節的審閱。
  • Google Cloud 使用者、團隊成員以及 Google Cloud Advanced Solutions Lab 的學員對改進說明的幫助。
  • TensorFlow、Keras 和 Google Cloud AI 工程團隊的支援。
  • 作者各自家人的支援。

電腦視覺中的機器學習

想像你坐在花園裡,觀察周圍發生的事情。你的身體中有兩個系統在運作:眼睛充當感測器,建立場景的表徵;認知系統則理解眼睛所看到的內容。因此,你可能會看到一隻鳥、一條蟲和一些動靜,並意識到鳥已經沿著小徑走下來,正在吃一條蟲(參見圖1-1)。

人類視覺與電腦視覺

人類視覺涉及我們的感官和認知系統。電腦視覺試圖模仿人類視覺能力,提供影像形成(模仿人類感官系統)和機器感知(模仿人類認知系統)的方法。模仿人類感官系統的重點在於硬體和感測器(如攝影機)的設計和擺放。

機器學習在電腦視覺中的角色

現代模仿人類認知系統的方法包括使用機器學習(ML)從影像中提取資訊。本文涵蓋的就是這些方法。當我們看到一張雛菊的照片時,我們的人類認知系統能夠將其識別為雛菊(參見圖1-2)。本文中建立的影像分類別機器學習模型透過從雛菊的照片開始,模仿了人類的這種能力。

機器學習的發展

如果你在2010年代初期讀一本關於電腦視覺的書,那時用來從照片中提取資訊的方法並不涉及機器學習。相反,你會學習到去噪、邊緣檢測、紋理檢測和形態學(根據形狀的)操作。隨著人工智慧(更具體地說是機器學習)的進步,這種情況已經改變了。

人工智慧(AI)探索電腦模仿人類能力的方法。機器學習是AI的一個子領域,透過向電腦展示大量資料並指示它們從中學習,從而教會電腦做到這一點。專家系統是AI的另一個子領域——專家系統透過程式設計電腦遵循人類邏輯來教會電腦模仿人類能力。在2010年代之前,像影像分類別這樣的電腦視覺任務通常透過構建定製的影像濾波器來實作專家制定的邏輯。如今,影像分類別是透過卷積網路(一種深度學習形式)實作的(參見圖1-3)。

機器學習與專家系統的比較

以圖1-2中的雛菊影像為例。機器學習方法透過向電腦展示大量影像及其標籤(或正確答案),教會電腦識別影像中的花卉型別。因此,我們會向電腦展示大量雛菊、鬱金香等的影像。根據這樣的標記訓練資料集,電腦能夠學會分類別它以前未曾遇到過的影像。這種過程將在第2章和第3章中討論。

另一方面,在專家系統方法中,我們首先會採訪一位人類植物學家,瞭解他們如何分類別花卉。如果植物學家解釋說雛菊(Bellis perennis,雛菊的科學名稱)由圍繞黃色中心的白色細長花瓣和綠色圓形葉片組成,我們會嘗試設計影像處理濾波器來匹配這些標準。例如,我們會尋找影像中白色、黃色和綠色的普遍性。然後,我們會設計邊緣濾波器來識別葉片的邊界,並匹配形態學濾波器來檢視它們是否符合預期的圓形。我們可能會在HSV(色調、飽和度、明度)空間中平滑影像,以確定花卉中心與花瓣顏色的對比。根據這些標準,我們可能會為影像評分,評估它是一朵雛菊的可能性。同樣,我們會為玫瑰、鬱金香、向日葵等設計和應用不同的規則集。要對新影像進行分類別,我們會選擇對該影像評分最高的類別。

AlexNet的突破

這種描述說明瞭建立影像分類別模型所需的相當大的定製工作。這就是為什麼影像分類別曾經具有有限的適用性。隨著2012年AlexNet論文的發表,這一切都改變了。作者Alex Krizhevsky、Ilya Sutskever和Geoffrey E. Hinton透過將卷積網路(在第3章中介紹)應用於ImageNet大型視覺識別挑戰賽(ILSVRC)的基準資料集,大大超越了當時任何現有的影像分類別方法。他們取得了15.3%的top-5錯誤率,而亞軍的錯誤率超過26%。像這樣的競賽中的典型改進幅度約為0.1%,因此AlexNet所展示的改進幅度是人們預期的100倍!這是一個引人注目的表現。

神經網路自20世紀70年代以來就已存在,而卷積神經網路(CNN)本身在那時也已經存在了二十多年—— Yann LeCun在1989年提出了這個想法。那麼,AlexNet有什麼新意呢?有四點:

圖形處理單元(GPUs)

卷積神經網路是一個很好的想法,但它們在計算上非常昂貴。AlexNet的作者在稱為GPU的專用晶片提供的圖形渲染函式庫上實作了卷積網路。GPU當時主要用於高階視覺化和遊戲。該論文將卷積分組,以使模型跨兩個GPU執行。GPU使訓練卷積網路變得可行(我們將在第7章中討論如何在GPU上分佈模型訓練)。

線性整流單元(ReLU)啟用函式

AlexNet的創造者在其神經網路中使用了一個稱為ReLU的非飽和啟用函式。我們將在第2章中更多地討論神經網路和啟用函式;目前,只需知道使用分段線性非飽和啟用函式使他們的模型收斂得更快。

# 示例程式碼:ReLU啟用函式
import numpy as np

def relu(x):
    return np.maximum(x, 0)

# 輸入範例
x = np.array([-1, 0, 1])
print(relu(x))  # 輸出:[0 0 1]

內容解密:

上述程式碼實作了ReLU啟用函式,用於神經網路中引入非線性。np.maximum(x, 0)確保所有小於0的值被設為0,而大於或等於0的值保持不變。這種非飽和特性有助於神經網路更快地收斂。

正規化技術與深度學習的重要性

在探討深度學習的過程中,我們經常會遇到一些關鍵技術和概念,它們對於提升模型的表現和穩定性至關重要。其中,正規化技術和模型的深度是兩個不可忽視的重要因素。

正規化技術

正規化技術的主要目的是防止模型的權重變得過大,從而導致數值不穩定。在使用ReLU(Rectified Linear Unit)啟用函式時,這種問題尤其突出。ReLU函式因其不飽和的特性,曾經使得神經網路的權重變得不穩定。然而,透過使用適當的正規化技術,可以有效地控制權重的大小,避免數值不穩定性的問題。

為什麼需要正規化?

  1. 防止過度擬合:正規化技術可以透過限制模型的複雜度,防止模型對訓練資料過度擬合,從而提高模型的泛化能力。
  2. 數值穩定性:正規化可以幫助保持模型的權重在一個合理的範圍內,避免因權重過大導致的數值不穩定性。

深度的重要性

模型的深度是指神經網路的層數。隨著層數的增加,模型可以學習到更加複雜和抽象的特徵表示。AlexNet的成功在很大程度上歸功於其增加的深度。透過結合ReLU啟用函式、GPU加速訓練和正規化技術,研究人員能夠訓練出比以往更深的神經網路。

為什麼深度如此重要?

  1. 特徵表示能力:更深的模型能夠學習到更加豐富和抽象的特徵,這對於影像分類別等任務至關重要。
  2. 模型表現:增加模型的深度通常能夠提高模型的表現,尤其是在處理複雜資料時。

深度學習的應使用案例項

深度學習已經在多個領域取得了巨大的成功,包括但不限於影像分類別、語音識別、自然語言處理等。以影像分類別為例,透過使用深度卷積神經網路(CNN),我們可以輕鬆地對影像進行分類別,無需手動設計複雜的特徵提取器。

光學字元識別(OCR)

OCR是一個典型的深度學習應使用案例項。傳統的OCR方法需要複雜的規則和範本匹配,而使用深度學習,可以將OCR轉化為一個影像分類別問題,從而簡化了流程並提高了準確率。

開始建立我們的機器學習模型

在下一章中,我們將開始建立我們的第一個機器學習模型,並探討如何使用Keras API來實作基本的機器學習原語和訓練模型。我們將使用一個包含近四千張花卉照片的資料集,並嘗試建立不同的機器學習模型來解決影像分類別問題。

影像表示和分類別

影像分類別是電腦視覺中的一個基本任務。我們將學習如何表示影像,並訓練基本的機器學習模型來進行分類別。同時,我們也將發現線性和全連線神經網路在影像分類別任務上的表現並不理想。

資料集介紹

我們將使用一個名為5-flowers的資料集,該資料集包含五種型別的花卉照片:雛菊、蒲公英、玫瑰、向日葵和鬱金香。每張影像都已經被標記上了正確的花卉型別。

程式碼實踐

讓我們開始我們的機器學習之旅,並探索如何使用Keras API來建立和訓練我們的模型。

影像分類別與電腦視覺:以花卉辨識為例

電腦視覺是人工智慧領域中一個重要的研究方向,其目標是使機器能夠理解和解釋影像內容。在本章中,我們將探討如何使用機器學習模型來進行影像分類別,特別是以花卉辨識為例。

5-Flowers 資料集介紹

5-Flowers 資料集是由 Google 建立並公開發布的資料集,包含五種不同花卉的影像。該資料集以 JPEG 格式儲存,並存放於 Google Cloud Storage 的公開儲存桶中(gs://cloud-ml-data/)。這個資料集非常適合用於學習和研究影像分類別任務。

為什麼不應將 5-Flowers 資料集視為範本?

雖然 5-Flowers 資料集是一個很好的學習資源,但它並不是建立訓練資料集的理想範本。主要原因包括:

  • 資料量不足:從頭開始訓練機器學習模型通常需要數百萬張影像。雖然有一些替代方法可以使用較少的影像,但盡可能收集更多的資料仍然是最佳實踐。
  • 資料格式不佳:將影像儲存為單獨的 JPEG 檔案效率很低,因為大部分的訓練時間將花費在等待資料讀取上。使用 TensorFlow Record 格式會是更好的選擇。
  • 資料內容:5-Flowers 資料集中的影像是「發現」的資料,也就是說它們不是專門為分類別任務而收集的。如果問題領域允許,應該更有目的地收集資料。
  • 標籤問題:影像標籤是一個重要的課題。5-Flowers 資料集是手動標籤的,這對於更大的資料集來說可能變得不切實際。

標準化影像收集

在實際應用中,可以透過標準化影像收集過程來簡化機器感知問題。例如,可以指定影像必須在受控條件下拍攝,具有平坦的照明和一致的縮放比例。這種方法在製造業中很常見,因為工廠條件可以被精確指定。

然而,必須注意的是,訓練資料集必須反映模型將被要求進行預測的條件。如果模型只在專業攝影師拍攝的花卉照片上進行訓練,那麼它很可能在業餘攝影師拍攝的照片上表現不佳,因為後者的照明、縮放和構圖選擇可能會有所不同。

讀取影像資料

要訓練影像模型,需要將影像資料讀入程式中。讀取標準格式(如 JPEG 或 PNG)的影像並將其準備好用於訓練機器學習模型的過程包括四個步驟:

import tensorflow as tf

def read_and_decode(filename, reshape_dims):
    # 1. 讀取檔案
    img = tf.io.read_file(filename)
    # 2. 將壓縮的字串轉換為 3D uint8 張量
    img = tf.image.decode_jpeg(img, channels=3)
    # 3. 將 3D uint8 轉換為 [0,1] 範圍內的浮點數
    img = tf.image.convert_image_dtype(img, tf.float32)
    # 4. 調整影像大小至所需尺寸
    return tf.image.resize(img, reshape_dims)

程式碼解析:

  1. 讀取檔案:使用 tf.io.read_file 將影像檔案讀入記憶體中。

    • img = tf.io.read_file(filename) 將檔案內容讀入 img 張量中。
  2. 解碼 JPEG 影像:使用 tf.image.decode_jpeg 將 JPEG 影像解碼為 3D 張量。

    • img = tf.image.decode_jpeg(img, channels=3)img 解碼為具有三個顏色通道(紅、綠、藍)的張量。
  3. 轉換資料型別和縮放:使用 tf.image.convert_image_dtype 將畫素值從 uint8 轉換為 float32,並縮放到 [0,1] 範圍內。

    • img = tf.image.convert_image_dtype(img, tf.float32) 將畫素值轉換為浮點數並進行縮放。
  4. 調整影像大小:使用 tf.image.resize 將影像調整至所需大小。

    • return tf.image.resize(img, reshape_dims) 將影像大小調整為指定的 reshape_dims

為什麼需要這些步驟?

  • 解碼和轉換:機器學習模型通常需要輸入資料具有一致的格式和範圍。將畫素值縮放到 [0,1] 範圍內有助於模型的穩定性和效能。
  • 調整大小:大多數機器學習模型要求輸入影像具有固定的大小,因此需要調整影像大小以符合模型的輸入需求。

張量(Tensor)是什麼?

在數學領域中,一維陣列被稱為向量(vector),而二維陣列則稱為矩陣(matrix)。張量(tensor)是一種能夠擁有任意維度的陣列,其維度數量被稱為秩(rank)。例如,一個具有12行和18列的矩陣,其形狀(shape)為(12, 18),秩為2。因此,張量可以具有任意形狀。

Python中常見的陣列數學函式庫稱為NumPy。雖然可以使用該函式庫建立n維陣列,但其運算並未經過硬體加速。例如,以下是一個形狀為(4)的一維陣列:

x = np.array([2.0, 3.0, 1.0, 0.0])

而這是一個形狀為(4, 3, 7, 8, 3)的五維零陣列(注意其形狀中有五個數字):

x5d = np.zeros(shape=(4, 3, 7, 8, 3))

要使用TensorFlow實作硬體加速,可以將NumPy陣列轉換為張量(Tensor),這是TensorFlow表示陣列的方式,方法如下:

tx = tf.convert_to_tensor(x, dtype=tf.float32)

也可以使用以下方法將張量轉換回NumPy陣列:

x = tx.numpy()

從數學角度來看,NumPy陣列和TensorFlow張量是相同的東西。然而,在實際應用中存在一個重要的差異:所有NumPy運算都在CPU上執行,而TensorFlow程式碼如果有可用的GPU,則會在GPU上執行。因此,執行以下程式碼:

x = x * 0.3

通常會比執行以下程式碼效率更低:

tx = tx * 0.3

一般來說,盡可能使用TensorFlow運算,能夠使程式更有效率。如果能夠向量化程式碼(以批次處理影像),使單一的張量運算取代多個小型的純量運算,也能提升效率。

內容解密:

上述程式碼展示了NumPy陣列與TensorFlow張量之間的轉換,以及兩者在運算效率上的差異。其中,tf.convert_to_tensor用於將NumPy陣列轉換為TensorFlow張量,而numpy()方法則用於將張量轉換回NumPy陣列。由於TensorFlow支援GPU加速,因此在可用GPU的情況下,使用TensorFlow運算通常比使用NumPy運算更有效率。

視覺化影像資料

為了確保正確讀取資料,視覺化幾張影像是非常重要的,這樣可以避免影像被錯誤旋轉或映象。視覺化影像也有助於評估機器感知問題的難度。

我們可以使用Matplotlib的imshow()函式來視覺化影像,但首先需要使用numpy()函式將TensorFlow張量轉換為NumPy陣列。

def show_image(filename):
    img = read_and_decode(filename, [IMG_HEIGHT, IMG_WIDTH])
    plt.imshow(img.numpy());

內容解密:

這段程式碼定義了一個名為show_image的函式,用於讀取並顯示指設定檔案名的影像。其中,read_and_decode函式負責讀取和解碼影像,而plt.imshow則用於顯示影像。由於img是一個TensorFlow張量,因此需要使用numpy()方法將其轉換為NumPy陣列,以便傳遞給imshow()函式。

讀取資料集檔案

現在我們已經知道如何讀取單張影像,但在訓練機器學習模型時,我們需要讀取多張影像並取得它們的標籤。

一種方法是使用glob()函式進行萬用字元匹配,以取得所有影像檔案的列表。例如:

tf.io.gfile.glob("gs://cloud-ml-data/img/flower_photos/*/*.jpg")

然後,根據檔案命名慣例,使用字串操作提取標籤。例如:

basename = tf.strings.regex_replace(
    filename,
    "gs://cloud-ml-data/img/flower_photos/", "")
label = tf.strings.split(basename, '/')[0]

內容解密:

這段程式碼展示瞭如何使用glob()函式取得所有影像檔案的列表,並根據檔案路徑提取標籤。其中,tf.strings.regex_replace用於移除檔案路徑的字首,而tf.strings.split則用於根據路徑分隔符號提取標籤。

然而,為了提高模型的泛化能力和重現性,最好預先將部分影像保留用於評估。在5-flowers資料集中,訓練和評估影像已經被分別儲存在兩個CSV檔案中:

gs://cloud-ml-data/img/flower_photos/train_set.csv
gs://cloud-ml-data/img/flower_photos/eval_set.csv

這些CSV檔案包含了檔案名稱和對應的標籤。

我們可以使用TextLineDataset讀取CSV檔案,並傳遞一個函式來處理每一行資料。

dataset = (tf.data.TextLineDataset(
    "gs://cloud-ml-data/img/flower_photos/train_set.csv").
    map(parse_csvline))

內容解密:

這段程式碼展示瞭如何使用TextLineDataset讀取CSV檔案,並使用map()函式對每一行資料進行處理。其中,parse_csvline是一個自定義函式,用於解析CSV檔案的每一行,提取檔案名稱和標籤,並讀取對應的影像。