丁香五月天婷婷久久婷婷色综合91|国产传媒自偷自拍|久久影院亚洲精品|国产欧美VA天堂国产美女自慰视屏|免费黄色av网站|婷婷丁香五月激情四射|日韩AV一区二区中文字幕在线观看|亚洲欧美日本性爱|日日噜噜噜夜夜噜噜噜|中文Av日韩一区二区

您正在使用IE低版瀏覽器,為了您的雷峰網(wǎng)賬號(hào)安全和更好的產(chǎn)品體驗(yàn),強(qiáng)烈建議使用更快更安全的瀏覽器
此為臨時(shí)鏈接,僅用于文章預(yù)覽,將在時(shí)失效
人工智能開發(fā)者 正文
發(fā)私信給AI研習(xí)社
發(fā)送

0

詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼

本文作者: AI研習(xí)社 2017-06-19 16:41
導(dǎo)語(yǔ):還在為驗(yàn)證碼糟心?

雷鋒網(wǎng)按:本文作者 Slyne_D,原載于作者個(gè)人博客,雷鋒網(wǎng)已獲授權(quán)。

這是去年博主心血來潮實(shí)現(xiàn)的一個(gè)小模型,現(xiàn)在把它總結(jié)一下。由于樓主比較懶,網(wǎng)上許多方法都需要切割圖片,但是樓主思索了一下感覺讓模型有多個(gè)輸出就可以了呀,沒必要一定要切割的吧?切不好還需要損失信息啊!本文比較簡(jiǎn)單,只基于傳統(tǒng)的驗(yàn)證碼。

Part 0 模型概覽

詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼

從圖片到序列實(shí)際上就是Image2text也就是seq2seq的一種。encoder是Image, decoder是驗(yàn)證碼序列。由于keras不支持傳統(tǒng)的在decoder部分每個(gè)cell輸出需要作為下一個(gè)rnn的cell的輸入(見下圖),所以我們這里把decoder部分的輸入用encoder(image)的最后一層復(fù)制N份作為decoder部分的每個(gè)cell的輸入。

詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼

典型的seq2seq

詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼

keras可以直接實(shí)現(xiàn)的image2text

當(dāng)然利用 recurrentshopseq2seq,我們也可以實(shí)現(xiàn)標(biāo)準(zhǔn)的seq2seq的網(wǎng)絡(luò)結(jié)構(gòu)(后文會(huì)寫)。

Part I 收集數(shù)據(jù)

網(wǎng)上還是有一些數(shù)據(jù)集可以用的,包括dataCastle也舉辦過驗(yàn)證碼識(shí)別的比賽,都有現(xiàn)成的標(biāo)注好了的數(shù)據(jù)集。(然而難點(diǎn)是各種花式驗(yàn)證碼啊,填字的,滑動(dòng)的,還有那個(gè)基于語(yǔ)義的reCaptcha~)。

因?yàn)槲蚁肱龈鞣N長(zhǎng)度的驗(yàn)證碼,所以我還是在github上下載了一個(gè)生成驗(yàn)證碼的python包。

下載后,按照例子生成驗(yàn)證碼(包含26個(gè)小寫英文字母):

#!/usr/bin/env python

# -*- coding: utf-8

from captcha.image import ImageCaptcha

from random import sample


image = ImageCaptcha() #fonts=[ "font/Xenotron.ttf"]

characters =  list("abcdefghijklmnopqrstuvwxyz")


def generate_data(digits_num, output, total):

    num = 0

    while(num<total):

        cur_cap = sample(characters, digits_num)

        cur_cap =''.join(cur_cap)

        _ = image.generate(cur_cap)

        image.write(cur_cap, output+cur_cap+".png")

        num += 1


generate_data(4, "images/four_digit/", 10000)  #產(chǎn)生四個(gè)字符長(zhǎng)度的驗(yàn)證碼

generate_data(5, "images/five_digit/", 10000) #產(chǎn)生五個(gè)字符長(zhǎng)度的驗(yàn)證碼

generate_data(6, "images/six_digit/", 10000) #產(chǎn)生六個(gè)字符長(zhǎng)度的驗(yàn)證碼

generate_data(7, "images/seven_digit/",10000) # 產(chǎn)生七個(gè)字符長(zhǎng)度的驗(yàn)證碼

產(chǎn)生的驗(yàn)證碼

詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼

(目測(cè)了一下生成驗(yàn)證碼的包的代碼,發(fā)現(xiàn)主要是在x,y軸上做一些變換,加入一些噪音)

Part II 預(yù)處理

由于生成的圖片不是相同尺寸的,為了方便訓(xùn)練我們需要轉(zhuǎn)換成相同尺寸的。另外由于驗(yàn)證碼長(zhǎng)度不同,我們需要在label上多加一個(gè)符號(hào)來表示這個(gè)序列的結(jié)束。

