ソースコードは次の場所にあります。
https://github.com/maxhodak/keras-molecules
私はそれをいじっていましたが、入力と出力の構造は MxN 行列で、M は SMILES 文字列の最大長 (この場合は 120) で、N は文字セットのサイズです。各行 M はゼロのベクトルですが、位置 M_i の文字が文字 N_j と一致する位置を除きます。出力行列を SMILE にデコードするには、行ごとに進み、文字セットの文字位置に一致させます。
このエンコーディングの問題は、大量のメモリを消費することです。keras イメージ イテレータ アプローチを使用すると、次のことができます。
最初に、すべての笑顔を「スパース」形式にエンコードします。これは、セット内の各笑顔の文字セット位置のリストです。
これで、すべての 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)