Lecture2 Image Classification Pipeline
1. 圖片在計(jì)算機(jī)中的儲(chǔ)存方式&圖像識(shí)別
1.1 圖片在計(jì)算機(jī)中的儲(chǔ)存方式
? ? 計(jì)算機(jī)中的圖片有兩種粟关,位圖和矢量圖玻募,其中矢量圖儲(chǔ)存的是該圖片的計(jì)算方式共耍,比如在這張圖距離頂部10%玷氏,距離右邊20%的地方有一個(gè)點(diǎn),哪里有一條線,線有多長(zhǎng)等等。從這里我們們可以看出冠王,矢量圖放大是沒(méi)有損失的。但是位圖不一樣舌镶。位圖儲(chǔ)存的是像素點(diǎn)柱彻,也就是說(shuō)把圖片當(dāng)作矩陣來(lái)存儲(chǔ)。
? ? 我們知道餐胀,紅黃藍(lán)三種顏色混合可以構(gòu)成不同的顏色哟楷。因此我們可以用三個(gè)矩陣來(lái)儲(chǔ)存一張圖片,即RGB三色否灾,我們用一個(gè)0-255的數(shù)字表示這個(gè)顏色的深度卖擅,比如說(shuō)一個(gè)像素點(diǎn)可以是R110,G250墨技,B120混合而成
1.2 圖像識(shí)別
? ? 圖像識(shí)別顧名思義惩阶,就是把圖片中的內(nèi)容提取出來(lái)。從Image 到 Label扣汪。
? ? 其實(shí)很多任務(wù)都可以歸結(jié)為断楷,給一個(gè)輸入,給一個(gè)輸出(或者多個(gè)輸出)崭别。也就是說(shuō)冬筒,我們需要找到一個(gè)函數(shù),這個(gè)函數(shù)在給定輸入圖片的像素矩陣之后茅主,可以輸出這張照片是什么舞痰,比如是一只貓。
? ? 對(duì)于我們?nèi)祟悂?lái)說(shuō)暗膜,看一眼就知道了匀奏,但是電腦畢竟需要我們來(lái)編程他不會(huì)自己想(起碼現(xiàn)在不會(huì))如何讓電腦來(lái)識(shí)別貓呢鞭衩?
? ? 我們可以嘗試自己寫出一個(gè)函數(shù)來(lái)識(shí)別貓嗎学搜?這……有點(diǎn)困難娃善。當(dāng)然我們也不會(huì)這么做。我們想讓計(jì)算機(jī)來(lái)自己學(xué)習(xí)出貓長(zhǎng)什么樣瑞佩,就像人一樣聚磺。我們是怎么知道一只動(dòng)物是貓的呢?我們見(jiàn)過(guò)很多相似的動(dòng)物(train_image)同時(shí)又有別人告訴我們這是貓(train_label)于是炬丸,當(dāng)我們?cè)僖?jiàn)到一個(gè)動(dòng)物的時(shí)候瘫寝,我們可以根據(jù)之前的“訓(xùn)練”來(lái)判斷我們面前的是不是貓。
? ? 因此我們希望也能對(duì)計(jì)算機(jī)執(zhí)行類似的操作稠炬,我們想要訓(xùn)練一個(gè)模型焕阿,基本步驟如下:
- 輸入:輸入是包含N個(gè)圖像的集合,每個(gè)圖像的標(biāo)簽是K種分類標(biāo)簽中的一種首启。這個(gè)集合稱為訓(xùn)練集(train_set)暮屡。
- 學(xué)習(xí):這一步的任務(wù)是使用訓(xùn)練集來(lái)學(xué)習(xí)每個(gè)類到底長(zhǎng)什么樣。一般該步驟叫做訓(xùn)練分類器或者學(xué)習(xí)一個(gè)模型毅桃。
- 評(píng)價(jià):讓分類器來(lái)預(yù)測(cè)它未曾見(jiàn)過(guò)的圖像(驗(yàn)證集 validation_set)的分類標(biāo)簽褒纲,并以此來(lái)評(píng)價(jià)分類器的質(zhì)量。我們會(huì)把分類器預(yù)測(cè)的標(biāo)簽和圖像真正的分類標(biāo)簽對(duì)比钥飞。毫無(wú)疑問(wèn)莺掠,分類器預(yù)測(cè)的分類標(biāo)簽和圖像真正的分類標(biāo)簽如果一致,那就是好事读宙,這樣的情況越多越好彻秆。
2. Nearest Neighbor 分類器
2.1 Nearest Neighbor 分類器
? ? 我們知道,判斷兩張照片里面的是不是同一種生物论悴,最簡(jiǎn)單的方法就是對(duì)比和兩張圖片的相似度掖棉,長(zhǎng)得像,就應(yīng)該是一個(gè)膀估,Nearest Neighbor 分類器正是這么想的幔亥。我們首先要定義一個(gè)距離函數(shù),來(lái)度量?jī)蓮垐D片之間的距離察纯,距離越近越有可能是同類帕棉。因此,我們對(duì)于一張圖片饼记,計(jì)算他和訓(xùn)練集所有圖片之間的距離香伴,取最近距離圖片的標(biāo)簽來(lái)作為這張圖片的標(biāo)簽。
2.2 KNN
? ? 但是上面的那種方法有點(diǎn)不足具则,就是可能會(huì)出現(xiàn)個(gè)別特例即纲,萬(wàn)一,有只狗像貓博肋,我們送進(jìn)去的貓的圖片還就特別像怎么辦低斋?所以蜂厅,我們改進(jìn)上述方案,不只用一個(gè)了膊畴,用好多個(gè)掘猿,我們?nèi)個(gè)離輸入圖片距離最近的訓(xùn)練圖片,投票唇跨,那個(gè)label最多稠通,就是哪個(gè)。
2.3 超參數(shù)
? ? 這是我們第一次面臨選擇买猖,距離改橘?圖片的距離?圖片有什么距離啊玉控。我們已經(jīng)知道圖片在計(jì)算機(jī)中使用像素點(diǎn)存儲(chǔ)唧龄,一種比較自然的想法就是,所有像素點(diǎn)數(shù)值相減取絕對(duì)值加和奸远。這種距離稱為L(zhǎng)1距離(曼哈頓距離)既棺。
- 曼哈頓距離 Manhattan Distance(L1)
- 歐氏距離 Euclidean Distance (L2)
? ?為什么叫做超參數(shù)呢,就是因?yàn)檫@兩種哪種好其實(shí)誰(shuí)都不知道懒叛,這個(gè)是試出來(lái)的有興趣的可以試試丸冕,我給的代碼里是L2距離。
? ? 還有一個(gè)選擇是K薛窥,K取多少比較合適呢胖烛?這個(gè)……自己慢慢試去吧。
2.4 數(shù)據(jù)集
數(shù)據(jù)集可以分為以下幾個(gè)類:訓(xùn)練集(train_set)诅迷,驗(yàn)證集(validation_set)佩番,測(cè)試集(test_set)。
- 訓(xùn)練集:用來(lái)訓(xùn)練模型罢杉,得出參數(shù)(過(guò)一會(huì)將出現(xiàn)的權(quán)重)的趟畏。
- 驗(yàn)證集:用來(lái)調(diào)超參數(shù)的。
- 測(cè)試集:評(píng)估最后結(jié)果的滩租。
? ?注意赋秀,訓(xùn)練集只是整個(gè)數(shù)據(jù)集的一部分,因?yàn)槿绻?xùn)練集包括了所有的數(shù)據(jù)律想,那么他們可能在這個(gè)數(shù)據(jù)集上表現(xiàn)良好猎莲,換一個(gè)就變差了,畢竟技即,這是用這個(gè)數(shù)據(jù)集訓(xùn)練出來(lái)的(過(guò)擬合)著洼。
? ?但是有時(shí)候數(shù)據(jù)較少,沒(méi)有那么多可以劃分,這時(shí)身笤,可以使用交叉驗(yàn)證年碘。我們將一份數(shù)據(jù)分成N份,前N-1份用于訓(xùn)練展鸡,第N份用于驗(yàn)證,循環(huán)著來(lái)埃难,取N次驗(yàn)證的平均值做最后結(jié)果莹弊。
3. 線性分類
3.1 開(kāi)始“學(xué)習(xí)”
? ?從KNN中我們可以看出去,其實(shí)這個(gè)算法是沒(méi)有在“學(xué)習(xí)”的涡尘,用我的話來(lái)說(shuō)忍弛,KNN只是跟過(guò)往的經(jīng)驗(yàn)來(lái)從表層判斷,沒(méi)有從中抽象出一般規(guī)律考抄。這一點(diǎn)表現(xiàn)在细疚,KNN的“訓(xùn)練”只是簡(jiǎn)單的把訓(xùn)練集輸入,沒(méi)有任何參數(shù)改變川梅。我們可以想象疯兼,這種方法在處理未知圖片時(shí)正確率一定不理想。我們希望從訓(xùn)練集中抽象出一般規(guī)律贫途,這樣可以更好地應(yīng)對(duì)未知的情況吧彪。
3.2 線性分類
? ? 我們先想下,比較理想的模型應(yīng)該是什么樣的丢早,給定輸入的圖片矩陣姨裸,直接輸出這張圖片是什么,比如這張圖片是狗怨酝,那就輸出一個(gè)【dog】傀缩。這顯然有點(diǎn)困難,畢竟從圖片到文字人類進(jìn)化了幾百萬(wàn)年农猬?我們簡(jiǎn)化一下問(wèn)題赡艰,輸入一個(gè)圖片矩陣,輸出一個(gè)矩陣(或者列向量)這個(gè)向量的n維代表n種類別斤葱,我們讓這個(gè)模型不管怎么樣瞄摊,必須歸一類(或者你把其中一類定成Null)。那么最簡(jiǎn)單的模型是什么樣的呢苦掘?沒(méi)錯(cuò)换帜,就是輸出列向量,是輸入列向量乘上一個(gè)矩陣鹤啡,也就是輸出是輸入的線性組合惯驼。
? ? 我們希望通過(guò)學(xué)習(xí),定出參數(shù)矩陣。
3.3 線性回歸-理解
? ? 為什么這樣可以分類呢祟牲。我們?cè)谝粋€(gè)二維平面上隙畜,給一堆點(diǎn),我們可以使用一條直線把它分成兩部分说贝,這個(gè)時(shí)候這條直線就是我們與這個(gè)類似议惰,線性回歸,可以看作把這些圖片映射到高維空間乡恕,在高維空間言询,他們是線性可分的。
4. KNN實(shí)現(xiàn)(Python)
4.1 環(huán)境
- Python 3.6.6
* Numpy == 1.14.5 - Pycharm 2018.2 Professional
- DataSet
* CIFAR-10 DataSet
4.2 代碼
https://github.com/lisixu1999/cs231n_programs/blob/master/Lecture%201/KNN/KNN.py
# 這是KNN分類器的示例程序
# 2018-8-15
import numpy as np
par_k = 3
#定義Knn的K
path = "E:\\2017-2018 Summer Vacation\\cs231n_program\\DataSets\\"
#這是存放數(shù)據(jù)集的路徑傲宜,這個(gè)數(shù)據(jù)集我自己處理過(guò)
train_images = np.reshape(np.load(path + "cifar10_images.npy"), (10000, 3 * 32 * 32))
train_labels = np.load(path + "cifar10_labels.npy")
validation_images = np.reshape(np.load(path + "cifar10_images_validation.npy"), (10000, 3 * 32 * 32))
validation_labels = np.load(path + "cifar10_labels_validation.npy")
train_images = train_labels[:10]
train_labels = train_labels[:10]
validation_images = train_labels[:10]
validation_labels = train_labels[:10]
#不想使用整個(gè)數(shù)據(jù)集做實(shí)驗(yàn)的可以從中選一部分运杭,要不然根本跑不完,
#i5-5200U下跑1000就費(fèi)勁了
print(train_images.shape)
print(train_labels.shape)
print(validation_images.shape)
print(validation_labels.shape)
#L1范數(shù)
def distance_l1(a, b):
return np.sum(np.abs(a - b), axis=1)
#L2范數(shù)
def distance_l2(a, b):
return np.sqrt(np.sum(np.power(a - b, 2)))
#定義一個(gè)正確率的計(jì)算
def accuracy(a, b):
total = a.shape[0]
counter = 0
for i in range(0, total):
if a[i] == b[i]:
counter = counter + 1
return counter / total
#正兒八經(jīng)的代碼
class KNN(object):
def __init__(self):
k = par_k
pass
#訓(xùn)練代碼就是把數(shù)據(jù)存進(jìn)去
def train(self, train_im, train_la):
self.tr_data = train_im
self.tr_label = train_la
pass
#預(yù)測(cè)代碼就是把每個(gè)距離算出來(lái)函卒,依次比較
def predict(self, input_vector):
k = par_k
round = input_vector.shape[0]
n_train = self.tr_data.shape[0]
dis_array = np.zeros((round, n_train))
label_test = np.zeros(round)
for i in range(0, round):
for j in range(0, n_train):
dis_array[i, j] = distance_l2(input_vector[i], self.tr_data[j])
for i in range(0, round):
temp = np.argsort(dis_array[i])
temp_label = np.zeros(par_k)
for j in range(0, par_k):
temp_label[j] = self.tr_label[temp[j]]
label_test[i] = np.argmax(np.bincount(temp_label.astype(int)))
return label_test
if __name__ == "__main__":
myknn = KNN()
myknn.train(train_im=train_images, train_la=train_labels)
predict_results = myknn.predict(input_vector=validation_images)
accuracy = accuracy(predict_results, validation_labels)
print(accuracy)