深度学习图像处理入门

1、环境搭建

在云服务器中开启Docker服务

systemctl start docker

systemctl start nvidia-docker

git clone https://github.com/Jinglue/DL4Img

安装驱动和cuda ......

nvidia-docker pull hubq/dl4img

nvidia-docker run -d -v ~/dl4img/notebook/:/srv -p 8888:8888 -p 6006:6006 hubq/dl4img

登录jupyter:http://xxx:8888,密码jizhitencent

2、机器学习基础知识

3、数形结合---图像处理基础知识

读取图像文件进行基本操作

提取特征

4、使用深度神经网络框架

简单计算图实现P95

5、深度神经网络框架的模型元件

CNN,RNN,Dense

6、深度神经网络框架的输入处理

批量生成训练数据

7、深度神经网络框架的模型训练

优化算法

8、使用深度神经网络进行CIFAR-10数据分类

keras实验

9、使用迁移学习提高准确率

kaggle猫狗图片

10、使用深度神经网络进行文字识别

       首先使用卷积神经网络加多个全连接分类器的方法来识别,然后使用CNN结合RNN的方式来识别,不经能够准确识别字符,不需要进行切割,还能够根据上下文以及语法规则猜测字符。

验证码识别,用captcha库,生成验证码P169,模型最后输出4个结果,每个36个字符

$ pip install captcha

生成样本

from captcha.image import ImageCaptcha
import matplotlib.pyplot as plt
import numpy as np
import random
import string
characters = string.digits + string.ascii_uppercase
print(characters) # 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
width, height, n_len, n_class = 170, 80, 4, len(characters)
generator = ImageCaptcha(width=width, height=height) # 生成图片大小
random_str = ''.join([random.choice(characters) for j in range(4)])
img = generator.generate_image(random_str)
plt.imshow(img)
plt.title(random_str)
plt.show()

编写数据生成器,两种方式:

  • 一次性生成几万张图,好处是显卡利用率高,如果需要经常调参,可以一次生成,然后多次训练。
  • 定义一个数据生成器,然后利用fit_generator函数进行训练,好处是不生成大量数据,训练时CPU生成,可以无限生成,提高泛化能力。

X:(batch_size, height, width, 3)

y:4个(batch_size, n_class),即(n_len, batch_size, n_class),使用next函数

def gen(batch_size=32):
    X = np.zeros((batch_size, height, width, 3), dtype=np.uint8)
    y = [np.zeros((batch_size, n_class), dtype=np.uint8) for i in range(n_len)]
    generator = ImageCaptcha(width=width, height=height)
    while True:
        for i in range(batch_size):
            random_str = ''.join([random.choice(characters) for j in range(4)])
            X[i] = generator.generate_image(random_str)
            for j, ch in enumerate(random_str):
                y[j][i, :] = 0
                y[j][i, characters.find(ch)] = 1
        yield X, y
def decode(y, index=0):
    # [0,0,...,1,0]
    y = np.argmax(np.array(y), axis=2)[:, index]
    return ''.join([characters[x] for x in y])
X, y = next(gen(1))
plt.imshow(X[0])
plt.title(decode(y))
plt.show()

构建深度CNN,最后连接4个分类器,每个分类器是36个神经元,输出36个字符的概率,可视化需要pydot和graphviz库

from keras.models import *
from keras.layers import *
from keras.optimizers import *
input_tensor = Input((height, width, 3))
x = input_tensor
for i in range(4):
    x = Convolution2D(32*2**i, 3, activation='relu')(x)
    x = Convolution2D(32*2**i, 3, activation='relu')(x)
    x = MaxPooling2D(2)(x)
x = Flatten()(x)
x = Dropout(0.25)(x)
x = [Dense(n_class, activation='softmax', name='c%d'%(i+1))(x) for i in range(4)]
model = Model(inputs=input_tensor, outputs=x)
model.compile(loss='categorical_crossentropy',
              optimizer='adadelta',
              metrics=['accuracy'])
# 模型可视化
from keras.utils.vis_utils import plot_model
from IPython.display import Image
plot_model(model, to_file="model.png", show_shapes=True)
Image('model.png')

训练模型

h = model.fit_generator(gen(128), steps_per_epoch=400, epochs=20, 
                        workers=4, pickle_safe=True, 
                        validation_data=gen(128), validation_steps=10)

