这周又要过一半了……

0x00.前言

腾讯云开发者实验室(beta),这个其实很早就看见了,还加了QQ群,不过当时以为只是第一次免费,所以一直没做,昨天发现可以无限次做……

0x01.引用

1.0 简介

验证码主要用于防刷,传统的验证码识别算法一般需要把验证码分割为单个字符,然后逐个识别,如果字符之间相互重叠,传统的算法就然并卵了,本文采用cnn对验证码进行整体识别。通过本文的学习,大家可以学到几点:1.captcha库生成验证码;2.如何将验证码识别问题转化为分类问题;3.可以训练自己的验证码识别模型。

1.1 安装captcha

sudo pip install captcha

1
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-105-generic x86_64)
2
3
 * Documentation:  https://help.ubuntu.com/
4
5
  System information as of Wed Aug 16 16:59:16 CST 2017
6
7
  System load:  0.71               Processes:           69
8
  Usage of /:   11.6% of 49.09GB   Users logged in:     0
9
  Memory usage: 5%                 IP address for eth0: 10.135.123.64
10
  Swap usage:   0%
11
12
  Graph this data and manage this system at:
13
    https://landscape.canonical.com/
14
15
ubuntu@VM-123-64-ubuntu:~$ sudo pip install captcha
16
Downloading/unpacking captcha
17
  Downloading captcha-0.2.4-py2-none-any.whl (103kB): 103kB downloaded
18
Requirement already satisfied (use --upgrade to upgrade): Pillow in /usr/local/lib/python2.7/dist-packages(from captcha)
19
Requirement already satisfied (use --upgrade to upgrade): olefile in /usr/local/lib/python2.7/dist-packages (from Pillow->captcha)
20
Installing collected packages: captcha
21
Successfully installed captcha
22
Cleaning up...

2.0 生成验证码训练数据

所有的模型训练,数据是王道,本文采用captcha库生成验证码,captcha可以生成语音和图片验证码,我们采用生成图片验证码功能,验证码是由数字、大写字母、小写字母组成(当然你也可以根据自己的需求调整,比如添加一些特殊字符),长度为4,所以总共有62^4种组合验证码。

2.1 验证码生成器

采用python中生成器方式来生成我们的训练数据,这样的好处是,不需要提前生成大量的数据,训练过程中生成数据,并且可以无限生成数据。
现在您可以在/home/ubuntu目录下创建源文件generate_captcha.py,内容可参考:

1
#!/usr/bin/python
2
# -*- coding: utf-8 -*
3
4
from captcha.image import ImageCaptcha
5
from PIL import Image
6
import numpy as np
7
import random
8
import string
9
10
class generateCaptcha():
11
    def __init__(self,
12
                 width = 160,#验证码图片的宽
13
                 height = 60,#验证码图片的高
14
                 char_num = 4,#验证码字符个数
15
                 characters = string.digits + string.ascii_uppercase + string.ascii_lowercase):#验证码组成,数字+大写字母+小写字母
16
        self.width = width
17
        self.height = height
18
        self.char_num = char_num
19
        self.characters = characters
20
        self.classes = len(characters)
21
22
    def gen_captcha(self,batch_size = 50):
23
        X = np.zeros([batch_size,self.height,self.width,1])
24
        img = np.zeros((self.height,self.width),dtype=np.uint8)
25
        Y = np.zeros([batch_size,self.char_num,self.classes])
26
        image = ImageCaptcha(width = self.width,height = self.height)
27
28
        while True:
29
            for i in range(batch_size):
30
                captcha_str = ''.join(random.sample(self.characters,self.char_num))
31
                img = image.generate_image(captcha_str).convert('L')
32
                img = np.array(img.getdata())
33
                X[i] = np.reshape(img,[self.height,self.width,1])/255.0
34
                for j,ch in enumerate(captcha_str):
35
                    Y[i,j,self.characters.find(ch)] = 1
36
            Y = np.reshape(Y,(batch_size,self.char_num*self.classes))
