【tf.keras】官方教程二 函數式API



搭建簡單模型

Setup

Introduction

訓練、驗證和推理

模型的保存與恢復

使用相同的層圖來定義多個模型

所有的模型都是可調用的,就像層一樣

操作復雜的圖拓撲

多輸入多輸出模型

ResNet Model(toy version)

共享層

API的延伸:使用自定義層


官方教程:https://tensorflow.google.cn/guide/keras/functional#training_evaluation_and_inference  

搭建簡單模型

Setup

導入需要的模塊:

1 from __future__ import absolute_import, division, print_function, unicode_literals
2 
3 import numpy as np
4 
5 import tensorflow as tf
6 
7 from tensorflow import keras
8 from tensorflow.keras import layers

Introduction

  什么是Keras 函數式API呢?它相對於tf.keras.Sequential API有什么優勢呢?

  Keras 函數式API是生成模型的一種方式,其相對於tf.keras.Sequential API更加靈活。函數式 API可以處理具有非線性拓撲結構的模型、具有共享層的模型以及具有多個輸入或輸出的模型

  深度學習模型的主要思想是層次的有向無環圖(DAG)因此,函數式API是一種構建層圖的方法

  例如構建一個如下網絡,包含3個全連接層的分類問題。

(input: 784-dimensional vectors)
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (64 units, relu activation)]
       ↧
[Dense (10 units, softmax activation)]
       ↧
(output: logits of a probability distribution over 10 classes)

  使用函數式API的步驟:

  • 首先生成一個輸入節點
inputs = keras.Input(shape=(784,))

  如果輸入的是shape為(32,32,3)的圖像,可以通過如下方式生成輸入節點:

1 # Just for demonstration purposes. 
2 img_inputs = keras.Input(shape=(32, 32, 3))

  上述生成的inputs中包含shape和dtype等信息:

1 inputs.shape
2 inputs.dtype

  會返回如下信息:

TensorShape([None, 784])
tf.float32
  • 在層的圖(graph of layers)中生成新的節點,並通過inputs進行回調。
1 dense = layers.Dense(64, activation='relu')
2  x = dense(inputs)

  上述操作,相當於將輸入inputs輸入到了創建的dense層中,並返回輸出x;其實可以將上述代碼簡化為一行。

  • 添加第二個層節點和第三個層節點,同樣采用的是上述的方式。
1 x = layers.Dense(64, activation='relu')(x) 
2 outputs = layers.Dense(10)(x)
  • 此時,可以通過在層圖中指定其輸入和輸出來創建最終的模型:
model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model')

  通過keras.Model()方法,結合輸入、輸出,整合成為最終的模型。

  • 生成模型以后,可以打印構建的模型, 就像paper中模型結構的表格:
model.summary()
Model: "mnist_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         [(None, 784)]             0         
_________________________________________________________________
dense (Dense)                (None, 64)                50240     
_________________________________________________________________
dense_1 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_2 (Dense)              (None, 10)                650       
=================================================================
Total params: 55,050
Trainable params: 55,050
Non-trainable params: 0
_________________________________________________________________
  • 同時,也可以 繪制模型結構的圖像
    • 僅顯示模型結構:
keras.utils.plot_model(model, 'my_first_model.png')

  • 顯示模型結構和輸入輸出的尺寸:【添加參數show_shapes=True】
1 keras.utils.plot_model(model, 'my_first_model_with_shape_info.png', show_shapes=True)

