使用TensorFlow進行驗證碼識別主要實現思路

主要的思路是傳統的機器學習思路,即:準備訓練集、提取特徵、使用定義好的神經網絡結構進行訓練生成訓練模型;對預測集進行特徵提取,再使用模型預測。

1、準備訓練集。對訓練集進行人工標註(也可以通過captcha驗證碼庫生成標註好的驗證碼),這些訓練集的格式必須是統一的,比如都是160*60分辨率,RGB格式的。(當然正式訓練時可以再進一步預處理,例如分辨率轉化、灰化等)

準備驗證集,可以是從訓練集中提取10%的樣本不加入訓練,這部分樣本可以在在訓練過程中對模型正確率進行檢驗,如果沒有達到預測的正確率,則繼續訓練。

準備預測集:主要是用訓練好的模型對預測集進行預測。但是預測集的格式必須是模型能夠識別的格式。

2、構建神經網絡。這邊就是利用卷積神經網絡的方式去創建。

首先要構建卷積神經網絡的結構,這是一種利用計算圖的思路,先定義好訓練的規則和過程,再賦予數值進行計算。一個完整的卷積神經網絡主要由以下幾個方面組成:

(1)輸入層。整個神經網絡的輸入,在處理驗證碼的卷積神經網絡中,一般代表了圖片的像素矩陣。(這邊就需要把圖片轉化爲像素矩陣)

X = tf.placeholder(tf.float32, [None, IMAGE_HEIGHT * IMAGE_WIDTH]) #定義了輸入層格式,把圖片經過處理保存在一個IMAGE_HEIGHT * IMAGE_WIDTH長度的數組中,個數不確定,先用None表示

x = tf.reshape(X, shape=[-1, IMAGE_HEIGHT, IMAGE_WIDTH, 1]) #這邊是把X維數進行了調整爲四維矩陣

(2)卷積層:和傳統全連接層不同,卷積層每一個節點的輸入只是上一層神經網絡的一小塊,他可以將每一小塊進行更加深入地分析從而得到更高的特徵。他通過一個過濾器(內核,卷積核)將當前層神經網絡上的一個子節點轉化爲下一層神經網絡上的一個單位節點矩陣(長和寬都爲1,但深度不限)。

構建卷積層的主要步驟:(可看TensorFlow教程P147)

    w_c1 = tf.Variable(w_alpha * tf.random_normal([3, 3, 1, 32])) #創建權重變量。這邊是隨機初始化。前兩維是過濾器尺寸,第三維是當前層的深度(這邊是把RGB的圖片灰花了,因此深度爲1),第四維是過濾器深度
    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') #(3)
    conv1 = tf.nn.dropout(conv1, keep_prob) #

tf.nn.conv2d:卷積層的前向傳播算法。第一個參數x爲輸入矩陣,這是一個四維矩陣;w_c1是權重變量;strides=[1, 1, 1, 1]代表不同維度上的步長,但是第一維和最後一維數字要求一定是1;padding='SAME'是填充,表示全0填充。

tf.nn.bias_add:可以給上面的計算結果中每一個節點加上偏置項。

tf.nn.relu:將以上結果使用ReLU函數完成去線性化。(使用了激活函數)

該代碼使用了3層卷積+池化,即使用了多個隱藏層使得神經網絡結構更深,可以解決更加複雜的問題。

(3)池化層:有效地縮小矩陣的尺寸,從而減少最後的全連接層中的參數,可以有效防止過擬合。這邊使用了max pooling是最大池化層,於此對應的還有一種叫average pooling,平均池化層。池化層也需要人工設定過濾器尺寸,是否使用權0填充以及過濾器移動步長等,但池化層隻影響一個深度上的節點(卷積層橫跨整個深度),即還需要在深度維上移動。

tf.nn.max_pool:conv1是卷積層計算結果,也就是當前節點矩陣,ksize=[1, 2, 2, 1]是過濾器尺寸(一般還用[1,3,3,1]),strides是步長,padding是是否使用全0填充。

(4)全連接層:經過幾輪卷積層和池化層處理後,可以認爲圖像中的信息被抽象成了信息含量更高的特徵。即卷積層和池化層可以看成圖像特徵提取的過程。在特徵提取完成之後,需要使用全連接層完成分類任務。

