1. 程式人生 > >【計算機視覺之三】運用k近鄰演算法進行圖片分類

【計算機視覺之三】運用k近鄰演算法進行圖片分類

這篇文章主要給不知道計算機視覺是啥的人介紹一下影象分類問題以及最近的最近鄰演算法。

目錄

  1. 影象分類
    1.1 影象分類的原理
    1.2 面臨的問題
    1.3 影象分類任務
  2. 最近鄰演算法
  3. 程式碼實現
  4. L2距離
  5. 用k-近鄰進行圖片分類
    5.1 k近鄰分類原理
    5.2 超引數的選取
  6. 小結一下最近鄰和k近鄰

1.影象分類
1.1 影象分類的原理

計算機視覺中的核心問題是給定一張圖片的類別,新來一張圖片,希望識別圖片中物品的類別,比較受歡迎的計算機視覺方面的任務有物體檢測,影象分割等。

舉個小栗子
影象資料必須要被計算機識別,所以要把圖片轉化成矩陣,這時候就會有很多問題了。

在下面這張圖片中,是使用了一個圖片分類模型分類,然後輸出這張圖片屬於那個類別的概率,一共有四個類別(cat,dog,hat,mug)。一張圖片可以用可以用一個非常大的三維陣列表示,表徵的是畫素,在下面這張圖片中,轉化為 陣列就是248X400的,同時呢,圖片是有顏色分別的,因此多出來了三個顏色通道,分別是紅,綠,藍(RGB),因此一張圖片一般用248X400X3表示,後面的3代表顏色通道,一共有297600個數,每個數都是在0-255的範圍內。我們的目的就是把這樣成千上萬維的數分類,輸出一個是貓還是狗的類別標籤。

1.2 面臨的問題

這裡寫圖片描述
當照相的時候,攝像頭換個角度,拍出來的圖片不一樣,但貓還是同一只貓,或者一些背景色的也會影響計算機識別。

還有像一些物體的形狀不同,卻是同以物體,計算機也很難識別。

或者是躲在草叢裡的貓,這些圖片人一眼就能識別,但計算機卻很難識別。
這裡寫圖片描述

還有很多問題,總之,計算機視覺還有很長一段路要走。哈哈

1.3 影象分類任務
影象分類簡單來說就是輸入一個表徵一張圖片的陣列,然後輸出這張圖片所屬的類別,總結來說可以分為一下三步:
輸入:我們的輸入是一個有N張圖片組成的集合,每張圖片都給了一個特定的類別標籤,我們稱這樣的資料為訓練集。

學習:我們的目的就是讓模型學習這些圖片大概是什麼樣子,然後記下來。我們稱這一步為訓練模型。

評估:這一步主要是看我們訓練的模型到底好不好。主要內容就是利用上一步訓練得來的模型預測一個模型沒看過的資料集的標籤,然後拿這些標籤和真正的標籤,我們希望預測出來的這些跟真正的標籤儘可能一致。

2.最近鄰演算法

下來看一下影象分類中的常用演算法—最近鄰演算法

最近鄰演算法在影象分類中非常簡單粗暴,我們用斯坦福大學公佈的資料,總共有6萬張32X32X3的圖片資料,有十個類別,每張圖片都屬於這十個類別中的一個,我們可以用5000張作為訓練集,用1000萬張作為測試集。用最近鄰算出和該類別中最類似的圖片。
這裡寫圖片描述
用最近鄰算出的結果如上右圖,可以發現還是有很多分類錯的。

最近鄰演算法的原理很簡答,在訓練的時候就是光記住每個類別的資料,然後預測的時候算一下測試集中的圖片與訓練集中的每一張圖片的距離,然後看一下跟他距離最近的訓練集中的圖片的類別,這個類別就是我們要預測的類別。

那距離應該怎麼計算呢?一般可以把這個32X32X3的陣列展開,然後用距離計算公式計算兩個向量之間的距離,L1距離可以表示為:

d1(I1,I2)=p|Ip1Ip2|

看一下它具體是怎麼做的:
這裡寫圖片描述
這是一個顏色通道里的L1距離計算,其實就是對應位置上相減,然後把每個方格里的數加起來。

3.程式碼實現

讓我們看一下怎麼在程式碼中實現最近鄰演算法分類,我們把資料匯入,分為四個陣列,分別為訓練集/標籤,和測試集/標籤。其中Xtr包含了所有的50000張圖片資料,Ytr為一維陣列,包含了這50000張資料的標籤(0-9).

Xtr, Ytr, Xte, Yte = load_CIFAR10('data/cifar10/') # 斯坦福大學的課程程式碼中提供的函式來匯入資料

# 把所有圖片變為一維向量
Xtr_rows = Xtr.reshape(Xtr.shape[0], 32 * 32 * 3) # Xtr_rows 變成 50000 x 3072
Xte_rows = Xte.reshape(Xte.shape[0], 32 * 32 * 3) # Xte_rows 變成 10000 x 3072

既然我們已經把圖片資料都轉為1維陣列,那就可以訓練模型並且評估了。

nn = NearestNeighbor() # 建立一個最近鄰分類器的類,相當於初始化
nn.train(Xtr_rows, Ytr) # 把訓練資料給模型,訓練
Yte_predict = nn.predict(Xte_rows) # 預測測試集的標籤
# 算一下分類的準確率,這裡取的是平均值
print 'accuracy: %f' % ( np.mean(Yte_predict == Yte) )

注意一下我們的衡量標準是準確率,他是分類正確的樣本佔總測試樣本的比例。注意一下我們建的模型,幾乎所有模型都有一個操作,那就是train(X,y),就是輸入訓練資料和標籤,讓模型學習一個判斷標準,判斷這張影象是那個類別的。然後,還有有一個predict(X)的操作,他接收一份跟訓練資料一樣樣式的資料,然後預測這些資料的標籤是什麼樣子的。

下面這段程式碼是用L1距離來計算的:

import numpy as np

class NearestNeighbor(object):
  def __init__(self):
    pass

  def train(self, X, y):
    #X是NXD的陣列,其中每一行代表一個樣本,Y是N行的一維陣列,對應X的標籤
    # 最近鄰分類器就是簡單的記住所有的資料
    self.Xtr = X
    self.ytr = y

  def predict(self, X):
    #X是NXD的陣列,其中每一行代表一個圖片樣本
    #看一下測試資料有多少行
    num_test = X.shape[0]
    # 確認輸出的結果型別符合輸入的型別
    Ypred = np.zeros(num_test, dtype = self.ytr.dtype)

    # 迴圈每一行,也就是每一個樣本
    for i in xrange(num_test):
      # 找到和第i個測試圖片距離最近的訓練圖片
      # 計算他們的L1距離
      distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
      min_index = np.argmin(distances) # 拿到最小那個距離的索引
      Ypred[i] = self.ytr[min_index] # 預測樣本的標籤,其實就是跟他最近的訓練資料樣本的標籤
    return Ypred

如果你執行這個程式碼,你會發現我們的準確率大約在38.6%左右,貌似比胡亂猜好很多,如果是人來識別的話,準確率大約會有94%,而卷積神經網路已經可以達到95%的準確率,可以看一下kaggle這個比賽,已經達到95%了

4 .L2距離

向量之間的距離計算除了L1距離之外,還可以使用L2距離:

d2(I1,I2)=p(Ip1Ip2)2這樣一個公式,其實用numpy一行程式碼就OK了。

因為平方根對應的是單調函式,它能在縮放距離的絕對大小的同時保留順序。

distances = np.sqrt(np.sum(np.square(self.Xtr - X[i,:]), axis = 1))

5.用k-近鄰進行圖片分類

5.1 k近鄰分類原理