37
            yield X,Y
38
39
    def decode_captcha(self,y):
40
        y = np.reshape(y,(len(y),self.char_num,self.classes))
41
        return ''.join(self.characters[x] for x in np.argmax(y,axis = 2)[0,:])
42
43
    def get_parameter(self):
44
        return self.width,self.height,self.char_num,self.characters,self.classes
45
46
    def gen_test_captcha(self):
47
        image = ImageCaptcha(width = self.width,height = self.height)
48
        captcha_str = ''.join(random.sample(self.characters,self.char_num))
49
        img = image.generate_image(captcha_str)
50
        img.save(captcha_str + '.jpg!webp')

然后执行:
cd /home/ubuntu
python
import generate_captcha
g = generate_captcha.generateCaptcha()
g.gen_test_captcha()
执行结果:
/home/ubuntu目录下查看生成的验证码,jpg格式的图片可以点击查看。

3.0 验证码识别模型

将验证码识别问题转化为分类问题,总共62^4种类型,采用4one-hot编码分别表示4个字符取值。
cnn验证码识别模型
3层隐藏层、2层全连接层,对每层都进行dropoutinput——>conv——>pool——>dropout——>conv——>pool——>dropout——>conv——>pool——>dropout——>fully connected layer——>dropout——>fully connected layer——>output

现在您可以在/home/ubuntu目录下创建源文件captcha_model.py,内容可参考:

1
#!/usr/bin/python
2
# -*- coding: utf-8 -*
3
4
import tensorflow as tf
5
import math
6
7
class captchaModel():
8
    def __init__(self,
9
                 width = 160,
10
                 height = 60,
11
                 char_num = 4,
12
                 classes = 62):
13
        self.width = width
14
        self.height = height
15
        self.char_num = char_num
16
        self.classes = classes
17
18
    def conv2d(self,x, W):
19
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
20
21
    def max_pool_2x2(self,x):
22
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
23
                              strides=[1, 2, 2, 1], padding='SAME')
24
25
    def weight_variable(self,shape):
26
        initial = tf.truncated_normal(shape, stddev=0.1)
27
        return tf.Variable(initial)
28
29
    def bias_variable(self,shape):
30
        initial = tf.constant(0.1, shape=shape)
31
        return tf.Variable(initial)
32
33
    def create_model(self,x_images,keep_prob):
34
        #first layer
35
        w_conv1 = self.weight_variable([5, 5, 1, 32])
36
        b_conv1 = self.bias_variable([32])
37
        h_conv1 = tf.nn.relu(tf.nn.bias_add(self.conv2d(x_images, w_conv1), b_conv1))
38
        h_pool1 = self.max_pool_2x2(h_conv1)
39
        h_dropout1 = tf.nn.dropout(h_pool1,keep_prob)
40
        conv_width = math.ceil(self.width/2)
41
        conv_height = math.ceil(self.height/2)
42
43
        #second layer
44
        w_conv2 = self.weight_variable([5, 5, 32, 64])
45
        b_conv2 = self.bias_variable([64])
46
        h_conv2 = tf.nn.relu(tf.nn.bias_add(self.conv2d(h_dropout1, w_conv2), b_conv2))
47
        h_pool2 = self.max_pool_2x2(h_conv2)
48
        h_dropout2 = tf.nn.dropout(h_pool2,keep_prob)
49
        conv_width = math.ceil(conv_width/2)
50
        conv_height = math.ceil(conv_height/2)
51
52
        #third layer
53
        w_conv3 = self.weight_variable([5, 5, 64, 64])
54
        b_conv3 = self.bias_variable([64])
55
        h_conv3 = tf.nn.relu(tf.nn.bias_add(self.conv2d(h_dropout2, w_conv3), b_conv3))
56
        h_pool3 = self.max_pool_2x2(h_conv3)
57
        h_dropout3 = tf.nn.dropout(h_pool3,keep_prob)
58
        conv_width = math.ceil(conv_width/2)
59
        conv_height = math.ceil(conv_height/2)
