TF01-06:Keras的Layer使用

本文主要說明Keras中Layer的使用,更希望能通過應(yīng)用理解Layer的實現(xiàn)原理,主要內(nèi)容包含:
??1. 通過Model來調(diào)用Layer的運算;
??2. 直接使用Layer的運算;
??3. 使用Layer封裝定制運算蝗蛙;


一.使用Layer做運算

  • Layer主要是對操作與操作結(jié)果存儲的封裝,比如對圖像執(zhí)行卷積運算醉鳖;運算的執(zhí)行兩種方式捡硅;

    • 通過Model執(zhí)行;
    • 通過Layer的call執(zhí)行盗棵;
  • 下面使用圖像的卷積運算處理來說明Layer承擔(dān)的計算功能壮韭。

1. Conv2D層說明

  • Conv2D主要是構(gòu)造器,屬性與函數(shù)都是來自Layer纹因。

  • 構(gòu)造器說明

    __init__(
        filters,
        kernel_size,
        strides=(1, 1),
        padding='valid',
        data_format=None,
        dilation_rate=(1, 1),
        activation=None,
        use_bias=True,
        kernel_initializer='glorot_uniform',
        bias_initializer='zeros',
        kernel_regularizer=None,
        bias_regularizer=None,
        activity_regularizer=None,
        kernel_constraint=None,
        bias_constraint=None,
        **kwargs
    )

2. 使用Model來運算層

  • 使用Model來運算泰涂,需要使用Layer構(gòu)建一個Model;
% matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import numpy as np
# 1. 加載需要處理的圖像
img = plt.imread('./04imgs/bird.png')
# 2. 初始化器
def kernel_initializer(shape, dtype=None):
    print(shape)
    # 浮雕卷積核
    kernel_value = np.array(
        [
            [-1.0, -1.0, 0.0],
            [-1.0,  0.0, 1.0],
            [ 0.0,   1.0, 1.0]
        ]
    )
    kernel = np.zeros(shape=(3, 3, 4, 1), dtype=np.float32)
    kernel[:, :, 0, 0] = kernel_value
    kernel[:, :, 1, 0] = kernel_value
    kernel[:, :, 2, 0] = kernel_value
    kernel[:, :, 3, 0] = kernel_value
    return kernel

# 3. 輸入層
# InputLayer一定按照Conv2D約定的數(shù)據(jù)格式定義input_shape
input_layer = keras.Input(shape=img.shape, dtype=tf.float32)

# 4. 卷積層
conv_layer = layers.Conv2D(
    filters=1,
    kernel_size=(3, 3),
    strides=1,
    padding='same',
    kernel_initializer=kernel_initializer,
    dtype=tf.float32
)

# 5. 構(gòu)建模型
conv = conv_layer(input_layer)
model = keras.Model(inputs=input_layer, outputs=conv)
(3, 3, 4, 1)
# 6. 模型計算
data = np.zeros(shape=(1, img.shape[0], img.shape[1], img.shape[2]), dtype=np.float32)
data[0] = img
conv_result = model(data)    # 返回的類型是:class 'tensorflow.python.framework.ops.EagerTensor'
# 7. 顯示計算前后圖像
figure = plt.figure(figsize=(8, 4))
org_ax = figure.add_subplot(121) 
cov_ax = figure.add_subplot(122) 

org_ax.imshow(img)
print(type(conv_result))
cov_ax.imshow(conv_result[0, :, :, 0] , cmap='gray')
plt.show()

<class 'tensorflow.python.framework.ops.EagerTensor'>
Model卷積運算結(jié)果比較

3. Layer的運算

  • 如果是單純的使用Layer來實現(xiàn)計算辐怕,不需要動用Model,Model主要封裝了優(yōu)化訓(xùn)練函數(shù)fit等从绘。

  • Layer的單純計算通過call調(diào)用實現(xiàn)寄疏;

% matplotlib inline
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import numpy as np

# 1. 加載需要處理的圖像
img = plt.imread('./04imgs/bird.png')


# 2. 初始化器
def kernel_initializer(shape, dtype=None):
    print(shape)
    # 浮雕卷積核
    kernel_value = np.array(
        [
            [-1.0, -1.0, 0.0],
            [-1.0,  0.0, 1.0],
            [ 0.0,   1.0, 1.0]
        ]
    )
    kernel = np.zeros(shape=(3, 3, 4, 1), dtype=np.float32)
    kernel[:, :, 0, 0] = kernel_value
    kernel[:, :, 1, 0] = kernel_value
    kernel[:, :, 2, 0] = kernel_value
    kernel[:, :, 3, 0] = kernel_value
    return kernel