可视化loss和acc曲线图

plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(h.history['loss'])
plt.plot(h.history['val_loss'])
plt.legend(['loss', 'val_loss'])
plt.ylabel('loss')
plt.xlabel('epoch')
plt.ylim(0, 1)

plt.subplot(1, 2, 2)
for i in range(4):
    plt.plot(h.history['val_c%d_acc' % (i+1)])
plt.legend(['val_c%d_acc' % (i+1) for i in range(4)])
plt.ylabel('acc')
plt.xlabel('epoch')
plt.ylim(0.9, 1)

模型评估,准确率97.8%

from tqdm import tqdm

def evaluate(batch_size=128, steps=20):
    batch_acc = 0
    generator = gen(batch_size)
    for i in tqdm(range(steps)):
        X, y = next(generator)
        y_pred = model.predict(X)
        y_pred = np.argmax(y_pred, axis=-1)
        y_true = np.argmax(y, axis=-1)
        batch_acc += np.equal(y_true, y_pred).all(axis=0).mean()
    return batch_acc / steps

测试模型

X, y = next(gen(12))
y_pred = model.predict(X)

plt.figure(figsize=(16, 8))
for i in range(12):
    plt.subplot(3, 4, i+1)
    plt.title('real: %s\npred:%s'%(decode(y, i), decode(y_pred, i)))
    plt.imshow(X[i])
model.save('model.h5')  # 16MB

使用循环神经网络改进模型

对于这种按照顺序书写的文字,还有一种方法可以使用,那就RNN来识别序列。变成37个字符。

from captcha.image import ImageCaptcha
import matplotlib.pyplot as plt
import numpy as np
import random

%matplotlib inline
%config InlineBackend.figure_format = 'retina'

import string
characters = string.digits + string.ascii_uppercase
print(characters)
width, height, n_len, n_class = 170, 80, 4, len(characters)+1
generator = ImageCaptcha(width=width, height=height)
random_str = ''.join([random.choice(characters) for j in range(4)])
img = generator.generate_image(random_str)
plt.imshow(img)
plt.title(random_str)

CTC Loss,可以在只直到序列的顺序,不知道具体位置的情况下让模型收敛(warp-ctc)。

在Keras中,内置了CTC Loss

from keras import backend as K
def ctc_lambda_func(args):
    y_pred, labels, input_length, label_length = args
    y_pred = y_pred[:, 2:, :]
    # input_length=15, label_length=4
    return K.ctc_batch_cost(labels, y_pred, input_length, label_length)

模型,

from keras.models import *
from keras.layers import *
from keras.optimizers import *
rnn_size = 128

input_tensor = Input((width, height, 3))
x = input_tensor
x = Lambda(lambda x:(x-127.5)/127.5)(x)
for i in range(3):
    for j in range(2):
        x = Conv2D(32*2**i, 3, kernel_initializer='he_uniform')(x)
        x = BatchNormalization()(x)
        x = Activation('relu')(x)
    x = MaxPooling2D((2, 2))(x)

conv_shape = x.get_shape().as_list()
rnn_length = conv_shape[1]
rnn_dimen = conv_shape[2]*conv_shape[3]
print(conv_shape, rnn_length, rnn_dimen)
x = Reshape(target_shape=(rnn_length, rnn_dimen))(x)
rnn_length -= 2

x = Dense(rnn_size, kernel_initializer='he_uniform')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Dropout(0.2)(x)

gru_1 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_uniform', name='gru1')(x)
gru_1b = GRU(rnn_size, return_sequences=True, kernel_initializer='he_uniform', 
             go_backwards=True, name='gru1_b')(x)
x = add([gru_1, gru_1b])

gru_2 = GRU(rnn_size, return_sequences=True, kernel_initializer='he_uniform', name='gru2')(x)
gru_2b = GRU(rnn_size, return_sequences=True, kernel_initializer='he_uniform', 
             go_backwards=True, name='gru2_b')(x)
x = concatenate([gru_2, gru_2b])

x = Dropout(0.2)(x)
x = Dense(n_class, activation='softmax')(x)
base_model = Model(inputs=input_tensor, outputs=x)

labels = Input(name='the_labels', shape=[n_len], dtype='float32')
input_length = Input(name='input_length', shape=[1], dtype='int64')
label_length = Input(name='label_length', shape=[1], dtype='int64')
loss_out = Lambda(ctc_lambda_func, output_shape=(1,), 
                  name='ctc')([x, labels, input_length, label_length])

