1. 程式人生 > 其它 >基於tensorflow _init_、 build和call的使用小結

基於tensorflow _init_、 build和call的使用小結

1.介紹

在使用tf構建網路框架的時候,經常會遇到__init__、build 和call這三個互相搭配著使用,那麼它們的區別主要在哪裡呢?

1)__init__主要用來做引數初始化用,比如我們要初始化卷積的一些引數,就可以放到這裡面

2)call可以把型別的物件當做函式來使用,這個物件可以是在__init__裡面也可以是在build裡面

3)build一般是和call搭配使用,這個時候,它的功能和__init__很相似,當build中存放本層需要初始化的變數,當call被第一次呼叫的時候,會先執行build()方法初始化變數,但後面再呼叫到call的時候,是不會再去執行build()方法初始化變數

2.程式碼

    class RB(tf.keras.layers.Layer): 
     def __init__(self, num_filters, *args, **kwargs):
     self.num_filters = num_filters
     super(RB, self).__init__(*args, **kwargs)
     #按需求添加捲積
     def build(self, input_shape):
     #按需求添加捲積
     self._layers = [
     ]
     super(RB, self).build(input_shape)
     
     def call(self, tensor):
     for layer in self._layers:
      tensor = layer(tensor) 
    class DecodeNet(tf.keras.layers.Layer):
     
     def __init__(self, num_filters, *args, **kwargs):
     self.num_filters = num_filters
     super(DecodeNet, self).__init__(*args, **kwargs)
     self.rb_block0 = RB(self.num_filters)
     self.rb_block1 = RB(self.num_filters)
     self.rb_block2 = RB(self.num_filters)
     
     def build(self, input_shape):
     self._layers = [
      RB(self.num_filters),
      RB(self.num_filters),
      RB(self.num_filters),
     ]
     super(DecodeNet, self).build(input_shape)
     
     def call(self, tensor):
     tensor = self.rb_block0(tensor)
     tensor = self.rb_block1(tensor)
     for layer in self._layers:
      tensor = layer(tensor)
     tensor = self.rb_block2(tensor)
     return tensor

補充:Python類中的__init__() 和 self 的解析

1、Python中self的含義

self,英文單詞意思很明顯,表示自己,本身。

此處有幾種潛在含義:

1.這裡的自己,指的是,例項Instance本身。

2.同時, 由於說到“自己”這個詞,都是和相對而言的“其他”而說的;而此處的其他,指的是,類Class,和其他變數,比如區域性變數,全域性變數等。

此處的self,是個物件(Object),是當前類的例項。

因此,對應的self.valueName 和
self.function()中的valueName:表示self物件,即例項的變數。與其他的,Class的變數,全域性的變數,區域性的變數,是相對應的。

function:表示是呼叫的是self物件,即例項的函式。與其他的全域性的函式,是相對應的。

2、Python中為何要有self

那就是:

在類的程式碼(函式)中,需要訪問當前的例項中的變數和函式的,即,訪問Instance中的:

對應的變數(屬性,property):Instance.ProperyNam,去讀取之前的值和寫入新的值

呼叫對應函式(function):Instance.function(),即執行對應的動作

-> 而需要訪問例項的變數和呼叫例項的函式,當然需要對應的例項Instance物件本身

-> 而Python中就規定好了,函式的第一個引數,就必須是例項物件本身,並且建議,約定俗成,把其名字寫為self

-> 所以,我們需要self(需要用到self)

而如果沒有用到self,即程式碼中,去掉self後,那種寫法所使用到的變數,實際上不是你所希望的,不是真正的例項中的變數和函式,而是的訪問到了其他部分的變數和函數了。甚至會由於沒有合適的初始化例項變數,而導致後續無法訪問的錯誤。

下面,就通過程式碼,來演示,如果去掉self,或者沒有合理的使用self,會出現哪些錯誤。

3、首先來看一下__init__()和self物件

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author: antcolonies 
    class Person(object):
     def __init__(self, name, lang, website):
      self.name = name
      self.lang = lang
      self.website = website
     
      print('self: ', self)
      print('type of self: ', type(self))
    '''
    未例項化時,執行程式,構造方法沒有執行
    ''' 
    p = Person('Tim', 'English', 'www.universal.com') 
    '''例項化後執行的結果
    self: <__main__.Person object at 0x00000000021EAF98>
    type of self: <class '__main__.Person'>
    '''

可以看出self為例項變數p,是一個Person型別的物件。

    class Dog(object):  
     def __init__(self,name,dog_type):
      self.name = name
      self.type = dog_type 
     def sayhi(self):
      print("hello,I am a dog, my name is ",self.name) 
     
    d = Dog('LiChuang',"京巴")   # 例項化
    d.sayhi()

以下是d = Dog('LiChuang',"京巴")例項化的示意圖:

4、如果沒有在__init__中初始化對應的例項變數的話,導致後續引用例項變數會出錯

