KNN(上)

來自斯坦福CS231n課程筆記image classification notes

內容列表如下:

  • 圖像分類、數據驅動方法和流程

  • Nearest Neighbor分類器

  • k-Nearest Neighbor

  • 驗證集、交叉驗證集和超參數調參

  • Nearest Neighbor的優劣

  • 小結

  • 小結:應用kNN實踐

  • 拓展閱讀

  • List item

圖像分類

目標:這一節我們將介紹圖像分類問題。所謂圖像分類問題,就是已有固定的分類標籤集合,然後對於輸入的圖像,從分類標籤集合中找出一個分類標籤,最後把分類標籤分配給該輸入圖像。雖然看起來挺簡單的,但這可是計算機視覺領域的核心問題之一,並且有着各種各樣的實際應用。在後面的課程中,我們可以看到計算機視覺領域中很多看似不同的問題(比如物體檢測和分割),都可以被歸結爲圖像分類問題。

例子:以下圖爲例,圖像分類模型讀取該圖片,並生成該圖片屬於集合 {cat, dog, hat, mug}中各個標籤的概率。需要注意的是,對於計算機來說,圖像是一個由數字組成的巨大的3維數組。在這個例子中,貓的圖像大小是寬248像素,高400像素,有3個顏色通道,分別是紅、綠和藍(簡稱RGB)。如此,該圖像就包含了248X400X3=297600個數字,每個數字都是在範圍0-255之間的整型,其中0表示全黑,255表示全白。我們的任務就是把這些上百萬的數字變成一個簡單的標籤,比如“貓”。
——————————————————————————————————————————————————————————————————

——————————————————————————————————————————————————————————————————
圖像分類的任務,就是對於一個給定的圖像,預測它屬於的那個分類標籤(或者給出屬於一系列不同標籤的可能性)。圖像是3維數組,數組元素是取值範圍從0到255的整數。數組的尺寸是寬度x高度x3,其中這個3代表的是紅、綠和藍3個顏色通道。

困難和挑戰:對於人來說,識別出一個像“貓”一樣視覺概念是簡單至極的,然而從計算機視覺算法的角度來看就值得深思了。我們在下面列舉了計算機視覺算法在圖像識別方面遇到的一些困難,要記住圖像是以3維數組來表示的,數組中的元素是亮度值。

視角變化(Viewpoint variation):同一個物體,攝像機可以從多個角度來展現。
大小變化(Scale variation):物體可視的大小通常是會變化的(不僅是在圖片中,在真實世界中大小也是變化的)。
形變(Deformation):很多東西的形狀並非一成不變,會有很大變化。
遮擋(Occlusion):目標物體可能被擋住。有時候只有物體的一小部分(可以小到幾個像素)是可見的。
光照條件(Illumination conditions):在像素層面上,光照的影響非常大。
背景干擾(Background clutter):物體可能混入背景之中,使之難以被辨認。
類內差異(Intra-class variation):一類物體的個體之間的外形差異很大,比如椅子。這一類物體有許多不同的對象,每個都有自己的外形。

面對以上所有變化及其組合,好的圖像分類模型能夠在維持分類結論穩定的同時,保持對類間差異足夠敏感。
——————————————————————————————————————————————————————————————
在這裏插入圖片描述
——————————————————————————————————————————————————————————————

數據驅動方法:如何寫一個圖像分類的算法呢?這和寫個排序算法可是大不一樣。怎麼寫一個從圖像中認出貓的算法?搞不清楚。因此,與其在代碼中直接寫明各類物體到底看起來是什麼樣的,倒不如說我們採取的方法和教小孩兒看圖識物類似:給計算機很多數據,然後實現學習算法,讓計算機學習到每個類的外形。這種方法,就是數據驅動方法。既然該方法的第一步就是收集已經做好分類標註的圖片來作爲訓練集,那麼下面就看看數據庫到底長什麼樣:

——————————————————————————————————————————————————————————————
在這裏插入圖片描述
一個有4個視覺分類的訓練集。在實際中,我們可能有上千的分類,每個分類都有成千上萬的圖像。
——————————————————————————————————————————————————————————————
圖像分類流程。在課程視頻中已經學習過,圖像分類就是輸入一個元素爲像素值的數組,然後給它分配一個分類標籤。完整流程如下:

  • 輸入:輸入是包含N個圖像的集合,每個圖像的標籤是K種分類標籤中的一種。這個集合稱爲訓練集。
  • 學習:這一步的任務是使用訓練集來學習每個類到底長什麼樣。一般該步驟叫做訓練分類器或者學習一個模型。
  • 評價:讓分類器來預測它未曾見過的圖像的分類標籤,並以此來評價分類器的質量。我們會把分類器預測的標籤和圖像真正的分類標籤對比。毫無疑問,分類器預測的分類標籤和圖像真正的分類標籤如果一致,那就是好事,這樣的情況越多越好。

