我正在尝试在多输入、单输出网络中使用 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 定义的多输入模型中使用它。