​零基础入门深度学习(六):图像分类任务之LeNet和AlexNet

课程名称 | 零基础入门深度学习

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

授课时间 | 每周二、周四晚20:00-21:00

  

01

导读

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

  1. 深度学习基础知识

  2. numpy实现神经网络构建和梯度下降算法

  3. 计算机视觉领域主要方向的原理、实践

  4. 自然语言处理领域主要方向的原理、实践

  5. 个性化推荐算法的原理、实践

本周为开讲第四周,百度深度学习技术平台部资深研发工程师孙高峰,开始讲解计算机视觉中图像分类任务。

02

图像分类概述

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

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

  • LeNet:Yan LeCun等人于1998年第一次将卷积神经网络应用到图像分类任务上[1],在手写数字识别任务上取得了巨大成功。

  • AlexNet:Alex Krizhevsky等人在2012年提出了AlexNet[2], 并应用在大尺寸图片数据集ImageNet上,获得了2012年ImageNet比赛冠军(ImageNet Large Scale Visual Recognition Challenge,ILSVRC)。

  • VGG:Simonyan和Zisserman于2014年提出了VGG网络结构[3],是当前最流行的卷积神经网络之一,由于其结构简单、应用性极强而深受广受研究者欢迎。

  • GoogLeNet:Christian Szegedy等人在2014提出了GoogLeNet[4],并取得了2014年ImageNet比赛冠军。

  • ResNet:Kaiming He等人在2015年提出了ResNet[5],通过引入残差模块加深网络层数,在ImagNet数据集上的识别错误率降低到3.6%,超越了人眼识别水平。ResNet的设计思想深刻的影响了后来的深度神经网络的设计。

03

LeNet

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


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

  • 第一轮卷积和池化:卷积提取图像中包含的特征模式(激活函数使用sigmoid),图像尺寸从32减小到28。经过池化层可以降低输出特征图对空间位置的敏感性,图像尺寸减到14。

  • 第二轮卷积和池化:卷积操作使图像尺寸减小到10,经过池化后变成5。

  • 第三轮卷积:将经过第3次卷积提取到的特征图输入到全连接层。第一个全连接层的输出神经元的个数是64,第二个全连接层的输出神经元个数是分类标签的类别数,对于手写数字识别其大小是10。然后使用Softmax激活函数即可计算出每个类别的预测概率。


【提示】:

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

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

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

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


03

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

LeNet网络的实现代码如下:

# 导入需要的包import paddleimport paddle.fluid as fluidimport numpy as npfrom paddle.fluid.dygraph.nn import Conv2D, Pool2D, FC
# 定义 LeNet 网络结构class LeNet(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=120, filter_size=4, act='sigmoid')        # 创建全连接层,第一个全连接层的输出神经元个数为64, 第二个全连接层输出神经元个数为分裂标签的类别数        self.fc1 = FC(name_scope, size=64, act='sigmoid')        self.fc2 = FC(name_scope, size=num_classes)    # 网络的前向计算过程    def forward(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)        return x

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

