卷积
卷积有两组输入:特征图和卷积核,依据输入特征和卷积核的形状、Layout不同、计算方式的不同,在Fluid里,有针对变长序列特征的一维卷积,有针对定长图像特征的二维(2D Conv)、三维卷积(3D Conv),同时也有卷积计算的逆向过程。
Conv2D、Conv3D
Conv2D将在神经网络中构建一个二维卷积层(Convolution2D Layer),其根据输入、滤波器参数(num_filters、filter_size)、步长(stride)、填充(padding)、膨胀系数(dilation)、组数(groups)参数来计算得到输出特征图。输入和输出是 NCHW
格式,N是批数据大小,C是特征图个数,H是特征图高度,W是特征图宽度。滤波器的维度是 [M, C, H, W] ,M是输出特征图个数,C是输入特征图个数,H是滤波器高度,W是滤波器宽度。如果组数大于1,C等于输入特征图个数除以组数的结果。如果提供了偏移属性和激活函数类型,卷积的结果会和偏移相加,激活函数会作用在最终结果上。详情请参考: 卷积 。 对每个输入 X
,有等式:
其中:
- :输入特征图,
NCHW
格式的Tensor
- :滤波器,维度为 [M, C, H, W] 的
Tensor
- :卷积操作
- :偏移值,2-D
Tensor
,维度为[M,1]
- :激活函数
- :输出值,
Out
和X
的维度可能不同
输出维度计算
**
- 输入:
输入维度:
滤波器维度: - 输出:
输出维度: - 其中
接口定义:
class paddle.fluid.dygraph.Conv2D(
num_channels,
num_filters,
filter_size,
stride=1,
padding=0,
dilation=1,
groups=None,
param_attr=None,
bias_attr=None,
use_cudnn=True,
act=None,
dtype='float32'
)
参数说明:
- num_channels (int) - 输入图像的通道数。
- num_filters (int) - 滤波器的个数,和输出特征图个数相同。
- filter_size (int|tuple) - 滤波器大小。如果
filter_size
是一个元组,则必须包含两个整型数,分别表示滤波器高度和宽度。否则,表示滤波器高度和宽度均为filter_size
。 - stride (int|tuple, 可选) - 步长大小。如果
stride
为元组,则必须包含两个整型数,分别表示垂直和水平滑动步长。否则,表示垂直和水平滑动步长均为stride
。默认值:1。 - padding (int|tuple, 可选) - 填充大小。如果
padding
为元组,则必须包含两个整型数,分别表示竖直和水平边界填充大小。否则,表示竖直和水平边界填充大小均为padding
。默认值:0。 - dilation (int|tuple, 可选) - 膨胀系数大小。如果
dialation
为元组,则必须包含两个整型数,分别表示垂直和水平膨胀系数。否则,表示垂直和水平膨胀系数均为dialation
。默认值:1。 - groups (int, 可选) - 二维卷积层的组数。根据Alex Krizhevsky的深度卷积神经网络(CNN)论文中的分组卷积:当group=2,滤波器的前一半仅和输入特征图的前一半连接。滤波器的后一半仅和输入特征图的后一半连接。默认值:1。
- param_attr (ParamAttr, 可选) - 指定权重参数属性的对象。默认值为None,表示使用默认的权重参数属性。具体用法请参见 ParamAttr 。
- bias_attr (ParamAttr|bool, 可选) - 指定偏置参数属性的对象。默认值为None,表示使用默认的偏置参数属性。具体用法请参见 ParamAttr 。
- use_cudnn (bool, 可选) - 是否用cudnn核,只有已安装cudnn库时才有效。默认值:True。
- act (str, 可选) - 应用于输出上的激活函数,如tanh、softmax、sigmoid,relu等,支持列表请参考 激活函数 ,默认值:None。
- dtype (str, 可选) - 数据类型,可以为”float32”或”float64”。默认值:”float32”。
调用示例:
Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
Conv3D与Conv2D类似,只是输入的时候多了一个D(特征深度),其输入输出为:
- 输入:
输入维度:
滤波器维度: - 输出:
输出维度: - 其中
参考:Conv2D、conv2d、Conv3D、conv3d
池化
池化的作用是对输入特征做下采样和降低过拟合。降低过拟合是减小输出大小的结果,它同样也减少了后续层中的参数的数量。
池化通常只需要将前一层的特征图作为输入,此外需要一些参数来确定池化具体的操作。在PaddlePaddle中我们同样通过设定池化的大小,方式,步长,是否是全局池化,是否使用cudnn,是否使用ceil函数计算输出等参数来选择具体池化的方式。 PaddlePaddle中有针对定长图像特征的二维(pool2d)、三维卷积(pool3d),RoI池化(roi_pool),以及针对序列的序列池化(sequence_pool),同时也有池化计算的反向过程。
Pool2D、Pool3D
Pool2D将在神经网络中构建一个二维池化层,并使用上述输入参数的池化配置,为二维空间池化操作,根据 input
, 池化类型 pool_type
, 池化核大小 pool_size
, 步长 pool_stride
,填充 pool_padding
这些参数得到输出。 输入X和输出Out是NCHW格式,N为批大小,C是通道数,H是特征高度,W是特征宽度。参数( ksize
, strides
, paddings
)含有两个整型元素。分别表示高度和宽度上的参数。输入X的大小和输出Out的大小可能不一致。
接口定义:
class paddle.fluid.dygraph.Pool2D(
pool_size=-1,
pool_type='max',
pool_stride=1,
pool_padding=0,
global_pooling=False,
use_cudnn=True,
ceil_mode=False,
exclusive=True
)
参数说明:
- pool_size (int|list|tuple, 可选) - 池化核的大小。如果它是一个元组或列表,它必须包含两个整数值, (pool_size_Height, pool_size_Width)。若为一个整数,则它的平方值将作为池化核大小,比如若pool_size=2, 则池化核大小为2x2。默认值:-1。
- pool_type (str, 可选) - 池化类型,可以是”max“对应max-pooling,“avg”对应average-pooling。默认为”max“。
- pool_stride (int|list|tuple, 可选) - 池化层的步长。如果它是一个元组或列表,它将包含两个整数,(pool_stride_Height, pool_stride_Width)。若为一个整数,则表示H和W维度上stride均为该值。默认值为1。
- pool_padding (int|list|tuple, 可选) - 填充大小。如果它是一个元组或列表,它必须包含两个整数值,(pool_padding_on_Height, pool_padding_on_Width)。若为一个整数,则表示H和W维度上padding均为该值。默认值为1。
- global_pooling (bool, 可选)- 是否用全局池化。如果global_pooling = True,
pool_size
和pool_padding
将被忽略,默认False。 - use_cudnn (bool, 可选)- 是否用cudnn核,只有已安装cudnn库时才有效。默认True。
- ceil_mode (bool, 可选)- 是否用ceil函数计算输出高度和宽度。如果设为False,则使用floor函数。默认为False。
- exclusive (bool, 可选) - 是否在平均池化模式忽略填充值。默认为True。
输入输出的形状
输入:
X shape:
输出:
Out shape:
如果 ceil_mode
= false:
如果 ceil_mode
= true:
如果 exclusive
= false:
如果 exclusive
= true:
调用示例:
Pool2D(pool_size=2, pool_stride=2, pool_type='max')
参考:
批归一化
BatchNorm
BatchNorm接口实现了批归一化层(Batch Normalization Layer)的功能,可用作卷积和全连接操作的批归一化函数,根据当前批次数据按通道计算的均值和方差进行归一化。
接口定义:
class paddle.fluid.dygraph.BatchNorm(
num_channels,
act=None,
is_test=False,
momentum=0.9,
epsilon=1e-05,
param_attr=None,
bias_attr=None,
dtype='float32',
data_layout='NCHW',
in_place=False,
moving_mean_name=None,
moving_variance_name=None,
do_model_average_for_mean_and_var=False,
use_global_stats=False,
trainable_statistics=False
)
参数说明:
- num_channels (int) - 指明输入
Tensor
的通道数量。 - act (str, 可选) - 应用于输出上的激活函数,如tanh、softmax、sigmoid,relu等,支持列表请参考 激活函数 ,默认值为None。
- is_test (bool, 可选) - 指示是否在测试阶段,非训练阶段使用训练过程中统计到的全局均值和全局方差。默认值:False。
- momentum (float, 可选) - 此值用于计算
moving_mean
和moving_var
。默认值:0.9。更新公式如上所示。 - epsilon (float, 可选) - 为了数值稳定加在分母上的值。默认值:1e-05。
- param_attr (ParamAttr, 可选) - 指定权重参数属性的对象。默认值为None,表示使用默认的权重参数属性。具体用法请参见 ParamAttr 。
- bias_attr (ParamAttr, 可选) - 指定偏置参数属性的对象。默认值为None,表示使用默认的偏置参数属性。具体用法请参见 ParamAttr 。
- dtype (str, 可选) - 指明输入
Tensor
的数据类型,可以为float32或float64。默认值:float32。 - data_layout (string, 可选) - 指定输入数据格式,数据格式可以为“NCHW”或者“NHWC”。默认值:“NCHW”。
- in_place (bool, 可选) - 指示
batch_norm
的输出是否可以复用输入内存。默认值:False。 - moving_mean_name (str, 可选) -
moving_mean
的名称,存储全局均值。如果将其设置为None,batch_norm
将随机命名全局均值;否则,batch_norm
将命名全局均值为moving_mean_name
。默认值:None。 - moving_variance_name (string, 可选) -
moving_var
的名称,存储全局方差。如果将其设置为None,batch_norm
将随机命名全局方差;否则,batch_norm
将命名全局方差为moving_variance_name
。默认值:None。 - do_model_average_for_mean_and_var (bool, 可选) - 指示是否为mean和variance做模型均值。默认值:False。
- use_global_stats (bool, 可选) – 指示是否使用全局均值和方差。在预测或测试模式下,将
use_global_stats
设置为true或将is_test
设置为true,这两种行为是等效的。在训练模式中,当设置use_global_stats
为True时,在训练期间也将使用全局均值和方差。默认值:False。 - trainable_statistics (bool, 可选) - eval模式下是否计算mean均值和var方差。eval模式下,trainable_statistics为True时,由该批数据计算均值和方差。默认值:False。
关于BatchNorm的计算过程
当useglobal_stats = False时,![](https://cdn.nlark.com/yuque/__latex/a66a36f1940d897caecd2c2cf266db80.svg#card=math&code=%5Cmu%5Cbeta&height=16&width=18)和是minibatch的统计数据。计算公式如下:
- : 批输入数据
- : 当前批次数据的大小
当useglobal_stats = True时,![](https://cdn.nlark.com/yuque/__latex/a66a36f1940d897caecd2c2cf266db80.svg#card=math&code=%5Cmu%5Cbeta&height=16&width=18)和是全局(或运行)统计数据(moving_mean和moving_variance),通常来自预先训练好的模型。计算公式如下:
归一化函数公式如下:
- ϵ : 添加较小的值到方差中以防止除零
- γ : 可训练的比例参数
- β : 可训练的偏差参数
参考:BatchNorm
输入数据形状是[N,K]的示例
这种情况下会分别对K的每一个分量计算N个样本的均值和方差,数据和参数对应如下:
- 输入 x, [N, K]
- 输出 y, [N, K]
- 均值 ,[K, ]
- 方差 , [K, ]
- 缩放参数, [K, ]
- 平移参数, [K, ]
使用paddle中BatchNorm的代码实现:
# 输入数据形状是 [N, K]时的示例
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import BatchNorm
# 创建数据
data = np.array([[1,2,3], [4,5,6], [7,8,9]]).astype('float32')
# 使用BatchNorm计算归一化的输出
with fluid.dygraph.guard():
# 输入数据维度[N, K],num_channels等于K
bn = BatchNorm(num_channels=3)
x = fluid.dygraph.to_variable(data)
y = bn(x)
print('output of BatchNorm Layer: \n {}'.format(y.numpy()))
输出:
output of BatchNorm Layer:
[[-1.2247438 -1.2247438 -1.2247438]
[ 0. 0. 0. ]
[ 1.2247438 1.2247438 1.2247438]]
使用numpy验证计算结果的代码实现:
import numpy as np
# 使用Numpy计算均值、方差和归一化的输出
a = np.array([[1,2,3], [4,5,6], [7,8,9]]).astype('float32')
a_mean = a.mean(axis=0)
a_std = a.std(axis=0)
b = (a - a_mean) / a_std
print('mean is: \n {}'.format(a_mean))
print('std is: \n {}'.format(a_std))
print('output: is \n {}'.format(b))
输出:
mean is:
[4. 5. 6.]
std is:
[2.4494898 2.4494898 2.4494898]
output: is
[[-1.2247448 -1.2247448 -1.2247448]
[ 0. 0. 0. ]
[ 1.2247448 1.2247448 1.2247448]]
之所以两种计算结果会有些许差别,是因为BatchNorm计算标准化的时候加入了一个极小量,上面有提到过。
输入数据形状是[𝑁,𝐶,𝐻,𝑊]的示例
这种情况下会沿着C这一维度进行展开,分别对每一个通道计算N个样本中总共个像素点的均值和方差,数据和参数对应如下:
- 输入 x, [N, C, H, W]
- 输出 y, [N, C, H, W]
- 均值 ,[C, ]
- 方差 , [C, ]
- 缩放参数, [C, ]
- 平移参数, [C, ]
示例如下:
# 输入数据形状是[N, C, H, W]时的batchnorm示例
import numpy as np
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import BatchNorm
# 设置随机数种子,这样可以保证每次运行结果一致
np.random.seed(100)
# 创建数据
data = np.random.rand(2,1,3,3).astype('float32')
print('input is: \n {}'.format(data))
# 使用BatchNorm计算归一化的输出
with fluid.dygraph.guard():
# 输入数据维度[N, C, H, W],num_channels等于C
bn = BatchNorm(num_channels=1)
x = fluid.dygraph.to_variable(data)
y = bn(x)
print('output of BatchNorm Layer: \n {}'.format(y.numpy()))
# 使用numpy验证输出结果
mean = data.mean()
std = data.std()
out = (data - mean) / std
print('output is: \n {}'.format(out))
结果可自行运行验证。
预测时使用BatchNorm
上面介绍了在训练过程中使用BatchNorm对一批样本进行归一化的方法,但如果使用同样的方法对需要预测的一批样本进行归一化,则预测结果会出现不确定性。
例如样本A、样本B作为一批样本计算均值和方差,与样本A、样本C和样本D作为一批样本计算均值和方差,得到的结果一般来说是不同的。那么样本A的预测结果就会变得不确定,这对预测过程来说是不合理的。解决方法是在训练过程中将大量样本的均值和方差保存下来,预测时直接使用保存好的值而不再重新计算。
实际上,在BatchNorm的具体实现中,训练时会计算均值和方差的移动平均值。在飞桨中,默认是采用如下方式计算:
在训练过程的最开始将和设置为0,每次输入一批新的样本,计算出和,然后通过上面的公式更新和,在训练的过程中不断的更新它们的值,并作为BatchNorm层的参数保存下来。预测的时候将会加载参数和,用他们来代替和 。
丢弃法
Dropout
丢弃或者保持输入的每个元素独立。Dropout是一种正则化手段,通过在训练过程中阻止神经元节点间的相关性来减少过拟合。根据给定的丢弃概率,dropout操作符按丢弃概率随机将一些神经元输出设置为0,其他的仍保持不变。
Dropout层可以删除,提高执行效率。
接口定义:
class paddle.fluid.dygraph.Dropout(
p=0.5,
seed=None,
dropout_implementation='downgrade_in_infer',
is_test=False
)
参数说明:
- p (float32,可选) - 输入单元的丢弃概率,即输入单元设置为0的概率。默认值:0.5
- seed (int,可选) - 整型数据,用于创建随机种子。如果该参数设为None,则使用随机种子。注:如果给定一个整型种子,始终丢弃相同的输出单元。训练过程中勿用固定不变的种子。默认值:None。
- dropout_implementation(str,可选) - 丢弃单元的方式,有两种’downgrade_in_infer’和’upscale_in_train’两种选择,默认:’downgrade_in_infer’。具体作用可以参考一下描述。
- downgrade_in_infer(default), 在预测时减小输出结果
- train: out = input * mask
- inference: out = input * (1.0 - p)
- (mask是一个张量,维度和输入维度相同,值为0或1,值为0的比例即为
p
) - upscale_in_train, 增加训练时的结果
- train: out = input * mask / ( 1.0 - p )
- inference: out = input
- (mask是一个张量,维度和输入维度相同,值为0或1,值为0的比例即为
p
)
- downgrade_in_infer(default), 在预测时减小输出结果
- is_test (bool,可选) - 标记是否是测试阶段。此标志仅对静态图模式有效。对于动态图模式,请使用
eval()
接口。默认:False。
参考:Dropout
downgrade_in_infer
使用downgrade_in_infer,将在预测时减小输出结果:
import paddle.fluid as fluid
from paddle.fluid.dygraph.base import to_variable
import numpy as np
# 设置随机数种子,这样可以保证每次运行结果一致
np.random.seed(100)
data = np.arange(1,13).reshape([-1, 3]).astype('float32')
print('origin data is: \n {}'.format(data))
with fluid.dygraph.guard():
x = to_variable(data)
m = fluid.dygraph.Dropout(p=0.5)
droped_train = m(x)
# 切换到 eval 模式
m.eval()
droped_eval = m(x)
print('droped_train is: \n {}'.format(droped_train.numpy()))
print('droped_eval is: \n {}'.format(droped_eval.numpy()))
输出:
origin data is:
[[ 1. 2. 3.]
[ 4. 5. 6.]
[ 7. 8. 9.]
[10. 11. 12.]]
droped_train is:
[[ 1. 2. 0.]
[ 4. 5. 6.]
[ 7. 8. 9.]
[10. 0. 0.]]
droped_eval is:
[[0.5 1. 1.5]
[2. 2.5 3. ]
[3.5 4. 4.5]
[5. 5.5 6. ]]
可以看出,在训练时,随机丢弃了数组中的一些元素(丢弃的元素值为0),在预测时,所有数据的值均减半(设置了p为0.5)。
upscale_in_train
使用upscale_in_train,将在训练时将数值放大,预测时保持不变:
import paddle.fluid as fluid
from paddle.fluid.dygraph.base import to_variable
import numpy as np
# 设置随机数种子,这样可以保证每次运行结果一致
np.random.seed(100)
data = np.arange(1,13).reshape([-1, 3]).astype('float32')
print('origin data is: \n {}'.format(data))
with fluid.dygraph.guard():
x = to_variable(data)
m = fluid.dygraph.Dropout(p=0.5, dropout_implementation = 'upscale_in_train')
droped_train = m(x)
# 切换到 eval 模式
m.eval()
droped_eval = m(x)
print('droped_train is: \n {}'.format(droped_train.numpy()))
print('droped_eval is: \n {}'.format(droped_eval.numpy()))
输出:
origin data is:
[[ 1. 2. 3.]
[ 4. 5. 6.]
[ 7. 8. 9.]
[10. 11. 12.]]
droped_train is:
[[ 0. 4. 0.]
[ 0. 0. 12.]
[ 0. 16. 0.]
[ 0. 0. 0.]]
droped_eval is:
[[ 1. 2. 3.]
[ 4. 5. 6.]
[ 7. 8. 9.]
[10. 11. 12.]]
可以看出,在训练时,随机丢弃了数组中的一些元素(丢弃的元素值为0),但保留的值放大了两倍(设置了p为0.5),在预测时,所有数据的值保持不变。