from captcha.image import ImageCaptcha
import matplotlib.pyplot as plt
import numpy as np
import random
import string
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
定义生成验证码函数
path:保存路径
num:生成张数
def img_gen(path, num):
characters = string.digits + string.ascii_uppercase
width , height, n_len, n_class = 170, 80, 4, len(characters)
generator = ImageCaptcha(width=width, height=height)
for i in range(num):
random_str = ''.join([random.choice(characters) for j in range(4)])
img = generator.generate_image(random_str)
img.save("{}.jpg".format(path + random_str))
创建训练数据集
import os
outfile = 'train/'
folder = os.path.join(os.getcwd(), outfile)
isExists = os.path.exists(folder)
if not isExists:
os.mkdir(folder)
print("文件夹已创建")
else:
print("文件夹已存在")
img_gen(folder, 1)
创建验证数据集
outfile = 'test/'
folder = os.path.join(os.getcwd(), outfile)
isExists = os.path.exists(folder)
if not isExists:
os.mkdir(folder)
print("文件夹已创建")
else:
print("文件夹已存在")
img_gen(folder, 1000)
import numpy as np
import os
import string
from keras.preprocessing import image
from keras.models import Model
from keras.layers import Dense, Dropout, Flatten, Input, concatenate, BatchNormalization
from keras.layers.convolutional import Conv2D, Convolution2D, MaxPooling2D
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adadelta
from keras.utils.vis_utils import plot_model
import glob
验证码所包含的字符
characters = string.digits + string.ascii_uppercase
captcha_word = list(characters)
width = 170
height = 80
word_length = 4
word_class = len(characters)
验证码素材目录
train_dir = os.getcwd() + "/train/"
生成字符索引,同时反向操作一次,方面还原
char_indices = dict((c, i) for i, c in enumerate(captcha_word))
indices_char = dict((i, c) for i, c in enumerate(captcha_word))
验证码字符串转数组
def captcha_to_vec(captcha):
vector = np.zeros(word_length * word_class)
for i, ch in enumerate(captcha):
idex = i * word_class + char_indices[ch]
vector[idex] = 1
return vector
把数组转换回文字
def vec_to_captcha(vec):
text = []
vec[vec < 0.5] = 0
char_pos = vec.nonzero()[0]
for i, ch in enumerate(char_pos):
text.append(captcha_word[ch % word_class])
return ''.join(text)
from keras.models import *
from keras.layers import *
input_tensor = Input((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 = MaxPool2D((2, 2))(x)
x = Flatten()(x)
x = Dropout(0.25)(x)
x = [Dense(word_class, activation='softmax', name = "c{}".format(i +1))(x) for i in range(4)]
output = concatenate(x)
model = Model(input = input_tensor, output = output)
model.compile(loss = 'categorical_crossentropy',
optimizer='adadelta',
metrics=['accuracy'])
train_dir = os.path.join(os.getcwd() + '/train')
test_dir = os.path.join(os.getcwd() + '/test')
img_list = []
for item in os.listdir(train_dir):
img_list.append(item)
img_list = img_list[:2000]
np.random.shuffle(img_list)
X = np.zeros((len(img_list), height, width, 3), dtype = np.uint8)
y = np.zeros((len(img_list), word_length, word_class),dtype=np.uint8)
for i,img in enumerate(img_list):
img_path = train_dir + "/" +img
raw_img = image.load_img(img_path, target_size=(height, width))
X[i] = image.img_to_array(raw_img)
raw_word = img.split('.')[0]
for m,j in enumerate(raw_word):
flag = captcha_word.index(j)
y[i][m][flag] = 1
y = y.reshape(2000, 144)
history = model.fit(X, y, epochs=5,batch_size=128)
import numpy as np
import os
import pickle
from keras.utils import np_utils
from keras.preprocessing import image
from keras.models import Model
from keras.layers import Dense, Dropout, Flatten, Input, concatenate
from keras.layers.convolutional import Conv2D, Convolution2D, MaxPooling2D
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adadelta
from matplotlib import pyplot as plt
import string
characters = string.digits + string.ascii_uppercase
captcha_word = characters
height = 170
width = 80
word_len = 4
word_class = len(captcha_word)
train_dir = os.getcwd() + "/train/"
char_indices = dict((c, i) for i,c in enumerate(captcha_word))
indices_char = dict((i, c) for i,c in enumerate(captcha_word))
def captcha_to_vec(captcha):
vector = np.zeros(word_len * word_class)
for i,ch in enumerate(captcha):
idex = i * word_class + char_indices[ch]
vector[idex] = 1
return vector
def vec_to_captcha(vec):
text = []
vec[vec < 0.5] = 0
char_pos = vec.nonzero()[0]
for i, ch in enumerate(char_pos):
text.append(captcha_word[ch % word_class])
return ''.join(text)
img_list = []
for item in os.listdir(train_dir):
img_list.append(item)
np.random.shuffle(img_list)
X = np.zeros((len(img_list), height, width, 3), dtype = np.uint8)
y = np.zeros((len(img_list), word_len * word_class),dtype = np.uint8)
for i,img in enumerate(img_list):
img_path = train_dir + "/" + img
raw_image = image.load_img(img_path, target_size=(height, width))
X[i] = image.img_to_array(raw_image)
y[i] = captcha_to_vec(img.split(".")[0])
创建输入,结构为 高,宽,通道
input_tensor = Input( shape=(height, width, 3))
x = input_tensor
构建卷积网络
两层卷积层,一层池化层,重复3次。因为生成的验证码比较小,padding使用same
x = Convolution2D(32, 3, padding='same', activation='relu')(x)
x = Convolution2D(32, 3, padding='same', activation='relu')(x)
x = MaxPooling2D((2, 2))(x)
x = Convolution2D(64, 3, padding='same', activation='relu')(x)
x = Convolution2D(64, 3, padding='same', activation='relu')(x)
x = MaxPooling2D((2, 2))(x)
x = Convolution2D(128, 3, padding='same', activation='relu')(x)
x = Convolution2D(128, 3, padding='same',activation='relu')(x)
x = MaxPooling2D((2, 2))(x)
Flatten层用来将输入“压平”,即把多维的输入一维化,常用在从卷积层到全连接层的过渡。
x = Flatten()(x)
为输入数据施加Dropout。Dropout将在训练过程中每次更新参数时随机断开一定百分比(rate)的输入神经元,Dropout层用于防止过拟合。
x = Dropout(0.25)(x)
Dense就是常用的全连接层
最后连接5个分类器,每个分类器是46个神经元,分别输出46个字符的概率。
x = [Dense(word_class, activation='softmax', name='c%d'%(i+1))(x) for i in range(word_len)]
output = concatenate(x)
构建模型
model = Model(inputs=input_tensor, outputs=output)
因为训练可能需要数个小时,所以这里加载了之前我训练好的参数。准确率为94%
可以直接使用此参数继续进行训练,也可以自己从头开始训练
model.load_weights('captcha_weights.0.9430.hdf5')
这里优化器选用Adadelta,学习率0.1
opt = Adadelta(lr=0.1)
编译模型以供训练,损失函数使用 categorical_crossentropy,使用accuracy评估模型在训练和测试时的性能的指标
model.compile(loss = 'categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
history = model.fit(X, y, epochs=5, validation_split=0.1)
model.compile(loss = 'categorical_crossentropy', optimizer=opt, metrics=['accuracy'])
每次epoch都保存一下权重,用于继续训练
checkpointer = ModelCheckpoint(filepath="output/weights.{epoch:02d}--{val_loss:.2f}-{val_acc:.4f}.hdf5",
verbose=2, save_weights_only=True)
开始训练,validation_split代表10%的数据不参与训练,用于做验证急
我之前训练了50个epochs以上,这里根据自己的情况进行选择。如果输出的val_acc已经达到你满意的数值,可以终止训练
model.fit(X, y, epochs= 30,callbacks=[checkpointer], validation_split=0.1)