在 TensorFlow 中,要实现全连接层,只需要定义好权值张量和偏置张量,并利用TensorFlow 提供的批量矩阵相乘函数 tf.matmul()即可完成网络层的计算。
#张量的实现
#输入3个样本,每个样本的特征长度为4
x = tf.random.normal([3,4])
#特征长度为4,输出节点为5,故权值矩阵的shape为[4,5]
w1 = tf.Variable(tf.random.truncated_normal([4, 5], stddev=0.1))
#偏置向量的shape为[5]
b1 = tf.Variable(tf.zeros([5]))
o1 = tf.matmul(x,w1) + b1 # 线性变换
o1 = tf.nn.relu(o1) # ReLu激活函数
print(o1)
全连接层本质上是矩阵的相乘和相加运算,实现并不复杂。但作为最常用的网络层之一,TensorFlow 中有更高层、使用更方便的层实现方式:layers.Dense(units, activation)。通过 layer.Dense 类,只需要指定输出节点数 Units 和激活函数类型 activation 即可。
需要注意的是,输入节点数会根据第一次运算时的输入 shape 确定,同时根据输入、输出节点数自动创建并初始化权值张量和偏置张量,因此在新建类 Dense 实例时,并不会立即创建权值张量和偏置张量,而是需要调用 build 函数或者直接进行一次前向计算,才能完成网络参数的创建。其中 activation 参数指定当前层的激活函数,可以为常见的激活函数或自定义激活函数,也可以指定为 None,即无激活函数。
from tensorflow.keras import layers # 导入层模块
x = tf.random.normal([3,4])#输入3个样本,每个样本的特征长度为4
fc = layers.Dense(5, activation=tf.nn.relu) # 创建全连接层,指定输出节点数和激活函数
h1 = fc(x) # 通过fc类实例完成一次全连接层的计算,返回输出张量
print(h1)
fc.kernel # 获取Dense类的权值矩阵
fc.bias # 获取Dense类的偏置向量
fc.trainable_variables # 返回待优化参数列表
fc.variables # 返回所有参数列表
可以通过类内部的成员名 kernel 和 bias 来获取权值张量和偏置张量对象。在优化参数时,若需要获得网络的所有待优化的张量参数列表,可以通过类的trainable_variables 来返回待优化参数列表。如果希望获得所有参数列表,可以通过类的 variables 返回所有内部张量列表。在Batch Normalization 层,可以通过non_trainable_variables 成员返回所有不需要优化的参数列表。
以输入层4个样本,每个样本的特征长度为28,含有3程隐藏层,各层输出节点数为[25,28,64],输出层的输出节点数为 10为例。
在使用 TensorFlow 自动求导功能计算梯度时,需要将前向计算过程放置在tf.GradientTape()环境中,从而利用GradientTape 对象的 gradient()方法自动求解参数的梯度,并利用 optimizers 对象更新参数。
#输入层,4个样本,每个样本的特征长度为28
x = tf.random.normal([4,28])
# 隐藏层 1 张量,特征长度为28,输出节点为25
w1 = tf.Variable(tf.random.truncated_normal([28, 25], stddev=0.1))
b1 = tf.Variable(tf.zeros([25]))
# 隐藏层 2 张量,特征长度为25,输出节点为28
w2 = tf.Variable(tf.random.truncated_normal([25, 28], stddev=0.1))
b2 = tf.Variable(tf.zeros([28]))
# 隐藏层 3 张量,特征长度为28,输出节点为64
w3 = tf.Variable(tf.random.truncated_normal([28, 64], stddev=0.1))
b3 = tf.Variable(tf.zeros([64]))
# 输出层张量,特征长度为64,输出节点为10
w4 = tf.Variable(tf.random.truncated_normal([64, 10], stddev=0.1))
b4 = tf.Variable(tf.zeros([10]))
with tf.GradientTape() as tape: # 梯度记录器
# 隐藏层 1 前向计算,[b, 28] => [b, 25]
h1 = x@w1 + tf.broadcast_to(b1, [x.shape[0], 25])
h1 = tf.nn.relu(h1)
# 隐藏层 2 前向计算,[b, 25] => [b, 28]
h2 = h1@w2 + b2
h2 = tf.nn.relu(h2)
# 隐藏层 3 前向计算,[b, 28] => [b, 64]
h3 = h2@w3 + b3
h3 = tf.nn.relu(h3)
# 输出层前向计算,[b, 64] => [b, 10]
h4 = h3@w4 + b4
print(h4)
.
通过层方式实现,首先新建各个网络层类,并
指定各层的激活函数类型。对于数据依次向前传播的网络,可以通过 Sequential 容器封装成一个网络大类对象,调用大类的前向计算函数一次即可完成所有层的前向计算,使用起来更加方便。
# 导入常用网络层 layers
from tensorflow.keras import layers,Sequential
fc1 = layers.Dense(256, activation=tf.nn.relu) # 隐藏层 1
fc2 = layers.Dense(128, activation=tf.nn.relu) # 隐藏层 2
fc3 = layers.Dense(64, activation=tf.nn.relu) # 隐藏层 3
fc4 = layers.Dense(10, activation=None) # 输出层
# 通过 Sequential 容器封装为一个网络类
model = Sequential([
layers.Dense(256, activation=tf.nn.relu) , # 创建隐藏层 1
layers.Dense(128, activation=tf.nn.relu) , # 创建隐藏层 2
layers.Dense(64, activation=tf.nn.relu) , # 创建隐藏层 3
layers.Dense(10, activation=None) , # 创建输出层
])
out = model(x) # 前向计算得到输出
print(out)
网络的最后一层除了和所有的隐藏层一样,完成维度变换、特征提取的功能,还作为输出层使用,需要根据具体的任务场景来决定是否使用激活函数,以及使用什么类型的激活函数等。常见的几种输出类型包括:
这一类问题比较普遍,像年龄的预测、股票走势的预测等都属于整个或者部分连续的实数空间,输出层可以不加激活函数。误差的计算直接基于最后一层的输出和真实值进行计算。
输出值属于[0,1]区间也比较常见,通过 Sigmoid 函数可以实现输出值在[0,1]区间。
通过在输出层添加 Softmax 函数实现输出值在[0,1]区间,且和为0。Softmax 函数不仅可以将输出值映射到[0,1]区间,还满足所有的输出值之和为 1 的特性。通过Softmax函数可以将输出层的输出转译为类别概率,在分类问题中使用的非常频繁。
如果希望输出值的范围分布在(−1,1)区间,可以简单地使用 tanh 激活函数。
全连接层是神经网络最基本的网络类型,全连接层前向计算流程相对简单,梯度求导也较简单,但是它有一个最大的缺陷,在处理较大特征长度的数据时,全连接层的参数量往往较大,使得深层数的全连接网络参数量巨大,训练起来比较困难。随着时代发展,相继提出了一系列的神经网络变种类型。
通过利用局部相关性和权值共享的思想,提出了卷积神经网络(Convolutional Neural Network,简称 CNN)。随着深度学习的兴盛,卷积神经网络在计算机视觉中的表现大大地超越了其它算法模型,呈现统治计算机视觉领域之势。
除了具有空间结构的数据外,序列信号也是非常常见的一种数据类型,其中一个最具代表性的序列信号就是文本数据。卷积神经网络由于缺乏 Memory 机制和处理不定长序列信号的能力,并不擅长序列信号的任务。循环神经网络(Recurrent Neural Network,简称 RNN)证明非常擅长处理序列信号。后续又提出了 LSTM 网络,作为 RNN 的变种,它较好地克服了 RNN 缺乏长期记忆、不擅长处理长序列的问题,在自然语言处理中得到了广泛的应用。
随着注意力机制(Attention Mechanism)的提出,克服了 RNN 训练不稳定、难以并行化等缺陷,在自然语言处理和图片生成等领域中逐渐崭露头角。注意力机制最初在图片分类任务上提出,但逐渐开始侵蚀NLP 各大任务。2017 年,Google 提出了第一个利用纯注意力机制实现的网络模型Transformer,随后基于 Transformer 模型相继提出了一系列的用于机器翻译的注意力网络模型。
图片、文本等数据具有规则的空间、时间结构,称为 Euclidean Data(欧几里德数据)。卷积神经网络和循环神经网络被证明非常擅长处理这种类型的数据。而像类似于社交网络、通信网络、蛋白质分子结构等一系列的不规则空间拓扑结构的数据,它们显得力不从心。2016 年,Thomas Kipf 等人基于前人在一阶近似的谱卷积算法上提出了图卷积网络(Graph Convolution Network,GCN)模型。GCN 算法实现简单,从空间一阶邻居信息聚合的角度也能直观地理解,在半监督任务上取得了不错效果。