DeepChem教程 6:图卷积
本教程我们学习更多的“图卷积”。处理分子数据有一个最为强大的深度学习工具。原因是分子可以很自然的看作图。注意,这类的标准的化学图形我们从高中就开始用于可视化分子作为图。在本教程的后一部分,我们将更详细的探求这种关系。我们将更深入的理解这些系统是如何工作的。
什么是图卷积?
考虑一下一种常用于处理图像的标准的卷积神经网络。每个像素有一个矢量数值,如红、绿、蓝通道值。数据传递给一系列的卷积层。每一层组合数据自像素及它的邻居以产生新的像素的矢量数据。早期的层检测局部的模式,后期的层检测更大的更抽象的模式。通常卷积层随池化层改变,池化层进行一些局部区域的最大最小化之类的操作。
图卷积相似,但是它们对图操作。它们从图上的节点的矢量数据开始(如,节点代表的原子的化学特征)。卷积和池化层组合信息自相连的节点(如,相结合的原子)以为每个节点产生新的数据矢量。
训练GraphConvModel
让我们来使用MoleculeNet加载Tox21数据集。为了特征化数据以便图卷积神经网络使用,我们设置特征化器选项为'GraphConv'。MoleculeNet的调用返回训练集、验证集、测试集供我们使用。它也返回tasks,即任务名的列表,也返回transformers,数据转换器的列表,用于处理数据集。(通常大部分深度网络都是挑剔的,需要一系列的数据转换过程以保证训练过程的稳定性。)
In [1]:
import deepchem as dc
tasks, datasets, transformers = dc.molnet.load_tox21(featurizer='GraphConv')
train_dataset, valid_dataset, test_dataset = datasets
现在我们用这个数据集来训练图卷积网络。DeepChem有一个GraphConvModel类打包标准的图卷积结构方便用户使用。我们来实例化类的对象并用我们的数据集来训练。
In [2]:
n_tasks = len(tasks)
model = dc.models.GraphConvModel(n_tasks, mode='classification')
model.fit(train_dataset, nb_epoch=50)
Out[2]:
0.28185401916503905
我们来评估一下我们训练的模型的性能。对于这个,我们需要确定一个度量来衡量模型的性能。dc.metrics 已经存贮了一系列的量度。对于这个数据集,使用ROC-AUC是标准,即接受者操作特征曲线下的面积(它测量了精确度和召回之间接妥协)。幸运的是,DeepChem已有ROC-AUC分可用。为了测量模型在这个量度下的性能,我们使用便利的函数 model.evaluate()。
In [3]:
metric = dc.metrics.Metric(dc.metrics.roc_auc_score)
print('Training set score:', model.evaluate(train_dataset, [metric], transformers))
print('Test set score:', model.evaluate(test_dataset, [metric], transformers))
Training set score: {'roc_auc_score': 0.96959686893055}
Test set score: {'roc_auc_score': 0.795793783300876}
结果很好,GraphConvModel非常易于使用。但是这到底发生了什么呢?我们可以自已构建GraphConvModel吗?当然!DeepChem提供了Keras层进行图卷积计算。我们打算使用DeepChem的如下层。
- GraphConv层: 这个层实施图卷积。图卷积以非线性的形式组合每个节点及其相邻节点的特征。这混合了图局部邻区域的信息。
- GraphPool层: 这一层最大化相邻原子的特征向量。你可以认为这一层是2D卷积的max-pooling层只是它对图操作。
- GraphGather: 许多的图卷积网络按图-节操作特征向量。拿个分子来举例,每个节点代表原子,网络可以操作汇总局总原子化学性质的原子的特征向量。但是,应用的最后,我们很可能处理分子水平的特征表示。这一层产生图水平的特征向量,通过组合所有节点水平的特征向量。
除此之外,我们要应用标准的神经网络层如 Dense, BatchNormalization和Softmax层。
In [4]:
from deepchem.models.layers import GraphConv, GraphPool, GraphGather
import tensorflow as tf
import tensorflow.keras.layers as layers
batch_size = 100
class MyGraphConvModel(tf.keras.Model):
def __init__(self):
super(MyGraphConvModel, self).__init__()
self.gc1 = GraphConv(128, activation_fn=tf.nn.tanh)
self.batch_norm1 = layers.BatchNormalization()
self.gp1 = GraphPool()
self.gc2 = GraphConv(128, activation_fn=tf.nn.tanh)
self.batch_norm2 = layers.BatchNormalization()
self.gp2 = GraphPool()
self.dense1 = layers.Dense(256, activation=tf.nn.tanh)
self.batch_norm3 = layers.BatchNormalization()
self.readout = GraphGather(batch_size=batch_size, activation_fn=tf.nn.tanh)
self.dense2 = layers.Dense(n_tasks*2)
self.logits = layers.Reshape((n_tasks, 2))
self.softmax = layers.Softmax()
def call(self, inputs):
gc1_output = self.gc1(inputs)
batch_norm1_output = self.batch_norm1(gc1_output)
gp1_output = self.gp1([batch_norm1_output] + inputs[1:])
gc2_output = self.gc2([gp1_output] + inputs[1:])
batch_norm2_output = self.batch_norm1(gc2_output)
gp2_output = self.gp2([batch_norm2_output] + inputs[1:])
dense1_output = self.dense1(gp2_output)
batch_norm3_output = self.batch_norm3(dense1_output)
readout_output = self.readout([batch_norm3_output] + inputs[1:])
logits_output = self.logits(self.dense2(readout_output))
return self.softmax(logits_output)
现在我们可以更清楚的看到发生了什么。有两个卷积块,每个都由GraphConv组成,接下来是batch normalization,接下来是GraphPool进行最大池化。最后是全链接层,另一个batch normalization,一个GraphGather组合所有不同的节点的数据,最后一个全链接层产生全局输出。
我们来创建DeepChem模型,它打包我们刚才产生的Keras模型。我们也指定损失函数使模型知道最小化的目标。
In [5]:
model = dc.models.KerasModel(MyGraphConvModel(), loss=dc.models.losses.CategoricalCrossEntropy())
这个模型的输出是什么?一个图卷积需要每个分子的完整的描述,包括节点列表,并描述节点的相互键合。事实上,如果我们检查数据集我们发现特征数组包ConvMol类型的Python对像。
In [6]:
test_dataset.X[0]
Out[6]:
<deepchem.feat.mol_graphs.ConvMol at 0x14d0b1650>
模型的期望输入为数值数组,而不是Python对象。我们必须转换ConvMol到GraphConv, GraphPool,和 GraphGather层期望的特殊的数组。幸运的是,ConvMol类包括了进行这种操作的代码,以及组合所有的分子到一个批以产生一个数组集。
下面的代码创建Python生成器以批量的产生输入、标签、权重数据,它们都是Numpy数组。 atom_features为每个原子存贮长度为75的特征向量。其它的输入需要支持TensorFlow的minibatching。 degree_slice 是索引,便于一定水平上定位分子中的原子。Membership确定分子中的原子的关系(原子i属于分子 membership[i])。
deg_adjs 是个包含原子水平分类的相邻性的列表的列表。更多的细节请见代码code.
In [7]:
from deepchem.metrics import to_one_hot
from deepchem.feat.mol_graphs import ConvMol
import numpy as np
def data_generator(dataset, epochs=1):
for ind, (X_b, y_b, w_b, ids_b) in enumerate(dataset.iterbatches(batch_size, epochs,deterministic=False, pad_batches=True)):
multiConvMol = ConvMol.agglomerate_mols(X_b)
inputs = [multiConvMol.get_atom_features(), multiConvMol.deg_slice, np.array(multiConvMol.membership)]
for i in range(1, len(multiConvMol.get_deg_adjacency_lists())):
inputs.append(multiConvMol.get_deg_adjacency_lists()[i])
labels = [to_one_hot(y_b.flatten(), 2).reshape(-1, n_tasks, 2)]
weights = [w_b]
yield (inputs, labels, weights)
我们现在可以用fit_generator(generator)来训练模型,它将使用我们用来训练模型的生成器。
In [8]:
model.fit_generator(data_generator(train_dataset, epochs=50))
Out[8]:
0.21941944122314452
现在我们已经训练了我们的图卷积方法,我们来评估一下性能。我们还要再一次使用定义的生成器来评估模型的性能。
In [9]:
print('Training set score:', model.evaluate_generator(data_generator(train_dataset), [metric], transformers))
print('Test set score:', model.evaluate_generator(data_generator(test_dataset), [metric], transformers))
Training set score: {'roc_auc_score': 0.8425638289185731}
Test set score: {'roc_auc_score': 0.7378436684114341}
成功了!我们构建的模型的表现与GraphConvModel几乎相同。如果你想要构建你自已的模型,你可以按照我们前面的例子去做。我们希望看到令人兴奋的结果。
下载全文请到www.data-vision.net,技术联系电话13712566524