全连接层

Layers:

  • input
  • hidden
  • output

Heroes:

  • BigDATA
  • ReLU
  • Dropout
  • BatchNorm
  • ResNet
  • XAVIR Initialization
  • Caffe/Tensorflow/PyTorch

tf.keras.layers.Dense(units,activation)

layer.Dense类:

  • .build(num)方法

    num为输入节点数

  • .kernel方法

    权值张量𝑾

  • .bias方法

    偏置张量𝒃

  • .trainable_variables属性

    待优化参数列表

  • .non_trainable_variables属性

    所有不需要优化的参数列表

  • .trainable属性

    所有内部张量列表

通过layer.Dense 类,只需要指定输出节点数Units 和激活函数类型activation 即可。需要注意的是,输入节点数会根据第一次运算时的输入shape 确定,同时根据输入、输出节点数自动创建并初始化权值张量𝑾和偏置张量𝒃,因此在新建类Dense 实例时,并不会立即创建权值张量𝑾和偏置张量𝒃,而是需要调用build 函数或者直接进行一次前向计算,才能完成网络参数的创建。其中activation 参数指定当前层的激活函数,可以为常见的激活函数或自定义激活函数,也可以指定为None,即无激活函数。

我们可以通过类内部的成员名kernel 和bias 来获取权值张量𝑾和偏置张量𝒃对象:

1
2
3
4
5
6
import tensorflow as tf
x=tf.random.normal([4,28*28])
f=tf.keras.layers.Dense(10.activation=tf.nn.relu)
out=f(x)# 用f类实例完成一层全连接层的计算
f.kernel #获取w
f.bias #获取b

机制:

利用网络层类对象进行前向计算时,只需要调用类的call方法即可,即写成f(x)方式便可,它会自动调用类的call方法,在call方法中会自动调用call 方法,这一设定由TensorFlow 框架自动完成,因此用户只需要将网络层的前向计算逻辑实现在call 方法中即可。对于全连接层类,在call 方法中实现𝜎(𝑿@𝑾 + 𝒃)的运算逻辑,非常简单,最后返回全连接层的输出张量即可。


神经网络

tf.keras.Sequential([layer1,layer2,…])

可以将每一个层封装起来,调用大类的前向计算函数一次即可完成所有层的前向计算.

  • .build(num)方法

    使用方法同上

  • .summary( )方法

    可以打印出每层的参数列表

  • call方法

    实现大类的向前运算,可以直接用model(x)实现

    在假定model=tf.keras.Sequential([layer1,layer2,…])时


实现代码:

1
2
3
4
5
6
7
8
9
10
11
import tensorflow as tf
from tensorflow.keras import layers,Sequential
model=Sequential([
layers.Dense(20,activation=tf.nn.relu),
layers.Dense(180,activation=tf.nn.relu),
layers.Dense(10,activation=tf.nn.relu),
])

x=tf.random.normal([4,28*28])
out=model(x)
model.summary()

激活函数

常见的激活函数

Sigmoid函数

它的一个优良特性就是能够把𝑥 ∈ 𝑅的输入“压缩”到𝑥 ∈ (0,1)区间,这个区间的数值在机
器学习常用来表示以下意义:

  • 概率分布 (0,1)区间的输出和概率的分布范围[0,1]契合,可以通过Sigmoid 函数将输出
    转译为概率输出
  • 信号强度 一般可以将0~1 理解为某种信号的强度,如像素的颜色强度,1 代表当前通
    道颜色最强,0 代表当前通道无颜色;抑或代表门控值(Gate)的强度,1 代表当前门控
    全部开放,0代表关闭
  • tf.nn.sigmoid(x)

ReLU函数

公式:$ReLU(𝑥) ≜ max(0, 𝑥)$

  • tf.nn.relu(x)

LeakyReLU

  • tf.nn.leaky_relu(x, alpha= )

Tanh函数

Tanh 函数能够将𝑥 ∈ 𝑅的输入“压缩”到(−1,1)区间

  • tf.nn.tanh(x)

输出层函数

恒等函数

