公司有一個業(yè)務需要抓取某網站數(shù)據寡夹,登錄需要識別驗證碼航徙,類似下面這種,這應該是很多網站使用的驗證碼類型陷虎。
首先由于驗證碼比較簡單到踏,圖像不復雜,而且全部是數(shù)字尚猿。于是試著采用傳統(tǒng)方式窝稿,按照網上教程自己簡單改了一個,使用 PHP 識別凿掂。大概流程就是切割二值化去噪等預處理伴榔,然后用字符串數(shù)組形式保存起來,識別傳來的圖片同樣預處理后比較字符串的相似度庄萎,選出一個相識度最高的分類踪少。識別率不是很理想(驗證碼比較簡單,應該能優(yōu)化得更好)糠涛,隱約記得只能超過60%援奢。
因為識別效果不理想,目標網站登錄狀態(tài)還是能保持很久忍捡,沒必要花太多精力在這上面集漾,于是找了一個人工打碼服務切黔。簡直太便宜了,一個月花不了多少錢具篇,效果還好纬霞,只是有時候延遲比較高。反正對于我們的業(yè)務來說是足夠用了驱显。
機器學習大潮來臨诗芜,我尋思著能不能用在這上面,于是參考 TensorFlow 識別手寫數(shù)字教程秒紧,開始照貓畫虎绢陌。
本文描述的只是作為一個普通開發(fā)者的一些粗淺理解,所有的代碼和數(shù)據均在文后的 GitHub 有存留熔恢,建議結合代碼閱讀本文脐湾。如果有什么理解錯誤或 Bug 歡迎留言交流 ^_^
TensorFlow 是什么
TensorFlow 是谷歌出的一款機器學習框架⌒鹛剩看名字秤掌,TensorFlow 就是“張量流”。呃鹰霍。闻鉴。什么是張量呢?張量我的理解就是數(shù)據茂洒。張量有自己的形狀孟岛,比如 0 階張量是標量,1 階是向量督勺,2 階是矩陣渠羞。。智哀。所以在后文我們會看到在 TensorFlow 里面使用的量幾乎都要定義其形狀次询,因為它們都是張量。
我們可以把 TensorFlow 看作一個黑盒子瓷叫,里面有一些架好的管道屯吊,喂給他一些“張量”,他吐出一些“張量”摹菠,吐出的東西就是我們需要的結果盒卸。
所以我們需要確定喂進去的是什么,吐出來的是什么次氨,管道如何搭建世落。
更多的入門概念可以查看這個 keras新手指南 ? 一些基本概念
為什么使用 TensorFlow
沒別的什么原因,只是因為谷歌大名,也沒想更多屉佳。先擼起袖子干起來谷朝。如果為了快速成型,我建議可以看一下 Keras武花,號稱為人類設計的機器學習框架圆凰,也就是用戶體驗友好,提供好幾個機器學習框架更高層的接口体箕。
大體流程
- 抓取驗證碼
- 給驗證碼打標簽
- 圖片預處理
- 保存數(shù)據集
- 構建模型訓練
- 提取模型使用
抓取驗證碼
這個簡單专钉,隨便什么方式,循環(huán)下載一大堆累铅,這里不再贅述跃须。我這里下載了 750 張驗證碼,用 500 張做訓練娃兽,剩下 250 張驗證模型效果菇民。
給驗證碼打標簽
這里的驗證碼有750張之巨,要是手工給每個驗證碼打標簽投储,那一定累尿了第练。這時候就可以使用人工打碼服務,用廉價勞動力幫我們做這件事玛荞。人工打碼后把識別結果保存下來娇掏。這里的代碼就不提供了,看你用哪家的驗證碼服務勋眯,相信聰明的你一定能解決 :)
圖片預處理
- 圖片信息:此驗證碼是 68x23婴梧,JPG格式
- 二值化:我確信這個驗證碼足夠簡單,在丟失圖片的顏色信息后仍然能被很好的識別客蹋。并且可以降低模型復雜度志秃,因此我們可以將圖片二值化。即只有兩個顏色嚼酝,全黑或者全白。
- 切割驗證碼: 觀察驗證碼竟坛,沒有特別扭曲或者粘連闽巩,所以我們可以把驗證碼平均切割成4塊,分別識別担汤,這樣圖片識別模型就只需要處理10個分類(如果有字母那將是36個分類而已)由于驗證碼外面有一圈邊框涎跨,所以順帶把邊框也去掉了。
- 處理結果: 16x21崭歧,黑白2位
相關 Python 代碼如下:
img = Image.open(file).convert('L') # 讀取圖片并灰度化
img = img.crop((2, 1, 66, 22)) # 裁掉邊變成 64x21
# 分離數(shù)字
img1 = img.crop((0, 0, 16, 21))
img2 = img.crop((16, 0, 32, 21))
img3 = img.crop((32, 0, 48, 21))
img4 = img.crop((48, 0, 64, 21))
img1 = np.array(img1).flatten() # 扁平化隅很,把二維弄成一維
img1 = list(map(lambda x: 1 if x <= 180 else 0, img1)) # 二值化
img2 = np.array(img2).flatten()
img2 = list(map(lambda x: 1 if x <= 180 else 0, img2))
img3 = np.array(img3).flatten()
img3 = list(map(lambda x: 1 if x <= 180 else 0, img3))
img4 = np.array(img4).flatten()
img4 = list(map(lambda x: 1 if x <= 180 else 0, img4))
保存數(shù)據集
數(shù)據集有輸入輸入數(shù)據和標簽數(shù)據,訓練數(shù)據和測試數(shù)據率碾。
因為數(shù)據量不大叔营,簡便起見屋彪,直接把數(shù)據存成python文件,供模型調用绒尊。就不保存為其他文件畜挥,然后用 pandas 什么的來讀取了。
最終我們的輸入模型的數(shù)據形狀為 [[0,1,0,1,0,1,0,1...],[0,1,0,1,0,1,0,1...],...]
標簽數(shù)據很特殊婴谱,本質上我們是對輸入的數(shù)據進行分類蟹但,所以雖然標簽應該是0到9的數(shù)字,但是這里我們使標簽數(shù)據格式是 one-hot vectors [[1,0,0,0,0,0,0,0,0,0,0],...]
一個one-hot向量除了某一位的數(shù)字是1以外其余各維度數(shù)字都是0**谭羔,比如[1,0,0,0,0,0,0,0,0,0] 代表1华糖,[0,1,0,0,0,0,0,0,0,0]代表2.
更進一步,這里的 one-hot 向量其實代表著對應的數(shù)據分成這十類的概率瘟裸。概率為1就是正確的分類客叉。
相關 Python 代碼如下:
# 保存輸入數(shù)據
def px(prefix, img1, img2, img3, img4):
with open('./data/' + prefix + '_images.py', 'a+') as f:
print(img1, file=f, end=",\n")
print(img2, file=f, end=",\n")
print(img3, file=f, end=",\n")
print(img4, file=f, end=",\n")
# 保存標簽數(shù)據
def py(prefix, code):
with open('./data/' + prefix + '_labels.py', 'a+') as f:
for x in range(4):
tmp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
tmp[int(code[x])] = 1
print(tmp, file=f, end=",\n")
經過上面兩步,我們在就獲得了訓練和測試用的數(shù)據和標簽數(shù)據景描,吶十办,就像這樣
構建模型訓練
數(shù)據準備好啦,到了要搭建“管道”的時候了超棺。
也就是你需要告訴 TensorFlow:
1. 輸入數(shù)據的形狀是怎樣的向族?
x = tf.placeholder(tf.float32, [None, DLEN])
None 表示不定義我們有多少訓練數(shù)據,DLEN是 16*21棠绘,即一維化的圖片的大小件相。
2. 輸出數(shù)據的形狀是怎樣的?
y_ = tf.placeholder("float", [None, 10])
同樣None 表示不定義我們有多少訓練數(shù)據氧苍,10 就是標簽數(shù)據的維度夜矗,即圖片有 10 個分類。每個分類對應著一個概率让虐,所以是浮點類型紊撕。
3. 輸入數(shù)據,模型赡突,標簽數(shù)據怎樣擬合对扶?
W = tf.Variable(tf.zeros([DLEN, 10])) # 權重
b = tf.Variable(tf.zeros([10])) # 偏置
y = tf.nn.softmax(tf.matmul(x, W) + b)
是不是一個很簡單的模型?大體就是
y = softmax(Wx+b)
其中 W 和 b 是 TensorFlow 中的變量惭缰,他們保存著模型在訓練過程中的數(shù)據浪南,需要定義出來。而我們模型訓練的目的漱受,也就是把 W 和 b 的值確定络凿,使得這個式子可以更好的擬合數(shù)據。
softmax 是所謂的激活函數(shù),把線性的結果轉換成我們需要的樣式絮记,也就是分類概率的分布摔踱。
關于 softmax 之類更多解釋請查看參考鏈接。
4. 怎樣評估模型的好壞到千?
模型訓練就是為了使模型輸出結果和實際情況相差盡可能小昌渤。所以要定義評估方式。
這里用所謂的交叉熵來評估憔四。
cross_entropy = -tf.reduce_sum(y_*tf.log(y))
5. 怎樣最小化誤差
現(xiàn)在 TensorFlow 已經知道了足夠的信息膀息,它要做的工作就是讓模型的誤差足夠小,它會使出各種方法使上面定義的交叉熵 cross_entropy 變得盡可能小了赵。
TensorFlow 內置了不少方式可以達到這個目的潜支,不同方式有不同的特點和適用條件。在這里使用梯度下降法來實現(xiàn)這個目的柿汛。
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)
訓練準備
大家知道 Python 作為解釋型語言冗酿,運行效率不能算是太好,而這種機器學習基本是需要大量計算力的場合络断。TensorFlow 在底層是用 C++ 寫就裁替,在 Python 端只是一個操作端口,所有的計算都要交給底層處理貌笨。這自然就引出了會話的概念弱判,底層和調用層需要通信。也正是這個特點锥惋,TensorFlow 支持很多其他語言接入昌腰,如 Java, C,而不僅僅是 Python膀跌。
和底層通信是通過會話完成的遭商。我們可以通過一行代碼來啟動會話:
sess = tf.Session()
# 代碼...
sess.close()
別忘了在使用完后關閉會話。當然你也可以使用 Python 的 with 語句來自動管理捅伤。
在 TensorFlow 中劫流,變量都是需要在會話啟動之后初始化才能使用。
sess.run(tf.global_variables_initializer())
開始訓練
for i in range(DNUM):
batch_xs = [train_images.data[i]]
batch_ys = [train_labels.data[i]]
sess.run(train_step, feed_dict={x: batch_xs, y_: batch_ys})
我們把模型和訓練數(shù)據交給會話丛忆,底層就自動幫我們處理啦祠汇。
我們可以一次傳入任意數(shù)量數(shù)據給模型(上面設置None的作用),為了訓練效果蘸际,可以適當調節(jié)每一批次訓練的數(shù)據。甚至于有時候還要隨機選擇數(shù)據以獲得更好的訓練效果徒扶。在這里我們就一條一條訓練了粮彤,反正最后效果還可以。要了解更多可以查看參考鏈接。
檢驗訓練結果
這里我們的測試數(shù)據就要派上用場了
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
print(sess.run(accuracy, feed_dict={x: test_images.data, y_: test_labels.data}))
我們模型輸出是一個數(shù)組导坟,里面存著每個分類的概率屿良,所以我們要拿出概率最大的分類和測試標簽比較”怪埽看在這 250 條測試數(shù)據里面尘惧,正確率是多少。當然這些也是定義完操作步驟递递,交給會話來運行處理的喷橙。
提取模型使用
在上面我們已經把模型訓練好了,而且效果還不錯哦登舞,近 99% 的正確率贰逾,或許比人工打碼還高一些呢(獲取測試數(shù)據時候常常返回有錯誤的值)。但是問題來了菠秒,我現(xiàn)在要把這個模型用于生產怎么辦疙剑,總不可能每次都訓練一次吧。在這里践叠,我們就要使用到 TensorFlow 的模型保存和載入功能了言缤。
保存模型
先在模型訓練的時候保存模型,定義一個 saver禁灼,然后直接把會話保存到一個目錄就好了管挟。
saver = tf.train.Saver()
# 訓練代碼
# ...
saver.save(sess, 'model/model')
sess.close()
當然這里的 saver 也有不少配置,比如保存最近多少批次的訓練結果之類匾二,可以自行查資料哮独。
恢復模型
同樣恢復模型也很簡單
saver.restore(sess, "model/model")
當然你還是需要定義好模型,才能恢復察藐。我的理解是這里模型保存的是訓練過程中各個變量的值皮璧,權重偏置什么的,所以結構架子還是要事先搭好才行分飞。
最后
這里只是展示了使用 TensorFlow 識別簡單的驗證碼悴务,效果還不錯,上機器學習應該也不算是殺雞用牛刀譬猫。畢竟模型無腦讯檐,節(jié)省很多時間。如果需要識別更加扭曲染服,更加變態(tài)的驗證碼别洪,或許需要上卷積神經網絡之類,圖片結構和顏色信息都不能丟掉了柳刮。另一方面挖垛,做網站安全這塊痒钝,純粹的圖形驗證碼恐怕不能作為判斷是不是機器人的依據。對抗到最后痢毒,就變成這樣的變態(tài)驗證碼哈哈哈送矩。
相關鏈接
https://github.com/purocean/tensorflow-simple-captcha
https://keras-cn.readthedocs.io/en/latest/for_beginners/concepts/
http://wiki.jikexueyuan.com/project/tensorflow-zh/