Nearest Neighbor分類器

作爲課程介紹的第一個方法,我們來實現一個Nearest Neighbor分類器。雖然這個分類器和卷積神經網絡沒有任何關係,實際中也極少使用,但通過實現它,可以讓讀者對於解決圖像分類問題的方法有個基本的認識。

圖像分類數據集:CIFAR-10。一個非常流行的圖像分類數據集是CIFAR-10。這個數據集包含了60000張32X32的小圖像。每張圖像都有10種分類標籤中的一種。這60000張圖像被分爲包含50000張圖像的訓練集和包含10000張圖像的測試集。在下圖中你可以看見10個類的10張隨機圖片。
——————————————————————————————————————————————————————————————
在這裏插入圖片描述
左邊:從CIFAR-10數據庫來的樣本圖像。右邊:第一列是測試圖像,然後第一列的每個測試圖像右邊是使用Nearest Neighbor算法,根據像素差異,從訓練集中選出的10張最類似的圖片。
——————————————————————————————————————————————————————————————
假設現在我們有CIFAR-10的50000張圖片(每種分類5000張)作爲訓練集,我們希望將餘下的10000作爲測試集並給他們打上標籤。Nearest Neighbor算法將會拿着測試圖片和訓練集中每一張圖片去比較,然後將它認爲最相似的那個訓練集圖片的標籤賦給這張測試圖片。上面右邊的圖片就展示了這樣的結果。請注意上面10個分類中,只有3個是準確的。比如第8行中,馬頭被分類爲一個紅色的跑車,原因在於紅色跑車的黑色背景非常強烈,所以這匹馬就被錯誤分類爲跑車了。

那麼具體如何比較兩張圖片呢?在本例中,就是比較32x32x3的像素塊。最簡單的方法就是逐個像素比較,最後將差異值全部加起來。換句話說,就是將兩張圖片先轉化爲兩個向量I1I_{1}I2I_{2},然後計算他們的L1距離d1(I1,I2)=pI1pI2pd_{1}(I_{1},I_{2})=\sum_{p}|I_{1}^p-I_{2}^p|這裏的求和是針對所有的像素。下面是整個比較流程的圖例:
——————————————————————————————————————————————————————————————
在這裏插入圖片描述
以圖片中的一個顏色通道爲例來進行說明。兩張圖片使用L1距離來進行比較。逐個像素求差值,然後將所有差值加起來得到一個數值。如果兩張圖片一模一樣,那麼L1距離爲0,但是如果兩張圖片很是不同,那L1值將會非常大。
——————————————————————————————————————————————————————————————
下面,讓我們看看如何用代碼來實現這個分類器。首先,我們將CIFAR-10的數據加載到內存中,並分成4個數組:訓練數據和標籤,測試數據和標籤。在下面的代碼中,Xtr(大小是50000x32x32x3)存有訓練集中所有的圖像,Ytr是對應的長度爲50000的1維數組,存有圖像對應的分類標籤(從0到9):

Xtr, Ytr, Xte, Yte = load_CIFAR10('data/cifar10/') # a magic function we provide
# flatten out all images to be one-dimensional
Xtr_rows = Xtr.reshape(Xtr.shape[0], 32 * 32 * 3) # Xtr_rows becomes 50000 x 3072
Xte_rows = Xte.reshape(Xte.shape[0], 32 * 32 * 3) # Xte_rows becomes 10000 x 3072

現在我們得到所有的圖像數據,並且把他們拉長成爲行向量了。接下來展示如何訓練並評價一個分類器:

nn = NearestNeighbor() # create a Nearest Neighbor classifier class
nn.train(Xtr_rows, Ytr) # train the classifier on the training images and labels
Yte_predict = nn.predict(Xte_rows) # predict labels on the test images
# and now print the classification accuracy, which is the average number
# of examples that are correctly predicted (i.e. label matches)
print 'accuracy: %f' % ( np.mean(Yte_predict == Yte) )