訓練、驗證和推理

  這一部分與Sequential models的處理方式相同。

  下述是使用mnist數據集,進行訓練、驗證和測試:

 1 (x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
 2 
 3 x_train = x_train.reshape(60000, 784).astype('float32') / 255
 4 x_test = x_test.reshape(10000, 784).astype('float32') / 255
 5 
 6 model.compile(loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
 7               optimizer=keras.optimizers.RMSprop(),
 8               metrics=['accuracy'])
 9 
10 history = model.fit(x_train, y_train,
11                     batch_size=64,
12                     epochs=5,
13                     validation_split=0.2)
14 
15 test_scores = model.evaluate(x_test, y_test, verbose=2)
16 print('Test loss:', test_scores[0])
17 print('Test accuracy:', test_scores[1])

  訓練和驗證的詳細指南詳見:https://tensorflow.google.cn/guide/keras/train_and_evaluate

模型的保存與恢復

  對於模型的保存,函數式API的方式與序列化模型也是一致的。標准的方式是通過model.save()進行模型的保存, keras.models.load_model()進行模型的恢復。

  保存的文件中包含:

  • 模型的結構
  • 模型的權重
  • 模型的訓練配置參數
  • 優化器和它的狀態
1 model.save('path_to_my_model')
2 del model
3 # Recreate the exact same model purely from the file:
4 model = keras.models.load_model('path_to_my_model')

模型的保存與恢復指南詳見:https://tensorflow.google.cn/guide/keras/save_and_serialize

使用相同的層圖來定義多個模型

  在函數式API中,模型的產生是通過具體化它們的輸入和輸出【keras.Model(inputs=, outputs=)】這就意味着,一個層圖可以被利用產生多個模型。(通過不同的輸入和輸出)

  以下代碼包含encoder decoder兩部分,encoder相當於FCN的卷積過程decoder相當於FCN的反卷積過程

  也就是說,Conv2D層與Conv2DTranspose互為反操作;MaxPooling2D與UpSampling2D互為反操作。卷積與反卷積,池化與反池化。

 1 encoder_input = keras.Input(shape=(28, 28, 1), name='img')
 2 x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
 3 x = layers.Conv2D(32, 3, activation='relu')(x)
 4 x = layers.MaxPooling2D(3)(x)
 5 x = layers.Conv2D(32, 3, activation='relu')(x)
 6 x = layers.Conv2D(16, 3, activation='relu')(x)
 7 encoder_output = layers.GlobalMaxPooling2D()(x)
 8 
 9 encoder = keras.Model(encoder_input, encoder_output, name='encoder')
10 encoder.summary()
11 
12 x = layers.Reshape((4, 4, 1))(encoder_output)
13 x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
14 x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
15 x = layers.UpSampling2D(3)(x)
16 x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
17 decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)
18 
19 autoencoder = keras.Model(encoder_input, decoder_output, name='autoencoder')
20 autoencoder.summary()

  值得注意的是,上述代碼是在encoder的基礎上,再建立decoder。建立decoder時,使用的是encoder模型的輸入和decoder的輸出作為keras.Model的輸入參數。也就是所謂的end2end,端到端操作。  

  所有的模型都是可調用的,就像層一樣

  可以通過調用輸入或另一層的輸出,將任何模型視為一個層。通過調用模型,不僅重用了模型的體系結構,還重用了它的權重。

  為了看到它的作用,這里有一個不同的自動編碼器的例子,它創建了一個編碼器模型,一個解碼器模型,並在兩個調用中鏈接它們,以獲得自動編碼器模型:

  

  之前的模型是通過使用encoder模型的輸入作為decoder的輸入,從而達到端到端的模型建立。當然,也可以建立decoder模型的輸入,通過兩個模型的調用達到端到端模型的實現。

  如下述代碼第12行,構建了decoder模型的輸入;

  23-26行,通過新建一個自動編碼器的輸入autoencoder_input,再通過兩個模型的分別調用(如同層的調用一樣),26行可以再次建立模型。 模型可以像層一樣調用,生成新的模型(keras.Model(inputs, outputs))。

 1 encoder_input = keras.Input(shape=(28, 28, 1), name='original_img')
 2 x = layers.Conv2D(16, 3, activation='relu')(encoder_input)
 3 x = layers.Conv2D(32, 3, activation='relu')(x)
 4 x = layers.MaxPooling2D(3)(x)
 5 x = layers.Conv2D(32, 3, activation='relu')(x)
 6 x = layers.Conv2D(16, 3, activation='relu')(x)
 7 encoder_output = layers.GlobalMaxPooling2D()(x)
 8 
 9 encoder = keras.Model(encoder_input, encoder_output, name='encoder')
10 encoder.summary()
11 
12 decoder_input = keras.Input(shape=(16,), name='encoded_img')
13 x = layers.Reshape((4, 4, 1))(decoder_input)
14 x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
15 x = layers.Conv2DTranspose(32, 3, activation='relu')(x)
16 x = layers.UpSampling2D(3)(x)
17 x = layers.Conv2DTranspose(16, 3, activation='relu')(x)
18 decoder_output = layers.Conv2DTranspose(1, 3, activation='relu')(x)
19 
20 decoder = keras.Model(decoder_input, decoder_output, name='decoder')
21 decoder.summary()
22 
23 autoencoder_input = keras.Input(shape=(28, 28, 1), name='img')
24 encoded_img = encoder(autoencoder_input)
25 decoded_img = decoder(encoded_img)
26 autoencoder = keras.Model(autoencoder_input, decoded_img, name='autoencoder')
27 autoencoder.summary()

  上述模型嵌套的方式在集成算法中比較常見,一堆弱學習機(Model)的再次組合。

  下述代碼真是一個簡單粗暴的感知機集成模型,get_model()中為一個包含128個節點的輸入層和包含1個節點的輸出層構成的感知機模型。代碼10-15行為集成模型的構建,同樣的輸入,平均的輸出。

 1 def get_model():
 2   inputs = keras.Input(shape=(128,))
 3   outputs = layers.Dense(1)(inputs)
 4   return keras.Model(inputs, outputs)
 5 
 6 model1 = get_model()
 7 model2 = get_model()
 8 model3 = get_model()
 9 