60
61
        #first fully layer
62
        conv_width = int(conv_width)
63
        conv_height = int(conv_height)
64
        w_fc1 = self.weight_variable([64*conv_width*conv_height,1024])
65
        b_fc1 = self.bias_variable([1024])
66
        h_dropout3_flat = tf.reshape(h_dropout3,[-1,64*conv_width*conv_height])
67
        h_fc1 = tf.nn.relu(tf.nn.bias_add(tf.matmul(h_dropout3_flat, w_fc1), b_fc1))
68
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
69
70
        #second fully layer
71
        w_fc2 = self.weight_variable([1024,self.char_num*self.classes])
72
        b_fc2 = self.bias_variable([self.char_num*self.classes])
73
        y_conv = tf.add(tf.matmul(h_fc1_drop, w_fc2), b_fc2)
74
75
        return y_conv

3.1 训练cnn验证码识别模型

每批次采用64个训练样本,每100次循环采用100个测试样本检查识别准确度,当准确度大于99%时,训练结束,采用GPU需要5-6个小时左右,CPU大概需要20个小时左右。
注:作为实验,你可以通过调整train_captcha.py文件中if acc > 0.99:代码行的准确度节省训练时间(比如将0.990.01);同时,我们已经通过长时间的训练得到了一个训练集,可以通过如下命令将训练集下载到本地。
wget http://tensorflow-1253902462.cosgz.myqcloud.com/captcha/capcha_model.zip
unzip capcha_model.zip
现在您可以在/home/ubuntu目录下创建源文件train_captcha.py,内容可参考:

1
#!/usr/bin/python
2
3
import tensorflow as tf
4
import numpy as np
5
import string
6
import generate_captcha
7
import captcha_model
8
9
if __name__ == '__main__':
10
    captcha = generate_captcha.generateCaptcha()
11
    width,height,char_num,characters,classes = captcha.get_parameter()
12
13
    x = tf.placeholder(tf.float32, [None, height,width,1])
14
    y_ = tf.placeholder(tf.float32, [None, char_num*classes])
15
    keep_prob = tf.placeholder(tf.float32)
16
17
    model = captcha_model.captchaModel(width,height,char_num,classes)
18
    y_conv = model.create_model(x,keep_prob)
19
    cross_entropy = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y_,logits=y_conv))
20
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
21
22
    predict = tf.reshape(y_conv, [-1,char_num, classes])
23
    real = tf.reshape(y_,[-1,char_num, classes])
24
    correct_prediction = tf.equal(tf.argmax(predict,2), tf.argmax(real,2))
25
    correct_prediction = tf.cast(correct_prediction, tf.float32)
26
    accuracy = tf.reduce_mean(correct_prediction)
27
28
    saver = tf.train.Saver()
29
    with tf.Session() as sess:
30
        sess.run(tf.global_variables_initializer())
31
        step = 0
32
        while True:
33
            batch_x,batch_y = next(captcha.gen_captcha(64))
34
            _,loss = sess.run([train_step,cross_entropy],feed_dict={x: batch_x, y_: batch_y, keep_prob: 0.75})
35
            print ('step:%d,loss:%f' % (step,loss))
36
            if step % 100 == 0:
37
                batch_x_test,batch_y_test = next(captcha.gen_captcha(100))
38
                acc = sess.run(accuracy, feed_dict={x: batch_x_test, y_: batch_y_test, keep_prob: 1.})
39
                print ('###############################################step:%d,accuracy:%f' % (step,acc))
40
                #if acc > 0.99:
41
                if acc > 0.01:
42
                    saver.save(sess,"capcha_model.ckpt")
43
                    break
44
            step += 1
