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