Python 自定义损失函数,Keras\\ValueError:无梯度

Python 自定义损失函数,Keras\\ValueError:无梯度,python,tensorflow,object,keras,Python,Tensorflow,Object,Keras,我正试图将我的Keras神经网络封装在类对象中。我已经在类设置之外实现了下面的功能,但是我想让它更友好。 总之,我的model调用函数sequential\u model,它创建了一个sequential模型。在compile步骤中,我定义了我自己的损失函数weighted\u categorical\u crossentropy,我希望序列模型实现它。 但是,当我运行下面的代码时,我得到以下错误:ValueError:没有为任何变量提供渐变: 我怀疑问题在于如何定义weighted\u cat

我正试图将我的Keras神经网络封装在
对象中。我已经在类设置之外实现了下面的功能,但是我想让它更友好。 总之,我的
model
调用函数
sequential\u model
,它创建了一个
sequential
模型。在
compile
步骤中,我定义了我自己的损失函数
weighted\u categorical\u crossentropy
,我希望序列模型实现它。 但是,当我运行下面的代码时,我得到以下错误:
ValueError:没有为任何变量提供渐变:

我怀疑问题在于如何定义
weighted\u categorical\u crossentropy
函数,以供
sequential
使用

同样,我能够以非面向对象的方式完成这项工作。任何帮助都将不胜感激

from tensorflow.keras import Sequential, backend as K

class MyNetwork(): 
        
    def __init__(self, file, n_output=4, n_hidden=20, epochs=3,
                 dropout=0.10, batch_size=64, metrics = ['categorical_accuracy'],
                 optimizer = 'rmsprop', activation = 'softmax'):

    [...] //Other Class attributes
 
    def model(self):
        self.model = self.sequential_model(False)
        self.model.summary()


    def sequential_model(self, val):
        K.clear_session()
        if val == False:
            self.epochs = 3
        regressor = Sequential()
        #regressor.run_eagerly = True
        regressor.add(LSTM(units = self.n_hidden, dropout=self.dropout, return_sequences = True, input_shape = (self.X.shape[1], self.X.shape[2])))
        regressor.add(LSTM(units = self.n_hidden, dropout=self.dropout, return_sequences = True))
        regressor.add(Dense(units = self.n_output, activation=self.activation))
    
        self.weights = np.array([0.025,0.225,0.78,0.020])

        regressor.compile(optimizer = self.optimizer, loss = self.weighted_categorical_crossentropy(self.weights), metrics = [self.metrics])
        regressor.fit(self.X, self.Y*1.0,batch_size=self.batch_size, epochs=self.epochs, verbose=1, validation_data=(self.Xval, self.Yval*1.0))

        return regressor

    def weighted_categorical_crossentropy(self, weights):
        weights = K.variable(weights)
        def loss(y_true, y_pred):
            y_pred /= K.sum(y_pred, axis=-1, keepdims=True)
            y_pred = K.clip(y_pred, K.epsilon(), 1 - K.epsilon())
            loss = y_true * K.log(y_pred) * weights
            loss = -K.sum(loss, -1)
            return loss

上面的代码有几个问题,但最值得注意的是,您没有从
weighted\u category\u crossentropy
返回
loss
。它应该看起来更像:

def-weighted\u-categorical\u交叉熵(自身,权重):
权重=K.变量(权重)
def损失(y_真,y_pred):
y_pred/=K.sum(y_pred,轴=-1,keepdims=True)
y_pred=K.clip(y_pred,K.epsilon(),1-K.epsilon())
损失=y_真*K.log(y_pred)*权重
损失=-K.sum(损失-1)
回波损耗
返回丢失#返回可调用函数!
错误是
ValueError:没有为任何变量提供梯度
,因为loss方法不返回任何内容,所以它不返回任何内容!如果尝试使用
loss=None
拟合方法,则模型将无法计算梯度,因此将抛出相同的精确错误

接下来是在非重复层之前的层中使用的
return\u sequences=True
。这会导致对形状错误的数据调用
密集的
层,这仅适用于重复出现的层。不要那样使用它。
如果您有充分的理由使用
return\u sequences=True
,则必须添加
densite
层,如:

model.add(keras.layers.TimeDistributed(keras.layers.Dense(…))
这将导致
密集
层分别作用于每个时间步上的输出序列。这也意味着您的
y_true
必须具有适当的形状

您定义的自定义损失函数可能还有其他问题,但我无法推断输入/输出形状,因此您必须运行它并添加,看看它是否有效。可能会出现矩阵乘法形状不匹配

最后但并非最不重要的一点是,考虑使用子分类API。它能使您的任何操作更容易编写吗


谢谢你的阅读,我会更新这个答案,一旦我有了这些信息。干杯。

感谢您指出了明显的逻辑错误,并感谢您的全面回复!关于第二点,我的
y\u true
输出是一个
nx时间X类
矩阵(在本例中:
nx20x4
),因此
n\u hidden
的大小是20,而
n\u输出是4个不同的类。隐藏大小为20意味着20个连续的建模周期(按每n的顺序),因此从上面的观点来看,我应该像您提到的那样定义
稠密
层。您的第三点是,必须查看
子分类API
。再次感谢,如果可能的话,我会请你吃一些好的kinkhali。我的荣幸@Josh。很乐意帮忙。还有,关于knikali:如果有一天你决定去乔治亚州,我会非常高兴地邀请你去当地的sakhinkle(提供服务的地方的名称)。干杯。:)这只是时间问题,我妻子来自波蒂,我被指示尽快拜访巴克马罗:Dhey@tornikeo,希望你一切顺利。你们是我所知道的这一领域的专家,若你们能看看我的另一个安问题,我将不胜感激:嘿,乔希,我很好。你好吗?我今天有一些事情要办,一旦我有空,我会尽力回答你的问题。我已经把它放进书签了。