Giter Club home page Giter Club logo

captcha_break's Introduction

使用深度学习来破解 captcha 验证码

本项目会通过 Keras 搭建一个深度卷积神经网络来识别 captcha 验证码,建议使用显卡来运行该项目。

下面的可视化代码都是在 jupyter notebook 中完成的,如果你希望写成 python 脚本,稍加修改即可正常运行,当然也可以去掉这些可视化代码。

2019 年更新了:

  • 适配了新版 API
  • 提高了数据生成器的效率
  • 使用了 CuDNNGRU 提高了训练和预测效率
  • 更新了文档

环境

本项目使用的环境如下:

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

下面几个包是用于可视化的:

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

captcha

captcha 是用 python 写的生成验证码的库,它支持图片验证码和语音验证码,我们使用的是它生成图片验证码的功能。

首先我们设置我们的验证码格式为数字加大写字母,生成一串验证码试试看:

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 默认占用所有显存,这样不利于我们同时进行多项实验,因此我们可以使用下面的代码当 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)

数据生成器

训练模型的时候,我们可以选择两种方式来生成我们的训练数据,一种是一次性生成几万张图,然后开始训练,一种是定义一个数据生成器,然后利用 fit_generator 函数来训练。

第一种方式的好处是训练的时候显卡利用率高,如果你需要经常调参,可以一次生成,多次使用;第二种方式的好处是你不需要生成大量数据,训练过程中可以利用 CPU 生成数据,而且还有一个好处是你可以无限生成数据。

我们的数据格式如下:

X

X 的形状是 (batch_size, height, width, 3),比如一批生成 128 个样本,图片宽度为170,高度为80,那么 X 的形状就是 (128, 64, 128, 3),如果你想取第一张图,代码可以这样写 X[0]

y

y 的形状是四个 (batch_size, n_class),如果转换成 numpy 的格式,则是 (n_len, batch_size, n_class),比如一批生成 128 个样本,验证码的字符有 36 种,长度是 4 位,那么它的形状就是 4 个 (128, 36) 的矩阵,也可以说是 (4, 32, 36)

数据生成器

为了让 Keras 能够使用多进程并行生成数据,我们需要使用 Keras 的 Sequence 类实现一个我们自己的数据类。

__init__ 初始化函数里,我们定义数据所需的参数,然后这个数据的长度就是 steps 数。在 __getitem__ 里,我们不用理会索引号,直接随机生成一批样本送去训练即可。

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

使用生成器

生成器的使用方法很简单,只需要用对它取第一个 batch 即可。下面是一个例子,初始化一个数据集,设置 batch_size 和 steps 都为 1,然后取出来第一个数据,对它可视化。

在这里我们对生成的 One-Hot 编码后的标签进行了解码,首先将它转为 numpy 数组,然后取36个字符中最大的数字的位置(axis=2代表字符的轴),实际上神经网络会输出36个字符的概率,我们需要将概率最大的四个字符的编号取出来,转换为字符串。

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))

构建深度卷积神经网络

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)

模型结构很简单,特征提取部分使用的是两个卷积,一个池化的结构,这个结构是学的 VGG16 的结构。我们重复五个 block,然后我们将它 Flatten,连接四个分类器,每个分类器是36个神经元,输出36个字符的概率。

模型可视化

得益于 Keras 自带的可视化,我们可以使用几句代码来可视化模型的结构:

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 这个库,以及 graphviz 这个库,在 macOS 系统上安装方法如下:

brew install graphviz
pip install pydot-ng

我们可以看到最后一层卷积层输出的形状是 (1, 6, 256),已经不能再加卷积层了。

训练模型

训练模型反而是所有步骤里面最简单的一个,直接使用 model.fit_generator 即可,这里的验证集使用了同样的生成器,由于数据是通过生成器随机生成的,所以我们不用考虑数据是否会重复。

为了避免手动调参,我们使用了 Adam 优化器,它的学习率是自动设置的,我们只需要给一个较好的初始学习率即可。

EarlyStopping 是一个 Keras 的 Callback,它可以在 loss 超过多少个 epoch 没有下降以后,就自动终止训练,避免浪费时间。

ModelCheckpoint 是另一个好用的 Callback,它可以保存训练过程中最好的模型。