如下程式碼,完整的演示了,如果沒有在類Class的最初的__init__函式中,正確的初始化例項變數,則會導致後續沒有變數可用,因而出現AttributeError的錯誤:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author: antcolonies 
    name = 'whole global name'
    '''
    注:此處全域性的變數名,寫成name,只是為了演示而用
    實際上,好的程式設計風格,應該寫成gName之類的名字,
    以表示該變數是Global的變數
    ''' 
    class Person(object):
     def __init__(self, newPersonName):
      # self.name = newPersonName
      '''
      如果此處不寫成self.name
      那麼此處的name,只是__init__函式中的區域性臨時變數name而已
      和全域性中的name,沒有半毛錢關係
      '''
      name = newPersonName
      '''
      此處只是為了程式碼演示,而使用了局部變數name,
      不過需要注意的是,此處很明顯,由於接下來的程式碼也沒有利用到此處的區域性變數name
      則就導致了,此處的name變數,實際上被浪費了,根本沒有利用到
      '''
     def sayYourName(self):
      '''
      此處由於找不到例項中的name變數,所以會報錯:
      AttributeError: Person instance has no attribute 'name'
      '''
      print('My name is %s' %self.name)
     
    def selfAndInitDemo():
     personInstance = Person('Tim')
     personInstance.sayYourName() 
    if __name__ == '__main__':
     selfAndInitDemo()
     
    ''' 未使用self.name時拋異常
    Traceback (most recent call last):
     File "E:/python14_workspace/s14/day06/test_1.py", line 18, in <module>
     selfAndInitDemo()
     File "E:/python14_workspace/s14/day06/test_1.py", line 15, in selfAndInitDemo
     personInstance.sayYourName()
     File "E:/python14_workspace/s14/day06/test_1.py", line 11, in sayYourName
     print('My name is %s' %self.name)
    AttributeError: 'Person' object has no attribute 'name'
    '''

從上述程式碼可見,由於在類的初始化(例項化)的__init__函式中,沒有給self.name設定值,使得例項中,根本沒有name這個變數,導致後續再去訪問self.name,就會出現AttributeError的錯誤了。

對應的,如果寫成self.name,則意思就正確了,就是初始化的時候,給例項中新增加,並且正常設定了正確的值newPersionName了,所以後續再去通過self.name,就可以訪問到,當前例項中正確的變數name了。

相應的正確寫法的程式碼如下:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author: antcolonies 
    name = 'whole global name'
    '''
    注:此處全域性的變數名,寫成name,只是為了演示而用
    實際上,好的程式設計風格,應該寫成gName之類的名字,
    以表示該變數是Global的變數
    ''' 
    class Person(object):
     def __init__(self, newPersonName):
      self.name = newPersonName
      '''
      此處正確的,通過訪問self.name的形式,實現了:
       1.給例項中,增加了name變數
       2.並且給name賦了初值,為newPersionName
      '''
     def sayYourName(self):
      '''
      此處由於開始正確的初始化了self物件,使得其中有了name變數,
      所以此處可以正確訪問了name值了
      '''
      print('My name is %s' %self.name)
     
    def selfAndInitDemo():
     personInstance = Person('Tim')
     personInstance.sayYourName()
     
    if __name__ == '__main__':
     selfAndInitDemo() 
    '''My name is Tim'''

5、在函式中,使用對應的變數

雖然程式碼是可以執行的,但是實際上卻是使用的,不是例項中的變數

有時候,雖然你寫的程式碼,可以執行,但是使用到的變數,由於沒有加self,實際上是用到的不是例項的變數,而是其他的變數。

此類問題,主要和Python中的變數的作用域有關,但是此處例子中,也和是否使用self有關:

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    # Author: antcolonies 
    name = 'whole global name'
    '''
    注:此處全域性的變數名,寫成name,只是為了演示而用
    實際上,好的程式設計風格,應該寫成gName之類的名字,
    以表示該變數是Global的變數
    '''
    class Person(object):
     name = 'class global name' 
     def __init__(self, newPersonName):
      # self.name = newPersonName
      '''
      此處,沒有使用self.name
      而使得此處的name,實際上仍是區域性變數name
      雖然此處賦值了,但是後面沒有被利用到,屬於被浪費了的區域性變數name
      '''
      name = newPersonName
     def sayYourName(self):
      '''
      此處,之所以沒有像之前一樣出現:
      AttributeError: Person instance has no attribute 'name'
      那是因為,雖然當前的例項self中,沒有在__init__中初始化對應的name變數,例項self中沒有對應的name變數
      但是由於例項所對應的類Person,有對應的name變數,所以也是可以正常執行程式碼的
      對應的,此處的self.name,實際上是Person.name
      '''
      print('My name is %s' %self.name)
      print('Name within class Person is actually the global name: %s' %name)
      print("Only access Person's name via Person.name = %s" %(Person.name))
     
    def selfAndInitDemo():
     personInstance = Person('Tim')
     personInstance.sayYourName()
     print('whole global name is %s' %name)
     
    if __name__ == '__main__':
     selfAndInitDemo()
    '''
    My name is class global name
    Name within class Person is actually the global name: whole global name
    Only access Person's name via Person.name = class global name
    whole global name is whole global name
    '''

其中,可見,此處開始__init__中,沒有給self例項初始化對應的name,

而後面的函式sayYourName中,雖然可以呼叫到self.name而沒有出現AttributeError錯誤,

但是實際上此處的值,不是所期望的,傳入的name,即"Tim",而是類中的name的值,即"class global name"。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援指令碼之家。如有錯誤或未考慮完全的地方,望不吝賜教。