计算机视觉(下)

  part2图像分类

目录 


图像分类是根据 图像的语义信息 对不同类别图像进行区分,计算机视觉的核心,是物体检测、图像分割、物体跟踪、行为分析、人脸识别等其他高层次视觉任务的基础。图像分类在许多领域都有着广泛的应用,如:

 

  • 安防领域的人脸识别和智能视频分析等
  • 交通领域的交通场景识别
  • 互联网领域基于内容的图像检索和相册自动归类
  • 医学领域的图像识别等

上一节主要介绍了卷积神经网络常用的一些基本模块,本节将基于眼疾分类数据集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的设计思想深刻地影响了后来的深度神经网络的设计。


LeNet

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


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

 

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

  • 第二模块:和第一模块尺寸相同,通道数由6增加为16。卷积操作使图像尺寸减小到10,经过池化后变成5。

  • 第三模块:包含5×5的120通道卷积。卷积之后的图像尺寸减小到1,但是通道数增加为120。将经过第3次卷积提取到的特征图输入到全连接层。第一个全连接层的输出神经元的个数是64,第二个全连接层的输出神经元个数是分类标签的类别数,对于手写数字识别其大小是10。然后使用Softmax激活函数即可计算出每个类别的预测概率


【提示】:

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

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

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

其中K=C×H×WK = C \times H \times WK=C×H×W,一个mini-batch的数据维度变成了N×KN\times KN×K的二维向量。


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

LeNet网络的实现代码如下:

 
  1. # 导入需要的包

  2. import paddle

  3. import paddle.fluid as fluid

  4. import numpy as np

  5. from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear

  6.  
  7. # 定义 LeNet 网络结构

  8. class LeNet(fluid.dygraph.Layer):

  9. def __init__(self, num_classes=1):

  10. super(LeNet, self).__init__()

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

  13. self.conv1 = Conv2D(num_channels=1, num_filters=6, filter_size=5, act='sigmoid')

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

  15. self.conv2 = Conv2D(num_channels=6, num_filters=16, filter_size=5, act='sigmoid')

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

  17. # 创建第3个卷积层

  18. self.conv3 = Conv2D(num_channels=16, num_filters=120, filter_size=4, act='sigmoid')

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

  20. self.fc1 = Linear(input_dim=120, output_dim=64, act='sigmoid')

  21. self.fc2 = Linear(input_dim=64, output_dim=num_classes)

  22. # 网络的前向计算过程

  23. def forward(self, x):

  24. x = self.conv1(x)

  25. x = self.pool1(x)

  26. x = self.conv2(x)

  27. x = self.pool2(x)

  28. x = self.conv3(x)

  29. x = fluid.layers.reshape(x, [x.shape[0], -1])

  30. x = self.fc1(x)

  31. x = self.fc2(x)

  32. return x

  33.  

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

 
  1. # 输入数据形状是 [N, 1, H, W]

  2. # 这里用np.random创建一个随机数组作为输入数据

  3. x = np.random.randn(*[3,1,28,28])

  4. x = x.astype('float32')

  5. with fluid.dygraph.guard():

  6. # 创建LeNet类的实例,指定模型名称和分类的类别数目

  7. m = LeNet(num_classes=10)

  8. # 通过调用LeNet从基类继承的sublayers()函数,

  9. # 查看LeNet中所包含的子层

  10. print(m.sublayers())

  11. x = fluid.dygraph.to_variable(x)

  12. for item in m.sublayers():

  13. # item是LeNet类中的一个子层

  14. # 查看经过子层之后的输出数据形状

  15. try:

  16. x = item(x)

  17. except:

  18. x = fluid.layers.reshape(x, [x.shape[0], -1])

  19. x = item(x)

  20. if len(item.parameters())==2:

  21. # 查看卷积和全连接层的数据和参数的形状,

  22. # 其中item.parameters()[0]是权重参数w,item.parameters()[1]是偏置参数b

  23. print(item.full_name(), x.shape, item.parameters()[0].shape, item.parameters()[1].shape)

  24. else:

  25. # 池化层没有参数

  26. print(item.full_name(), x.shape)

 
  1. [<paddle.fluid.dygraph.nn.Conv2D object at 0x7f1656aab770>, <paddle.fluid.dygraph.nn.Pool2D object at 0x7f1656aaba10>, <paddle.fluid.dygraph.nn.Conv2D object at 0x7f1656aabef0>, <paddle.fluid.dygraph.nn.Pool2D object at 0x7f1616bb1050>, <paddle.fluid.dygraph.nn.Conv2D object at 0x7f1616bb10b0>, <paddle.fluid.dygraph.nn.Linear object at 0x7f1616bb11d0>, <paddle.fluid.dygraph.nn.Linear object at 0x7f1616bb1350>]

  2. conv2d_0 [3, 6, 24, 24] [6, 1, 5, 5] [6]

  3. pool2d_0 [3, 6, 12, 12]

  4. conv2d_1 [3, 16, 8, 8] [16, 6, 5, 5] [16]

  5. pool2d_1 [3, 16, 4, 4]

  6. conv2d_2 [3, 120, 1, 1] [120, 16, 4, 4] [120]

  7. linear_0 [3, 64] [120, 64] [64]

  8. linear_1 [3, 10] [64, 10] [10]