1
ubuntu@VM-123-64-ubuntu:~$ cd /home/ubuntu;
2
ubuntu@VM-123-64-ubuntu:~$ python
3
Python 2.7.6 (default, Oct 26 2016, 20:30:19)
4
[GCC 4.8.4] on linux2
5
Type "help", "copyright", "credits" or "license" for more information.
6
>>> exit()
7
ubuntu@VM-123-64-ubuntu:~$ wget http://tensorflow-1253902462.cosgz.myqcloud.com/captcha/capcha_model.zip
8
--2017-08-17 08:34:17--  http://tensorflow-1253902462.cosgz.myqcloud.com/captcha/capcha_model.zip
9
Resolving tensorflow-1253902462.cosgz.myqcloud.com (tensorflow-1253902462.cosgz.myqcloud.com)... 10.59.222.139, 10.59.222.140, 10.59.224.23, ...
10
Connecting to tensorflow-1253902462.cosgz.myqcloud.com (tensorflow-1253902462.cosgz.myqcloud.com)|10.59.222.139|:80... connected.
11
HTTP request sent, awaiting response... 200 OK
12
Length: 119460119 (114M) [application/zip]
13
Saving to: ‘capcha_model.zip’
14
15
100%[=================================================================>] 119,460,119 15.9MB/s   in 7.5s
16
17
2017-08-17 08:34:25 (15.2 MB/s) - ‘capcha_model.zip’ saved [119460119/119460119]
18
19
ubuntu@VM-123-64-ubuntu:~$ unzip capcha_model.zip
20
Archive:  capcha_model.zip
21
  inflating: capcha_model.ckpt.data-00000-of-00001
22
  inflating: capcha_model.ckpt.index
23
  inflating: capcha_model.ckpt.meta
24
```  
25
```python
26
step:0,loss:50.325314
27
###############################################step:0,accuracy:0.010000
28
step:1,loss:43.411045
29
step:2,loss:35.461922
30
step:3,loss:30.740393
31
step:4,loss:25.651911
32
step:5,loss:21.852701
33
step:6,loss:18.238785
34
step:7,loss:15.623116
35
step:8,loss:12.930873
36
step:9,loss:11.302278
37
step:10,loss:9.714245
38
step:11,loss:8.534842
39
step:12,loss:7.268080
40
step:13,loss:6.742800
41
step:14,loss:6.121271
42
step:15,loss:5.694220
43
step:16,loss:4.978926
44
step:17,loss:4.716562
45
step:18,loss:4.378264
46
step:19,loss:3.976461
47
step:20,loss:3.853441
48
step:21,loss:3.679165
49
step:22,loss:3.387414
50
step:23,loss:3.391394
51
step:24,loss:2.966349
52
step:25,loss:2.982628
53
step:26,loss:2.803583
54
step:27,loss:2.705063
55
step:28,loss:2.569064
56
step:29,loss:2.581648
57
step:30,loss:2.514476
58
step:31,loss:2.508118
59
step:32,loss:2.318839
60
step:33,loss:2.272651
61
step:34,loss:2.220107
62
step:35,loss:2.345184
63
step:36,loss:2.195375
64
step:37,loss:2.048596
65
step:38,loss:2.115082
66
step:39,loss:2.134182
67
step:40,loss:2.090564
68
step:41,loss:1.996044
69
step:42,loss:2.004114
70
step:43,loss:2.000343
71
step:44,loss:1.876917
72
step:45,loss:1.821006
73
step:46,loss:1.757054
74
step:47,loss:1.891048
75
step:48,loss:1.764870
76
step:49,loss:1.677284
77
step:50,loss:1.698854
78
step:51,loss:1.590738
79
step:52,loss:1.790131
80
step:53,loss:1.710812
81
step:54,loss:1.691247
82
step:55,loss:1.629667
83
step:56,loss:1.571551
84
step:57,loss:1.575357
85
step:58,loss:1.659061
86
step:59,loss:1.614290
87
step:60,loss:1.623624
88
step:61,loss:1.569570
89
step:62,loss:1.545897
90
step:63,loss:1.516088
91
step:64,loss:1.552601
92
step:65,loss:1.527424
93
step:66,loss:1.424360
94
step:67,loss:1.462316
95
step:68,loss:1.476518
96
step:69,loss:1.412114
97
step:70,loss:1.336995
98
step:71,loss:1.363961
99
step:72,loss:1.385963
100
step:73,loss:1.360555
101
step:74,loss:1.388468
102
step:75,loss:1.294948
103
step:76,loss:1.326732
104
step:77,loss:1.371871
105
step:78,loss:1.267641
106
step:79,loss:1.345698
107
step:80,loss:1.380897
108
step:81,loss:1.343030
109
step:82,loss:1.265661
110
step:83,loss:1.198451
111
step:84,loss:1.266352
112
step:85,loss:1.207551
113
step:86,loss:1.188452
114
step:87,loss:1.181654
115
step:88,loss:1.201548
116
step:89,loss:1.156219
117
step:90,loss:1.178628
118
step:91,loss:1.108201
119
step:92,loss:1.212235
120
step:93,loss:1.138633
121
step:94,loss:1.169782
122
step:95,loss:1.144565
123
step:96,loss:1.080015
124
step:97,loss:1.140321
125
step:98,loss:1.095102
126
step:99,loss:1.101533
127
step:100,loss:1.099733
128
###############################################step:100,accuracy:0.012500