作爲評價標準,我們常常使用準確率,它描述了我們預測正確的得分。請注意以後我們實現的所有分類器都需要有這個API:**train(X, y)函數。該函數使用訓練集的數據和標籤來進行訓練。從其內部來看,類應該實現一些關於標籤和標籤如何被預測的模型。這裏還有個predict(X)**函數,它的作用是預測輸入的新數據的分類標籤。現在還沒介紹分類器的實現,下面就是使用L1距離的Nearest Neighbor分類器的實現套路:

import numpy as np

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

  def train(self, X, y):
    """ X is N x D where each row is an example. Y is 1-dimension of size N """
    # the nearest neighbor classifier simply remembers all the training data
    self.Xtr = X
    self.ytr = y

  def predict(self, X):
    """ X is N x D where each row is an example we wish to predict label for """
    num_test = X.shape[0]
    # lets make sure that the output type matches the input type
    Ypred = np.zeros(num_test, dtype = self.ytr.dtype)

    # loop over all test rows
    for i in xrange(num_test):
      # find the nearest training image to the i'th test image
      # using the L1 distance (sum of absolute value differences)
      distances = np.sum(np.abs(self.Xtr - X[i,:]), axis = 1)
      min_index = np.argmin(distances) # get the index with smallest distance
      Ypred[i] = self.ytr[min_index] # predict the label of the nearest example

    return Ypred

如果你用這段代碼跑CIFAR-10,你會發現準確率能達到38.6%。這比隨機猜測的10%要好,但是比人類識別的水平(據研究推測是94%)和卷積神經網絡能達到的95%還是差多了。點擊查看基於CIFAR-10數據的Kaggle算法競賽排行榜

距離選擇:計算向量間的距離有很多種方法,另一個常用的方法是L2距離,從幾何學的角度,可以理解爲它在計算兩個向量間的歐式距離。L2距離的公式如下:d2(I1,I2)=p(I1pI2p)2d_{2}(I_{1},I_{2})=\sqrt{\sum_{p}(I_{1}^p-I_{2}^p)^2}換句話說,我們依舊是在計算像素間的差值,只是先求其平方,然後把這些平方全部加起來,最後對這個和開方。在Numpy中,我們只需要替換上面代碼中的1行代碼就行:

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

注意在這裏使用了np.sqrt,但是在實際中可能不用。因爲求平方根函數是一個單調函數,它對不同距離的絕對值求平方根雖然改變了數值大小,但依然保持了不同距離大小的順序。所以用不用它,都能夠對像素差異的大小進行正確比較。如果你在CIFAR-10上面跑這個模型,正確率是35.4%,比剛纔低了一點。

L1和L2比較。比較這兩個度量方式是挺有意思的。在面對兩個向量之間的差異時,L2比L1更加不能容忍這些差異。也就是說,相對於1個巨大的差異,L2距離更傾向於接受多箇中等程度的差異。L1和L2都是在p-norm常用的特殊形式。

k-Nearest Neighbor分類器

你可能注意到了,爲什麼只用最相似的1張圖片的標籤來作爲測試圖像的標籤呢?這不是很奇怪嗎!是的,使用k-Nearest Neighbor分類器就能做得更好。它的思想很簡單:與其只找最相近的那1個圖片的標籤,我們找最相似的k個圖片的標籤,然後讓他們針對測試圖片進行投票,最後把票數最高的標籤作爲對測試圖片的預測。所以當k=1的時候,k-Nearest Neighbor分類器就是Nearest Neighbor分類器。從直觀感受上就可以看到,更高的k值可以讓分類的效果更平滑,使得分類器對於異常值更有抵抗力。
——————————————————————————————————————————————————————————————
在這裏插入圖片描述
上面示例展示了Nearest Neighbor分類器和5-Nearest Neighbor分類器的區別。例子使用了2維的點來表示,分成3類(紅、藍和綠)。不同顏色區域代表的是使用L2距離的分類器的決策邊界。白色的區域是分類模糊的例子(即圖像與兩個以上的分類標籤綁定)。需要注意的是,在NN分類器中,異常的數據點(比如:在藍色區域中的綠點)製造出一個不正確預測的孤島。5-NN分類器將這些不規則都平滑了,使得它針對測試數據的**泛化(generalization)**能力更好(例子中未展示)。注意,5-NN中也存在一些灰色區域,這些區域是因爲近鄰標籤的最高票數相同導致的(比如:2個鄰居是紅色,2個鄰居是藍色,還有1個是綠色)。
——————————————————————————————————————————————————————————————
在實際中,大多使用k-NN分類器。但是k值如何確定呢?接下來就討論這個問題。

點擊查看KNN(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章