以前用tensorflow1.13作了一个验证码识别的小东西准确率仍是至关高的(固然其中大部分逻辑都是从网上不少大神的博客中借鉴之后再本身试验的)python
前不久tensorflow2.0的alpha版发布之后就一直想着用2.0的keras方式重写一遍,由于看了deeplearning.ai中的几个视频中都是以keras方式来实现的,感受比原生的tensorflow方式创建模型的方法要简单清晰不少,并且训练结果的保存和从新加载也是简化了不少。git
====================================================github
验证码生成及预处理数组
这里保留了以前验证码生成的方式,仍然使用captcha来生成验证码网络
验证码的内容是10个数字0~9,小写英文字母和大写英文字母,因此总的字符量为62种app
number = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] ALPHABET = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] CHAR_SET = number + alphabet + ALPHABET
能够看到生成的验证码都是带干扰线干扰点,而且各类颜色和变形处理都是有的dom
首先将图片转成numpy数组ide
captcha_image = Image.open(captcha) captcha_image = np.array(captcha_image)
根据分析,这类验证码的颜色对咱们的识别没有影响,因此将图像进行预处理——灰度化(根据图片的不一样能够作灰度化或者二值化等预处理操做)函数
这里使用的是求均值的方法(正规的方法应该是RGB三个通道上按照必定的比例取值)学习
gray = np.mean(img, -1)
====================================================
创建卷积神经网络模型
1.输入的图片为160 * 60的,灰度化预处理之后为一维数组,每张图片总共有9600个输入值
IMAGE_HEIGHT = 60 IMAGE_WIDTH = 160
2.输出的字符集有62个字符,而且每张图片有4位字符,总共有4 * 62 = 248个输出值(下面的batch_size为每批训练的图片数量)
batch_y = np.zeros([batch_size, MAX_CAPTCHA, CHAR_SET_LEN])
3.输入层有9600个值,输出层有248个值,若是使用全链接层做为隐藏层则会须要天量的计算
因此须要先使用卷积核池化操做尽量的减小计算量(若是有一些深度学习基础的同窗应该知道计算机视觉中通常都是用卷积升级网络来解决这类问题)
图片像素不高,因此使用的卷积核和池大小不能太大,优先考虑3 * 3 和5 * 5 的卷积核,池大小使用2 * 2
按照下面的神经网络模型,卷积池化之后的输出应该是128 * 17 * 5 = 10880(若是最后一层的深度仍然使用64的话,大小会减为一半)
model.add(tf.keras.layers.Conv2D(32, (3, 3))) model.add(tf.keras.layers.PReLU()) model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2)) model.add(tf.keras.layers.Conv2D(64, (5, 5))) model.add(tf.keras.layers.PReLU()) model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2)) model.add(tf.keras.layers.Conv2D(128, (5, 5))) model.add(tf.keras.layers.PReLU()) model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2))
4.输出的的每一位的字符之间没有关联关系,因此仍然将输出值当作4组,须要将输出值调整为(4, 62)的数组
model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(MAX_CAPTCHA * CHAR_SET_LEN)) model.add(tf.keras.layers.Reshape([MAX_CAPTCHA, CHAR_SET_LEN]))
6.识别的原理是计算每一位字符上某个字符出现的可能性最大,因此每张图片都是一个4位的多分类问题,最终输出使用softmax进行归一化
model.add(tf.keras.layers.Softmax())
7.在tensorflow2.0中softmax对应的损失函数是categorical_crossentropy,按照这个来配置模型
model.compile(optimizer='Adam', metrics=['accuracy'], loss='categorical_crossentropy')
8.最后将可能性最大的那个下标取出,做为字符集的下标,获取实际对应的字符(固然咱们在训练的时候没有必要转化为字符,直接下标比较一下是否正确就能够了)
prediction_value = vec2text(np.argmax(prediction_value, axis=2)[0])
====================================================
训练模型
使用一个循环逻辑开启训练,每批训练512张图片,每批训练4次
for times in range(500000): batch_x, batch_y = get_next_batch(512) model.fit(batch_x, batch_y, epochs=4) print("y预测=\n", np.argmax(model.predict(batch_x), axis=2)) print("y实际=\n", np.argmax(batch_y, axis=2))
刚开始的时候可能损失值会在4左右徘徊,多跑几个批次之后损失值会明显降低,精确度accuracy也会直线上升
下面能够看到每批的第一次训练结果中精确度只有不到60%,而在第四次训练结果中精确度基本上都能达到99%以上
可是这个精确度达到99%了也不能说明整个模型就训练结束了,这个精确度只是针对这一批512张图片来讲的
毕竟4位验证码有62 * 62 * 62 * 62种可能,训练集并不能表明全部的可能性,因此咱们须要使用新生成的验证码来证实整个训练结果是否能结束
Epoch 1/4 32/512 [>.............................] - ETA: 5s - loss: 2.4209 - accuracy: 0.5703 64/512 [==>...........................] - ETA: 5s - loss: 2.2339 - accuracy: 0.5703 96/512 [====>.........................] - ETA: 5s - loss: 2.1561 - accuracy: 0.5911 128/512 [======>.......................] - ETA: 4s - loss: 2.0170 - accuracy: 0.6016 160/512 [========>.....................] - ETA: 4s - loss: 1.9622 - accuracy: 0.6031 192/512 [==========>...................] - ETA: 3s - loss: 1.9425 - accuracy: 0.6029 224/512 [============>.................] - ETA: 3s - loss: 1.9192 - accuracy: 0.6038 256/512 [==============>...............] - ETA: 3s - loss: 1.8921 - accuracy: 0.6113 288/512 [===============>..............] - ETA: 2s - loss: 1.8746 - accuracy: 0.6094 320/512 [=================>............] - ETA: 2s - loss: 1.8479 - accuracy: 0.6031 352/512 [===================>..........] - ETA: 1s - loss: 1.8367 - accuracy: 0.5987 384/512 [=====================>........] - ETA: 1s - loss: 1.8379 - accuracy: 0.5931 416/512 [=======================>......] - ETA: 1s - loss: 1.8287 - accuracy: 0.5913 448/512 [=========================>....] - ETA: 0s - loss: 1.8086 - accuracy: 0.5887 480/512 [===========================>..] - ETA: 0s - loss: 1.7682 - accuracy: 0.5917 512/512 [==============================] - 6s 12ms/sample - loss: 1.7781 - accuracy: 0.5864 ...... Epoch 4/4 32/512 [>.............................] - ETA: 5s - loss: 0.0034 - accuracy: 1.0000 64/512 [==>...........................] - ETA: 5s - loss: 0.0066 - accuracy: 1.0000 96/512 [====>.........................] - ETA: 4s - loss: 0.0094 - accuracy: 1.0000 128/512 [======>.......................] - ETA: 4s - loss: 0.0089 - accuracy: 1.0000 160/512 [========>.....................] - ETA: 4s - loss: 0.0097 - accuracy: 0.9984 192/512 [==========>...................] - ETA: 3s - loss: 0.0100 - accuracy: 0.9987 224/512 [============>.................] - ETA: 3s - loss: 0.0095 - accuracy: 0.9989 256/512 [==============>...............] - ETA: 3s - loss: 0.0088 - accuracy: 0.9990 288/512 [===============>..............] - ETA: 2s - loss: 0.0084 - accuracy: 0.9991 320/512 [=================>............] - ETA: 2s - loss: 0.0083 - accuracy: 0.9992 352/512 [===================>..........] - ETA: 1s - loss: 0.0081 - accuracy: 0.9993 384/512 [=====================>........] - ETA: 1s - loss: 0.0080 - accuracy: 0.9993 416/512 [=======================>......] - ETA: 1s - loss: 0.0080 - accuracy: 0.9994 448/512 [=========================>....] - ETA: 0s - loss: 0.0077 - accuracy: 0.9994 480/512 [===========================>..] - ETA: 0s - loss: 0.0075 - accuracy: 0.9995 512/512 [==============================] - 6s 12ms/sample - loss: 0.0074 - accuracy: 0.9995
在训练150多批之后,试着进行识别,成功率大概在15%~20%,提升训练批次之后整个模型的识别率应该会很高
y预测= XAZj y实际= iAzj 预测失败。 y预测= EbqY y实际= EbqY 预测成功。 y预测= WjMl y实际= WjMl 预测成功。 y预测= Jppw y实际= Jlpw 预测失败。 y预测= RFQq y实际= RFQq 预测成功。 ...... y预测= SRC2 y实际= SaKZ 预测失败。 y预测= Kfza y实际= KpZa 预测失败。 y预测= yrct y实际= yrtt 预测失败。 y预测= LpKb y实际= Lpwb 预测失败。 y预测= iWWl y实际= iWqL 预测失败。 预测 100 次 成功率 = 0.16
====================================================
完整代码以下,在python3.6.八、tensorflow2.0.0-alpha0 环境下成功运行
https://github.com/yukiti2007/sample/blob/master/python/tensorflow/keras_cnn.py
# coding:utf-8 from captcha.image import ImageCaptcha import random from PIL import Image import numpy as np import tensorflow as tf number = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] ALPHABET = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] SAVE_PATH = "D:/test/tf2/keras_cnn/" CHAR_SET = number + alphabet + ALPHABET CHAR_SET_LEN = len(CHAR_SET) IMAGE_HEIGHT = 60 IMAGE_WIDTH = 160 def random_captcha_text(char_set=None, captcha_size=4): if char_set is None: char_set = number + alphabet + ALPHABET captcha_text = [] for i in range(captcha_size): c = random.choice(char_set) captcha_text.append(c) return captcha_text def gen_captcha_text_and_image(width=160, height=60, char_set=CHAR_SET): image = ImageCaptcha(width=width, height=height) captcha_text = random_captcha_text(char_set) captcha_text = ''.join(captcha_text) captcha = image.generate(captcha_text) captcha_image = Image.open(captcha) captcha_image = np.array(captcha_image) return captcha_text, captcha_image text, image = gen_captcha_text_and_image(char_set=CHAR_SET) MAX_CAPTCHA = len(text) print('CHAR_SET_LEN=', CHAR_SET_LEN, ' MAX_CAPTCHA=', MAX_CAPTCHA) def convert2gray(img): if len(img.shape) > 2: gray = np.mean(img, -1) return gray else: return img def text2vec(text): vector = np.zeros([MAX_CAPTCHA, CHAR_SET_LEN]) for i, c in enumerate(text): idx = CHAR_SET.index(c) vector[i][idx] = 1.0 return vector def vec2text(vec): text = [] for i, c in enumerate(vec): text.append(CHAR_SET[c]) return "".join(text) def get_next_batch(batch_size=128): batch_x = np.zeros([batch_size, IMAGE_HEIGHT, IMAGE_WIDTH, 1]) batch_y = np.zeros([batch_size, MAX_CAPTCHA, CHAR_SET_LEN]) def wrap_gen_captcha_text_and_image(): while True: text, image = gen_captcha_text_and_image(char_set=CHAR_SET) if image.shape == (60, 160, 3): return text, image for i in range(batch_size): text, image = wrap_gen_captcha_text_and_image() image = tf.reshape(convert2gray(image), (IMAGE_HEIGHT, IMAGE_WIDTH, 1)) batch_x[i, :] = image batch_y[i, :] = text2vec(text) return batch_x, batch_y def crack_captcha_cnn(): model = tf.keras.Sequential() model.add(tf.keras.layers.Conv2D(32, (3, 3))) model.add(tf.keras.layers.PReLU()) model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2)) model.add(tf.keras.layers.Conv2D(64, (5, 5))) model.add(tf.keras.layers.PReLU()) model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2)) model.add(tf.keras.layers.Conv2D(128, (5, 5))) model.add(tf.keras.layers.PReLU()) model.add(tf.keras.layers.MaxPool2D((2, 2), strides=2)) model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(MAX_CAPTCHA * CHAR_SET_LEN)) model.add(tf.keras.layers.Reshape([MAX_CAPTCHA, CHAR_SET_LEN])) model.add(tf.keras.layers.Softmax()) return model def train(): try: model = tf.keras.models.load_model(SAVE_PATH + 'model') except Exception as e: print('#######Exception', e) model = crack_captcha_cnn() model.compile(optimizer='Adam', metrics=['accuracy'], loss='categorical_crossentropy') for times in range(500000): batch_x, batch_y = get_next_batch(512) print('times=', times, ' batch_x.shape=', batch_x.shape, ' batch_y.shape=', batch_y.shape) model.fit(batch_x, batch_y, epochs=4) print("y预测=\n", np.argmax(model.predict(batch_x), axis=2)) print("y实际=\n", np.argmax(batch_y, axis=2)) if 0 == times % 10: print("save model at times=", times) model.save(SAVE_PATH + 'model') def predict(): model = tf.keras.models.load_model(SAVE_PATH + 'model') success = 0 count = 100 for _ in range(count): data_x, data_y = get_next_batch(1) prediction_value = model.predict(data_x) data_y = vec2text(np.argmax(data_y, axis=2)[0]) prediction_value = vec2text(np.argmax(prediction_value, axis=2)[0]) if data_y.upper() == prediction_value.upper(): print("y预测=", prediction_value, "y实际=", data_y, "预测成功。") success += 1 else: print("y预测=", prediction_value, "y实际=", data_y, "预测失败。") print("预测", count, "次", "成功率=", success / count) pass if __name__ == "__main__": train() predict()