您好,登錄后才能下訂單哦!
為了保證完整性,從算法所用的訓練數據講起,訓練數據是由 MNIST 手寫數字組成的,MNIST 數據集來自美國國家標準與技術研究所,由來自 250 個不同人手寫的數字構成,其中訓練集包含 60000 張圖片,測試集包含 10000 張圖片,每個圖片都有其標簽,圖片大小為 28*28。許多機器學習庫提供了加載 MNIST 數據集的方法,這里使用 keras 庫進行加載:
# 導入 keras 庫 import keras # 加載數據 (train_dataset, train_labels), (test_dataset, test_labels) = keras.datasets.mnist.load_data() train_labels = np.array(train_labels, dtype=np.int32) # 打印數據集形狀 print(train_dataset.shape, test_dataset.shape) # 圖像預覽 for i in range(40): plt.subplot(4, 10, i+1) plt.imshow(train_dataset[i], cmap='gray') plt.title(train_labels[i], fontsize=10) plt.axis('off') plt.show()
加載數據集后,我們嘗試使用 KNN 分類器識別數字,在原始方法中,我們首先使用原始像素值作為特征,因此圖像描述符的大小為 28 × 28 = 784。
首先利用 keras 加載所有數字圖像,為了了解數據訓練的全部流程,我們將加載的訓練數據集劃分為 訓練數據集 + 測試數據集,每部分占比 50%:
# 加載數據集 (train_dataset, train_labels), (test_dataset, test_labels) = keras.datasets.mnist.load_data() train_labels = np.array(train_labels, dtype=np.int32) # 將原始圖像作為描述符 def raw_pixels(img): return img.flatten() # 數據打散 shuffle = np.random.permutation(len(train_dataset)) train_dataset, train_labels = train_dataset[shuffle], train_labels[shuffle] # 計算每個圖像的描述符,這里特征描述符是原始像素 raw_descriptors = [] for img in train_dataset: raw_descriptors.append(np.float32(raw_pixels(img))) raw_descriptors = np.squeeze(raw_descriptors) # 將數據拆分為訓練和測試數據(各占 50%) # 因此,使用 30000 個數字來訓練分類器,30000 位數字來測試訓練后的分類器 partition = int(0.5 * len(raw_descriptors)) raw_descriptors_train, raw_descriptors_test = np.split(raw_descriptors, [partition]) labels_train, labels_test = np.split(train_labels, [partition])
現在,我們就可以使用 knn.train() 方法訓練 KNN 模型并使用 get_accuracy() 函數對其進行測試:
# 訓練 KNN 模型 knn = cv2.ml.KNearest_create() knn.train(raw_descriptors_train, cv2.ml.ROW_SAMPLE, labels_train) # 測試 kNN 模型 k = 5 ret, result, neighbours, dist = knn.findNearest(raw_descriptors_test, k) # 根據真實值和預測值計算準確率 def get_accuracy(predictions, labels): acc = (np.squeeze(predictions) == labels).mean() return acc * 100 acc = get_accuracy(result, labels_test) print("Accuracy: {}".format(acc))
我們可以看到當 K = 5 時,KNN 模型可以獲得 96.48% 的準確率,但我們仍然可以對其進行改進,以獲取更高性能。
我們已經知道在 KNN 算法中,一個影響算法性能的重要參數就是 K,因此,我們可以首先嘗試使用不同的 K 值,查看其對識別手寫數字精確度的影響。
為了比較不同 K 值時模型的準確率,我們首先需要創建一個字典來存儲測試不同 K 值時的準確率:
from collections import defaultdict results = defaultdict(list)
接下來,計算 knn.findNearest() 方法,改變 K 參數,并將結果存儲在字典中:
# K 取值范圍為 (1, 9) for k in range(1, 10): ret, result, neighbours, dist = knn.findNearest(raw_descriptors_test, k) acc = get_accuracy(result, labels_test) print(" {}".format("%.2f" % acc)) results['50'].append(acc)
最后,繪制結果:
ax = plt.subplot(1, 1, 1) ax.set_xlim(0, 10) dim = np.arange(1, 10) for key in results: ax.plot(dim, results[key], linestyle='--', marker='o', label="50%") plt.legend(loc='upper left', title="% training") plt.title('Accuracy of the K-NN model varying k') plt.xlabel("number of k") plt.ylabel("accuracy") plt.show()
程序運行結果如下圖所示:
如上圖所示,改變 K 參數獲得的準確率也是不同的,因此,在應用程序用可以通過調整 K 參數來獲取最佳性能。
在機器學習中,使用更多的數據訓練分類器通常會提高模型的性能,這是由于分類器可以更好地學習特征的結構。在 KNN 分類器中,增加訓練數也會增加在特征空間中找到測試數據正確匹配的概率。
接下來,我們就修改=用于訓練和測試模型的圖像百分比,來觀察訓練數據量對識別手寫數字精確度的影響:
# 劃分訓練數據集和測試數據集 split_values = np.arange(0.1, 1, 0.1) # 存儲結果準確率 results = defaultdict(list) # 創建模型 knn = cv2.ml.KNearest_create() # 不同訓練數據量對識別手寫數字精確度的影響 for split_value in split_values: # 將數據集劃分為訓練和測試數據集 partition = int(split_value * len(raw_descriptors)) raw_descriptors_train, raw_descriptors_test = np.split(raw_descriptors, [partition]) labels_train, labels_test = np.split(train_labels, [partition]) # 訓練 KNN 模型 print('Training KNN model - raw pixels as features') knn.train(raw_descriptors_train, cv2.ml.ROW_SAMPLE, labels_train) # 同時對于每種劃分測試不同 K 值影響 for k in range(1, 10): ret, result, neighbours, dist = knn.findNearest(raw_descriptors_test, k) acc = get_accuracy(result, labels_test) print("{}".format("%.2f" % acc)) results[int(split_value * 100)].append(acc)
訓練算法的數字圖像的百分比為10%、20%、…、90%,測試算法的數字百分比為90%、80%、…、10%,最后,繪制結果:
ax = plt.subplot(1, 1, 1) ax.set_xlim(0, 10) dim = np.arange(1, 10) for key in results: ax.plot(dim, results[key], linestyle='--', marker='o', label=str(key) + "%") plt.legend(loc='upper left', title="% training") plt.title('Accuracy of the KNN model varying both k and the percentage of images to train/test') plt.xlabel("number of k") plt.ylabel("accuracy") plt.show()
從上圖可以看出,隨著訓練圖像數量的增加,準確率也會增加。因此當條件允許的情況下,可以通過增加訓練數據量來提高模型性能。
雖然可以看到準確率雖然已經可以到達97%以上,但是我們不能就此止步。
在以上示例中,我們均使用原始像素值作為特征來訓練分類器。在機器學習中,訓練分類器之前的一個通常可以對輸入數據進行某種預處理,用以提高分類器訓練性能,因此,接下來我們應用預處理以查看其對識別手寫數字精確度的影響。
預處理函數 desew() 如下:
def deskew(img): m = cv2.moments(img) if abs(m['mu02']) < 1e-2: return img.copy() skew = m['mu11'] / m['mu02'] M = np.float32([[1, skew, -0.5 * SIZE_IMAGE * skew], [0, 1, 0]]) img = cv2.warpAffine(img, M, (SIZE_IMAGE, SIZE_IMAGE), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR) return img
desew() 函數通過使用其二階矩對數字進行去歪斜。更具體地說,可以通過兩個中心矩的比值 (mu11/mu02) 計算偏斜的度量。計算出的偏斜用于計算仿射變換,從而消除數字的偏斜。接下來對比預處理的前后圖片效果:
for i in range(10): plt.subplot(2, 10, i+1) plt.imshow(train_dataset[i], cmap='gray') plt.title(train_labels[i], fontsize=10) plt.axis('off') plt.subplot(2, 10, i+11) plt.imshow(deskew(train_dataset[i]), cmap='gray') plt.axis('off') plt.show()
在下圖的第一行顯示了原始數字圖像,第二行顯示了預處理后的數字圖像:
通過應用此預處理,識別的準確率得到提高,準確率曲線如下圖所示:
可以看到經過預處理的分類器準確率甚至可以接近98%,考慮到我們僅僅是使用了簡單的 KNN 模型,效果已經很不錯了,但是我們還可以進一步提高模型性能。
在以上示例中,我們一直使用原始像素值作為特征描述符。在機器學習中,一種常見的方法是使用更高級的描述符,接下來將使用定向梯度直方圖 (Histogram of Oriented Gradients, HOG) 作為圖像特征用以提高 KNN 算法準確率。
特征描述符是圖像的一種表示,它通過提取描述基本特征(例如形狀、顏色或紋理等)的有用信息來簡化圖像。通常,特征描述符將圖像轉換為長度為 n 的特征向量,HOG 是一種用于計算機視覺的流行特征描述符。
接下來定義 get_hog() 函數獲取 HOG 描述符:
(train_dataset, train_labels), (test_dataset, test_labels) = keras.datasets.mnist.load_data() SIZE_IMAGE = train_dataset.shape[1] train_labels = np.array(train_labels, dtype=np.int32) def get_hog(): hog = cv2.HOGDescriptor((SIZE_IMAGE, SIZE_IMAGE), (8, 8), (4, 4), (8, 8), 9, 1, -1, 0, 0.2, 1, 64, True) print("hog descriptor size: {}".format(hog.getDescriptorSize())) return hog
然后使用 HOG 特征訓練 KNN 模型
hog = get_hog() hog_descriptors = [] for img in train_dataset: hog_descriptors.append(hog.compute(deskew(img))) hog_descriptors = np.squeeze(hog_descriptors)
訓練完成的模型的準確率,如下圖所示:
通過上述改進過程,可以看到編寫機器學習模型時的一個好方法是從解決問題的基本基線模型開始,然后通過添加更好的預處理、更高級的特征描述符或其他機器學習技術來迭代改進模型。最后,如果條件允許,可以收集更多數據用于訓練和測試模型。
最終完整代碼如下所示,改進過程中的其他代碼可以根據上述講解對以下代碼進行簡單修改獲得:
import cv2 import numpy as np import matplotlib.pyplot as plt from collections import defaultdict import keras (train_dataset, train_labels), (test_dataset, test_labels) = keras.datasets.mnist.load_data() SIZE_IMAGE = train_dataset.shape[1] train_labels = np.array(train_labels, dtype=np.int32) def get_accuracy(predictions, labels): acc = (np.squeeze(predictions) == labels).mean() return acc * 100 def raw_pixels(img): return img.flatten() def deskew(img): m = cv2.moments(img) if abs(m['mu02']) < 1e-2: return img.copy() skew = m['mu11'] / m['mu02'] M = np.float32([[1, skew, -0.5 * SIZE_IMAGE * skew], [0, 1, 0]]) img = cv2.warpAffine(img, M, (SIZE_IMAGE, SIZE_IMAGE), flags=cv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR) return img def get_hog(): hog = cv2.HOGDescriptor((SIZE_IMAGE, SIZE_IMAGE), (8, 8), (4, 4), (8, 8), 9, 1, -1, 0, 0.2, 1, 64, True) print("hog descriptor size: {}".format(hog.getDescriptorSize())) return hog shuffle = np.random.permutation(len(train_dataset)) train_dataset, train_labels = train_dataset[shuffle], train_labels[shuffle] # 高級圖像描述符 hog = get_hog() hog_descriptors = [] for img in train_dataset: hog_descriptors.append(hog.compute(deskew(img))) hog_descriptors = np.squeeze(hog_descriptors) # 數據劃分 split_values = np.arange(0.1, 1, 0.1) # 創建字典用于存儲準確率 results = defaultdict(list) # 創建 KNN 模型 knn = cv2.ml.KNearest_create() for split_value in split_values: partition = int(split_value * len(hog_descriptors)) hog_descriptors_train, hog_descriptors_test = np.split(hog_descriptors, [partition]) labels_train, labels_test = np.split(train_labels, [partition]) print('Training KNN model - HOG features') knn.train(hog_descriptors_train, cv2.ml.ROW_SAMPLE, labels_train) # 存儲準確率 for k in np.arange(1, 10): ret, result, neighbours, dist = knn.findNearest(hog_descriptors_test, k) acc = get_accuracy(result, labels_test) print(" {}".format("%.2f" % acc)) results[int(split_value * 100)].append(acc) fig = plt.figure(figsize=(12, 5)) plt.suptitle("k-NN handwritten digits recognition", fontsize=14, fontweight='bold') ax = plt.subplot(1, 1, 1) ax.set_xlim(0, 10) dim = np.arange(1, 10) for key in results: ax.plot(dim, results[key], linestyle='--', marker='o', label=str(key) + "%") plt.legend(loc='upper left', title="% training") plt.title('Accuracy of the k-NN model varying both k and the percentage of images to train/test with pre-processing ' 'and HoG features') plt.xlabel("number of k") plt.ylabel("accuracy") plt.show()
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。