OpenCV-Python實戰(番外篇)——利用 SVM 演算法識別手寫數字
- 前言
- 使用 SVM 進行手寫數字識別
- 引數 C 和 γ 對識別手寫數字精確度的影響
- 完整代碼
- 相關鏈接
前言
支持向量機 (Support Vector Machine, SVM) 是一種監督學習技術,它通過根據指定的類對訓練資料進行最佳分離,從而在高維空間中構建一個或一組超平面,在博文《OpenCV-Python實戰(13)——OpenCV與機器學習的碰撞》中,我們已經學習了如何在 OpenCV 中實作和訓練 SVM 演算法,同時通過簡單的示例了解了如何使用 SVM 演算法,在本文中,我們將學習如何使用 SVM 分類器執行手寫數字識別,同時也將探索不同的引數對于模型性能的影響,以獲取具有最佳性能的 SVM 分類器,
使用 SVM 進行手寫數字識別
我們已經在《利用 KNN 演算法識別手寫數字》中介紹了 MNIST 手寫數字資料集,以及如何利用 KNN 演算法識別手寫數字,并通過對數字影像進行預處理( desew() 函式)并使用高級描述符( HOG 描述符)作為用于描述每個數字的特征向量來獲得最佳分類準確率,因此,對于相同的內容不再贅述,接下來將直接使用在《利用 KNN 演算法識別手寫數字》中介紹預處理和 HOG 特征,利用 SVM 演算法對數字影像進行分類,
首先加載資料,并將其劃分為訓練集和測驗集:
# 加載資料
(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 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
# HOG 高級描述符
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)
results = defaultdict(list)
# 資料劃分
split_values = np.arange(0.1, 1, 0.1)
接下來,初始化 SVM,并進行訓練:
# 模型初始化函式
def svm_init(C=12.5, gamma=0.50625):
model = cv2.ml.SVM_create()
model.setGamma(gamma)
model.setC(C)
model.setKernel(cv2.ml.SVM_RBF)
model.setType(cv2.ml.SVM_C_SVC)
model.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6))
return model
# 模型訓練函式
def svm_train(model, samples, responses):
model.train(samples, cv2.ml.ROW_SAMPLE, responses)
return model
# 模型預測函式
def svm_predict(model, samples):
return model.predict(samples)[1].ravel()
# 模型評估函式
def svm_evaluate(model, samples, labels):
predictions = svm_predict(model, samples)
acc = (labels == predictions).mean()
print('Percentage Accuracy: %.2f %%' % (acc * 100))
return acc *100
# 使用不同訓練集、測驗集劃分方法進行訓練和測驗
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 SVM model ...')
model = svm_init(C=12.5, gamma=0.50625)
svm_train(model, hog_descriptors_train, labels_train)
print('Evaluating model ... ')
acc = svm_evaluate(model, hog_descriptors_test, labels_test)
results['svm'].append(acc)

從上圖所示,使用默認引數的 SVM 模型在使用 70% 的數字影像訓練演算法時準確率可以達到 98.60%,接下來我們通過修改 SVM 模型的引數 C 和 γ 來測驗模型是否還有提升空間,
引數 C 和 γ 對識別手寫數字精確度的影響
SVM 模型在使用 RBF 核時,有兩個重要引數——C 和 γ,上例中我們使用 C=12.5 和 γ=0.50625 作為引數值,C 和 γ 的設定依賴于特定的資料集,因此,必須使用某種方法進行引數搜索,本例中使用網格搜索合適的引數 C 和 γ,
for C in [1, 10, 100, 1000]:
for gamma in [0.1, 0.15, 0.25, 0.3, 0.35, 0.45, 0.5, 0.65]:
model = svm_init(C, gamma)
svm_train(model, hog_descriptors_train, labels_train)
acc = svm_evaluate(model, hog_descriptors_test, labels_test)
print(" {}".format("%.2f" % acc))
results[C].append(acc)
最后,可視化結果:
fig = plt.figure(figsize=(10, 6))
plt.suptitle("SVM handwritten digits recognition", fontsize=14, fontweight='bold')
ax = plt.subplot(1, 1, 1)
ax.set_xlim(0, 0.65)
dim = [0.1, 0.15, 0.25, 0.3, 0.35, 0.45, 0.5, 0.65]
for key in results:
ax.plot(dim, results[key], linestyle='--', marker='o', label=str(key))
plt.legend(loc='upper left', title="C")
plt.title('Accuracy of the SVM model varying both C and gamma')
plt.xlabel("gamma")
plt.ylabel("accuracy")
plt.show()
程式的運行結果如下所示:

如圖所示,通過使用不同引數,準確率可以達到 99.25% 左右,通過比較 KNN 分類器和 SVM 分類器在手寫數字識別任務中的表現,我們可以得出在手寫數字識別任務中 SVM 優于 KNN 分類器的結論,
完整代碼
程式的完整代碼如下所示:
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 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
def svm_init(C=12.5, gamma=0.50625):
model = cv2.ml.SVM_create()
model.setGamma(gamma)
model.setC(C)
model.setKernel(cv2.ml.SVM_RBF)
model.setType(cv2.ml.SVM_C_SVC)
model.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6))
return model
def svm_train(model, samples, responses):
model.train(samples, cv2.ml.ROW_SAMPLE, responses)
return model
def svm_predict(model, samples):
return model.predict(samples)[1].ravel()
def svm_evaluate(model, samples, labels):
predictions = svm_predict(model, samples)
acc = (labels == predictions).mean()
return acc * 100
# 資料打散
shuffle = np.random.permutation(len(train_dataset))
train_dataset, train_labels = train_dataset[shuffle], train_labels[shuffle]
# 使用 HOG 描述符
hog = get_hog()
hog_descriptors = []
for img in train_dataset:
hog_descriptors.append(hog.compute(deskew(img)))
hog_descriptors = np.squeeze(hog_descriptors)
# 訓練資料與測驗資料劃分
partition = int(0.9 * 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 SVM model ...')
results = defaultdict(list)
for C in [1, 10, 100, 1000]:
for gamma in [0.1, 0.15, 0.25, 0.3, 0.35, 0.45, 0.5, 0.65]:
model = svm_init(C, gamma)
svm_train(model, hog_descriptors_train, labels_train)
acc = svm_evaluate(model, hog_descriptors_test, labels_test)
print(" {}".format("%.2f" % acc))
results[C].append(acc)
fig = plt.figure(figsize=(10, 6))
plt.suptitle("SVM handwritten digits recognition", fontsize=14, fontweight='bold')
ax = plt.subplot(1, 1, 1)
ax.set_xlim(0, 0.65)
dim = [0.1, 0.15, 0.25, 0.3, 0.35, 0.45, 0.5, 0.65]
for key in results:
ax.plot(dim, results[key], linestyle='--', marker='o', label=str(key))
plt.legend(loc='upper left', title="C")
plt.title('Accuracy of the SVM model varying both C and gamma')
plt.xlabel("gamma")
plt.ylabel("accuracy")
plt.show()
相關鏈接
OpenCV-Python實戰(13)——OpenCV與機器學習的碰撞
OpenCV-Python實戰(番外篇)——利用 KNN 演算法識別手寫數字
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/386598.html
標籤:python