model = Model(inputs=[input_tensor, labels, input_length, label_length], outputs=[loss_out])
model.compile(loss={'ctc': lambda y_true, y_pred: y_pred}, optimizer='adam')
# 可视化
from keras.utils.vis_utils import plot_model
from IPython.display import Image
plot_model(model, to_file="model.png", show_shapes=True)
Image('model.png')

       从Input到最后一个MaxPooling2D,是一个很深的卷积神经网络,它负责学习字符的各个特征,尽可能区分不同的字符。它输出shape是[None, 17, 6, 128],这个形状相当于把一张170x80的彩色图(170,80,3),压缩成17x6的128维特征的特征图(17,6,128)。然后我们把图像reshape为(17,768),也就是把高和特征放在一个维度,再降维成(17,128),也就是从左到右有17条特征,每个特征128个维度。

     这128个维度就是一条图像的非常高维、抽象的概括,然后将17个特征向量依次输入到GRU中,GRU有能力学会不同特征向量的组合会代表什么字符,即使是字符之间有粘连也不怕。这里使用了双向GRU,最后Dropout接一个全连接层,作为分类器输出每个字符的概率。

       最后计算CTC Loss。

       可以看到模型比上一个模型复杂了许多,但实际上只是因为输入比较多,所以它显得很大,还有一个值得注意的地方,我们的图片在输入时是经过了transpose函数旋转的,这是因为希望以水平方向输入RNN。

数据生成器,input_length表示模型输出的长度,这里是15。

def gen(batch_size=128):
    X = np.zeros((batch_size, width, height, 3), dtype=np.uint8)
    y = np.zeros((batch_size, n_len), dtype=np.uint8)
    generator = ImageCaptcha(width=width, height=height)
    while True:
        for i in range(batch_size):
            random_str = ''.join([random.choice(characters) for j in range(n_len)])
            X[i] = np.array(generator.generate_image(random_str)).transpose(1, 0, 2)
            y[i] = [characters.find(x) for x in random_str]
        yield [X, y, np.ones(batch_size)*rnn_length, np.ones(batch_size)*n_len], np.ones(batch_size)
(X_vis, y_vis, input_length_vis, label_length_vis), _ = next(gen(1))
print(X_vis.shape, y_vis, input_length_vis, label_length_vis)

plt.imshow(X_vis[0].transpose(1, 0, 2))
plt.title(''.join([characters[i] for i in y_vis[0]]))

评估模型,只有全部正确,才算预测正确。这里有个坑,就是模型最开始训练的时候,并不一定会输出4个字符,所以如果遇到所有的字符都不到4个的时候,就不用计算了,一定是全错。遇到多于4个字符的时候,只取前4个。因为Keras没有针对CTC模型计算准确率的选项,因此需要自定义一个回调函数,它会在每一代训练完成的时候计算模型的准确率。

def evaluate(batch_size=128, steps=10):
    batch_acc = 0
    generator = gen(batch_size)
    for i in range(steps):
        [X_test, y_test, _, _], _  = next(generator)
        y_pred = base_model.predict(X_test)
        shape = y_pred[:,2:,:].shape
        ctc_decode = K.ctc_decode(y_pred[:,2:,:], input_length=np.ones(shape[0])*shape[1])[0][0]
        out = K.get_value(ctc_decode)[:, :n_len]
        if out.shape[1] == n_len:
            batch_acc += (y_test == out).all(axis=1).mean()
    return batch_acc / steps
from keras.callbacks import *
class Evaluator(Callback):
    def __init__(self):
        self.accs = []
  
    def on_epoch_end(self, epoch, logs=None):
        acc = evaluate(steps=20)*100
        self.accs.append(acc)
        print('')
        print('acc: %f%%' % acc)
evaluator = Evaluator()

训练模型,先按照Adam(1e-3)的学习率训练20代,让模型快速收敛,然后以Adam(1e-4)的学习率再训练。这里设置每迭代400个step,也就是400*128=51200个样本,验证集设置的是20*128=2048个样本。准确率99.46%

h = model.fit_generator(gen(128), steps_per_epoch=400, epochs=20,
                        callbacks=[evaluator],
                        validation_data=gen(128), validation_steps=20)
