【小白學PyTorch】18 TF2構建自定義模型

忽逢桃林發表於2020-10-04

【機器學習煉丹術】的煉丹總群已經快滿了,要加入的快聯絡煉丹兄WX:cyx645016617

參考目錄:

之前講過了如何用tensorflow構建資料集,然後這一節課講解如何用Tensorflow2.0來建立模型。

TF2.0中建立模型的API基本上都放到了它的Keras中了,Keras可以理解為TF的高階API,裡面封裝了很多的常見網路層、常見損失函式等。 後續會詳細介紹keras的全面功能,本篇文章講解如何構建模型。

1 建立自定義網路層

import tensorflow as tf
import tensorflow.keras as keras

class MyLayer(keras.layers.Layer):
    def __init__(self, input_dim=32, output_dim=32):
        super(MyLayer, self).__init__()

        w_init = tf.random_normal_initializer()
        self.weight = tf.Variable(
            initial_value=w_init(shape=(input_dim, output_dim), dtype=tf.float32),
            trainable=True) # 如果是false則是不參與梯度下降的變數

        b_init = tf.zeros_initializer()
        self.bias = tf.Variable(initial_value=b_init(
            shape=(output_dim), dtype=tf.float32), trainable=True)

    def call(self, inputs):
        return tf.matmul(inputs, self.weight) + self.bias


x = tf.ones((3,5))
my_layer = MyLayer(input_dim=5,
                   output_dim=10)
out = my_layer(x)
print(out.shape)
>>> (3, 10)

這個就是定義了一個TF的網路層,其實可以看出來和PyTorch定義的方式非常的類似:

  • 這個類要繼承tf.keras.layers.Layer,這個pytorch中要繼承torch.nn.Module類似;
  • 網路層的元件在__def__中定義,和pytorch的模型類相同;
  • call()和pytorch中的forward()的類似。

上面程式碼中實現的是一個全連線層的定義,其中可以看到使用tf.random_normal_initializer()來作為引數的初始化器,然後用tf.Variable來產生網路層中的權重變數,通過trainable=True這個引數說明這個權重變數是一個參與梯度下降的可以訓練的變數。

我通過tf.ones((3,5))產生一個shape為[3,5]的一個全是1的張量,這裡面第一維度的3表示有3個樣本,第二維度的5就是表示要放入全連線層的資料(全連線層的輸入是5個神經元);然後設定的全連線層的輸出神經元數量是10,所以最後的輸出是(3,10)。

2 建立一個完整的CNN

import tensorflow as tf
import tensorflow.keras as keras

class CBR(keras.layers.Layer):
    def __init__(self,output_dim):
        super(CBR,self).__init__()
        self.conv = keras.layers.Conv2D(filters=output_dim, kernel_size=4, padding='same', strides=1)
        self.bn = keras.layers.BatchNormalization(axis=3)
        self.ReLU = keras.layers.ReLU()

    def call(self, inputs):
        inputs = self.conv(inputs)
        inputs = self.ReLU(self.bn(inputs))
        return inputs

class MyNet(keras.Model):
    def __init__ (self,input_dim=3):
        super(MyNet,self).__init__()
        self.cbr1 = CBR(16)
        self.maxpool1 = keras.layers.MaxPool2D(pool_size=(2,2))
        self.cbr2 = CBR(32)
        self.maxpool2 = keras.layers.MaxPool2D(pool_size=(2,2))

    def call(self, inputs):
        inputs = self.maxpool1(self.cbr1(inputs))
        inputs = self.maxpool2(self.cbr2(inputs))
        return inputs

model = MyNet(3)
data = tf.random.normal((16,224,224,3))
output = model(data)
print(output.shape)
>>> (16, 56, 56, 32)

這個是構建了一個非常簡單的卷積網路,結構是常見的:卷積層+BN層+ReLU層。可以發現這裡繼承的一個tf.keras.Model這個類。

2.1 keras.Model vs keras.layers.Layer

Model比Layer的功能更多,反過來說,Layer的功能更精簡專一。

  • Layer:僅僅用作張量的操作,輸入一個張量,輸出也要求是一個張量,對張量的操作都可以用Layer來封裝;
  • Model:一個更加複雜的結構,由多個Layer組成。 Model的話,可以使用.fit(),.evaluate().predict()等方法來快速訓練。儲存和載入模型也是在Model這個級別進行的。

現在說一說上面的程式碼和pytorch中的區別,作為一個對比學習、也作為一個對pytorch的回顧:

  • 卷積層Conv2D中,Keras中不用輸入輸入的通道數,filters就是卷積後的輸出特徵圖的通道數;而PyTorch的卷積層是需要輸入兩個通道數的引數,一個是輸入特徵圖的通道數,一個是輸出特徵圖的通道數;
  • keras.layers.BatchNormalization(axis=3)是BN層,這裡的axis=3說明第三個維度(從0開始計數)是通道數,是需要作為批歸一化的維度(這個瞭解BN演算法的朋友應該可以理解吧,不瞭解的話去重新看我之前剖析BN層演算法的那個文章吧,在文章末尾有相關連結)。pytorch的影像的四個維度是:

\[【樣本數量,通道數,width,height】 \]

而tensorflow是:

\[【樣本數量,width,height,通道數】 \]

總之,學了pytorch之後,再看keras的話,對照的keras的API,很多東西都直接就會了,兩者的API越來越相似了。

上面最後輸出是(16, 56, 56, 32),輸入的是\(224\times 224\)的維度,然後經過兩個最大池化層,就變成了\(56\times 56\)了。

到此為止,我們現在應該是可以用keras來構建模型了。

相關文章