零基础入门深度学习六图像分类任务之L

课程名称

零基础入门深度学习授课讲师

孙高峰百度深度学习技术平台部资深研发工程师授课时间

每周二、周四晚20:00-21:00

01导读

本课程是百度官方开设的零基础入门深度学习课程,主要面向没有深度学习技术基础或者基础薄弱的同学,帮助大家在深度学习领域实现从0到1+的跨越。从本课程中,你将学习到:

深度学习基础知识numpy实现神经网络构建和梯度下降算法计算机视觉领域主要方向的原理、实践自然语言处理领域主要方向的原理、实践个性化推荐算法的原理、实践本周为开讲第四周,百度深度学习技术平台部资深研发工程师孙高峰,开始讲解计算机视觉中图像分类任务。

02图像分类概述

图像分类是根据图像的语义信息对不同类别图像进行区分,是计算机视觉中重要的基础问题,是物体检测、图像分割、物体跟踪、行为分析、人脸识别等其他高层次视觉任务的基础。图像分类在许多领域都有着广泛的应用,如:安防领域的人脸识别和智能视频分析等,交通领域的交通场景识别,互联网领域基于内容的图像检索和相册自动归类,医学领域的图像识别等。

上一节主要介绍了卷积神经网络常用的一些基本模块,本节将基于眼疾分类数据集iChallenge-PM,对图像分类领域的经典卷积神经网络进行剖析,介绍如何应用这些基础模块构建卷积神经网络,解决图像分类问题。涵盖如下卷积神经网络:

LeNet:YanLeCun等人于年第一次将卷积神经网络应用到图像分类任务上[1],在手写数字识别任务上取得了巨大成功。AlexNet:AlexKrizhevsky等人在年提出了AlexNet[2],并应用在大尺寸图片数据集ImageNet上,获得了年ImageNet比赛冠军(ImageNetLargeScaleVisualRecognitionChallenge,ILSVRC)。VGG:Simonyan和Zisserman于年提出了VGG网络结构[3],是当前最流行的卷积神经网络之一,由于其结构简单、应用性极强而深受广受研究者欢迎。GoogLeNet:ChristianSzegedy等人在提出了GoogLeNet[4],并取得了年ImageNet比赛冠军。ResNet:KaimingHe等人在年提出了ResNet[5],通过引入残差模块加深网络层数,在ImagNet数据集上的识别错误率降低到3.6%,超越了人眼识别水平。ResNet的设计思想深刻的影响了后来的深度神经网络的设计。03LeNet

LeNet是最早的卷积神经网络之一[1]。年,YanLeCun第一次将LeNet卷积神经网络应用到图像分类上,在手写数字识别任务中取得了巨大成功。LeNet通过连续使用卷积和池化层的组合提取图像特征,其架构如图1所示,这里展示的是作者论文中的LeNet-5模型:

图1:LeNet模型网络结构示意图

第一轮卷积和池化:卷积提取图像中包含的特征模式(激活函数使用sigmoid),图像尺寸从32减小到28。经过池化层可以降低输出特征图对空间位置的敏感性,图像尺寸减到14。第二轮卷积和池化:卷积操作使图像尺寸减小到10,经过池化后变成5。第三轮卷积:将经过第3次卷积提取到的特征图输入到全连接层。第一个全连接层的输出神经元的个数是64,第二个全连接层的输出神经元个数是分类标签的类别数,对于手写数字识别其大小是10。然后使用Softmax激活函数即可计算出每个类别的预测概率。:

卷积层的输出特征图如何当作全连接层的输入使用呢?

卷积层的输出数据格式是,在输入全连接层的时候,会自动将数据拉平,

也就是对每个样本,自动将其转化为长度为的向量,

其中,一个mini-batch的数据维度变成了的二维向量。

03LeNet在手写数字识别上的应用

LeNet网络的实现代码如下:

#导入需要的包

importpaddle

importpaddle.fluidasfluid

importnumpyasnp

frompaddle.fluid.dygraph.nnimportConv2D,Pool2D,FC

#定义LeNet网络结构

classLeNet(fluid.dygraph.Layer):

def__init__(self,name_scope,num_classes=1):

super(LeNet,self).__init__(name_scope)

name_scope=self.full_name()

#创建卷积和池化层块,每个卷积层使用Sigmoid激活函数,后面跟着一个2x2的池化

self.conv1=Conv2D(name_scope,num_filters=6,filter_size=5,act=sigmoid)

self.pool1=Pool2D(name_scope,pool_size=2,pool_stride=2,pool_type=max)

self.conv2=Conv2D(name_scope,num_filters=16,filter_size=5,act=sigmoid)

self.pool2=Pool2D(name_scope,pool_size=2,pool_stride=2,pool_type=max)

