图卷积的正确输入形状

问题描述 投票:0回答:0

我正在尝试在多输入、单输出网络中使用 deepchem 的图卷积层,但我无法理解输入层的工作原理,非常感谢任何可以提供帮助的人。

我有一个图形卷积层(这几乎是 deepchem 的 GraphConv;只有构建方法很重要):

class GraphConv(tf.keras.layers.Layer):
    """Graph Convolutional Layers
    The graph convolution combines per-node feature vectures in a nonlinear fashion with
    the feature vectors for neighboring nodes.  This "blends" information in local neighborhoods of a graph. """

def __init__(self,
             out_channel: int,
             min_deg: int = 0,
             max_deg: int = 10,
             activation_fn: Optional[Callable] = None,
             **kwargs):
    super(GraphConv, self).__init__(**kwargs)
    self.out_channel = out_channel
    self.min_degree = min_deg
    self.max_degree = max_deg
    self.activation_fn = activation_fn

def build(self, input_shape):
    print("Input Shape: ", input_shape)
    #print("Input Type: ", type(input_shape))
    # Generate the nb_affine weights and biases
    num_deg = 2 * self.max_degree + (1 - self.min_degree)
    self.W_list = [
        self.add_weight(name='kernel' + str(k),
                        shape=(int(input_shape[0][-1]), self.out_channel),
                        initializer='glorot_uniform',
                        trainable=True) for k in range(num_deg)
    ]
    self.b_list = [
        self.add_weight(name='bias' + str(k),
                        shape=(self.out_channel,),
                        initializer='zeros',
                        trainable=True) for k in range(num_deg)
    ]
    self.built = True

def get_config(self):
    config = super(GraphConv, self).get_config()
    config['out_channel'] = self.out_channel
    config['min_deg'] = self.min_degree
    config['max_deg'] = self.max_degree
    config['activation_fn'] = self.activation_fn
    return config

def call(self, inputs):

    # Extract atom_features
    atom_features = inputs[0]

    # Extract graph topology
    deg_slice = inputs[1]
    deg_adj_lists = inputs[3:]

    W = iter(self.W_list)
    b = iter(self.b_list)

    # Sum all neighbors using adjacency matrix
    deg_summed = self.sum_neigh(atom_features, deg_adj_lists)

    # Get collection of modified atom features
    new_rel_atoms_collection = (self.max_degree + 1 -
                                self.min_degree) * [None]

    split_features = tf.split(atom_features, deg_slice[:, 1])
    for deg in range(1, self.max_degree + 1):
        # Obtain relevant atoms for this degree
        rel_atoms = deg_summed[deg - 1]

        # Get self atoms
        self_atoms = split_features[deg - self.min_degree]

        # Apply hidden affine to relevant atoms and append
        rel_out = tf.matmul(rel_atoms, next(W)) + next(b)
        self_out = tf.matmul(self_atoms, next(W)) + next(b)
        out = rel_out + self_out

        new_rel_atoms_collection[deg - self.min_degree] = out

    # Determine the min_deg=0 case
    if self.min_degree == 0:
        self_atoms = split_features[0]

        # Only use the self layer
        out = tf.matmul(self_atoms, next(W)) + next(b)

        new_rel_atoms_collection[0] = out

    # Combine all atoms back into the list
    atom_features = tf.concat(axis=0, values=new_rel_atoms_collection)

    if self.activation_fn is not None:
        atom_features = self.activation_fn(atom_features)

    return atom_features

def sum_neigh(self, atoms, deg_adj_lists):
    """Store the summed atoms by degree"""
    deg_summed = self.max_degree * [None]

    # Tensorflow correctly processes empty lists when using concat
    for deg in range(1, self.max_degree + 1):
        gathered_atoms = tf.gather(atoms, deg_adj_lists[deg - 1])
        # Sum along neighbors as well as self, and store
        summed_atoms = tf.reduce_sum(gathered_atoms, 1)
        deg_summed[deg - 1] = summed_atoms

    return deg_summed

完整的输入实际上是一个张量列表,但 weights 维数取决于 input_shape[0][-1],在本例中为 75。 input_shape 中的 print 语句给出的典型 build 为:

Input Shape:  [TensorShape([3926, 75]), TensorShape([11, 2]), TensorShape([3926]), TensorShape([778, 1]), TensorShape([2020, 2]), TensorShape([916, 3]), TensorShape([39, 4]), TensorShape([2, 5]), TensorShape([0, 6]), TensorShape([0, 7]), TensorShape([0, 8]), TensorShape([0, 9]), TensorShape([0, 10])]

然后我在通过子类化模型类制作的 keras 模型中使用这一层: 类测试模型(tf.keras.Model):

  def __init__(self):
    super(testmodel, self).__init__()

    self.gc1 = GraphConv(128, activation_fn=tf.nn.tanh)
    self.dense2 = layers.Dense(1)

    self.relu = layers.ReLU()


  def call(self, inputs):
    gc1_output = self.gc1(inputs)
    dense2_output = self.dense2(gc1_output)
    relu_output = self.relu(dense2_output)

    return relu_output

gcmod_input = tf.keras.Input(shape=75, name="test")
gcmod = testmodel()(gcmod_input)

但这会引发错误:

/content/gcn.py in <listcomp>(.0)
     50         self.W_list = [
     51             self.add_weight(name='kernel' + str(k),
---> 52                             shape=(int(input_shape[0][-1]), self.out_channel),
     53                             initializer='glorot_uniform',
     54                             trainable=True) for k in range(num_deg)

TypeError: 'NoneType' object is not subscriptable
Call arguments received by layer "testmodel_6" (type testmodel):
  • inputs=tf.Tensor(shape=(None, 75), dtype=float32)

这显然与我定义的输入层有关,但我该如何解决它,即在这种情况下定义输入层的正确方法是什么。最终我想在通过函数式 API 定义的多输入模型中使用它。

keras deep-learning
© www.soinside.com 2019 - 2024. All rights reserved.