核心思想:類似的輸入必會產生類似的輸出。node
原理:首先從訓練樣本矩陣中選擇第一個特徵進行劃分,使每一個子表中該特徵的值所有相同(好比第一個特徵是男女,則能夠劃分出兩個子表,男表和女表),而後再在每一個子表中選擇下一個特徵按照一樣的規則繼續劃分更小的子表(好比第二個特徵是年齡,我能夠劃分紅三個子表(固然根據狀況的不一樣而不一樣),小於18,大於18小於60,大於60,則在男女表中分別又有三個子表,每一個子表下的特徵值都相同),不斷重複直到全部的特徵所有使用完爲止,此時便獲得葉級子表,其中全部樣本的特徵值所有相同。python
解釋:決策樹是一種分類方法,用於對樣本的特徵分類。而分類完成以後,獲得的結果是同一類(或者稱爲表)的全部特徵基本相同,而後根據某一類的全部樣本經過平均(迴歸)或者投票(分類)獲得一個輸出。那麼,當有新的待預測樣本須要預測輸出時,我只需知道樣本屬於哪一個類(表)。算法
工程優化(剪枝):沒必要用盡全部的特徵,葉級子表中容許混雜不一樣的特徵值,以此下降決策樹的層數,在精度犧牲可接受的前提下,提升模型的性能。一般狀況下,能夠優先選擇使信息熵減小量最大的特徵做爲劃分子表的依據。(通俗的講就是有些特徵值並不區分,好比第一個特徵是男女,我並不分紅兩個表,而是放在一個表裏,這種狀況通常是男女這個特徵對輸出的影響不大),如何區分有用特徵和無用特徵或者說影響不大的特徵呢?經過信息熵或基尼指數來區分。也能夠用PCA和ICA等方法對特徵先進行降維操做。api
class sklearn.tree.DecisionTreeClassifier()
參數數組
前面使用的驗證碼特徵和類別對應過於明顯,因此咱們選擇接口的另外一種驗證碼,即70x25大小的,以下:
雖然一樣很簡單,可是加入了字符。
至於預處理和數字驗證碼同樣,正常驗證碼->灰度圖->二值化->切割->標註。不過通過測試發現,不管我如何調參,準確率都比較低。看了全部的字符才發現,圖片的字符雖然沒有傾斜變形但有粗體和細體的區別,而我在標註的時候並無嚴格讓粗體和細體的樣本數同樣。並且字符的位置不在圖片的中間,字符大小也不同,有的偏上,有的偏下,有的偏小,有的又偏大。即便從新標註的準確率仍是難達到我要的標準。微信
對於這種分割線和字符邊緣明顯的驗證碼來講,咱們能夠將字符從切割後的圖片中提取出來,也就是去掉邊緣外的空白,而後都調整到同樣的大小。這樣就去掉了字符位置和大小對算法的干擾,至於粗體和細體,只要保證這兩個的訓練樣本數量相同就能夠了。代碼以下:app
def img_preprocess(file): img1 = Image.open(file) pix = np.array(img1) pix = (pix > 180) * 255 width, height = pix.shape for i in range(width): if np.sum(pix[i]==0): xstart = i break for i in range(width-1, 0, -1): if np.sum(pix[i]==0): xend = i + 1 break for i in range(height): if np.sum(pix[:,i]==0): ystart = i break for i in range(height-1, 0, -1): if np.sum(pix[:,i]==0): yend = i + 1 break new_pix = pix[xstart:xend, ystart:yend] img = Image.fromarray(new_pix).convert('L') if new_pix.size != (8, 10): img = img.resize((8, 10), resample=Image.NEAREST) img.save(file)
接着咱們使用決策樹從新訓練樣本並調整參數,咱們先看max_depth這個參數,代碼以下:dom
from sklearn.tree import DecisionTreeClassifier import os from PIL import Image import numpy as np import matplotlib.pyplot as mp def func(k): x = [] y = [] for label in os.listdir('train'): for file in os.listdir(f'train/{label}'): im = Image.open(f'train/{label}/{file}') pix = np.array(im) pix = (pix > 180) * 1 pix = pix.ravel() x.append(list(pix)) y.append(label) train_x = np.array(x) train_y = np.array(y) model = DecisionTreeClassifier(max_depth=k) model.fit(train_x, train_y) x = [] y = [] for label in os.listdir('test'): for file in os.listdir(f'test/{label}'): im = Image.open(f'test/{label}/{file}') pix = np.array(im) pix = (pix > 180) * 1 pix = pix.ravel() x.append(list(pix)) y.append(label) test_x = np.array(x) test_y = np.array(y) score = model.score(test_x, test_y) return score if __name__ == "__main__": os.chdir('G:\\knn\\字符驗證碼\\') x = list(range(1, 15)) y = [func(i) for i in x] mp.scatter(x, y) mp.show()
運行結果:
能夠看到當max_depth=8的時候,準確率已經很接近1了,因此咱們直接將max_depth取8就好了。既然識別的準確率已經接近1,其餘的參數調不調整好像並不重要了,不過由於這是驗證碼的識別,不容易出現過擬合的狀況,在其餘狀況下,若是準確率接近1就更要去調整隨機參數(random_state和splitter)和剪枝參數(min_samples_leaf等)來防止過擬合。我後面也試着調整了一下其餘參數,發現模型的準確率變化不大,默認便可。機器學習
訓練測試數據集:https://www.lanzous.com/i8joo0f性能
最後,我正在學習一些機器學習的算法,對於一些我須要記錄的內容我都會分享到博客和微信公衆號(python成長路),歡迎關注。平時的話通常分享一些爬蟲或者Python的內容。