全连接层的数学表述可以参见Maples丶丶的《详解神经网络的前向传播和反向传播(从头推导)》的“前向传播”一节。
为了容易理解,我还是从经典的波士顿房价谈起。波士顿房价的详细内容请参见https://www.paddlepaddle.org.cn/documentation/docs/zh/beginners_guide/basics/fit_a_line/README.cn.html
简化一下,假设房价y与犯罪率x1、教育资源x2、建设时间x3、收入水平x4这四个因素线性相关:
则 y = a1*x1 + a2*x2 + a3*x3 + a4*x4 + b
我通过建立 Paddle Fluid 的全连接层fc,就是根据大量的房价的调研数据,即把已知大量的 y 与 x1、x2、x3、x4对应关系的数据告诉FC,FC通过学习求出a1、a2、a3、a4、b。通过一段的学习后,即使给FC一组为见过的x1、x2、x3、x4,FC也能预测出y。
常量 b 由 fc 的 bias_attr 参数指定。为了简化,我使用了默认值None,表示该参数由 param_attr 参数决定。而 param_attr 也使用了默认值None,意味着a1、a2、a3、a4采用Xavier初始化方式以及偏置参数 b = 0。
所谓全连接层是指每个输入节点都与每个输出节点相连。这里只有一个输出节点,所以 fc 的 size = 1,因不考虑非线性的激活函数,因而 fc 的 act = None。fc 函数的使用可写为如下格式:
y = paddle.fluid.layers.fc(input = x,size=1,act=None) #语句1
上式中的参数input=x中的x是一个Tensor(对于复杂问题可以是多个Tensor),本例指有四个输入节点(shape=4),每个输入节点可以接受32bit的浮点数(dtype=float32)的Tensor。
我想给输入端喂入的数据就是犯罪率x1、教育资源x2、建设时间x3、收入水平x4,假设x1=0.00632,x2=18,x3=2.31,x4=0.538
则在程序中可以如下定义数据:
input_data = numpy.array([[0.00632,18,2.31,0.538]]).astype('float32') #语句2
我如果定义成numpy.array([0.00632,18,2.31,0.538]),则喂入数据时报错。我想,这是因为全连接层是为了接收多组数据而设计的。我虽然只提供了一组数据,也不能shape=(4),而必须使得shape=(n,4) n>=1。参见https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/layers_cn/fc_cn.html
但是这组数据不能直接喂入到全连接层的输入节点,必须通过输入层才能将数据输入到神经网络中。输入层可以使用data算子创建,本例的输入层在程序中可以如下定义,注意输入层必须与输入的数据完全匹配才行:
x = paddle.fluid.data(name='datax',shape=[-1,4],dtype='float32') #语句3
其中语句3的x与语句1的input值保持一致。上句中的name='datax'是输入层输出的前缀标识。
注:老版本使用的是 paddle.fluid.layers.data,fluid1.6改为 paddle.fluid.data后,Executor运行时检查输入数据的维度和数据类型。详见
https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/fluid_cn/data_cn.html
https://www.paddlepaddle.org.cn/documentation/docs/zh/api_cn/layers_cn/data_cn.html
语句3和语句2就搭建了一个简单的神经网络,语句2准备了输入数据。欲使网络工作,还需要定义执行器:
cpu = paddle.fluid.core.CPUPlace()
exe = paddle.fluid.Executor(cpu)一致。
exe.run(paddle.fluid.default_startup_program())
下面就可以喂入数据,指定需要观察的中间过程:
outs = exe.run( feed={'datax':input_data}, # datax与语句3的name值保持一致,input_data与语句2的input_data一致。
fetch_list=[y]) # 对全连接层的输出值感兴趣
这个例子的完整程序如下:
import paddle.fluid as fluid
import numpy
#准备原始数据
input_data=numpy.array([[0.00632,18,2.31,0.538]]).astype('float32')
#print(input_data.shape) # 查看原始数据的shape
#定义输入层
#以下2条语句都可以用。任选其中一条
x=fluid.data(name="datax",shape=[-1,4],dtype='float32') # 喂入的数据组数不确定
# x=fluid.data(name="datax",shape=[1,4],dtype='float32') # 只喂入一组数据
#定义全连接层
y=fluid.layers.fc(input=x,size=1,act=None)
#定义执行器:cpu版
cpu = fluid.core.CPUPlace()
exe = fluid.Executor(cpu)
exe.run(fluid.default_startup_program()) # 运行默认的启动程序
# 运行并打印结果
out = exe.run(feed={'datax':input_data},fetch_list=[x,y])
print(out)
某次的运行结果(因为有学习能力,所以每次的结果不同)如下:
那么a1、a2、a3、a4到底等于多少呢?那是机器学习自己调整的模型参数,我看不到。我只关心给一组x1、x2、x3、x4后,y是多少。
为了达到实用效果,我还要对这个FC进行考核,把考核的结果通知FC,FC就会不断进步了。这牵涉到误差计算和优化方法,相关的学习笔记我会另外再写。
最后,选取 fluid1.5 上的一个官方代码实例,作为本篇的结尾。
#!/usr/bin/python
#_*_ coding: utf-8 _*_
'''
官网上的一个fluid 配置网络的代码示例,略作改编
https://www.paddlepaddle.org.cn/documentation/docs/zh/1.5/beginners_guide/programming_guide/programming_guide.html#id2
CSY 2019-2-30
'''
#加载库
import paddle.fluid as fluid
import numpy
'''
已知:
当x=1时,y=2;
当x=2时,y=4;
当x=3时,y=6;
当x=4时,y=8;
'''
#定义X数值
train_data=numpy.array([[1.0],[2.0],[3.0],[4.0]]).astype('float32')
#定义期望预测的真实值y_true
# y_true = numpy.array([[2.0],[4.0],[6.0],[8.0]]).astype('float32') # 用于损失函数,暂忽略
#定义输入层
x = fluid.data(name="x",shape=[-1,1],dtype='float32')
#定义全连接层
y_predict = fluid.layers.fc(input=x,size=1,act=None)
#参数初始化
cpu = fluid.core.CPUPlace()
exe = fluid.Executor(cpu)
exe.run(fluid.default_startup_program())
#开始训练
outs = exe.run(
feed={'x':train_data},
fetch_list=[y_predict.name])
#观察结果
print (outs) # 一组随机数字,与 y_true 相差甚远
运行结果如下:
最后,附上官方的相关解释:
关于paddle.fluid.data
paddle.fluid.data()是一个OP(算子),作用就是创建一个全局变量,可供计算图中的算子访问,可作为占位符用于数据输入。
name 是paddle.fluid.data()创建的全局变量的名字,是输入层输出的前缀标识。
shape 声明了paddle.fluid.data()创建的全局变量的维度信息。
shape中的None 表示不确定该维的元素数量,待程序执行中确定。
shape中的-1 只能在shape的最前面,表示可以适应任何 batch size
dtype 是paddle.fluid.data()创建的全局变量的数据类型,支持 bool,float16,float32,float64,int8,int16,int32,int64。
用户 feed 的数据必须与 paddle.fluid.data() 创建的变量具有相同的 shape
虽然feed的数据,其类型是unsigned Byte,但softmax 回归是要进行浮点运算的,所以数据类型都转换成了float32
关于paddle.fluid.layers.fc
paddle.fluid.layers.fc()是一个OP,作用就是建立一个全连接层。为每个输入的Tensor创建一个权重变量,即一个从每个输入单元到每个输出单元的全连接权重矩阵。
FC层将每个输入Tensor和其对应的权重(weights)相乘得到shape为 [M,size] 输出Tensor,其中 M 为batch_size大小。如果有多个输入Tensor,则多个shape为 [M,size] 的Tensor计算结果会被累加起来,作为最终输出。
来源:CSDN
作者:aLife2P6
链接:https://blog.csdn.net/aLife2P6/article/details/103728321