深度學習模型一般由各種模型層組合而成。
tf.keras.layers內置了非常豐富的各種功能的模型層。例如,
layers.Dense,layers.Flatten,layers.Input,layers.DenseFeature,layers.Dropout
layers.Conv2D,layers.MaxPooling2D,layers.Conv1D
layers.Embedding,layers.GRU,layers.LSTM,layers.Bidirectional等等。
如果這些內置模型層不能夠滿足需求,我們也可以通過編寫tf.keras.Lambda匿名模型層或繼承tf.keras.layers.Layer基類構建自定義的模型層。
其中tf.keras.Lambda匿名模型層只適用於構造沒有學習參數的模型層。
一,內置模型層
一些常用的內置模型層簡單介紹如下。
基礎層
-
Dense:密集連接層。參數個數 = 輸入層特征數× 輸出層特征數(weight)+ 輸出層特征數(bias)
-
Activation:激活函數層。一般放在Dense層后面,等價於在Dense層中指定activation。
-
Dropout:隨機置零層。訓練期間以一定幾率將輸入置0,一種正則化手段。
-
BatchNormalization:批標准化層。通過線性變換將輸入批次縮放平移到穩定的均值和標准差。可以增強模型對輸入不同分布的適應性,加快模型訓練速度,有輕微正則化效果。一般在激活函數之前使用。
-
SpatialDropout2D:空間隨機置零層。訓練期間以一定幾率將整個特征圖置0,一種正則化手段,有利於避免特征圖之間過高的相關性。
-
Input:輸入層。通常使用Functional API方式構建模型時作為第一層。
-
DenseFeature:特征列接入層,用於接收一個特征列列表並產生一個密集連接層。
-
Flatten:壓平層,用於將多維張量壓成一維。
-
Reshape:形狀重塑層,改變輸入張量的形狀。
-
Concatenate:拼接層,將多個張量在某個維度上拼接。
-
Add:加法層。
-
Subtract: 減法層。
-
Maximum:取最大值層。
-
Minimum:取最小值層。
卷積網絡相關層
-
Conv1D:普通一維卷積,常用於文本。參數個數 = 輸入通道數×卷積核尺寸(如3)×卷積核個數
-
Conv2D:普通二維卷積,常用於圖像。參數個數 = 輸入通道數×卷積核尺寸(如3乘3)×卷積核個數
-
Conv3D:普通三維卷積,常用於視頻。參數個數 = 輸入通道數×卷積核尺寸(如3乘3乘3)×卷積核個數
-
SeparableConv2D:二維深度可分離卷積層。不同於普通卷積同時對區域和通道操作,深度可分離卷積先操作區域,再操作通道。即先對每個通道做獨立卷積操作區域,再用1乘1卷積跨通道組合操作通道。參數個數 = 輸入通道數×卷積核尺寸 + 輸入通道數×1×1×輸出通道數。深度可分離卷積的參數數量一般遠小於普通卷積,效果一般也更好。
-
DepthwiseConv2D:二維深度卷積層。僅有SeparableConv2D前半部分操作,即只操作區域,不操作通道,一般輸出通道數和輸入通道數相同,但也可以通過設置depth_multiplier讓輸出通道為輸入通道的若干倍數。輸出通道數 = 輸入通道數 × depth_multiplier。參數個數 = 輸入通道數×卷積核尺寸× depth_multiplier。
-
Conv2DTranspose:二維卷積轉置層,俗稱反卷積層。並非卷積的逆操作,但在卷積核相同的情況下,當其輸入尺寸是卷積操作輸出尺寸的情況下,卷積轉置的輸出尺寸恰好是卷積操作的輸入尺寸。
-
LocallyConnected2D: 二維局部連接層。類似Conv2D,唯一的差別是沒有空間上的權值共享,所以其參數個數遠高於二維卷積。
-
MaxPooling2D: 二維最大池化層。也稱作下采樣層。池化層無參數,主要作用是降維。
-
AveragePooling2D: 二維平均池化層。
-
GlobalMaxPool2D: 全局最大池化層。每個通道僅保留一個值。一般從卷積層過渡到全連接層時使用,是Flatten的替代方案。
-
GlobalAvgPool2D: 全局平均池化層。每個通道僅保留一個值。
循環網絡相關層
-
Embedding:嵌入層。一種比Onehot更加有效的對離散特征進行編碼的方法。一般用於將輸入中的單詞映射為稠密向量。嵌入層的參數需要學習。
-
LSTM:長短記憶循環網絡層。最普遍使用的循環網絡層。具有攜帶軌道,遺忘門,更新門,輸出門。可以較為有效地緩解梯度消失問題,從而能夠適用長期依賴問題。設置return_sequences = True時可以返回各個中間步驟輸出,否則只返回最終輸出。
-
GRU:門控循環網絡層。LSTM的低配版,不具有攜帶軌道,參數數量少於LSTM,訓練速度更快。
-
SimpleRNN:簡單循環網絡層。容易存在梯度消失,不能夠適用長期依賴問題。一般較少使用。
-
ConvLSTM2D:卷積長短記憶循環網絡層。結構上類似LSTM,但對輸入的轉換操作和對狀態的轉換操作都是卷積運算。
-
Bidirectional:雙向循環網絡包裝器。可以將LSTM,GRU等層包裝成雙向循環網絡。從而增強特征提取能力。
-
RNN:RNN基本層。接受一個循環網絡單元或一個循環單元列表,通過調用tf.keras.backend.rnn函數在序列上進行迭代從而轉換成循環網絡層。
-
LSTMCell:LSTM單元。和LSTM在整個序列上迭代相比,它僅在序列上迭代一步。可以簡單理解LSTM即RNN基本層包裹LSTMCell。
-
GRUCell:GRU單元。和GRU在整個序列上迭代相比,它僅在序列上迭代一步。
-
SimpleRNNCell:SimpleRNN單元。和SimpleRNN在整個序列上迭代相比,它僅在序列上迭代一步。
-
AbstractRNNCell:抽象RNN單元。通過對它的子類化用戶可以自定義RNN單元,再通過RNN基本層的包裹實現用戶自定義循環網絡層。
-
Attention:Dot-product類型注意力機制層。可以用於構建注意力模型。
-
AdditiveAttention:Additive類型注意力機制層。可以用於構建注意力模型。
-
TimeDistributed:時間分布包裝器。包裝后可以將Dense、Conv2D等作用到每一個時間片段上。
二,自定義模型層
如果自定義模型層沒有需要被訓練的參數,一般推薦使用Lamda層實現。
如果自定義模型層有需要被訓練的參數,則可以通過對Layer基類子類化實現。
Lamda層由於沒有需要被訓練的參數,只需要定義正向傳播邏輯即可,使用比Layer基類子類化更加簡單。
Lamda層的正向邏輯可以使用Python的lambda函數來表達,也可以用def關鍵字定義函數來表達。
import tensorflow as tf from tensorflow.keras import layers,models,regularizers mypower = layers.Lambda(lambda x:tf.math.pow(x,2)) mypower(tf.range(5))
<tf.Tensor: shape=(5,), dtype=int32, numpy=array([ 0, 1, 4, 9, 16], dtype=int32)>
Layer的子類化一般需要重新實現初始化方法,Build方法和Call方法。下面是一個簡化的線性層的范例,類似Dense.
class Linear(layers.Layer): def __init__(self, units=32, **kwargs): super(Linear, self).__init__(**kwargs) self.units = units #build方法一般定義Layer需要被訓練的參數。 def build(self, input_shape): self.w = self.add_weight(shape=(input_shape[-1], self.units), initializer='random_normal', trainable=True) self.b = self.add_weight(shape=(self.units,), initializer='random_normal', trainable=True) super(Linear,self).build(input_shape) # 相當於設置self.built = True #call方法一般定義正向傳播運算邏輯,__call__方法調用了它。 def call(self, inputs): return tf.matmul(inputs, self.w) + self.b #如果要讓自定義的Layer通過Functional API 組合成模型時可以序列化,需要自定義get_config方法。 def get_config(self): config = super(Linear, self).get_config() config.update({'units': self.units}) return config
linear = Linear(units = 8) print(linear.built) # 指定input_shape,顯式調用build方法,第0維代表樣本數量,用None填充 linear.build(input_shape = (None,16)) print(linear.built)
False
True
linear = Linear(units = 8) print(linear.built) linear.build(input_shape = (None,16)) print(linear.compute_output_shape(input_shape = (None,16)))
False
(None, 8)
linear = Linear(units = 16) print(linear.built) # 如果built = False,調用__call__時會先調用build方法, 再調用call方法。 linear(tf.random.uniform((100,64))) print(linear.built) config = linear.get_config() print(config)
False
True
{'name': 'linear_3', 'trainable': True, 'dtype': 'float32', 'units': 16}
tf.keras.backend.clear_session() model = models.Sequential() # 注意該處的input_shape會被模型加工,無需使用None代表樣本數量維 model.add(Linear(units = 16,input_shape = (64,))) print("model.input_shape: ",model.input_shape) print("model.output_shape: ",model.output_shape) model.summary()
model.input_shape: (None, 64)
model.output_shape: (None, 16)
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
linear (Linear) (None, 16) 1040
=================================================================
Total params: 1,040
Trainable params: 1,040
Non-trainable params: 0
參考:
開源電子書地址:https://lyhue1991.github.io/eat_tensorflow2_in_30_days/
GitHub 項目地址:https://github.com/lyhue1991/eat_tensorflow2_in_30_days