使用 TensorFlow 和 PyTorch 创建模型#

Jupyter Notebook

Jupyter Notebook 中文翻译版查看

Jupyter Notebook 中文翻译版下载

在之前的教程中,我们使用的是 DeepChem 提供的标准模型。这对于许多应用来说都没问题,但是迟早你会希望使用你自己定义的体系结构创建一个全新的模型。DeepChem提供了与 TensorFlow (Keras) 和 PyTorch 的集成,所以你可以在这两个框架的模型中使用它。

实际上,在DeepChem中使用TensorFlow或PyTorch模型时,你可以采用两种不同的方法。这取决于你是想使用TensorFlow/PyTorch APIs 还是DeepChem APIs 来训练和评估你的模型。对于前一种情况,DeepChem的 Dataset 类有一些方法可以方便地将其与其他框架一起使用。 make_tf_dataset() 返回一个遍历数据的 tensorflow.data.Dataset 对象。 make_pytorch_dataset() 返回一个遍历数据的 torch.utils.data.IterableDataset 对象。这让你可以使用 DeepChem 的数据集(datasets)、加载器(loaders)、特征器(featurizers)、变换器(transformers)、拆分器(splitters)等,并轻松将它们集成到你现有的 TensorFlow 或 PyTorch 代码中。

但 DeepChem 还提供了许多其他有用的功能。另一种让你使用这些功能的方法是将你的模型包装在一个 DeepChem 的 Model 对象中。让我们看看如何做到这一点。

KerasModel#

KerasModel 是DeepChem Model 类的子类。它充当 tensorflow.keras.Model 的包装器。让我们看一个使用它的例子。对于本例,我们创建了一个由两个密集层组成的简单顺序(sequential)模型。

import deepchem as dc
import tensorflow as tf

keras_model = tf.keras.Sequential([
    tf.keras.layers.Dense(1000, activation='relu'),
    tf.keras.layers.Dropout(rate=0.5),
    tf.keras.layers.Dense(1)
])
model = dc.models.KerasModel(keras_model, dc.models.losses.L2Loss())

对于本例,我们使用了Keras的 Sequential 类。我们的模型由一个具有 ReLU 激活的密集层、50% 的 dropout 提供正则化和最后一个产生标量输出的层组成。我们还需要指定在训练模型时使用的损失函数,在本例中 \(L^2\) 损失。我们现在可以训练和评估该模型,就像我们对任何其他 DeepChem 模型一样。例如,让我们加载 Delaney 溶解度数据集。我们的模型如何基于分子的扩展连通性指纹 (ECFPs) 来预测分子的溶解性?

tasks, datasets, transformers = dc.molnet.load_delaney(featurizer='ECFP', splitter='random')
train_dataset, valid_dataset, test_dataset = datasets
model.fit(train_dataset, nb_epoch=50)
metric = dc.metrics.Metric(dc.metrics.pearson_r2_score)
print('training set score:', model.evaluate(train_dataset, [metric]))
print('test set score:', model.evaluate(test_dataset, [metric]))

TorchModel#

TorchModel 的工作原理与 KerasModel 类似,只不过它包装了一个 torch.nn.Module。让我们使用PyTorch来创建另一个模型,就像前面的模型一样,并用相同的数据训练它。

import torch

pytorch_model = torch.nn.Sequential(
    torch.nn.Linear(1024, 1000),
    torch.nn.ReLU(),
    torch.nn.Dropout(0.5),
    torch.nn.Linear(1000, 1)
)
model = dc.models.TorchModel(pytorch_model, dc.models.losses.L2Loss())

model.fit(train_dataset, nb_epoch=50)
print('training set score:', model.evaluate(train_dataset, [metric]))
print('test set score:', model.evaluate(test_dataset, [metric]))

损失的计算#

现在让我们看一个更高级的例子。在上述模型中,损失是直接从模型的输出计算出来的。这通常是可以的,但并非总是如此。考虑一个输出概率分布的分类模型。虽然从概率中计算损失是可能的,但从 logits 中计算损失在数值上更稳定。