# Fully connected layer
    w_d = tf.Variable(w_alpha * tf.random_normal([8 * 32 * 40, 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)

(5)輸出層:定義了輸出層參數

    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)

3、定義損失函數

神經網絡模型的效果以及優化目標是通過損失函數定義的。train_crack_captcha_cnn方法下的語句:

loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=output, labels=Y))

sigmoid_cross_entropy_with_logits的作用是計算經sigmoid 函數激活之後的交叉熵。logits是神經網絡輸出結果,Y是標準答案,也就是標註的值。

tf.reduce_mean 函數用於計算張量tensor沿着指定的數軸(tensor的某一維度)上的的平均值,主要用作降維或者計算tensor(圖像)的平均值。以上就是可以計算出一個batch的交叉熵平均值。

經典的損失函數:

(1)分類問題:交叉熵,刻畫兩個概率分佈之間的距離。交叉熵值越小,兩個概率分佈越接近。此外還有 一種softmax_cross_entropy_with_logits,即使用softmax迴歸之後的交叉熵損失函數。

(2)迴歸問題:均方誤差,MSE。迴歸問題是對具體數值預測,一般只有一個輸出節點。tf.reduce_mean(tf.square(y_ - y))

4、定義優化函數:

optimizer = tf.train.AdamOptimizer(learning_rate=0.001).minimize(loss)

反向傳播算法是訓練神經網絡的核心算法,它可以根據定義好的損失函數優化神經網絡中參數的取值,從而使神經網絡在訓練數據集上的損失函數達到一個較小值。參數優化過程直接決定了模型的質量。

梯度下降算法會迭代更新參數,不斷沿着梯度的反方向讓參數朝着總損失最小(minimize(loss))的方向更新。還需要定義一個學習率learning_rate來定義每次參數更新的幅度。

爲了減少收斂所需要的迭代次數,每次計算一小部分的訓練數據的損失函數,這部分數據被稱爲batch。即每次迭代的時候都是選用一批數據的。

5、驗證集正確率函數構建:

predict = tf.reshape(output, [-1, MAX_CAPTCHA, CHAR_SET_LEN])
    max_idx_p = tf.argmax(predict, 2)
    max_idx_l = tf.argmax(tf.reshape(Y, [-1, MAX_CAPTCHA, CHAR_SET_LEN]), 2)
    correct_pred = tf.equal(max_idx_p, max_idx_l)
    accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))

tf.argmax(input,axis)根據axis取值的不同返回每行或者每列最大值的索引。 axis是維度,即取該維上的最大值。tf.argmax(predict, 2),tf.argmax(tf.reshape(Y, [-1, MAX_CAPTCHA, CHAR_SET_LEN]), 2)都是表示僅在CHAR_SET_LEN,即標籤維度上去取最大值。輸出的是一個長度爲batch的一維數組,這個一維數組中的值就表示了每一個樣例對應的數字結果。

tf.equal:判斷兩個張量每一維是否相等,它的判斷方法不是整體判斷,而是逐個元素進行判斷,如果相等就是True,不相等,就是False。由於是逐個元素判斷,所以x,y 的維度要一致。即判斷輸出結果和正確標註的值是否一致,每個對應位置比較的結果張量。

tf.cast:執行 tensorflow 中張量數據類型轉換。也就是把布爾型數值轉換爲實數,tf.reduce_mean計算平均值,得出模型在這組batch上的正確率。

6、以上是定義計算圖中所有的計算,第二個階段爲執行計算。也就是進入具體訓練環節

(1)

saver = tf.train.Saver() #該類用於保存和還原一個神經網絡模型
    with tf.Session() as sess: #創建一個會話
        sess.run(tf.global_variables_initializer()) #初始化計算圖中的變量

        step = 0 #定義了訓練的輪次

tf.Session() :會話擁有並管理TensorFlow程序運行時所有資源,所有計算完成後需要關閉會話來幫助系統回收資源。這邊使用with語句,即上下文管理器來使用會話。然後使用 sess.run()函數還執行相應的計算。