處理之后的結(jié)果就是圖像size全部為Height=60, Width=250, Channel=3。label全部用字符id表示,并且末尾加上表示<EOF>的id。比如假設(shè)a-z的id為0-25,<EOF>的id為26,那么對(duì)于驗(yàn)證碼"abdf"的label也就是[0,1,3,5,26,26,26,26],"abcdefg"的label為[0,1,2,3,4,5,6,26]。

由于我們用的是categorical_crossentropy來判斷每個(gè)輸出的結(jié)果,所以對(duì)label我們還需要把其變成one-hot的形式,那么用Keras現(xiàn)成的工具to_categorical函數(shù)對(duì)上面的label做一下處理就可以了。比如abdf的label進(jìn)一步轉(zhuǎn)換成:

[[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]]

Part III 構(gòu)建模型

不借助外部包可以實(shí)現(xiàn)的模型

def create_simpleCnnRnn(image_shape, max_caption_len,vocab_size):

    image_model = Sequential()

    # image_shape : C,W,H

    # input: 100x100 images with 3 channels -> (3, 100, 100) tensors.

    # this applies 32 convolution filters of size 3x3 each.

    image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=image_shape))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(Convolution2D(32, 3, 3))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(MaxPooling2D(pool_size=(2, 2)))

    image_model.add(Dropout(0.25))

    image_model.add(Convolution2D(64, 3, 3, border_mode='valid'))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(Convolution2D(64, 3, 3))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(MaxPooling2D(pool_size=(2, 2)))

    image_model.add(Dropout(0.25))

    image_model.add(Flatten())

    # Note: Keras does automatic shape inference.

    image_model.add(Dense(128))

    image_model.add(RepeatVector(max_caption_len)) # 復(fù)制8份

    image_model.add(Bidirectional(GRU(output_dim=128, return_sequences=True)))

    image_model.add(TimeDistributed(Dense(vocab_size)))

    image_model.add(Activation('softmax'))

    sgd = SGD(lr=0.002, decay=1e-6, momentum=0.9, nesterov=True)

    image_model.compile(loss='categorical_crossentropy', optimizer=sgd, metrics=['accuracy'])

    return image_model

借助recurrentshop和seq2seq可以實(shí)現(xiàn)的結(jié)構(gòu)

def create_imgText(image_shape, max_caption_len,vocab_size):

    image_model = Sequential()

    # image_shape : C,W,H

    # input: 100x100 images with 3 channels -> (3, 100, 100) tensors.

    # this applies 32 convolution filters of size 3x3 each.

    image_model.add(Convolution2D(32, 3, 3, border_mode='valid', input_shape=image_shape))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(Convolution2D(32, 3, 3))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(MaxPooling2D(pool_size=(2, 2)))

    image_model.add(Dropout(0.25))

    image_model.add(Convolution2D(64, 3, 3, border_mode='valid'))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(Convolution2D(64, 3, 3))

    image_model.add(BatchNormalization())

    image_model.add(Activation('relu'))

    image_model.add(MaxPooling2D(pool_size=(2, 2)))

    image_model.add(Dropout(0.25))

    image_model.add(Flatten())

    # Note: Keras does automatic shape inference.

    image_model.add(Dense(128))

    image_model.add(RepeatVector(1)) # 為了兼容seq2seq,要多包一個(gè)[]

    #model = AttentionSeq2Seq(input_dim=128, input_length=1, hidden_dim=128, output_length=max_caption_len, output_dim=128, depth=2) 

    model = Seq2Seq(input_dim=128, input_length=1, hidden_dim=128, output_length=max_caption_len,

                             output_dim=128, peek=True)

    image_model.add(model)

    image_model.add(TimeDistributed(Dense(vocab_size)))

    image_model.add(Activation('softmax'))

    image_model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])


    return image_model

Part IV 模型訓(xùn)練

之前寫過固定長(zhǎng)度的驗(yàn)證碼的序列準(zhǔn)確率可以達(dá)到99%,項(xiàng)目可以參考這里

另外,我們?cè)谟肒eras訓(xùn)練的時(shí)候會(huì)有一個(gè)acc,這個(gè)acc是指的一個(gè)字符的準(zhǔn)確率,并不是這一串序列的準(zhǔn)確率。也就是說在可以預(yù)期的情況下,如果你的一個(gè)字符的準(zhǔn)確率達(dá)到了99%,那么如果你的序列長(zhǎng)度是5的時(shí)候,理論上你的序列準(zhǔn)確率是0.99^5 = 0.95, 如果像我們一樣序列長(zhǎng)度是7,則為0.99^8=0.923。

所以當(dāng)你要看到實(shí)際的驗(yàn)證集上的準(zhǔn)確率的時(shí)候,應(yīng)該自己寫一個(gè)callback的類來評(píng)測(cè),只有當(dāng)序列中所有的字符都和label一樣才可以算正確。

