1、深度学习网络概述

1.1 深度学习网络简单介绍

        常见的深度学习网络由卷积层+池化层+全连接层等组成,通过构建多层卷积层和池化层,可以学习到一些有用的模型,一个简单的深度学习模型结构如下所示:

卷积层 -> 池化层 -> ... -> 卷积层 -> 池化层 -> 全连接层 -> 全连接层

        对应的函数层如下所示:

Conv2D -> Pool -> .. -> Conv2D -> Pool -> Flatten -> Dense -> Dense

2、Python代码示例

2.1 准备数据阶段(读入图片转成数组)

2.1.1 代码示例

import tensorflow as tf;
 import numpy as np;
 from PIL import Image;
 import matplotlib.pyplot as plt;imgObj = Image.open("D:/study/image/1.png");
plt.subplot(2,3,2);
 plt.imshow(imgObj);
 plt.title(label='layers 1: conv2d',loc='center');img = np.array(imgObj, dtype='float32');
# 一般彩色图片由RGB三色构成,所以读入图片后数组的原始维度是: HEIGHT * WIDTH * 3,其中3可以理解为图片的通道数
 # conv2d函数需要4维的输入,我们需要把图片3维数组转成4维的数组
 # 4维数组的维度依次是:BATCH_SIZE,HEIGHT,WIDTH,CHANNELS
 x_train = img[tf.newaxis,...];
 print("img.shape=", img.shape, ", x_train=", x_train.shape);

2.1.2 代码执行结果

因为图片本身宽和高是1200*675,又是彩色图片,所以数据维度输出如下:

BATCH_SIZE=1,图片只有一张;

HEIGHT=675,图片的高度;

WIDTH=1200,图片的宽度;

CHANNELS=3,彩色图片有RGB三色,所以通道数是3;

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_tensorflow

2.2 第1层:卷积运算Conv2D

2.2.1 代码示例

conv2d_1 = tf.keras.layers.Conv2D(filters=32, kernel_size=3,  strides=(1,1),
                                   padding='same', data_format='channels_last')(x_train);
 print("conv2d_1.shape=", conv2d_1.shape);
 # 打印卷积运算结果前3个通道的图片
 for i in range(3):
     showImg = conv2d_1[0,:,:,i];
     plt.subplot(2,3,4+i);
     plt.imshow(showImg, 'gray');
     plt.title(label="channel"+str((i+1)), loc='center');
 plt.show();

2.2.2 代码执行结果

在我们查看输出结果之前,需要简单介绍一下Conv2D函数常用的参数:

1、filters=32,这是通道数。原始彩色图片的通道是3,经过卷积运算后,通道数变成32;

2、kernel_size=3,这是卷积运算的核数。等价于kernel_size=(3,3),卷积运算从图片左上角开始,每次取3*3大小位置的数字,乘以一个3*3大小的过滤器(按元素位置对应相乘求和),重复计算到图片的右下角。

补充:图示卷积运算的计算过程

原始图片左上角4个元素,按位置乘以kernel的4个元素,得到输出的左上角位置的值2;

原始图片上方居中的4个元素,按位置乘以kernel的4个元素,得到输出的上方居中位置的值0;

原始图片右上角的4个元素,按位置乘以kernel的4个元素,得到输出的右上角位置的值-4;

同理,依次从左往右,从上到下读取原始图片的2*2元素,乘以kernel,最终得到一个3*3的输出。

深度学习auxiliaryhead 深度学习基础教程_深度学习_02


3、strides=(1,1),这是步长。步长是控制读取原始图片的移动步伐,strides=(1,1)的第一个值控制着从左往右的移动速度,1表示每次移动一格;第二个值则控制从上到下的移动速度,1表示每次移动一格;由此可以理解步长越大,移动速度越快,最终输出的尺寸就越小。

4、padding='same',这是控制上下左右是否填充0,取值same,表示要填充0,取值为valid,表示不填充0。下图是填充0的示意图,因为kernel=3,所以是上下左右各填充多一维0,如果kernel=5,那么上下左右就要各多填充二维0。填充维度计算公式:p = kernel / 2,这样才能保持输出尺寸不变(前提条件是步长为1,否则无法保证输出尺寸大小不变化)。

 

