Keras第一层的权重没有改变。

3

我是Keras的新手,正在编写实现高斯函数的自定义层[exp(-(w*x-mean)^2/sigma^2)其中W、mean和sigma都是随机生成的]。
下面是自定义层的代码:

class Gaussian(Layer):
    def __init__(self,**kwargs):
        super(Gaussian, self).__init__(**kwargs)

    def build(self, input_shape):
        # Create trainable weights for this layer.
        self.W_init = np.random.rand(1,input_shape[1])
        self.W = K.variable(self.W_init, name="W")

        # Create trainable means for this layer.
        self.mean_init = np.random.rand(1,input_shape[1])
        self.mean = K.variable(self.mean_init, name="mean")

        # Create trainable sigmas for this layer.
        self.sigma_init = np.random.rand(1,input_shape[1])
        self.sigma = K.variable(self.sigma_init, name="sigma")

        self.trainable_weights = [self.mean, self.sigma]
        super(Gaussian, self).build(input_shape)  # Be sure to call this somewhere!

    def call(self, x):
        result = tf.multiply(x, self.W)
        result = tf.subtract(x, self.mean)
        result = tf.multiply(tf.square(result),-1)
        result = tf.divide(result, tf.square(self.sigma))
        return result

    def compute_output_shape(self, input_shape):
        return input_shape

Keras mnist教程中,我将自定义层放在第一层(只是为了确保它运行时不会产生错误,不关心准确性),并训练模型后发现,在约4轮后,损失停止下降,训练后“mean”和“sigma”的数字发生了变化,而“W”的数字保持不变。但是,如果将其作为第二层,则不会发生这种情况。
我再次运行了没有自定义层的Keras mnist教程,发现第一层的权重也没有改变。
这是Keras的一个问题,还是我漏掉了什么?我能强制更新吗?
谢谢!

2
你的网络可能存在消失梯度问题。在反向传播阶段,梯度越来越小,导致权重更新变得不存在。这里有一篇维基页面关于此问题:https://en.wikipedia.org/wiki/Vanishing_gradient_problem。 - Scratch'N'Purr
@Scratch'N'Purr 谢谢!那么,如果出现消失的问题,但准确性仍然很高,这是否可能是Keras mnist教程中没有自定义层的情况? - WhaleShark
当你说精度好的时候,您是指测试数据集上的精度吗?在训练中具有良好的精度,但在测试中具有较差的精度,这意味着过度拟合。 - Scratch'N'Purr
我突然想到一件事:如果问题是梯度消失,那么权重在完全停止变化之前不应该还会稍微改变一点吗?或者说即使是第一次反向传播,变化也太小了吗? - WhaleShark
1
不确定我是否理解您的意思。基本上,观察到的行为是您最后一层中的权重将发生变化,但是当您向第一层反向传播时,权重的变化会越来越少,因此,当您到达第一层时,梯度非常小,权重不会改变。我不太确定您创建了多少层,但是这个问题发生在非常深的层中。此外,如果您使用ReLU作为激活函数,那么我怀疑消失的梯度不是问题。 - Scratch'N'Purr
1个回答

2

你没有正确实现你的层,Keras不知道你的权重,这意味着它们没有被梯度下降训练。请参考这个示例:

from keras import backend as K
from keras.engine.topology import Layer
import numpy as np

class MyLayer(Layer):

    def __init__(self, output_dim, **kwargs):
        self.output_dim = output_dim
        super(MyLayer, self).__init__(**kwargs)

    def build(self, input_shape):
        # Create a trainable weight variable for this layer.
        self.kernel = self.add_weight(name='kernel', 
                                      shape=(input_shape[1], self.output_dim),
                                      initializer='uniform',
                                      trainable=True)
        super(MyLayer, self).build(input_shape)  # Be sure to call this at the end

    def call(self, x):
        return K.dot(x, self.kernel)

    def compute_output_shape(self, input_shape):
        return (input_shape[0], self.output_dim)

在这里,您需要使用add_weight来获取可训练的权重,而不仅仅像您目前正在做的那样使用K.variable。这样,您的权重将被注册到Keras中,并且它们将被正确地训练。您应该对层中的所有可训练参数都这样做。


1
谢谢您的回答,但我尝试了这种方法,结果还是一样。 - WhaleShark
你好!你的例子很有帮助。你能告诉我们如何实现像tanh这样的激活函数吗? - amy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接