Представление строкового ввода-вывода в вариационном автоэнкодере RNN

Я просматриваю... Молекулярный автоэнкодер позволяет нам интерполировать и оптимизировать соединения на основе градиента https://arxiv.org/pdf/1610.02415.pdf

Документ берет входную строку Smiles (текстовое представление молекулы), а затем отображает ее с помощью вариационного кодировщика в двумерное скрытое пространство.

Пример строки Smiles для гексан-3-ола «CCCC(O)CC»

В статье они дополняют короткие строки до 120 символов пробелами.

В документе строка закодирована с помощью стека одномерных сверточных сетей в скрытое представление строки смайлов.

Затем он использует рекуррентные единицы GRU с 3 воротами, чтобы затем отображать позиции в скрытом пространстве обратно в строку улыбок.

Проблема, с которой я столкнулся при понимании этой статьи, заключается в том, чтобы определить, как выглядят входные и выходные структуры.

В документе немного расплывчата структура ввода и вывода. Из-за использования одномерных сетей я подозреваю, что входные данные представляют собой векторизованное представление, похожее на

'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]

Спасибо за вашу помощь...


person Chris Arthur    schedule 15.10.2016    source источник


Ответы (2)


Определение SMILES сложнее, чем вы можете ожидать, поскольку это линейное представление графика.

https://en.wikipedia.org/wiki/Упрощенная_молекулярная-input_line-entry_system

Короче говоря, буква обозначает атом, такой как C = углерод, O = кислород. Граф может быть разветвлен скобками, т. е. C(C)C будет образовывать структуру «Y». Наконец, циклы могут быть созданы с замыканиями, представленными в виде чисел. т.е. «C1CCC1» образует квадрат (т. е. буква 1 связана с другой буквой 1).

Обратите внимание, что это описание не является полным, но должно служить хорошим обоснованием.

Если строка является допустимой строкой смайлов, простое добавление ее к другой допустимой строке смайлов чаще всего дает другую допустимую строку. т.е. "C1CC1" + "C1CC1" => "C1CC1C1CC1" допустим.

Часто on может извлечь линейную часть строки смайлов и «встроить» ее в другую, после чего будет сформирована допустимая строка смайлов.

Я считаю, что автокодировщик учится тому, как выполнять эти преобразования. Глупый пример замены галогенидов (хлор, бром, йод), прикрепленный к приведенному выше примеру, может быть таким:

C1CCC1Cl C1CCC1Br C1CCC1I

Автокодер запоминает постоянную часть и переменную часть, но в линейном пространстве строк. Теперь это не идеально, и если вы заметили в статье, при исследовании непрерывно дифференцируемого пространства им нужно найти ближайшую допустимую строку смайлов.

Если вы хотите изучить строки смайлов, все используемые в документе были сгенерированы с помощью rdkit:

https://github.com/rdkit/

который, в полном раскрытии, я помогаю поддерживать. Надеюсь, это поможет.

person Brian Kelley    schedule 03.11.2016

Вы можете найти исходный код здесь:

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

Я играл с этим, и структуры ввода и вывода представляют собой матрицы MxN, где M — максимальная длина строки SMILES (в данном случае 120), а N — размер набора символов. Каждая строка M является вектором нулей, за исключением позиции, в которой символ в позиции M_i соответствует символу N_j. Чтобы декодировать выходную матрицу в SMILE, вы затем идете по строке и сопоставляете позиции символов в вашем наборе символов.

Проблема с этой кодировкой в ​​том, что она занимает много памяти. Используя подход итератора изображений keras, вы можете сделать следующее:

Сначала закодируйте все улыбки в «разреженный» формат, который представляет собой список позиций набора символов для каждой улыбки в вашем наборе.

Теперь у вас есть определенный набор символов для всех SMILES (набор символов), и каждый SMILE теперь представляет собой список чисел, представляющих положение каждого символа в вашем наборе символов. Затем вы можете начать использовать итератор, чтобы делать это на лету, обучая модель keras с помощью функции fit_generator.

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)
person tmarstrand    schedule 22.11.2016