日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

pytorch中forwod函數(shù)在父類(lèi)中的調(diào)用方式解讀_python

作者:Ai_Taoism ? 更新時(shí)間: 2023-05-20 編程語(yǔ)言

pytorch forwod函數(shù)在父類(lèi)中的調(diào)用

問(wèn)題背景

最近在研究Detetron2的代碼結(jié)構(gòu)時(shí),發(fā)現(xiàn)有些網(wǎng)絡(luò)代碼里面沒(méi)有forward函數(shù),卻照樣可以推理,深入挖掘之后,發(fā)現(xiàn)其將forword函數(shù)都寫(xiě)在了同一個(gè)父類(lèi)里面。

這就牽涉到了下面這個(gè)問(wèn)題,子類(lèi)中沒(méi)有forward函數(shù),只有父類(lèi)中有forward函數(shù),這樣能不能正常調(diào)用網(wǎng)絡(luò)。

import torch.nn as nn

class Network1(nn.Module):
? ? def __init__(self):
? ? ? ? super().__init__()

? ? def forward(self,x):
? ? ? ? return x

class Network2(Network1):
? ? def __init__(self):
? ? ? ? super().__init__()


data = [1,2,3]
model = Network2().eval()
output = model(data)
print(output)

輸出結(jié)果如下:

[1,2,3]

pytorch forward方法調(diào)用原理

在使用Pytorch自定義網(wǎng)絡(luò)模型的時(shí)候,我們需要繼承nn.Module這個(gè)類(lèi),然后定義forward方法來(lái)實(shí)現(xiàn)前向轉(zhuǎn)播。

如下圖的一個(gè)自定義的網(wǎng)絡(luò)模型

首先該網(wǎng)絡(luò)模型的初始化方法__init__需要繼承父類(lèi)nn.Module的初始化方法,用語(yǔ)句super().init()實(shí)現(xiàn)。

并在初始化方法里面,定義了卷積、BN、激活函數(shù)等。接下來(lái)定義forward方法,將整個(gè)網(wǎng)絡(luò)連接起來(lái)。

有了上面的定義,我們可以實(shí)例化一個(gè)對(duì)象,例如:

fire2 = Fire(96, 128,16,64,64)

實(shí)現(xiàn)前向傳播,使用?y= fire2(x)?其中x是該網(wǎng)絡(luò)的輸入,y是輸出,實(shí)現(xiàn)了forward方法的額功能。

這里就會(huì)有人感到奇怪,forward作為Fire這個(gè)類(lèi)的方法,使用的時(shí)候不應(yīng)該是?y= fire2.forward(x)嗎。

這里為什么一個(gè)類(lèi)的實(shí)例可以當(dāng)做方法直接使用?這是因?yàn)檫@個(gè)Fire類(lèi)繼承的父類(lèi)nn.Module里面定義了__call__方法。

一個(gè)類(lèi)如果定義了__call__方法,則該類(lèi)的實(shí)例就可以作為一個(gè)方法那樣直接使用。

例如下列代碼[1]

class A():
    def __call__(self):
        print('i can be called like a function')
 
a = A()
a()

就會(huì)執(zhí)行print函數(shù),打印其中搞的文字。這里需要區(qū)別的是,實(shí)例化的時(shí)候,類(lèi)的名稱后面括號(hào)可以傳遞參數(shù),例如前面實(shí)例化Fire的時(shí)候,傳遞in_channel,out_channel等參數(shù)。

但是要利用__call__的特性,是在實(shí)例名后面的括號(hào)中傳遞參數(shù),例如上面的例子a(),這里雖然沒(méi)有參數(shù),但是也可以改變__call__的定義使之可以傳遞參數(shù)。

回到網(wǎng)絡(luò)模型的內(nèi)容上來(lái)。翻看nn.Module的部分源碼[2],可以發(fā)現(xiàn),nn.Module里面果然定義了__call__,并且傳遞了參數(shù)*input。在__call__的定義中國(guó),調(diào)用了self.forward。

這里其實(shí)還有一個(gè)點(diǎn)值得注意。其實(shí)nn.Module里面并沒(méi)有定義forward,但他卻調(diào)用self.forward,嚴(yán)格來(lái)說(shuō),他是“想要”調(diào)用self.forward。

如果我們沒(méi)有定義一個(gè)類(lèi),例如Fire,來(lái)繼承nn.Module,并且在這個(gè)類(lèi)里面定義forward,那么nn.Module中__call__下面的self.forward就是無(wú)效的。

這意味著,父類(lèi)中__call__下面調(diào)用的函數(shù),可以在繼承他的子類(lèi)中定義

下面給出一個(gè)簡(jiǎn)單的例子。

class father():
    def __call__(self):
        self.forward()
        print('I''m the father!')

class child(father):
    def forward(self):
        print('Forward!')
F=father()
C=child()

這里定義了父類(lèi)father,并定義了繼承他的一個(gè)子類(lèi)child。此外還進(jìn)行了他們的實(shí)例化。

顯然,在father的__call__方法下面,調(diào)用了self.forward,但是沒(méi)有定義。child在繼承了father之后,定義了forward。

首先,這段代碼不會(huì)報(bào)錯(cuò),即使father的__call__下面的self.forward并沒(méi)有定義,這也是前面我說(shuō)的,雖然沒(méi)有定義forward,但是可以理解為他“想要”調(diào)用self.forward。

那么在child記成了father之后,進(jìn)行了forward的定義,這使得child本身可以調(diào)用forward。

在上面這段代碼的基礎(chǔ)上,如果我們執(zhí)行F(),匯報(bào)下面這一段錯(cuò)誤,這解釋了forward沒(méi)有定義,只是“想要”調(diào)用self.forward。

如果我們執(zhí)行C(),則如下圖輸出。

顯然,在child中補(bǔ)充了forward的定義,就可以成功調(diào)用。

總結(jié)

原文鏈接:https://blog.csdn.net/ahhhhhh520/article/details/124437247

欄目分類(lèi)
最近更新