# 3. 輸入層
# InputLayer一定按照Conv2D約定的數(shù)據(jù)格式定義input_shape
input_layer = keras.Input(shape=img.shape, dtype=tf.float32)

# 4. 卷積層
conv_layer = layers.Conv2D(
    filters=1,
    kernel_size=(3, 3),
    strides=1,
    padding='same',
    kernel_initializer=kernel_initializer,
    dtype=tf.float32
)

# 層的鏈?zhǔn)秸{(diào)用
conv_layer(input_layer)


# 6. 模型計算
data = np.zeros(shape=(1, img.shape[0], img.shape[1], img.shape[2]), dtype=np.float32)
data[0] = img
conv_result = conv_layer.call(data)

# 7. 顯示計算前后圖像
figure = plt.figure(figsize=(8, 4))
org_ax = figure.add_subplot(121) 
cov_ax = figure.add_subplot(122) 

org_ax.imshow(img)
cov_ax.imshow(conv_result[0, :, :, 0] , cmap='gray')
plt.show()

(3, 3, 4, 1)
Layer直接卷積運算結(jié)果比較

二. 定制Layer的運算

  • 既然Layer承擔(dān)了運算的職責(zé),本身等同于函數(shù)或者過程僵井。

1. 定制的Layer的編程模式

  • Layer定制需要搞清楚Layer相關(guān)成員函數(shù)作用與工作流程陕截。

1.1. 理解Layer運算的相關(guān)接口說明

  • Keras 層的工作機制就是一個調(diào)用流程,并通過繼承覆蓋某些成員函數(shù)就可以實現(xiàn)Layer工作過程的干預(yù)批什,其中與計算有關(guān)的只需要覆蓋實現(xiàn)三個方法即可:
    1. build(input_shape): 這是你定義權(quán)重的地方农曲。這個方法必須設(shè) self.built = True,可以通過調(diào)用 super([Layer], self).build() 完成驻债。
    2. call(x): 這里是編寫層的功能邏輯的地方乳规。你只需要關(guān)注傳入 call 的第一個參數(shù):輸入張量,除非你希望你的層支持masking合呐。
    3. compute_output_shape(input_shape): 如果你的層更改了輸入張量的形狀暮的,你應(yīng)該在這里定義形狀變化的邏輯,這讓Keras能夠自動推斷各層的形狀淌实。
  1. build函數(shù)接口

    • 覆蓋這個函數(shù)冻辩,在函數(shù)中實現(xiàn)權(quán)重的定義猖腕。
    • 這個方法必須設(shè) self.built = True,可以通過調(diào)用super([Layer], self).build() 完成恨闪。
    • 該函數(shù)無需返回數(shù)據(jù)倘感,主要用來初始化self.weights
    build(input_shape)
  1. call函數(shù)接口
    • 覆蓋這個函數(shù),在函數(shù)中實現(xiàn)功能邏輯的運算咙咽;
    • 這個函數(shù)的第一個參數(shù)inputs就是輸入張量老玛;一般使用這個參數(shù)就足夠;如果需要支持masking運算犁珠,可以使用更多的參數(shù)逻炊;
    • 返回一個張量,或者輸出值犁享;
    call(
        inputs,
        **kwargs
    )
  1. compute_output_shape函數(shù)接口
    • 可選:如果需要層更改輸入張量的形狀余素,則覆蓋這個函數(shù),在函數(shù)中實現(xiàn)形狀變化的邏輯炊昆,定義新的形狀桨吊;
    • 該函數(shù)可以讓Keras能夠自動推斷各層的形狀;

    compute_output_shape(input_shape)

1.2. 理解Layer的定制工作流程

  • 通過程序的輸出凤巨,觀察如下幾個方面视乐,來理解Layer的工作流程:

    • 輸出的順序;
    • 輸出的參數(shù)敢茁;
  • 代碼與執(zhí)行輸出如下:

import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import tensorflow.keras.backend as K
import numpy as np