#创建第3个卷积层

self.conv3=Conv2D(name_scope,num_filters=,filter_size=4,act=sigmoid)

#创建全连接层,第一个全连接层的输出神经元个数为64,第二个全连接层输出神经元个数为分裂标签的类别数

self.fc1=FC(name_scope,size=64,act=sigmoid)

self.fc2=FC(name_scope,size=num_classes)

#网络的前向计算过程

defforward(self,x):

x=self.conv1(x)

x=self.pool1(x)

x=self.conv2(x)

x=self.pool2(x)

x=self.conv3(x)

x=self.fc1(x)

x=self.fc2(x)

returnx

程序使用随机数作为输入,查看经过LeNet-5的每一层作用之后,输出数据的形状

04LeNet在眼疾识别数据集iChallenge-PM上的应用

iChallenge-PM是百度大脑和中山大学中山眼科中心联合举办的iChallenge比赛中,提供的关于病理性近视(PathologicMyopia,PM)的医疗类数据集,包含0个受试者的眼底视网膜图片,训练、验证和测试数据集各张。下面我们详细介绍LeNet在iChallenge-PM上的训练过程。

说明:

如今近视已经成为困扰人们健康的一项全球性负担,在近视人群中,有超过35%的人患有重度近视。近视将会导致眼睛的光轴被拉长,有可能引起视网膜或者络网膜的病变。随着近视度数的不断加深,高度近视有可能引发病理性病变,这将会导致以下几种症状:视网膜或者络网膜发生退化、视盘区域萎缩、漆裂样纹损害、Fuchs斑等。因此,及早发现近视患者眼睛的病变并采取治疗,显得非常重要。

数据可以从AIStudio下载

示例图片如下

数据集准备

/home/aistudio/data/data目录包括如下三个文件,解压缩后存放在/home/aistudio/work/palm目录下。

training.zip:包含训练中的图片和标签validation.zip:包含验证集的图片valid_gt.zip:包含验证集的标签注意:

valid_gt.zip文件解压缩之后,需要将/home/aistudio/work/palm/PALM-Validation-GT/目录下的PM_Label_and_Fovea_Location.xlsx文件转存成csv格式,本节代码示例中已经提前转成文件labels.csv。

#初次运行时将注释取消,以便解压文件

#如果已经解压过了,则不需要运行此段代码,否则文件已经存在解压会报错

#!unzip-d/home/aistudio/work/palm/home/aistudio/data/data/training.zip

#%cd/home/aistudio/work/palm/PALM-Training/

#!unzipPALM-Training.zip

#!unzip-d/home/aistudio/work/palm/home/aistudio/data/data/validation.zip

#!unzip-d/home/aistudio/work/palm/home/aistudio/data/data/valid_gt.zip

查看数据集图片

iChallenge-PM中既有病理性近视患者的眼底图片,也有非病理性近视患者的图片,命名规则如下:

病理性近视(PM):文件名以P开头非病理性近视(non-PM):高度近视(highmyopia):文件名以H开头正常眼睛(normal):文件名以N开头我们将病理性患者的图片作为正样本,标签为1;非病理性患者的图片作为负样本,标签为0。从数据集中选取两张图片,通过LeNet提取特征,构建分类器,对正负样本进行分类,并将图片显示出来。代码如下所示:

importos

importnumpyasnp

importmatplotlib.pyplotasplt

%matplotlibinline

fromPILimportImage

DATADIR=/home/aistudio/work/palm/PALM-Training/PALM-Training

#文件名以N开头的是正常眼底图片,以P开头的是病变眼底图片

file1=N.jpg

file2=P.jpg

#读取图片

img1=Image.open(os.path.join(DATADIR,file1))

img1=np.array(img1)

img2=Image.open(os.path.join(DATADIR,file2))

img2=np.array(img2)

#画出读取的图片

plt.figure(figsize=(16,8))

f=plt.subplot()

f.set_title(Normal,fontsize=20)

plt.imshow(img1)

f=plt.subplot()

f.set_title(PM,fontsize=20)

plt.imshow(img2)

plt.show()

#查看图片形状

img1.shape,img2.shape

定义数据读取器

使用OpenCV从磁盘读入图片,将每张图缩放到大小,并且将像素值调整到之间,代码如下所示:

importcv2

importrandom

importnumpyasnp

#对读入的图像数据进行预处理

deftransform_img(img):

#将图片尺寸缩放道x

img=cv2.resize(img,(,))

#读入的图像数据格式是[H,W,C]

#使用转置操作将其变成[C,H,W]

img=np.transpose(img,(2,0,1))

img=img.astype(float32)

#将数据范围调整到[-1.0,1.0]之间

img=img/.

img=img*2.0-1.0

