taoCMS-基于php+sqlite最小巧的CMS http://www.taocms.org/ taoCMS是基于php+sqlite/mysql的国内最小(100Kb左右)的功能完善的CMS管理系统 2018-02-22 taoCMS-基于php+sqlite最小巧的CMS 1138 使用腾讯云GPU学习深度学习系列之五:文字的识别与定位 文档:https://keras-cn.readthedocs.io/en/latest/

这是《使用腾讯云GPU学习深度学习》系列文章的第五篇,以车牌识别和简单OCR为例,谈了谈如何进行字母、数字的识别以及定位。本系列文章主要介绍如何使用腾讯云GPU服务器进行深度学习运算,前面主要介绍原理部分,后期则以实践为主。

往期内容:

  1. 使用腾讯云 GPU 学习深度学习系列之一:传统机器学习的回顾
  2. 使用腾讯云 GPU 学习深度学习系列之二:Tensorflow 简明原理
  3. 使用腾讯云 GPU 学习深度学习系列之三:搭建深度神经网络
  4. 使用腾讯云 GPU 学习深度学习系列之四:深度学习的特征工程

上一节,我们简要介绍了一些与深度学习相关的数据预处理方法。其中我们特别提到,使用 基于深度学习的 Spatial Transform 方法,可以让“草书” 字体的手写数字同样也可以被高效识别。

但无论是工整书写的 Tensorflow 官网上的 MNIST 教程,还是上节提到“草书”数字,都是 单一的数字识别问题。 但是,在实际生活中,遇到数字、字母识别问题时,往往需要识别一组数字。这时候一个简单的深度神经网络可能就做不到了。本节内容,就是在讨论遇到这种情况时,应该如何调整深度学习模型。

1. 固定长度

固定长度的字符、数字识别,比较常见的应用场景包括:

  • 识别验证码
  • 识别机动车车牌

识别验证码的方法,这篇文章 有详细介绍。不过该文章使用的是版本较早的 Keras1,实际使用时会有一些问题。如果想尝试,根据Jupyter 的提示更改就好,最终效果也是相当不错:

我们这里要识别的内容,是中华人民共和国机动车车牌。相比上面例子的 4 位验证码,车牌长度更长,达到了 7 位,并且内容也更加丰富,第一位是各省的汉字简称,第二位是 A-Z 的大写字母,3-7位则是数字、字母混合。

由于车牌涉及个人隐私,我们使用了用户 szad670401 在 Github 上开源的一个车牌生成器,随机的生成一些车牌的图片,用于模型训练。当然这个项目同样提供了完整的 MXNet 深度学习框架编写的代码,我们接下来会用 Keras 再写一个。

首先做些准备工作,从 szad670401 的开源项目中获取必要的文件:

