全連接層的數學表述可以參見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計算結果會被累加起來,作爲最終輸出。