【深度学习】Tensorflow2.x入门(一)建立模型的三种模式
前言
最近做實驗比較焦慮,因此準備結合推薦算法梳理下Tensorflow2.x的知識。介紹Tensorflow2.x的文章有很多,但本文(系列)是按照作者構建模型的思路來展開的,因此不會從Eager Execution開始。另外,盡量擺脫小白文,加入自己的理解。
本文約2.7k字,預計閱讀10分鐘。
Tensorflow2.x的三種建模方式
Tensorflow2.x創建模型的方式主要有三種:
Sequential ?API,順序模型;
Function API,函數式模型;
Subclassing API,子類化模型;
其中Sequential API只適用于簡單的層堆疊,很難實現復雜模型,而Function API與Subclassing API各有優劣,也不必區分,因為可以進行混搭。
1. Sequential API
順序API是layer-by-layer的方式,適用于簡單的層堆棧,但對于構建多輸入、多輸出的模型難以實現。個人并不推薦使用這種方式構建模型,因此簡單放個例子:
model?=?Sequential([Input(shape=(3,)),Dense(2,?activation='relu',?name='layer1'),Dense(3,?activation='relu',?name='layer2'),Dense(4,?name='layer3'),] )2. Function API
函數式API能很好的處理非線性拓撲、共享層、具有多輸入多輸出的模型。且模型通常都是層的有向無環圖(DAG),因此函數式API是構建層計算圖的一種方式。
以下是Encoder-Decoder結構:
def?get_models():encoder_input?=?Input(shape=(28,?28,?1),?name="img")x?=?layers.Conv2D(16,?3,?activation="relu")(encoder_input)x?=?layers.Conv2D(32,?3,?activation="relu")(x)x?=?layers.MaxPooling2D(3)(x)x?=?layers.Conv2D(32,?3,?activation="relu")(x)x?=?layers.Conv2D(16,?3,?activation="relu")(x)encoder_output?=?layers.GlobalMaxPooling2D()(x)x?=?layers.Reshape((4,?4,?1))(encoder_output)x?=?layers.Conv2DTranspose(16,?3,?activation="relu")(x)x?=?layers.Conv2DTranspose(32,?3,?activation="relu")(x)x?=?layers.UpSampling2D(3)(x)x?=?layers.Conv2DTranspose(16,?3,?activation="relu")(x)decoder_output?=?layers.Conv2DTranspose(1,?3,?activation="relu")(x)autoencoder?=?Model(encoder_input,?decoder_output,?name="autoencoder")return?encoder,?autoencoder有時候,內置的tf.keras層并不滿足我們構建復雜的模型,因此需要實現Subclassing API中的自定義層。
3. Subclassing API
子類化API是通過繼承tf.keras.layers.Layer類或tf.keras.Model類的自定義層和自定義模型。它們與函數式API并不沖突,特別是自定義層---創建自己的層來擴展API,很方便的與函數式API結合構建模型。
3.1 Layer類
Keras的一個中心抽象是Layer類。層封裝了狀態(權重)和從輸入到輸出的轉換(層的前向傳播)。
一個簡單的線性層定義如下:
class?Linear(keras.layers.Layer):def?__init__(self,?units=32,?input_dim=32,?**kwargs):super(Linear,?self).__init__(**kwargs)self.w?=?self.add_weight(shape=(input_dim,?units),?initializer="random_normal",?trainable=True)self.b?=?self.add_weight(shape=(units,),?initializer="zeros",?trainable=True)def?call(self,?inputs,?**kwargs):return?tf.matmul(inputs,?self.w)?+?self.b有幾個注意點:
可以在__iniit__()方法中創建類子層(tf.keras的內置層API,或者是自定義的),并可以在call()中調用;
在定義變量時,有時候會看到:
????與add_weight()方法相同,但上述需要先定義初始化,再構造變量,而add_weight()可以在定義變量的同時進行初始化,推薦add_weight()方法;
有時候變量會定義在build(self, input_shape)方法中,一種是因為書寫習慣,另一種更重要的原因是「有時候事先并不知道輸入的大小(即沒有input_dim),希望在對層實例化后的某個時間再延遲創建權重」:
其中input_shape代表輸入的形狀;
call(self, inputs, **kwargs),其中inputs是張量或張量的嵌套結構(多輸入,張量列表),**kwargs是非張量參數。更一般的,call()方法應該為:
? ?training和mask是call()方法中的特權參數,training針對BatchNormalization和Dropout層在訓練和推斷期間具有不同的行為,mask則是當先前層生成了掩碼時,Keras會自動將正確的mask傳遞給__call__(),具體可見下文。
3.2 Model類
Layer類通常是來定義內部的計算模塊,例如一個FM、self-attention等,Model類則是用來定義整個外部模型,例如DeepFM、SASRec等。
Model類與Layer具有相同的API,但有以下區別:
Model會公開內置訓練fit()、評估evaluate()、預測predict();
model.layers屬性會公開其內部層的列表;
會公開保存和序列化API(save()、save_weights());
例如:
class?MyModel(keras.Model):def?__init__(self,?units=32,?**kwargs):super(MyModel,?self).__init(**kwrags)self.units?=?unitsself.linear?=?Linear(self.units)??#?去除input_dimdef?call(self,?inputs,?**kwargs):outputs?=?self.linear(inputs)return?outputsmodel?=?MyModel(32) #?model.compile(...) #?model.fit(...)3.3 call()方法
上述提到,call()中包含兩個特權參數,training和mask。
「training」:
模型中,BatchNormalization和Dropout層,在訓練和推斷期間具有不同的行為(簡單說一下「推斷」的含義,模型經過訓練后,可以高效的從新的數據推斷各種結論,即「預測」)。我們簡單來看一下Dropout與BatchNormalizationAPI中的描述:
?Dropout:
Note that the Dropout layer only applies when training is set to True such that no values are dropped during inference. When using model.fit, training will be appropriately set to True automatically, and in other contexts, you can set the kwarg explicitly to True when calling the layer.
?簡單來說,當traning=True時,dropout不會在推理(inference)中起作用。在訓練時,自動默認為True。
?BatchNormalization:
「training」: Python boolean indicating whether the layer should behave in training mode or in inference mode.
training=True: The layer will normalize its inputs using the mean and variance of the current batch of inputs.
training=False: The layer will normalize its inputs using the mean and variance of its moving statistics, learned during training.
在call()方法中,當training=True時,使用當前batch的輸入平均值和方差對輸入進行歸一化,training=False則是使用在「訓練期間」學習到的移動統計數據的均值與方差做歸一化。
所以training是一個布爾參數,call()方法通過公開它,用來控制模型在哪個模式下運行(訓練或推斷)。
【注】對于Dropout層,默認即可,而BatchNormalization則需要自己考量,另外training與trainable是不同的,trainable=False是來凍結該層的,具體的可以看API。「當然可以不指定training,因為在fit()時,模型會根據相應的階段(訓練、推斷)決定使用training值。」
「mask」:
對于mask參數,當我們構建Attention機制或者序列模型時會使用到。如果先前的層生成了掩碼,這里特別指的是tf.keras.layers.Embedding層,它包含了mask_zero參數,如果指定為True,那么Keras會自動將正確的mask參數傳遞給__call__()【函數式 API中 ,掩碼會自動傳播】。
當然如果不使用mask參數,對于生成掩碼的層Embedding也會公開一個compute_mask(input, previous_mask)方法計算mask;
class?MyLayer(layers.Layer):def?__init__(self,?**kwargs):super(MyLayer,?self).__init__(**kwargs)self.embedding?=?layers.Embedding(input_dim=5000,?output_dim=16,?mask_zero=True)self.lstm?=?layers.LSTM(32)def?call(self,?inputs):x?=?self.embedding(inputs)#?Note?that?you?could?also?prepare?a?`mask`?tensor?manually.#?It?only?needs?to?be?a?boolean?tensor#?with?the?right?shape,?i.e.?(batch_size,?timesteps).mask?=?self.embedding.compute_mask(inputs)output?=?self.lstm(x,?mask=mask)??#?The?layer?will?ignore?the?masked?valuesreturn?output【注】當然也可以自己通過inputs或者其它方式,自己計算mask。
4. 源碼學習
建議閱讀Tensorflow中的Transformer源碼加深對Function API和Subclassing API的理解。
地址(點擊原文):https://www.tensorflow.org/tutorials/text/transformer
當然也可以參考下我的github(廣告):https://github.com/ZiyaoGeng/Recommender-System-with-TF2.0
總結
上述是個人對Tensorflow2.x構建模型方式的總結,自己偏好使用子類化模型(Java的習慣),當然函數式API也有很多的優點,開源項目deepctr就是采用函數式API。關于add_loss、add_metric方法,放在自定義損失中進行討論。
往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯《統計學習方法》的代碼復現專輯 AI基礎下載機器學習的數學基礎專輯 獲取本站知識星球優惠券,復制鏈接直接打開: https://t.zsxq.com/qFiUFMV 本站qq群704220115。加入微信群請掃碼:總結
以上是生活随笔為你收集整理的【深度学习】Tensorflow2.x入门(一)建立模型的三种模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【算法基础】数据结构导论第六章-查找.p
- 下一篇: 【算法基础】数据结构导论第七章-排序.p