keras concatenate_Keras结合Keras后端搭建个性化神经网络模型
Keras是基于Tensorflow等底層張量處理庫的高級API庫。它幫我們實現(xiàn)了一系列經(jīng)典的神經(jīng)網(wǎng)絡(luò)層(全連接層、卷積層、循環(huán)層等),以及簡潔的迭代模型的接口,讓我們能在模型層面寫代碼,從而不用仔細(xì)考慮模型各層張量之間的數(shù)據(jù)流動。
但是,當(dāng)我們有了全新的想法,想要個性化模型層的實現(xiàn),Keras的高級API是不能滿足這一要求的,而換成Tensorflow又要重新寫很多輪子,這時,Keras的后端就派上用場了。Keras將底層張量庫的函數(shù)功能統(tǒng)一封裝在“backend”中,用戶可以用統(tǒng)一的函數(shù)接口調(diào)用不同的后端實現(xiàn)的相同功能。所以,如果不追求速度的話,可以僅使用Keras實現(xiàn)你的任何獨特想法,從而避免使用原生Tensorflow寫重復(fù)的輪子。
我們定義并訓(xùn)練一個神經(jīng)網(wǎng)絡(luò)模型需要考慮的要素有三個:層、損失函數(shù)、優(yōu)化器。而我們創(chuàng)新主要在于前兩個,因此下面介紹如何結(jié)合Keras高級API與后端,自定義特殊神經(jīng)網(wǎng)絡(luò)層以及損失函數(shù)。
1 自定義網(wǎng)絡(luò)層
自定義層可以通過兩種方式實現(xiàn):使用Lambda層和繼承Layer類。
1.1 lambda層
Lambda層僅能對輸入做固定的變換,并不能定義可以通過反向傳播訓(xùn)練的參數(shù)(通過Keras的fit訓(xùn)練),因此能實現(xiàn)的東西較少。以下代碼實現(xiàn)了Dropout的功能:
from keras import backend as Kfrom keras import layersdef my_layer(x): mask = K.random_binomial(K.shape(x),0.5) return x*mask*2x = layers.Lambda(my_layer)(x)其中my_layer函數(shù)是自定義層要實現(xiàn)的操作,傳遞參數(shù)只能是Lambda層的輸入。定義好函數(shù)后,直接在layers.Lambda中傳入函數(shù)對象即可。實際上,這些變換不整合在lambda層中而直接寫在外面也是可以的:
from keras import backend as Kfrom keras import layersx = layers.Dense(500,activation='relu')(x) mask = K.random_binomial(K.shape(x),0.5)x = x*mask*2數(shù)據(jù)先經(jīng)過一個全連接層,然后再被0.5概率Dropout。以上實現(xiàn)Dropout只是作舉例,你可以以同樣的方式實現(xiàn)其它的功能。
1.2 繼承l(wèi)ayer類
如果你想自定義可以訓(xùn)練參數(shù)的層,就需要繼承實現(xiàn)Keras的抽象類Layer。主要實現(xiàn)以下三個方法:
1、__init__(self, *args, **kwargs):構(gòu)造函數(shù),在實例化層時調(diào)用。此時還沒有添加輸入,也就是說此時輸入規(guī)模未知,但可以定義輸出規(guī)模等與輸入無關(guān)的變量。類比于Dense層里的units、activations參數(shù)。
2、build(self, input_shape):在添加輸入時調(diào)用(__init__之后),且參數(shù)只能傳入輸入規(guī)模input_shape。此時輸入規(guī)模與輸出規(guī)模都已知,可以定義訓(xùn)練參數(shù),比如全連接層的權(quán)重w和偏執(zhí)b。
3、call(self, *args, **kwargs):編寫層的功能邏輯。
1.2.1 單一輸入
當(dāng)輸入張量只有一個時,下面是實現(xiàn)全連接層的例子:
import numpy as npfrom keras import layers,Model,Input,utilsfrom keras import backend as Kimport tensorflow as tfclass MyDense(layers.Layer): def __init__(self, units=32): #初始化 super(MyDense, self).__init__()#初始化父類 self.units = units #定義輸出規(guī)模 def build(self, input_shape): #定義訓(xùn)練參數(shù) self.w = K.variable(K.random_normal(shape=[input_shape[-1],self.units])) #訓(xùn)練參數(shù) self.b = tf.Variable(K.random_normal(shape=[self.units]),trainable=True) #訓(xùn)練參數(shù) self.a = tf.Variable(K.random_normal(shape=[self.units]),trainable=False) #非訓(xùn)練參數(shù) def call(self, inputs): #功能實現(xiàn) return K.dot(inputs, self.w) + self.b #定義模型input_feature = Input([None,28,28]) x = layers.Reshape(target_shape=[28*28])(input_feature)x = layers.Dense(500,activation='relu')(x) x = MyDense(100)(x)x = layers.Dense(10,activation='softmax')(x) model = Model(input_feature,x) model.summary() utils.plot_model(model)模型結(jié)構(gòu)如下:
在build()中,訓(xùn)練參數(shù)可以用K.variable或tf.Variable定義。并且,只要是用這兩個函數(shù)定義并存入self中,就會被keras認(rèn)定為訓(xùn)練參數(shù),不管是在build還是__init__或是其它函數(shù)中定義。但是K.variable沒有trainable參數(shù),不能設(shè)置為Non-trainable params,所以還是用tf.Variable更好更靈活些。
1.2.2 多源輸入
如果輸入包括多個張量,需要傳入張量列表。實現(xiàn)代碼如下:
import numpy as npfrom keras import layers,Model,Input,utilsfrom keras import backend as Kimport tensorflow as tfclass MyLayer(layers.Layer): def __init__(self, output_dims): super(MyLayer, self).__init__() self.output_dims = output_dims def build(self, input_shape): [dim1,dim2] = self.output_dims self.w1 = tf.Variable(K.random_uniform(shape=[input_shape[0][-1],dim1])) self.b1 = tf.Variable(K.random_uniform(shape=[dim1])) self.w2 = tf.Variable(K.random_uniform(shape=[input_shape[1][-1],dim2])) self.b2 = tf.Variable(K.random_uniform(shape=[dim2])) def call(self, x): [x1, x2] = x y1 = K.dot(x1, self.w1)+self.b1 y2 = K.dot(x2, self.w2)+self.b2 return K.concatenate([y1,y2],axis = -1) #定義模型input_feature = Input([None,28,28])#輸入x = layers.Reshape(target_shape=[28*28])(input_feature) x1 = layers.Dense(500,activation='relu')(x) x2 = layers.Dense(500,activation='relu')(x) x = MyLayer([100,80])([x1,x2]) x = layers.Dense(10,activation='softmax')(x) model = Model(input_feature,x) model.summary() utils.plot_model(model,show_layer_names=False,show_shapes=True)模型結(jié)構(gòu)如下:
總之,傳入張量列表,build傳入的input_shape就是各個張量形狀的列表。其它都與單一輸入類似。
2 自定義損失函數(shù)
根據(jù)Keras能添加自定義損失的特性,這里將添加損失的方法分為兩類:
1、損失需要根據(jù)模型輸出與真實標(biāo)簽來計算,也就是只有模型的輸出與外部真實標(biāo)簽作為計算損失的參數(shù)。
2、損失無需使用外部真實標(biāo)簽,也就是只用模型內(nèi)部各層的輸出作為計算損失的參數(shù)。
這兩類損失添加的方式并不一樣,希望以后Keras能把API再改善一下,這種冗余有時讓人摸不著頭腦。
2.1 第一類損失
這類損失可以通過自定義函數(shù)的形式來實現(xiàn)。函數(shù)的參數(shù)必須是兩個:真實標(biāo)簽與模型輸出,不能多也不能少,并且順序不能變。然后你可以在這個函數(shù)中定義你想要的關(guān)于輸出與真實標(biāo)簽之間的損失。然后在model.compile()中將這個函數(shù)對象傳給loss參數(shù)。代碼示例如下(參考鏈接):
def customed_loss(true_label,predict_label): loss = keras.losses.categorical_crossentropy(true_label,predict_label) loss += K.max(predict_label) return lossmodel.compile(optimizer='rmsprop', loss=customed_loss)如果硬是想用這種方法把模型隱層的輸出拿來算損失的話,也不是不可以。只要把相應(yīng)隱層的輸出添加到模型的輸出列表中,自定義損失函數(shù)就可以從模型輸出列表中取出隱層輸出來用了。即:
model = Model(input,[model_output, hidden_layer_output])當(dāng)然,這樣就把模型結(jié)構(gòu)改了,如果不想改模型的結(jié)構(gòu)而添加“正則化”損失,可以使用下面的方法。
2.2 第二類損失
這類損失可以用Model.add_loss(loss)方法實現(xiàn),loss可以使用Keras后端定義計算圖來實現(xiàn)。但是顯然,計算圖并不能把未來訓(xùn)練用的真實標(biāo)簽傳入,所以,add_loss方法只能計算模型內(nèi)部的“正則化”損失。
add_loss方法可以使用多次,損失就是多次添加的loss之和。使用了add_loss方法后,compile中就可以不用給loss賦值,不給loss賦值的話使用fit()時就不能傳入數(shù)據(jù)的標(biāo)簽,也就是y_train。如果給compile的loss賦值,最終的目標(biāo)損失就是多次add_loss添加的loss和compile中l(wèi)oss之和。另外,如果要給各項損失加權(quán)重的話,直接在定義loss的時候加上即可。代碼示例如下:
loss = 100000*K.mean(K.square(somelayer_output))#somelayer_output是定義model時獲得的某層輸出model.add_loss(loss)model.compile(optimizer='rmsprop')以上講的都是關(guān)于層輸出的損失,層權(quán)重的正則化損失并不這樣添加,自定義正則項可以看下面。
keras中添加正則化_Bebr的博客-CSDN博客_keras 正則化
里面介紹了已實現(xiàn)層的自定義正則化,但沒有介紹自定義層的自定義正則化,這里先挖個坑,以后要用再研究。
總結(jié)
以上是生活随笔為你收集整理的keras concatenate_Keras结合Keras后端搭建个性化神经网络模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python os模块system_py
- 下一篇: vs 没有足够的内存继续执行程序_科赋内