class WorkflowLayer(layers.Layer):
    # 1. 覆蓋build函數(shù)
    def build(self, input_shape):
        print('build權(quán)重初始化:', type(input_shape))
        self.built=True
        # super(WorkflowLayer, self).build(input_shape)
        
    # 2. 覆蓋call
    def call(self, inputs, **kwargs):
        print('call運算邏輯實現(xiàn):', type(inputs), kwargs)
        return inputs
    # 3. 覆蓋compute_output_shape
    def compute_output_shape(self, input_shape):
        print('......')
        return super(WorkflowLayer,self).compute_output_shape(input_shape)

        
input_layer = keras.Input(shape=(None,))
workflow_layer = WorkflowLayer()
workflow_layer(input_layer)     # 觸發(fā)build與call函數(shù)調(diào)用
result = workflow_layer.call([1])    # 只觸發(fā)call函數(shù)調(diào)用佑淀,不觸發(fā)build函數(shù)調(diào)用
print(type(result))
build權(quán)重初始化: <class 'tensorflow.python.framework.tensor_shape.TensorShape'>
call運算邏輯實現(xiàn): <class 'tensorflow.python.framework.ops.Tensor'> {}
call運算邏輯實現(xiàn): <class 'list'> {}
<class 'list'>

1.3. Layer的工作流程說明

  1. 使用對象調(diào)用

    • 使用對象調(diào)用,會先調(diào)用build函數(shù)彰檬,構(gòu)建權(quán)重矩陣伸刃;
    • 對象調(diào)用主要用來構(gòu)建計算環(huán)境;
    • build的參數(shù)類型為:<class 'tensorflow.python.framework.tensor_shape.TensorShape'>
  2. 使用call調(diào)用

    • 不會調(diào)用build函數(shù)逢倍,直接計算捧颅;
    • 通過對象調(diào)用,傳遞的參數(shù)類型是:<class 'tensorflow.python.framework.ops.Tensor'>
    • 通過call函數(shù)調(diào)用较雕,傳遞的參數(shù)類型就是傳遞的原始類型碉哑。
  3. 在Model中使用的情況

    • call的參數(shù)類型為:<class 'tensorflow.python.framework.ops.EagerTensor'>
    • Model返回的是<Tensor>
input_layer = keras.Input(shape=(None,))
workflow_layer = WorkflowLayer()
workflow = workflow_layer(input_layer)     # 觸發(fā)build與call函數(shù)調(diào)用


model = keras.Model(inputs=input_layer, outputs=workflow)
model([1])
build權(quán)重初始化: <class 'tensorflow.python.framework.tensor_shape.TensorShape'>
call運算邏輯實現(xiàn): <class 'tensorflow.python.framework.ops.Tensor'> {}
call運算邏輯實現(xiàn): <class 'tensorflow.python.framework.ops.EagerTensor'> {}





<tf.Tensor: id=76, shape=(), dtype=int32, numpy=1>
  1. 更多的說明,可以參考源代碼
    • 從上面代碼的運行亮蒋,我們可以得知compute_output_shape函數(shù)沒有被調(diào)用红氯。
    • Dense實現(xiàn)的代碼地址:https://github.com/tensorflow/tensorflow/blob/r2.0/tensorflow/python/keras/layers/core.py#L913-L1079

2. 運行官方文檔提供的例子

  • 在官方的文檔中提供了一個例子元旬,運行的時候,需要注意幾個問題:

    1. 數(shù)據(jù)類型的問題;
    2. 輸入的數(shù)據(jù)需要使用張量Tensor類型屿附;
  • 下面是代碼及其運行

import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import tensorflow.keras.backend as K
import numpy as np


class MyLayer(layers.Layer):

    def __init__(self, output_dim, **kwargs):
        self.output_dim = output_dim
        super(MyLayer, self).__init__(**kwargs)

    def build(self, input_shape):
        print('build函數(shù)袄琳!')
        # 為該層創(chuàng)建一個可訓(xùn)練的權(quán)重
        self.kernel = self.add_weight(name='kernel',
                                      shape=(input_shape[1], self.output_dim),
                                      initializer='uniform',
                                      trainable=True, dtype=tf.float32)    # <-----指定數(shù)據(jù)類型
        super(MyLayer, self).build(input_shape)  # 或者self.built = True

    def call(self, x):
        print('call函數(shù)!')
        return K.dot(x, self.kernel)    # 內(nèi)積運算

    # 
    def compute_output_shape(self, input_shape):
        print('compute_output_shape函數(shù)!')
        return (input_shape[0], self.output_dim)


