wikke / Captcha-Recognizer

A Convolutional Neural Network based Captcha Recognizer

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

验证码识别

4位固定长度验证码,最终结果accuracy: 0.9706, loss: 0.5285 = 0.1078 + 0.1180 + 0.1762 + 0.1265。非固定长度验证码,采用CTC Loss,准确率也达到82%。

固定长度验证码

神经网络模型如下,

核心就是神经网络的设计,这里基于作者的基础做了很多尝试,总结经验如下:

  • 神经网络模型太简单(比如只有2层卷积网络),会有strong bias,最终难以达到一个好的预测结果
  • 模型太复杂(比如10层),收敛会变得非常缓慢。当然我相信数据量充足(这里几乎是无限)的情况下,可以达到更好的效果,不过我等不及了。这是一个trade off
  • 2层卷积+1层Pooling的组合多次感觉效果最佳。我尝试过“2层卷积+1层Pooling”和"3层卷积+1层Pooling"多种情况,都不如之前。
  • strides=1, padding='same'是蛮不错的选择,虽然默认是valid
  • 个人觉得,我们有必要不断做卷积操作提起特征,然后Pooling,直到图形的size缩小到一定经验值以内(比如10),这样才算是最充分“榨取”了图像的特征信息。再多卷积可能得不到更多有价值信息了,因为已经只有10 size,而Conv的kernel_size和Stride结合起来会马上超过。
  • Adadelta, Adagrad, RMSprop是不错的Optimizor选择。Ref:深度学习最全优化方法总结比较(SGD,Adagrad,Adadelta,Adam,Adamax,Nadam)

训练结果

其它

  • captcha.generate_image生成的验证码是(height, width, channel)的格式,不是(width, height, channel)
  • 使用generator而不是fixed size datasets,这里X和y的格式需要注意
X = np.zeros((batch_size, height, width, 3), dtype=np.uint8)
# not like this
# y = np.zeros((batch_size, n_len, n_class))
y = [np.zeros((batch_size, n_class), dtype=np.uint8) for i in range(n_len)]

因为我们最终预测的结果是4个完全独立无关的字母/数字,它们的组合是无意义的,因此预测结果最好是4个,而不是1个'字符串'。神经网络肯定会自动感知到预测的第一个和图像最左边的相关,最后一个和最右边相关。

非固定长度验证码

神经网络模型

3层卷积/卷积/池化模型,再引入2层GRU,正/反叠加(有add,有concatenate),引入Dropout。其中dense_2的输出为(None, 17, 37),可以这样理解:None表示batch num,17相当于图片的宽度被分割为17个做识别,37是character的总个数+1(我理解是额外表示空格)。

CTC Loss

CTC Loss的定义可以Google,它神奇的地方在于可以计算2个非等长序列的Loss(本例中就是图像被分割的17(可变)“块”和序列的非固定长度,从3到4的验证码长度)。从而实现反向传递用于神经网络。不过训练过程收敛很慢,需要耐心。

ctc_batch_cost(y_true, y_pred, input_length, label_length) 
  • y_true: 实际序列,或称labels。本例中是(None, MAX_CAPTCHA_LEN),每一项是一个验证码
  • y_pred: 预测序列,本例中是(None, 17, <channels>),每一项是一个“图像”
  • input_length: 输入,即y_pred的长度,(None, 1),本例中是输入图像的“长度”,为17,即把图像划分成了17“块”。不过实际中要减去2,因为RNN的前2项一般都会废弃。
  • label_length: 预测目标,即y_true/labels的长度,(None, 1),本例中是每一个验证码的长度。

总结一句话,一个batch通过CTC-Loss计算,计算参数是batch中每一项中,有2个序列的内容和各自长度。

Train Objective

这个模型一个有意思的地方是整个模型最终的输出,正好也是其Loss,模型compile如下

ctc_model.compile(loss=lambda y_true, y_pred: y_pred, optimizer='adadelta')

这样便可以非常好地讲模型的生成器(也就是代码中的base_model)和CTC Loss的计算结合在一起,并且模型的训练目标loss也得到很好的表示。

Generator

def ctc_gen(batch_size=32, is_dynamic_len = False)

这个generator可以生成CTC训练需要的输入、输出batch。其中输入是由4部分组成,[generate_images, labels, input_length, label_length],输出是全0(符合期望的数据,期望其CTC Loss为0)

自定义评估Keras Callback

每次epoch完成的时候,运行自定义callback,自定义模型评估。见代码class ModelEvaluator(Callback)

训练结果

其它

  • captcha_generator.generate_image得到的图片plt.imshow()刚好OK,因为它们都是height,width,channel格式的,而我们的理解和模型经常需要转化为width,height,channel的格式,所以显示的时候需要transpose

Ref

灵感与部分代码来自 使用 Keras 来破解 captcha 验证码

About

A Convolutional Neural Network based Captcha Recognizer


Languages

Language:Jupyter Notebook 100.0%