tf不对输出层处理即可

回归问题


softmax函数

分类问题

  • tf.nn.softmax

在 Softmax 函数的数值计算过程中,容易因输入值偏大发生数值溢出现象;在计算交叉熵时,也会出现数值溢出的问题。为了数值计算的稳定性,TensorFlow 中提供了一个统一的接口,将Softmax 与交叉熵损失函数同时实现,同时也处理了数值不稳定的异常,一般推荐使用这些接口函数,避免分开使用Softmax 函数与交叉熵损失函数。

函数式接口为tf.keras.losses.categorical_crossentropy(y_true,y_pred,from_logits=False),其中y_true 代表了One-hot 编码后的真实标签,y_pred 表示网络的预测值,当from_logits 设置为True 时,y_pred 表示须为未经过Softmax 函数的变量z;

当from_logits 设置为False 时,y_pred 表示为经过Softmax 函数的输出。为了数值计算稳定性,一般设置from_logits 为True,此时tf.keras.losses.categorical_crossentropy 将在内部进行Softmax 函数计算,所以不需要在模型中显式调用Softmax 函数

  • tf.keras.losses.categorical_crossentropy(y_true,y_pred,from_logits=False)
1
2
3
4
5
6
7
8
9
10
11
12
import tensorflow as tf 
from tensorflow import keras
z = tf.random.normal([2,10]) # 构造输出层的输出
print(z.shape)
y_onehot = tf.constant([1,3]) # 构造真实值
y_onehot = tf.one_hot(y_onehot, depth=10) # one-hot 编码
print(y_onehot.shape)
# 输出层未使用Softmax 函数,故from_logits 设置为True
# 这样categorical_crossentropy 函数在计算损失函数前,会先内部调用Softmax 函数
loss = keras.losses.categorical_crossentropy(y_onehot,z,from_logits=True)
loss = tf.reduce_mean(loss) # 计算平均交叉熵损失
print(loss)

6.5 输出层设计
我们来特别地讨论网络的最后一层的设计,它除了和所有的隐藏层一样,完成维度变换、特征提取的功能,还作为输出层使用,需要根据具体的任务场景来决定是否使用激活函数,以及使用什么类型的激活函数等。
我们将根据输出值的区间范围来分类讨论。常见的几种输出类型包括:
❑ 𝑜𝑖 ∈ 𝑅𝑑 输出属于整个实数空间,或者某段普通的实数空间,比如函数值趋势的预
测,年龄的预测问题等。
❑ 𝑜𝑖 ∈ [0,1] 输出值特别地落在[0, 1]的区间,如图片生成,图片像素值一般用[0, 1]区间
的值表示;或者二分类问题的概率,如硬币正反面的概率预测问题。
❑ 𝑜𝑖 ∈ [0, 1], 𝑖 𝑜𝑖 = 1 输出值落在[0,1]的区间,并且所有输出值之和为 1,常见的如
多分类问题,如MNIST 手写数字图片识别,图片属于10 个类别的概率之和应为1。
❑ 𝑜𝑖 ∈ [−1, 1] 输出值在[-1, 1]之间


误差分析

Outline:

  • MSE
  • Cross Entopy Loss
  • Hinge Loss

MSE

  • tf.losses.MSE(y_true,y_pred)

同时也与tf.reduce_mean( )搭配使用,求出平均误差

Cross Entopy Loss

  • tf.keras.losses.categorical_crossentropy(y_true,y_pred,from_logits=False)

对y_true一定要one_hot化

交叉熵使用的更多

反向传播

tf.GradientTape( )