10 inputs = keras.Input(shape=(128,))
11 y1 = model1(inputs)
12 y2 = model2(inputs)
13 y3 = model3(inputs)
14 outputs = layers.average([y1, y2, y3])
15 ensemble_model = keras.Model(inputs=inputs, outputs=outputs)

操作復雜的圖拓撲

  通過以上的介紹,或許你覺得keras 函數式API和Sequence API相比並沒有什么亮點。那是因為上述的模型結構還相對簡單。 接下來,在多輸入多輸出模型 和 共享層中探究其精妙之處。

多輸入多輸出模型

  函數式API可以容易的解決多輸入和多輸出的問題。而這在Sequential API中很難處理。

  下述代碼包含兩個輸入,兩個輸入分別經過LSTM產生輸出后,利用特征拼接形成一個特征。再對該特征進行分別兩個全連接操作,產生兩個輸出。而最終構成的模型。

 1 num_tags = 12  # Number of unique issue tags
 2 num_words = 10000  # Size of vocabulary obtained when preprocessing text data
 3 num_departments = 4  # Number of departments for predictions
 4 
 5 title_input = keras.Input(shape=(None,), name='title')  # Variable-length sequence of ints
 6 body_input = keras.Input(shape=(None,), name='body')  # Variable-length sequence of ints
 7 tags_input = keras.Input(shape=(num_tags,), name='tags')  # Binary vectors of size `num_tags`
 8 
 9 # Embed each word in the title into a 64-dimensional vector
10 title_features = layers.Embedding(num_words, 64)(title_input)
11 # Embed each word in the text into a 64-dimensional vector
12 body_features = layers.Embedding(num_words, 64)(body_input)
13 
14 # Reduce sequence of embedded words in the title into a single 128-dimensional vector
15 title_features = layers.LSTM(128)(title_features)
16 # Reduce sequence of embedded words in the body into a single 32-dimensional vector
17 body_features = layers.LSTM(32)(body_features)
18 
19 # Merge all available features into a single large vector via concatenation
20 x = layers.concatenate([title_features, body_features, tags_input])
21 
22 # Stick a logistic regression for priority prediction on top of the features
23 priority_pred = layers.Dense(1, name='priority')(x)
24 # Stick a department classifier on top of the features
25 department_pred = layers.Dense(num_departments, name='department')(x)
26 
27 # Instantiate an end-to-end model predicting both priority and department
28 model = keras.Model(inputs=[title_input, body_input, tags_input],
29                     outputs=[priority_pred, department_pred])

  顯示其模型結構,包含三個輸入,兩個輸出:

  在編譯模型是,由於模型包含兩個輸出,可以為兩個模型分別設置損失函數。

1 model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
2               loss=[keras.losses.BinaryCrossentropy(from_logits=True),
3                     keras.losses.CategoricalCrossentropy(from_logits=True)],
4               loss_weights=[1., 0.2])

  為了代碼的可讀性,在設定損失函數時可以使用字典的方式,用於聲明每個損失函數的歸屬。

1 model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
2               loss={'priority':keras.losses.BinaryCrossentropy(from_logits=True),
3                     'department': keras.losses.CategoricalCrossentropy(from_logits=True)},
4               loss_weights=[1., 0.2])

  訓練模型:

 1 # Dummy input data
 2 title_data = np.random.randint(num_words, size=(1280, 10))
 3 body_data = np.random.randint(num_words, size=(1280, 100))
 4 tags_data = np.random.randint(2, size=(1280, num_tags)).astype('float32')
 5 
 6 # Dummy target data
 7 priority_targets = np.random.random(size=(1280, 1))
 8 dept_targets = np.random.randint(2, size=(1280, num_departments))
 9 
10 model.fit({'title': title_data, 'body': body_data, 'tags': tags_data},
11           {'priority': priority_targets, 'department': dept_targets},
12           epochs=2,
13           batch_size=32)

  更詳細的訓練驗證指南:https://tensorflow.google.cn/guide/keras/train_and_evaluate

ResNet Model(toy version)

 1 inputs = keras.Input(shape=(32, 32, 3), name='img')
 2 x = layers.Conv2D(32, 3, activation='relu')(inputs)
 3 x = layers.Conv2D(64, 3, activation='relu')(x)
 4 block_1_output = layers.MaxPooling2D(3)(x)
 5 
 6 x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_1_output)
 7 x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
 8 block_2_output = layers.add([x, block_1_output])
 9 