CSVLogger 可以记录 loss 为 CSV 文件,这样我们就可以在训练完成以后绘制训练过程中的 loss 曲线。

注意,这段代码在笔记本电脑上可能要较长时间,建议使用带有 NVIDIA 显卡的机器运行。注意我们这里使用了一个小技巧,添加 workers=4 参数让 Keras 自动实现多进程生成数据,摆脱 python 单线程效率低的缺点。

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)

载入最好的模型继续训练一会

为了让模型充分训练,我们可以载入之前最好的模型权值,然后降低学习率为原来的十分之一,继续训练,这样可以让模型收敛得更好。

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)

测试模型

当我们训练完成以后,可以识别一个验证码试试看:

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')

计算模型总体准确率

模型在训练的时候只会显示每一个字符的准确率,为了统计模型的总体准确率,我们可以写下面的函数:

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)

这里用到了一个库叫做 tqdm,它是一个进度条的库,为的是能够实时反馈进度。然后我们通过一些 numpy 计算去统计我们的准确率,这里计算规则是只要有一个错,那么就不算它对。经过计算,我们的模型的总体准确率在经过充分训练以后,可以达到 98.26% 的总体准确率。

模型总结

模型的大小是10.7MB,总体准确率是 98.26%,基本上可以确定破解了此类验证码。

改进

对于这种按顺序书写的文字,我们还有一种方法可以使用,那就是循环神经网络来识别序列。下面我们来了解一下如何使用循环神经网络来识别这类验证码。

CTC Loss

这个 loss 是一个特别神奇的 loss,它可以在只知道序列的顺序,不知道具体位置的情况下,让模型收敛。这里有一个非常好的文章介绍了 CTC Loss: Sequence Modeling With CTC

在 Keras 里面已经内置了 CTC Loss ,我们实现下面的代码即可在模型里使用 CTC Loss。

  • y_pred 是模型的输出,是按顺序输出的37个字符的概率,因为我们这里用到了循环神经网络,所以需要一个空白字符的概念;
  • labels 是验证码,是四个数字,每个数字代表字符在字符集里的位置
  • input_length 表示 y_pred 的长度,我们这里是16
  • label_length 表示 labels 的长度,我们这里是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)

模型结构

我们的模型结构是这样设计的,首先通过卷积神经网络去识别特征,然后按水平顺序输入到 GRU 进行序列建模,最后使用一个分类器对每个时刻输出的特征进行分类。

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)

为了训练这个模型,我们还需要搭建一个 loss 计算网络,代码如下:

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)

真正训练出来的模型是 base_model,由于 Keras 的限制,我们没办法直接使用 base_model 搭建 CTCLoss,所以我们只能按照上面的方法,让模型直接输出 loss。

模型可视化

可视化的代码同上,这里只贴图。

可以看到模型比上一个模型复杂了许多,但实际上只是因为输入比较多,所以它显得很大。

首先模型输入一个 (height, width, 3) 维度的图片,然后经过一系列的层降维到了 (2, 16, 256),之后我们使用 Permute 把 width 轴调整到第一个维度以适配 RNN 的输入格式。调整以后的维度是 (16, 2, 256),然后使用 TimeDistributed(Flatten()) 把后两个维度压成一维,也就是 (16, 512),之后经过 2 层双向的 GRU 对序列横向建模,最后经过 Dense 分类器输出水平方向上每个字符的概率分布。

使用 CuDNNGRU 是因为它在 NVIDIA 显卡上可以加速非常多倍,如果你使用的是 CPU,改为 GRU 即可。

使用 RNN 的原因是,如果你看到一句话是 今天我*了一个非常好吃的苹果,有一个字看不清,你很容易猜到这个字是“吃”,但是使用 CNN,你就很难有这么大的感受野,从苹果推测出前面的字是吃。

数据生成器

数据生成器和 CNN 的差不多,这里需要多几个矩阵,一个是 input_length,代表序列长度,一个是 label_length,代表验证码长度,还有一个 np.ones,没有意义,只是为了适配 Keras 训练需要的矩阵输入。

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)

评估模型

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

我们会通过这个函数来评估我们的模型,和上面的评估标准一样,只有全部正确,我们才算预测正确,中间有个坑,就是模型最开始训练的时候,并不一定会输出四个字符,所以我们如果遇到所有的字符都不到四个的时候,就不计算了,相当于加0,遇到多于4个字符的时候,只取前四个。