tensorflow有自动求导的功能,如果遇到一定要手动求导的问题,也可以自己构建计算图的方式来求导

  • tf.GradientTape()可以提供求导的上下文管理器来连接需要计算梯度的函数和变量,一般与with as语句连用

    1
    2
    3
    4
    5
    6
    import tensorflow as tf
    x=tf.constant(3.0)
    with tf.GradientTape() as tape:
    tape.watch(x)
    y=x*x*x
    dy_dx=tape.gradient(y,[x])

    tf.GradientTape(persistent=True,watch_accessed_variables=True)

    • persistent: 布尔值,用来指定新创建的gradient tape是否是可持续性的。默认是False,意味着只能够调用一次gradient()函数。
    • watch_accessed_variables: 布尔值,表明这个gradien tap是不是会自动追踪任何能被训练(trainable)的变量。默认是True。要是为False的话,意味着你需要手动去指定你想追踪的那些变量。

    但tf.GradientTape只能默认追踪Variable变量,如果是constant变量则需要用.watch( )方法来添加为可追踪变量

Himmelblau函数优化练习

Himmelblau 函数是用来测试优化算法的常用样例函数之一

用python代码来表示:

1
2
3
4
5
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
def Himmelblau(x):
return (x[0]**2+x[1]-11)**2+(x[0]+x[1]**2-7)**2

用Matplotlib 库可视化Himmelblau 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
x = np.arange(-6, 6, 0.1) # 可视化的x 坐标范围为-6~6
y = np.arange(-6, 6, 0.1) # 可视化的y 坐标范围为-6~6
print('x,y range:', x.shape, y.shape)
# 生成x-y 平面采样网格点,方便可视化
x, y = np.meshgrid(x, y)
print('X,Y maps:', X.shape, Y.shape)
z = Himmelblau([x, y]) # 计算网格点上的函数值

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure('himmelblau')
ax = fig.gca(projection='3d') # 设置3D 坐标轴
ax.plot_surface(x, y, z) # 3D 曲面图
ax.view_init(60, -30)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()

自动求导:

  • tf.constant版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 参数的初始化值对优化的影响不容忽视,可以通过尝试不同的初始化值,
# 检验函数优化的极小值情况
# [1., 0.], [-4, 0.], [4, 0.]
x = tf.constant([4., 0.]) # 初始化参数
for step in range(200):# 循环优化200 次
with tf.GradientTape() as tape: #梯度跟踪
tape.watch([x]) # 加入梯度跟踪列表
y = himmelblau(x) # 前向传播
# 反向传播
grads = tape.gradient(y, [x])[0]
# 更新参数,0.01 为学习率
x -= 0.01*grads
# 打印优化的极小值
if step % 20 == 19:
print ('step {}: x = {}, f(x) = {}'.format(step, x.numpy(), y.numpy()))
  • tf.Variable版
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 参数的初始化值对优化的影响不容忽视,可以通过尝试不同的初始化值,
# 检验函数优化的极小值情况
# [1., 0.], [-4, 0.], [4, 0.]
x = tf.Variable([4., 0.]) # 初始化参数
for step in range(200):# 循环优化200 次
with tf.GradientTape() as tape: #梯度跟踪
tape.watch([x]) # 加入梯度跟踪列表
y = Himmelblau(x) # 前向传播
# 反向传播
grads = tape.gradient(y,[x,])
# 更新参数,0.01 为学习率
x.assign_sub(0.01*grads[0])
# 打印优化的极小值
if step % 20 == 19:
print ('step {}: x = {}, f(x) = {}'.format(step, x.numpy(), y.numpy()))

Keras API for tensorflow

网络层类

常见内置模型层介绍

tf.keras.layers下

基础层

  • Dense:密集连接层。参数个数 = 输入层特征数× 输出层特征数(weight)+ 输出层特征数(bias)
  • Activation:激活函数层。一般放在Dense层后面,等价于在Dense层中指定activation。
  • Dropout:随机置零层。训练期间以一定几率将输入置0,一种正则化手段。
  • BatchNormalization:批标准化层。通过线性变换将输入批次缩放平移到稳定的均值和标准差。可以增强模型对输入不同分布的适应性,加快模型训练速度,有轻微正则化效果。一般在激活函数之前使用。
  • SpatialDropout2D:空间随机置零层。训练期间以一定几率将整个特征图置0,一种正则化手段,有利于避免特征图之间过高的相关性。
  • Input:输入层。通常使用Functional API方式构建模型时作为第一层。
  • DenseFeature:特征列接入层,用于接收一个特征列列表并产生一个密集连接层。
  • Flatten:压平层,用于将多维张量压成一维。
  • Reshape:形状重塑层,改变输入张量的形状。
  • Concatenate:拼接层,将多个张量在某个维度上拼接。
  • Add:加法层。
  • Subtract: 减法层。
  • Maximum:取最大值层。
  • Minimum:取最小值层。

