預(yù)熱TensorFlow2.0(2)——房?jī)r(jià)預(yù)估實(shí)戰(zhàn)

這篇文章承接上一篇預(yù)熱TensoFlow2.0——IRIS數(shù)據(jù)集實(shí)戰(zhàn),主是想更加詳細(xì)介紹一下如何使用tensorflow2.0中使用Keras高級(jí)API快速構(gòu)建模型。Keras是一個(gè)底層為tensoflow或者theano的高度封裝的深度學(xué)習(xí)庫(kù)。TensorFlow2.0官方推薦使用Keras進(jìn)行深度神經(jīng)網(wǎng)絡(luò)構(gòu)建,說(shuō)明Keras在網(wǎng)絡(luò)構(gòu)建方面確實(shí)有著其獨(dú)到的優(yōu)勢(shì)。很多人不滿意Keras的靈活性,稱(chēng)其無(wú)法根據(jù)自己的需求構(gòu)建網(wǎng)絡(luò)結(jié)構(gòu)。我這里小小的反駁一下:

  • 其實(shí)Keras可以可以很方便的讓用戶的自定義神經(jīng)網(wǎng)絡(luò)層,
  • Keras也可以讓用戶很方便的定義損失函數(shù),
  • Keras本身就是基于tensorflow封裝的,能非常好的和tensorflow兼容。

接下來(lái)筆者就通過(guò)一個(gè)房?jī)r(jià)預(yù)估實(shí)戰(zhàn),為大家介紹一下這個(gè)即將成為T(mén)ensoFlow2.0版模型構(gòu)建新寵兒的Keras。這里筆者只使用了tensorflow去構(gòu)建損失函數(shù),大多數(shù)API依然是pure Keras,不過(guò)整個(gè)流程參考的是TensorFlow2.0 preview版本的教程。

import tensorflow as tf
import keras
from keras.optimizers import Adam
import keras.backend as K

載入數(shù)據(jù)

from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
housing = fetch_california_housing()

房?jī)r(jià)預(yù)測(cè)任務(wù)就是給模型輸入房屋信息數(shù)據(jù),模型能夠準(zhǔn)確的預(yù)測(cè)出房?jī)r(jià)的值。數(shù)據(jù)格式如下圖所示一個(gè)data是一個(gè)維度為8維的數(shù)組,是一些房屋的年歲,臥室面積大小之類(lèi)的信息,需要預(yù)測(cè)的target是房?jī)r(jià)的值。

data

數(shù)據(jù)預(yù)處理

將數(shù)據(jù)分為訓(xùn)練集和測(cè)試劑,然后對(duì)數(shù)據(jù)做一些簡(jiǎn)單的預(yù)處理工作——標(biāo)準(zhǔn)化:即將數(shù)據(jù)轉(zhuǎn)換為均值為0,方差為1的正態(tài)分布。

X_train_full, X_test, y_train_full, y_test = train_test_split(
    housing.data, housing.target.reshape(-1, 1), random_state=42)
X_train, X_valid, y_train, y_valid = train_test_split(
    X_train_full, y_train_full, random_state=42)

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_valid_scaled = scaler.transform(X_valid)
X_test_scaled = scaler.transform(X_test)

用戶自定義層

這里就開(kāi)始使用keras自定義層了,需要注意keras自定義層的格式:

  • 一定要繼承keras.layers.Layer這個(gè)類(lèi)
  • __ init __ 定義網(wǎng)絡(luò)層的一些基本屬性,神經(jīng)元個(gè)數(shù),激活函數(shù)之類(lèi)。這里激活函數(shù)繼承了keras自帶的Activation,
  • build方法用于定義需要優(yōu)化的參數(shù),
  • call方法很重要,這里你需要定義網(wǎng)絡(luò)層需要做的操作,筆者定義的就是一個(gè)簡(jiǎn)單的Dense層的操作F(wx+b),
  • compute_output_shape是用來(lái)控制輸出的維度,如果這個(gè)網(wǎng)絡(luò)層的輸入和輸出形狀一致,那么此方法可以省略。

筆者這里為了演示,把keras.layers.Dense重新實(shí)現(xiàn)了一遍而已。大家可以通過(guò)這樣的格式定義各種其他功能的網(wǎng)絡(luò)層。

class MyDense(keras.layers.Layer):
    def __init__(self,units, activation=None, **kwargs):
        self.units = units
        self.output_dim = units
        self.activation = keras.layers.Activation(activation)
        super(MyDense, self).__init__(**kwargs)

    def build(self, input_shape):
        self.kernel = self.add_weight(name='kernel', 
                                      shape=(input_shape[1], self.units),
                                      initializer='uniform',
                                      trainable=True)
        self.biases = self.add_weight(name='bias', 
                                      shape=(self.units,),
                                      initializer='zeros',
                                      trainable=True)
        super(MyDense, self).build(input_shape)

    def call(self, X):
        return self.activation(K.dot(X,self.kernel) + self.biases)
    def compute_output_shape(self, input_shape):
        # 計(jì)算輸出形狀,如果輸入和輸出形狀一致,那么可以省略,否則最好加上
        return (input_shape[0], self.output_dim)

用戶自定義模型(Wide and Deep)

