Python OpenCV實戰(zhàn)之與機器學(xué)習(xí)的碰撞
0. 前言
機器學(xué)習(xí)是人工智能的子集,它為計算機以及其它具有計算能力的系統(tǒng)提供自動預(yù)測或決策的能力,諸如虛擬助理、車牌識別系統(tǒng)、智能推薦系統(tǒng)等機器學(xué)習(xí)應(yīng)用程序給我們的日常生活帶來了便捷的體驗。機器學(xué)習(xí)的蓬勃發(fā)展,得益于以下三個關(guān)鍵因素:1) 海量數(shù)據(jù)集;2) 算法的快速發(fā)展;3) 計算機硬件的發(fā)展。在本文中,我們將學(xué)習(xí) OpenCV 提供的常見機器學(xué)習(xí)算法和技術(shù),用于解決計算機視覺項目中的實際問題,例如分類和回歸問題。
1. 機器學(xué)習(xí)簡介
機器學(xué)習(xí)是利用計算機編程,從歷史數(shù)據(jù)中學(xué)習(xí)以對新數(shù)據(jù)進行預(yù)測的過程。機器學(xué)習(xí)可以分為三類——監(jiān)督學(xué)習(xí)、無監(jiān)督學(xué)習(xí)和半監(jiān)督學(xué)習(xí),這些技術(shù)所包含的算法如下圖所示:
1.1 監(jiān)督學(xué)習(xí)
監(jiān)督學(xué)習(xí)使用的樣本都有相應(yīng)的期望輸出值(或稱為樣本標(biāo)簽),由于我們知道每個訓(xùn)練數(shù)據(jù)的正確標(biāo)簽,因此監(jiān)督學(xué)習(xí)可以根據(jù)預(yù)測與相應(yīng)的期望輸出之間的差異來校正這些預(yù)測?;谶@些校準(zhǔn),算法可以從錯誤中學(xué)習(xí)以調(diào)整其內(nèi)部參數(shù),以擬合出最接近樣本集合與相應(yīng)的期望輸出之間的函數(shù)。
監(jiān)督學(xué)習(xí)問題可以進一步分為以下分類和回歸:
- 分類:當(dāng)輸出變量是類別時,可以認為該問題是分類問題。在分類問題中,算法將輸入映射到輸出標(biāo)簽。
- 回歸:當(dāng)輸出變量為實數(shù)時,在回歸問題中,算法將輸入映射到連續(xù)的實數(shù)輸出。
在監(jiān)督學(xué)習(xí)中,主要需要考慮以下問題:
偏差-方差的權(quán)衡 (Bias-variance trade-off):模型對數(shù)據(jù)欠擬合的模型具有高偏差,而對數(shù)據(jù)過擬合的模型具有高方差:
偏差是由于學(xué)習(xí)算法中的錯誤假設(shè)而產(chǎn)生的誤差,可以定義為模型的預(yù)測與期望的正確值之間的差異。具有高偏差的模型無法找到數(shù)據(jù)中的所有模式(欠擬合),因此它不能很好地擬合訓(xùn)練集,也不會很好地擬合測試集。
方差定義為算法學(xué)習(xí)錯誤事物的傾向,其會同時擬合數(shù)據(jù)中的真實信號以及噪聲。因此,具有高方差的模型(過擬合)非常適合訓(xùn)練集,但無法泛化到測試集,因為它學(xué)習(xí)了數(shù)據(jù)中的噪聲。
模型復(fù)雜度和訓(xùn)練數(shù)據(jù)量:模型復(fù)雜度是指機器學(xué)習(xí)算法試圖的復(fù)雜度。模型的復(fù)雜度通常由訓(xùn)練數(shù)據(jù)決定:例如,如果使用少量數(shù)據(jù)來訓(xùn)練模型,那么低復(fù)雜度的模型更可取,這是因為高復(fù)雜度的模型會導(dǎo)致過擬合。
輸入空間的維度:在處理高維空間數(shù)據(jù)時,學(xué)習(xí)可能非常困難,因為會有許多額外的特征會混淆學(xué)習(xí)過程,也稱為維度災(zāi)難。因此,在處理高維空間數(shù)據(jù)時,常見的方法是修改學(xué)習(xí)算法,使其具有高偏差和低方差。
1.2 無監(jiān)督學(xué)習(xí)
在無監(jiān)督學(xué)習(xí)中,樣本集合缺少每個樣本對應(yīng)的輸出值(樣本集合沒有被標(biāo)記、分類或歸類)。無監(jiān)督學(xué)習(xí)的目標(biāo)是對樣本集合中的結(jié)構(gòu)或分布進行建模和推斷。因此,在無監(jiān)督學(xué)習(xí)中,算法利用數(shù)據(jù)中進行推斷,并試圖揭示其中的隱藏分布信息。聚類和降維是無監(jiān)督學(xué)習(xí)中最常用的兩種算法。
1.3 半監(jiān)督學(xué)習(xí)
半監(jiān)督學(xué)習(xí)可以看作是監(jiān)督學(xué)習(xí)和無監(jiān)督學(xué)習(xí)之間的折衷,因為它同時使用標(biāo)記和未標(biāo)記的數(shù)據(jù)進行訓(xùn)練。許多現(xiàn)實世界的機器學(xué)習(xí)問題可以歸類為半監(jiān)督,因為正確標(biāo)記所有數(shù)據(jù)可能非常困難或耗時,而未標(biāo)記的數(shù)據(jù)更容易收集。
2. K均值 (K-Means) 聚類
OpenCV 提供了 cv2.kmeans() 函數(shù)實現(xiàn) K-Means 聚類算法,該算法找到簇的中心并將輸入樣本分組到簇周圍。
K-Means 聚類算法的目標(biāo)是將 n 個樣本劃分(聚類)為 K 個簇,其中每個樣本都屬于具有最近均值的簇,cv2.kmeans() 函數(shù)用法如下:
retval, bestLabels, centers=cv.kmeans(data, K, bestLabels, criteria, attempts, flags[, centers])
data 表示用于聚類的輸入數(shù)據(jù),它是 np.float32 數(shù)據(jù)類型,每一列包含一個特征;K 指定最后需要的簇數(shù);算法終止標(biāo)準(zhǔn)由 criteria 參數(shù)指定,該參數(shù)設(shè)置最大迭代次數(shù)或所需精度,當(dāng)滿足這些標(biāo)準(zhǔn)時,算法終止。criteria 是具有三個參數(shù) (type, max_item, epsilon) 的元組:
criteria 參數(shù)的標(biāo)準(zhǔn)示例如下:
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
上述語句表示,最大迭代次數(shù)設(shè)置為 20 (max_iterm = 20),所需精度為 1.0 (epsilon = 1.0)。
attempts
參數(shù)指定使用不同的初始標(biāo)簽執(zhí)行算法的次數(shù)。flags
參數(shù)指定初始化簇中心的方法,其可選值包括:cv2.KMEANS_RANDOM_CENTERS
每次選擇隨機初始化簇中心;cv2.KMEANS_PP_CENTERS
使用 Arthur 等人提出的 K-Means++
中心初始化。
cv2.kmeans()
返回以下內(nèi)容:
返回值 | 解釋 |
---|---|
bestLabels | 整數(shù)數(shù)組,用于存儲每個樣本的簇索引 |
center | 包含每個簇中心的數(shù)組 |
compactness | 每個點到其簇中心的距離平方和 |
2.1 K-Means 聚類示例
作為示例,我們將使用 K-Means 聚類算法對一組 2D 點進行聚類。這組 2D 點由 240 個點組成,使用兩個特征進行了描述:
# 2D數(shù)據(jù) data = np.float32(np.vstack((np.random.randint(0, 50, (80, 2)), np.random.randint(40, 90, (80, 2)), np.random.randint(70, 110, (80, 2))))) # 可視化 plt.scatter(data[:, 0], data[:, 1], c='c') plt.show()
如上圖所示,數(shù)據(jù)將作為聚類算法的輸入,每個數(shù)據(jù)點有兩個特征對應(yīng)于 (x, y) 坐標(biāo),例如,這些坐標(biāo)可以表示 240 人人的身高和體重,而 K-Means 聚類算法用于決定衣服的尺寸(例如 K=3,則相應(yīng)表示尺寸為 S、M 或 L)。
接下來,我們將數(shù)據(jù)劃分為 2 個簇。第一步是定義算法終止標(biāo)準(zhǔn),將最大迭代次數(shù)設(shè)置為 20 (max_iterm = 20),epsilon 設(shè)置為 1.0 (epsilon = 1.0):
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
然后調(diào)用 cv2.kmeans() 函數(shù)應(yīng)用 K-Means 算法:
ret, label, center = cv2.kmeans(data, 2, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
由于返回值 label 存儲每個樣本的聚類索引,因此,我們可以根據(jù) label 將數(shù)據(jù)拆分為不同的集群:
A = data[label.ravel() == 0] B = data[label.ravel() == 1]
最后繪制 A 和 B 以及聚類前后的數(shù)據(jù),以便更好地理解聚類過程:
fig = plt.figure(figsize=(12, 6)) plt.suptitle("K-means clustering algorithm", fontsize=14, fontweight='bold') # 繪制原始數(shù)據(jù) ax = plt.subplot(1, 2, 1) plt.scatter(data[:, 0], data[:, 1], c='c') plt.title("data") # 繪制聚類后的數(shù)據(jù)和簇中心 ax = plt.subplot(1, 2, 2) plt.scatter(A[:, 0], A[:, 1], c='b') plt.scatter(B[:, 0], B[:, 1], c='g') plt.scatter(center[:, 0], center[:, 1], s=100, c='m', marker='s') plt.title("clustered data and centroids (K = 2)") plt.show()
接下來,我們修改參數(shù) K 進行聚類并進行相應(yīng)的可視化。例如需要將數(shù)據(jù)分為三個簇,則首先應(yīng)用相同的過程對數(shù)據(jù)進行聚類,只需要修改參數(shù) (K=3) 將數(shù)據(jù)分為 3 個簇:
ret, label, center = cv2.kmeans(data, 3, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
然后,當(dāng)使用標(biāo)簽輸出分離數(shù)據(jù)時,將數(shù)據(jù)分為三組:
A = data[label.ravel() == 0] B = data[label.ravel() == 1] C = data[label.ravel() == 2]
最后一步是顯示 A 、 B 和 C ,以及簇中心和訓(xùn)練數(shù)據(jù):
fig = plt.figure(figsize=(12, 6)) plt.suptitle("K-means clustering algorithm", fontsize=14, fontweight='bold') # 繪制原始數(shù)據(jù) ax = plt.subplot(1, 2, 1) plt.scatter(data[:, 0], data[:, 1], c='c') plt.title("data") # 繪制聚類后的數(shù)據(jù)和簇中心 ax = plt.subplot(1, 2, 2) plt.scatter(A[:, 0], A[:, 1], c='b') plt.scatter(B[:, 0], B[:, 1], c='g') plt.scatter(C[:, 0], C[:, 1], c='r') plt.scatter(center[:, 0], center[:, 1], s=100, c='m', marker='s') plt.title("clustered data and centroids (K = 3)") plt.show()
我們也可以將簇數(shù)設(shè)置為 4,觀察算法運行結(jié)果:
ret, label, center = cv2.kmeans(data, 4, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
3. K最近鄰
k-最近鄰 (k-nearest neighbours, kNN) 是監(jiān)督學(xué)習(xí)中最簡單的算法之一,kNN 可用于分類和回歸問題。在訓(xùn)練階段,kNN 存儲所有訓(xùn)練樣本的特征向量和類別標(biāo)簽。在測試階段,將未標(biāo)記的向量分類為距離最近的 k 個訓(xùn)練樣本中出現(xiàn)頻率最高的類標(biāo)簽,其中 k 是用戶定義的常數(shù):
如上圖所示,如果 k = 5,則綠色圓圈(未標(biāo)記的測試樣本)將被歸類為三角形,因為離其最近的 5 個樣本中有 3 個三角形但只有 1 個菱形;如果 k = 9,則綠色圓圈將被歸類為菱形,因為離其最近的 9 個樣本中有 5 個菱形但只有 4 個三角形。
在 OpenCV 中,使用 kNN 分類器首先需要使用 cv2.ml.KNearest_create() 創(chuàng)建 kNN 分類器,然后提供數(shù)據(jù)和標(biāo)簽以使用 train() 方法訓(xùn)練 kNN分類器。最后,使用 findNearest() 方法用于查找測試樣本鄰居,使用如下:
retval, results, neighborResponses, dist=cv2.ml_KNearest.findNearest(samples, k[, results[, neighborResponses[, dist]]])
其中,samples 是輸入樣本,k 設(shè)置為最近鄰居的個數(shù),results 存儲每個輸入樣本的預(yù)測值,neighborResponses 存儲對應(yīng)的鄰居,dist 存儲輸入樣本到相應(yīng)鄰居的距離。
3.1 K最近鄰示例
接下來,為了演示 kNN 算法,首先隨機創(chuàng)建一組點并分配一個標(biāo)簽 (0 或 1)。標(biāo)簽 0 將代表紅色三角形,而標(biāo)簽 1 將代表藍色方塊;然后,使用 kNN 算法根據(jù) k 個最近鄰對樣本點進行分類。
第一步是創(chuàng)建具有相應(yīng)標(biāo)簽的點集和要分類的樣本點:
# 點集由50個點組成 data = np.random.randint(0, 100, (50, 2)).astype(np.float32) # 為1每個點創(chuàng)建標(biāo)簽 (0:紅色, 1:藍色) labels = np.random.randint(0, 2, (50, 1)).astype(np.float32) # 創(chuàng)建要分類的樣本點 sample = np.random.randint(0, 100, (1, 2)).astype(np.float32)
接下來,創(chuàng)建 kNN 分類器,訓(xùn)練分類器,并找到要分類樣本點的 k 個最近鄰居:
# 創(chuàng)建 kNN 分類器 knn = cv2.ml.KNearest_create() # 訓(xùn)練 kNN 分類器 knn.train(data, cv2.ml.ROW_SAMPLE, labels) # 找到要分類樣本點的 k 個最近鄰居 k = 3 ret, results, neighbours, dist = knn.findNearest(sample, k) # 打印結(jié)果 print("result: {}".format(results)) print("neighbours: {}".format(neighbours)) print("distance: {}".format(dist)) # 可視化 fig = plt.figure(figsize=(8, 6)) red_triangles = data[labels.ravel() == 0] plt.scatter(red_triangles[:, 0], red_triangles[:, 1], 200, 'r', '^') blue_squares = data[labels.ravel() == 1] plt.scatter(blue_squares[:, 0], blue_squares[:, 1], 200, 'b', 's') plt.scatter(sample[:, 0], sample[:, 1], 200, 'g', 'o') plt.show()
獲得結(jié)果如下所示:
result: [[0.]]
neighbours: [[0. 0. 1.]]
distance: [[13. 40. 65.]]
4. 支持向量機
支持向量機 (Support Vector Machine, SVM) 是一種監(jiān)督學(xué)習(xí)技術(shù),它通過根據(jù)指定的類對訓(xùn)練數(shù)據(jù)進行最佳分離,從而在高維空間中構(gòu)建一個或一組超平面。
已二維平面為例,在下圖中看到,其中綠線是能夠?qū)蓚€類分開的最佳超平面,因為其到兩個類中的最近元素的距離是最大的:
上圖第一種情況下,決策邊界是一條線,而在第二種情況下,決策邊界是一條圓形曲線,虛線代表其他決策邊界,但它們并非最好地將兩個類分開的決策邊界。
OpenCV 中的 SVM 實現(xiàn)基于 LIBSVM,使用 cv2.ml.SVM_create() 函數(shù)創(chuàng)建空模型,然后為模型分配主要參數(shù):
svmType :設(shè)置 SVM 類型,可選值如下:
- SVM_C_SVC:C CC-支持向量分類,可用于 n 分類 (n≥2) 問題
- NU_SVC: v vv-支持向量分類
- ONE_CLASS: 分布估計(單類 SVM)
- EPS_SVR: ? \epsilon?-支持向量回歸
- NU_SVR: v vv-支持向量回歸
kernelType :這設(shè)置了 SVM 的核類型,可選值如下:
- LINEAR : 線性核
- POLY :多項式核
- RBF : Radial Basis Function (RBF),大多數(shù)情況下是不錯的選擇
- SIGMOID : Sigmoid 核
- CHI2 : 指數(shù) Chi2 核,類似于 RBF 核
- INTER : 直方圖交集核;運行速度較快的核
degree : 核函數(shù)的 degree 參數(shù) (用于 POLY 核)
gamma :核函數(shù)的 γ \gammaγ 參數(shù)(用于 POLY/RBF/SIGMOID/CHI2 核)
coef0 : 核函數(shù)的 coef0 參數(shù) (用于 POLY/SIGMOID 核)
Cvalue : SVM 優(yōu)化問題的 C 參數(shù) (用于 C_SVC/EPS_SVR/NU_SVR 類型)
nu : SVM 優(yōu)化問題的 v vv 參數(shù) (用于 NU_SVC/ONE_CLASS/NU_SVR 類型)
p : SVM 優(yōu)化問題的 ? \epsilon? 參數(shù) (用于 EPS_SVR 類型)
classWeights : C_SVC 問題中的可選權(quán)重,分配給特定的類
termCrit :迭代 SVM 訓(xùn)練過程的終止標(biāo)準(zhǔn)
核函數(shù)選擇通常取決于數(shù)據(jù)集,通常可以首先使用 RBF 核進行測試,因為該核將樣本非線性地映射到更高維空間,可以方便的處理類標(biāo)簽和屬性之間的關(guān)系是非線性的情況。
默認構(gòu)造函數(shù)使用以下值初始化 SVM:
svmType: C_SVC, kernelType: RBF, degree: 0, gamma: 1, coef0: 0, C: 1, nu: 0, p: 0, classWeights: 0, termCrit: TermCriteria(MAX_ITER+EPS, 1000, FLT_EPSILON )
4.1 支持向量機示例
為了解如何在 OpenCV 中使用 SVM,首先需要創(chuàng)建訓(xùn)練數(shù)據(jù)和標(biāo)簽:
labels = np.array([1, 1, -1, -1, -1]) data = np.matrix([[800, 40], [850, 400], [500, 10], [550, 300], [450, 600]], dtype=np.float32)
以上代碼創(chuàng)建了 5 個點,前 2 個點被指定為 1 類,而另外 3 個被指定為 -1 類。接下來使用 svm_init() 函數(shù)初始化 SVM 模型:
def svm_init(C=12.5, gamma=0.50625): """ 創(chuàng)建 SVM 模型并為其分配主要參數(shù),返回模型 """ model = cv2.ml.SVM_create() model.setGamma(gamma) model.setC(C) model.setKernel(cv2.ml.SVM_LINEAR) model.setType(cv2.ml.SVM_C_SVC) model.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6)) return model # 初始化 SVM 模型 svm_model = svm_init(C=12.5, gamma=0.50625)
創(chuàng)建的 SVM 核類型設(shè)置為 LINEAR,SVM 的類型設(shè)置為 C_SVC。
然后,編寫 svm_train() 函數(shù)訓(xùn)練 SVM 模型:
def svm_train(model, samples, responses): # 使用 samples 和 responses 訓(xùn)練模型 model.train(samples, cv2.ml.ROW_SAMPLE, responses) return model # 訓(xùn)練 SVM svm_train(svm_model, data, labels)
然后創(chuàng)建一個圖像,并繪制 SVM 響應(yīng):
def show_svm_response(model, image): colors = {1: (255, 255, 0), -1: (0, 255, 255)} for i in range(image.shape[0]): for j in range(image.shape[1]): sample = np.matrix([[j, i]], dtype=np.float32) response = svm_predict(model, sample) image[i, j] = colors[response.item(0)] cv2.circle(image, (800, 40), 10, (255, 0, 0), -1) cv2.circle(image, (850, 400), 10, (255, 0, 0), -1) cv2.circle(image, (500, 10), 10, (0, 255, 0), -1) cv2.circle(image, (550, 300), 10, (0, 255, 0), -1) cv2.circle(image, (450, 600), 10, (0, 255, 0), -1) support_vectors = model.getUncompressedSupportVectors() for i in range(support_vectors.shape[0]): cv2.circle(image, (support_vectors[i, 0], support_vectors[i, 1]), 15, (0, 0, 255), 6) # 創(chuàng)建圖像 img_output = np.zeros((640, 1200, 3), dtype="uint8") # 顯示 SVM 響應(yīng) show_svm_response(svm_model, img_output)
如上圖所示,SVM 使用訓(xùn)練數(shù)據(jù)進行了訓(xùn)練,可用于對圖像中所有點進行分類。SVM 將圖像劃分為黃色和青色區(qū)域,可以看到兩個區(qū)域之間的邊界對應(yīng)于兩個類之間的最佳間隔,因為到兩個類中最近元素的距離最大,支持向量用紅線邊框顯示。
小結(jié)
在本文中,首先介紹機器學(xué)習(xí)的概念及其相關(guān)話題,然后總結(jié)了機器學(xué)習(xí)中的三種主要方法,并總結(jié)了分類、回歸和聚類問題的三種最常見的技術(shù)。最后,我們通過示例了解了常用機器學(xué)習(xí)算法,具體而言,包括 K-Means 聚類算法、kNN 算法和 SVM 算法。?
以上就是Python OpenCV實戰(zhàn)之與機器學(xué)習(xí)的碰撞的詳細內(nèi)容,更多關(guān)于OpenCV 機器學(xué)習(xí)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python企業(yè)編碼生成系統(tǒng)之系統(tǒng)主要函數(shù)設(shè)計詳解
這篇文章主要介紹了Python企業(yè)編碼生成系統(tǒng)之系統(tǒng)主要函數(shù)設(shè)計,涉及目錄操作、文件讀寫、驗證判斷、編碼輸出等功能實現(xiàn)技巧,需要的朋友可以參考下2019-07-07Django分頁查詢并返回jsons數(shù)據(jù)(中文亂碼解決方法)
這篇文章主要介紹了Django分頁查詢并返回jsons數(shù)據(jù)(中文亂碼解決方法),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08pygame+opencv實現(xiàn)讀取視頻幀的方法示例
由于pygame.movie.Movie.play()只支持MPEG格式的視頻,所以決定使用與opencv讀取視頻幀的畫面,本文就詳細的介紹了pygame+opencv實現(xiàn)讀取視頻幀,感興趣的可以了解一下2021-12-12Python實現(xiàn)讀取txt文件中的數(shù)據(jù)并繪制出圖形操作示例
這篇文章主要介紹了Python實現(xiàn)讀取txt文件中的數(shù)據(jù)并繪制出圖形操作,涉及Python文件讀取、數(shù)值運算及基于pylab庫的圖形繪制相關(guān)操作技巧,需要的朋友可以參考下2019-02-02