卷积网络相关层

  • Conv1D:普通一维卷积,常用于文本。参数个数 = 输入通道数×卷积核尺寸(如3)×卷积核个数
  • Conv2D:普通二维卷积,常用于图像。参数个数 = 输入通道数×卷积核尺寸(如3乘3)×卷积核个数
  • Conv3D:普通三维卷积,常用于视频。参数个数 = 输入通道数×卷积核尺寸(如3乘3乘3)×卷积核个数
  • SeparableConv2D:二维深度可分离卷积层。不同于普通卷积同时对区域和通道操作,深度可分离卷积先操作区域,再操作通道。即先对每个通道做独立卷积操作区域,再用1乘1卷积跨通道组合操作通道。参数个数 = 输入通道数×卷积核尺寸 + 输入通道数×1×1×输出通道数。深度可分离卷积的参数数量一般远小于普通卷积,效果一般也更好。
  • DepthwiseConv2D:二维深度卷积层。仅有SeparableConv2D前半部分操作,即只操作区域,不操作通道,一般输出通道数和输入通道数相同,但也可以通过设置depth_multiplier让输出通道为输入通道的若干倍数。输出通道数 = 输入通道数 × depth_multiplier。参数个数 = 输入通道数×卷积核尺寸× depth_multiplier。
  • Conv2DTranspose:二维卷积转置层,俗称反卷积层。并非卷积的逆操作,但在卷积核相同的情况下,当其输入尺寸是卷积操作输出尺寸的情况下,卷积转置的输出尺寸恰好是卷积操作的输入尺寸。
  • LocallyConnected2D: 二维局部连接层。类似Conv2D,唯一的差别是没有空间上的权值共享,所以其参数个数远高于二维卷积。
  • MaxPooling2D: 二维最大池化层。也称作下采样层。池化层无参数,主要作用是降维。
  • AveragePooling2D: 二维平均池化层。
  • GlobalMaxPool2D: 全局最大池化层。每个通道仅保留一个值。一般从卷积层过渡到全连接层时使用,是Flatten的替代方案。
  • GlobalAvgPool2D: 全局平均池化层。每个通道仅保留一个值。

循环网络相关层

  • Embedding:嵌入层。一种比Onehot更加有效的对离散特征进行编码的方法。一般用于将输入中的单词映射为稠密向量。嵌入层的参数需要学习。
  • LSTM:长短记忆循环网络层。最普遍使用的循环网络层。具有携带轨道,遗忘门,更新门,输出门。可以较为有效地缓解梯度消失问题,从而能够适用长期依赖问题。设置return_sequences = True时可以返回各个中间步骤输出,否则只返回最终输出。
  • GRU:门控循环网络层。LSTM的低配版,不具有携带轨道,参数数量少于LSTM,训练速度更快。
  • SimpleRNN:简单循环网络层。容易存在梯度消失,不能够适用长期依赖问题。一般较少使用。
  • ConvLSTM2D:卷积长短记忆循环网络层。结构上类似LSTM,但对输入的转换操作和对状态的转换操作都是卷积运算。
  • Bidirectional:双向循环网络包装器。可以将LSTM,GRU等层包装成双向循环网络。从而增强特征提取能力。
  • RNN:RNN基本层。接受一个循环网络单元或一个循环单元列表,通过调用tf.keras.backend.rnn函数在序列上进行迭代从而转换成循环网络层。
  • LSTMCell:LSTM单元。和LSTM在整个序列上迭代相比,它仅在序列上迭代一步。可以简单理解LSTM即RNN基本层包裹LSTMCell。
  • GRUCell:GRU单元。和GRU在整个序列上迭代相比,它仅在序列上迭代一步。
  • SimpleRNNCell:SimpleRNN单元。和SimpleRNN在整个序列上迭代相比,它仅在序列上迭代一步。
  • AbstractRNNCell:抽象RNN单元。通过对它的子类化用户可以自定义RNN单元,再通过RNN基本层的包裹实现用户自定义循环网络层。
  • Attention:Dot-product类型注意力机制层。可以用于构建注意力模型。
  • AdditiveAttention:Additive类型注意力机制层。可以用于构建注意力模型。
  • TimeDistributed:时间分布包装器。包装后可以将Dense、Conv2D等作用到每一个时间片段上。

