<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

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

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

          本項(xiàng)目會(huì)通過 Keras 搭建一個(gè)深度卷積神經(jīng)網(wǎng)絡(luò)來識(shí)別 captcha 驗(yàn)證碼,建議使用顯卡來運(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à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í)候,我們可以選擇兩種方式來生成我們的訓(xùn)練數(shù)據(jù),一種是一次性生成幾萬張圖,然后開始訓(xùn)練,一種是定義一個(gè)數(shù)據(jù)生成器,然后利用 fit_generator 函數(shù)來訓(xùn)練。

          第一種方式的好處是訓(xùn)練的時(shí)候顯卡利用率高,如果你需要經(jīng)常調(diào)參,可以一次生成,多次使用;第二種方式的好處是你不需要生成大量數(shù)據(jù),訓(xùn)練過程中可以利用 CPU 生成數(shù)據(jù),而且還有一個(gè)好處是你可以無限生成數(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) 的矩陣,也可以說是 (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,然后取出來第一個(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)取出來,轉(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 自帶的可視化,我們可以使用幾句代碼來可視化模型的結(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ù)是通過生成器隨機(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 超過多少個(gè) epoch 沒有下降以后,就自動(dòng)終止訓(xùn)練,避免浪費(fèi)時(shí)間。

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

          CSVLogger 可以記錄 loss 為 CSV 文件,這樣我們就可以在訓(xùn)練完成以后繪制訓(xùn)練過程中的 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í)率為原來的十分之一,繼續(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)度。然后我們通過一些 numpy 計(jì)算去統(tǒng)計(jì)我們的準(zhǔn)確率,這里計(jì)算規(guī)則是只要有一個(gè)錯(cuò),那么就不算它對(duì)。經(jīng)過計(jì)算,我們的模型的總體準(zhǔn)確率在經(jīng)過充分訓(xùn)練以后,可以達(dá)到 98.26% 的總體準(zhǔn)確率。

          模型總結(jié)

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

          改進(jìn)

          對(duì)于這種按順序書寫的文字,我們還有一種方法可以使用,那就是循環(huán)神經(jīng)網(wǎng)絡(luò)來識(shí)別序列。下面我們來了解一下如何使用循環(huán)神經(jīng)網(wǎng)絡(luò)來識(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ì)的,首先通過卷積神經(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)練出來的模型是 base_model,由于 Keras 的限制,我們沒辦法直接使用 base_model 搭建 CTCLoss,所以我們只能按照上面的方法,讓模型直接輸出 loss。

          模型可視化

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

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

          首先模型輸入一個(gè) (height, width, 3) 維度的圖片,然后經(jīng)過一系列的層降維到了 (2, 16, 256),之后我們使用 Permute 把 width 軸調(diào)整到第一個(gè)維度以適配 RNN 的輸入格式。調(diào)整以后的維度是 (16, 2, 256),然后使用 TimeDistributed(Flatten()) 把后兩個(gè)維度壓成一維,也就是 (16, 512),之后經(jīng)過 2 層雙向的 GRU 對(duì)序列橫向建模,最后經(jīng)過 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,沒有意義,只是為了適配 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ì)通過這個(gè)函數(shù)來評(píng)估我們的模型,和上面的評(píng)估標(biāo)準(zhǔn)一樣,只有全部正確,我們才算預(yù)測(cè)正確,中間有個(gè)坑,就是模型最開始訓(xùn)練的時(shí)候,并不一定會(huì)輸出四個(gè)字符,所以我們?nèi)绻龅剿械淖址疾坏剿膫€(gè)的時(shí)候,就不計(jì)算了,相當(dāng)于加0,遇到多于4個(gè)字符的時(shí)候,只取前四個(gè)。

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

          因?yàn)?Keras 沒有針對(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 一開始下降很快,后面就很平了,但是我們把在對(duì)數(shù)尺度下繪制 loss 圖的話,還是能看到 loss 一直在下降的。acc 上升得也很快,雖然前期訓(xùn)練的時(shí)候 acc 很抖動(dòng),但是后期學(xué)習(xí)率降下來以后就不會(huì)再跌下來了。

          最終模型的準(zhǔn)確率達(dá)到了 99.21%,訓(xùn)練過程中的準(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ī)出來的驗(yàn)證碼很厲害,是O0OP,不過更厲害的是模型認(rèn)出來了。

          有趣的問題

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

          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)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品在线播放 | 国产久 | 一级内射片 | 久草新在线 | 经典无码一区二区三区 |