returnimg

#定义训练集数据读取器

defdata_loader(datadir,batch_size=10,mode=train):

#将datadir目录下的文件列出来,每条文件都要读入

filenames=os.listdir(datadir)

defreader():

ifmode==train:

#训练时随机打乱数据顺序

random.shuffle(filenames)

batch_imgs=[]

batch_labels=[]

fornameinfilenames:

filepath=os.path.join(datadir,name)

img=cv2.imread(filepath)

img=transform_img(img)

ifname[0]==Horname[0]==N:

#H开头的文件名表示高度近视,N开头的文件名表示正常视力

#高度近视和正常视力的样本,都不是病理性的,属于负样本,标签为0

label=0

elifname[0]==P:

#P开头的是病理性近视,属于正样本,标签为1

label=1

else:

raise(Notexceptedfilename)

#每读取一个样本的数据,就将其放入数据列表中

batch_imgs.append(img)

batch_labels.append(label)

iflen(batch_imgs)==batch_size:

#当数据列表的长度等于batch_size的时候,

#把这些数据当作一个mini-batch,并作为数据生成器的一个输出

imgs_array=np.array(batch_imgs).astype(float32)

labels_array=np.array(batch_labels).astype(float32).reshape(-1,1)

yieldimgs_array,labels_array

batch_imgs=[]

batch_labels=[]

iflen(batch_imgs)0:

#剩余样本数目不足一个batch_size的数据,一起打包成一个mini-batch

imgs_array=np.array(batch_imgs).astype(float32)

labels_array=np.array(batch_labels).astype(float32).reshape(-1,1)

yieldimgs_array,labels_array

returnreader

#定义验证集数据读取器

defvalid_data_loader(datadir,csvfile,batch_size=10,mode=valid):

#训练集读取时通过文件名来确定样本标签,验证集则通过csvfile来读取每个图片对应的标签

#请查看解压后的验证集标签数据,观察csvfile文件里面所包含的内容

#csvfile文件所包含的内容格式如下,每一行代表一个样本,

#其中第一列是图片id,第二列是文件名,第三列是图片标签,

#第四列和第五列是Fovea的坐标,与分类任务无关

#ID,imgName,Label,Fovea_X,Fovea_Y

#1,V.jpg,0,.74,.87

#2,V.jpg,1,.82,.47

#打开包含验证集标签的csvfile,并读入其中的内容

filelists=open(csvfile).readlines()

defreader():

batch_imgs=[]

batch_labels=[]

forlineinfilelists[1:]:

line=line.strip().split(,)

name=line[1]

label=int(line[2])

#根据图片文件名加载图片,并对图像数据作预处理

filepath=os.path.join(datadir,name)

img=cv2.imread(filepath)

img=transform_img(img)

#每读取一个样本的数据,就将其放入数据列表中

batch_imgs.append(img)

batch_labels.append(label)

iflen(batch_imgs)==batch_size:

#当数据列表的长度等于batch_size的时候,

#把这些数据当作一个mini-batch,并作为数据生成器的一个输出

imgs_array=np.array(batch_imgs).astype(float32)

labels_array=np.array(batch_labels).astype(float32).reshape(-1,1)

yieldimgs_array,labels_array

batch_imgs=[]

batch_labels=[]

iflen(batch_imgs)0:

#剩余样本数目不足一个batch_size的数据,一起打包成一个mini-batch

imgs_array=np.array(batch_imgs).astype(float32)

labels_array=np.array(batch_labels).astype(float32).reshape(-1,1)

yieldimgs_array,labels_array

returnreader

#查看数据形状

DATADIR=/home/aistudio/work/palm/PALM-Training/PALM-Training

train_loader=data_loader(DATADIR,

batch_size=10,mode=train)

data_reader=train_loader()

data=next(data_reader)

data[0].shape,data[1].shap

通过运行结果可以看出,在眼疾筛查数据集iChallenge-PM上,LeNet的loss很难下降,模型没有收敛。这是因为MNIST数据集的图片尺寸比较小(),但是眼疾筛查数据集图片尺寸比较大(原始图片尺寸约为,经过缩放之后变成),LeNet模型很难进行有效分类。这说明在图片尺寸比较大时,LeNet在图像分类任务上存在局限性。

05AlexNet

通过上面的实际训练可以看到,虽然LeNet在手写数字识别数据集上取得了很好的结果,但在更大的数据集上表现却并不好。自从年LeNet问世以来,接下来十几年的时间里,神经网络并没有在计算机视觉领域取得很好的结果,反而一度被其它算法所超越,原因主要有两方面,一是神经网络的计算比较复杂,对当时计算机的算力来说,训练神经网络是件非常耗时的事情;另一方面,当时还没有专门针对神经网络做算法和训练技巧的优化,神经网络的收敛性是件非常困难的事情。