class ValidateAcc(Callback):

    def __init__(self, image_model, val_data, val_label, model_output):

        self.image_model = image_model

        self.val = val_data

        self.val_label = val_label

        self.model_output = model_output


    def on_epoch_end(self, epoch, logs={}):  # 每個(gè)epoch結(jié)束后會(huì)調(diào)用該方法

        print '\n———————————--------'

        self.image_model.load_weights(self.model_output+'weights.%02d.hdf5' % epoch)

        r = self.image_model.predict(val, verbose=0)

        y_predict = np.asarray([np.argmax(i, axis=1) for i in r])

        val_true = np.asarray([np.argmax(i, axis = 1) for i in self.val_label])

        length = len(y_predict) * 1.0

        correct = 0

        for (true,predict) in zip(val_true,y_predict):

            print true,predict

            if list(true) == list(predict):

                correct += 1

        print "Validation set acc is: ", correct/length

        print '\n———————————--------'



val_acc_check_pointer = ValidateAcc(image_model,val,val_label,model_output)

記錄每個(gè)epoch的模型結(jié)果

check_pointer = ModelCheckpoint(filepath=model_output + "weights.{epoch:02d}.hdf5")

訓(xùn)練

image_model.fit(train, train_label,

                shuffle=True, batch_size=16, nb_epoch=20, validation_split=0.2, callbacks=[check_pointer, val_acc_check_pointer])

Part V 訓(xùn)練結(jié)果

在39866張生成的驗(yàn)證碼上,27906張作為訓(xùn)練,11960張作為驗(yàn)證集。

第一種模型:

序列訓(xùn)練了大約80輪,在驗(yàn)證集上最高的準(zhǔn)確率為0.9264, 但是很容易變化比如多跑一輪就可能變成0.7,主要原因還是因?yàn)轭A(yù)測(cè)的時(shí)候考慮的是整個(gè)序列而不是單個(gè)字符,只要有一個(gè)字符沒有預(yù)測(cè)準(zhǔn)確整個(gè)序列就是錯(cuò)誤的。

第二種模型:

第二個(gè)模型也就是上面的create_imgText,驗(yàn)證集上的最高準(zhǔn)確率差不多是0.9655(當(dāng)然我沒有很仔細(xì)的去調(diào)參,感覺調(diào)的好的話兩個(gè)模型應(yīng)該是差不多的,驗(yàn)證集達(dá)到0.96之后相對(duì)穩(wěn)定)。

Part VI 其它

看起來還是覺得keras實(shí)現(xiàn)簡(jiǎn)單的模型會(huì)比較容易,稍微變形一點(diǎn)的模型就很糾結(jié)了,比較好的是基礎(chǔ)的模型用上其他包都可以實(shí)現(xiàn)。keras 2.0.x開始的版本跟1.0.x還是有些差異的,而且recurrentshop現(xiàn)在也是支持2.0版本的。如果在建模型的時(shí)候想更flexible一點(diǎn)的話,還是用tensorflow會(huì)比較好,可以調(diào)整的東西也比較多,那下一篇可以寫一下img2txt的tensorflow版本。

Part VII 代碼

完整源代碼:https://github.com/Slyne/CaptchaVariLength

Part VIII 后續(xù)

現(xiàn)在的這兩個(gè)模型還是需要指定最大的長(zhǎng)度,后面有時(shí)間會(huì)在訓(xùn)練集最多只有8個(gè)字符的情況下,利用rnn的最后一層進(jìn)一步對(duì)于有9個(gè)以及以上字符的驗(yàn)證碼效果,看看是不是可以再進(jìn)一步的擴(kuò)展到任意長(zhǎng)度。(又立了一個(gè)flag~)

雷鋒網(wǎng)相關(guān)閱讀:

LSTM Networks 應(yīng)用于股票市場(chǎng)探究

一文詳解如何用 TensorFlow 實(shí)現(xiàn)基于 LSTM 的文本分類(附源碼)

雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。

詳解如何用 LSTM 自動(dòng)識(shí)別驗(yàn)證碼

分享:
相關(guān)文章

編輯

聚焦數(shù)據(jù)科學(xué),連接 AI 開發(fā)者。更多精彩內(nèi)容,請(qǐng)?jiān)L問:yanxishe.com
當(dāng)月熱門文章
最新文章
請(qǐng)?zhí)顚懮暾?qǐng)人資料
姓名
電話
郵箱
微信號(hào)
作品鏈接
個(gè)人簡(jiǎn)介
為了您的賬戶安全,請(qǐng)驗(yàn)證郵箱
您的郵箱還未驗(yàn)證,完成可獲20積分喲!
請(qǐng)驗(yàn)證您的郵箱
立即驗(yàn)證
完善賬號(hào)信息
您的賬號(hào)已經(jīng)綁定,現(xiàn)在您可以設(shè)置密碼以方便用郵箱登錄
立即設(shè)置 以后再說