RNN变分自动编码器

时间:2016-10-15 09:40:07

标签: keras recurrent-neural-network autoencoder cheminformatics

我正在看...... 分子自动编码器允许我们对化合物https://arxiv.org/pdf/1610.02415.pdf

进行插值和基于梯度的优化

本文采用输入Smiles字符串(分子的文本表示),然后使用变分编码器将其映射到2D潜在空间。

示例微笑己烷-3-醇“CCCC(O)CC”的字符串

在论文中,他们将短字符串填充为带有空格的120个字符。

该论文使用一堆1D卷积网络将字符串编码为微笑字符串的潜在表示

然后使用3门控复现单位GRU然后将潜在空间中的位置映射回微笑字符串。

我在理解本文时遇到的问题是确定输入和输出结构是什么样的。

本文对输入和输出结构有点模糊。从使用1D转换网我怀疑输入是类似于

的矢量化表示
'C' = 1
'O' = 2
'(' = 3
')' =4
' ' = 0 #for padding

#so the hexan-3-ol smiles above would be 

[1,1,1,1,3,2,4,1,1,0...padding to fixed length]

关于输出,论文说

  

RNN解码器的最后一层定义了SMILES字符串中每个位置的所有可能字符的概率分布

因此,对于纸张中使用的最大微笑长度为120,可能有35个微笑字符,这意味着输出是[120x35]数组吗?

向前推进该逻辑它是否表明输入是扁平的[120 * 35]阵列 - 牢记其自动编码器。

我的问题是1dConv,它使用的最大长度为9,如果它是一个扁平的[120 * 35]数组,则不足以覆盖序列中的下一个原子

感谢您的帮助......

2 个答案:

答案 0 :(得分:2)

SMILES的定义比您预期的更复杂,因为它是图形的线性表示。

https://en.wikipedia.org/wiki/Simplified_molecular-input_line-entry_system

简而言之,一个字母表示一个原子,如C =碳,O =氧。该图可以用parens分支,即C(C)C将形成“Y”结构。最后,可以使用表示为数字的闭包创建循环。即“C1CCC1”形成一个正方形(即字母1与另一个字母1相连)。

请注意,此描述并不完整,但应该是一个良好的基础。

如果字符串是有效的微笑字符串,只需将其添加到另一个有效的微笑字符串中,通常会生成另一个有效的字符串。即“C1CC1”+“C1CC1”=> “C1CC1C1CC1”有效。

通常,on可以提取微笑字符串的线性部分并将其“嵌入”另一个字符串中,并形成一个有效的微笑字符串。

我相信自动编码器正在学习,是如何进行这些转换的。替换上述示例中的卤化物(氯,溴,碘)的愚蠢示例可能是:

C1CCC1Cl C1CCC1Br C1CCC1I

自动编码器学习常量部分和可变部分 - 但是在线性字符串空间中。现在这并不完美,如果你在论文中注意到,当探索连续可微的空间时,他们需要找到最接近的有效微笑字符串。

如果你想探索微笑字符串,本文中使用的所有字符串都是使用rdkit生成的:

https://github.com/rdkit/

,在完全披露中,我帮助维护。希望这会有所帮助。

答案 1 :(得分:1)

您可以在此处找到源代码:

https://github.com/maxhodak/keras-molecules

我一直在玩它,输入和输出结构是MxN矩阵,M是SMILES字符串的最大长度(在这种情况下是120),N是字符集的大小。除了位置M_i处的字符与字符N_j匹配的位置之外,每行M是零的向量。要将输出矩阵解码为SMILE,您可以逐行进行匹配并匹配字符集中的字符位置。

此编码的一个问题是它占用了大量内存。使用keras图像迭代器方法,您可以执行以下操作:

首先将所有微笑编码为一个稀疏的' format是集合中每个微笑的字符集位置列表。

现在,您在所有SMILES(字符集)上都有一个已定义的字符集,每个SMILE现在都是一个数字列表,表示字符集中每个字符的位置。然后,您可以开始使用迭代器在使用fit_generator函数训练keras模型时动态执行此操作。

import numpy as np
import threading
import collections

class SmilesIterator(object):
    def __init__(self, X, charset, max_length, batch_size=256, shuffle=False, seed=None):
        self.X = X
        self.charset = charset
        self.max_length = max_length
        self.N = len(X)
        self.batch_size = batch_size
        self.shuffle = shuffle
        self.batch_index = 0
        self.total_batches_seen = 0
        self.lock = threading.Lock()
        self.index_generator = self._flow_index(len(X), batch_size, shuffle, seed)

    def reset(self):
        self.batch_index = 0

    def __iter__(self):
        return self

    def _flow_index(self, N, batch_size, shuffle=False, seed=None):
        self.reset()
        while True:
            if self.batch_index == 0:
            index_array = np.arange(N)
            if shuffle:
                if seed is not None:
                    np.random.seed(seed + total_batches_seen)
                index_array = np.random.permutation(N)
            current_index = (self.batch_index * batch_size) % N
            if N >= current_index + batch_size:
                current_batch_size = batch_size
                self.batch_index += 1
            else:
                current_batch_size = N - current_index
                self.batch_index = 0
            self.total_batches_seen += 1
            yield(index_array[current_index: current_index +    current_batch_size],
            current_index, current_batch_size)

    def next(self):
        with self.lock:
            index_array, current_index, current_batch_size = next(self.index_generator)
        #one-hot encoding is not under lock and can be done in parallel
        #reserve room for the one-hot encoded
        #batch, max_length, charset_length
        batch_x = np.zeros(tuple([current_batch_size, self.max_length, len(self.charset)]))
        for i, j in enumerate(index_array):
            x = self._one_hot(self.X[j])
            batch_x[i] = x
        return (batch_x, batch_x) #fit_generator returns input and target

    def _one_hot(self, sparse_smile):
        ss = []
        counter = 0
        for s in sparse_smile:
            cur = [0] * len(self.charset)
            cur[s] = 1
            ss.append(cur)
            counter += 1
        #handle end of line, make sure space ' ' is first in the charset
        for i in range(counter, len(self.charset)):
            cur = [0] * len(self.charset)
            cur[0] = 1
            ss.append(cur)
        ss = np.array(ss)
        return(ss)