你可能會很好奇,當我們預測的時候只用訓練集中最接近的樣本的標籤作為預測結果。事實上,用k-近鄰演算法可能會得到更好的結果。原理很簡單,這一次,我們不在使用訓練集中的單張圖片,我們可以使用訓練集中的topK張圖片,然後根據這些圖片對這個測試的圖片投票,得到這張測試圖片的標籤。其實k=1就是最近鄰分類,我們比較一下最近鄰分類和5-近鄰分類。

這裡寫圖片描述

這是在二維空間中的三個類別,紅,藍,綠。顏色的交界處就是用L2距離算出來的決策邊界,白色區域表示的是模糊區域,就是不知道到底是那個類別。貌似k近鄰比最近鄰確實好很多,但這個k具體應該怎選取呢?

5.2 超引數的選取
k近鄰分類要求我們自己尋定k取多少,那到底一開始這個k應該怎麼選取呢?此外,還有很多距離函式,比如L1,L2可以選擇,還有其他的一些可以嘗試,比如點乘。這樣的選擇我們稱之為超引數的選取,在機器學習演算法中,往往不同的資料集就會有不同的超引數,所以,這個很大一部分是依賴於資料的分佈和樣式的。

那我們具體應該怎麼選取呢?你可能會說我們可以都試一下呀!牛逼啊,這可是一個好主意,其實我們基本就這麼幹的,但是在乾的時候,我們很多小技巧可以幫我們節省很多功夫。但首先,我們不能使用測試集中的資料,因為這部分資料在被用來做測試之前,模型必須是從沒見過的。如果你使用了測試集來訓練,很可能會導致一個後果,就是你用這份測試資料來訓練得到的模型在測試資料上的表現也非常好,但是,如果將來還有新的資料來的時候,你的模型就不會有這個效果,我們稱模型在測試資料上過擬合了。因此,為了選擇好的超引數,我們必須準備一份評估資料集。

評估集
那這份評估集應該怎麼選取呢?很簡單,就是在訓練資料中分割一部分資料出來,比如,我們的50000張圖片資料,我們可以分出來1000張作為評估資料,其餘的4900張作為訓練集。

看一下程式碼是怎麼實現的

# 假設我們之前有 Xtr_rows, Ytr, Xte_rows, Yte 這幾份資料
#  Xtr_rows 是 50,000 x 3072 的矩陣
Xval_rows = Xtr_rows[:1000, :] # 抽取前1000張作為評估集
Yval = Ytr[:1000]
Xtr_rows = Xtr_rows[1000:, :] # 其餘的4900張作為訓練集
Ytr = Ytr[1000:]

# 找到在評估集上表現做好的超引數
validation_accuracies = []
for k in [1, 3, 5, 10, 20, 50, 100]:

  # 使用確定的k值作用在評估集上
  nn = NearestNeighbor()
  nn.train(Xtr_rows, Ytr)
  # 這裡假設我們有一個最近鄰的類,可以把k值作為輸入
  Yval_predict = nn.predict(Xval_rows, k = k)
  acc = np.mean(Yval_predict == Yval)
  print 'accuracy: %f' % (acc,)

  # 記錄在評估集上每個k對應的準確率
  validation_accuracies.append((k, acc))

最後可以就每個k值和對應的準確率畫一張圖,選取準確率最高的那個k來作為引數在測試集中使用。

6.小結一下最近鄰和k近鄰

最近鄰分類的優缺點值得我們去深思。最大的優點就是它在訓練的時候非常簡單粗暴,只要記住這些圖片就OK了,但是在預測的時候要計算一張圖片和所有訓練集中的圖片的距離,這就在測試集上花費了太多的時間了。而我們希望的是,模型在訓練的時候多花點時間沒關係,但在測試的時間一定要儘量快。

接下來的卷積神經網路正是在訓練集中花了很多時間,但在測試集上花的時間很少。