自定义层

对于自定义的网络层,至少需要实现初始化init方法和前向传播逻辑call方法。

如一个没有偏置向量的全连接层:即bias 为0,同时固定激活函数为ReLU 函数。

实现代码如下:

1
2
3
4
5
6
7
8
9
10
class MyDense(layer.Layer):
def __init__(self,inp_dim,outp_dim):
super(MyDense,self).__init__()
self.kernel=self.add_variable('w',[inp_dim,outp_dim],trainable=True)

def call(self,inputs,training=None):
out=inputs@self.kernel
out=tf.nn.relu(out)

return out

自定义类的前向运算逻辑实现在call(inputs, training=None)函数中,其中inputs代表输入,由用户在调用时传入;training 参数用于指定模型的状态:training 为True 时执行训练模式,training 为False 时执行测试模式,默认参数为None,即测试模式。由于全连接层的训练模式和测试模式逻辑一致,此处不需要额外处理。对于部份测试模式和训练模式不一致的网络层,需要根据training 参数来设计需要执行的逻辑。

网络类

内置网络容器Sequential

1
2
3
4
5
6
7
8
9
10
from tensorflow.keras import layers, Sequential
network = Sequential([ # 封装为一个网络
layers.Dense(3, activation=None), # 全连接层,此处不使用激活函数
layers.ReLU(),#激活函数层
layers.Dense(2, activation=None), # 全连接层,此处不使用激活函数
layers.ReLU() #激活函数层
])
x = tf.random.normal([4,3])

out = network(x) # 输入从第一层开始,逐层传播至输出层,并返回输出层的输出

Sequential 容器也可以通过add()方法继续追加新的网络层,实现动态创建网络的功能:

1
2
3
4
5
6
7
8
from tensorflow.keras import layers, Sequential
layers_num = 2 # 堆叠2 次
network = Sequential([]) # 先创建空的网络容器
for _ in range(layers_num):
network.add(layers.Dense(3)) # 添加全连接层
network.add(layers.ReLU())# 添加激活函数层
network.build(input_shape=(4, 4)) # 创建网络参数
network.summary()

模型装配、训练与测试

假定有一个全连接模型:

1
2
3
4
5
6
7
8
9
10
from tensorflow.keras import Sequential,optimizers,losses
network = Sequential([
layers.Dense(256, activation='relu'),
layers.Dense(128, activation='relu'),
layers.Dense(64, activation='relu'),
layers.Dense(32, activation='relu'),
layers.Dense(10)
])
network.build(input_shape=(4, 28*28))
network.summary()

在keras.model类中

  • 模型装配

    • .compile( )方法

      可以指定网络使用的优化器对象,损失函数类型,评价指标

      1
      2
      3
      4
      5
      6
      # 采用Adam 优化器,学习率为0.01;采用交叉熵损失函数,包含Softmax
      network.compile(
      optimizer=optimizers.Adam(lr=0.01),
      loss=losses.CategoricalCrossentropy(from_logits=True),
      metrics=['accuracy'] # 设置测量指标为准确率
      )
  • 模型训练

    • .fit( )方法

      模型装配完成后,即可通过fit()函数送入待训练的数据集和验证用的数据集,进行训练

      1
      2
      3
      4
      # 指定训练集为train_db,验证集为val_db,训练5 个epochs,每2 个epoch 验证一次
      # 返回训练轨迹信息保存在history 对象中
      history = network.fit(train_db, epochs=5, validation_data=val_db,
      validation_freq=2,callback=[tensorboard])
  • 模型测试,推理

    • .predict( )方法

      可以进行模型推理,并返回预测结果

    • .evaluate( )方法

      可以直接测试指定数据集所有样本,并打印出性能指标