3.2 测试cnn验证码识别模型

1
#!/usr/bin/python
2
3
from PIL import Image, ImageFilter
4
import tensorflow as tf
5
import numpy as np
6
import string
7
import sys
8
import generate_captcha
9
import captcha_model
10
11
if __name__ == '__main__':
12
    captcha = generate_captcha.generateCaptcha()
13
    width,height,char_num,characters,classes = captcha.get_parameter()
14
15
    gray_image = Image.open(sys.argv[1]).convert('L')
16
    img = np.array(gray_image.getdata())
17
    test_x = np.reshape(img,[height,width,1])/255.0
18
    x = tf.placeholder(tf.float32, [None, height,width,1])
19
    keep_prob = tf.placeholder(tf.float32)
20
21
    model = captcha_model.captchaModel(width,height,char_num,classes)
22
    y_conv = model.create_model(x,keep_prob)
23
    predict = tf.argmax(tf.reshape(y_conv, [-1,char_num, classes]),2)
24
    init_op = tf.global_variables_initializer()
25
    saver = tf.train.Saver()
26
    gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.95)
27
    with tf.Session(config=tf.ConfigProto(log_device_placement=False,gpu_options=gpu_options)) as sess:
28
        sess.run(init_op)
29
        saver.restore(sess, "capcha_model.ckpt")
30
        pre_list =  sess.run(predict,feed_dict={x: [test_x], keep_prob: 1})
31
        for i in pre_list:
32
            s = ''
33
            for j in i:
34
                s += characters[j]
35
            print s

现在您可以在/home/ubuntu目录下创建源文件predict_captcha.py,内容可参考:
predict_captcha.py
然后执行:
cd /home/ubuntu
python predict_captcha.py Kz2J.jpg!webp
执行结果:
Kz2J
注:因为实验时间的限制,你可能调整了准确度导致执行结果不符合预期,属于正常情况。
在训练时间足够长的情况下,你可以采用验证码生成器生成测试数据,cnn训练出来的验证码识别模型还是很强大的,大小写的z都可以区分,甚至有时候人都无法区分,该模型也可以正确的识别。
python predict_captcha.py Ljni.jpg!webp

1
u4l7

这就很尴尬了

0x02.后记

还剩点时间,那就继续跑吧……

1
step:2300,loss:0.091317
2
###############################################step:2300,accuracy:0.007500
3
step:2301,loss:0.090167
4
5
ubuntu@VM-123-64-ubuntu:~$ python predict_captcha.py U2Kw.jpg!webp
6
uPl7
7
ubuntu@VM-123-64-ubuntu:~$ python predict_captcha.py K1jZ.jpg!webp
8
59x7

还是很尴尬,一张也没有成功……
跟其他类似的实验室不同,代码可以直接复制,所以我全是复制的,然而到最后只是大体上走了个流程,代码注释较少
未完待续……