评估回调

因为 Keras 没有针对这种输出计算准确率的选项,因此我们需要自定义一个回调函数,它会在每一代训练完成的时候计算模型的准确率。

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}')

训练模型

我们还是按照之前的训练策略,先训练 100 代,等 loss 不降低以后,降低学习率,再训练 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 一开始下降很快,后面就很平了,但是我们把在对数尺度下绘制 loss 图的话,还是能看到 loss 一直在下降的。acc 上升得也很快,虽然前期训练的时候 acc 很抖动,但是后期学习率降下来以后就不会再跌下来了。

最终模型的准确率达到了 99.21%,训练过程中的准确率最高达到了 99.49%。

测试模型

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])))

这里随机出来的验证码很厉害,是O0OP,不过更厉害的是模型认出来了。

有趣的问题

我又用之前的模型做了个测试,对于 O0O0 这样丧心病狂的验证码,模型偶尔也能正确识别,这让我非常惊讶,它是真的能识别 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')

总结

模型的大小是12.8MB,准确率达到了惊人的 99.21%,即使连 0 和 O 都能精准区分,非常成功。

扩展

如果你比较喜欢 PyTorch,可以看 ctc_pytorch.ipynb,精度更高,达到了 99.57%。

如果你想查看更多经验,可以看看我在百度云魅族深度学习应用大赛的代码和思路:https://github.com/ypwhs/baiduyun_deeplearning_competition

参考链接

captcha_break's People

Contributors

s0ap00 avatar ypwhs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

captcha_break's Issues

在ctc_pytorch的训练过程中,损失值总会变为nan

相关信息

pytorch版本使用的是1.1.0;
系统是Ubuntu16.04;

相关修改

代码基本没有修改,只是改了输入图片的尺寸为96*34后,对应的生成器其中的参数n_input_length = 6 也进行了修改,并未报错。

问题

训练中,loss总是会变为nan,尝试着改batch_size和lr,也只能让其晚出现2个epoch左右,acc至多变为70左右。


求指点啊TAT

python 版本?

我的python是3.7版本的,tensorflow-gpu 安装不了了,最好能把依赖包版本给出来比较好。

用keras2的api改写后,无法收敛

因为我使用的keras版本是keras2, 所以把网络结构定义和训练的代码做了一些修改,但是输出的每个字符的准确率一直在0.028左右徘徊,这个就是完全随机1/36的概率,烦请看一下代码是不是有问题
我使用的python版本是2.7.13,keras版本是2.0.2,都是使用anaconda安装的
我已经尝试过修改参数初始化方式(使用Xavier方法),修改优化方法(sgd,RMSprop等),调节学习率(0.1-10),修改batch_size等都没有效果。
我修改后的代码如下:

from keras.models import *
from keras.layers import *
from keras.optimizers import *

input_tensor = Input(shape = (height, width, 3))
x = input_tensor

for i in range(4):
    x = Conv2D(32 * 2 ** i, (3, 3), activation = 'relu')(x)
    x = Conv2D(32 * 2 ** i, (3, 3), activation = 'relu')(x)
    x = MaxPooling2D((2, 2))(x)

x = Flatten()(x)
x = Dropout(rate = 0.25)(x)
x = [Dense(n_class, activation='softmax', name='c{}'.format(i))(x) for i in range(n_len)]

model = Model(inputs = input_tensor, outputs = x)

model.compile(optimizer = 'adadelta', loss='categorical_crossentropy', metrics=['accuracy']) 
from keras.callbacks import EarlyStopping
#early_stop = EarlyStopping(monitor='val_loss', patience=2)
model.fit_generator(gen(), steps_per_epoch = 1600, epochs = 10, validation_steps = 40, validation_data = gen())

about y_pred[:, 2:, :]

def ctc_lambda_func(args):
y_pred, labels, input_length, label_length = args
y_pred = y_pred[:, 2:, :]
return K.ctc_batch_cost(labels, y_pred, input_length, label_length)

Can any one explain the mean of y_pred[:, 2:, :]?
Does this "2" have any particular mean?
Thanks a lot.

