首页 > 解决方案 > 用 Keras 计数

问题描述

我正在尝试做的事情

我目前正在使用带有轻微扭曲的 Keras 制作一个非常简单的序列到序列 LSTM,序列中的早期预测应该比后来的预测少计入损失。我试图做到这一点的方法是计算序列号并乘以该计数的平方根。(我想这样做是因为这个值代表了泊松过程中基于收集的样本数量的不确定性的相对比率。我的网络正在收集数据并尝试根据迄今为止收集的数据估计一个不变的值。)

我是如何做到的

我已经实现了自定义损失函数和自定义层。

损失函数:

def loss_function(y_true, y_pred):
    # extract_output essentially concatenates the first three regression outputs of y
    # into a list representing an [x, y, z] vector, and returns it along with the rest as a tuple
    r, e, n = extract_output(y_true)
    r_h, e_h, n_h = extract_output(y_pred)

    # Hyperperameters
    dir_loss_weight = 10
    dist_loss_weight = 1
    energy_loss_weight = 3

    norm_r = sqrt(dot(r, r))
    norm_r_h = sqrt(dot(r_h, r_h))

    dir_loss = mean_squared_error(r/norm_r, r_h/norm_r_h)
    dist_loss = mean_squared_error(norm_r, norm_r_h)
    energy_loss = mean_squared_error(e, e_h)

    return sqrt(n) * (dir_loss_weight * dir_loss + dist_lost_weight * dist_loss + energy_loss_weight * energy_loss)

自定义层:

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

    def build(self, input_shape):
        self.sequence_number = 0
        pass

    def call(self, x):
        self.sequence_number += 1
        return [self.sequence_number]

    def compute_output_shape(self, input_shape):
        return (1,)

然后我将输入作为连接添加到常规输出中:

seq_num = CounterLayer()(inputs)
outputs = concatenate([out, seq_num])

出了什么问题

我的错误是:

Traceback (most recent call last):
  File "lstm.py", line 119, in <module>
    main()
  File "lstm.py", line 115, in main
    model = create_model()
  File "lstm.py", line 74, in create_model
    seq_num = CounterLayer()(inputs)
  File "/usr/lib/python3.7/site-packages/keras/engine/base_layer.py", line 497, in __call__
    arguments=user_kwargs)
  File "/usr/lib/python3.7/site-packages/keras/engine/base_layer.py", line 565, in _add_inbound_node
    output_tensors[i]._keras_shape = output_shapes[i]
AttributeError: 'int' object has no attribute '_keras_shape'

我假设我的形状错误。但是我不知道怎么做。有谁知道我是否以错误的方式处理这个问题?我应该怎么做才能做到这一点?

进一步的冒险

根据@Mohammad Jafar Mashhadi 的评论,我的call回报需要用keras.backend.variable; 但是,根据他的链接答案,我的方法不起作用,因为call没有像我最初假设的那样多次调用。

如何获得 RNN 的计数器?

为清楚起见,如果给定输入 xi 的 RNN 输出 yi,我试图将 i 作为输出的一部分。

x1 -> RNN -> (y1, 1)
    h1 |
       v
x2 -> RNN -> (y2, 2)
    h2 |
       v
x3 -> RNN -> (y3, 3)
    h3 |
       v
x4 -> RNN -> (y4, 4)

标签: pythontensorflowkeras

解决方案


错误是说,inputs在行seq_num = CounterLayer()(inputs)中,是一个整数。

您不能将整数作为输入传递给图层。您必须通过 keras 张量,并且只能通过 keras 张量。


其次,这不起作用,因为 Keras 以静态图形样式工作。层中的Acall不计算事物,它只构建空张量的图。当您将数据传递给张量时,只有张量会得到更新,整数值不会。当你说它self.sequence_number += 1时,它只会在构建模型时被调用,它不会被一遍又一遍地调用。


我们需要细节

如果您不向我们提供足够的信息,我们将无法真正了解发生了什么,例如:

  • 该模型
  • 摘要
  • 您的输入数据形状
  • 目标数据形状
  • 自定义函数
  • 等等

如果下面的解释是正确的,那么了解模型在摘要中的输出形状和传递给 fit 时的目标数据形状是绝对重要的。


建议的解决方案

如果我理解您所描述的内容,您希望拥有一个递增整数的序列以及序列的时间步长,因此这些数字将用于您的损失函数。

如果这个解释是正确的,你不需要不断更新数字,你只需要创建一个范围张量就可以了。

所以,在你的损失中(除非你向我们提供自定义函数,否则我不明白)你应该创建这个张量:

def custom_loss(y_true, y_pred):
    #use this if you defined your model with static sequence length - input_shape =(length, features)    
    length = K.int_shape(y_pred)[1]

    #use this if you defined your model with dynamic sequence length - input_shape = (None, features)
    length = K.shape(y_pred)[1]

    #this is the sequence vector:
    seq = tf.range(1, length+1)

    #you can get the root with 
    sec = K.sqrt(seq)

    #you reshape to match the shape of the loss, which is probably (batch, length)
    sec = K.reshape(sec, (1,-1)) #shape (1, lenght)

    #compute your loss normally, taking care to reduce the last axis and keep the two first
    loss = ..... #shape (batch, length)

    #multiply the weights by the loss
    return loss * sec

您必须将所有内容作为一个整体张量来处理!您不能将其解释为逐步进行。您必须尽一切努力保持损失中的第一和第二维度。


推荐阅读