tf.global_variables_initializer():是實現初始化所有變量的過程,無需對變量一個個地初始化了。

(2)

while True:
            batch_x, batch_y = get_next_batch('./train/', 64) #從訓練集目錄讀取;64是一次訓練的樣本數,可以更改
            _, loss_ = sess.run([optimizer, loss], feed_dict={X: batch_x, Y: batch_y, keep_prob: 0.75})
            #輸出當前時間,訓練輪次,損失值
            #print (time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())),step, loss_)

            # 每100 step計算一次準確率
            if step % 100 == 0:
                batch_x_test, batch_y_test = get_next_batch('./valide/', 50)#從驗證集目錄讀取50張圖片特徵和標籤向量進行驗證
                acc = sess.run(accuracy, feed_dict={X: batch_x_test, Y: batch_y_test, keep_prob: 1.})
                print (u'***************************************************************第%s次的準確率爲%s'%(step, acc))
                # 如果準確率大於50%,保存模型,完成訓練
                if acc > 0.95:                  ##我這裏設了0.9,設得越大訓練要花的時間越長,如果設得過於接近1,很難達到。如果使用cpu,花的時間很長,cpu佔用很高電腦發燙。
                    saver.save(sess, "crack_capcha.model", global_step=step) #保存模型
                    print (time.time()-start_time)
                    break

            step += 1

batch_x, batch_y = get_next_batch('./train/', 64):是從訓練集中抽取64個樣本,batch_x是64張圖片構成的2維特徵向量,batch_y 是標籤向量,也就是用一個二維數組,每張圖片的特徵和標籤用保存在一行中。

_, loss_ = sess.run([optimizer, loss], feed_dict={X: batch_x, Y: batch_y, keep_prob: 0.75}) : 訓練並且優化。這裏的列表中optimizer和loss應該是指利用用X,Y的值,同時跑這兩個函數,同時會把loss結果返回,而optimizer的值這裏不關心,所以用_代替。

keep_prob: 是每個元素被保留的概率,一般在大量數據訓練時,爲了防止過擬合,添加Dropout層,設置一個0~1之間的小數,keep_prob:1就是所有元素全部保留的意思。也就是dropout中的參數。

同理:acc = sess.run(accuracy, feed_dict={X: batch_x_test, Y: batch_y_test, keep_prob: 1.}) 也就是用X,Y跑accuracy的值,並且返回。

預測:以上是一個通過訓練和優化生成模型的過程。下面解析利用模型預測的過程:

1、加載定義好的神經網絡前向傳播結果和訓練好的模型。

output = crack_captcha_cnn() #其實是輸出了前向傳播結果,但是還未給數據的值初始化,所以這是獲取了一個計算圖
saver = tf.train.Saver()
sess = tf.Session()
saver.restore(sess, tf.train.latest_checkpoint('.')) #從當前目錄加載模型

2、獲取預測集,並進行格式轉換

    text, image = gen_captcha_text_and_image_local(dir_path)
    image = convert2gray(image)
    image = image.flatten() / 255  #把訓練圖片轉成了一維數組

3、獲取預測結果    

predict = tf.argmax(tf.reshape(output, [-1, MAX_CAPTCHA, CHAR_SET_LEN]), 2) #也是先定義了一個獲取前向傳播結果中第3維的值,也就是預測值的函數。
    text_list = sess.run(predict, feed_dict={X: [image], keep_prob: 1}) #給predict 正式賦值,並跑一下。但此處返回的是
    predict_text = text_list[0].tolist() #獲取預測值

4、把預測結果保存到一個MAX_CAPTCHA * CHAR_SET_LEN的向量中,該預測值對應的位置標註爲1。再通過vec2text函數轉化爲文本。

vector = np.zeros(MAX_CAPTCHA * CHAR_SET_LEN)
    i = 0
    for t in predict_text:
        vector[i * CHAR_SET_LEN + t] = 1
        i += 1
        # break

    print("正確值: {} 預測值:{}".format(text, vec2text(vector)))

 

其他:

使用Tensorflow進行數字(字母)驗證碼訓練和預測

發佈了101 篇原創文章 · 獲贊 317 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章