Any way to make captcha_break more generic?

I found it can only recognize captcha generate from the python captcha lib.
It didn't work when I give it different style 4 size char+number .
Since it is hard to mock a captcha with different style every time. I wonder is there any more generic way to do this?

What I can imagine is :

  1. use many different font (simple, but only the specical font would help)
  2. some preprocess, like convert RGB to greyscale first, to beat reversed out image(反白图,字白,底有颜色) .But this would lost some feature too, because some great captchas use one color one char, just have high(maybe not so high) contrast with the surrounding area,
  3. Use some deep learning method , learn char from unlabeled captcha dataset, then label the learned features/pattern to AZ 09 , then use that to build model. I think this way is best, but I have no idea how to start. I have heard deconvolution or some clustering method can generate some pattern , but I am not very familiar with these technique.

Could you give me some tips?

base_model与model的关系

大佬 能讲一下为什么训练的是model而测试的是base_model呢?我看了你的解释但是没看懂

Model loading Issues

Hi @ypwhs,

I tired to load the 'model.h5' weights to the CTC model, by using Keras 2.1 and Python 3.6
The network architecture is as the same as yours.

After evaluation, the accuracy is 0.005.
Any ideas on it?
Thanks.

楼主,请教

我参考你的pytorch代码,改为读取图片,下载了350张验证码,100张做验证集,5张测试,其它的都训练。
Loss 0.001 : 250 epoch
Loss 0.0001 : 30 epoch

Train Loss =0.0021 , Acc = 0.9687 (比较满意)
但:
Valid Loss = 0.7251 , Acc = 0.4862 (50%不到)

pth保存后,又从train 和 valid 里各复制了几张到 Test 中做测试,共13张,对了7张。请问如何改进,能进一步提高 Acc

将pytorch_ctc改为py文件时出现问题

您好!我将从ctc_pytorch改为py文件运行,在我本地电脑运行没有错误,改为远程服务器时出现以下错误:
liurui@eversec-desktop:~/yzmre$ python ctc.py
File "ctc.py", line 64
modules[f'conv{name}'] = nn.Conv2d(in_channels, out_channels, kernel_size, padding=(1, 1) if kernel_size == 3 else 0)
^
SyntaxError: invalid syntax
请问您知道这是什么原因吗

PNG该如何修改参数?

因为要读取PNG, 所以将参数改为
width, height, n_len, n_classes = 120, 32, 4, len(characters)

last_channel = 4

model = Model(n_classes, input_shape=(4, height, width))

报错信息是:
“RuntimeError: Expected input_lengths to have value at most 7, but got value 12 (while checking arguments for ctc_loss_gpu)”

烦请指教,谢谢!

不能多线程运行?

您说:添加 workers=4 参数让 Keras 自动实现多进程生成数据,摆脱 python 单线程效率低的缺点。
然而我加了后反而没法运行,这是为什么?怀疑和版本有关,能提供keras版本吗?

还有个问题,图片不用变成灰度图吗,用彩图训练不太好吧

图片切割

这个不需要对图片中字符进行字符切割吗?captcha 验证码

model variable vs base_model

Hello ypwhs,
On the ctc code you are creating a model called model:
model = Model(input=[input_tensor, labels, input_length, label_length], output=[loss_out])

And that is the one you train, but when you make the prediction you use a the model called base_model.
y_pred = base_model.predict(X_test)

Why is that? Is it correct?
Shouldn't you use model.predict? If yes, what would be a correct way to call it, since is waiting for 4 parameters, what should it be sent on "labels", "input_length" and "label_length".

Thank you.

ctc.ipynb merge([gru_1, gru_1b], mode='sum') 找不到这个函数

WARNING:tensorflow:From /home/qgb/.local/lib/python3.7/site-packages/tensorflow/python/framework/op_def_library.py:263: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
/root/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:8: UserWarning: Update your Conv2D call to the Keras 2 API: Conv2D(32, (3, 3), activation="relu")

/root/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:9: UserWarning: Update your Conv2D call to the Keras 2 API: Conv2D(32, (3, 3), activation="relu")
if name == 'main':
/root/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:17: UserWarning: Update your GRU call to the Keras 2 API: GRU(128, return_sequences=True, name="gru1", kernel_initializer="he_normal")
/root/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:18: UserWarning: Update your GRU call to the Keras 2 API: GRU(128, return_sequences=True, go_backwards=True, name="gru1_b", kernel_initializer="he_normal")