接下來(lái)就是用戶自定義模型,這里依然有個(gè)keras官方建議的標(biāo)準(zhǔn)格式:

  • 首先必須繼承keras.models.Model這個(gè)類(lèi),
  • __ init __ 定義模型需要的網(wǎng)絡(luò)層,如果把搭網(wǎng)絡(luò)架構(gòu)比作堆積木的話,這里就是從袋子里把積木塊拿出來(lái)。
  • call 就是將這層就是用已有的積木塊組合搭建網(wǎng)絡(luò)結(jié)構(gòu)啦

這里筆者搭建了一個(gè)Wide and Deep的網(wǎng)絡(luò)架構(gòu),筆者之前的文章對(duì)這個(gè)模型進(jìn)行了詳細(xì)的介紹,不明白的可以點(diǎn)這個(gè)鏈接去了解下。從call方法的代碼中可以看出其具體的操作是——將原輸入經(jīng)過(guò)兩層神經(jīng)網(wǎng)絡(luò)后的輸出原輸入拼接之后再喂給一層感知器。

class MyModel(keras.models.Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.hidden1 = MyDense(30, activation="relu")
        self.hidden2 = MyDense(30, activation="relu")
        self.output_ = MyDense(1)

    def call(self, input):
        hidden1 = self.hidden1(input)
        hidden2 = self.hidden2(hidden1)
        concat = keras.layers.concatenate([input, hidden2])
        output = self.output_(concat)
        return output

用戶自定以損失函數(shù)

自定義損失函數(shù),用tensoflow的api,就可以定義你自己想要的損失函數(shù)。由于是做一個(gè)回歸問(wèn)題,筆者這里定義了一個(gè)mean square error的損失函數(shù)。

def my_mse(y_true, y_pred):
    return tf.reduce_mean(tf.square(y_pred - y_true))

實(shí)例化模型

解下來(lái)要做的就是實(shí)例化和編譯模型,代碼也很簡(jiǎn)單。使用keras自帶的compile方法,傳入之前定義好的損失函數(shù)和優(yōu)化器即可。

model = MyModel()
model.compile(loss=my_mse, optimizer=Adam(lr=1e-4))

定以callbacks

callbacks在筆者看來(lái)是keras里面的一個(gè)神奇的特性,你可以通過(guò)callbacks定義模型一些在訓(xùn)練過(guò)程中的適當(dāng)時(shí)機(jī)可被調(diào)用函數(shù)。keras里面已經(jīng)幫你封裝了一些特別有用的callbacks函數(shù),這里介紹三個(gè)常用的方法:

  • keras.callbacks.TensorBoard 可以通過(guò)tensorboard 可視化訓(xùn)練過(guò)程,需要傳一個(gè)可視化文件保存的路徑給這個(gè)函數(shù),
  • keras.callbacks.EarlyStopping能夠有效的防止模型過(guò)擬合,其機(jī)制就是如果模型的val_loss不降反增,此方法能夠提前終止模型訓(xùn)練。筆者這里設(shè)置的是連續(xù)5個(gè)epoch模型的val_loss都是增加的話,模型就停止訓(xùn)練。
  • keras.callbacks.ModelCheckpoint 保存模型的checkpoint,同時(shí)你可以自己定義checkpoint的命名和保存頻率,這里筆者選擇5個(gè)epoch,保存一下checkpoint。
    大家也可以自己定義一下其他的callback函數(shù),比如計(jì)算acc和recall之類(lèi)的。
logdir = "./log"
filepath="./checkpoint/model_{epoch:02d}-{val_loss:.2f}.hdf5"
checkpoint = keras.callbacks.ModelCheckpoint(filepath,
    monitor='val_loss', save_weights_only=True,verbose=1,save_best_only=True, period=5)
callbacks = [
    keras.callbacks.TensorBoard(logdir),
    keras.callbacks.EarlyStopping(patience=5),
    checkpoint,
]

模型訓(xùn)練

接下來(lái)模執(zhí)行下面代碼模型就跑起來(lái)了,記得把之前定義的callbacks函數(shù)的list傳給fit方法。

model.fit(X_train_scaled, y_train,batch_size=32, epochs=20,
             validation_data=(X_valid_scaled, y_valid),
              callbacks=callbacks)

從模型訓(xùn)練輸出,我們可以看到,模型每5個(gè)epoch幫我保存了一個(gè)checkpoint。不知道你們發(fā)現(xiàn)沒(méi)有,原本筆者定義跑20個(gè)epoch,但是最終只跑著13個(gè)epoch模型就終止了訓(xùn)練。仔細(xì)觀察的話,9-13epoch的過(guò)程中val_loss一直在增加,說(shuō)明模型開(kāi)始過(guò)擬合了,callbacks定義的keras.callbacks.EarlyStopping起了作用,在模型過(guò)擬合時(shí)提前終止了模型的訓(xùn)練。


train

使用tensorboard --logdir log 之后我們通過(guò)瀏覽器訪問(wèn)localhost:6006 我們就可以看到模型訓(xùn)練過(guò)程中的loss曲線了。

tensorboard

結(jié)語(yǔ)

整個(gè)過(guò)程代碼量極少,而且模型的構(gòu)建(復(fù)雜的模型也可以很方便的構(gòu)建),模型的訓(xùn)練,模型的保存,loss的可視化相比于tensorflow之前的操作容易了許多。是時(shí)候?qū)W習(xí)一波tensorflow2.0官方推薦的模型構(gòu)建API——Keras了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容