这个教程我用SeqToSeq模型来产生指纹以分类分子。这是基于以下论文的,尽管一些实施细节不同:Xu et al., "Seq2seq Fingerprint: An Unsupervised Deep Molecular Embedding for Drug Discovery" (https://doi.org/10.1145/3107411.3107424).
用SeqToSeq学习Embeddings
有许多的模型要求输入有固定的长度。由于分子的原子数量和键的数量变化,使得很难应用模型。我们要一种方法来为分子产生固定长度的指纹。为此设计了多种不同的方法,如我们前面用过的Extended-Connectivity Fingerprints (ECFPs)。但是这次我们不是手工设计指纹,我们让SeqToSeq模型学习它的方法来产生指纹。
SeqToSeq模型进行序列到序列的变换。例如,通常用于将文本从一种语言转换为另一种语言。它有两部分,称为“编码器”和“解码器”。编码器是一种循环层。给它饲入输入序列,一次一个,它就产生固定长度的向量叫做"embedding vector"。解码器是另一种循环层堆叠,它进行反向操作:它以embedding vector为输入,得到输出序列。通过适当的输入/输出对训练它,你就可以得到模型进行各种转换。
这种情况下,我们使用描述分子的SMILES字串作为输入序列。我们将要训练一个模型作为autoencoder,使得它能产生与输入序列一样的输出序列。为此,编码器必须产生含有所有来自原始序列的信息的embedding vectors。这正是我们的指纹中想要的,因此那些embedding vectors然后就可以被其它模型当作表示分子的方法。
我们从加载数据开始。我们将使用MUV数据集。它包括74,501个分子在训练集中,9313个分子在验证集中,所以它有足够多的SMILES供我们使用。
In [1]:
import deepchem as dc
tasks, datasets, transformers = dc.molnet.load_muv(split='stratified')
train_dataset, valid_dataset, test_dataset = datasets
train_smiles = train_dataset.ids
valid_smiles = valid_dataset.ids
我们要为我们的SeqToSeq模型确定“字母”,所有可以出现在序列中的标记的列表。(对于输入和输出序列也可能有不同的字母,但是因为我们要训练它作为自编码器,这种情况下它们是相同的。)列出所有训练序列中出现的每个字符的列表。
In [2]:
tokens = set()
for s in train_smiles:
tokens = tokens.union(set(c for c in s))
tokens = sorted(list(tokens))
创建模型并确定使用的优化方法。本例,如果我们不断降低学习速率,学习工作会越来越好。我们用ExponentialDecay,每个epoch将学习速率乘于0.9。
In [3]:
from deepchem.models.optimizers import Adam, ExponentialDecay
max_length = max(len(s) for s in train_smiles)
batch_size = 100
batches_per_epoch = len(train_smiles)/batch_size
model = dc.models.SeqToSeq(tokens,
tokens,
max_length,
encoder_layers=2,
decoder_layers=2,
embedding_dimension=256,
model_dir='fingerprint',
batch_size=batch_size,
learning_rate=ExponentialDecay(0.001, 0.9, batches_per_epoch))
我们来训练它!fit_sequences()函数的输入是生成器能产生输入/输出对。对于好的GPU,要花费几个小时的时间。
In [4]:
def generate_sequences(epochs):
for i in range(epochs):
for s in train_smiles:
yield (s, s)
model.fit_sequences(generate_sequences(40))
让我们来看一下作为自编码器它工作的到底好不好。我们从验证集中给它运行500个分子,看有多少个正好重新生成。
In [5]:
predicted = model.predict_from_sequences(valid_smiles[:500])
count = 0
for s,p in zip(valid_smiles[:500], predicted):
if ''.join(p) == s:
count += 1
print('reproduced', count, 'of 500 validation SMILES strings')
reproduced 161 of 500 validation SMILES strings
现在有编码器作为一种产生分子指纹的方法。我们为训练集和验证集中的所有分子计算embedding vectors,并产生新的数据集作为它们的特征向量。由于数据的数量足够小,所以我们可以把所有的东西存进内存。
In [6]:
import numpy as np
train_embeddings = model.predict_embeddings(train_smiles)
train_embeddings_dataset = dc.data.NumpyDataset(train_embeddings,
train_dataset.y,
train_dataset.w.astype(np.float32),
train_dataset.ids)
valid_embeddings = model.predict_embeddings(valid_smiles)
valid_embeddings_dataset = dc.data.NumpyDataset(valid_embeddings,
valid_dataset.y,
valid_dataset.w.astype(np.float32),
valid_dataset.ids)
对于分类,我们将使用一个简单的全链接网络带一个隐藏层。
In [7]:
classifier = dc.models.MultitaskClassifier(n_tasks=len(tasks),
n_features=256,
layer_sizes=[512])
classifier.fit(train_embeddings_dataset, nb_epoch=10)
Out[7]:
0.0014195525646209716
看看它到底工作得好不好。为训练集和验证集计算ROC AUC。
In [8]:
metric = dc.metrics.Metric(dc.metrics.roc_auc_score, np.mean, mode="classification")
train_score = classifier.evaluate(train_embeddings_dataset, [metric], transformers)
valid_score = classifier.evaluate(valid_embeddings_dataset, [metric], transformers)
print('Training set ROC AUC:', train_score)
print('Validation set ROC AUC:', valid_score)
Training set ROC AUC: {'mean-roc_auc_score': 0.9598792603154332}
Validation set ROC AUC: {'mean-roc_auc_score': 0.7251350862464794}
下载全文请到www.data-vision.net,技术联系电话13712566524