0. tf.keras与Keras的关系与区别:
Kras是Python编写的开源神经网络库。可以作为Tensorflow,Microsoft-CNTK和Theano的高阶应用程序接口,进行深度学的模型的设计,调试,评估,应用和可视化。它被设计为高度模块化和容扩展的高层神经网络接口。可以使得用户不需要过多的专业知识就可以简介,快速的完成模型的搭建和训练。
Keras库分为前端和后端,期中后端一般是调用现有的深度学习框架实现底层运算,前端接口是Keras抽象过的统一接口函数。
2015年,Tensorflow就被加入Keras后端支持。2019年,Tensorflow2版本中,Keras被证实确定为Tensorflow的高层唯一接口API。 取代了Tensorflow1版本中自带的tf.layers等高层接口。这就是tf.keras子模块。
1. tf.keras简介:
tf.keras是Tensorflow2.0的高阶API接口。为Tensorflow的代码提供了新的风格和设计模式。大大提升了TF代码的简洁性和复用性。官方也推荐使用tf.keras来进行模型的设计和开发。
常用模块:
模块 概述
activations 激活函数
applications 预训练网络模块
Callbacks 在模型训练期间被调用
datasets tf.keras数据集模块,包括boston_housing,cifar10,fashion_mnist,imdb ,mnist
layers Keras层API
losses 各种损失函数
metircs 各种评价指标
models 模型创建模块,以及与模型相关的API
optimizers 优化方法
preprocessing Keras数据的预处理模块
regularizers 正则化,L1,L2等
utils 辅助功能实现
2. 使用tf.keras进行模型构建:
有三种方法进行构建。
A. 简单模型使用Sequential进行构建。
B. 复杂模型使用函数式变成进行构建。
C. 自定义Layers (model的子类)构建.
2.1:使用Sequential构建模型:
tf.keras是一个神经网络库。我们可以用它构建相应的神经网络。期中简单的神经网络如人工神经网络ANN这样简单的模型,可以用Sequential构建。
tf.keras.Sequenial模型是层的线性堆叠。它的构造函数会采用一系列层实例;
例如:Layer1是输入层,Layer4是输出层。Layer2,Layer是隐藏层。
如图所示,可以构建2个密集层,分别包含5个节点,1个输出层,包含4个预测节点。第一个层的input_shape参数对应该数据集中的特征数量。
import tensorflow as tf from tensorflow.keras.models import Sequential # import tensorflow.keras.utils from tensorflow.keras import utils from tensorflow.keras.layers import Dense
model = Sequential([ #第一个隐藏层, 5个神经元,输入为3个特征. Dense表明是全连接层 Dense(5, activation="relu", input_shape=(3,)), #第二个隐藏层, 5个神经元 Dense(5, activation="relu"), #输出层, 4个分类。激活函数使用softmax Dense(4, activation="softmax") ], name = "My_Network" )
model.summary() # utils.plot_model(model, to_file='model.png', show_shapes=True, show_dtype=True,show_layer_names=True)
构建结果如下:
Model: "My_Network"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 5) 20
dense_1 (Dense) (None, 5) 30
dense_2 (Dense) (None, 4) 24
=================================================================
Total params: 74
Trainable params: 74
Non-trainable params: 0
_________________________________________________________________
2.2:利用tf.keras提供的API建立较为复杂的模型:
建立于2.1同样的模型。使用Function API的方式。 import tensorflow as tf import tensorflow.keras as keras import tensorflow.keras.layers as layers #定义模型输入 Input_Layer = keras.Input(shape=(3,), name="input_1") #第一个隐藏层 x = layers.Dense(5, activation = "relu", name="Layer1")(Input_Layer) #第二个隐藏层 x = layers.Dense(5, activation="relu", name="Layer2")(x) #输出层 Output_Layer = layers.Dense(4, activation ="softmax", name="Output")(x) #创建模型 model = keras.Model(inputs = Input_Layer, outputs=Output_Layer, name="Functional API Model") model.summary()
结果:
Model: "Functional API Model"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) [(None, 3)] 0
Layer1 (Dense) (None, 5) 20
Layer2 (Dense) (None, 5) 30
Output (Dense) (None, 4) 24
=================================================================
Total params: 74
Trainable params: 74
Non-trainable params: 0
_________________________________________________________________
2.3: model的子类进行构建:
通过model的子类构建模型,在__init__中定义神经网络的层,在call方法中一定网络的前向传播方法。
import tensorflow as tf import tensorflow.keras as keras import tensorflow.keras.layers as layers class My_Models(keras.Model): #定义网络层结构 def __init__(self): super(My_Models, self).__init__() #第一个隐藏层 self.layer1 = layers.Dense(5, activation="relu", name="Layer1") # 第二个隐藏层 self.layer2 = layers.Dense(5, activation="relu", name="Layer2") #定义输出层 self.output_layer = layers.Dense(4, activation="softmax", name="Output") #定义网络的前向传播 def __call__(self, inputs): x = self.layer1(inputs) x = self.layer2(x) outputs = self.output_layer(x) return outputs #实例化 model1 = My_Models() #设置输入 x = tf.ones((1, 4)) y = model1(x)
3. 使用tf.keras构建卷积神经网络:
3.1: AlexNet网络架构:
AlexNet使用了8层神经网络(5层卷积,2层全连接层,1个全连接输出层),以很大的优势赢得了ImageNet2012图像识别挑战赛,它首次证明了神经网络学习到的特征可以超越手工设计的特征。从而改变了计算机视觉研究的方向。(在此前,是研究如何找到更好的特征。此后,则是研究如何设计网络,以让网络能够更好的获取特征)
8层神经网络(5层卷积,2层全连接层,1个全连接输出层)
卷积核:第一个11x11. 第二个5x5.后面几个都是3x3.
池化:3x3. stride=2.
激活函数使用relu.
全连接层加入dropout.
import tensorflow as tf import tensorflow.keras as keras from tensorflow.keras.models import Sequential import tensorflow.keras.layers as layers AlexNet = Sequential([ #第一个卷积层11x11x96. 步长4.激活函数relu layers.Conv2D(filters=96, kernel_size=11, strides=4, activation="relu"), #池化 3x3. strides=2 layers.MaxPool2D(pool_size=3, strides=2), #第二个卷积层5x5x256 padding=2 layers.Conv2D(filters=256, kernel_size=5, padding="same", activation="relu"), #池化 3x3. strides=2 layers.MaxPool2D(pool_size=3, strides=2), #第三个卷积层3x3x384. pad=1 layers.Conv2D(filters=384, kernel_size=3, padding="same", activation="relu"), #第四个卷积层3x3x384. pad=1 layers.Conv2D(filters=384, kernel_size=3, padding="same", activation="relu"), #第五个卷积层3x3x256. pad=1 layers.Conv2D(filters=256, kernel_size=3, padding="same", activation="relu"), #池化 3x3. strides=2 layers.MaxPool2D(pool_size=3, strides=2), #Flatten layers.Flatten(), #FC1. 4096. relu layers.Dense(4096, activation="relu"), layers.Dropout(0.5), #FC2 4096 relu layers.Dense(4096, activation="relu"), #Output layers.Dense(10, activation="softmax") ], name = "My_AlexNet" ) # AlexNet.build(input_shape=[None,227,227,3]) # NHWC x = tf.random.uniform((1,227,227,1)) y = AlexNet(x) AlexNet.summary()
结果:
Model: "My_AlexNet"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (1, 55, 55, 96) 11712
max_pooling2d (MaxPooling2D (1, 27, 27, 96) 0
)
conv2d_1 (Conv2D) (1, 27, 27, 256) 614656
max_pooling2d_1 (MaxPooling (1, 13, 13, 256) 0
2D)
conv2d_2 (Conv2D) (1, 13, 13, 384) 885120
conv2d_3 (Conv2D) (1, 13, 13, 384) 1327488
conv2d_4 (Conv2D) (1, 13, 13, 256) 884992
max_pooling2d_2 (MaxPooling (1, 6, 6, 256) 0
2D)
flatten (Flatten) (1, 9216) 0
dense (Dense) (1, 4096) 37752832
dropout (Dropout) (1, 4096) 0
dense_1 (Dense) (1, 4096) 16781312
dense_2 (Dense) (1, 10) 40970
=================================================================
Total params: 58,299,082
Trainable params: 58,299,082
Non-trainable params: 0
_________________________________________________________________
3.2: ResNet网络架构:
ResNet是2015年ImageNet冠军。 错误率低于人类。
理论上,网络越深,获取的信息就越多,特征也越丰富,效果应该越好。但在实践中,随着网络的层数增加,有时优化效果反而越差。针对这一问题,提出了残差网络(ResNet).在2015年ImageNet图像识别挑战赛夺得冠军,深刻影响了后来的深度神经网络的设计。
ResNet的核心思想就是引入了残差边。即一条直接从输入添加到输出的边。
这条边shortcut,有时需要1x1卷积。下面图的虚线shortcut,就是有1x1卷积的。
假如新加的这些层的学习效果非常差,那我们就可以通过一条残差边将这一部分直接“跳过”。实现这一目的很简单,将这些层的权重参数设置为0就行了。这样一来,不管网络中有多少层,效果好的层我们保留,效果不好的我们可以跳过。总之,添加的新网络层至少不会使效果比原来差,就可以较为稳定地通过加深层数来提高模型的效果了。
此外,使用残差边的另一个好处在于可以避免梯度消失的问题。因为它的这个特点,它可以训练几百甚至上千层的网络
ResNet沿用了VGG全3x3卷积层的设计。
层数较少的神经网络中:残差块里首先有2个相同输出通道的3x3卷积层。每个卷积层后接BN层和Relu激活函数。再将输入直接加到最后的Relu激活函数前。
若输入通道数较多,就需要引入1x1卷积层来调整输入的通道数。常用于网络层数较多的结构。
残差块实现代码:
ResNet18模型结构:
ResNet18. 共有4组(每组4个)卷积层,加上开始的7x7卷积层和全连接层。共18层。所以叫ResNet18. (不包含1x1卷积层)。
注意,如果不是第一组残差模块,且又是第一个残差块,则残差块需要做1x1卷积,以实现通道数和宽高的调整。(见虚线)
不同颜色的卷积层,构成不同的残差模块。
残差网络,就是一系列残差块串联的结果。
import tensorflow as tf from tensorflow.keras import activations import tensorflow.keras.layers as layers #残差块构成 #两个同输出的conv. 第一个conv+BN+激活函数。 第二个conv+BN后,结果与输入x相加, 再使用激活函数 class Residual(tf.keras.Model): #定义网络结构 def __init__(self, num_channels, use_1x1conv=False, strides=1): super(Residual, self).__init__() #第一个卷积层 self.conv1 = layers.Conv2D(num_channels, padding="same", kernel_size=3, strides=strides) #第二个卷积层 self.conv2 = layers.Conv2D(num_channels, kernel_size=3, padding = "same") #是否使用1x1卷积层 if(use_1x1conv): self.conv3 = layers.Conv2D(num_channels, kernel_size= 1, strides=strides) else: self.conv3 = None #BN self.bn1 = layers.BatchNormalization() self.bn2 = layers.BatchNormalization() #定义前向传播过程 def call(self, x): Y = activations.relu(self.bn1(self.conv1(x))) Y = self.bn2(self.conv2(Y)) if (self.conv3): x = self.conv3(x) outputs = activations.relu(x+Y) return outputs #残差模块. 每个残差模块包含多个(2个)残差块,每个残差快包含2个conv层 class Resnet_Block(tf.keras.layers.Layer): #定义所需的网络结构. num_channels, num_res:残差模块中的残差块个数, first_block:是不是第一个残差模块 def __init__(self, num_channels, num_res, first_block=False): super(Resnet_Block, self).__init__() #存储残差块 self.listLayers = [] #遍历残差块数量 for i in range(num_res): #如果是第一个残差块,并且不是第一个残差模块时 if(i == 0 and not first_block): self.listLayers.append(Residual(num_channels, use_1x1conv=True, strides=2)) else: self.listLayers.append(Residual(num_channels)) #定义前向传播 def call(self, X): for layer in self.listLayers.layers: X = layer(X) return X #构建ResNet #前面分别定义了残差块(2conv)和残差模块,现在把残差7x7卷积+4个残差模块+FC, 就构成了RestNet18 class ResNet(tf.keras.Model): #定义网络构成. num_blocks: list,依次指定没一个残差模块中残差快的个数 def __init__(self, num_blocks): super(ResNet, self).__init__() #7x7卷积 self.conv = layers.Conv2D(64, kernel_size=7, strides=2, padding="same") #BN层 self.bn = layers.BatchNormalization() #激活层 self.relu = layers.Activation("relu") #池化 self.mp = layers.MaxPool2D(pool_size=3, strides=2, padding="same") #串联残差模块 #残差模块1 self.res_block1=Resnet_Block(64,num_blocks[0], first_block=True) #残差模块2 self.res_block2=Resnet_Block(128, num_blocks[1], first_block=False) #残差模块3 self.res_block3 = Resnet_Block(256, num_blocks[2], first_block=False) # 残差模块4 self.res_block4 = Resnet_Block(512, num_blocks[3], first_block=False) #平均池化 self.gap = layers.GlobalAvgPool2D() #FC self.fc = layers.Dense(units=10, activation=tf.keras.activations.softmax) def call(self, x): #输入部分的传输过程 x = self.conv(x) x = self.bn(x) x = self.relu(x) x = self.mp(x) #Res block x =self.res_block1(x) x = self.res_block2(x) x = self.res_block3(x) x = self.res_block4(x) #输出部分的传输 x = self.gap(x) x = self.fc(x) return x #实例化 mynet = ResNet([2,2,2,2]) X = tf.random.uniform([1,224,224,1]) y = mynet(X) mynet.summary()
Model: "res_net"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) multiple 3200
batch_normalization (BatchN multiple 256
ormalization)
activation (Activation) multiple 0
max_pooling2d (MaxPooling2D multiple 0
)
resnet__block (Resnet_Block multiple 148736
)
resnet__block_1 (Resnet_Blo multiple 526976
ck)
resnet__block_2 (Resnet_Blo multiple 2102528
ck)
resnet__block_3 (Resnet_Blo multiple 8399360
ck)
global_average_pooling2d (G multiple 0
lobalAveragePooling2D)
dense (Dense) multiple 5130
=================================================================
Total params: 11,186,186
Trainable params: 11,178,378
Non-trainable params: 7,808
_________________________________________________________________