为此,我们创建一个返回多个输出的模型,包括概率和 logits。 KerasModelTorchModel 让你指定一个“输出类型(output_types)”列表。如果一个特定的输出具有 ‘prediction’ 类型,这意味着它是一个正常的输出,在调用 predict() 时应该返回。如果它有 ‘loss’ 类型,这意味着它应该传递给损失函数,而不是正常的输出。

顺序模型不允许多个输出,因此我们使用子类化样式模型(subclassing style model)。

class ClassificationModel(tf.keras.Model):

    def __init__(self):
        super(ClassificationModel, self).__init__()
        self.dense1 = tf.keras.layers.Dense(1000, activation='relu')
        self.dense2 = tf.keras.layers.Dense(1)

    def call(self, inputs, training=False):
        y = self.dense1(inputs)
        if training:
            y = tf.nn.dropout(y, 0.5)
        logits = self.dense2(y)
        output = tf.nn.sigmoid(logits)
        return output, logits

keras_model = ClassificationModel()
output_types = ['prediction', 'loss']
model = dc.models.KerasModel(keras_model, dc.models.losses.SigmoidCrossEntropy(), output_types=output_types)

我们可以在 BACE 数据集中训练我们的模型。这是一个二元分类任务,试图预测一个分子是否会抑制BACE-1酶。

tasks, datasets, transformers = dc.molnet.load_bace_classification(feturizer='ECFP', splitter='scaffold')
train_dataset, valid_dataset, test_dataset = datasets
model.fit(train_dataset, nb_epoch=100)
metric = dc.metrics.Metric(dc.metrics.roc_auc_score)
print('training set score:', model.evaluate(train_dataset, [metric]))
print('test set score:', model.evaluate(test_dataset, [metric]))

类似地,我们将创建一个自定义的分类器模型类来与 TorchModel 一起使用。理由跟与上面的 KerasModel 相似,自定义模型允许轻松得到第二个密集层的未缩放输出(Tensorflow 中的 logits)。自定义类允许定义如何向前传递;在最终的sigmoid被应用产生预测之前得到 logits。

最后,用一个需要概率和 logits 的 ClassificationModel 的实例与一个损失函数搭配生成一个 TorchModel 的实例进行训练。

class ClassificationModel(torch.nn.Module):

    def __init__(self):
        super(ClassificationModel, self).__init__()
        self.dense1 = torch.nn.Linear(1024, 1000)
        self.dense2 = torch.nn.Linear(1000, 1)

    def forward(self, inputs):
        y = torch.nn.functional.relu( self.dense1(inputs) )
        y = torch.nn.functional.dropout(y, p=0.5, training=self.training)
        logits = self.dense2(y)
        output = torch.sigmoid(logits)
        return output, logits

torch_model = ClassificationModel()
output_types = ['prediction', 'loss']
model = dc.models.TorchModel(torch_model, dc.models.losses.SigmoidCrossEntropy(), output_types=output_types)

我们将使用相同的 BACE 数据集。和以前一样,该模型将尝试进行二元分类任务,试图预测一个分子是否会抑制BACE-1酶。

tasks, datasets, transformers = dc.molnet.load_bace_classification(feturizer='ECFP', splitter='scaffold')
train_dataset, valid_dataset, test_dataset = datasets
model.fit(train_dataset, nb_epoch=100)
metric = dc.metrics.Metric(dc.metrics.roc_auc_score)
print('training set score:', model.evaluate(train_dataset, [metric]))
print('test set score:', model.evaluate(test_dataset, [metric]))

其他功能#

KerasModelTorchModel 有很多其他的功能。下面列一些比较重要的。

  • 训练过程中自动保存检查点(checkpoints)。

  • 将进度记录到控制台(console)或者传送到 TensorBoardWeights & Biases

  • f(输出,标签,权重) 的形式定义损失函数。

  • 使用 ValidationCallback 类来提前停止。

  • 从已训练的模型加载参数。

  • 估计模型输出的不确定性。

  • 通过显著性映射(saliency mapping)识别重要特征。

通过将你自己的模型包装在 KerasModelTorchModel 中,你就可以使用所有这些功能。有关它们的详细信息,请参阅API文档。