随着技术的进步和发展,计算机的算力越来越强大,尤其是在GPU并行计算能力的推动下,复杂神经网络的计算也变得更加容易实施。另一方面,互联网上涌现出越来越多的数据,极大的丰富了数据库。同时也有越来越多的研究人员开始专门针对神经网络做算法和模型的优化,AlexKrizhevsky等人提出的AlexNet以很大优势获得了年ImageNet比赛的冠军。这一成果极大的激发了业界对神经网络的兴趣,开创了使用深度神经网络解决图像问题的途径,随后也在这一领域涌现出越来越多的优秀工作。

AlexNet与LeNet相比,具有更深的网络结构,包含5层卷积和3层全连接,同时使用了如下三种方法改进模型的训练过程:

数据增多:深度学习中常用的一种处理方式,通过对训练随机加一些变化,比如平移、缩放、裁剪、旋转、翻转或者增减亮度等,产生一系列跟原始图片相似但又不完全相同的样本,从而扩大训练数据集。通过这种方式,可以随机改变训练样本,避免模型过度依赖于某些属性,能从一定程度上抑制过拟合。使用Dropout抑制过拟合使用ReLU激活函数少梯度消失现象说明:

下一节详细介绍数据增多的具体实现方式。

AlexNet的具体结构如图2所示:

图2:AlexNet模型网络结构示意图

AlexNet在眼疾筛查数据集iChallenge-PM上具体实现的代码如下所示:

#-*-coding:utf-8-*-

#导入需要的包

importpaddle

importpaddle.fluidasfluid

importnumpyasnp

frompaddle.fluid.dygraph.nnimportConv2D,Pool2D,FC

#定义AlexNet网络结构

classAlexNet(fluid.dygraph.Layer):

def__init__(self,name_scope,num_classes=1):

super(AlexNet,self).__init__(name_scope)

name_scope=self.full_name()

#AlexNet与LeNet一样也会同时使用卷积和池化层提取图像特征

#与LeNet不同的是激活函数换成了‘relu’

self.conv1=Conv2D(name_scope,num_filters=96,filter_size=11,stride=4,padding=5,act=relu)

self.pool1=Pool2D(name_scope,pool_size=2,pool_stride=2,pool_type=max)

self.conv2=Conv2D(name_scope,num_filters=,filter_size=5,stride=1,padding=2,act=relu)

self.pool2=Pool2D(name_scope,pool_size=2,pool_stride=2,pool_type=max)

self.conv3=Conv2D(name_scope,num_filters=,filter_size=3,stride=1,padding=1,act=relu)

self.conv4=Conv2D(name_scope,num_filters=,filter_size=3,stride=1,padding=1,act=relu)

self.conv5=Conv2D(name_scope,num_filters=,filter_size=3,stride=1,padding=1,act=relu)

self.pool5=Pool2D(name_scope,pool_size=2,pool_stride=2,pool_type=max)

self.fc1=FC(name_scope,size=,act=relu)

self.drop_ratio1=0.5

self.fc2=FC(name_scope,size=,act=relu)

self.drop_ratio2=0.5

self.fc3=FC(name_scope,size=num_classes)

defforward(self,x):

x=self.conv1(x)

x=self.pool1(x)

x=self.conv2(x)

x=self.pool2(x)

x=self.conv3(x)

x=self.conv4(x)

x=self.conv5(x)

x=self.pool5(x)

x=self.fc1(x)

#在全连接之后使用dropout抑制过拟合

x=fluid.layers.dropout(x,self.drop_ratio1)

x=self.fc2(x)

#在全连接之后使用dropout抑制过拟合

x=fluid.layers.dropout(x,self.drop_ratio2)

x=self.fc3(x)

returnx

withfluid.dygraph.guard():

model=AlexNet(AlexNet)

train(model)

通过运行结果可以发现,在眼疾筛查数据集iChallenge-PM上使用AlexNet,loss能有效下降,经过5个epoch的训练,在验证集上的准确率可以达到94%左右。

06总结

本周课程中孙老师主要为大家讲解了计算机视觉中分类任务的主要内容,以眼疾识别任务为例,分别介绍了经典的LeNet和AlexNet神经网络结构。在后期课程中,将继续为大家带来内容更丰富的课程,帮助学员快速掌握深度学习方法。

如何观看配套视频?如何代码实践?视频+代码已经发布在AIStudio实践平台上,视频支持PC端/手机端同步观看,也鼓励大家亲手体验运行代码哦。打开以下链接:




转载请注明:http://www.qianzhuangguoji.net/zlff/11287.html

  • 上一篇文章:
  •   
  • 下一篇文章: 没有了