# 输入数据形状是 [N, 3, H, W]# 这里用np.random创建一个随机数组作为输入数据x = np.random.randn(*[3,3,28,28])x = x.astype('float32')with fluid.dygraph.guard():    # 创建LeNet类的实例,指定模型名称和分类的类别数目    m = LeNet('LeNet', num_classes=10)    # 通过调用LeNet从基类继承的sublayers()函数,    # 查看LeNet中所包含的子层    print(m.sublayers())    x = fluid.dygraph.to_variable(x)    for item in m.sublayers():        # item是LeNet类中的一个子层        # 查看经过子层之后的输出数据形状        x = item(x)        if len(item.parameters())==2:            # 查看卷积和全连接层的数据和参数的形状,            # 其中item.parameters()[0]是权重参数w,item.parameters()[1]是偏置参数b            print(item.full_name(), x.shape, item.parameters()[0].shape, item.parameters()[1].shape)        else:            # 池化层没有参数            print(item.full_name(), x.shape)
# -*- coding: utf-8 -*-# LeNet 识别手写数字import osimport randomimport paddleimport paddle.fluid as fluidimport numpy as np# 定义训练过程def train(model):    print('start training ... ')    model.train()    epoch_num = 5    opt = fluid.optimizer.Momentum(learning_rate=0.001, momentum=0.9)    # 使用Paddle自带的数据读取器    train_loader = paddle.batch(paddle.dataset.mnist.train(), batch_size=10)    valid_loader = paddle.batch(paddle.dataset.mnist.test(), batch_size=10)    for epoch in range(epoch_num):        for batch_id, data in enumerate(train_loader()):            # 调整输入数据形状和类型            x_data = np.array([item[0] for item in data], dtype='float32').reshape(-1, 1, 28, 28)            y_data = np.array([item[1] for item in data], dtype='int64').reshape(-1, 1)            # 将numpy.ndarray转化成Tensor            img = fluid.dygraph.to_variable(x_data)            label = fluid.dygraph.to_variable(y_data)            # 计算模型输出            logits = model(img)            # 计算损失函数            loss = fluid.layers.softmax_with_cross_entropy(logits, label)            avg_loss = fluid.layers.mean(loss)            if batch_id % 1000 == 0:                print("epoch: {}, batch_id: {}, loss is: {}".format(epoch, batch_id, avg_loss.numpy()))            avg_loss.backward()            opt.minimize(avg_loss)            model.clear_gradients()        model.eval()        accuracies = []        losses = []        for batch_id, data in enumerate(valid_loader()):            # 调整输入数据形状和类型            x_data = np.array([item[0] for item in data], dtype='float32').reshape(-1, 1, 28, 28)            y_data = np.array([item[1] for item in data], dtype='int64').reshape(-1, 1)            # 将numpy.ndarray转化成Tensor            img = fluid.dygraph.to_variable(x_data)            label = fluid.dygraph.to_variable(y_data)            # 计算模型输出            logits = model(img)            pred = fluid.layers.softmax(logits)            # 计算损失函数            loss = fluid.layers.softmax_with_cross_entropy(logits, label)            acc = fluid.layers.accuracy(pred, label)            accuracies.append(acc.numpy())            losses.append(loss.numpy())        print("[validation] accuracy/loss: {}/{}".format(np.mean(accuracies), np.mean(losses)))        model.train()    # 保存模型参数    fluid.save_dygraph(model.state_dict(), 'mnist')if __name__ == '__main__':    # 创建模型    with fluid.dygraph.guard():        model = LeNet("LeNet", num_classes=10)        #启动训练过程        train(model)

通过运行结果可以看出,LeNet在手写数字识别MNIST验证数据集上的准确率高达92%以上。那么对于其它数据集效果如何呢?我们通过眼疾识别数据集iChallenge-PM验证一下。

04

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

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


说明:

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

数据可以从AIStudio下载

示例图片如下


数据集准备

/home/aistudio/data/data19065 目录包括如下三个文件,解压缩后存放在/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/data19065/training.zip#%cd /home/aistudio/work/palm/PALM-Training400/#!unzip PALM-Training400.zip#!unzip -d /home/aistudio/work/palm /home/aistudio/data/data19065/validation.zip#!unzip -d /home/aistudio/work/palm /home/aistudio/data/data19065/valid_gt.zip

查看数据集图片

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

  • 病理性近视(PM):文件名以P开头

  • 非病理性近视(non-PM):

    • 高度近视(high myopia):文件名以H开头

    • 正常眼睛(normal):文件名以N开头

我们将病理性患者的图片作为正样本,标签为1;非病理性患者的图片作为负样本,标签为0。从数据集中选取两张图片,通过LeNet提取特征,构建分类器,对正负样本进行分类,并将图片显示出来。代码如下所示:

import osimport numpy as npimport matplotlib.pyplot as plt%matplotlib inlinefrom PIL import Image
DATADIR = '/home/aistudio/work/palm/PALM-Training400/PALM-Training400'# 文件名以N开头的是正常眼底图片,以P开头的是病变眼底图片file1 = 'N0012.jpg'file2 = 'P0095.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(121)f.set_title('Normal', fontsize=20)plt.imshow(img1)f = plt.subplot(122)f.set_title('PM', fontsize=20)plt.imshow(img2)plt.show()
# 查看图片形状img1.shape, img2.shape

定义数据读取器

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

import cv2import randomimport numpy as np
# 对读入的图像数据进行预处理def transform_img(img):    # 将图片尺寸缩放道 224x224    img = cv2.resize(img, (224, 224))    # 读入的图像数据格式是[H, W, C]    # 使用转置操作将其变成[C, H, W]    img = np.transpose(img, (2,0,1))    img = img.astype('float32')    # 将数据范围调整到[-1.0, 1.0]之间    img = img / 255.    img = img * 2.0 - 1.0    return img
# 定义训练集数据读取器def data_loader(datadir, batch_size=10, mode = 'train'):    # 将datadir目录下的文件列出来,每条文件都要读入    filenames = os.listdir(datadir)    def reader():        if mode == 'train':            # 训练时随机打乱数据顺序            random.shuffle(filenames)        batch_imgs = []        batch_labels = []        for name in filenames:            filepath = os.path.join(datadir, name)            img = cv2.imread(filepath)            img = transform_img(img)            if name[0] == 'H' or name[0] == 'N':                # H开头的文件名表示高度近视,N开头的文件名表示正常视力                # 高度近视和正常视力的样本,都不是病理性的,属于负样本,标签为0                label = 0            elif name[0] == 'P':                # P开头的是病理性近视,属于正样本,标签为1                label = 1            else:                raise('Not excepted file name')            # 每读取一个样本的数据,就将其放入数据列表中            batch_imgs.append(img)            batch_labels.append(label)            if len(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)                yield imgs_array, labels_array                batch_imgs = []                batch_labels = []
        if len(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)            yield imgs_array, labels_array
    return reader
