1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        captcha_break驗(yàn)證碼識(shí)別

        聯(lián)合創(chuàng)作 · 2023-09-26 03:28

        使用深度學(xué)習(xí)來(lái)破解 captcha 驗(yàn)證碼

        本項(xiàng)目會(huì)通過(guò) Keras 搭建一個(gè)深度卷積神經(jīng)網(wǎng)絡(luò)來(lái)識(shí)別 captcha 驗(yàn)證碼,建議使用顯卡來(lái)運(yùn)行該項(xiàng)目。

        下面的可視化代碼都是在 jupyter notebook 中完成的,如果你希望寫成 python 腳本,稍加修改即可正常運(yùn)行,當(dāng)然也可以去掉這些可視化代碼。

        2019 年更新了:

        • 適配了新版 API
        • 提高了數(shù)據(jù)生成器的效率
        • 使用了 CuDNNGRU 提高了訓(xùn)練和預(yù)測(cè)效率
        • 更新了文檔

        環(huán)境

        本項(xiàng)目使用的環(huán)境如下:

        • captcha 0.3
        • tensorflow-gpu 1.13.1
        • numpy 1.16.4
        • tqdm 4.28.1

        下面幾個(gè)包是用于可視化的:

        • matplotlib 2.2.2
        • pandas 0.23.0
        • pydot 1.4.1
        • graphviz 2.38.0-12ubuntu2.1

        captcha

        captcha 是用 python 寫的生成驗(yàn)證碼的庫(kù),它支持圖片驗(yàn)證碼和語(yǔ)音驗(yàn)證碼,我們使用的是它生成圖片驗(yàn)證碼的功能。

        首先我們?cè)O(shè)置我們的驗(yàn)證碼格式為數(shù)字加大寫字母,生成一串驗(yàn)證碼試試看:

        from captcha.image import ImageCaptcha
        import matplotlib.pyplot as plt
        import numpy as np
        import random
        
        %matplotlib inline
        %config InlineBackend.figure_format = 'retina'
        
        import string
        characters = string.digits + string.ascii_uppercase
        print(characters)
        
        width, height, n_len, n_class = 170, 80, 4, len(characters)
        
        generator = ImageCaptcha(width=width, height=height)
        random_str = ''.join([random.choice(characters) for j in range(4)])
        img = generator.generate_image(random_str)
        
        plt.imshow(img)
        plt.title(random_str)

        防止 tensorflow 占用所有顯存

        眾所周知 tensorflow 默認(rèn)占用所有顯存,這樣不利于我們同時(shí)進(jìn)行多項(xiàng)實(shí)驗(yàn),因此我們可以使用下面的代碼當(dāng) tensorflow 使用它需要的顯存,而不是直接占用所有顯存。

        import tensorflow as tf
        import tensorflow.keras.backend as K
        
        config = tf.ConfigProto()
        config.gpu_options.allow_growth=True
        sess = tf.Session(config=config)
        K.set_session(sess)

        數(shù)據(jù)生成器

        訓(xùn)練模型的時(shí)候,我們可以選擇兩種方式來(lái)生成我們的訓(xùn)練數(shù)據(jù),一種是一次性生成幾萬(wàn)張圖,然后開(kāi)始訓(xùn)練,一種是定義一個(gè)數(shù)據(jù)生成器,然后利用 fit_generator 函數(shù)來(lái)訓(xùn)練。

        第一種方式的好處是訓(xùn)練的時(shí)候顯卡利用率高,如果你需要經(jīng)常調(diào)參,可以一次生成,多次使用;第二種方式的好處是你不需要生成大量數(shù)據(jù),訓(xùn)練過(guò)程中可以利用 CPU 生成數(shù)據(jù),而且還有一個(gè)好處是你可以無(wú)限生成數(shù)據(jù)。

        我們的數(shù)據(jù)格式如下:

        X

        X 的形狀是 (batch_size, height, width, 3),比如一批生成 128 個(gè)樣本,圖片寬度為170,高度為80,那么 X 的形狀就是 (128, 64, 128, 3),如果你想取第一張圖,代碼可以這樣寫 X[0]。

        y

        y 的形狀是四個(gè) (batch_size, n_class),如果轉(zhuǎn)換成 numpy 的格式,則是 (n_len, batch_size, n_class),比如一批生成 128 個(gè)樣本,驗(yàn)證碼的字符有 36 種,長(zhǎng)度是 4 位,那么它的形狀就是 4 個(gè) (128, 36) 的矩陣,也可以說(shuō)是 (4, 32, 36)。

        數(shù)據(jù)生成器

        為了讓 Keras 能夠使用多進(jìn)程并行生成數(shù)據(jù),我們需要使用 Keras 的 Sequence 類實(shí)現(xiàn)一個(gè)我們自己的數(shù)據(jù)類。

        __init__ 初始化函數(shù)里,我們定義數(shù)據(jù)所需的參數(shù),然后這個(gè)數(shù)據(jù)的長(zhǎng)度就是 steps 數(shù)。在 __getitem__ 里,我們不用理會(huì)索引號(hào),直接隨機(jī)生成一批樣本送去訓(xùn)練即可。

        from tensorflow.keras.utils import Sequence
        
        class CaptchaSequence(Sequence):
            def __init__(self, characters, batch_size, steps, n_len=4, width=128, height=64):
                self.characters = characters
                self.batch_size = batch_size
                self.steps = steps
                self.n_len = n_len
                self.width = width
                self.height = height
                self.n_class = len(characters)
                self.generator = ImageCaptcha(width=width, height=height)
            
            def __len__(self):
                return self.steps
        
            def __getitem__(self, idx):
                X = np.zeros((self.batch_size, self.height, self.width, 3), dtype=np.float32)
                y = [np.zeros((self.batch_size, self.n_class), dtype=np.uint8) for i in range(self.n_len)]
                for i in range(self.batch_size):
                    random_str = ''.join([random.choice(self.characters) for j in range(self.n_len)])
                    X[i] = np.array(self.generator.generate_image(random_str)) / 255.0
                    for j, ch in enumerate(random_str):
                        y[j][i, :] = 0
                        y[j][i, self.characters.find(ch)] = 1
                return X, y

        使用生成器

        生成器的使用方法很簡(jiǎn)單,只需要用對(duì)它取第一個(gè) batch 即可。下面是一個(gè)例子,初始化一個(gè)數(shù)據(jù)集,設(shè)置 batch_size 和 steps 都為 1,然后取出來(lái)第一個(gè)數(shù)據(jù),對(duì)它可視化。

        在這里我們對(duì)生成的 One-Hot 編碼后的標(biāo)簽進(jìn)行了解碼,首先將它轉(zhuǎn)為 numpy 數(shù)組,然后取36個(gè)字符中最大的數(shù)字的位置(axis=2代表字符的軸),實(shí)際上神經(jīng)網(wǎng)絡(luò)會(huì)輸出36個(gè)字符的概率,我們需要將概率最大的四個(gè)字符的編號(hào)取出來(lái),轉(zhuǎn)換為字符串。

        def decode(y):
            y = np.argmax(np.array(y), axis=2)[:,0]
            return ''.join([characters[x] for x in y])
        
        data = CaptchaSequence(characters, batch_size=1, steps=1)
        X, y = data[0]
        plt.imshow(X[0])
        plt.title(decode(y))

        構(gòu)建深度卷積神經(jīng)網(wǎng)絡(luò)

        from tensorflow.keras.models import *
        from tensorflow.keras.layers import *
        
        input_tensor = Input((height, width, 3))
        x = input_tensor
        for i, n_cnn in enumerate([2, 2, 2, 2, 2]):
            for j in range(n_cnn):
                x = Conv2D(32*2**min(i, 3), kernel_size=3, padding='same', kernel_initializer='he_uniform')(x)
                x = BatchNormalization()(x)
                x = Activation('relu')(x)
            x = MaxPooling2D(2)(x)
        
        x = Flatten()(x)
        x = [Dense(n_class, activation='softmax', name='c%d'%(i+1))(x) for i in range(n_len)]
        model = Model(inputs=input_tensor, outputs=x)

        模型結(jié)構(gòu)很簡(jiǎn)單,特征提取部分使用的是兩個(gè)卷積,一個(gè)池化的結(jié)構(gòu),這個(gè)結(jié)構(gòu)是學(xué)的 VGG16 的結(jié)構(gòu)。我們重復(fù)五個(gè) block,然后我們將它 Flatten,連接四個(gè)分類器,每個(gè)分類器是36個(gè)神經(jīng)元,輸出36個(gè)字符的概率。

        模型可視化

        得益于 Keras 自帶的可視化,我們可以使用幾句代碼來(lái)可視化模型的結(jié)構(gòu):

        from tensorflow.keras.utils import plot_model
        from IPython.display import Image
        
        plot_model(model, to_file='cnn.png', show_shapes=True)
        Image('cnn.png')

        這里需要使用 pydot 這個(gè)庫(kù),以及 graphviz 這個(gè)庫(kù),在 macOS 系統(tǒng)上安裝方法如下:

        brew install graphviz
        pip install pydot-ng

        我們可以看到最后一層卷積層輸出的形狀是 (1, 6, 256),已經(jīng)不能再加卷積層了。

        訓(xùn)練模型

        訓(xùn)練模型反而是所有步驟里面最簡(jiǎn)單的一個(gè),直接使用 model.fit_generator 即可,這里的驗(yàn)證集使用了同樣的生成器,由于數(shù)據(jù)是通過(guò)生成器隨機(jī)生成的,所以我們不用考慮數(shù)據(jù)是否會(huì)重復(fù)。

        為了避免手動(dòng)調(diào)參,我們使用了 Adam 優(yōu)化器,它的學(xué)習(xí)率是自動(dòng)設(shè)置的,我們只需要給一個(gè)較好的初始學(xué)習(xí)率即可。

        EarlyStopping 是一個(gè) Keras 的 Callback,它可以在 loss 超過(guò)多少個(gè) epoch 沒(méi)有下降以后,就自動(dòng)終止訓(xùn)練,避免浪費(fèi)時(shí)間。

        ModelCheckpoint 是另一個(gè)好用的 Callback,它可以保存訓(xùn)練過(guò)程中最好的模型。

        CSVLogger 可以記錄 loss 為 CSV 文件,這樣我們就可以在訓(xùn)練完成以后繪制訓(xùn)練過(guò)程中的 loss 曲線。

        注意,這段代碼在筆記本電腦上可能要較長(zhǎng)時(shí)間,建議使用帶有 NVIDIA 顯卡的機(jī)器運(yùn)行。注意我們這里使用了一個(gè)小技巧,添加 workers=4 參數(shù)讓 Keras 自動(dòng)實(shí)現(xiàn)多進(jìn)程生成數(shù)據(jù),擺脫 python 單線程效率低的缺點(diǎn)。

        from tensorflow.keras.callbacks import EarlyStopping, CSVLogger, ModelCheckpoint
        from tensorflow.keras.optimizers import *
        
        train_data = CaptchaSequence(characters, batch_size=128, steps=1000)
        valid_data = CaptchaSequence(characters, batch_size=128, steps=100)
        callbacks = [EarlyStopping(patience=3), CSVLogger('cnn.csv'), ModelCheckpoint('cnn_best.h5', save_best_only=True)]
        
        model.compile(loss='categorical_crossentropy',
                      optimizer=Adam(1e-3, amsgrad=True), 
                      metrics=['accuracy'])
        model.fit_generator(train_data, epochs=100, validation_data=valid_data, workers=4, use_multiprocessing=True,
                            callbacks=callbacks)

        載入最好的模型繼續(xù)訓(xùn)練一會(huì)

        為了讓模型充分訓(xùn)練,我們可以載入之前最好的模型權(quán)值,然后降低學(xué)習(xí)率為原來(lái)的十分之一,繼續(xù)訓(xùn)練,這樣可以讓模型收斂得更好。

        model.load_weights('cnn_best.h5')
        
        callbacks = [EarlyStopping(patience=3), CSVLogger('cnn.csv', append=True), 
                     ModelCheckpoint('cnn_best.h5', save_best_only=True)]
        
        model.compile(loss='categorical_crossentropy',
                      optimizer=Adam(1e-4, amsgrad=True), 
                      metrics=['accuracy'])
        model.fit_generator(train_data, epochs=100, validation_data=valid_data, workers=4, use_multiprocessing=True,
                            callbacks=callbacks)

        測(cè)試模型

        當(dāng)我們訓(xùn)練完成以后,可以識(shí)別一個(gè)驗(yàn)證碼試試看:

        X, y = data[0]
        y_pred = model.predict(X)
        plt.title('real: %s\npred:%s'%(decode(y), decode(y_pred)))
        plt.imshow(X[0], cmap='gray')
        plt.axis('off')

        計(jì)算模型總體準(zhǔn)確率

        模型在訓(xùn)練的時(shí)候只會(huì)顯示每一個(gè)字符的準(zhǔn)確率,為了統(tǒng)計(jì)模型的總體準(zhǔn)確率,我們可以寫下面的函數(shù):

        from tqdm import tqdm
        def evaluate(model, batch_num=100):
            batch_acc = 0
            with tqdm(CaptchaSequence(characters, batch_size=128, steps=100)) as pbar:
                for X, y in pbar:
                    y_pred = model.predict(X)
                    y_pred = np.argmax(y_pred, axis=-1).T
                    y_true = np.argmax(y, axis=-1).T
        
                    batch_acc += (y_true == y_pred).all(axis=-1).mean()
            return batch_acc / batch_num
        
        evaluate(model)

        這里用到了一個(gè)庫(kù)叫做 tqdm,它是一個(gè)進(jìn)度條的庫(kù),為的是能夠?qū)崟r(shí)反饋進(jìn)度。然后我們通過(guò)一些 numpy 計(jì)算去統(tǒng)計(jì)我們的準(zhǔn)確率,這里計(jì)算規(guī)則是只要有一個(gè)錯(cuò),那么就不算它對(duì)。經(jīng)過(guò)計(jì)算,我們的模型的總體準(zhǔn)確率在經(jīng)過(guò)充分訓(xùn)練以后,可以達(dá)到 98.26% 的總體準(zhǔn)確率。

        模型總結(jié)

        模型的大小是10.7MB,總體準(zhǔn)確率是 98.26%,基本上可以確定破解了此類驗(yàn)證碼。

        改進(jìn)

        對(duì)于這種按順序書(shū)寫的文字,我們還有一種方法可以使用,那就是循環(huán)神經(jīng)網(wǎng)絡(luò)來(lái)識(shí)別序列。下面我們來(lái)了解一下如何使用循環(huán)神經(jīng)網(wǎng)絡(luò)來(lái)識(shí)別這類驗(yàn)證碼。

        CTC Loss

        這個(gè) loss 是一個(gè)特別神奇的 loss,它可以在只知道序列的順序,不知道具體位置的情況下,讓模型收斂。這里有一個(gè)非常好的文章介紹了 CTC Loss: Sequence Modeling With CTC

        在 Keras 里面已經(jīng)內(nèi)置了 CTC Loss ,我們實(shí)現(xiàn)下面的代碼即可在模型里使用 CTC Loss。

        • y_pred 是模型的輸出,是按順序輸出的37個(gè)字符的概率,因?yàn)槲覀冞@里用到了循環(huán)神經(jīng)網(wǎng)絡(luò),所以需要一個(gè)空白字符的概念;
        • labels 是驗(yàn)證碼,是四個(gè)數(shù)字,每個(gè)數(shù)字代表字符在字符集里的位置
        • input_length 表示 y_pred 的長(zhǎng)度,我們這里是16
        • label_length 表示 labels 的長(zhǎng)度,我們這里是4
        import tensorflow.keras.backend as K
        
        def ctc_lambda_func(args):
            y_pred, labels, input_length, label_length = args
            return K.ctc_batch_cost(labels, y_pred, input_length, label_length)

        模型結(jié)構(gòu)

        我們的模型結(jié)構(gòu)是這樣設(shè)計(jì)的,首先通過(guò)卷積神經(jīng)網(wǎng)絡(luò)去識(shí)別特征,然后按水平順序輸入到 GRU 進(jìn)行序列建模,最后使用一個(gè)分類器對(duì)每個(gè)時(shí)刻輸出的特征進(jìn)行分類。

        from tensorflow.keras.models import *
        from tensorflow.keras.layers import *
        
        input_tensor = Input((height, width, 3))
        x = input_tensor
        for i, n_cnn in enumerate([2, 2, 2, 2, 2]):
            for j in range(n_cnn):
                x = Conv2D(32*2**min(i, 3), kernel_size=3, padding='same', kernel_initializer='he_uniform')(x)
                x = BatchNormalization()(x)
                x = Activation('relu')(x)
            x = MaxPooling2D(2 if i < 3 else (2, 1))(x)
        
        x = Permute((2, 1, 3))(x)
        x = TimeDistributed(Flatten())(x)
        
        rnn_size = 128
        x = Bidirectional(CuDNNGRU(rnn_size, return_sequences=True))(x)
        x = Bidirectional(CuDNNGRU(rnn_size, return_sequences=True))(x)
        x = Dense(n_class, activation='softmax')(x)
        
        base_model = Model(inputs=input_tensor, outputs=x)

        為了訓(xùn)練這個(gè)模型,我們還需要搭建一個(gè) loss 計(jì)算網(wǎng)絡(luò),代碼如下:

        labels = Input(name='the_labels', shape=[n_len], dtype='float32')
        input_length = Input(name='input_length', shape=[1], dtype='int64')
        label_length = Input(name='label_length', shape=[1], dtype='int64')
        loss_out = Lambda(ctc_lambda_func, output_shape=(1,), name='ctc')([x, labels, input_length, label_length])
        
        model = Model(inputs=[input_tensor, labels, input_length, label_length], outputs=loss_out)

        真正訓(xùn)練出來(lái)的模型是 base_model,由于 Keras 的限制,我們沒(méi)辦法直接使用 base_model 搭建 CTCLoss,所以我們只能按照上面的方法,讓模型直接輸出 loss。

        模型可視化

        可視化的代碼同上,這里只貼圖。

        可以看到模型比上一個(gè)模型復(fù)雜了許多,但實(shí)際上只是因?yàn)檩斎氡容^多,所以它顯得很大。

        首先模型輸入一個(gè) (height, width, 3) 維度的圖片,然后經(jīng)過(guò)一系列的層降維到了 (2, 16, 256),之后我們使用 Permute 把 width 軸調(diào)整到第一個(gè)維度以適配 RNN 的輸入格式。調(diào)整以后的維度是 (16, 2, 256),然后使用 TimeDistributed(Flatten()) 把后兩個(gè)維度壓成一維,也就是 (16, 512),之后經(jīng)過(guò) 2 層雙向的 GRU 對(duì)序列橫向建模,最后經(jīng)過(guò) Dense 分類器輸出水平方向上每個(gè)字符的概率分布。

        使用 CuDNNGRU 是因?yàn)樗?NVIDIA 顯卡上可以加速非常多倍,如果你使用的是 CPU,改為 GRU 即可。

        使用 RNN 的原因是,如果你看到一句話是 今天我*了一個(gè)非常好吃的蘋果,有一個(gè)字看不清,你很容易猜到這個(gè)字是“吃”,但是使用 CNN,你就很難有這么大的感受野,從蘋果推測(cè)出前面的字是吃。

        數(shù)據(jù)生成器

        數(shù)據(jù)生成器和 CNN 的差不多,這里需要多幾個(gè)矩陣,一個(gè)是 input_length,代表序列長(zhǎng)度,一個(gè)是 label_length,代表驗(yàn)證碼長(zhǎng)度,還有一個(gè) np.ones,沒(méi)有意義,只是為了適配 Keras 訓(xùn)練需要的矩陣輸入。

        from tensorflow.keras.utils import Sequence
        
        class CaptchaSequence(Sequence):
            def __init__(self, characters, batch_size, steps, n_len=4, width=128, height=64, 
                         input_length=16, label_length=4):
                self.characters = characters
                self.batch_size = batch_size
                self.steps = steps
                self.n_len = n_len
                self.width = width
                self.height = height
                self.input_length = input_length
                self.label_length = label_length
                self.n_class = len(characters)
                self.generator = ImageCaptcha(width=width, height=height)
            
            def __len__(self):
                return self.steps
        
            def __getitem__(self, idx):
                X = np.zeros((self.batch_size, self.height, self.width, 3), dtype=np.float32)
                y = np.zeros((self.batch_size, self.n_len), dtype=np.uint8)
                input_length = np.ones(self.batch_size)*self.input_length
                label_length = np.ones(self.batch_size)*self.label_length
                for i in range(self.batch_size):
                    random_str = ''.join([random.choice(self.characters) for j in range(self.n_len)])
                    X[i] = np.array(self.generator.generate_image(random_str)) / 255.0
                    y[i] = [self.characters.find(x) for x in random_str]
                return [X, y, input_length, label_length], np.ones(self.batch_size)

        評(píng)估模型

        from tqdm import tqdm
        
        def evaluate(model, batch_size=128, steps=20):
            batch_acc = 0
            valid_data = CaptchaSequence(characters, batch_size, steps)
            for [X_test, y_test, _, _], _ in valid_data:
                y_pred = base_model.predict(X_test)
                shape = y_pred.shape
                out = K.get_value(K.ctc_decode(y_pred, input_length=np.ones(shape[0])*shape[1])[0][0])[:, :4]
                if out.shape[1] == 4:
                    batch_acc += (y_test == out).all(axis=1).mean()
            return batch_acc / steps

        我們會(huì)通過(guò)這個(gè)函數(shù)來(lái)評(píng)估我們的模型,和上面的評(píng)估標(biāo)準(zhǔn)一樣,只有全部正確,我們才算預(yù)測(cè)正確,中間有個(gè)坑,就是模型最開(kāi)始訓(xùn)練的時(shí)候,并不一定會(huì)輸出四個(gè)字符,所以我們?nèi)绻龅剿械淖址疾坏剿膫€(gè)的時(shí)候,就不計(jì)算了,相當(dāng)于加0,遇到多于4個(gè)字符的時(shí)候,只取前四個(gè)。

        評(píng)估回調(diào)

        因?yàn)?Keras 沒(méi)有針對(duì)這種輸出計(jì)算準(zhǔn)確率的選項(xiàng),因此我們需要自定義一個(gè)回調(diào)函數(shù),它會(huì)在每一代訓(xùn)練完成的時(shí)候計(jì)算模型的準(zhǔn)確率。

        from tensorflow.keras.callbacks import Callback
        
        class Evaluate(Callback):
            def __init__(self):
                self.accs = []
            
            def on_epoch_end(self, epoch, logs=None):
                logs = logs or {}
                acc = evaluate(base_model)
                logs['val_acc'] = acc
                self.accs.append(acc)
                print(f'\nacc: {acc*100:.4f}')

        訓(xùn)練模型

        我們還是按照之前的訓(xùn)練策略,先訓(xùn)練 100 代,等 loss 不降低以后,降低學(xué)習(xí)率,再訓(xùn)練 100 代,代碼如下:

        from tensorflow.keras.callbacks import EarlyStopping, CSVLogger, ModelCheckpoint
        from tensorflow.keras.optimizers import *
        
        train_data = CaptchaSequence(characters, batch_size=128, steps=1000)
        valid_data = CaptchaSequence(characters, batch_size=128, steps=100)
        callbacks = [EarlyStopping(patience=5), Evaluate(), 
                     CSVLogger('ctc.csv'), ModelCheckpoint('ctc_best.h5', save_best_only=True)]
        
        model.compile(loss={'ctc': lambda y_true, y_pred: y_pred}, optimizer=Adam(1e-3, amsgrad=True))
        model.fit_generator(train_data, epochs=100, validation_data=valid_data, workers=4, use_multiprocessing=True,
                            callbacks=callbacks)
        model.load_weights('ctc_best.h5')
        
        callbacks = [EarlyStopping(patience=5), Evaluate(), 
                     CSVLogger('ctc.csv', append=True), ModelCheckpoint('ctc_best.h5', save_best_only=True)]
        
        model.compile(loss={'ctc': lambda y_true, y_pred: y_pred}, optimizer=Adam(1e-4, amsgrad=True))
        model.fit_generator(train_data, epochs=100, validation_data=valid_data, workers=4, use_multiprocessing=True,
                            callbacks=callbacks)

        可以看到 loss 一開(kāi)始下降很快,后面就很平了,但是我們把在對(duì)數(shù)尺度下繪制 loss 圖的話,還是能看到 loss 一直在下降的。acc 上升得也很快,雖然前期訓(xùn)練的時(shí)候 acc 很抖動(dòng),但是后期學(xué)習(xí)率降下來(lái)以后就不會(huì)再跌下來(lái)了。

        最終模型的準(zhǔn)確率達(dá)到了 99.21%,訓(xùn)練過(guò)程中的準(zhǔn)確率最高達(dá)到了 99.49%。

        測(cè)試模型

        characters2 = characters + ' '
        [X_test, y_test, _, _], _  = data[0]
        y_pred = base_model.predict(X_test)
        out = K.get_value(K.ctc_decode(y_pred, input_length=np.ones(y_pred.shape[0])*y_pred.shape[1], )[0][0])[:, :4]
        out = ''.join([characters[x] for x in out[0]])
        y_true = ''.join([characters[x] for x in y_test[0]])
        
        plt.imshow(X_test[0])
        plt.title('pred:' + str(out) + '\ntrue: ' + str(y_true))
        
        argmax = np.argmax(y_pred, axis=2)[0]
        list(zip(argmax, ''.join([characters2[x] for x in argmax])))

        這里隨機(jī)出來(lái)的驗(yàn)證碼很厲害,是O0OP,不過(guò)更厲害的是模型認(rèn)出來(lái)了。

        有趣的問(wèn)題

        我又用之前的模型做了個(gè)測(cè)試,對(duì)于 O0O0 這樣喪心病狂的驗(yàn)證碼,模型偶爾也能正確識(shí)別,這讓我非常驚訝,它是真的能識(shí)別 O 與 0 的差別呢,還是猜出來(lái)的呢?這很難說(shuō)。

        generator = ImageCaptcha(width=width, height=height)
        random_str = 'O0O0'
        X = generator.generate_image(random_str)
        X = np.expand_dims(X, 0) / 255.0
        
        y_pred = base_model.predict(X)
        out = K.get_value(K.ctc_decode(y_pred, input_length=np.ones(y_pred.shape[0])*y_pred.shape[1], )[0][0])[:, :4]
        out = ''.join([characters[x] for x in out[0]])
        
        plt.title('real: %s\npred:%s'%(random_str, out))
        plt.imshow(X[0], cmap='gray')

        總結(jié)

        模型的大小是12.8MB,準(zhǔn)確率達(dá)到了驚人的 99.21%,即使連 0 和 O 都能精準(zhǔn)區(qū)分,非常成功。

        擴(kuò)展

        如果你比較喜歡 PyTorch,可以看 ctc_pytorch.ipynb,精度更高,達(dá)到了 99.57%。

        如果你想查看更多經(jīng)驗(yàn),可以看看我在百度云魅族深度學(xué)習(xí)應(yīng)用大賽的代碼和思路:https://github.com/ypwhs/baiduyun_deeplearning_competition

        參考鏈接

        瀏覽 35
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产又黄的a级鬼片色鬼投胎 | 麻豆网站免费 | 伊人成综合人网站 | 亚洲成人AV无码 | 国产精品嫩草影院88av漫画 | 高清欧美性猛交XXXX | 真实国产亂伦免费看国产精品大黄 | 爆操视频在线观看 | 国产中文二区 | 国产精品久久久久77777按摩 |