本文章转载自博客:
https://blog.csdn.net/hewb14/article/details/53414068
Keras拥有不错的扩展性,这一方面是因为设计时就留好的接口,另一方面是因为清晰的代码结构,让你可以有很多自定义的空间。所以下面用几个例子介绍在Keras中如何自定义层和各种方法。
0、backend
如果想在Keras中自定义各种层和函数,一定会用到的就是backend。一般导入的方法是
- from keras import backend as K
通过引入这个backend,就可以让Keras来处理兼容性。
比如求x的平均,就是K.mean(x)。backend文件本身在keras/backend文件夹下,可以通过阅读代码来了解backend都支持哪些操作。backend里面函数很多,一般都够用了。
1、Lambda 层
如果你只是想对流经该层的数据做个变换,而这个变换本身没有什么需要学习的参数,那么直接用Lambda Layer是最合适的了。
导入的方法是
- <span style="font-size:18px;"> from keras.layers.core import Lambda</span>
- <span style="font-size:18px;">def sub_mean(x):
- x -= K.mean(x,axis=1,keepdims=True)
- return x
- model.add( Lambda(sub_mean,output_shape=lambda input_shape:input_shape ))</span>
把模型完整地建立出来:
- <span style="font-size:18px;">def get_submean_model():
- model = Sequential()
- model.add(Dense(5,input_dim=7))
- def sub_mean(x):
- x -= K.mean(x,axis=1,keepdims=True)
- return x
- model.add( Lambda(sub_mean,output_shape=lambda input_shape:input_shape))
- model.compile(optimizer='rmsprop',loss='mse')
- return model
- model = get_submean_model()
- res=model.predict(np.random.random((3,7)))</span>
2、自定义非递归层
如果自己想定义的层中有需要学习的变量,那么就不能用lambda层了,需要自己写一个出来。
比如说我想定义一个层,它的效果是对张量乘一个正对角阵(换句话说,输入向量与一个要学习的向量逐元素相乘),那么可以这样写:
首先要导入基类 from keras.engine.topology import Layer
然后对MyLaber定义如下:
- <span style="font-size:18px;">class MyLayer(Layer):
- def __init__(self,output_dim,**kw):
- self.output_dim = output_dim
- super(MyLayer,self).__init__(**kw)
- def build(self,input_shape):
- input_dim = input_shape[1]
- assert(input_dim == self.output_dim)
- inital_SCALER = np.ones((input_dim,))*1000
- self.SCALER = K.variable(inital_SCALER)
- self.trainable_weights = [self.SCALER]
- super(MyLayer,self).build(input_shape)
- def call(self,x,mask=None):
- #return x - K.mean(x,axis=1,keepdims=True)
- x *= self.SCALER
- return x
- def get_output_shape_for(self,input_shape):
- return input_shape</span>
- <span style="font-size:18px;">def get_mylayer_model():
- model = Sequential()
- model.add(Dense(5,input_dim=7))
- model.add(MyLayer(5))
- model.compile(optimizer='rmsprop',loss='mse')
- return model
- model = get_mylayer_model()
- res=model.predict(np.random.random((3,7)))
- print res</span>
[[ 271.2746582 -1053.31506348 147.17185974 -1120.33740234 609.54876709]
[ -263.69671631 -390.41921997 291.17721558 -594.58721924 615.97369385]
[ -46.58752823 -733.11328125 -21.9815979 -570.79351807 649.44158936]]
都是很大的数,而不加MyLayer时每个值一般也不超过+-2,这个层确实起了作用。
在fit之前调用model.get_weights(),看到该层的权重都是1000,随便随机出来个测试集,fit几千个epoch只后,loss变得很小,MyLayer的权重变成了997左右,而前面一层Dense的权重都成10^-4量级,说明MyLayer中的参数也确实是可学习的。
3、自定义损失函数
Keras内置的损失函数都在keras/objectives.py中,比如mse的定义是:
- <span style="font-size:18px;">def mean_squared_error(y_true, y_pred):
- return K.mean(K.square(y_pred - y_true), axis=-1)</span>
- <span style="font-size:18px;">def my_object(y_true,y_pred):
- return K.mean(K.square(K.square(y_pred-y_true)),axis=-1)</span>
- def get_myobj_model():
- model = Sequential()
- model.add(Dense(5,input_dim=7))
- model.add(Dense(3))
- def my_object(y_true,y_pred):
- return K.mean(K.square(K.square(y_pred-y_true)),axis=-1)
- model.compile(optimizer='sgd',loss=my_object)
- return model
- model = get_myobj_model()
另外一些很有用的损失函数如warp-ctc,就可以在这里集成进模型。
4、自定义递归层
递归层的定义方法和非递归层不太一样。根据Keras内LSTM的写法,它还有一个reset_states函数和step函数,这是由递归的性质决定的。例子都在keras/layers/recurrent.py中。
之前看学长用lasagne写的LSTM的变体,看得我想哭,还不如在Keras中把LSTM得代码复制过来修修改改。不过LSTM也不能直接复制过来,还需要import几个依赖:
- rom keras.layers.recurrent import LSTM,Recurrent,time_distributed_dense
- from keras import initializations,regularizers,activations
- from keras.engine import InputSpec
5、自定义优化函数
Keras的代码确实好,耦合度很低。Keras内置的优化函数在keras/optimizers.py中,基类Optimizer也在这个文件里。例如把它内置的SGD算法拷贝到自己的文件中,只要先from keras.optimizers import Optimizer就能编译通过。
有时候要得到state-of-the-art的结果,需要用sgd加动量法充分收敛。比如学习率0.01学习上100epoch,再把学习率减半,再学100epoch,依次类推。如果不自定义优化函数的话,就要分阶段调用fit函数,修改学习率,可能还要重新compile。这就不是很优美了。其它一些奇葩的学习策略,也可以通过自定义优化函数来得到。
6、后记
Keras确实非常强大,不但能用来写大作业,做一些研究也够用了。