用「活着的」CNN進行驗證碼識別

1 驗證碼介紹

驗證碼( CAPTCHA )是一種區分用戶是計算機或人的公共全自動程序。在 CAPTCHA 測試中,做爲服務器的計算機會自動生成一個問題由用戶來解答。這個問題能夠由計算機生成並評判,可是必須只有人類才能解答。因爲計算機沒法解答 CAPTCHA 的問題,因此回答出問題的用戶就能夠被認爲是人類。前端

2 CNN 驗證碼識別介紹

傳統的方法是經過兩個不相關的步驟來進行文字識別:1)將圖片中的文字的位置進行定位,而後經過「小框」來切分,將圖片中的文字剪切下來 2)再進行識別。可是在現今的驗證碼識別中,當要識別的圖片中的文字變成手寫體互相重疊,這種「切分」法就難以排上用場。所以卷積神經網絡(CNN)就被用來識別這些無從下手的手寫體。這種CNN 是經過一個或多個卷積層和頂端的全連通層(對應經典的神經網絡)組成來對圖像識別。CNN 訓練模型須要大量的人工標註的圖片來訓練,可是本文方法就是自主產生隨機的字符併產生相應的圖片來在運行過程當中調整參數。 本文關注具備 4 個字符的的驗證碼圖片。每一個字符在輸出層被表現爲 62 個神經元。咱們能夠假設一個映射函數python

x \in 
\lbrace 0’...'9','A'...'Z','a'...'z'\rbrace

來對應服務器

l \in \lbrace0...61\rbrace

即:微信

\Theta(x)=\begin{cases}0...9 &  x ='0'...'9'\\10...35 &  x = 'A'...'Z'\\36...61 &  x = 'a'...'z'
\end{cases}

將前 62 個神經元分配給序列中的第一個字符,第二批 62 個神經元分配給序列中的第二個字符。所以,對於字符xi網絡

所對應的神經元的索引爲app

n = i * 62 + \Theta(x_i) 
i \in \lbrace 0...3 \rbrace

輸出層一共有 4*62=128 個。若是第一個預測字符的索引爲 c0=52,所以能夠反推預測的字符爲dom

x = \Theta^-1(c0) =q

3 實現步驟

1 驗證碼生成

1 驗證碼中的字符

number = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
ALPHABET = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
            'V', 'W', 'X', 'Y', 'Z']
alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
            'v', 'w', 'x', 'y', 'z']

gen_char_set = number + ALPHABET  # 用於生成驗證碼的數據集
複製代碼

2 生成驗證碼的字符

# char_set=number + alphabet + ALPHABET,
        char_set=gen_char_set,
        # char_set=number,
        captcha_size=4):
    """ 生成隨機字符串,4位 :param char_set: :param captcha_size: :return: """
    captcha_text = []
    for i in range(captcha_size):
        c = random.choice(char_set)
        captcha_text.append(c)
    return captcha_text
複製代碼

3 按照字符生成對應的驗證碼

def gen_captcha_text_and_image():
    """ 生成字符對應的驗證碼 :return: """
    image = ImageCaptcha()

    captcha_text = random_captcha_text()
    captcha_text = ''.join(captcha_text)

    captcha = image.generate(captcha_text)

    captcha_image = Image.open(captcha)
    captcha_image = np.array(captcha_image)
    return captcha_text, captcha_image
複製代碼

4 訓練

def crack_captcha_cnn(w_alpha=0.01, b_alpha=0.1):
    """1 定義CNN cnn在圖像大小是2的倍數時性能最高, 若是你用的圖像大小不是2的倍數,能夠在圖像邊緣補無用像素。 np.pad(image,((2,3),(2,2)), 'constant', constant_values=(255,)) # 在圖像上補2行,下補3行,左補2行,右補2行 """

    x = tf.reshape(X, shape=[-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1])

    # w_c1_alpha = np.sqrt(2.0/(IMAGE_HEIGHT*IMAGE_WIDTH)) #
    # w_c2_alpha = np.sqrt(2.0/(3*3*32))
    # w_c3_alpha = np.sqrt(2.0/(3*3*64))
    # w_d1_alpha = np.sqrt(2.0/(8*32*64))
    # out_alpha = np.sqrt(2.0/1024)

    # 3 conv layer
    w_c1 = tf.Variable(w_alpha * tf.random_normal([3, 3, 1, 32]))
    b_c1 = tf.Variable(b_alpha * tf.random_normal([32]))
    conv1 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(x, w_c1, strides=[1, 1, 1, 1], padding='SAME'), b_c1))
    conv1 = tf.nn.max_pool(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv1 = tf.nn.dropout(conv1, keep_prob)

    w_c2 = tf.Variable(w_alpha * tf.random_normal([3, 3, 32, 64]))
    b_c2 = tf.Variable(b_alpha * tf.random_normal([64]))
    conv2 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv1, w_c2, strides=[1, 1, 1, 1], padding='SAME'), b_c2))
    conv2 = tf.nn.max_pool(conv2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv2 = tf.nn.dropout(conv2, keep_prob)

    w_c3 = tf.Variable(w_alpha * tf.random_normal([3, 3, 64, 64]))
    b_c3 = tf.Variable(b_alpha * tf.random_normal([64]))
    conv3 = tf.nn.relu(tf.nn.bias_add(tf.nn.conv2d(conv2, w_c3, strides=[1, 1, 1, 1], padding='SAME'), b_c3))
    conv3 = tf.nn.max_pool(conv3, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
    conv3 = tf.nn.dropout(conv3, keep_prob)

    # Fully connected layer
    w_d = tf.Variable(w_alpha * tf.random_normal([8 * 20 * 64, 1024]))
    b_d = tf.Variable(b_alpha * tf.random_normal([1024]))
    dense = tf.reshape(conv3, [-1, w_d.get_shape().as_list()[0]])
    dense = tf.nn.relu(tf.add(tf.matmul(dense, w_d), b_d))
    dense = tf.nn.dropout(dense, keep_prob)

    w_out = tf.Variable(w_alpha * tf.random_normal([1024, MAX_CAPTCHA * CHAR_SET_LEN]))
    b_out = tf.Variable(b_alpha * tf.random_normal([MAX_CAPTCHA * CHAR_SET_LEN]))
    out = tf.add(tf.matmul(dense, w_out), b_out)  # 36*4
    # out = tf.reshape(out, (CHAR_SET_LEN, MAX_CAPTCHA)) # 從新變成4,36的形狀
    # out = tf.nn.softmax(out)
    return out
複製代碼

因爲時間和設備的限制,我在驗證碼生成字符串中刪去了英文字母只剩下了數字進行訓練。要否則能夠算到地老天荒也仍是3%的準確率。下圖是gen_char_set = number + ALPHABET的訓練1萬多步的結果的訓練截圖ide

5 總結

本文采用了「活着的 CNN」進行驗證碼識別,能夠免去大量進行人工標註的步驟,對工做效率有不小的提高。函數


文 / JoeCDC性能

數學愛好者

編 / 熒聲

本文已由做者受權發佈,版權屬於創宇前端。歡迎註明出處轉載本文。本文連接:knownsec-fed.com/2018-09-28-…

想要訂閱更多來自知道創宇開發一線的分享,請搜索關注咱們的微信公衆號:創宇前端(KnownsecFED)。歡迎留言討論,咱們會盡量回復。

感謝您的閱讀。

相關文章
相關標籤/搜索