深度学习auxiliaryhead 深度学习基础教程_深度学习auxiliaryhead_03

 5、data_format='channels_last',这是指明通道是哪一维。取值channels_last,表示原始数据的通道在高度和宽度之后,数组维度是(BATCH_SIZE,HEIGHT,WIDTH,CHANNELS);取值channels_first,表示原始数据的通道在高度和宽度之前,数组维度是(BATCH_SIZE,CHANNELS,HEIGHT,WIDTH)。

 当然Conv2D还有其他参数,想了解更多知识,自己可以去查阅资料。

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_卷积_04

下图是第1层—卷积运算层,卷积运算结果前3个通道的输出效果图:

        

深度学习auxiliaryhead 深度学习基础教程_池化_05

 2.3 第2层:最大池化运算MaxPool2D

2.3.1 代码示例

pool_1 = tf.keras.layers.MaxPool2D(pool_size=(2,2), strides=(1,1), padding='same')(conv2d_1);
 print("pool_1.shape=", pool_1.shape);
 plt.subplot(2,3,2);
 plt.imshow(imgObj);
 plt.title(label='layers 2: pool',loc='center');
 # 打印最大池化结果前3通道的图片
 for i in range(3):
     showImg = pool_1[0,:,:,i];
     plt.subplot(2,3,4+i);
     plt.imshow(showImg, 'gray');
     plt.title(label="channel"+str((i+1)), loc='center');
 plt.show();

2.3.2 代码执行结果

在查看输出日志和效果图之前,先解释一些MaxPool2D函数的参数:

1、pool_size=(2,2),这是指定池化运算过滤器大小。下图是最大池化运算的示意图,从左往右,从上往下,依次取2*2的元素,取出当中的最大值,作为输出的内容。

深度学习auxiliaryhead 深度学习基础教程_深度学习_06

 2、strides=(1,1),这是步长。步长是控制读取原始图片的移动步伐,strides=(1,1)的第一个值控制着从左往右的移动速度,1表示每次移动一格;第二个值则控制从上到下的移动速度,1表示每次移动一格;由此可以理解步长越大,移动速度越快,最终输出的尺寸就越小。

注意:如果不设置步长,则步长默认取pool_size的值。

3、padding='same',这是控制上下左右是否填充0,取值same,表示要填充0,取值为valid,表示不填充0。pool_size=(2,2),上下左右各填充多一维0,如果pool_size=(4,4),那么上下左右就要各多填充二维0。填充维度计算公式:p = pool_size / 2,这样才能保持输出尺寸不变(前提条件是步长为1,否则无法保证输出尺寸大小不变化)。

下图是填充0的示意图:

 

深度学习auxiliaryhead 深度学习基础教程_tensorflow_07

 下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_深度学习_08

 下图是第2层—最大池化层,池化运算前3个通道的输出效果图:

深度学习auxiliaryhead 深度学习基础教程_tensorflow_09

 2.4 第3层:卷积运算Conv2D

2.4.1 代码示例

conv2d_2 = tf.keras.layers.Conv2D(filters=16, kernel_size=3, strides=(1,1),
                                   padding='same', data_format='channels_last')(pool_1);
 print("conv2d_2.shape=", conv2d_2.shape);
 plt.subplot(2,3,2);
 plt.imshow(imgObj);
 plt.title(label='layers 3: conv2d',loc='center');
 # 打印卷积结果前3通道的图片
 for i in range(3):
     showImg = conv2d_2[0,:,:,i];
     plt.subplot(2,3,4+i);
     plt.imshow(showImg, 'gray');
     plt.title(label="channel"+str((i+1)), loc='center');
 plt.show();

 2.4.2 代码执行结果

前面已经解释过函数的参数了,这里不再重复。

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_池化_10

 下图是第3层—卷积运算层,卷积运算结果前3个通道的输出效果图:

深度学习auxiliaryhead 深度学习基础教程_tensorflow_11

 2.5 第4层:最大池化运算MaxPool2D

2.5.1 代码示例