10 x = layers.Conv2D(64, 3, activation='relu', padding='same')(block_2_output)
11 x = layers.Conv2D(64, 3, activation='relu', padding='same')(x)
12 block_3_output = layers.add([x, block_2_output])
13 
14 x = layers.Conv2D(64, 3, activation='relu')(block_3_output)
15 x = layers.GlobalAveragePooling2D()(x)
16 x = layers.Dense(256, activation='relu')(x)
17 x = layers.Dropout(0.5)(x)
18 outputs = layers.Dense(10)(x)
19 
20 model = keras.Model(inputs, outputs, name='toy_resnet')
21 model.summary()

  看一眼它的結構:

keras.utils.plot_model(model, 'mini_resnet.png', show_shapes=True)

  然后訓練模型:

 1 (x_train, y_train), (x_test, y_test) = keras.datasets.cifar10.load_data()
 2 
 3 x_train = x_train.astype('float32') / 255.
 4 x_test = x_test.astype('float32') / 255.
 5 y_train = keras.utils.to_categorical(y_train, 10)
 6 y_test = keras.utils.to_categorical(y_test, 10)
 7 
 8 model.compile(optimizer=keras.optimizers.RMSprop(1e-3),
 9               loss=keras.losses.CategoricalCrossentropy(from_logits=True),
10               metrics=['acc'])
11 
12 model.fit(x_train, y_train,
13           batch_size=64,
14           epochs=1,
15           validation_split=0.2)

共享層

  談到共享層,便會想到Siamese Network系列的各種模型,包括圖像分類,目標追蹤,few shot目標檢測等。了解這一類模型,便不難理解共享層在keras中的使用:

  聲明兩個輸入,將其輸入到相同的層中,產生輸出即可。

 1 # Embedding for 1000 unique words mapped to 128-dimensional vectors
 2 shared_embedding = layers.Embedding(1000, 128)
 3 
 4 # Variable-length sequence of integers
 5 text_input_a = keras.Input(shape=(None,), dtype='int32')
 6 
 7 # Variable-length sequence of integers
 8 text_input_b = keras.Input(shape=(None,), dtype='int32')
 9 
10 # Reuse the same layer to encode both inputs
11 encoded_input_a = shared_embedding(text_input_a)
12 encoded_input_b = shared_embedding(text_input_b)

API的延伸:使用自定義層

  tf.keras中包含廣泛的層,例如:

  • Convolutional layers: Conv1D, Conv2D, Conv3D, Conv2DTranspose
  • Pooling layers: MaxPooling1D, MaxPooling2D, MaxPooling3D, AveragePooling1D
  • RNN layers: GRU, LSTM, ConvLSTM2D
  • BatchNormalization, Dropout, Embedding, etc.

  但如果你找不到想要的層,便可以使用API的延伸,生成自己需要的層。自定義層需要繼承layers.Layer類,並定義build 和call函數:

  • call函數定義具體的前向計算過程;
  • build函數初始化各層的權重;
 1 class CustomDense(layers.Layer):
 2   def __init__(self, units=32):
 3     super(CustomDense, self).__init__()
 4     self.units = units
 5 
 6   def build(self, input_shape):
 7     self.w = self.add_weight(shape=(input_shape[-1], self.units),
 8                              initializer='random_normal',
 9                              trainable=True)
10     self.b = self.add_weight(shape=(self.units,),
11                              initializer='random_normal',
12                              trainable=True)
13 
14   def call(self, inputs):
15     return tf.matmul(inputs, self.w) + self.b
16 
17 
18 inputs = keras.Input((4,))
19 outputs = CustomDense(10)(inputs)
20 
21 model = keras.Model(inputs, outputs)

  對於自定義層中的序列化支持,定義一個get_config方法,該方法返回層實例的構造函數參數:

 1 class CustomDense(layers.Layer):
 2 
 3   def __init__(self, units=32):
 4     super(CustomDense, self).__init__()
 5     self.units = units
 6 
 7   def build(self, input_shape):
 8     self.w = self.add_weight(shape=(input_shape[-1], self.units),
 9                              initializer='random_normal',
10                              trainable=True)
11     self.b = self.add_weight(shape=(self.units,),
12                              initializer='random_normal',
13                              trainable=True)
14 
15   def call(self, inputs):
16     return tf.matmul(inputs, self.w) + self.b
17 
18   def get_config(self):    # 新增函數,返回構造函數中的數值
19     return {'units': self.units}
20 
21 
22 inputs = keras.Input((4,))
23 outputs = CustomDense(10)(inputs)
24 
25 model = keras.Model(inputs, outputs)
26 config = model.get_config()   # 通過get_config來獲取config信息
27 
28 new_model = keras.Model.from_config(
29     config, custom_objects={'CustomDense': CustomDense})

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM