当前位置:   article > 正文

TensorFlow之卷积神经网络_conv(3,64,7,3,2)

conv(3,64,7,3,2)

一、概念

卷积可以认为是一种有效提取图像特征的方法。

一般会用一个正方形的 卷积核,按指定步长,在输入特征图上滑动,遍历输入特征图中的每个像素点。每一个步长, 卷积核会与输入特征图出现重合区域,重合区域对应元素相乘、求和再加上偏置项得到输出 特征的一个像素点。

卷积核通道数与输入特征的通道数一致。

卷积核的个数决定输出特征图的深度。

感受野:卷积神经网络各输出层每个像素点在原始图像上 的映射区域大小

全零填充(padding):为了保持输出图像尺寸与输入图像一致,经常会在输入图像 周围进行全零填充,padding = ‘SAME’时进行填充,padding = ‘VALID’时则不进行填充。

二、模型构建基本知识

  1. tf.keras.layers.Conv2D(
  2. input_shape = (高, 宽, 通道数), #仅在第一层有
  3. filters = 卷积核个数,
  4. kernel_size = 卷积核尺寸,
  5. strides = 卷积步长,
  6. padding = 'SAME' or 'VALID',
  7. activation = 'relu' or 'sigmoid' or 'tanh' or 'softmax'#如有 BN 则此处不用写
  8. )

Batch Normalization(批标准化):对一小批数据在网络各层的输出做标准化处理,都调整到均值为 0,方差为 1 的标准正态分 布,其目的是解决神经网络中梯度消失的问题。(当 training = True 时,BN 操作采用当前 batch 的均值和标准差;当 training = False 时,BN 操作采用滑动平均的均值和标准差。)

tf.keras.layers.BatchNormalization()

池化(pooling):池化的作用是减少特征数量(降维)。最大值池化可提取图片纹 理,均值池化可保留背景特征。

 

  1. tf.keras.layers.MaxPool2D(
  2. pool_size = 池化核尺寸,
  3. strides = 池化步长,
  4. padding = 'SAME' or 'VALID'
  5. )
  6. tf.keras.layers.AveragePooling2D(
  7. pool_size = 池化核尺寸,
  8. strides = 池化步长,
  9. padding = 'SAME' or 'VALID'
  10. )

舍弃(Dropout):在神经网络的训练过程中,将一部分神经元按照一定概率从神经 网络中暂时舍弃,使用时被舍弃的神经元恢复链接.

tf.keras.layers.Dropout 

三、模型构建

核心思路为在 CNN 中利用卷积核(kernel)提取特征后,送入全连接网络。

利用 tf.keras.Sequential模型以及 class 定义两种方式都可以构建,但后者在实际应用中更加常用,因为一些复杂的网络经常会有 Sequential 模型无法表达的结构或设计。

  1. class Baseline(Model):
  2. def __init__(self):
  3. super(Baseline, self).__init__()
  4. self.c1 = Conv2D(filters=6, kernel_size=(5, 5), padding='same') # 卷积层
  5. self.b1 = BatchNormalization() # BN层
  6. self.a1 = Activation('relu') # 激活层
  7. self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same') # 池化层
  8. self.d1 = Dropout(0.2) # dropout层
  9. self.flatten = Flatten()
  10. self.f1 = Dense(128, activation='relu')
  11. self.d2 = Dropout(0.2)
  12. self.f2 = Dense(10, activation='softmax')
  13. def call(self, x):
  14. x = self.c1(x)
  15. x = self.b1(x)
  16. x = self.a1(x)
  17. x = self.p1(x)
  18. x = self.d1(x)
  19. x = self.flatten(x)
  20. x = self.f1(x)
  21. x = self.d2(x)
  22. y = self.f2(x)
  23. return y
  24. model = Baseline()

四、CNN经典网络

1.LeNet

借鉴点:共享卷积核,减少网络参数。

经典思路:卷积提取特征→全连接分类

 

  1. class LeNet5(Model):
  2. def __init__(self):
  3. super(LeNet5, self).__init__()
  4. self.c1 = Conv2D(filters=6, kernel_size=(5, 5),
  5. activation='sigmoid')
  6. self.p1 = MaxPool2D(pool_size=(2, 2), strides=2)
  7. self.c2 = Conv2D(filters=16, kernel_size=(5, 5),
  8. activation='sigmoid')
  9. self.p2 = MaxPool2D(pool_size=(2, 2), strides=2)
  10. self.flatten = Flatten()
  11. self.f1 = Dense(120, activation='sigmoid')
  12. self.f2 = Dense(84, activation='sigmoid')
  13. self.f3 = Dense(10, activation='softmax')
  14. def call(self, x):
  15. x = self.c1(x)
  16. x = self.p1(x)
  17. x = self.c2(x)
  18. x = self.p2(x)
  19. x = self.flatten(x)
  20. x = self.f1(x)
  21. x = self.f2(x)
  22. y = self.f3(x)
  23. return y
  24. model = LeNet5()

