心法利器[3] | tf.keras自學筆記

2021-03-02 CS的陋室

說起來自己還挺菜的,tensorflow很久之前就已經把keras合併到自己的生態裡,雖然早就會了keras,但是因為排期的原因自己一直沒有更新這塊的知識,這次系統地學習一下,並記錄下來。

當然,我不想寫成一個教程,也不想整成一個API,更希望這是一個導學,能給大家一些學習的思路,學這個之前,希望大家還是要對深度學習和tensorflow有些了解。下面講的實質性知識的東西,全部來源於tensorflow的源碼和官網版本的API文檔。給個連結吧:

tensorflow源碼:https://github.com/tensorflowtensorflow1.15.0文檔:https://tensorflow.google.cn/versions/r1.15/api_docs/python/tf/keras/wrapperstensorflow keras文檔:https://tensorflow.google.cn/guide/keras

我的目標是學的1.14,但是1.14文檔因為一些原因我好像上不去,所以我主要參考的是1.15的文檔,在更新公告上沒有對keras進行太大幅度的更新,因此不用太擔心。

tf.keras生態

keras本身是一套tensorflow老版本的優化方案,由於其便捷性一直受到大家歡迎,因此google將其收編也是大勢所趨,合併的具體版本並不清楚,但是在1.14版本已經成型,因此我以這個版本為基礎進行自己的學習,有API更新的我後續也會跟進。

接著說tf.keras,最簡單的了解一個包,其實就是用help了,我們來看看help(tensorflow.keras)會發生什麼,內容比較多,這裡我只想把這玩意下屬的包給列舉出來,特殊的我加點解釋:

PACKAGE CONTENTS
    activations (package) # 激活函數
    applications (package) # 應用,預裝的一些固定結構,例如mobilenet
    backend (package) # 後端,底層函數,類似絕對值、點積之類的都有
    callbacks (package) # 回調函數接口
    constraints (package) # 訓練過程中對函數的約束,如MaxNorm
    datasets (package) # 數據集操作
    estimator (package) # 基於keras構造的estimator類
    experimental (package) # 一些實驗模塊會放在這裡,例如學習率變化策略
    initializers (package) # 初始化工具
    layers (package) # 各種深度學習的層
    losses (package) # 各種損失函數定義
    metrics (package) # 各種評估指標,可用於訓練過程監測
    mixed_precision (package)
    models (package) # 就是keras裡的model類,組合各種層形成模型
    optimizers (package) # 優化器
    preprocessing (package) # 預處理工具
    regularizers (package) # 正則化
    utils (package) # 各種工具函數
    wrappers (package) # 實現多個工具共通,目前實現了sklearn的

可見tf.keras實現了大量的功能,形成了相對完備的深度學習生態,這個完整的框架能讓我們輕鬆實現深度學習。

當然看API學習本身缺少系統性,API更適合學完之後的深入學習或者是平時的詞典查閱,學習還是要系統性的。

建模框架

如何寫模型應該是初學者最關心的問題,keras建模主要有兩種模式,分別是序列式和函數式。

序列式建模

連結:https://tensorflow.google.cn/guide/keras/sequential_model

序列式建模是keras最基本的結構,簡單的理解就是和搭積木一樣一個接著一個的堆疊就好了,來看看例子:

# Define Sequential model with 3 layers
model = keras.Sequential(
    [
        layers.Dense(2, activation="relu", name="layer1"),
        layers.Dense(3, activation="relu", name="layer2"),
        layers.Dense(4, name="layer3"),
    ]
)
# Call model on a test input
x = tf.ones((3, 3))
y = model(x)

通過keras.Sequential將每一個層堆疊起來,這個堆疊的實際上就是keras.layers的對象,如上圖所示就是3個全連接層,這樣子堆疊相比古老的tensorflow.nn就避免了計算每一層輸入輸出的維數的問題,很方便。

當然,除了直接構造一個keras.layers對象向量直接放入Sequential之外,還可以用add的方式進行放入。

