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/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)