2.AlexNet

借鉴点:激活函数使用 Relu,提升训练速度;Dropout 防止过拟合。

  1. class AlexNet8(Model):
  2. def __init__(self):
  3. super(AlexNet8, self).__init__()
  4. self.c1 = Conv2D(filters=96, kernel_size=(3, 3))
  5. self.b1 = BatchNormalization()
  6. self.a1 = Activation('relu')
  7. self.p1 = MaxPool2D(pool_size=(3, 3), strides=2)
  8. self.c2 = Conv2D(filters=256, kernel_size=(3, 3))
  9. self.b2 = BatchNormalization()
  10. self.a2 = Activation('relu')
  11. self.p2 = MaxPool2D(pool_size=(3, 3), strides=2)
  12. self.c3 = Conv2D(filters=384, kernel_size=(3, 3), padding='same',
  13. activation='relu')
  14. self.c4 = Conv2D(filters=384, kernel_size=(3, 3), padding='same',
  15. activation='relu')
  16. self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same',
  17. activation='relu')
  18. self.p3 = MaxPool2D(pool_size=(3, 3), strides=2)
  19. self.flatten = Flatten()
  20. self.f1 = Dense(2048, activation='relu')
  21. self.d1 = Dropout(0.5)
  22. self.f2 = Dense(2048, activation='relu')
  23. self.d2 = Dropout(0.5)
  24. self.f3 = Dense(10, activation='softmax')
  25. def call(self, x):
  26. x = self.c1(x)
  27. x = self.b1(x)
  28. x = self.a1(x)
  29. x = self.p1(x)
  30. x = self.c2(x)
  31. x = self.b2(x)
  32. x = self.a2(x)
  33. x = self.p2(x)
  34. x = self.c3(x)
  35. x = self.c4(x)
  36. x = self.c5(x)
  37. x = self.p3(x)
  38. x = self.flatten(x)
  39. x = self.f1(x)
  40. x = self.d1(x)
  41. x = self.f2(x)
  42. x = self.d2(x)
  43. y = self.f3(x)
  44. return y
  45. model = AlexNet8()

3.VGGNet

借鉴点:小卷积核减少参数的同时,提高识别准确率;网络结构规整,适合并行加速。

  1. class VGG16(Model):
  2. def __init__(self):
  3. super(VGG16, self).__init__()
  4. self.c1 = Conv2D(filters=64, kernel_size=(3, 3), padding='same') # 卷积层1
  5. self.b1 = BatchNormalization() # BN层1
  6. self.a1 = Activation('relu') # 激活层1
  7. self.c2 = Conv2D(filters=64, kernel_size=(3, 3), padding='same', )
  8. self.b2 = BatchNormalization() # BN层1
  9. self.a2 = Activation('relu') # 激活层1
  10. self.p1 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
  11. self.d1 = Dropout(0.2) # dropout层
  12. self.c3 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
  13. self.b3 = BatchNormalization() # BN层1
  14. self.a3 = Activation('relu') # 激活层1
  15. self.c4 = Conv2D(filters=128, kernel_size=(3, 3), padding='same')
  16. self.b4 = BatchNormalization() # BN层1
  17. self.a4 = Activation('relu') # 激活层1
  18. self.p2 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
  19. self.d2 = Dropout(0.2) # dropout层
  20. self.c5 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
  21. self.b5 = BatchNormalization() # BN层1
  22. self.a5 = Activation('relu') # 激活层1
  23. self.c6 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
  24. self.b6 = BatchNormalization() # BN层1
  25. self.a6 = Activation('relu') # 激活层1
  26. self.c7 = Conv2D(filters=256, kernel_size=(3, 3), padding='same')
  27. self.b7 = BatchNormalization()
  28. self.a7 = Activation('relu')
  29. self.p3 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
  30. self.d3 = Dropout(0.2)
  31. self.c8 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
  32. self.b8 = BatchNormalization() # BN层1
  33. self.a8 = Activation('relu') # 激活层1
  34. self.c9 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
  35. self.b9 = BatchNormalization() # BN层1
  36. self.a9 = Activation('relu') # 激活层1
  37. self.c10 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
  38. self.b10 = BatchNormalization()
  39. self.a10 = Activation('relu')
  40. self.p4 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
  41. self.d4 = Dropout(0.2)
  42. self.c11 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
  43. self.b11 = BatchNormalization() # BN层1
  44. self.a11 = Activation('relu') # 激活层1
  45. self.c12 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
  46. self.b12 = BatchNormalization() # BN层1
  47. self.a12 = Activation('relu') # 激活层1
  48. self.c13 = Conv2D(filters=512, kernel_size=(3, 3), padding='same')
  49. self.b13 = BatchNormalization()
  50. self.a13 = Activation('relu')
  51. self.p5 = MaxPool2D(pool_size=(2, 2), strides=2, padding='same')
  52. self.d5 = Dropout(0.2)
  53. self.flatten = Flatten()
  54. self.f1 = Dense(512, activation='relu')
  55. self.d6 = Dropout(0.2)
  56. self.f2 = Dense(512, activation='relu')
  57. self.d7 = Dropout(0.2)
  58. self.f3 = Dense(10, activation='softmax')
  59. def call(self, x):
  60. x = self.c1(x)
  61. x = self.b1(x)
  62. x = self.a1(x)
  63. x = self.c2(x)
  64. x = self.b2(x)
  65. x = self.a2(x)
  66. x = self.p1(x)
  67. x = self.d1(x)
  68. x = self.c3(x)
  69. x = self.b3(x)
  70. x = self.a3(x)
  71. x = self.c4(x)
  72. x = self.b4(x)
  73. x = self.a4(x)
  74. x = self.p2(x)
  75. x = self.d2(x)
  76. x = self.c5(x)
  77. x = self.b5(x)
  78. x = self.a5(x)
  79. x = self.c6(x)
  80. x = self.b6(x)
  81. x = self.a6(x)
  82. x = self.c7(x)
  83. x = self.b7(x)
  84. x = self.a7(x)
  85. x = self.p3(x)
  86. x = self.d3(x)
  87. x = self.c8(x)
  88. x = self.b8(x)
  89. x = self.a8(x)
  90. x = self.c9(x)
  91. x = self.b9(x)
  92. x = self.a9(x)
  93. x = self.c10(x)
  94. x = self.b10(x)
  95. x = self.a10(x)
  96. x = self.p4(x)
  97. x = self.d4(x)
  98. x = self.c11(x)
  99. x = self.b11(x)
  100. x = self.a11(x)
  101. x = self.c12(x)
  102. x = self.b12(x)
  103. x = self.a12(x)
  104. x = self.c13(x)
  105. x = self.b13(x)
  106. x = self.a13(x)
  107. x = self.p5(x)
  108. x = self.d5(x)
  109. x = self.flatten(x)
  110. x = self.f1(x)
  111. x = self.d6(x)
  112. x = self.f2(x)
  113. x = self.d7(x)
  114. y = self.f3(x)
  115. return y
  116. model = VGG16()

4.InceptionNet

借鉴点:一层内使用不同尺寸的卷积核,提升感知力(通过 padding 实现输出特征面积一致); 使用 1 * 1 卷积核,改变输出特征 channel 数(减少网络参数)。

缺点:当网络深度不断增加时,训练会 十分困难,甚至无法收敛

 

 

  1. class ConvBNRelu(Model):
  2. def __init__(self, ch, kernelsz=3, strides=1, padding='same'):
  3. super(ConvBNRelu, self).__init__()
  4. self.model = tf.keras.models.Sequential([
  5. Conv2D(ch, kernelsz, strides=strides, padding=padding),
  6. BatchNormalization(),
  7. Activation('relu')
  8. ])
  9. def call(self, x):
  10. x = self.model(x, training=False)
  11. return x
  12. class InceptionBlk(Model):
  13. def __init__(self, ch, strides=1):
  14. super(InceptionBlk, self).__init__()
  15. self.ch = ch
  16. self.strides = strides
  17. self.c1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
  18. self.c2_1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
  19. self.c2_2 = ConvBNRelu(ch, kernelsz=3, strides=1)
  20. self.c3_1 = ConvBNRelu(ch, kernelsz=1, strides=strides)
  21. self.c3_2 = ConvBNRelu(ch, kernelsz=5, strides=1)
  22. self.p4_1 = MaxPool2D(3, strides=1, padding='same')
  23. self.c4_2 = ConvBNRelu(ch, kernelsz=1, strides=strides)
  24. def call(self, x):
  25. x1 = self.c1(x)
  26. x2_1 = self.c2_1(x)
  27. x2_2 = self.c2_2(x2_1)
  28. x3_1 = self.c3_1(x)
  29. x3_2 = self.c3_2(x3_1)
  30. x4_1 = self.p4_1(x)
  31. x4_2 = self.c4_2(x4_1)
  32. # concat along axis=channel
  33. x = tf.concat([x1, x2_2, x3_2, x4_2], axis=3)
  34. return x
  35. class Inception10(Model):
  36. def __init__(self, num_blocks, num_classes, init_ch=16, **kwargs):
  37. super(Inception10, self).__init__(**kwargs)
  38. self.in_channels = init_ch
  39. self.out_channels = init_ch
  40. self.num_blocks = num_blocks
  41. self.init_ch = init_ch
  42. self.c1 = ConvBNRelu(init_ch)
  43. self.blocks = tf.keras.models.Sequential()
  44. for block_id in range(num_blocks):
  45. for layer_id in range(2):
  46. if layer_id == 0:
  47. block = InceptionBlk(self.out_channels, strides=2)
  48. else:
  49. block = InceptionBlk(self.out_channels, strides=1)
  50. self.blocks.add(block)
  51. # enlarger out_channels per block
  52. self.out_channels *= 2
  53. self.p1 = GlobalAveragePooling2D()
  54. self.f1 = Dense(num_classes, activation='softmax')
  55. def call(self, x):
  56. x = self.c1(x)
  57. x = self.blocks(x)
  58. x = self.p1(x)
  59. y = self.f1(x)
  60. return y
  61. model = Inception10(num_blocks=2, num_classes=10)

5.ResNet

借鉴点:层间残差跳连,引入前方信息,减少梯度消失,使神经网络层数变身成为可能。

 

  1. class ResnetBlock(Model):
  2. def __init__(self, filters, strides=1, residual_path=False):
  3. super(ResnetBlock, self).__init__()
  4. self.filters = filters
  5. self.strides = strides
  6. self.residual_path = residual_path
  7. self.c1 = Conv2D(filters, (3, 3), strides=strides, padding='same', use_bias=False)
  8. self.b1 = BatchNormalization()
  9. self.a1 = Activation('relu')
  10. self.c2 = Conv2D(filters, (3, 3), strides=1, padding='same', use_bias=False)
  11. self.b2 = BatchNormalization()
  12. # residual_path为True时,对输入进行下采样,即用1x1的卷积核做卷积操作,保证x能和F(x)维度相同,顺利相加
  13. if residual_path:
  14. self.down_c1 = Conv2D(filters, (1, 1), strides=strides, padding='same', use_bias=False)
  15. self.down_b1 = BatchNormalization()
  16. self.a2 = Activation('relu')
  17. def call(self, inputs):
  18. residual = inputs # residual等于输入值本身,即residual=x
  19. # 将输入通过卷积、BN层、激活层,计算F(x)
  20. x = self.c1(inputs)
  21. x = self.b1(x)
  22. x = self.a1(x)
  23. x = self.c2(x)
  24. y = self.b2(x)
  25. if self.residual_path:
  26. residual = self.down_c1(inputs)
  27. residual = self.down_b1(residual)
  28. out = self.a2(y + residual) # 最后输出的是两部分的和,即F(x)+x或F(x)+Wx,再过激活函数
  29. return out
  30. class ResNet18(Model):
  31. def __init__(self, block_list, initial_filters=64): # block_list表示每个block有几个卷积层
  32. super(ResNet18, self).__init__()
  33. self.num_blocks = len(block_list) # 共有几个block
  34. self.block_list = block_list
  35. self.out_filters = initial_filters
  36. self.c1 = Conv2D(self.out_filters, (3, 3), strides=1, padding='same', use_bias=False)
  37. self.b1 = BatchNormalization()
  38. self.a1 = Activation('relu')
  39. self.blocks = tf.keras.models.Sequential()
  40. # 构建ResNet网络结构
  41. for block_id in range(len(block_list)): # 第几个resnet block
  42. for layer_id in range(block_list[block_id]): # 第几个卷积层
  43. if block_id != 0 and layer_id == 0: # 对除第一个block以外的block的输入采样
  44. block = ResnetBlock(self.out_filters, strides=2, residual_path=True)
  45. else:
  46. block = ResnetBlock(self.out_filters, residual_path=False)
  47. self.blocks.add(block) # 将构建好的block加入resnet
  48. self.out_filters *= 2 # 下一个block的卷积核数是上一个block的2倍
  49. self.p1 = tf.keras.layers.GlobalAveragePooling2D()
  50. self.f1 = tf.keras.layers.Dense(10, activation='softmax',
  51. kernel_regularizer=tf.keras.regularizers.l2())
  52. def call(self, inputs):
  53. x = self.c1(inputs)
  54. x = self.b1(x)
  55. x = self.a1(x)
  56. x = self.blocks(x)
  57. x = self.p1(x)
  58. y = self.f1(x)
  59. return y
  60. model = ResNet18([2, 2, 2, 2])

五、总结

另外,些训练方法和超参数的设定对模型训练结果的影响是相当显著的,以 ResNet18 为例,如果采取合适的训练技巧,cifar10的识别准确率是足以突破 90%的。所以,在神经网络的训练中,除了选择合适的模型以外,如何更好地训练一个模型也是一个非常值得探究的问题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/559635
推荐阅读
相关标签
  

闽ICP备14008679号