layer = MyLayer(1)

input_layer = keras.Input(shape=(2,))
v_layer = layer(input_layer)
result = layer.call(tf.constant(np.array([[1.0, 2.0]], dtype=np.float32)))   # <-----指定數(shù)據(jù)類型铸敏,而且需要是Tensor類型

print(result.numpy())

build函數(shù)!
call函數(shù)!
call函數(shù)!
[[0.07733139]]

3. 使用Layer封裝實現(xiàn)backend的卷積操作

  • 盡管這種封裝沒有多大意義闰蛔,甚至是多此一舉,唯一的好處就是練習(xí)下Layer的定制封裝與Layer的作用图柏。

    • 在tenorflow中tensorflow.keras.backend中提供了基礎(chǔ)的卷積運算:conv2d序六;
  • 只需要在上述代碼上修改即可。

import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras as keras
import tensorflow.keras.layers as layers
import tensorflow.keras.backend as K
import numpy as np


class ConvLayer(layers.Layer):

    def build(self, input_shape):
        print('build函數(shù)蚤吹!')
        # 構(gòu)建卷積運算需要的核
        kernel_value = np.array(
            [
                [-1.0, -1.0, 0.0],
                [-1.0, 0.0, 1.0],
                [0.0, 1.0, 1.0]
            ]
        )
        self.kernel = np.zeros(shape=(3, 3, 4, 1), dtype=np.float32)
        self.kernel[:, :, 0, 0] = kernel_value
        self.kernel[:, :, 1, 0] = kernel_value
        self.kernel[:, :, 2, 0] = kernel_value
        self.kernel[:, :, 3, 0] = kernel_value

        super(ConvLayer, self).build(input_shape)  # 或者self.built = True

    def call(self, x):
        print('call函數(shù)!')
        return K.conv2d(x, self.kernel, padding='same',strides=(1, 1, 1, 1))


# 加載圖像
img = plt.imread('./04imgs/bird.png')
data = np.zeros(shape=(1, img.shape[0], img.shape[1], img.shape[2]), dtype=np.float32)
data[0] = img

# 定制的Layer
layer = ConvLayer()
# 輸入
input_layer = keras.Input(shape=img.shape)
# 輸入調(diào)用
layer(input_layer)

result = layer.call(tf.constant(data, dtype=np.float32))
# print(result.numpy())   # 輸出numpy的數(shù)組格式

# 顯示計算前后圖像
figure = plt.figure(figsize=(8, 4))
org_ax = figure.add_subplot(121)
cov_ax = figure.add_subplot(122)

org_ax.imshow(img)
cov_ax.imshow(result[0, :, :, 0], cmap='gray')
plt.show()
build函數(shù)例诀!
call函數(shù)!
call函數(shù)!
定制Layer卷積運算結(jié)果比較

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市裁着,隨后出現(xiàn)的幾起案子繁涂,更是在濱河造成了極大的恐慌,老刑警劉巖二驰,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扔罪,死亡現(xiàn)場離奇詭異,居然都是意外死亡桶雀,警方通過查閱死者的電腦和手機矿酵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矗积,“玉大人全肮,你說我怎么就攤上這事〖罚” “怎么了辜腺?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乍恐。 經(jīng)常有香客問我哪自,道長,這世上最難降的妖魔是什么禁熏? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮邑彪,結(jié)果婚禮上瞧毙,老公的妹妹穿的比我還像新娘。我一直安慰自己寄症,他們只是感情好宙彪,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著有巧,像睡著了一般释漆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上篮迎,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天男图,我揣著相機與錄音示姿,去河邊找鬼。 笑死逊笆,一個胖子當(dāng)著我的面吹牛栈戳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播难裆,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼子檀,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了乃戈?” 一聲冷哼從身側(cè)響起褂痰,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎症虑,沒想到半個月后缩歪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡侦讨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年驶冒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片韵卤。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡骗污,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沈条,到底是詐尸還是另有隱情需忿,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布蜡歹,位于F島的核電站屋厘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏月而。R本人自食惡果不足惜汗洒,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望父款。 院中可真熱鬧溢谤,春花似錦、人聲如沸憨攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肝集。三九已至瞻坝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間杏瞻,已是汗流浹背所刀。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工衙荐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人勉痴。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓赫模,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蒸矛。 傳聞我的和親對象是個殘疾皇子瀑罗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容