model.compile(loss={'ctc': lambda y_true, y_pred: y_pred}, optimizer=Adam(1e-4))
h2 = model.fit_generator(gen(128), steps_per_epoch=400, epochs=20, 
                        callbacks=[evaluator],
                        validation_data=gen(128), validation_steps=20)
plt.figure(figsize=(10, 4))
plt.subplot(1, 2, 1)
plt.plot(h.history['loss'] + h2.history['loss'])
plt.plot(h.history['val_loss'] + h2.history['val_loss'])
plt.legend(['loss', 'val_loss'])
plt.ylabel('loss')
plt.xlabel('epoch')
plt.ylim(0, 1)

plt.subplot(1, 2, 2)
plt.plot(evaluator.accs)
plt.ylabel('acc')
plt.xlabel('epoch')

测试模型

(X_vis, y_vis, input_length_vis, label_length_vis), _ = next(gen(12))

y_pred = base_model.predict(X_vis)
shape = y_pred[:,2:,:].shape
ctc_decode = K.ctc_decode(y_pred[:,2:,:], input_length=np.ones(shape[0])*shape[1])[0][0]
out = K.get_value(ctc_decode)[:, :4]

plt.figure(figsize=(16, 8))
for i in range(12):
    plt.subplot(3, 4, i+1)
    plt.imshow(X_vis[i].transpose(1, 0, 2))
    plt.title('pred:%s\nreal :%s' % (''.join([characters[x] for x in out[i]]), 
                                     ''.join([characters[x] for x in y_vis[i]])))

再次评估模型,我们可以尝试计算模型的总体准确率,以及查看模型到底错在哪里。首先生成1024个样本,再用base_model进行预测,然后裁剪并进行ctc解码,最后裁剪到4个label并与真实值进行对比。

(X_vis, y_vis, input_length_vis, label_length_vis), _ = next(gen(10000))

y_pred = base_model.predict(X_vis, verbose=1)
shape = y_pred[:,2:,:].shape
ctc_decode = K.ctc_decode(y_pred[:,2:,:], input_length=np.ones(shape[0])*shape[1])[0][0]
out = K.get_value(ctc_decode)[:, :4]

(y_vis == out).all(axis=1).mean()
# 0.99460000000000004
# 对预测错的样本进行统计
from collections import Counter
Counter(''.join([characters[i] for i in y_vis[y_vis != out]]))
# Counter({'0': 37, 'O': 14, 'Q': 1, 'T': 1, 'W': 1})

可以发现模型在O和0的准确率稍微低一点,其他的只是个例。O和0确实是很难分辨的,可以尝试用代码生成一个”O0O0“的图像,然后用模型进行预测。

base_model.save('model.h5')

characters2 = characters + ' '

generator = ImageCaptcha(width=width, height=height)
random_str = '0O0O'
X_test = np.array(generator.generate_image(random_str))
X_test = X_test.transpose(1, 0, 2)
X_test = np.expand_dims(X_test, 0)

y_pred = base_model.predict(X_test)
shape = y_pred[:,2:,:].shape
ctc_decode = K.ctc_decode(y_pred[:,2:,:], input_length=np.ones(shape[0])*shape[1])[0][0]
out = K.get_value(ctc_decode)[:, :4]
out = ''.join([characters[x] for x in out[0]])

plt.imshow(X_test[0].transpose(1, 0, 2))
plt.title('pred:' + str(out))

argmax = np.argmax(y_pred, axis=2)[0]
list(zip(argmax, ''.join([characters2[x] for x in argmax])))

识别四则混合运算验证码(初赛)

百度云和魅族联合办了一个深度学习比赛,识别四则混合运算

初赛数据集一共包含10万张180*60的图片和一个labels.txt的文本文件。

评价指标是准确率,要求序列与运算结果都正确才会判定为正确。我们本地还会使用CTC Loss来评估模型。

数据生成器,由于比赛减号都是细的,但captcha库会得到粗的,所以修改captcha库中的image.py,在_draw_character函数增加了一句判断,如果是减号就不进行resize操作,这样能够防止减号变粗。

            if c != '-':
                im = im.resize((w2, h2))
                im = im.transform((w, h), Image.QUAD, data)
import string
import os

digits = string.digits
operators = '+-*'
characters = digits + operators + '() '
print(characters)