# 定义验证集数据读取器def valid_data_loader(datadir, csvfile, batch_size=10, mode='valid'):    # 训练集读取时通过文件名来确定样本标签,验证集则通过csvfile来读取每个图片对应的标签    # 请查看解压后的验证集标签数据,观察csvfile文件里面所包含的内容    # csvfile文件所包含的内容格式如下,每一行代表一个样本,    # 其中第一列是图片id,第二列是文件名,第三列是图片标签,    # 第四列和第五列是Fovea的坐标,与分类任务无关    # ID,imgName,Label,Fovea_X,Fovea_Y    # 1,V0001.jpg,0,1157.74,1019.87    # 2,V0002.jpg,1,1285.82,1080.47    # 打开包含验证集标签的csvfile,并读入其中的内容    filelists = open(csvfile).readlines()    def reader():        batch_imgs = []        batch_labels = []        for line in filelists[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)            if len(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)                yield imgs_array, labels_array                batch_imgs = []                batch_labels = []
        if len(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)            yield imgs_array, labels_array
    return reader

# 查看数据形状DATADIR = '/home/aistudio/work/palm/PALM-Training400/PALM-Training400'train_loader = data_loader(DATADIR,                            batch_size=10, mode='train')data_reader = train_loader()data = next(data_reader)data[0].shape, data[1].shape
启动训练
# -*- coding: utf-8 -*-# LeNet 识别眼疾图片import osimport randomimport paddleimport paddle.fluid as fluidimport numpy as npDATADIR = '/home/aistudio/work/palm/PALM-Training400/PALM-Training400'DATADIR2 = '/home/aistudio/work/palm/PALM-Validation400'CSVFILE = '/home/aistudio/work/palm/PALM-Validation-GT/labels.csv'# 定义训练过程def train(model):    with fluid.dygraph.guard():        print('start training ... ')        model.train()        epoch_num = 5        # 定义优化器        opt = fluid.optimizer.Momentum(learning_rate=0.001, momentum=0.9)        # 定义数据读取器,训练数据读取器和验证数据读取器        train_loader = data_loader(DATADIR, batch_size=10, mode='train')        valid_loader = valid_data_loader(DATADIR2, CSVFILE)        for epoch in range(epoch_num):            for batch_id, data in enumerate(train_loader()):                x_data, y_data = data                img = fluid.dygraph.to_variable(x_data)                label = fluid.dygraph.to_variable(y_data)                # 运行模型前向计算,得到预测值                logits = model(img)                # 进行loss计算                loss = fluid.layers.sigmoid_cross_entropy_with_logits(logits, label)                avg_loss = fluid.layers.mean(loss)                if batch_id % 10 == 0:                    print("epoch: {}, batch_id: {}, loss is: {}".format(epoch, batch_id, avg_loss.numpy()))                # 反向传播,更新权重,清除梯度                avg_loss.backward()                opt.minimize(avg_loss)                model.clear_gradients()            model.eval()            accuracies = []            losses = []            for batch_id, data in enumerate(valid_loader()):                x_data, y_data = data                img = fluid.dygraph.to_variable(x_data)                label = fluid.dygraph.to_variable(y_data)                # 运行模型前向计算,得到预测值                logits = model(img)                # 二分类,sigmoid计算后的结果以0.5为阈值分两个类别                # 计算sigmoid后的预测概率,进行loss计算                pred = fluid.layers.sigmoid(logits)                loss = fluid.layers.sigmoid_cross_entropy_with_logits(logits, label)                # 计算预测概率小于0.5的类别                pred2 = pred * (-1.0) + 1.0                # 得到两个类别的预测概率,并沿第一个维度级联                pred = fluid.layers.concat([pred2, pred], axis=1)                acc = fluid.layers.accuracy(pred, fluid.layers.cast(label, dtype='int64'))                accuracies.append(acc.numpy())                losses.append(loss.numpy())            print("[validation] accuracy/loss: {}/{}".format(np.mean(accuracies), np.mean(losses)))            model.train()        # save params of model        fluid.save_dygraph(model.state_dict(), 'mnist')        # save optimizer state        fluid.save_dygraph(opt.state_dict(), 'mnist')# 定义评估过程def evaluation(model, params_file_path):    with fluid.dygraph.guard():        print('start evaluation .......')        #加载模型参数        model_state_dict, _ = fluid.load_dygraph(params_file_path)        model.load_dict(model_state_dict)        model.eval()        eval_loader = load_data('eval')        acc_set = []        avg_loss_set = []        for batch_id, data in enumerate(eval_loader()):            x_data, y_data = data            img = fluid.dygraph.to_variable(x_data)            label = fluid.dygraph.to_variable(y_data)            # 计算预测和精度            prediction, acc = model(img, label)            # 计算损失函数值            loss = fluid.layers.cross_entropy(input=prediction, label=label)            avg_loss = fluid.layers.mean(loss)            acc_set.append(float(acc.numpy()))            avg_loss_set.append(float(avg_loss.numpy()))        # 求平均精度        acc_val_mean = np.array(acc_set).mean()        avg_loss_val_mean = np.array(avg_loss_set).mean()        print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))if __name__ == '__main__':    # 创建模型    with fluid.dygraph.guard():        model = LeNet("LeNet", num_classes=1)    train(model)

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

