您的代码中存在两套不同的问题,可以分为语法问题和体系结构问题。提出的错误(即
No gradients provided for anyvariable)与语法问题有关,我将在下面主要解决这些问题,但是在此之后,我也会尝试为您提供有关体系结构问题的一些提示。
语法问题的主要原因是有关模型的命名输入和输出。当模型具有多个输入和/或输出层时,用Keras命名的输入和输出最有用。但是,您的模型只有一个输入层和一个输出层。因此,在这里使用命名的输入和输出可能不是很有用,但是如果您是我的决定,我将解释如何正确完成它。
首先,请记住,使用Keras模型时,从任何输入管道(无论是Python生成器还是
tf.data.Dataset)生成的数据都应以元组(即
(input_batch,output_batch)或)的形式提供
(input_batch, output_batch,sample_weights)。而且,正如我说的那样,这是Keras处理输入管道时到处都可以使用的格式,即使我们使用命名的输入和输出作为字典也是如此。
例如,如果我想使用输入/输出命名,并且我的模型有两个名为“ words”和“ importance”的输入层,还有两个名为“ output1”和“
output2”的输出层,则应采用以下格式:
({'words': words_data, 'importance': importance_data}, {'output1': output1_data, 'output2': output2_data})因此,如您在上面看到的,它是一个元组,其中元组的每个元素都是一个字典;第一个元素对应于模型的输入,第二个元素对应于模型的输出。现在,根据这一点,让我们看看应该对您的代码进行哪些修改:
在其中,
sample_generator
我们应该返回一则字典,而不是一个字典。所以:example = tuple([ {'inputs': text_enprer.enpre(inputs) + [EOS_ID]}, {'targets': text_enprer.enpre(targets) + [EOS_ID]},])
在
make_dataset
函数中,的输入参数tf.data.Dataset
应遵守此规定:output_types=({'inputs': tf.int64},{'targets': tf.int64})
padded_shapes=(
{‘inputs’: (None,)},
{‘targets’: (None,)}
)的签名
prepare_example
及其主体也应修改:def prepare_example(ex_inputs: dict, ex_outputs: dict, params: dict):# Make sure targets are one-hot enpredex_outputs['targets'] = tf.one_hot(ex_outputs['targets'], depth=params['vocab_size'])return ex_inputs, ex_outputs
最后,
call
子类化模型的方法:return {'targets': x}还有一件事:
name
构造图层时,我们还应该使用参数将这些名称放在相应的输入和输出图层上(例如Dense(..., name='output')
;但是,由于我们在Model
此处使用子类来定义模型,因此无需这样做。
好了,这些将解决输入/输出问题,并且与梯度有关的错误将消失。但是,如果在应用以上修改后运行代码,则仍然会出现有关不兼容形状的错误。如前所述,您的模型中存在架构问题,下面将简要介绍。
正如您提到的,这应该是一个从序列到序列的模型。因此,输出是单热编码矢量的序列,其中每个矢量的长度等于(目标序列)词汇量。结果,softmax分类器应具有与词汇表大小一样多的单位(注意:注意:在任何模型或问题中,切勿使用只有一个单位的softmax层;这完全是错误的!请思考为什么是错误的!):
self.out_layer = keras.layers.Dense(params['vocab_size'], activation='softmax')
接下来要考虑的事实是,我们正在处理一维序列(即标记/单词序列)。因此,在这里使用2D卷积和2D池化层没有意义。您可以使用它们的一维对应物,也可以将其替换为RNN层之类的东西。结果,该
Lambda层也应被去除。另外,如果要使用卷积和池化,则应适当调整每一层中的过滤器数量以及池大小(即,一个conv过滤器
Conv1D(1,...)可能不是最佳选择,并且池大小为1毫无意义)。
此外,
Dense最后一层之前只有一个单元的那一层可能会严重限制模型的表示能力(即,本质上是模型的瓶颈)。增加其单位数量,或将其删除。
另一件事是,没有理由不对开发集的标签进行一次热编码。相反,它们应该像训练集的标签一样被一键编码。因此,应该完全删除的
training参数,
make_generator或者,如果您有其他用例,则应使用
training=True传递给
make_dataset函数的参数来创建dev数据集。
最后,在完成所有这些更改之后,您的模型可能会开始工作并开始适合数据。但是经过几批之后,您可能会再次遇到形状不兼容的错误。根据需要(通过使用即那是因为你与未知维度生成输入数据,还可以使用轻松填充的方法来垫每批高达
(None,)的
padded_shapes)。要解决此问题,您应该确定固定的输入/输出尺寸(例如,通过考虑输入/输出序列的固定长度),然后调整模型的体系结构或超参数(例如,转换内核大小,转换填充,池大小)
,添加更多层等)以及
padded_shapes相应地争论。即使您希望模型支持可变长度的输入/输出序列,也应该在模型的体系结构,超参数以及自
padded_shapes变量中考虑它
。由于此解决方案取决于您脑海中的任务和所需的设计,并且没有万能的解决方案,因此,我不会对此做进一步评论,而是由您自己解决。但这是一个可行的解决方案(可能根本不是,也可能不是最优),只是为了给您一个想法:
self.out_layer = keras.layers.Dense(params['vocab_size'], activation='softmax')self.model_layers = [ keras.layers.Embedding(params['vocab_size'], params['vocab_size']), keras.layers.Conv1D(32, 4, padding='same'), keras.layers.TimeDistributed(self.out_layer)]# ...padded_shapes=( {'inputs': (10,)}, {'targets': (10,)})


