对于一些学校项目,我正在尝试实现一个树卷积,如“树结构上的卷积神经网络用于编程语言处理”Lili Mou,等。
目标
基本上,结果应该是一个神经网络。该网络的样本是二叉树,其节点具有固定长度的特征,如1xN。对我来说,最具挑战性的是树形的自由。这意味着样本树可能具有任意形状的任意数量的节点。一棵左深的树,右深的树,完整的树都是可能的.唯一的限制是它们都应该是二叉树。
用3个加权矩阵W_p, W_l, W_r定义了样本树上的树卷积。这些权重用于树中的每个节点,以生成另一棵具有相同形状但具有不同特性(如1xM )的树(如果权重为NxM形状)。对于每个节点,其特性乘以W_p,其子节点乘以W_l, W_r,因此新树中的节点将包含有关自身及其两个子节点的信息。
最后,在所有的树节点上都有一个动态池层,最后有一个1xM平坦的向量,这样它就可以被输入到一个密集的层中。它的工作方式是,他们将1xM向量的每个条目称为信道。然后,对于每个信道,返回所有节点上的最大值,使其具有1xM向量。
问题
这是对这篇论文的快速解释。现在,正如我在第一段中所说的,问题是这些二叉树的子树的数量是不同的。首先,我尝试使用Keras,但显然它需要层的固定大小的输入。然后发生在我身上,我可以用二叉树的数组实现对每棵树进行固定大小的编码。例如,这意味着节点i的父节点将在2*i和2*i+1上拥有其子节点。当某些地方没有子特性时,如果特性的长度为N,则将N零用于填充。
这要求我获得关于所有树的最大索引的信息,这样我就可以创建一些AxN数组,其中A是这个固定大小模式中使用的最大索引。遗憾的是,输入树可能很深,节点较少,所以为了编码16个节点,我必须创建一个60000xN或6000xN数组,其中大部分都是零填充的,因为树不是很好的平衡。
然后,我切换到一个自定义SGD实现,在那里我定义了密集,树卷积,动态池快速。向前传球真的很容易。然而,在后台,我可以将导数从稠密传播到池到树,然后在树中进行权重更新,而不是对前面的树进行更新。由于Keras/TF在背景中处理分化,因此实际上更容易。
现在,我真的感到在选择解决这个问题的方法之间陷入了困境。显然,Keras/TF有很多功能可用于设计这样的网络。应该有一种有效的方法将这个树结构的数据传递给这些库,这样,对于30个节点,我就不会以创建60000个节点和59970个向量结束了?为大约15个节点生成6000个或60000个节点的想法在这一点上是疯狂的,即使你得到了最好的GPU。
或者我应该在纸上导出导数方程来继续自定义SGD实现?
作为参考,这是Keras的样子,上面提到的树的编码效率很低。
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=(3, input_shape[2], self.output_dim[1]),
initializer='ones',
trainable=True)
super(MyLayer, self).build(input_shape) # Be sure to call this at the end
def call(self, x):
_, tree_size, feature_size = K.int_shape(x)
new_tree = []
for i in range(tree_size // 2):
parent = tf.gather_nd(x, (0,i))
left = tf.gather_nd(x, (0, 2*i + 1) )
right = tf.gather_nd(x, (0, 2*i + 2))
p_l_r = K.expand_dims(K.stack([parent, left, right]), axis = 1)
product = K.sum(K.batch_dot(p_l_r, self.kernel), axis = 0)
new_tree.append(product)
for j in range (tree_size //2, tree_size):
parent = tf.gather_nd(x, (0, j))
parent = K.expand_dims(parent, axis = 0)
product = K.dot(parent, self.kernel[0])
new_tree.append(product)
new_tree = K.stack(new_tree, axis = 1)
return new_tree
def compute_output_shape(self, input_shape):
return (input_shape[0], self.output_dim[0], self.output_dim[1])发布于 2020-08-08 23:06:55
过去,Tensorflow有一个决策树实现。您可以看到它在这里使用的数据结构(变量):https://github.com/tensorflow/tensorflow/blob/v0.10.0rc0/tensorflow/contrib/tensor_forest/python/tensor_forest.py#L155
它显示,您可以通过创建(max_nodes, max_children)形状的2D张量来实现一棵树。(i, j)条目有一个整数,它以相同的张量表示ith节点的jth子节点的索引。因此,具有三个节点的倒V型二叉树将是[[1, 2], [-1, -1], [-1, -1]]。
您可以很容易地创建第二个张量来保存这些特性,其中ith行保存ith节点的特性。然后就可以执行您提到的卷积运算,尽管它需要循环。我不认为有一种方法可以将其矢量化,但这是使用(某种程度上)稀疏表示的代价。
https://stackoverflow.com/questions/58876412
复制相似问题