先宣告,雖然端午節假期將至,但我們今天聊的真不是破解12306的驗證碼,原因嘛你懂的。

我們今天 以破解世界上最流行的WordPress驗證碼外掛為例。
每個人都很討厭驗證碼吧?這些煩人的小照片裡有很多文字資訊,只有輸入它們後才能訪問網站。人們設計驗證碼系統的初衷是為了驗證訪問網站的使用者是一個真實的人。但隨著深度學習和計算機視覺技術的進步,我們很容易就能打敗這些驗證碼系統。(除非你遇到 12306 那種騷騷的圖片識別驗證,有時真的會陷入絕望)


Adrian 沒有訪問網站生成驗證碼照片的工具的原始碼,所以為了破解驗證碼,他必須下載幾百個示例影象,然後手動用它們訓練自己建立的機器學習系統。
但如果我們想破解一個我們能訪問原始碼的開源驗證碼系統呢?
我去 WordPress 的外掛註冊網站上搜了一下“captcha”,第一條搜尋結果是“Really Simple CAPTCHA”,有超過1百萬的活動安裝量:

我們能在15分鐘內完全破解這個驗證碼系統嗎?試試看!

特此宣告:這麼做完全沒有批評“Really Simple CAPTCHA”外掛及其作者的意思。外掛作者自己也曾說過這款外掛已經不是很安全了,建議換用其它外掛。因此這純屬一次好玩的快速技術挑戰。不過,如果你也是這款外掛的使用者,或許你真的該換其它的了。
挑戰開始
在“發動進攻”前,我們先看看“Really Simple CAPTCHA”能生成什麼樣的驗證碼照片。在演示網站上,我們看到這樣:

OK,那麼驗證碼似乎是4個字母和數字的組合形式。我們在PHP原始碼中驗證一下:
public function __construct() {
/* Characters available in images */
$this->chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
/* Length of a word in an image */
$this->char_length = 4;
/* Array of fonts. Randomly picked up per character */
$this->fonts = array(
dirname( __FILE__ ) . '/gentium/GenBkBasR.ttf',
dirname( __FILE__ ) . '/gentium/GenBkBasI.ttf',
dirname( __FILE__ ) . '/gentium/GenBkBasBI.ttf',
dirname( __FILE__ ) . '/gentium/GenBkBasB.ttf',
);
複製程式碼
沒錯,它以隨機混合 4 個不同字型的形式生成 4 個字的驗證碼。我們也能發現它從不用字母“O”和“I”,因為這倆字母比較容易被使用者搞混。所以我們需要識別 24 個英文字母和 10 個阿拉伯數字,也就是 32 個字混合組成的驗證碼。沒問題!
迄今用時:2 分鐘
使用工具
在正式開始挑戰前,先講一下我們會用到的工具:
Python 3
Python 是一種很有意思的程式語言,有很多用於機器學習和計算機視覺的很不錯的程式庫。
OpenCV
OpenCV 是計算機視覺和影象處理中一個很流行的框架。我們會用 OpenCV 處理驗證碼照片。它有一個 Python API,所以我們可以直接從 Python 中使用它。
Keras
Keras 是一個用 Python 編寫的深度學習框架。用 Keras 可以只需編寫少量程式碼就能很容易地定義、訓練和使用深度神經網路。
TensorFlow
TensorFlow 是谷歌出品的一款用於機器學習的程式庫。我們會在 Keras 上的程式設計,但 Keras 自己並不會實際使用神經網路邏輯,相反它會在幕後使用谷歌的 TensorFlow 程式庫挑起重擔。
OK,我們回到挑戰中!
建立我們的資料集
不管是訓練什麼機器學習系統,我們都要有訓練資料。要破解一個驗證碼系統,我們希望能有這樣的訓練資料:

如圖示:輸入驗證碼照片用以訓練模型,然後模型能夠輸出正確的答案。
因為我們有這款 WordPress 外掛的原始碼,我們可以對其修改儲存出 1 萬張驗證碼照片,以及每張照片的預期答案。
在花了幾分鐘修改程式碼和新增一個簡單的 for 迴圈後,我獲得了一個訓練資料資料夾,包含了 1 萬張 PNG 照片,每張照片都有正確答案作為照片的檔名:

這是本文唯一一部分沒有給出我的工作示例程式碼的地方,因為我考慮再三後覺得,這篇教程純粹出於一個好玩的點子,我不希望有人真的用垃圾資訊去刷爆 WordPress 網站。但我會在文末提供我上面生成的這1萬張照片,大家可以拿去用。
迄今用時:5分鐘
將問題簡單化
現在,我們已經獲取了所需的訓練資料,可以用它們直接訓練一個神經網路:

有了足夠的訓練資料,這種方法應該能湊效了,但我們可以讓問題更簡單些。問題越簡單,解決問題所需的訓練資料和計算力就越少。畢竟我們只有 15 分鐘啊!!

幸好,這款外掛生成的驗證碼照片都是隻有 4 個字,所以我們可以以某種方式將照片分拆,每個字作為一張單獨的小照片,這樣我們只需訓練神經網路每次識別一個單獨的字:


因此我們以一張原始驗證碼影象開始:

然後我們將其轉為純黑純白的照片(這步叫做闕值化),這樣就能比較容易地發現照片中的連續區域了:



但是等等,我發現了一個問題!有時驗證碼會出現字母重疊的情況,比如這樣:

這意味著我們有可能把兩個字母提取為一個區域:

這裡有個簡單的技巧,如果一個輪廓線區域的寬度遠大於其高度,這意味著可能有兩個字母相互重疊了。在這種情況下,我們只需從中間將它們切分,作為兩個分開的字母:

現在我們能準確地獲取單個字母了,那就把所有的驗證碼照片過一遍吧。目標是收集每個字母的不同變體。為了容易整理,我們可以將每個字母儲存在其自身資料夾內。
比如,下面是我提取所有字母后,字母“W”的資料夾狀況:

迄今用時:10分鐘
搭建和訓練神經網路
因為我們只需識別單個字母和數字,我們不需要用非常複雜的神經網路架構。識別字母比識別阿狗阿貓這樣的複雜照片要容易的多。 我們會用到一個簡單的卷積神經網路,它有兩個卷積層和兩個完全相連的層:

如果你想了解更多卷積神經網路工作原理的知識,以及為何它們是用於照片識別理想工具,可以翻閱 Adrian 的書籍《Deep Learning for Computer Vision with Python》。
使用 Keras 只需幾行程式碼就能定義這個神經網路架構:
# 搭建神經網路!
model = Sequential()
# 第一個卷積層並最大池化
model.add(Conv2D(20, (5, 5), padding="same", input_shape=(20, 20, 1), activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
# 第二個卷積層並最大池化
model.add(Conv2D(50, (5, 5), padding="same", activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
# 包含500個節點的隱藏層
model.add(Flatten())
model.add(Dense(500, activation="relu"))
# 包含32個節點的輸出層 (每個節點對應我們預測的可能的字母或數字)
model.add(Dense(32, activation="softmax"))
# 用Keras搭建TensorFlow模型
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
複製程式碼
現在可以訓練它了!
# 訓練神經網路
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), batch_size=32, epochs=10, ver
複製程式碼
在用訓練資料集訓練 10 次後,我們達到了接近 100% 的準確率。這時,我們就能隨時自動通過這款工具的驗證碼了!完成任務!
迄今用時:15分鐘(Bingo!)

使用訓練後的模型解決填寫驗證碼問題
現在我們有了一個訓練後的神經網路,用它破解真正的驗證碼非常簡單:
-
從使用文中這款WordPress外掛的網站中抓取一張真正的驗證碼照片。
-
使用我們在建立訓練資料集時所用的方式,將驗證碼照片分成4個單獨的字母照片。
-
讓我們的神經網路為每張字母照片做出獨立的預測。
-
使用4個預測的字母填寫驗證碼。
-
歡呼吧!
這是我們的模型破解真正的驗證碼系統時的樣子:


自己動手試試吧!
如果你想自己動手嘗試,可以點選這裡獲取程式碼,包含了本文所用的1萬張示例照片和每一步中所有的程式碼。檢視資料夾中的 README.md 檔案,獲取執行程式碼的說明。
但是如果你真的想學習每行程式碼的執行知識,我還是強烈建議你看看《Deep Learning for Computer Vision with Python》這本書,講了很多細節知識,還有大量的詳細示例。這是我迄今看到的唯一一本既講解了原理又介紹了怎樣應用於解決實際問題的書籍,去看看吧!

歡迎關注我們,學習資源,AI教程,論文解讀,趣味專案,你想看的都在這裡!