width, height, n_len, n_class = 180, 60, 7, len(characters) + 1
print(n_class)
# 原图片
from captcha.image import ImageCaptcha
generator = ImageCaptcha(width=width, height=height, font_sizes=range(35, 56), 
                         fonts=['fonts/%s'%x for x in os.listdir('fonts') if '.tt' in x])
generator.generate_image('(1-2)-3')
# 改进
from image import ImageCaptcha
generator = ImageCaptcha(width=width, height=height, font_sizes=range(35, 56), 
                         fonts=['fonts/%s'%x for x in os.listdir('fonts') if '.tt' in x])
generator.generate_image('(1-2)-3')

模型结构,与之前一样,只是把卷积核的个数改多了一点,并且做了一点小改动,支持多GPU训练。如果是单卡,可以直接去掉base_model2 = make_parallel(base_model, 4)。

识别四则混合运算验证码(决赛)

多行,公式,大小不一

需要先经过图像处理,然后才能输入到神经网络中进行端到端的文本序列识别。

评价指标,决赛的数字通常都有5位数,并且会有很多乘法和加法,以及一定会存在的一个分数,所以结果很容易超出64位浮点数所能表示的范围,因此官方在经过讨论后只考虑文本序列的识别,不评价运算结果。

我们本地除了会使用官方的准确率作为评估标准以外,还会使用CTC Loss来评估模型。

示例:流=42072;圳=86;(圳-(97510*45921))*流/35864

中文除了“不”字出现了两次,概率翻倍,其他字概率基本相等。中文字取自于下面两句诗:“君不见,黄河之水天上来,奔流到海不复回 烟锁池塘柳,深圳铁板烧”,所以也可以推断出是按字直接随机取的。

数据预处理

主要使用以下几种技术,在第3章图像处理入门中基本都有所涉及:

首先进行初步的关键区域提取,操作步骤如下:

去噪;连接公式;关键区域提取;微调;连接三个公式;并行预处理;

模型与之前差不多,修改了base_model

为什么使用RNN

l2正则化的参数直接参考了Xception论文5.3节给的参数:

生成器,

模型的训练,Adam(1e-3)和Adam(1e-4)

预测结果,0.99868

其他尝试

不定长图像识别,也就是不定长的跨度;分别识别,考虑每行之间有上下文关系;生成器尝试,很难与官方给的图像一样。

其他CNN模型的尝试,除了自己搭模型,还尝试过用ResNet,DenseNet替换CNN,但是这些模型本身就很大,训练起来速度很慢,前面的val_loss一直在抖动,并且最终提交的效果又和浅层模型没有太大差别,所以为了快速尝试更多方案,舍弃了类似ResNet复杂模型。

替换GRU为LSTM,准确率有轻微下降,理论上这个序列长度并没有很长,GRU和LSTM影响不大。

11、使用全卷积神经网络分割病理切片中的癌细胞

赛题来自数愿组织的病理切片AI识别挑战赛初赛赛题(www.datadreams.org/race-race-3.html)

由于需要实现像素级别的图像分割,因此考虑使用全卷积神经网络(FCN)

在这种架构基础之上,有以下想法:

  • 图形分辨率很高,可以考虑将一张图片拆分为多张小图片进行模型训练。
  • 由于只有700张图片,并且是RGB图片,考虑引入迁移学习
  • 样本数量有限,可能会有过拟合,考虑在反卷积层中引入l2正则化
  • 数据增强

 

12、如何写一个深度学习APP

制作一个可以在iPhone上利用摄像头识别猫狗,并且可视化卷积神经网络关注的区域的一个应用程序。

CAM可视化(Class Activation Mapping,类激活图),原理是全局平均池化层(GlobalAveragePooling, GAP)通常用在CNN最后一层,用于降维,使用GAP层可以极大地降低特征的维度,使用全连接层参数不至于过多,保留了特征的强度信息,丢弃了特征的位置信息,因为是分类问题,对位置信息不敏感,所以使用GAP层来降维效果很好。周博磊指出,可以对CNN的输出加权平均,权重是GAP层到这个分类的权重。这样就能得到一个CAM可视化图。简单来说,可以在最后一层卷积层后面加一层。

在iOS 11中,可以直接使用Keras的模型,只需要使用苹果的模型转换库Core ML Tools将Keras以HDF5格式存储的模型转换为mlmodel格式即可。

使用Xcode编辑APP

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值