05

AlexNet

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

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

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

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

  • 使用Dropout抑制过拟合

  • 使用ReLU激活函数少梯度消失现象


说明:

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


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


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

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

# -*- coding:utf-8 -*-
# 导入需要的包import paddleimport paddle.fluid as fluidimport numpy as npfrom paddle.fluid.dygraph.nn import Conv2D, Pool2D, FC

# 定义 AlexNet 网络结构class AlexNet(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=256, 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=384, filter_size=3, stride=1, padding=1, act='relu')        self.conv4 = Conv2D(name_scope, num_filters=384, filter_size=3, stride=1, padding=1, act='relu')        self.conv5 = Conv2D(name_scope, num_filters=256, 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=4096, act='relu')        self.drop_ratio1 = 0.5        self.fc2 = FC(name_scope, size=4096, act='relu')        self.drop_ratio2 = 0.5        self.fc3 = FC(name_scope, size=num_classes)

    def forward(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)        return x
with fluid.dygraph.guard():    model = AlexNet("AlexNet")
train(model)

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

06

总结

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

【如何学习】

  1. 如何观看配套视频?如何代码实践?

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

https://aistudio.baidu.com/aistudio/course/introduce/888

  1. 学习过程中,有疑问怎么办?

加入深度学习集训营QQ群:726887660,班主任与飞桨研发会在群里进行答疑与学习资料发放。

  1. 如何学习更多内容?

百度飞桨将通过飞桨深度学习集训营的形式,继续更新《零基础入门深度学习》课程,由百度深度学习高级研发工程师亲自授课,每周二、每周四8:00-9:00不见不散,采用直播+录播+实践+答疑的形式,欢迎关注~

请搜索AI Studio,点击课程-百度架构师手把手教深度学习,或者点击文末「阅读原文」收看。

参考文献

[1] Yann LeCun, Léon Bottou, Yoshua Bengio, and Patrick Haffner. Gradient-based learn- ing applied to document recognition. Proc. of the IEEE, 86(11):2278–2324, 1998

[2] Alex Krizhevsky, Ilya Sutskever, and Geoffrey E. Hinton. Imagenet classification with deep convolutional neural networks. In Advances in Neural Information Processing Systems, pages 1097–1105, 2012.

[3] Karen Simonyan and Andrew Zisserman. Very deep convolutional networks for large-scale image recognition. arXiv preprint arXiv:1409.1556, 2014b.

[4]Christian Szegedy, Wei Liu, Yangqing Jia, Pierre Sermanet, Scott Reed, Dragomir Anguelov, Dumitru Erhan, Vincent Vanhoucke, and Andrew Rabinovich. Going deeper with convolu- tions. In Proc. of the IEEE Conference on Computer Vision and Pattern Recognition, pages 1–9, 2015.

[5] Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. Deep residual learning for im- age recognition. In Proc. of the IEEE Conference on Computer Vision and Pattern Recognition, pages 770–778, 2016a.

欢迎讨论

如果将LeNet中中间层的激活函数Sigmoid换成ReLU,在眼底筛查数据集上将会得到什么样的结果?Loss是否能收敛,ReLU和Sigmoid之间的区别是引起结果不同的原因吗?请发表你的观点

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值