機器學習之KNN演算法實現影象分類
阿新 • • 發佈:2019-02-07
閒著無聊,這次自己動手實現一下簡單的KNN分類演算法,來實現對圖片的分類,夯實一下自己的基礎。
首先,KNN演算法流程:
1)計算測試資料與各個訓練資料之間的距離;
2)按照距離的遞增關係進行排序;
3)選取距離最小的點;
4)確定最小點所在的位置;
5)返回最小點位置所在的類別作為測試資料的預測分類
資料集:資料集採用Sort_1000pics資料集。資料集包含1000張圖片,總共分為10類。分別是人,沙灘,建築,大卡車,恐龍,大象,花朵,馬,山峰,食品十類,每類100張,(資料集可以到網上下載)。參考
將所得到的圖片至“./photo目錄下”,(這裡採用的是Anaconda3作為開發環境)。
首先採用自己的程式碼試試:
import datetime starttime = datetime.datetime.now() import numpy as np from sklearn.cross_validation import train_test_split from sklearn.metrics import confusion_matrix, classification_report import os import cv2 X = [] Y = [] for i in range(0, 10): #遍歷資料夾,讀取圖片 for f in os.listdir("./photo/%s" % i): #開啟一張圖片並灰度化 Images = cv2.imread("./photo/%s/%s" % (i, f)) image=cv2.resize(Images,(256,256),interpolation=cv2.INTER_CUBIC) hist = cv2.calcHist([image], [0,1], None, [256,256], [0.0,255.0,0.0,255.0]) X.append(((hist/255).flatten())) Y.append(i) X = np.array(X) Y = np.array(Y) #切分訓練集和測試集 X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=1) #隨機率為100%(保證唯一性)選取其中的30%作為測試集 class KNN: def __init__(self,train_data,train_label,test_data): self.train_data = train_data self.train_label = train_label self.test_data = test_data def knnclassify(self): num_train = (self.train_data).shape[0] num_test = (self.test_data).shape[0] labels = [] for i in range(num_test): y = [] for j in range(num_train): dis = np.sum(np.square((self.train_data)[j]-(self.test_data)[i])) y.append(dis) labels.append(self.train_label[y.index(min(y))]) labels = np.array(labels) return labels knn = KNN(X_train,y_train,X_test) predictions_labels = knn.knnclassify() print(confusion_matrix(y_test, predictions_labels)) print (classification_report(y_test, predictions_labels)) endtime = datetime.datetime.now() print (endtime - starttime)
輸出結果為:
[[28 0 0 1 0 2 0 0 0 0] [ 3 11 0 0 0 9 0 2 4 2] [ 5 2 9 1 0 5 0 0 2 2] [ 0 0 0 14 0 1 1 0 1 12] [ 0 1 0 0 31 0 0 0 0 0] [ 3 0 0 1 0 27 0 2 1 0] [ 5 0 0 0 0 0 22 0 0 3] [ 0 0 0 0 0 1 0 25 0 0] [ 7 3 0 1 0 7 0 2 4 7] [ 3 0 0 2 0 3 1 1 0 20]] precision recall f1-score support 0 0.52 0.90 0.66 31 1 0.65 0.35 0.46 31 2 1.00 0.35 0.51 26 3 0.70 0.48 0.57 29 4 1.00 0.97 0.98 32 5 0.49 0.79 0.61 34 6 0.92 0.73 0.81 30 7 0.78 0.96 0.86 26 8 0.33 0.13 0.19 31 9 0.43 0.67 0.53 30 avg / total 0.67 0.64 0.62 300 0:00:33.881616
從中可以看出,混淆矩陣,精度,召回率,以及f1分數,以及所用的時間。機器學習中採用的特徵提取方法為顏色直方圖(提取RGB中三顏色中的BG色值)。更多影象特徵提取方法請參考。至於影象的讀取,處理等操作都是大同小異,再看看整合庫的KNN分類效果。
import datetime
starttime = datetime.datetime.now()
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.metrics import confusion_matrix, classification_report
import os
import cv2
X = []
Y = []
for i in range(0, 10):
#遍歷資料夾,讀取圖片
for f in os.listdir("./photo/%s" % i):
#開啟一張圖片並灰度化
Images = cv2.imread("./photo/%s/%s" % (i, f))
image=cv2.resize(Images,(256,256),interpolation=cv2.INTER_CUBIC)
hist = cv2.calcHist([image], [0,1], None, [256,256], [0.0,255.0,0.0,255.0])
X.append(((hist/255).flatten()))
Y.append(i)
X = np.array(X)
Y = np.array(Y)
#切分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=1)
#隨機率為100%選取其中的30%作為測試集
clf0 = KNeighborsClassifier(n_neighbors=10).fit(X_train, y_train)
predictions0 = clf0.predict(X_test)
print(confusion_matrix(y_test, predictions0))
print (classification_report(y_test, predictions0))
endtime = datetime.datetime.now()
print (endtime - starttime)
輸出結果為
[[28 0 0 0 0 3 0 0 0 0]
[ 7 1 0 0 0 18 0 0 2 3]
[10 1 3 1 0 6 1 1 1 2]
[ 2 0 0 18 0 2 1 0 2 4]
[ 0 0 0 1 30 0 0 0 1 0]
[ 5 0 0 0 0 24 0 4 1 0]
[ 5 0 0 0 0 0 21 3 0 1]
[ 0 0 0 0 0 1 0 25 0 0]
[10 1 0 3 0 8 0 1 1 7]
[ 5 0 0 2 0 1 1 2 0 19]]
precision recall f1-score support
0 0.39 0.90 0.54 31
1 0.33 0.03 0.06 31
2 1.00 0.12 0.21 26
3 0.72 0.62 0.67 29
4 1.00 0.94 0.97 32
5 0.38 0.71 0.49 34
6 0.88 0.70 0.78 30
7 0.69 0.96 0.81 26
8 0.12 0.03 0.05 31
9 0.53 0.63 0.58 30
avg / total 0.59 0.57 0.51 300
0:00:36.135252
可以看出,精度有所下降,時間略微上升。再看看,第三個版本的KNN。
‘’‘前半部分程式碼相同’’
......
......
from numpy import *
# KNN分類演算法函式定義
def kNNClassify(newInput, dataSet, labels, k):
numSamples = dataSet.shape[0] # shape[0]表示行數
# tile(A, reps): 構造一個矩陣,通過A重複reps次得到
# the following copy numSamples rows for dataSet
diff = np.tile(newInput, (numSamples, 1)) - dataSet # 按元素求差值
squaredDiff = diff ** 2 # 將差值平方
squaredDist = sum(squaredDiff, axis = 1) # 按行累加
distance = squaredDist ** 0.5 # 將差值平方和求開方,即得距離
# # step 2: 對距離排序
# argsort() 返回排序後的索引值
sortedDistIndices = argsort(distance)
classCount = {} # define a dictionary (can be append element)
for i in range(k):
# # step 3: 選擇k個最近鄰
voteLabel = labels[sortedDistIndices[i]]
# # step 4: 計算k個最近鄰中各類別出現的次數
# when the key voteLabel is not in dictionary classCount, get()
# will return 0
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
# # step 5: 返回出現次數最多的類別標籤
maxCount = 0
for key, value in classCount.items():
if value > maxCount:
maxCount = value
maxIndex = key
return maxIndex
predictions=[]
for i in range(X_test.shape[0]):
predictions_labes = kNNClassify(X_test[i], X_train, y_train, 10)
predictions.append(predictions_labes)
print (confusion_matrix(y_test, predictions))
#列印預測結果混淆矩陣
print (classification_report(y_test, predictions))
#列印精度、召回率、FI結果
endtime = datetime.datetime.now()
print (endtime - starttime)
輸出結果為
[[28 0 0 0 0 2 0 0 1 0]
[ 6 1 0 0 0 18 0 0 3 3]
[10 1 3 1 0 5 1 1 2 2]
[ 2 0 0 16 0 2 1 0 2 6]
[ 0 0 0 1 30 0 0 0 1 0]
[ 5 0 0 0 0 24 0 4 1 0]
[ 5 0 0 0 0 0 21 2 0 2]
[ 0 0 0 0 0 1 0 25 0 0]
[10 0 0 2 0 7 0 1 2 9]
[ 5 0 0 1 0 1 1 1 0 21]]
precision recall f1-score support
0 0.39 0.90 0.55 31
1 0.50 0.03 0.06 31
2 1.00 0.12 0.21 26
3 0.76 0.55 0.64 29
4 1.00 0.94 0.97 32
5 0.40 0.71 0.51 34
6 0.88 0.70 0.78 30
7 0.74 0.96 0.83 26
8 0.17 0.06 0.09 31
9 0.49 0.70 0.58 30
avg / total 0.62 0.57 0.52 300
0:01:01.444997