### 从 szad670401 github 项目下载车牌生成器以及字体文件
!git clone https://github.com/szad670401/end-to-end-for-chinese-plate-recognition
!cp -r end-to-end-for-chinese-plate-recognition/* ./
!sed 's/for i in range(batchSize):/l_plateStr = []n        l_plateImg = []n        for i in range(batchSize):/g' ./genplate.py  | sed 's/cv2.imwrite(outputPath/l_plateStr.append(plateStr)n                l_plateImg.append(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))n                #cv2.imwrite(outputPath/g' | sed 's/img);/img);n        return l_plateStr,l_plateImg/g'  >genplateRev.py

来看看生成器的效果:

from keras.models import Model
from keras.callbacks import ModelCheckpoint
from keras.layers import Conv2D, MaxPool2D, Flatten, Dropout, Dense, Input
from keras.optimizers import Adam
from keras.backend.tensorflow_backend import set_session
from keras.utils.vis_utils import model_to_dot
import tensorflow as tf
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties

from IPython.display import SVG

from genplate import *

%matplotlib inline

np.random.seed(5)
config = tf.ConfigProto()
config.gpu_options.allow_growth=True
set_session(tf.Session(config=config))

chars = ["京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂",
             "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁", "新", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
             "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
             "Y", "Z"
             ];

M_strIdx = dict(zip(chars, range(len(chars))))

n_generate = 100
rows = 20
cols = int(n_generate/rows)

G = GenPlate("./font/platech.ttf",'./font/platechar.ttf',"./NoPlates")
l_plateStr,l_plateImg = G.genBatch(100,2,range(31,65),"./plate",(272,72))

l_out = []
for i in range(rows):
    l_tmp = []
    for j in range(cols):
        l_tmp.append(l_plateImg[i*cols+j])

    l_out.append(np.hstack(l_tmp))

    fig = plt.figure(figsize=(10, 10))
    ax  = fig.add_subplot(111)
    ax.imshow( np.vstack(l_out), aspect="auto" )

看来 szad670401 开源的车牌生成器,随机生成的车牌确实达到了以假乱真的效果。于是我们基于这个生成器,再自己写一个生成器,用于深度神经网络的数据输入:

def gen(batch_size=32):
    while True:
        l_plateStr,l_plateImg = G.genBatch(batch_size, 2, range(31,65),"./plate",(272,72))
        X = np.array(l_plateImg, dtype=np.uint8)
        ytmp = np.array(list(map(lambda x: [M_strIdx[a] for a in list(x)], l_plateStr)), dtype=np.uint8)
        y = np.zeros([ytmp.shape[1],batch_size,len(chars)])
        for batch in range(batch_size):
            for idx,row_i in enumerate(ytmp[batch]):
                y[idx,batch,row_i] = 1

        yield X, [yy for yy in y]

因为是固定长度,所以我们有个想法,就是既然我们知道识别七次,那就可以用七个模型按照顺序识别。这个思路没有问题,但实际上根据之前卷积神经网络的原理,实际上卷积神经网络在扫描整张图片的过程中,已经对整个图像的内容以及相对位置关系有所了解,所以,七个模型的卷积层实际上是可以共享的。我们实际上可以用一个 一组卷积层+7个全链接层 的架构,来对应输入的车牌图片:

adam = Adam(lr=0.001)

input_tensor = Input((72, 272, 3))
x = input_tensor
for i in range(3):
    x = Conv2D(32*2**i, (3, 3), activation='relu')(x)
    x = Conv2D(32*2**i, (3, 3), activation='relu')(x)
    x = MaxPool2D(pool_size=(2, 2))(x)
x = Flatten()(x)
x = Dropout(0.25)(x)

n_class = len(chars)
x = [Dense(n_class, activation='softmax', name='c%d'%(i+1))(x) for i in range(7)]
model = Model(inputs=input_tensor, outputs=x)
model.compile(loss='categorical_crossentropy',
              optimizer=adam,
              metrics=['accuracy'])

SVG(model_to_dot(model=model, show_layer_names=True, show_shapes=True).create(prog='dot', format='svg'))

训练模型:

best_model = ModelCheckpoint("chepai_best.h5", monitor='val_loss', verbose=0, save_best_only=True)

model.fit_generator(gen(32), steps_per_epoch=2000, epochs=5,
                    validation_data=gen(32), validation_steps=1280,
                    callbacks=[best_model]
)

Epoch 1/5
2000/2000 [==============================] - 547s - loss: 11.1077 - c1_loss: 1.3878 - c2_loss: 0.7512 - c3_loss: 1.1270 - c4_loss: 1.3997 - c5_loss: 1.7955 - c6_loss: 2.3060 - c7_loss: 2.3405 - c1_acc: 0.6157 - c2_acc: 0.7905 - c3_acc: 0.6831 - c4_acc: 0.6041 - c5_acc: 0.5025 - c6_acc: 0.3790 - c7_acc: 0.3678 - val_loss: 3.1323 - val_c1_loss: 0.1970 - val_c2_loss: 0.0246 - val_c3_loss: 0.0747 - val_c4_loss: 0.2076 - val_c5_loss: 0.5099 - val_c6_loss: 1.0774 - val_c7_loss: 1.0411 - val_c1_acc: 0.9436 - val_c2_acc: 0.9951 - val_c3_acc: 0.9807 - val_c4_acc: 0.9395 - val_c5_acc: 0.8535 - val_c6_acc: 0.7065 - val_c7_acc: 0.7190
Epoch 2/5
2000/2000 [==============================] - 546s - loss: 2.7473 - c1_loss: 0.2008 - c2_loss: 0.0301 - c3_loss: 0.0751 - c4_loss: 0.1799 - c5_loss: 0.4407 - c6_loss: 0.9450 - c7_loss: 0.8757 - c1_acc: 0.9416 - c2_acc: 0.9927 - c3_acc: 0.9790 - c4_acc: 0.9467 - c5_acc: 0.8740 - c6_acc: 0.7435 - c7_acc: 0.7577 - val_loss: 1.4777 - val_c1_loss: 0.1039 - val_c2_loss: 0.0118 - val_c3_loss: 0.0300 - val_c4_loss: 0.0665 - val_c5_loss: 0.2145 - val_c6_loss: 0.5421 - val_c7_loss: 0.5090 - val_c1_acc: 0.9725 - val_c2_acc: 0.9978 - val_c3_acc: 0.9937 - val_c4_acc: 0.9824 - val_c5_acc: 0.9393 - val_c6_acc: 0.8524 - val_c7_acc: 0.8609
Epoch 3/5
2000/2000 [==============================] - 544s - loss: 1.7686 - c1_loss: 0.1310 - c2_loss: 0.0156 - c3_loss: 0.0390 - c4_loss: 0.0971 - c5_loss: 0.2689 - c6_loss: 0.6416 - c7_loss: 0.5754 - c1_acc: 0.9598 - c2_acc: 0.9961 - c3_acc: 0.9891 - c4_acc: 0.9715 - c5_acc: 0.9213 - c6_acc: 0.8223 - c7_acc: 0.8411 - val_loss: 1.0954 - val_c1_loss: 0.0577 - val_c2_loss: 0.0088 - val_c3_loss: 0.0229 - val_c4_loss: 0.0530 - val_c5_loss: 0.1557 - val_c6_loss: 0.4247 - val_c7_loss: 0.3726 - val_c1_acc: 0.9849 - val_c2_acc: 0.9987 - val_c3_acc: 0.9948 - val_c4_acc: 0.9861 - val_c5_acc: 0.9569 - val_c6_acc: 0.8829 - val_c7_acc: 0.8994
Epoch 4/5
2000/2000 [==============================] - 544s - loss: 1.4012 - c1_loss: 0.1063 - c2_loss: 0.0120 - c3_loss: 0.0301 - c4_loss: 0.0754 - c5_loss: 0.2031 - c6_loss: 0.5146 - c7_loss: 0.4597 - c1_acc: 0.9677 - c2_acc: 0.9968 - c3_acc: 0.9915 - c4_acc: 0.9773 - c5_acc: 0.9406 - c6_acc: 0.8568 - c7_acc: 0.8731 - val_loss: 0.8221 - val_c1_loss: 0.0466 - val_c2_loss: 0.0061 - val_c3_loss: 0.0122 - val_c4_loss: 0.0317 - val_c5_loss: 0.1085 - val_c6_loss: 0.3181 - val_c7_loss: 0.2989 - val_c1_acc: 0.9870 - val_c2_acc: 0.9986 - val_c3_acc: 0.9969 - val_c4_acc: 0.9910 - val_c5_acc: 0.9696 - val_c6_acc: 0.9117 - val_c7_acc: 0.9182
Epoch 5/5
2000/2000 [==============================] - 553s - loss: 1.1712 - c1_loss: 0.0903 - c2_loss: 0.0116 - c3_loss: 0.0275 - c4_loss: 0.0592 - c5_loss: 0.1726 - c6_loss: 0.4305 - c7_loss: 0.3796 - c1_acc: 0.9726 - c2_acc: 0.9971 - c3_acc: 0.9925 - c4_acc: 0.9825 - c5_acc: 0.9503 - c6_acc: 0.8821 - c7_acc: 0.8962 - val_loss: 0.7210 - val_c1_loss: 0.0498 - val_c2_loss: 0.0079 - val_c3_loss: 0.0132 - val_c4_loss: 0.0303 - val_c5_loss: 0.0930 - val_c6_loss: 0.2810 - val_c7_loss: 0.2458 - val_c1_acc: 0.9862 - val_c2_acc: 0.9987 - val_c3_acc: 0.9971 - val_c4_acc: 0.9915 - val_c5_acc: 0.9723 - val_c6_acc: 0.9212 - val_c7_acc: 0.9336

可见五轮训练后,即便是位置靠后的几位车牌,也实现了 93% 的识别准确率。

展示下模型预测结果:

myfont = FontProperties(fname='./font/Lantinghei.ttc')  
matplotlib.rcParams['axes.unicode_minus']=False  

fig = plt.figure(figsize=(12,12))
l_titles = list(map(lambda x: "".join([M_idxStr[xx] for xx in x]), np.argmax(np.array(model.predict( np.array(l_plateImg) )), 2).T))
for idx,img in enumerate(l_plateImg[0:40]):
    ax = fig.add_subplot(10,4,idx+1)
    ax.imshow(img)
    ax.set_title(l_titles[idx],fontproperties=myfont)
    ax.set_axis_off()

可见预测的其实相当不错,很多字体已经非常模糊,模型仍然可以看出来。图中一个错误是 皖TQZ680 被预测成了 皖TQZG8D,当然这也和图片裁剪不当有一定的关系。

2. 不固定长度

车牌的应用场景中,我们固定了长度为7位,并且基于这个预设设计了卷积神经网络。但是在实际运用中,可能长度并不固定。此时如果长度过长,用这个架构也将会导致参数过多,占用过多显存。

针对这种情况,Keras 的案例中,提供了一种基于循环神经网络的方法,在 Keras Example 中有写到。具体而言,就是数据首先通过卷积神经网络部分扫描特征,然后通过循环神经网络部分,同时从左到右、从右到左扫描特征,最后基于扫描的结果,通过计算 Conectionist Temporal Classification(CTC) 损失函数,完成模型训练。

2.1. 循环神经网络

使用循环神经网络,是因为循环神经网络有一个很重要的特点,就是相邻的节点之间,可以相互影响。这里相邻节点,既可以是时间上的(前一秒数据和后一秒数据),也可以是位置关系上的,比如我们这里从左向右扫描,左边一列的扫描结果会影响右边一列的扫描结果。

图片来源:知乎:CNN(卷积神经网络)、RNN(循环神经网络)、DNN(深度神经网络)的内部网络结构有什么区别

2.2. CTC 损失函数

同时,对于循环神经网络的结果,由于长度不固定,可能会有空间上的“错配”:

图片来源:Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networks

但由于这种错配实际上并没有什么严重的影响,如上图所示, __TH____E_T__H__EE 其实都是 THE 这个单词,因此这里这种错配在损失函数的优化环节中,是需要被忽略掉的。于是这里就使用了CTC 优化函数。CTC 可以在计算过程中,通过综合所有可能情况的排列组合,进而忽略相对的位置关系。

Keras 的 CTC loss 函数位于 https://github.com/fchollet/keras/blob/master/keras/backend/tensorflow_backend.py 这个文件中,内容如下:

import tensorflow as tf
from tensorflow.python.ops import ctc_ops as ctc

#...

def ctc_batch_cost(y_true, y_pred, input_length, label_length):
    """Runs CTC loss algorithm on each batch element.
    # Arguments
        y_true: tensor `(samples, max_string_length)`
            containing the truth labels.
        y_pred: tensor `(samples, time_steps, num_categories)`
            containing the prediction, or output of the softmax.
        input_length: tensor `(samples, 1)` containing the sequence length for
            each batch item in `y_pred`.
        label_length: tensor `(samples, 1)` containing the sequence length for
            each batch item in `y_true`.
    # Returns
        Tensor with shape (samples,1) containing the
            CTC loss of each element.
    """
    label_length = tf.to_int32(tf.squeeze(label_length))
    input_length = tf.to_int32(tf.squeeze(input_length))
    sparse_labels = tf.to_int32(ctc_label_dense_to_sparse(y_true, label_length))

    y_pred = tf.log(tf.transpose(y_pred, perm=[1, 0, 2]) + 1e-8)

    return tf.expand_dims(ctc.ctc_loss(inputs=y_pred,
                                       labels=sparse_labels,
                                       sequence_length=input_length), 1)

3.3. 完整代码

首先是一些必要的函数:

import os
import itertools
import re
import datetime
import cairocffi as cairo
import editdistance
import numpy as np
from scipy import ndimage
import pylab

from keras import backend as K
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.layers import Input, Dense, Activation, Reshape, Lambda
from keras.layers.merge import add, concatenate
from keras.layers.recurrent import GRU
from keras.models import Model
from keras.optimizers import SGD
from keras.utils.data_utils import get_file
from keras.preprocessing import image
from keras.callbacks import EarlyStopping,Callback

from keras.backend.tensorflow_backend import set_session
import tensorflow as tf
import matplotlib.pyplot as plt

%matplotlib inline

config = tf.ConfigProto()
config.gpu_options.allow_growth=True
set_session(tf.Session(config=config))


OUTPUT_DIR = 'image_ocr'

np.random.seed(55)

# 从 Keras 官方文件中 import 相关的函数
!wget https://raw.githubusercontent.com/fchollet/keras/master/examples/image_ocr.py
from image_ocr import *

必要的参数:

run_name = datetime.datetime.now().strftime('%Y:%m:%d:%H:%M:%S')
start_epoch = 0
stop_epoch  = 200
img_w = 128
img_h = 64
words_per_epoch = 16000
val_split = 0.2
val_words = int(words_per_epoch * (val_split))

# Network parameters
conv_filters = 16
kernel_size = (3, 3)
pool_size = 2
time_dense_size = 32
rnn_size = 512
input_shape = (img_w, img_h, 1)

使用这些函数以及对应参数构建生成器,生成不固定长度的验证码:

fdir = os.path.dirname(get_file('wordlists.tgz',
                                    origin='http://www.mythic-ai.com/datasets/wordlists.tgz', untar=True))

img_gen = TextImageGenerator(monogram_file=os.path.join(fdir, 'wordlist_mono_clean.txt'),
                                 bigram_file=os.path.join(fdir, 'wordlist_bi_clean.txt'),
                                 minibatch_size=32,
                                 img_w=img_w,
                                 img_h=img_h,
                                 downsample_factor=(pool_size ** 2),
                                 val_split=words_per_epoch - val_words
                                 )
act = 'relu'

构建网络:

input_data = Input(name='the_input', shape=input_shape, dtype='float32')
inner = Conv2D(conv_filters, kernel_size, padding='same',
                   activation=act, kernel_initializer='he_normal',
                   name='conv1')(input_data)
inner = MaxPooling2D(pool_size=(pool_size, pool_size), name='max1')(inner)
inner = Conv2D(conv_filters, kernel_size, padding='same',
                   activation=act, kernel_initializer='he_normal',
                   name='conv2')(inner)
inner = MaxPooling2D(pool_size=(pool_size, pool_size), name='max2')(inner)

conv_to_rnn_dims = (img_w // (pool_size ** 2), (img_h // (pool_size ** 2)) * conv_filters)
inner = Reshape(target_shape=conv_to_rnn_dims, name='reshape')(inner)

# cuts down input size going into RNN:
inner = Dense(time_dense_size, activation=act, name='dense1')(inner)

# Two layers of bidirecitonal GRUs
# GRU seems to work as well, if not better than LSTM:
gru_1 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal', name='gru1')(inner)
gru_1b = GRU(rnn_size, return_sequences=True, go_backwards=True, kernel_initializer='he_normal', name='gru1_b')(inner)
gru1_merged = add([gru_1, gru_1b])
gru_2 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_normal', name='gru2')(gru1_merged)
gru_2b = GRU(rnn_size, return_sequences=True, go_backwards=True, kernel_initializer='he_normal', name='gru2_b')(gru1_merged)

# transforms RNN output to character activations:
inner = Dense(img_gen.get_output_size(), kernel_initializer='he_normal',
                  name='dense2')(concatenate([gru_2, gru_2b]))
y_pred = Activation('softmax', name='softmax')(inner)

Model(inputs=input_data, outputs=y_pred).summary()
labels = Input(name='the_labels', shape=[img_gen.absolute_max_string_len], dtype='float32')
input_length = Input(name='input_length', shape=[1], dtype='int64')
label_length = Input(name='label_length', shape=[1], dtype='int64')
# Keras doesn't currently support loss funcs with extra parameters
# so CTC loss is implemented in a lambda layer

loss_out = Lambda(ctc_lambda_func, output_shape=(1,), name='ctc')([y_pred, labels, input_length, label_length])

# clipnorm seems to speeds up convergence
sgd = SGD(lr=0.02, decay=1e-6, momentum=0.9, nesterov=True, clipnorm=5)

model = Model(inputs=[input_data, labels, input_length, label_length], outputs=loss_out)

# the loss calc occurs elsewhere, so use a dummy lambda func for the loss
model.compile(loss={'ctc': lambda y_true, y_pred: y_pred}, optimizer=sgd)
if start_epoch > 0:
    weight_file = os.path.join(OUTPUT_DIR, os.path.join(run_name, 'weights%02d.h5' % (start_epoch - 1)))
    model.load_weights(weight_file)

# captures output of softmax so we can decode the output during visualization
test_func = K.function([input_data], [y_pred])

# 反馈函数,即运行固定次数后,执行反馈函数可保存模型,并且可视化当前训练的效果
viz_cb = VizCallback(run_name, test_func, img_gen.next_val())

模型完整架构如下图所示:

执行训练:

model.fit_generator(generator=img_gen.next_train(), steps_per_epoch=(words_per_epoch - val_words),
                        epochs=stop_epoch, validation_data=img_gen.next_val(), validation_steps=val_words,
                        callbacks=[EarlyStopping(patience=10), viz_cb, img_gen], initial_epoch=start_epoch)

Epoch 1/200
12799/12800 [============================>.] - ETA: 0s - loss: 0.4932
Out of 256 samples:  Mean edit distance: 0.000 Mean normalized edit distance: 0.000
12800/12800 [==============================] - 2025s - loss: 0.4931 - val_loss: 3.7432e-04

完成一个 Epoch 后,输出文件夹 image_ocr 里,可以看到,一轮训练后,我们模型训练效果如下:

]]>
taoCMS-基于php+sqlite最小巧的CMS 2018-01-27 14:01:11
1137 解决spotlight无法创建索引(新文件查不到)的问题 如果发现使用系统自带的spotlight查找文件出现问题,可以采用如下方式解决:

现象:开始是找的特别慢,完全没有往日的闪电侠风采,今天干脆找不到了,风火轮不停的转,msd进程占cpu的30%。

猜测可能是索引出问题了,于是采用如下命令重建索引:
sudo mdutil -i off /
该命令用来关闭索引
sudo mdutil -E /
该命令用来删除索引
sudo mdutil -i on /
该命令用来重建索引
然后用快捷键呼出spotlight菜单,随便输入一个词,就能看到提示,正在进行索引,并且告诉你重建索引的时间。

完成之后,spotlight又可以运转如飞了。

]]>
taoCMS-基于php+sqlite最小巧的CMS 2018-01-25 22:01:31
1136 keras的使用流程 前文了解了keras的重要模块后,我们就可以使用keras了:

1、构造数据

第一步,我们需要根据模型fit(训练)时需要的数据格式来构造数据的shape,用numpy构造两个矩阵:

一个是数据矩阵,一个是标签矩阵,我们举个例子

data=np.random.random((1000,784))
labels=np.random.randint(2,size=(1000,1))
  • 1
  • 2

通过numpy的random生成随机矩阵,数据矩阵是1000行784列的矩阵,标签矩阵是1000行1列的句子,所以数据矩阵的一行就是一个样本,这个样本是784维的

2、构造模型

第二步,我们来构造一个神经网络模型

用泛型模型举例:

两种构造model的方法

model = Sequential([ Dense(32, input_dim=784), Activation('relu'), Dense(10), Activation('softmax'), ])
  • 1

model = Sequential() 
model.add(Dense(32, input_dim=784)) model.add(Activation('relu'))
  • 1
  • 2

在这一步中可以add多个层,也可以merge合并两个模型

3、编译模型

第三步,我们编译上一步构造好的模型,并指定一些模型的参数,比如目标函数、优化器等

model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
  • 1

compile方法的三个参数:

optimizer(优化器),loss(目标函数或损失函数),metrics(评估模型的指标) 
具体见上一篇文章

4、训练模型

第四步,传入要训练的数据和标签,并指定训练的一些参数,然后进行模型训练

fit(self, x, y, batch_size=32, nb_epoch=10, verbose=1, callbacks=[], validation_split=0.0, validation_data=None, shuffle=True, class_weight=None, sample_weight=None)
  • 1

fit方法参数解析:

verbose:训练时显示实时信息,0表示不显示数据,1表示显示进度条,2表示用只显示一个数据

validation_split:0.2表示20%作为数据的验证集

validation_data:形式为(X,y)的tuple,是指定的验证集。此参数将覆盖validation_spilt。

class_weight:字典,将不同的类别映射为不同的权值,该参数用来在训练过程中调整损失函数(只能用于训练)

sample_weight:权值的numpy array,用于在训练时调整损失函数(仅用于训练)。可以传递一个1D的与样本等长的向量用于对样本进行1对1的加权,或者在面对时序数据时,传递一个的形式为(samples,sequence_length)的矩阵来为每个时间步上的样本赋不同的权。这种情况下请确定在编译模型时添加了sample_weight_mode=’temporal’。

(以上两个参数据说分别为类别权重和样本权重,类别权重没太理解,样本权重貌似就是比如这个样本对分类贡献大,就增加他的权重,有点像TF-IDF,是否是一种注意力机制呢?)

x:输入数据。如果模型只有一个输入,那么x的类型是numpy array,如果模型有多个输入,那么x的类型应当为list,list的元素是对应于各个输入的numpy array

y:标签,numpy array

batch_size:整数,指定进行梯度下降时每个batch包含的样本数。训练时一个batch的样本会被计算一次梯度下降,使目标函数优化一步。

nb_epoch:整数,训练的轮数,训练数据将会被遍历nb_epoch次。Keras中nb开头的变量均为”number of”的意思

5、测试数据

第五步,用测试数据测试已经训练好的模型,并可以获得测试结果,从而对模型进行评估。

  • evaluate:
evaluate(self, x, y, batch_size=32, verbose=1, sample_weight=None)
  • 1

本函数返回一个测试误差的标量值(如果模型没有其他评价指标),或一个标量的list(如果模型还有其他的评价指标)

  • predict
predict(self, x, batch_size=32, verbose=0)
  • 1

函数的返回值是预测值的numpy array

  • predict_classes
predict_classes(self, x, batch_size=32, verbose=1)
  • 1
  • 2

本函数按batch产生输入数据的类别预测结果

函数的返回值是类别预测结果的numpy array或numpy

还有其他评估指标,具体见: 
http://keras-cn.readthedocs.io/en/latest/models/sequential/


以上就是keras编程常用的五个步骤


6、保存与读取模型

将模型保存为json
json_string = model.to_json()  
将模型保存为yaml
yaml_string = model.to_yaml()  
从保存的json中加载模型  
from keras.modelsimport model_from_json  
model = model_from_json(json_string)  
从保存的yaml中加载模型  
model =model_from_yaml(yaml_string)  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

7、保存与加载权重数据

model.save_weights('my_model_weights.h5')  
model.load_weights('my_model_weights.h5')  
  • 1
  • 2

以上是根据keras中文文档进行总结与修改的,主要参考 
http://keras-cn.readthedocs.io/en/latest

]]>
taoCMS-基于php+sqlite最小巧的CMS 2018-01-23 14:01:10
1135 阿里云GPU服务器上Torch安装与测试
  • 本文个人博客访问地址: 点击查看
  • 一、介绍

    • 阿里云的GPU也有了竞价服务,每小时大概1块多,还是可以接受的
    • 主要想跑github上的一个论文代码,使用的GPU,(奈何实验室没有GPU), 本来我已经改成CPU版本的了,但是他训练好的模型是基于GPU的,所以还需要重新训练,结果非常的慢…
    • 包含以下内容: 
      • 购买竞价GPU
      • 通过SSH连接云服务器
      • 安装Torch、hdf5、cjson、loadcaffe
      • 安装cuda、cudnn、cunn

    二、购买GPU服务器

    • 进入阿里云GPU介绍页,点击访问,界面如下,我选择的是GN5(P100)

    GPU介绍页
    - 选择竞价实例

    选择竞价实例
    - 选择GPU

    选择GPU
    - 选择Ubuntu版本和带宽 
    - 这里按使用流量,所以带宽设置大点没有影响

    选择系统和带宽

    • 在控制台可以看到服务器信息,下面需要使用公网IP连接

    控制台

    三、连接GPU服务器以及软件的安装

    1、使用SecureCRT连接服务器

    连接服务器

    2、安装前准备工作

    • apt clean
    • apt update
    • 安装git命令行:apt install git
    • 生成ssh-key : ssh-keygen -t rsa -C "youremail@example.com" 
      • /root/.ssh/id_rsa.pub中内容加入到github

    3、安装Torch

    torch

    4、 安装hdf5

    • 地址: https://github.com/deepmind/torch-hdf5/blob/master/doc/usage.md
    • apt-get install libhdf5-serial-dev hdf5-tools
    • git clone https://github.com/deepmind/torch-hdf5
    • cd torch-hdf5
    • luarocks make hdf5-0-0.rockspec LIBHDF5_LIBDIR="/usr/lib/x86_64-linux-gnu/" 
      • 注意这里 luarocks 是 Torch 里的,在 /root/torch/install/bin 目录下

    5、 安装 cjson 和 loadcaffe

    • luarocks install lua-cjson
    • apt-get install libprotobuf-dev protobuf-compiler
    • luarocks install loadcaffe

    6、安装Cuda

    cuda
    sudo dpkg -i cuda-repo-ubuntu1604-9-1-local_9.1.85-1_amd64.deb 
    sudo apt-key add /var/cuda-repo-<version>/7fa2af80.pub 
    sudo apt-get update 
    sudo apt-get install cuda 
    - 安装完成后会在/usr/local/目录下出现cuda-9.1的目录 
    - 加入到环境变量 
    echo "export PATH=/usr/local/cuda-9.1/bin/:$PATH; export LD_LIBRARY_PATH=/usr/local/cuda-9.1/lib64/:$LD_LIBRARY_PATH; " >>~/.bashrc && source ~/.bashrc
    - 此时cuda已经安装成功,可以通过nvcc -V测试是否安装成功 
    nvidia-smi命令查看GPU使用情况

    cuda安装测试 
    - 有时可能需要重启一下

    7、安装cudnn

    • 网址1:点击查看
    • 网址2:下载cudnn 
      • 需要先注册登录才能下载
    • 注意这里下载的版本,我这里使用的是5.1版本(尝试了最新的7.x版本,有问题)

    cudnn版本
    - 直接luarocks install cudnn是可以成功安装的,但是有问题 
    - 下载的是压缩包,里面有两个文件夹

    cudnn压缩包 
    - 将include下的cudnn.h文件拷贝到/usr/local/cuda-9.1/include/文件夹下 
    - 将lib64下的libcudnn.so.5.1.10文件拷贝到/usr/local/cuda-9.1/lib64/文件夹下 
    - 并且创建软连接: ln -s libcudnn.so.5.1.10 libcudnn.so.5 
    - 添加环境变量:export CUDNN_PATH="/usr/local/cuda-9.1/lib64/libcudnn.so.5"

    cudnn5.x

    四、测试

    • 下面是我跑的一个程序

    GPU使用情况

    五、其他一些说明

    1、rz/sz文件传输

    • wget https://raw.githubusercontent.com/lawlite19/LinuxSoftware/master/rz-sz/lrzsz-0.12.20.tar.gz
    • tar zxvf lrzsz-0.12.20.tar.gz
    • cd lrzsz-0.12.20
    • ./configure && make && make install
    • cd /usr/local/bin
    • ln -s lrz rz
    • ln -s lsz sz

    2、使用xftp等工具传输文件

    • 服务器上需要安装ftp服务

    3、wget 下载百度云盘文件

    • wget -c ----referer=百度云盘分享地址 -O 要保存的文件名 "百度云文件真实地址"
    • 文件的真实地址获取 
      • 浏览器按F12, 点击下载找到download?的信息
      • dlink为真实地址,注意去除转义字符

    获取百度云真实地址
    - 比如: wget -c --referer=https://pan.baidu.com/s/1kV7Xo7H -O lstm1_rnn512_bestACC.zip "https://d.pcs.baidu.com/file/4e4cd12ad77d7ac60d2cfcb8e009bf1c?fid=3174489928-250528-212189063946307&time=1514127189&rt=pr&sign=FDTAERVCY-DCb740ccc5511e5e8fedcff06b081203-LWe3VIBsW3foAEVnTUqSROJQ46s%3D&expires=8h&chkv=1&chkbd=1&chkpc=et&dp-logid=8301954057401711855&dp-callid=0&r=884079691"

    Reference

    ]]>
    taoCMS-基于php+sqlite最小巧的CMS 2018-01-13 01:01:38
    1134 tesseract-ocr 提高验证码识别率手段之—识别码库训练方法 关于ORC验证码识别可以看本博客的另一篇文章

    常用的两种ORC 验证码 识别方法及实践感言

     

    本文是对tesseract-ocr 使用的进一步技术升级说明,使用默认的识别库识别率比较低怎么办?

    不用着急,tesseract-ocr本身的工具中提供了使用你提供的素材进行人工修正以提高识别率的方法。下面我们就来看一下。

     

     

    参考:

    http://my.oschina.net/lixinspace/blog/60124

     

    1    下载并安装3.02版本的tesseract

     

    2     如果你的训练素材是很多张非tiff格式的图片,首先要做的事情就是将这么图片合并(个人觉得素材越多,基本每个字母和数字都覆盖了训练出来的识别率比较好)

    http://sourceforge.net/projects/vietocr/files/latest/download?source=files

    下载这个工具:VietOCR.NET-3.3.zip

    首先进行jpg,gif,bmptiff的转换,这个用自带的画图就可以。然后使用VietOCR.NET-3.3进行多张 tiffmerge

     

    3      
    Make Box Files
    。在orderNo.tif所在的目录下打开一个命令行,输入

    C:Program FilesTesseract-OCR>tesseract.exe lang.jhy.exp8.TIF lang.jhy.exp8 batch.nochop makebox

     

    4       使用jTessBoxEditor打开orderNo.tif文件,需要记住的是第2步生成的orderNo.box要和这个orderNo.tif文件同在一个目录下。逐个校正文字,后保存。

    http://sourceforge.net/projects/vietocr/files/

     下载jTessBoxEditor工具进行每个自的纠正(注意有nextpage逐页进行纠正)

     

    5       
    Run Tesseract for Training
    。输入命令:

    C:Program FilesTesseract-OCR>tesseract.exe lang.jhy.exp8.TIF lang.jhy.exp8 nob

    atch box.train

     

    补充关于命名格式解释:lang.jhy.exp8.TIF

    Make Box Files

    For the next step below, Tesseract needs a 'box' file to go with each training image. The box file is a text file that lists the characters in the training image, in order, one per line, with the coordinates of the bounding box around the image.
    Tesseract 3.0 has a mode in which it will output a text file of the required format, but if the character set is different to its current training, it will naturally have the text incorrect. So the key process here is to manually edit the file to put the correct
    characters in it.

    Run Tesseract on each of your training images using this command line:

    tesseract [lang].[fontname].exp[num].tif [lang].[fontname].exp[num] batch.nochop makebox

     

     

    6      
    Compute the Character Set
    。输入命令:

    C:Program FilesTesseract-OCR>unicharset_extractor.exe lang.jhy.exp8.box

    Extracting unicharset from lang.jhy.exp8.box

    Wrote unicharset file ./unicharset.

     

     

     

    7        新建文件“font_properties”。如果是3.01版本,那么需要在目录下新建一个名字为“font_properties”的文件,并且输入文本
    :
    (这里的jhy就是lang.jhy.exp8的中间字段)

    jhy   1 0 0 1 0

     

    C:Program FilesTesseract-OCR>mftraining.exe -F font_properties -U unicharset

    ang.jhy.exp8.tr

    Warning: No shape table file present: shapetable

    Reading lang.jhy.exp8.tr ...

    Flat shape table summary: Number of shapes = 18 max unichars = 1 number with mu

    tiple unichars = 0

    Done!

     

    8      
    Clustering
    。输入命令:

    C:Program FilesTesseract-OCR>cntraining.exe lang.jhy.exp8.tr

    Reading lang.jhy.exp8.tr ...

    Clustering ...

     

    Writing normproto ...

     

    9         此时,在目录下应该生成若干个文件了,把unicharset, inttemp, normproto, pfftable这几个文件加上前缀“selfverify.”。然后输入命令:

     

    必须确定的是1、3、4、5、13行的数据不是-1,那么一个新的字典就算生成了。

    此时目录下“selfverify.traineddata”的文件拷贝到tesseract程序目录下的“tessdata”目录。

    以后就可以使用该该字典来识别了,例如:

    tesseract.exe test.jpg out –l selfverify

     

    通过训练出来的新语言,识别率提高了不少。

    ]]>
    taoCMS-基于php+sqlite最小巧的CMS 2018-01-11 20:01:13
    1133 利用Python破解验证码实例详解
    这篇文章主要给大家介绍的是如何通过一个简单的例子来实现破解验证码。从本文中我们可以学习到 Python 基本知识,PIL 模块的使用,破解验证码的原理。文中通过实例一步步介绍的很详细,相信对大家理解和学习具有一定的参考借鉴价值,有需要的朋友们下面来一起看看吧。

    一、前言

    本实验将通过一个简单的例子来讲解破解验证码的原理,将学习和实践以下知识点:

          Python基本知识

          PIL模块的使用

    二、实例详解

    安装 pillow(PIL)库:

    $ sudo apt-get update
    
    $ sudo apt-get install python-dev
    
    $ sudo apt-get install libtiff5-dev libjpeg8-dev zlib1g-dev 
    libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk
    
    $ sudo pip install pillow

    下载实验用的文件:

    $ wget http://labfile.oss.aliyuncs.com/courses/364/python_captcha.zip
    $ unzip python_captcha.zip
    $ cd python_captcha

    这是我们实验使用的验证码 captcha.gif


    提取文本图片

    在工作目录下新建 crack.py 文件,进行编辑。

    #-*- coding:utf8 -*-
    from PIL import Image
    
    im = Image.open("captcha.gif")
    #(将图片转换为8位像素模式)
    im = im.convert("P")
    
    #打印颜色直方图
    print im.histogram()

    输出:

    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0 , 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 1, 2, 0, 1, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 3, 1, 3, 3, 0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 132, 1, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 15, 0 , 1, 0, 1, 0, 0, 8, 1, 0, 0, 0, 0, 1, 6, 0, 2, 0, 0, 0, 0, 18, 1, 1, 1, 1, 1, 2, 365, 115, 0, 1, 0, 0, 0, 135, 186, 0, 0, 1, 0, 0, 0, 116, 3, 0, 0, 0, 0, 0, 21, 1, 1, 0, 0, 0, 2, 10, 2, 0, 0, 0, 0, 2, 10, 0, 0, 0, 0, 1, 0, 625]

    颜色直方图的每一位数字都代表了在图片中含有对应位的颜色的像素的数量。

    每个像素点可表现256种颜色,你会发现白点是最多(白色序号255的位置,也就是最后一位,可以看到,有625个白色像素)。红像素在序号200左右,我们可以通过排序,得到有用的颜色。

    his = im.histogram()
    values = {}
    
    for i in range(256):
     values[i] = his[i]
    
    for j,k in sorted(values.items(),key=lambda x:x[1],reverse = True)[:10]:
     print j,k

    输出:

    255 625
    212 365
    220 186
    219 135
    169 132
    227 116
    213 115
    234 21
    205 18
    184 15

    我们得到了图片中最多的10种颜色,其中 220 与 227 才是我们需要的红色和灰色,可以通过这一讯息构造一种黑白二值图片。

    #-*- coding:utf8 -*-
    from PIL import Image
    
    im = Image.open("captcha.gif")
    im = im.convert("P")
    im2 = Image.new("P",im.size,255)
    
    
    for x in range(im.size[1]):
     for y in range(im.size[0]):
      pix = im.getpixel((y,x))
      if pix == 220 or pix == 227: # these are the numbers to get
       im2.putpixel((y,x),0)
    
    im2.show()

    得到的结果:


    提取单个字符图片

    接下来的工作是要得到单个字符的像素集合,由于例子比较简单,我们对其进行纵向切割:

    inletter = False
    foundletter=False
    start = 0
    end = 0
    
    letters = []
    
    for y in range(im2.size[0]): 
     for x in range(im2.size[1]):
      pix = im2.getpixel((y,x))
      if pix != 255:
       inletter = True
     if foundletter == False and inletter == True:
      foundletter = True
      start = y
    
     if foundletter == True and inletter == False:
      foundletter = False
      end = y
      letters.append((start,end))
    
     inletter=False
    print letters

    输出:

    [(6, 14), (15, 25), (27, 35), (37, 46), (48, 56), (57, 67)]

    得到每个字符开始和结束的列序号。

    import hashlib
    import time
    
    count = 0
    for letter in letters:
     m = hashlib.md5()
     im3 = im2.crop(( letter[0] , 0, letter[1],im2.size[1] ))
     m.update("%s%s"%(time.time(),count))
     im3.save("./%s.gif"%(m.hexdigest()))
     count += 1

    (接上面的代码)

    对图片进行切割,得到每个字符所在的那部分图片。

    AI 与向量空间图像识别

    在这里我们使用向量空间搜索引擎来做字符识别,它具有很多优点:

    1. 不需要大量的训练迭代
    2. 不会训练过度
    3. 你可以随时加入/移除错误的数据查看效果
    4. 很容易理解和编写成代码
    5. 提供分级结果,你可以查看最接近的多个匹配
    6. 对于无法识别的东西只要加入到搜索引擎中,马上就能识别了。

    当然它也有缺点,例如分类的速度比神经网络慢很多,它不能找到自己的方法解决问题等等。

    向量空间搜索引擎名字听上去很高大上其实原理很简单。拿文章里的例子来说:

    你有 3 篇文档,我们要怎么计算它们之间的相似度呢?2 篇文档所使用的相同的单词越多,那这两篇文章就越相似!但是这单词太多怎么办,就由我们来选择几个关键单词,选择的单词又被称作特征,每一个特征就好比空间中的一个维度(x,y,z 等),一组特征就是一个矢量,每一个文档我们都能得到这么一个矢量,只要计算矢量之间的夹角就能得到文章的相似度了。

    用 Python 类实现向量空间:

    import math
    
    class VectorCompare:
     #计算矢量大小
     def magnitude(self,concordance):
      total = 0
      for word,count in concordance.iteritems():
       total += count ** 2
      return math.sqrt(total)
    
     #计算矢量之间的 cos 值
     def relation(self,concordance1, concordance2):
      relevance = 0
      topvalue = 0
      for word, count in concordance1.iteritems():
       if concordance2.has_key(word):
        topvalue += count * concordance2[word]
      return topvalue / (self.magnitude(concordance1) * self.magnitude(concordance2))

    它会比较两个 python 字典类型并输出它们的相似度(用 0~1 的数字表示)

    将之前的内容放在一起

    还有取大量验证码提取单个字符图片作为训练集合的工作,但只要是有好好读上文的同学就一定知道这些工作要怎么做,在这里就略去了。可以直接使用提供的训练集合来进行下面的操作。

    iconset目录下放的是我们的训练集。

    最后追加的内容:

    #将图片转换为矢量
    def buildvector(im):
     d1 = {}
     count = 0
     for i in im.getdata():
      d1[count] = i
      count += 1
     return d1
    
    v = VectorCompare()
    
    iconset = ['0','1','2','3','4','5','6','7','8','9','0','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']
    
    #加载训练集
    imageset = []
    for letter in iconset:
     for img in os.listdir('./iconset/%s/'%(letter)):
      temp = []
      if img != "Thumbs.db" and img != ".DS_Store":
       temp.append(buildvector(Image.open("./iconset/%s/%s"%(letter,img))))
      imageset.append({letter:temp})
    
    
    count = 0
    #对验证码图片进行切割
    for letter in letters:
     m = hashlib.md5()
     im3 = im2.crop(( letter[0] , 0, letter[1],im2.size[1] ))
    
     guess = []
    
     #将切割得到的验证码小片段与每个训练片段进行比较
     for image in imageset:
      for x,y in image.iteritems():
       if len(y) != 0:
        guess.append( ( v.relation(y[0],buildvector(im3)),x) )
    
     guess.sort(reverse=True)
     print "",guess[0]
     count += 1

    得到结果

    一切准备就绪,运行我们的代码试试:

    python crack.py

    输出

    (0.96376811594202894, '7')
    (0.96234028545977002, 's')
    (0.9286884286888929, '9')
    (0.98350370609844473, 't')
    (0.96751165072506273, '9')
    (0.96989711688772628, 'j')

    是正解,干得漂亮。

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

    ]]>
    taoCMS-基于php+sqlite最小巧的CMS 2018-01-11 20:01:55