登入網站時必須輸入的圖片驗證碼可以用來識別訪問者到底是人還是機器——這同時也是某種程度上的「圖靈測試」,人工智慧研究者們尋求破解的方向,讓計算機學會破解驗證碼,我們就距離通用智慧更近了一步(前不久 Vicarious 發表在 Science 上的論文就介紹了一種用於破解圖片驗證碼的機器學習新模型)。今天,破解全世界最為流行的圖片驗證碼需要多久?本文作者 Adam Geitgey 告訴你:僅需 15 分鐘。
每個人都討厭 CAPTCHA——這些惱人的圖片中包含你必須輸入的文字,正確地填寫它你才能訪問網站。CAPTCHA 全稱「全自動區分計算機和人類的公開圖靈測試(Completely Automated Public Turing test to tell Computers and Humans Apart)」,旨在確認訪問者是真正的人類,防止惡意程式的入侵。然而,隨著深度學習和計算機視覺技術的發展,現在這些認證方法可以被我們輕鬆破解了。
最近,我正在讀 Adrian RoseBrock 撰寫的《Deep Learning for Computer Vision with Python》。在這本書中,Adrian 利用機器學習破解了 E-ZPass New York 網站上的 CAPTCHA 驗證碼:
在這裡,Adrian 沒有接入生成 CAPTCHA 圖片應用原始碼的許可權。為了破解這樣的系統,我們必須找到數百張示例圖片,然後訓練機器學習模型來破解它。
但是如果我們想要破解開源的 CAPTCHA 系統——在這裡我們擁有所有原始碼的訪問權,事情又會如何呢?
我訪問了 WordPress.org (http://wordpress.org/) 外掛登記網站,在其中搜尋「CAPTCHA」。結果中顯示的第一個內容是「Really Simple CAPTCHA」,已經擁有超過 100 萬次活躍安裝了:https://wordpress.org/plugins/really-simple-captcha/。
重點在於,這裡有它的原始碼!有了生成 CAPTCHA 圖片的原始碼,我們就可以輕鬆破解驗證碼了。在這裡,為了讓任務更具挑戰性,我們先給自己新增一點限制:我們能不能在 15 分鐘內破解它?Let's try it!
Note:這並不意味著我們在批評「Really Simple CAPTCHA」外掛及其作者。目前,外掛的作者已表示該款驗證碼已經不再安全,並推薦使用者尋找其他更加具有安全性的認證方式。但如果你真的是這 100 萬使用者中的一員,或許你應該有所防備了:)
挑戰
首先,我們需要做好計劃,讓我們看看 Really Simple CAPTCHA 生成的圖片是什麼樣子。在 Demo 站中,我們看到了這樣的情景:
一個 CAPTCHA 圖片範例
看起來它會生成由四個字元組成的圖片。讓我們在這個外掛的 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',
);
沒錯,它會生成四個字母/數字組成的 CAPTCHA 驗證碼,每個字元的字型各不相同,在程式碼中我們也可以看出驗證碼中不會包含「O」或者「I」,因為這兩個字母很可能會讓人與數字產生混淆。所以,我們共有 32 個數字或字母需要識別。沒問題!
至此用時:2 分鐘
我們需要的工具
在開始破解之前,我們先要介紹一下行動所需的工具:
Python 3
Python 是目前人工智慧領域中最為流行的程式語言,包含多種機器學習和計算機視覺庫。
OpenCV
OpenCV 是計算機視覺和影像處理任務上的流行框架。在這裡,我們需要使用 OpenCV 來處理 CAPTCHA 生成的影像,OpenCV 擁有 Python API,所以我們可以直接使用 Python 呼叫它。
Keras
Keras 是一個使用 Python 編寫的深度學習框架。他可以讓我們更加輕鬆地定義、訓練和使用深度神經網路——僅需編寫很少的程式碼。
TensorFlow
TensorFlow 是谷歌推出與維護的機器學習庫,也是目前人工智慧領域裡最為流行的框架。我們會在 Keras 之上寫程式碼,但 Keras 實際上並沒有實現神經網路運算的方法——它需要使用 TensorFlow 作為後端來完成具體的工作。
好了,讓我們回到挑戰之中。
創立資料集
想要訓練任何機器學習系統,我們都需要相應的資料集。為了破解 CAPTCHA 驗證碼系統,我們需要這樣的訓練資料:
看起來少不了大量的標註工作。不過在這裡我們有了 WordPress 外掛的原始碼,我們可以稍稍修改外掛,讓它自動輸出 10,000 個 CAPTCHA 圖片,以及相應的正確答案。
在對原始碼的幾分鐘破解之後(只要簡單地加個『for』迴圈),我們就擁有了一個內含 10,000 張 PNG 圖片的訓練集,而圖片的正確答案就是每張圖片的檔名:
Note:在這部分我不會給你示例程式碼。因為本文面向教學,希望各位不會真的去破解各家 WordPress 網站。不過這裡我會給你 10,000 張生成的圖片讓大家用於復現。
至此用時:5 分鐘
簡化問題
現在我們已經有了訓練資料,我們可以直接用它來訓練一個簡單的神經網路:
因為有了足夠的資料,這種方法將能很好地工作,但我們可以使問題變得更簡單。因為問題越簡單、訓練資料越少,我們解決問題所需要的計算力就越少,畢竟我們總共只有 15 分鐘的時間。
幸運的是,一個 CAPTCHA 影像由四個符合組成,因此我們可以以某種方式將影像分割開以令每張影像只有一個符號。這樣的話我們只需訓練神經網路識別單個字元就行了。
我們並不能手動地用 Photoshop 等影像軟體將它們分割開,因為訓練影像總共有 1 萬張。此外,我們也不能將影像切分為四個等大小的影像塊,因為 CAPTCHA 會隨機地將這些不同的字元放置在不同的水平線上,如下所示:
幸運的是,我們能使用已有的方法自動完成這一部分。在影像處理中,我們經常需要檢測有相同色彩的畫素塊,這些連續畫素塊的邊界可以稱之為輪廓。而 OpenCV 有一個內建的 findContours() 函式可以檢測這些輪廓的區域。
所以我們原始的 CAPTCHA 影像為如下所示:
然後我們將該影像轉換為純淨的黑白畫素點(即採用色彩閾值的方法),因此我們將很容易尋找到連續的輪廓邊界:
下面我們使用 OpenCV 的 findContours() 函式以檢測包含連續相同畫素塊的分離部分:
隨後將每個區域儲存為一個單獨的影像檔案就非常簡單了,而且我們也知道每張影像從左到右有四個字元,因此我們可以在儲存的時候使用這種知識標註各個字元。我們只需要按順序儲存它們,並將每一張影像儲存為對應的字元名。
但是還有一個問題,有些 CAPTCHA 影像包含重疊的字元:
這就意味著我們很可能會將兩個字元抽取為一個分割區域:
如果我們不解決這個問題,那麼我們最後就會建立一個非常糟糕的訓練集。我們需要解決這個問題,以免模型會將兩個重疊的字元識別為一個。
這裡有一個簡單的解決方案,如果字元輪廓的寬要比高長一些,那麼很有可能這一個切分內就包含了兩個字元。因此我們可以將這種連體的字元拆分為兩半,並將它們視為單獨的字元。
我們將寬度大於高度一定數值的影像拆分為兩個數值,雖然這種方法非常簡單,但在 CAPTCHA 上卻十分有效。
現在我們有方法抽取獨立的字元,因此我們需要將所有的 CAPTCHA 影像都執行這種處理。我們的目標是收集每個字元的不同變體,並將單個字元的所有變體保留在一個資料夾中。
上圖展示了字元「W」的抽取情況,我們最後從 1 萬張 CAPTCHA 影像中獲取了 1147 張不同的「W」。處理完這些影像後,我們總共大約花了 10 分鐘。
構建並訓練神經網路
因為我們一次只需要識別單個字元,所以並不需要一個複雜的神經網路架構,且識別這種字母與數字的任務要比其它識別複雜影像的任務簡單地多。因此我們使用了一個簡單的卷積神經網路,它一共包含兩個卷積層與兩個全連線層。
如果我們使用的是 Keras,那麼只需要幾行程式碼就能構建一個神經網路架構:
# Build the neural network!
model = Sequential()
# First convolutional layer with max pooling
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)))
# Second convolutional layer with max pooling
model.add(Conv2D(50, (5, 5), padding="same", activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
# Hidden layer with 500 nodes
model.add(Flatten())
model.add(Dense(500, activation="relu"))
# Output layer with 32 nodes (one for each possible letter/number we predict)
model.add(Dense(32, activation="softmax"))
# Ask Keras to build the TensorFlow model behind the scenes
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
現在開始訓練
# Train the neural network
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), batch_size=32, epochs=10, verbose=1)
在經過 10 個 Epoch 的訓練後,我們的訓練準確度可以到達 100%,因此我們就能終止程式以完成整個模型的訓練。所以最後我們一共花了 15 分鐘。
使用訓練後的模型解決 CAPTCHA 識別問題
現在我們利用已訓練的神經網路可以輕鬆識別 CAPTCHA 驗證碼:
- 在網站上使用 WordPress 外掛獲取真正的 CAPTCHA 驗證碼;
- 將 CAPTCHA 影像分割為四個獨立的字元塊,這裡使用的方法和建立訓練集的方法一樣;
- 呼叫神經網路對這四個獨立的字元塊進行預測;
- 將四個預測結果排列以作為該 CAPTCHA 驗證碼的返回結果。
或者我們可以直接使用命令列執行:
試試看!
如果你想自己試驗一下,這裡有程式碼:https://s3-us-west-2.amazonaws.com/mlif-example-code/solving_captchas_code_examples.zip
這個壓縮檔案包中包含 10,000 張例項圖片以及本文中涉及的每一步的程式碼。其中還有 README 檔案告訴你如何執行它。
如果你想要深入瞭解程式碼背後的知識,那麼最好讀一讀那本《Deep Learning for Computer Vision with Python》。它涵蓋了很多細節,並介紹了大量示例,如果你對解決現實生活中困難問題的示例感興趣,那麼它或許很適合你。
原文連結:https://medium.com/@ageitgey/how-to-break-a-captcha-system-in-15-minutes-with-machine-learning-dbebb035a710