
簡 介: 這是 Paddle中的模型與層 的内容學習筆記。對于Paddle中的層的構造,操作進行了初步的測試與相關的學習。 關鍵詞
: Layer,Paddle
01 模型與層
模型是深度學習中的重要概念之一。模型的核心功能是将一組輸入變量經過一系列計算,映射到另一組輸出變量,該映射函數即代表一種深度學習算法。在
Paddle
架構中,模型包括以下兩方面内容:
- 一系列層的組合用于進行映射(前向執行)
- 一些參數變量在訓練過程中實時更新
本文檔中,你将學習如何定義與使用
Paddle
模型,并了解模型與層的關系。
一、在Paddle中定義模型與層
在
Paddle
中,大多數模型由一系列層組成,層是模型的基礎邏輯執行單元。層中持有兩方面内容:
- 一方面是計算所需的變量,以臨時變量或參數的形式作為層的成員持有
- 另一方面則持有一個或多個具體的
來完成相應的計算。Operator
1、模型與層
從零開始建構變量、
Operator
,進而組建層、模型是一個很複雜的過程,并且當中難以避免的會出現很多備援代碼,是以
Paddle
提供了基礎資料類型
paddle.nn.Layer
,來友善你快速的實作自己的層和模型。模型和層都可以基于
paddle.nn.Layer
擴充實作,是以也可以說模型隻是一種特殊的層。下面将示範如何利用
paddle.nn.Layer
建立自己的模型:
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
self.flatten = paddle.nn.Flatten()
def forward(self, inputs):
y = self.flatten(inputs)
return y
目前示例中,通過繼承 paddle.nn.Layer 的方式建構了一個模型類型 Model ,模型中僅包含一個 paddle.nn.Flatten 層。模型執行時,輸入變量inputs會被 paddle.nn.Flatten 層展平。
2、測試用例
x = paddle.to_tensor([[1,2,3],[4,5,6]])
print(x)
model = Model()
y = model(x)
print(y)
Tensor(shape=[2, 3], dtype=int64, place=CPUPlace, stop_gradient=True,
[[1, 2, 3],
[4, 5, 6]])
Tensor(shape=[2, 3], dtype=int64, place=CPUPlace, stop_gradient=True,
[[1, 2, 3],
[4, 5, 6]])
這是什麼鬼?結果怎麼沒有被層展平呢?
将前面的代碼修改:
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
def forward(self, inputs):
y = self.Flatten(inputs)
return y
def Flatten(self, x):
xx = x.numpy().flatten()
return paddle.to_tensor(xx)
x = paddle.to_tensor([[1,2,3],[4,5,6]])
print(x)
model = Model()
y = model(x)
print(y)
可以得到想要的結果了:
Tensor(shape=[2, 3], dtype=int64, place=CPUPlace, stop_gradient=True,
[[1, 2, 3],
[4, 5, 6]])
Tensor(shape=[6], dtype=int64, place=CPUPlace, stop_gradient=True,
[1, 2, 3, 4, 5, 6])
二、子層接口
如果想要通路或修改一個模型中定義的層,則可以調用SubLayer相關的接口。
1、繼承子層
以上文建立的簡單模型為例
,
如果想要檢視模型中定義的所有子層:
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
self.flatten = paddle.nn.Flatten()
def forward(self, inputs):
y = self.Flatten(inputs)
return y
[Flatten()]
('flatten', Flatten())
以看到,通過調用
model.sublayers()
接口,列印出了前述模型中持有的全部子層(這時模型中隻有一個
paddle.nn.Flatten
子層)。
而周遊
model.named_sublayers()
時,每一輪循環會拿到一組 ( 子層名稱
('flatten')
,子層對象
(paddle.nn.Flatten)
)的元組。
下面增加self中的一個層次,可以看到多出了更多的層。
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
self.flatten = paddle.nn.Flatten()
self.f1 = paddle.nn.Flatten()
def forward(self, inputs):
y = self.Flatten(inputs)
return y
model = Model()
print(model.sublayers())
print("----------------------")
for item in model.named_sublayers():
print(item)
[Flatten(), Flatten()]
('flatten', Flatten())
('f1', Flatten())
2、增加子層
接下來如果想要進一步添加一個子層,則可以調用
add_sublayer()
接口:
fc = paddle.nn.Linear(10, 3)
model.add_sublayer("fc", fc)
print(model.sublayers())
[Flatten(), Linear(in_features=10, out_features=3, dtype=float32)]
可以看到
model.add_sublayer()
向模型中添加了一個
paddle.nn.Linear
子層,這樣模型中總共有
paddle.nn.Flatten
和
paddle.nn.Linear
兩個子層了。
3、修改子層
通過上述方法可以往模型中添加成千上萬個子層,當模型中子層數量較多時,如何高效地對所有子層進行統一修改呢?
Paddle
提供了
apply()
接口。通過這個接口,可以自定義一個函數,然後将該函數批量作用在所有子層上:
def function(layer):
print(layer)
model.apply(function)
Flatten()
Linear(in_features=10, out_features=3, dtype=float32)
Model(
(flatten): Flatten()
(fc): Linear(in_features=10, out_features=3, dtype=float32)
)
目前例子中,定義了一個以
layer
作為參數的函數
function
,用來列印傳入的
layer
資訊。通過調用
model.apply()
接口,将
function
作用在模型的所有子層中,也是以輸出資訊中列印了model中所有子層的資訊。
另外一個批量通路子層的接口是
children()
或者
named_children()
。這兩個接口通過
Iterator
的方式通路每個子層:
sublayer_iter = model.children()
for sublayer in sublayer_iter:
print(sublayer)
Flatten()
Linear(in_features=10, out_features=3, dtype=float32)
可以看到,周遊
model.children()
時,每一輪循環都可以按照子層注冊順序拿到對應 paddle.nn.Layer 的對象。
三、層中的變量成員
1、參數變量添加與修改
有的時候希望向網絡中添加一個參數作為輸入。比如在使用圖像風格轉換模型時,會使用參數作為輸入圖像,在訓練過程中不斷更新該圖像參數,最終拿到風格轉換後的圖像。
這時可以通過
create_parameter()
與
add_parameter()
組合,來建立并記錄參數:
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
img = self.create_parameter([1,3,256,256])
self.add_parameter("img", img)
self.flatten = paddle.nn.Flatten()
def forward(self):
y = self.flatten(self.img)
return y
上述例子建立并向模型中添加了一個名字為
"img"
的參數。随後可以直接通過調用
model.img
來通路該參數。
對于已經添加的參數,可以通過
parameters()
named_parameters()
來通路
model = Model()
model.parameters()
print('--------------------------------------------')
for item in model.named_parameters():
print(item)
[Parameter containing:
Tensor(shape=[1, 3, 256, 256], dtype=float32, place=CPUPlace, stop_gradient=False,
[[[[-0.00323536, 0.00417978, 0.00387184, ..., -0.00263438,
0.00336105, -0.00079275],
[-0.00398997, 0.00305213, 0.00338405, ..., 0.00321609,
0.00385862, 0.00383085],
[ 0.00456822, 0.00335924, -0.00396630, ..., -0.00260351,
0.00388722, 0.00292703],
...,
...,
[-0.00302772, -0.00052290, -0.00259735, ..., 0.00325148,
0.00051726, 0.00464376],
[ 0.00238924, -0.00105374, 0.00219904, ..., -0.00279356,
-0.00214116, -0.00319181],
[ 0.00180969, 0.00476100, 0.00380237, ..., 0.00249749,
0.00374650, 0.00050141]]]])]
('img', Parameter containing:
Tensor(shape=[1, 3, 256, 256], dtype=float32, place=CPUPlace, stop_gradient=False,
[[[[-0.00323536, 0.00417978, 0.00387184, ..., -0.00263438,
0.00336105, -0.00079275],
[-0.00398997, 0.00305213, 0.00338405, ..., 0.00321609,
0.00385862, 0.00383085],
[ 0.00456822, 0.00335924, -0.00396630, ..., -0.00260351,
0.00388722, 0.00292703],
...,
...,
[-0.00302772, -0.00052290, -0.00259735, ..., 0.00325148,
0.00051726, 0.00464376],
[ 0.00238924, -0.00105374, 0.00219904, ..., -0.00279356,
-0.00214116, -0.00319181],
[ 0.00180969, 0.00476100, 0.00380237, ..., 0.00249749,
0.00374650, 0.00050141]]]]))
可以看到,
model.parameters()
将模型中所有參數以數組的方式傳回。
在實際的模型訓練過程中,當調用反向圖執行方法後,
Paddle
會計算出模型中每個參數的梯度并将其儲存在相應的參數對象中。如果已經對該參數進行了梯度更新,或者出于一些原因不希望該梯度累加到下一輪訓練,則可以調用
clear_gradients()
來清除這些梯度值。
model = Model()
out = model()
out.backward()
model.clear_gradients()
2、非參數變量的添加
參數變量往往需要參與梯度更新,但很多情況下隻是需要一個臨時變量甚至一個常量。比如在模型執行過程中想将一個中間變量儲存下來,這時需要調用
create_tensor()
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
self.saved_tensor = self.create_tensor(name="saved_tensor0")
self.flatten = paddle.nn.Flatten()
self.fc = paddle.nn.Linear(10, 100)
def forward(self, input):
y = self.flatten(input)
# Save intermediate tensor
paddle.assign(y, self.saved_tensor)
y = self.fc(y)
return y
model = Model()
print(model.sublayers())
[Flatten()]
這裡調用
self.create_tensor()
創造了一個臨時變量并将其記錄在模型的
self.saved_tensor
中。在模型執行時調用
paddle.assign
用該臨時變量記錄變量
y
的數值。
3、Buffer變量的添加
Buffer
的概念僅僅影響動态圖向靜态圖的轉換過程。在上一節中建立了一個臨時變量用來臨時存儲中間變量的值。但這個臨時變量在動态圖向靜态圖轉換的過程中并不會被記錄在靜态的計算圖當中。如果希望該變量成為靜态圖的一部分,就需要進一步調用
register_buffers()
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
saved_tensor = self.create_tensor(name="saved_tensor0")
self.register_buffer("saved_tensor", saved_tensor, persistable=True)
self.flatten = paddle.nn.Flatten()
self.fc = paddle.nn.Linear(10, 100)
def forward(self, input):
y = self.flatten(input)
# Save intermediate tensor
paddle.assign(y, self.saved_tensor)
y = self.fc(y)
return y
這樣在動态圖轉靜态圖時
saved_tensor
就會被記錄到靜态圖中。
對于模型中已經注冊的
Buffer
,可以通過
buffers()
named_buffers()
進行通路
:
model = Model()
print(model.buffers())
for item in model.named_buffers():
print(item)
[Tensor(Not initialized)]
('saved_tensor', Tensor(Not initialized))
model.buffers()
以數組形式傳回了模型中注冊的所有
Buffer
四、執行層的功能
經過一系列對模型的配置,假如已經準備好了一個
Paddle
模型如下:
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
self.flatten = paddle.nn.Flatten()
def forward(self, inputs):
y = self.flatten(inputs)
return y
想要執行該模型,首先需要對執行模式進行設定
1、執行模式設定
模型的執行模式有兩種,如果需要訓練的話調用
train()
,如果隻進行前向執行則調用
eval()
:
x = paddle.randn([10, 1], 'float32')
model = Model()
model.eval() # set model to eval mode
out = model(x)
model.train() # set model to train mode
out = model(x)
這裡将模型的執行模式先後設定為
eval
train
。兩種執行模式是互斥的,新的執行模式設定會覆寫原有的設定。
2、執行函數
模式設定完成後可以直接調用執行函數。可以直接調用
forward()
方法進行前向執行,也可以調用
__call__()
,進而執行在
forward()
當中定義的前向計算邏輯。
class Model(paddle.nn.Layer):
def __init__(self):
super(Model, self).__init__()
self.flatten = paddle.nn.Flatten()
def forward(self, inputs):
y = self.Flatten(inputs)
return y
def Flatten(self, inputs):
return paddle.to_tensor(inputs.numpy().flatten())
model = Model()
x = paddle.randn([10, 1], 'float32')
out = model(x)
print(out)
Tensor(shape=[10], dtype=float32, place=CPUPlace, stop_gradient=True,
[-0.26968753, -2.34697795, 0.87075204, 1.20670414, 2.26653862,
0.25821996, 0.70133287, 1.44512081, 0.96671742, 0.96629554])
這裡直接調用
__call__()
方法調用模型的前向執行邏輯。
3、添加hook函數
有時希望某些變量在進入層前首先進行一些預處理,這個功能可以通過注冊
hook
來實作。
hook
是一個作用于變量的自定義函數,在模型執行時調用。對于注冊在層上的
hook
函數,可以分為
pre_hook
post_hook
兩種。
pre_hook
可以對層的輸入變量進行處理,用函數的傳回值作為新的變量參與層的計算。
post_hook
則可以對層的輸出變量進行處理,将層的輸出進行進一步處理後,用函數的傳回值作為層計算的輸出。
通過
register_forward_post_hook()
接口,我們可以注冊一個
post_hook
def forward_post_hook(layer, input, output):
return 2*output
x = paddle.ones([10, 1], 'float32')
model = Model()
forward_post_hook_handle = model.flatten.register_forward_post_hook(forward_post_hook)
out = model(x)
print(out)
Tensor(shape=[10, 1], dtype=float32, place=CPUPlace, stop_gradient=True,
[[2.],
[2.],
[2.],
[2.],
[2.],
[2.],
[2.],
[2.],
[2.],
[2.]])
五、儲存模型參數
如果想要儲存模型中參數而不存儲模型本身,則可以首先調用
state_dict()
接口将模型中的參數以及永久變量存儲到一個
Python
字典中,随後儲存該字典。
model = Model()
state_dict = model.state_dict()
paddle.save( state_dict, "./paddle_dy.pdparams")
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/framework/io.py:729: UserWarning: The input state dict is empty, no need to save.
warnings.warn("The input state dict is empty, no need to save.")
運作了第二遍的時候,就沒有這個提示。
※ 總 結 ※
這是
■ 相關文獻連結:
□
★ 本文來自于CSDN文獻:<
簡 介: 本文對于來自于龍邱通過對于來自LQ發送過來的新版的信标燈進行測試,可以看到: 它可以與以往的信标燈控制器進行相容; 通過光電檢測,可以檢測到上面經過的車模物體;無線車模上面安裝磁鐵。 關鍵詞
: 智能車競賽,信标燈
01 信标燈
在
第十七屆智能車競賽競速比賽中,有一個組别“平衡信标組”,它利用左右雙輪車模完成對場地内的信标搜尋與熄滅(碾壓)。現在收到從LongQiu發送過來的新版的信标燈。他們在博文
基于LM567制作的反射式紅外檢測電路,用于節能信标檢測電路基本檢測原理上,對原來的信标燈盤進行了改造。
一、信标燈基本狀況
信标燈是由發光燈盤與燈罩組成。所使用的中央控制器應該與原來的節能信标燈是相容的。
▲ 圖1.1.1 信标燈盤與燈罩
1、發光燈盤
(1)外形尺寸
發CD光牒形狀為圓形,直徑:11.8厘米。 該發CD光牒在使用過程中放置在燈罩的下面,并中心對準燈罩的中心。
(2)接口
發CD光牒具有三個外部接口,它們的功能是:
● 發CD光牒外部接口:
控制器
:連接配接信标燈控制器接口,4Pins。
控制進
: 發CD光牒現場連線,4Pins
控制出
: 發CD光牒現場連線,6Pins
雖然控制進,出(兩個6pins接口)名稱上是有差別的,但在實際中,這兩個接口是等小的。
▲ 圖1.1.2 信标燈外部接口
注意:
雖然控制進、控制出兩個接口在使用上是等效的,但其中除了+24V,PGND是并聯之外,另外兩條信号線(RXDIO,IOOUT)則不是并聯的。
現場通過6pin的現場電纜将所有的發CD光牒連接配接在一起。第一個發CD光牒通過4pin的接口與控制盒相連。 +24V電源可以接入第一個發CD光牒,也可以在最後一個發CD光牒上接入。
▲ 圖1.1.3 信标燈現場連接配接關系
2、燈罩
燈罩的外形為圓形,直徑為,26厘米;中心高度為15毫米。燈罩中心具有直徑為18毫米的圓孔。該圓孔是為了檢測壓過的車模。
燈罩放置在發CD光牒的是上面,中心圓孔對準發CD光牒中心的光電管(ITR9909)。如果發CD光牒中心的圓孔沒有對準發CD光牒的光電管,會造成燈罩無法檢測到外部通過的車模,同時自身也始終處在觸發狀态。
▲ 圖1.1.4 燈罩與發CD光牒擺放位置
二、測試信标燈
下面對于手邊的一個信标燈進行測試。信标燈的控制盒是由LQ發送過來的。
1、連接配接電源與控制器
(1)連接配接+24V
制作臨時的+24V接口,将發CD光牒6PIN的控制出,或者控制入上的 +24V,GND接入直流穩壓電源。
▲ 圖1.2.1 通過6PIn端口連接配接+24V電源
● 測試結果:
工作電源
:使用+24V作為工作電源;
靜态電流
:57mA
(2)連接配接控制盒
使用4pin的扁平電纜将控制盒與發CD光牒連接配接在一起。控制盒的工作電源為+7.5V。
● 控制盒工作狀态:
工作電源
:+7.5V
工作電流
:32mA
▲ 圖1.2.2 将控制盒與發CD光牒連接配接
将控制盒與發CD光牒上電後,按動控制盒上的“RST”按鍵,控制盒會顯示檢測到一個發CD光牒。
▲ 圖1.2.3 控制盒檢測到一個發CD光牒
2、點亮信标燈
信标燈可以獨自在控制盒控制下發光。
▲ 圖1.2.4 發CD光牒在控制和控制下發光
這一點與控制盒控制原有的節能燈發CD光牒的效果是一樣的。
▲ 圖1.2.5 節能燈可以在控制盒控制下正常發光點亮
3、光電觸發
(1)基本原理
CD光牒檢測車模通過的方案是按照
原理制作的。
LM567自激振蕩的方波通過9018驅動ITR9909發送紅外光調制的紅外光,有物體發射回來的光電信号耦合金;LM567進行同步檢波。
根據
LM567 資料手冊中給出的計算公式,LM567振蕩頻率中心值為:
$$f_0 = {{1.1} \over {R_{15} \cdot C_{12} }} = {{1.1} \over {10k \cdot 0.01\mu }} = 11kHz$$
▲ 圖1.2.6 光電檢測原理圖
(2)實測波形
Ⅰ.發送信号
測試T2集電極的電壓波形,可以測量LM567的實際振蕩頻率。
● LM567實際振蕩頻率:
振蕩頻率
:9.909kHz
占空比
:43.59%
▲ 圖1.2.7 實際測試T2集電極的波形
Ⅱ.接收信号
用手在光電管上方通過時,可以測量到LM567的Pin3管腳(接收信号管腳)的波形如下。
▲ 圖1.2.8 接收到的信号
▲ 圖1.2.9 手經過發CD光牒上方
▲ 圖1.2.10 LM567 輸出管腳(Pin8) 的波形
通過上面測試,可以看到光電盤在實際工作情況下可以檢測到上方通過的反射物。
4、信号接入U1
▲ 圖1.2.11 電路中沒有将該信号連入MCU
無線信标功能調試-2021-3-9-HALL檢測與主要接口 中對于原有的無線信标組修改描述,觸發信号應該進入 U1:AD0,也就是其中KEY1的位置。
▲ 圖1.2.12 U8信号接入U1的PAD0
使用示波器可以檢測到在U1的KEY(PA0)處存在U6輸出的檢測信号。由于手邊沒有更多的發CD光牒,是以現在無法測試
5、測試發CD光牒被觸發
使用短接線就控制端口中的IO引線(PIN1)接地(PIN3),可以使得發CD光牒在上電後就開始閃爍。這一點是設計軟體中特地留有用于檢測的功能。
上電後5秒後,發CD光牒便可以接收檢測信号的輸入了。
▲ 圖1.2.12 使用短接線就控制端口中的IO引線(PIN1)接地(PIN3)
通過測試,可以看到 手掠過發CD光牒,觸發發CD光牒熄滅。這說明新版的信标燈可以正常檢測到 上面通過的車模。
▲ 圖1.2.13 手掠過發CD光牒,觸發發CD光牒熄滅
※ 測試總結 ※
通過對于來自LQ發送過來的新版的信标燈進行測試,可以看到:
- 它可以與以往的信标燈控制器進行相容;
- 通過光電檢測,可以檢測到上面經過的車模物體;無線車模上面安裝磁鐵。
往屆的無線信标燈也可以用于今年的車模調試中。可以根據本文提供的原理,補充光電檢測闆即可。也可以沿用往屆的磁鐵觸發。
一、信标燈參考原理圖
1、原理圖
▲ 圖1.1.5 信标燈設計原理圖
▲ 圖1.1.6 信标燈設計 原理圖
2、PCB版圖
▲ 圖1.1.7 信标燈PCB圖
■
第十七屆全國大學智能汽車競賽競速比賽規則● 相關圖表連結:
- 圖1.1.1 信标燈盤與燈罩
- 圖1.1.2 信标燈外部接口
- 圖1.1.3 信标燈現場連接配接關系
- 圖1.1.4 燈罩與發CD光牒擺放位置
- 圖1.2.1 通過6PIn端口連接配接+24V電源
- 圖1.2.2 将控制盒與發CD光牒連接配接
- 圖1.2.3 控制盒檢測到一個發CD光牒
- 圖1.2.4 發CD光牒在控制和控制下發光
- 圖1.2.5 節能燈可以在控制盒控制下正常發光點亮
- 圖1.2.6 光電檢測原理圖
- 圖1.2.7 實際測試T2集電極的波形
- 圖1.2.8 接收到的信号
- 圖1.2.9 手經過發CD光牒上方
- 圖1.2.10 LM567 輸出管腳(Pin8) 的波形
- 圖1.2.12 U8信号接入U1的PAD0
- 圖1.2.12 使用短接線就控制端口中的IO引線(PIN1)接地(PIN3)
- 圖1.2.13 手掠過發CD光牒,觸發發CD光牒熄滅
- 圖1.1.5 信标燈設計原理圖
- 圖1.1.6 信标燈設計 原理圖
- 圖1.1.7 信标燈PCB圖