model = keras.Sequential()
model.add(layers.Dense(2, activation="relu"))
model.add(layers.Dense(3, activation="relu"))
model.add(layers.Dense(4))

構造完之後,如果想看看自己的建模內容,可以用``model.summary`查看並匯總,上面的模型執行後的效果如下:

Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_7 (Dense)              (1, 2)                    10        
_________________________________________________________________
dense_8 (Dense)              (1, 3)                    9         
_________________________________________________________________
dense_9 (Dense)              (1, 4)                    16        
=================================================================
Total params: 35
Trainable params: 35
Non-trainable params: 0

函數式建模

函數式建模是keras有一種建模框架,文檔:https://tensorflow.google.cn/guide/keras/functional。

函數式是一種更為靈活的模式,對於更複雜的網絡,就可以用它來整。來看一個完整的例子:

inputs = keras.Input(shape=(784,))

dense = layers.Dense(64, activation="relu")
x = dense(inputs)

x = layers.Dense(64, activation="relu")(x)

outputs = layers.Dense(10)(x)

model = keras.Model(inputs=inputs, outputs=outputs, name="mnist_model")

首先初始化了一個輸入點,然後後面構造的dense對象,這個對象是可調用的(內部其實就是有一個call函數),調用了這個inputs,這就代表了在inputs後接了一個dense層,一個接著一個,就能實現整體建模,我們用``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
_________________________________________________________________

如我們所料就是一個完整網絡。

有了網絡,就可以開始訓練和預測了,整個過程也不複雜:

# 加載數據
(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()

# 數據預處理
x_train = x_train.reshape(60000, 784).astype("float32") / 255
x_test = x_test.reshape(10000, 784).astype("float32") / 255

# 模型編譯
model.compile(
    loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    optimizer=keras.optimizers.RMSprop(),
    metrics=["accuracy"],
)

# 訓練模型
history = model.fit(x_train, y_train, batch_size=64, epochs=2, validation_split=0.2)

# 模型效果評估
test_scores = model.evaluate(x_test, y_test, verbose=2)
print("Test loss:", test_scores[0])
print("Test accuracy:", test_scores[1])

剛才說到,函數式能構造更為複雜的模型,我們來看一個例子,我們先看一張模型的結構圖:

可以清楚看到,模型有多個輸入和多個輸出,這種模型結構就只能用函數式來處理了。

num_tags = 12  # Number of unique issue tags
num_words = 10000  # Size of vocabulary obtained when preprocessing text data
num_departments = 4  # Number of departments for predictions

# 模型的輸入部分,一共3個輸入
title_input = keras.Input(
    shape=(None,), name="title"
)  # Variable-length sequence of ints
body_input = keras.Input(shape=(None,), name="body")  # Variable-length sequence of ints
tags_input = keras.Input(
    shape=(num_tags,), name="tags"
)  # Binary vectors of size `num_tags`

# 對模型的輸入分別進行處理
# Embed each word in the title into a 64-dimensional vector
title_features = layers.Embedding(num_words, 64)(title_input)
# Embed each word in the text into a 64-dimensional vector
body_features = layers.Embedding(num_words, 64)(body_input)
# Reduce sequence of embedded words in the title into a single 128-dimensional vector
title_features = layers.LSTM(128)(title_features)
# Reduce sequence of embedded words in the body into a single 32-dimensional vector
body_features = layers.LSTM(32)(body_features)

# 處理以後將他們拼接起來
# Merge all available features into a single large vector via concatenation
x = layers.concatenate([title_features, body_features, tags_input])

# Stick a logistic regression for priority prediction on top of the features
priority_pred = layers.Dense(1, name="priority")(x)
# Stick a department classifier on top of the features
department_pred = layers.Dense(num_departments, name="department")(x)

# 整理結果,完成輸出
# Instantiate an end-to-end model predicting both priority and department
model = keras.Model(
    inputs=[title_input, body_input, tags_input],
    outputs=[priority_pred, department_pred],
)

當然的,構造好模型以後,就需要編譯模型,定義好訓練方式,針對這種多目標的問題還要設計好對應的權重,一般的有兩種方式:

# 向量形式的損失函數定製
model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss=[
        keras.losses.BinaryCrossentropy(from_logits=True),
        keras.losses.CategoricalCrossentropy(from_logits=True),
    ],
    loss_weights=[1.0, 0.2],
)
# 字典形式的損失函數定製
model.compile(
    optimizer=keras.optimizers.RMSprop(1e-3),
    loss={
        "priority": keras.losses.BinaryCrossentropy(from_logits=True),
        "department": keras.losses.CategoricalCrossentropy(from_logits=True),
    },
    loss_weights=[1.0, 0.2],
)

並在訓練過程中按照要求灌入數據,完成訓練:

# Dummy input data
title_data = np.random.randint(num_words, size=(1280, 10))
body_data = np.random.randint(num_words, size=(1280, 100))
tags_data = np.random.randint(2, size=(1280, num_tags)).astype("float32")

# Dummy target data
priority_targets = np.random.random(size=(1280, 1))
dept_targets = np.random.randint(2, size=(1280, num_departments))

model.fit(
    {"title": title_data, "body": body_data, "tags": tags_data},
    {"priority": priority_targets, "department": dept_targets},
    epochs=2,
    batch_size=32,
)

當然,還有一些諸如共享層、權重提取、重用之類的場景,keras都有實現,詳情可以看這個連結:https://tensorflow.google.cn/guide/keras/functional。

自定義層

類似transformer之類的,都是對模型的創新,這時候我們需要自己去寫了,只需要按照keras給定的API去寫就行:

class CustomDense(layers.Layer):
    def __init__(self, units=32):
        super(CustomDense, self).__init__()
        self.units = units

    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
        )

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b

    def get_config(self):
        return {"units": self.units}


inputs = keras.Input((4,))
outputs = CustomDense(10)(inputs)

model = keras.Model(inputs, outputs)
config = model.get_config()

new_model = keras.Model.from_config(config, custom_objects={"CustomDense": CustomDense})

類裡面有幾個需要注意的點:

繼承layers,並定義好對應的構造函數__init__。build中可以進一步對參數進行定義,在第一次使用該層的時候調用該部分代碼,在這裡創建變量可以使得變量的形狀自適應輸入的形狀,並且給裡面的參數進行初始化,可以看到裡面有個initializercall,在函數式下,執行這一層的時候會調用這個函數進行前向計算。像上面的例子就是做了一個線性變換。如果是需要支持序列化,則需要用get_config或者是from_config的方式把必要的超參返回出來。

有關自定義新層的方法,詳見:https://tensorflow.google.cn/guide/keras/custom_layers_and_models

訓練、評估和預測

上面講的都是模型的建立,現在我們就要談談怎麼怎麼折騰這個模型了。

首先是訓練,在進行建模和編譯以後,就可以開始訓練了:

# 模型編譯
model.compile(
    optimizer=keras.optimizers.RMSprop(),  # Optimizer
    # Loss function to minimize
    loss=keras.losses.SparseCategoricalCrossentropy(),
    # List of metrics to monitor
    metrics=[keras.metrics.SparseCategoricalAccuracy()],
)

# 開始訓練
history = model.fit(
    x_train,
    y_train,
    batch_size=64,
    epochs=2,
    # We pass some validation for
    # monitoring validation loss and metrics
    # at the end of each epoch
    validation_data=(x_val, y_val),
)

在進行訓練以後就可以進行評估和預測了:

results = model.evaluate(x_test, y_test, batch_size=128)

predictions = model.predict(x_test[:3])

注意,這裡的predict其實只是把輸出層的預測結果給出,如果是分類問題,可能還要做類似篩選最大之類的後處理。

保存模型

保存模型一共分3種:

這3種模式都有很明確的使用場景,例如最後一個權重的,就是在模型預測、遷移學習時使用。

保存和加載整個模型

對於這種模式的保存,是最完整的,按照官方說法,一共覆蓋著4種東西:

API也非常簡單:

model.save() 或 tf.keras.models.save_model()tf.keras.models.load_model()保存架構

保存架構是只保存模型的結構,而不保存權重。

get_config() 和 from_config()tf.keras.models.model_to_json() 和 tf.keras.models.model_from_json()僅保存權重get_config() 和 from_config()tf.keras.models.model_to_json() 和 tf.keras.models.model_from_json()

到此,整個模型的核心流程已經完成,下面我們看一下keras還為我們提供了什麼有意思的功能供我們使用。

值得關注的API

經過上面的詳細學習,其實大家已經對tensorflow.keras有足夠的了解,剩下就是在實踐過程中逐步學習成長,但這裡面,也有很多指的閱讀的API和源碼,有興趣的大家可以好好看看。

layers

這應該是建模的關鍵,keras目前已經支持幾乎所有主流模型,全連接、卷積、池化、RNN等。有興趣的多去看看,包括裡面涉及的變量,都多了解,對後續自己寫自定義層有很大好處。

losses

各種損失函數,常見的如BinaryCrossentropy、MeanSquaredError,也有一些比較有意思的Hinge、Poisson等。

activations

激活函數,可以看看有些啥,煉丹的時候可以都嘗試嘗試,softmax、tanh、relu,也有elu之類的新東西。

preprocessing

預處理,這裡分成了三個模塊,image、sequence、text,很多的預處理工作其實在這裡面都有實現。

metrics

評估指標是評價模型好壞的核心,keras裡面也內置了大量的已經寫好的函數,AUC、Accuracy等。

backend

backend裡面有大量非常有用的基礎函數,熟悉一下,可以大大減輕自己手寫代碼的壓力。來看看幾個例子:

argmax,argmin,dot,各種簡單算子。manual_variable_initialization,人工特徵初始化。小結

至今仍然感覺,看API、看源碼、看文檔是對一個工具了解的必經之路,看看博客之類的遠遠無法真正了解他們,即使是我們已經熟練使用了,回頭看看文檔,也會發現裡面有很多好東西,總之,學無止境吧。

相關焦點

  • Keras 2.3.0 發布,後續將被 tf.keras 取代
    Keras 2.3.0 發布了(multi-backend Keras)。
  • TensorFlow 1.9 新增 tf.keras 官方入門教程(Keras與TF的深度集成)
    好消息是Keras的許多核心功能已經融入了原生的TensorFlow(tf.keras),TensorFlow 1.9新增tf.keras官方入門文檔,介紹了tf.keras的使用。這幾天打開TensorFlow的官方教程頁(https://www.tensorflow.org/tutorials/),會發現教程頁中給出的DEMO變成了基於tf.keras的教程。
  • Tensorflow.keras筆記-卷積神經網絡
    Tensorflow.keras筆記-卷積神經網絡cifar10數據集    1.
  • tf.keras.losses.binary_crossentropy
    tf.keras.losses: 評價機器學習模型預測值和實際值偏離情況的一個類。loss類用來評估預測結果的好壞。
  • 一文上手Tensorflow2.0之tf.keras|三
    Tensorflow2.0 的安裝(CPU和GPU)「tf.keras」API3 TensorFlow2.0使用3.2 「tf.keras」APIKeras是一個基於Python編寫的高層神經網絡API,Keras強調用戶友好性、模塊化以及易擴展等,其後端可以採用TensorFlow、Theano以及CNTK,目前大多是以TensorFlow作為後端引擎。
  • TensorFlow筆記:高級封裝—tf.Estimator
    tf.Estimator的特點是:既能在model_fn中靈活的搭建網絡結構,也不至於像原生tensorflow那樣複雜繁瑣。相比於原生tensorflow更便捷、相比與keras更靈活,屬於二者的中間態。實現一個tf.Estimator主要分三個部分:input_fn、model_fn、main三個函數。
  • TensorFlow 1.9.0-rc0 升級 tf.keras 至 Keras 2.1.6 API
    該版本帶來了不少改進和新特性:Update tf.keras to the Keras 2.1.6 API.tfe.Network is deprecated. Please inherit from tf.keras.Model.
  • Python安裝TensorFlow 2、tf.keras和深度學習模型的定義
    在本教程中,您將找到使用tf.keras API在TensorFlow中開發深度學習模型的分步指南。完成本教程後,您將知道:Keras和tf.keras之間的區別以及如何安裝和確認TensorFlow是否有效。tf.keras模型的5個步驟的生命周期以及如何使用順序和功能性API。
  • tf.keras.activations.softmax
    這是一個嗷嗷厲害的激活函數,常常用於最後階段的分類它將輸入數據概率化,輸出概率值接下來我們一探究竟:softmax函數的完整寫法為:tf.keras.activations.softmax(    x,axis=-1)包含兩個參數,其中axis默認為-1為了更好的展示這個函數做了什麼,我們利用:1、輸入數據到激活函數2、激活函數輸出數據3、對輸出數據圖像化展示
  • 用RNN構建文本生成器(TensorFlow Eager+ tf.keras)
    簡介教程包含了用Tensorflow Eager(動態圖)和tf.keras實現的可執行代碼,下面是代碼運行的示例結果:QUEENE:I had thought thou hadst a Roman; for the oracle,Thus by All bids the man against
  • 深度學習第17講:keras入門和快速上手指南
    作者:魯偉一個數據科學踐行者的學習日記。
  • tf.keras.preprocessing.sequence.pad_sequences
    如果要實現這樣的一個需求:請將下面的矩陣:[[1],[2,3],[4,5,6],]
  • 教程 | 一招教你使用 tf.keras 和 eager execution 解決複雜問題
    (圖片標註)在暑期實習期間,我使用 TensorFlow 的兩個最新 API(tf.keras 和 eager execution)開發了這些示例,以下是分享內容。希望你們能覺得它們有用,有趣! Eager execution 是一個由運行定義的命令式接口,一旦從 Python 調用,其操作將被立即執行。這使得入門 TensorFlow 變得更簡單,也使研發更直觀。
  • 使用 Keras Tuner 調節超參數
    下方示例中,我們構建了一個簡單的可調參模型,並使用 CIFAR-10 進行訓練:import tensorflow as tfdef build_model(hp): inputs = tf.keras.Input(shape=(32, 32, 3)) x = inputs for i in range(hp.Int('conv_blocks
  • tf.keras.layers.Dense()
    本文共615字每一個神經層都都會讓模型變得更複雜完整表達式:tf.keras.layers.Dense
  • 【五分鐘學習Tensorflow系列】tf.keras.layers.Prelu原理
    二、tf.keras.layers.Prelu接口tf.keras.layers.PReLU( alpha_initializer='zeros', alpha_regularizer=None, alpha_constraint=None, shared_axes=None, **kwargs)表達式是:f(x)
  • 擴展之Tensorflow2.0 | 21 Keras的API詳解(上)卷積、激活、初始化、正則
    input_shape = (4, 28, 28, 3)x = tf.random.normal(input_shape)y = tf.keras.layers.Conv2D( filters=2,kernel_size=3, activation='relu',padding='same')print(y(x).shape)
  • 用python建立第一個機器學習模型-TF教程
    請記住:1、在建立任何一個模型前,需要搞清楚數據結構,數據要包含兩部分,分別是預測數據和結果數據比如數據是:[[1,2,3],[1]],[[2,3,4],[0]]這裡預測數據指的是[1,2,3],[2,3,4],結果數據指的是[1],[0]2、將數據拆分成兩組,第一組訓練模型,第二組測試模型的準確性。
  • Keras實例:PointNet點雲分類
    trimeshimport numpy as npimport tensorflow as tffrom tensorflow import kerasfrom tensorflow.keras import layersfrom matplotlib import pyplot as plttf.random.set_seed(1234)加載數據集
  • Keras結合Keras後端搭建個性化神經網絡模型(不用原生Tensorflow)
    3、call(self, *args, **kwargs):編寫層的功能邏輯。單一輸入當輸入張量只有一個時,下面是實現全連接層的例子:import numpy as npfrom keras import layers,Model,Input,utilsfrom keras import backend as Kimport tensorflow as tfclass