pool_2 = tf.keras.layers.MaxPool2D(pool_size=(2,2), strides=(1,1), padding='same')(conv2d_2);
 print("pool_2.shape=", pool_2.shape);
 plt.subplot(2,3,2);
 plt.imshow(imgObj);
 plt.title(label='layers 4: pool',loc='center');
 # 打印最大池化结果前3通道的图片
 for i in range(3):
     showImg = pool_2[0,:,:,i];
     plt.subplot(2,3,4+i);
     plt.imshow(showImg, 'gray');
     plt.title(label="channel"+str((i+1)), loc='center');
 plt.show();

2.5.2 代码执行结果

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_池化_12

 下图是第4层—最大池化运算层,最大池化运算结果前3个通道的输出效果图:

深度学习auxiliaryhead 深度学习基础教程_池化_13

 2.6 Flatten函数

2.6.1 代码示例

flatten = tf.keras.layers.Flatten(data_format='channels_last')(pool_2);
 print("flatten.shape=", flatten.shape);

2.6.1 代码执行结果

在看输出结果之前,先解释一下Flatten函数的参数:

1、data_format='channels_last',这是指明通道是哪一维。取值channels_last,表示原始数据的通道在高度和宽度之后,数组维度是(BATCH_SIZE,HEIGHT,WIDTH,CHANNELS);取值channels_first,表示原始数据的通道在高度和宽度之前,数组维度是(BATCH_SIZE,CHANNELS,HEIGHT,WIDTH)。

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_卷积_14

 2.7 第5层:全连接运算Dense

2.7.1 代码示例

dense_1 = tf.keras.layers.Dense(units=16, activation='relu')(flatten);
print("dense_1.shape=", dense_1.shape);

2.7.2 代码执行结果

在看输出结果之前,先解释一下Dense函数的参数:

1、units=16, 这是表示输出的维度,就是输出多少个节点;

2、activation='relu',这是激活函数,常见的激活函数在tf.keras.activations里面定义,这里使用relu激活函数。

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_池化_15

2.8 第6层:全连接运算Dense

2.8.1 代码示例

y_pred = tf.keras.layers.Dense(units=1, activation='sigmoid')(dense_1);
print("y_pred.shape=", y_pred.shape, ", y_pred=", y_pred);

2.8.2 代码执行结果

Dense函数之前已经解释过了,这里不再重复;

因为这已经模型是最后一层,所以我们直接输出模型预测结果。

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_卷积_16

2.9 计算误差

2.9.1 代码示例

# 标记图片是否是风景图
 y_true = np.array([1]);
 print("y_true.shape=", y_true.shape, ", y_true=", y_true);# 平均绝对误差
 loss = tf.keras.losses.MAE(y_true, y_pred);
 print("loss.shape=", loss.shape, ",loss=", loss);

2.9.2 代码运行结果

在看输出结果之前,我们先解释一下上面的代码:

1、y_true=[1],因为我们输入只有一张图片,所以只需要定义长度为1的数组,来标记图片的分类,我们约定值为1表示图片是风景图,那么值为0就不是风景图;

2、我们使用MAE(平均绝对误差),计算最终的误差值;常见的误差函数,定义在tf.keras.losses,大家可以阅读,选择合适的误差函数。

下图是运行输出日志:

深度学习auxiliaryhead 深度学习基础教程_卷积_17

 3 总结

1、Conv2D卷积层,负责学习图片一些特征。靠前的卷积网络层,可以学习到简单的特征(边界,位置等),后面的卷积层则在简单特征上,学习更复杂的特征(线条,形状等),从而实现功能强大的系统;

2、MaxPool2D池化层,一是可以放大占主导位置的特征,二是能够有效减少运算,本文设置strides=(1,1),所以没有缩小输出尺寸,实际应用过程中可以设置strides=(2,2),这样输出尺寸直接缩小为原图片的一半,极大地减少运算量;

3、可以在深度学习网络中,添加多层卷积层和池化层,让网络学习到更复杂的特征;

4、Flatten函数,是把多维数组平铺成1维数组,为了后续的全连接层而准备;

5、Dense全连接层,构造合适的输出结果;

6、误差函数,在训练过程可以根据训练误差,调整我们的模型参数;在测试/验证过程,可以记录测试误差,从而重新调整我们的训练模型;

文中只用极少的一部分函数和功能,更多的用法需要大家自行摸索。