String Входное выходное представление в RNN Variational autoencoder

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

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

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

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

Бумага закодировала строку, используя стек 1D сверточных сетей, в скрытое представление строки смайлов

Затем он использует 3 Gated рекуррентных блока 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 ответа

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

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

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

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

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

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

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

C1CCC1Cl C1CCC1Br C1CCC1I

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

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

https://github.com/rdkit/

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

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

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

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

Проблема с этой кодировкой заключается в том, что она занимает много памяти. Используя итератор образа 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)
Другие вопросы по тегам