LeNet 识别手写数字

 
  1. # -*- coding: utf-8 -*-

  2.  
  3. # LeNet 识别手写数字

  4.  
  5. import os

  6. import random

  7. import paddle

  8. import paddle.fluid as fluid

  9. import numpy as np

  10.  
  11. # 定义训练过程

  12. def train(model):

  13. print('start training ... ')

  14. model.train()

  15. epoch_num = 5

  16. opt = fluid.optimizer.Momentum(learning_rate=0.001, momentum=0.9, parameter_list=model.parameters())

  17. # 使用Paddle自带的数据读取器

  18. train_loader = paddle.batch(paddle.dataset.mnist.train(), batch_size=10)

  19. valid_loader = paddle.batch(paddle.dataset.mnist.test(), batch_size=10)

  20. for epoch in range(epoch_num):

  21. for batch_id, data in enumerate(train_loader()):

  22. # 调整输入数据形状和类型

  23. x_data = np.array([item[0] for item in data], dtype='float32').reshape(-1, 1, 28, 28)

  24. y_data = np.array([item[1] for item in data], dtype='int64').reshape(-1, 1)

  25. # 将numpy.ndarray转化成Tensor

  26. img = fluid.dygraph.to_variable(x_data)

  27. label = fluid.dygraph.to_variable(y_data)

  28. # 计算模型输出

  29. logits = model(img)

  30. # 计算损失函数

  31. loss = fluid.layers.softmax_with_cross_entropy(logits, label)

  32. avg_loss = fluid.layers.mean(loss)

  33. if batch_id % 1000 == 0:

  34. print("epoch: {}, batch_id: {}, loss is: {}".format(epoch, batch_id, avg_loss.numpy()))

  35. avg_loss.backward()

  36. opt.minimize(avg_loss)

  37. model.clear_gradients()

  38.  
  39. model.eval()

  40. accuracies = []

  41. losses = []

  42. for batch_id, data in enumerate(valid_loader()):

  43. # 调整输入数据形状和类型

  44. x_data = np.array([item[0] for item in data], dtype='float32').reshape(-1, 1, 28, 28)

  45. y_data = np.array([item[1] for item in data], dtype='int64').reshape(-1, 1)

  46. # 将numpy.ndarray转化成Tensor

  47. img = fluid.dygraph.to_variable(x_data)

  48. label = fluid.dygraph.to_variable(y_data)

  49. # 计算模型输出

  50. logits = model(img)

  51. pred = fluid.layers.softmax(logits)

  52. # 计算损失函数

  53. loss = fluid.layers.softmax_with_cross_entropy(logits, label)

  54. acc = fluid.layers.accuracy(pred, label)

  55. accuracies.append(acc.numpy())

  56. losses.append(loss.numpy())

  57. print("[validation] accuracy/loss: {}/{}".format(np.mean(accuracies), np.mean(losses)))

  58. model.train()

  59.  
  60. # 保存模型参数

  61. fluid.save_dygraph(model.state_dict(), 'mnist')

  62.  
  63.  
  64. if __name__ == '__main__':

  65. # 创建模型

  66. with fluid.dygraph.guard():

  67. model = LeNet(num_classes=10)

  68. #启动训练过程

  69. train(model)

 
  1. start training ...

  2. Cache file /home/aistudio/.cache/paddle/dataset/mnist/train-images-idx3-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/train-images-idx3-ubyte.gz

  3. Begin to download

  4.  
  5. Download finished

  6. Cache file /home/aistudio/.cache/paddle/dataset/mnist/train-labels-idx1-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/train-labels-idx1-ubyte.gz

  7. Begin to download

  8. ........

  9. Download finished

  10. Cache file /home/aistudio/.cache/paddle/dataset/mnist/t10k-images-idx3-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/t10k-images-idx3-ubyte.gz

  11. Begin to download

  12.  
  13. Download finished

  14. Cache file /home/aistudio/.cache/paddle/dataset/mnist/t10k-labels-idx1-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/t10k-labels-idx1-ubyte.gz

  15. Begin to download

  16. ..

  17. Download finished

  18. epoch: 0, batch_id: 0, loss is: [2.4476852]

  19. epoch: 0, batch_id: 1000, loss is: [2.2948332]

  20. epoch: 0, batch_id: 2000, loss is: [2.334548]

  21. epoch: 0, batch_id: 3000, loss is: [2.283103]

  22. epoch: 0, batch_id: 4000, loss is: [2.2783697]

  23. epoch: 0, batch_id: 5000, loss is: [2.3309145]

  24. [validation] accuracy/loss: 0.10530000925064087/2.2899725437164307

  25. epoch: 1, batch_id: 0, loss is: [2.2863708]

  26. epoch: 1, batch_id: 1000, loss is: [2.276087]

  27. epoch: 1, batch_id: 2000, loss is: [2.3081589]

  28. epoch: 1, batch_id: 3000, loss is: [2.2395322]

  29. epoch: 1, batch_id: 4000, loss is: [2.2013073]

  30. epoch: 1, batch_id: 5000, loss is: [2.257289]

  31. [validation] accuracy/loss: 0.554900050163269/2.004681348800659

  32. epoch: 2, batch_id: 0, loss is: [1.9385501]

  33. epoch: 2, batch_id: 1000, loss is: [1.5589781]

  34. epoch: 2, batch_id: 2000, loss is: [1.3764482]

  35. epoch: 2, batch_id: 3000, loss is: [0.8067332]

  36. epoch: 2, batch_id: 4000, loss is: [0.6518642]

  37. epoch: 2, batch_id: 5000, loss is: [0.77211165]

  38. [validation] accuracy/loss: 0.8388999700546265/0.6187658905982971

  39. epoch: 3, batch_id: 0, loss is: [0.41283408]

  40. epoch: 3, batch_id: 1000, loss is: [0.40613115]

  41. epoch: 3, batch_id: 2000, loss is: [0.4012765]

  42. epoch: 3, batch_id: 3000, loss is: [0.15993488]

  43. epoch: 3, batch_id: 4000, loss is: [0.27918053]

  44. epoch: 3, batch_id: 5000, loss is: [0.23777664]

  45. [validation] accuracy/loss: 0.9035999774932861/0.357360303401947

  46. epoch: 4, batch_id: 0, loss is: [0.23886052]

  47. epoch: 4, batch_id: 1000, loss is: [0.2531582]

  48. epoch: 4, batch_id: 2000, loss is: [0.25078607]

  49. epoch: 4, batch_id: 3000, loss is: [0.0727628]

  50. epoch: 4, batch_id: 4000, loss is: [0.15234414]

  51. epoch: 4, batch_id: 5000, loss is: [0.11065292]

  52. [validation] accuracy/loss: 0.9294999241828918/0.25718018412590027

  53. </
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值