自定义网络

Sequential 容器适合于数据按序从第一层传播到第二层,再从第二层传播到第三层,以
此规律传播的网络模型。对于复杂的网络结构,例如第三层的输入不仅是第二层的输出,
还有第一层的输出,此时使用自定义网络更加灵活。

  • 网络层的创建
  • 前向运算逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyModel(keras.Model):
# 自定义网络类,继承自Model 基类
def __init__(self):
super(MyModel, self).__init__()
# 完成网络内需要的网络层的创建工作
self.fc1 = MyDense(28*28, 256)
self.fc2 = MyDense(256, 128)
self.fc3 = MyDense(128, 64)
self.fc4 = MyDense(64, 32)
self.fc5 = MyDense(32, 10)

def call(self, inputs, training=None):
# 自定义前向运算逻辑
x = self.fc1(inputs)
x = self.fc2(x)
x = self.fc3(x)
x = self.fc4(x)
x = self.fc5(x)
return x

模型保存和加载

  • 张量方式

    • Model.save_weights(path)

      通过调用Model.save_weights(path)方法即可将当前的网络参数保存到path 文件上,

    • Model.load_weights(path)

      然后调用网络对象的load_weights(path)方法即可将指定的模型文件中保存的张量数值写入
      到当前网络参数中去

  • 网络方式

    • Model.save(path)

      将模型的参数和结构都保存到path上,一般以h5为文件后缀

    • tf.keras.models.load_model(file)

      加载已保存的模型文件( 用Model.save( ) 和tf.saved_model.save( ) )

  • savemodel方式

    将网络及参数保存为pb格式,方便其他工具读取,如pytorch,Netron, 也方便跨平台使用

    • tf.saved_model.save(model,path)

      保存模型与参数数据

    • tf.saved_model.load(path)

      加载模型

      Loading Keras models

      Keras models are trackable, so they can be saved to SavedModel. The object returned by tf.saved_model.load is not a Keras object (i.e. doesn’t have .fit, .predict, etc. methods). A few attributes and functions are still available: .variables, .trainable_variables and .__call__.

      所以我们如果要加载回keras模型可以用回tf.keras.models.load_model(file)

加载模型

在keras.application中有多种在ImageNet上训练好参数的模型,可以直接调用

如:

1
2
from tensorflow import keras
resnet=keras.application.Resnet50(weight='imagenet',include_top=false)

测量工具

keras。。。

可视化

关键工具TensorBoard和关键模块tf.summary或来自tf.keras.model模块中的callback里tf.keras.callbacks.TensorBoard


在cmd中使用

1
tensorboard --logdir=PATH

> 就是保存log_dir的路径

keras.Model类中fit方法使用callback

Example(Basic):

1
2
3
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=".\\logs")
model.fit(x_train, y_train, epochs=2, callbacks=[tensorboard_callback])
# run the tensorboard command to view the visualizations.

Example(Profile):

1
2
3
4
5
6
7
8
9
# profile a single batch, e.g. the 5th batch.
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir='.\\logs',profile_batch=5)
model.fit(x_train, y_train, epochs=2, callbacks=[tensorboard_callback])
# Now run the tensorboard command to view the visualizations (profile plugin).

# profile a range of batches, e.g. from 10 to 20.
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir='.\\logs',profile_batch='10,20')
model.fit(x_train, y_train, epochs=2, callbacks=[tensorboard_callback])
# Now run the tensorboard command to view the visualizations (profile plugin).

关键模块tf.summary

1
2
3
4
5
6
summary_writer=tf.summary.create_file_writer(log_dir)
with summary_writer.as_dafault():
for step in range(100):
# other model code would go here
tf.summary.scalar("my_metric", 0.5, step=step)
writer.flush()