TypeError Traceback (most recent call last)
in
17 gru_1 = GRU(rnn_size, return_sequences=True, init='he_normal', name='gru1')(x)
18 gru_1b = GRU(rnn_size, return_sequences=True, go_backwards=True, init='he_normal', name='gru1_b')(x)
---> 19 gru1_merged = merge([gru_1, gru_1b], mode='sum')
20
21 gru_2 = GRU(rnn_size, return_sequences=True, init='he_normal', name='gru2')(gru1_merged)

TypeError: 'module' object is not callable

预测输出咋有空的

您好,我的标签是四位,训练完了后看效果是,发现还有输出结果只有3位的情况

小白求问,3点疑惑

您好,我看到您了程序,有3点疑惑:
1)由于我们使用的是循环神经网络,所以默认丢掉前面两个输出,因为它们通常无意义,且会影响模型的输出。
请问这是为什么啊?还有,“前面两个输出”指的是什么?

2) “y_pred 是模型的输出,是按顺序输出的37个字符的概率,因为我们这里用到了循环神经网络,所以需要一个空白字符的概念;”, 为什么“用到了循环神经网络,所以需要一个空白字符的概念”?

3)如果是实际中一段的语音,比如 0.2s---1.8s 是“哦”, 3.4s----6.4s是“嗨”, 这样的事件,该如何制作相应的标签?

希望您可以留下邮箱或者QQ微信,希望可以得到您的回复。

你好,cnn模型不加BN层不收敛

你好,我使用你的cnn模型时并不能收敛,loss越来越高。我加了BN解决了问题,但是我不太明白为什么一定需要加BN。
此外,我发现在使用ImageCaptcha生成验证码的时候,当使用单一字体验证码时,模型能够很好的学习,但我使用20种不同字体去生成验证码,训练集能够很好的学习,但是验证集一直的loss一直没有下降,样本数量30000,这种过拟合的情况需要怎么解决呢?谢谢。

准确率回调函数

您好,在运行ctc_2019.ipynb时,一个epoch跑完以后,进入到evaluate()函数后,就一直不在进行第二个epoch,电脑的性能很好,8G的显卡,是不是evaluate()函数的计算时间太长了,这个跟用不用GPU版的加速训练有关系吗?

ctc_2019.ipynb中,训练完一个epoch后,就不再继续了

相关信息

TensorFlow版本使用的是1.12.0;
系统是Ubuntu16.04;

相关修改

代码基本没有修改,只是改了输入图片的尺寸后,对应的生成器其中的参数也进行了修改并未报错。

相关问题

在运行

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)

当运行到第一个epoch的999step的时候就停止了,输入只有:

Epoch 1/100
 999/1000 [============================>.] - ETA: 0s - loss: 4.6288WARNING:tensorflow:From /home/shizai/anaconda3/envs/baiqiao/lib/python3.6/site-packages/tensorflow/python/keras/backend.py:4831: sparse_to_dense (from tensorflow.python.ops.sparse_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Create a `tf.sparse.SparseTensor` and use `tf.sparse.to_dense` instead.

求解答。

InvalidArgumentError (see above for traceback): Saw a non-null label (index >= num_classes - 1) following a null label, batch: 75 num_classes: 36 labels: [[Node: ctc/CTCLoss = CTCLoss[ctc_merge_repeated=true, ignore_longer_outputs_than_inputs=false, preprocess_collapse_repeated=false, _device="/job:localhost/replica:0/task:0/cpu:0"](ctc/Log/_97, ctc/ToInt64/_99, ctc/ToInt32_2/_101, ctc/ToInt32_1/_103)]]

我现在把需要训练的图片存入到samples文件下,比如:AB67HG.jpeg,然后我读入数据
rnn_size = 128

characters = string.digits + string.ascii_uppercase

width,height,n_len,n_class= 140,44,6,len(characters)
folder = "sample"
imageList = os.listdir(folder)
imageList = [os.path.join(folder,item) for item in imageList if os.path.isfile(os.path.join(folder,item))]
image_size = len(imageList)

image_array = np.zeros((image_size,height,width,3),dtype=np.uint8)
image_names = []

for i in range(image_size):
img = Image.open(imageList[i])
image_array [i]=img
image_names.append(imageList[i][-11:-5].upper()

产生数据

def gen(batch_size=128):
X = np.zeros((batch_size, width, height, 3), dtype=np.uint8)
y = np.zeros((batch_size, n_len), dtype=np.uint8)
while True:
for i in range(batch_size):
num = random.randint(0, image_size - 1)
random_str = image_names[num]
X[i] = np.array(image_array[num]).transpose(1, 0, 2)
y[i] = [characters.find(x) for x in random_str]
yield [X, y, np.ones(batch_size) * int(conv_shape[1] - 2), np.ones(batch_size) * n_len], np.ones(batch_size)

显示这个错误,
InvalidArgumentError (see above for traceback): Saw a non-null label (index >= num_classes - 1) following a null label, batch: 75 num_classes: 36 labels:
[[Node: ctc/CTCLoss = CTCLoss[ctc_merge_repeated=true, ignore_longer_outputs_than_inputs=false, preprocess_collapse_repeated=false, _device="/job:localhost/replica:0/task:0/cpu:0"](ctc/Log/_97, ctc/ToInt64/_99, ctc/ToInt32_2/_101, ctc/ToInt32_1/_103)]]
我使用的keras是2.0.3,tensorflow-gpu(1.2.1),用cpu可以运行,但是gpu出问题

您好,关于四层全连接层的问题

您好,请问模型中的四层全连接层是否对应着验证码中的四个字符?或者说全连接层的个数和验证码中字符的个数有无对应关系,因为当我再减小全连接层个数的时候会报错模型期望的数组个数和实际得到的数组个数不一致,但是当将n_class参数也进行相应的修改,比如n_class变为5,全连接层也为5个时,就不会报错。

训练报错

我的环境如下:
win10 Anaconda3 python3.6 tensorflow1.13.1-gpu cudn10.0

tensorflow已经安装好,并且使用import tensorflow as tf测试安装没有问题

运行ctc_2019.ipynb时,前面都通过,运行到训练脚本部分,后台一直报错如下:
Traceback (most recent call last):
File "", line 1, in
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 105, in spawn_main
exitcode = _main(fd)
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
AttributeError: Can't get attribute 'CaptchaSequence' on <module 'main' (built-in)>
Traceback (most recent call last):
File "", line 1, in
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 105, in spawn_main
exitcode = _main(fd)
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
AttributeError: Can't get attribute 'CaptchaSequence' on <module 'main' (built-in)>
Traceback (most recent call last):
File "", line 1, in
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 105, in spawn_main
exitcode = _main(fd)
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
AttributeError: Can't get attribute 'CaptchaSequence' on <module 'main' (built-in)>
Traceback (most recent call last):
File "", line 1, in
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 105, in spawn_main
exitcode = _main(fd)
File "d:\softwares\anaconda3-64\envs\shy\lib\multiprocessing\spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
AttributeError: Can't get attribute 'CaptchaSequence' on <module 'main' (built-in)>
[I 10:42:01.964 NotebookApp] Saving file at /ctc_2019.ipynb

Perform bad with same code.

Hi, I have tried with epoch 5 and 20 serveral times, all failed, the code is same with your example.
But the trained model is very disappointed:

image

I can't understand why, could you give me a tip for this?

My env:
python2.7
Keras (1.2.0)
tensorflow-gpu (0.12.1)

cnn模型构建的问题

在构建cnn模型的时候:

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)

关于keras某些包的更改

你好!
我在使用Python3执行到这一句时报错了:
from keras.utils.visualize_util import plot
提示无法找到模块
搜索之后发现visualize_util已经被更名为了vis_utils,
plot被更名为了plot_model.

如果把模型保存下来,然后重新加载load_model报错

报错是这样的: ValueError: Unknown loss
function:《lambda》

然后我改成这样
model = load_model(os.path.join(KERAS_MODEL_PATH, 'cnn.h5'),
custom_objects={'ctc': lambda y_true, y_pred: y_pred})
还是报错,求解。谢谢

如果我想把训练好的模型保存下来,并做成一个服务,你这个模型改怎么保存啊?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.