decimer 2.2.2__tar.gz → 2.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/Efficient_Net_encoder.py +7 -26
- decimer-2.5.0/DECIMER/Predictor_usingCheckpoints.py +275 -0
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/Repack_model.py +71 -21
- decimer-2.5.0/DECIMER/Transformer_decoder.py +210 -0
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/Utils/Create_TFrecord_From_pickles.py +3 -4
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/Utils/Create_image_tokenizer.py +3 -4
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/__init__.py +3 -4
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/config.py +80 -84
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/decimer.py +66 -21
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/efficientnetv2/effnetv2_configs.py +2 -1
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/efficientnetv2/effnetv2_model.py +5 -5
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/efficientnetv2/hparams.py +4 -1
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/efficientnetv2/utils.py +81 -29
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/utils.py +30 -0
- {decimer-2.2.2/decimer.egg-info → decimer-2.5.0}/PKG-INFO +5 -5
- {decimer-2.2.2 → decimer-2.5.0}/README.md +1 -1
- {decimer-2.2.2 → decimer-2.5.0/decimer.egg-info}/PKG-INFO +5 -5
- {decimer-2.2.2 → decimer-2.5.0}/decimer.egg-info/SOURCES.txt +2 -3
- {decimer-2.2.2 → decimer-2.5.0}/decimer.egg-info/requires.txt +3 -1
- {decimer-2.2.2 → decimer-2.5.0}/setup.py +15 -5
- decimer-2.2.2/DECIMER/Predictor_EfficientNet2.py +0 -160
- decimer-2.2.2/DECIMER/Transformer_decoder.py +0 -445
- decimer-2.2.2/tests/test_functions.py +0 -10
- {decimer-2.2.2 → decimer-2.5.0}/DECIMER/DECIMER_EfficinetNetV2_Transfomer_Trainer.py +8 -8
- {decimer-2.2.2 → decimer-2.5.0}/LICENSE +0 -0
- {decimer-2.2.2 → decimer-2.5.0}/decimer.egg-info/dependency_links.txt +0 -0
- {decimer-2.2.2 → decimer-2.5.0}/decimer.egg-info/top_level.txt +0 -0
- {decimer-2.2.2 → decimer-2.5.0}/setup.cfg +0 -0
|
@@ -1,37 +1,25 @@
|
|
|
1
1
|
# EfficientNet-V2 config
|
|
2
2
|
import tensorflow as tf
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import DECIMER.efficientnetv2
|
|
5
|
+
from DECIMER.efficientnetv2 import effnetv2_configs
|
|
6
|
+
from DECIMER.efficientnetv2 import effnetv2_model
|
|
4
7
|
|
|
5
8
|
BATCH_SIZE_DEBUG = 2
|
|
6
|
-
MODEL = "efficientnetv2-
|
|
9
|
+
MODEL = "efficientnetv2-m" # @param
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
# Define encoder
|
|
10
13
|
def get_efficientnetv2_backbone(
|
|
11
|
-
model_name, include_top=False, input_shape=(
|
|
14
|
+
model_name, include_top=False, input_shape=(512, 512, 3), pooling=None, weights=None
|
|
12
15
|
):
|
|
13
|
-
"""Initiate and get the desired Efficient-Net V2 backbone as encoder
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
model_name (str): Name of the Efficient-Net V2 model
|
|
17
|
-
include_top (bool, optional): Defaults to False.
|
|
18
|
-
input_shape (tuple, optional): Image shape. Defaults to (299, 299, 3).
|
|
19
|
-
pooling (int, optional): Max pooling values. Defaults to None.
|
|
20
|
-
weights ( optional): Pretrained weights. Defaults to None.
|
|
21
|
-
|
|
22
|
-
Raises:
|
|
23
|
-
NotImplementedError: At this time we only want to use the raw
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
Efficient Net V2 backbone
|
|
27
|
-
"""
|
|
28
16
|
# Catch unsupported arguments
|
|
29
17
|
if pooling or weights or include_top:
|
|
30
18
|
raise NotImplementedError(
|
|
31
19
|
"\n...At this time we only want to use the raw "
|
|
32
20
|
"(no pretraining), headless, features with no pooling ...\n"
|
|
33
21
|
)
|
|
34
|
-
backbone =
|
|
22
|
+
backbone = effnetv2_model.EffNetV2Model(model_name=model_name)
|
|
35
23
|
backbone(
|
|
36
24
|
tf.ones((BATCH_SIZE_DEBUG, *input_shape)), training=False, features_only=True
|
|
37
25
|
)
|
|
@@ -39,12 +27,6 @@ def get_efficientnetv2_backbone(
|
|
|
39
27
|
|
|
40
28
|
|
|
41
29
|
class Encoder(tf.keras.Model):
|
|
42
|
-
"""Encoder class
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
tf (_type_): tensorflow model module
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
30
|
def __init__(
|
|
49
31
|
self,
|
|
50
32
|
image_embedding_dim,
|
|
@@ -56,7 +38,6 @@ class Encoder(tf.keras.Model):
|
|
|
56
38
|
pretrained_weights=None,
|
|
57
39
|
scale_factor=0,
|
|
58
40
|
):
|
|
59
|
-
|
|
60
41
|
super(Encoder, self).__init__()
|
|
61
42
|
|
|
62
43
|
self.image_embedding_dim = image_embedding_dim
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pickle
|
|
3
|
+
import sys
|
|
4
|
+
from typing import List
|
|
5
|
+
from typing import Tuple
|
|
6
|
+
|
|
7
|
+
import pystow
|
|
8
|
+
import tensorflow as tf
|
|
9
|
+
import Transformer_decoder
|
|
10
|
+
|
|
11
|
+
if int(tf.__version__.split(".")[1]) <= 10:
|
|
12
|
+
import Efficient_Net_encoder
|
|
13
|
+
else:
|
|
14
|
+
raise ImportError("Please use tensorflow 2.10 when working with the checkpoints.")
|
|
15
|
+
import config
|
|
16
|
+
import utils
|
|
17
|
+
|
|
18
|
+
print(tf.__version__)
|
|
19
|
+
|
|
20
|
+
# Set GPU
|
|
21
|
+
os.environ["CUDA_VISIBLE_DEVICES"] = ""
|
|
22
|
+
"""
|
|
23
|
+
gpus = tf.config.experimental.list_physical_devices("GPU")
|
|
24
|
+
for gpu in gpus:
|
|
25
|
+
tf.config.experimental.set_memory_growth(gpu, True)
|
|
26
|
+
"""
|
|
27
|
+
# Set path
|
|
28
|
+
default_path = pystow.join("DECIMER-V2")
|
|
29
|
+
|
|
30
|
+
# model download location
|
|
31
|
+
checkpoint_url = "https://zenodo.org/record/8093783/files/DECIMER_512_checkpoints.zip"
|
|
32
|
+
checkpoint_path = str(default_path) + "/DECIMER_checkpoints/"
|
|
33
|
+
|
|
34
|
+
if not os.path.exists(checkpoint_path):
|
|
35
|
+
config.download_trained_weights(checkpoint_url, default_path)
|
|
36
|
+
|
|
37
|
+
# load assets
|
|
38
|
+
|
|
39
|
+
tokenizer = pickle.load(
|
|
40
|
+
open(
|
|
41
|
+
default_path.as_posix() + "/DECIMER_model/assets/tokenizer_SMILES.pkl",
|
|
42
|
+
"rb",
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
max_length = 302
|
|
47
|
+
|
|
48
|
+
# Image partameters
|
|
49
|
+
IMG_EMB_DIM = (16, 16, 512)
|
|
50
|
+
IMG_EMB_DIM = (IMG_EMB_DIM[0] * IMG_EMB_DIM[1], IMG_EMB_DIM[2])
|
|
51
|
+
IMG_SHAPE = (512, 512, 3)
|
|
52
|
+
PE_INPUT = IMG_EMB_DIM[0]
|
|
53
|
+
IMG_SEQ_LEN, IMG_EMB_DEPTH = IMG_EMB_DIM
|
|
54
|
+
D_MODEL = 512
|
|
55
|
+
|
|
56
|
+
# Network parameters
|
|
57
|
+
N_LAYERS = 6
|
|
58
|
+
D_MODEL = 512
|
|
59
|
+
D_FF = 2048
|
|
60
|
+
N_HEADS = 8
|
|
61
|
+
DROPOUT_RATE = 0.1
|
|
62
|
+
|
|
63
|
+
# Misc
|
|
64
|
+
MAX_LEN = max_length
|
|
65
|
+
VOCAB_LEN = len(tokenizer.word_index)
|
|
66
|
+
PE_OUTPUT = MAX_LEN
|
|
67
|
+
TARGET_V_SIZE = VOCAB_LEN
|
|
68
|
+
REPLICA_BATCH_SIZE = 1
|
|
69
|
+
|
|
70
|
+
# Config Encoder
|
|
71
|
+
PREPROCESSING_FN = tf.keras.applications.efficientnet.preprocess_input
|
|
72
|
+
BB_FN = Efficient_Net_encoder.get_efficientnetv2_backbone
|
|
73
|
+
|
|
74
|
+
# Config Model
|
|
75
|
+
testing_config = config.Config()
|
|
76
|
+
|
|
77
|
+
testing_config.initialize_encoder_config(
|
|
78
|
+
image_embedding_dim=IMG_EMB_DIM,
|
|
79
|
+
preprocessing_fn=PREPROCESSING_FN,
|
|
80
|
+
backbone_fn=BB_FN,
|
|
81
|
+
image_shape=IMG_SHAPE,
|
|
82
|
+
do_permute=IMG_EMB_DIM[1] < IMG_EMB_DIM[0],
|
|
83
|
+
)
|
|
84
|
+
testing_config.initialize_transformer_config(
|
|
85
|
+
vocab_len=VOCAB_LEN,
|
|
86
|
+
max_len=MAX_LEN,
|
|
87
|
+
n_transformer_layers=N_LAYERS,
|
|
88
|
+
transformer_d_dff=D_FF,
|
|
89
|
+
transformer_n_heads=N_HEADS,
|
|
90
|
+
image_embedding_dim=D_MODEL,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# print(f"Encoder config:\n\t -> {testing_config.encoder_config}\n")
|
|
94
|
+
# print(f"Transformer config:\n\t -> {testing_config.transformer_config}\n")
|
|
95
|
+
|
|
96
|
+
# Prepare model
|
|
97
|
+
optimizer, encoder, transformer = config.prepare_models(
|
|
98
|
+
encoder_config=testing_config.encoder_config,
|
|
99
|
+
transformer_config=testing_config.transformer_config,
|
|
100
|
+
replica_batch_size=REPLICA_BATCH_SIZE,
|
|
101
|
+
verbose=0,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Load trained model checkpoint
|
|
105
|
+
ckpt = tf.train.Checkpoint(
|
|
106
|
+
encoder=encoder, transformer=transformer, optimizer=optimizer
|
|
107
|
+
)
|
|
108
|
+
ckpt_manager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=50)
|
|
109
|
+
if ckpt_manager.latest_checkpoint:
|
|
110
|
+
ckpt.restore(tf.train.latest_checkpoint(checkpoint_path))
|
|
111
|
+
start_epoch = int(ckpt_manager.latest_checkpoint.split("-")[-1])
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def main():
|
|
115
|
+
if len(sys.argv) != 2:
|
|
116
|
+
print("Usage: {} $image_path".format(sys.argv[0]))
|
|
117
|
+
else:
|
|
118
|
+
SMILES = predict_SMILES(sys.argv[1])
|
|
119
|
+
print(SMILES)
|
|
120
|
+
SMILES_with_confidence = predict_SMILES_with_confidence(sys.argv[1])
|
|
121
|
+
for tup in SMILES_with_confidence:
|
|
122
|
+
print(tup)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class DECIMER_Predictor(tf.Module):
|
|
126
|
+
def __init__(self, encoder, tokenizer, transformer, max_length):
|
|
127
|
+
self.encoder = encoder
|
|
128
|
+
self.tokenizer = tokenizer
|
|
129
|
+
self.transformer = transformer
|
|
130
|
+
self.max_length = max_length
|
|
131
|
+
|
|
132
|
+
def __call__(self, Decoded_image):
|
|
133
|
+
"""
|
|
134
|
+
Run the DECIMER predictor model when called.
|
|
135
|
+
Usage of predict_SMILES or predict_SMILES_with_confidence is recommended instead
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
Decoded_image (_type_): output of config.decode_image
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Tuple[tf.Tensor, tf.Tensor]: predicted tokens, confidence values
|
|
142
|
+
"""
|
|
143
|
+
assert isinstance(Decoded_image, tf.Tensor)
|
|
144
|
+
|
|
145
|
+
_image_batch = tf.expand_dims(Decoded_image, 0)
|
|
146
|
+
_image_embedding = encoder(_image_batch, training=False)
|
|
147
|
+
|
|
148
|
+
start_token = tf.cast(
|
|
149
|
+
tf.convert_to_tensor([tokenizer.word_index["<start>"]]), tf.int32
|
|
150
|
+
)
|
|
151
|
+
end_token = tf.cast(
|
|
152
|
+
tf.convert_to_tensor([tokenizer.word_index["<end>"]]), tf.int32
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
output_array = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True)
|
|
156
|
+
output_array = output_array.write(0, start_token)
|
|
157
|
+
confidence_array = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True)
|
|
158
|
+
|
|
159
|
+
for t in tf.range(max_length):
|
|
160
|
+
output = tf.transpose(output_array.stack())
|
|
161
|
+
combined_mask = Transformer_decoder.create_masks_decoder(output)
|
|
162
|
+
|
|
163
|
+
# predictions.shape == (batch_size, seq_len, vocab_size)
|
|
164
|
+
prediction_batch = transformer(
|
|
165
|
+
output, _image_embedding, training=False, look_ahead_mask=combined_mask
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# select the last word from the seq_len dimension
|
|
169
|
+
predictions = prediction_batch[:, -1:, :] # (batch_size, 1, vocab_size)
|
|
170
|
+
|
|
171
|
+
predicted_id = tf.cast(tf.argmax(predictions, axis=-1), tf.int32)
|
|
172
|
+
confidence = predictions[-1][-1][int(predicted_id)]
|
|
173
|
+
output_array = output_array.write(t + 1, predicted_id[0])
|
|
174
|
+
confidence_array = confidence_array.write(t + 1, confidence)
|
|
175
|
+
if predicted_id == end_token:
|
|
176
|
+
break
|
|
177
|
+
output = tf.transpose(output_array.stack())
|
|
178
|
+
|
|
179
|
+
return output, confidence_array.stack()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def detokenize_output(predicted_array: tf.Tensor) -> str:
|
|
183
|
+
"""
|
|
184
|
+
This function takes the predicted array of tokens and returns the predicted SMILES
|
|
185
|
+
string.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
str: SMILES string
|
|
192
|
+
"""
|
|
193
|
+
outputs = [tokenizer.index_word[i] for i in predicted_array[0].numpy()]
|
|
194
|
+
prediction = (
|
|
195
|
+
"".join([str(elem) for elem in outputs])
|
|
196
|
+
.replace("<start>", "")
|
|
197
|
+
.replace("<end>", "")
|
|
198
|
+
)
|
|
199
|
+
return prediction
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def detokenize_output_add_confidence(
|
|
203
|
+
predicted_array: tf.Tensor,
|
|
204
|
+
confidence_array: tf.Tensor,
|
|
205
|
+
) -> List[Tuple[str, float]]:
|
|
206
|
+
"""
|
|
207
|
+
This function takes the predicted array of tokens as well as the confidence values
|
|
208
|
+
returned by the Transformer Decoder and returns a list of tuples
|
|
209
|
+
that contain each token of the predicted SMILES string and the confidence
|
|
210
|
+
value.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
str: SMILES string
|
|
217
|
+
"""
|
|
218
|
+
prediction_with_confidence = [
|
|
219
|
+
(
|
|
220
|
+
tokenizer.index_word[predicted_array[0].numpy()[i]],
|
|
221
|
+
confidence_array[i].numpy(),
|
|
222
|
+
)
|
|
223
|
+
for i in range(len(confidence_array))
|
|
224
|
+
]
|
|
225
|
+
decoded_prediction_with_confidence = list(
|
|
226
|
+
[(utils.decoder(tok), conf) for tok, conf in prediction_with_confidence[1:-1]]
|
|
227
|
+
)
|
|
228
|
+
decoded_prediction_with_confidence.append(prediction_with_confidence[-1])
|
|
229
|
+
return decoded_prediction_with_confidence
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Initiate the DECIMER class
|
|
233
|
+
DECIMER = DECIMER_Predictor(encoder, tokenizer, transformer, MAX_LEN)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def predict_SMILES_with_confidence(image_path: str) -> List[Tuple[str, float]]:
|
|
237
|
+
"""
|
|
238
|
+
This function takes an image path (str) and returns a list of tuples
|
|
239
|
+
that contain each token of the predicted SMILES string and the confidence
|
|
240
|
+
level from the last layer of the Transformer decoder.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
image_path (str): Path of chemical structure depiction image
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
(List[Tuple[str, float]]): Tuples that contain the tokens and the confidence
|
|
247
|
+
values of the predicted SMILES
|
|
248
|
+
"""
|
|
249
|
+
decodedImage = config.decode_image(image_path)
|
|
250
|
+
predicted_tokens, confidence_values = DECIMER(tf.constant(decodedImage))
|
|
251
|
+
predicted_SMILES_with_confidence = detokenize_output_add_confidence(
|
|
252
|
+
predicted_tokens, confidence_values
|
|
253
|
+
)
|
|
254
|
+
return predicted_SMILES_with_confidence
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def predict_SMILES(image_path: str) -> str:
|
|
258
|
+
"""
|
|
259
|
+
This function takes an image path (str) and returns the SMILES
|
|
260
|
+
representation of the depicted molecule (str).
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
image_path (str): Path of chemical structure depiction image
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
(str): SMILES representation of the molecule in the input image
|
|
267
|
+
"""
|
|
268
|
+
decodedImage = config.decode_image(image_path)
|
|
269
|
+
predicted_tokens, _ = DECIMER(tf.constant(decodedImage))
|
|
270
|
+
predicted_SMILES = utils.decoder(detokenize_output(predicted_tokens))
|
|
271
|
+
return predicted_SMILES
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
if __name__ == "__main__":
|
|
275
|
+
main()
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import pickle
|
|
3
|
+
from typing import List
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
|
|
2
6
|
import tensorflow as tf
|
|
3
7
|
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import config
|
|
8
|
+
import DECIMER.config as config
|
|
9
|
+
import DECIMER.Efficient_Net_encoder as Efficient_Net_encoder
|
|
10
|
+
import DECIMER.Transformer_decoder as Transformer_decoder
|
|
8
11
|
|
|
9
12
|
print(tf.__version__)
|
|
10
13
|
|
|
@@ -15,19 +18,19 @@ for gpu in gpus:
|
|
|
15
18
|
tf.config.experimental.set_memory_growth(gpu, True)
|
|
16
19
|
|
|
17
20
|
# load assets
|
|
18
|
-
tokenizer = pickle.load(open("
|
|
19
|
-
max_length = pickle.load(open("
|
|
21
|
+
tokenizer = pickle.load(open("tokenizer.pkl", "rb"))
|
|
22
|
+
max_length = pickle.load(open("max_length.pkl", "rb"))
|
|
20
23
|
|
|
21
24
|
# Image parameters
|
|
22
|
-
IMG_EMB_DIM = (
|
|
25
|
+
IMG_EMB_DIM = (16, 16, 512)
|
|
23
26
|
IMG_EMB_DIM = (IMG_EMB_DIM[0] * IMG_EMB_DIM[1], IMG_EMB_DIM[2])
|
|
24
|
-
IMG_SHAPE = (
|
|
27
|
+
IMG_SHAPE = (512, 512, 3)
|
|
25
28
|
PE_INPUT = IMG_EMB_DIM[0]
|
|
26
29
|
IMG_SEQ_LEN, IMG_EMB_DEPTH = IMG_EMB_DIM
|
|
27
|
-
D_MODEL =
|
|
30
|
+
D_MODEL = 512
|
|
28
31
|
|
|
29
32
|
# Network parameters
|
|
30
|
-
N_LAYERS =
|
|
33
|
+
N_LAYERS = 6
|
|
31
34
|
D_MODEL = 512
|
|
32
35
|
D_FF = 2048
|
|
33
36
|
N_HEADS = 8
|
|
@@ -55,14 +58,13 @@ testing_config.initialize_encoder_config(
|
|
|
55
58
|
image_shape=IMG_SHAPE,
|
|
56
59
|
do_permute=IMG_EMB_DIM[1] < IMG_EMB_DIM[0],
|
|
57
60
|
)
|
|
58
|
-
|
|
59
61
|
testing_config.initialize_transformer_config(
|
|
60
62
|
vocab_len=VOCAB_LEN,
|
|
61
63
|
max_len=MAX_LEN,
|
|
62
64
|
n_transformer_layers=N_LAYERS,
|
|
63
65
|
transformer_d_dff=D_FF,
|
|
64
66
|
transformer_n_heads=N_HEADS,
|
|
65
|
-
image_embedding_dim=
|
|
67
|
+
image_embedding_dim=D_MODEL,
|
|
66
68
|
)
|
|
67
69
|
|
|
68
70
|
# Prepare model
|
|
@@ -84,6 +86,56 @@ if ckpt_manager.latest_checkpoint:
|
|
|
84
86
|
start_epoch = int(ckpt_manager.latest_checkpoint.split("-")[-1])
|
|
85
87
|
|
|
86
88
|
|
|
89
|
+
def detokenize_output(predicted_array: tf.Tensor) -> str:
|
|
90
|
+
"""
|
|
91
|
+
This function takes the predicted array of tokens and returns the predicted SMILES
|
|
92
|
+
string.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
str: SMILES string
|
|
99
|
+
"""
|
|
100
|
+
outputs = [tokenizer.index_word[i] for i in predicted_array[0].numpy()]
|
|
101
|
+
prediction = (
|
|
102
|
+
"".join([str(elem) for elem in outputs])
|
|
103
|
+
.replace("<start>", "")
|
|
104
|
+
.replace("<end>", "")
|
|
105
|
+
)
|
|
106
|
+
return prediction
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def detokenize_output_add_confidence(
|
|
110
|
+
predicted_array: tf.Tensor,
|
|
111
|
+
confidence_array: tf.Tensor,
|
|
112
|
+
) -> List[Tuple[str, float]]:
|
|
113
|
+
"""
|
|
114
|
+
This function takes the predicted array of tokens as well as the confidence values
|
|
115
|
+
returned by the Transformer Decoder and returns a list of tuples
|
|
116
|
+
that contain each token of the predicted SMILES string and the confidence
|
|
117
|
+
value.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
str: SMILES string
|
|
124
|
+
"""
|
|
125
|
+
prediction_with_confidence = [
|
|
126
|
+
(
|
|
127
|
+
tokenizer.index_word[predicted_array[0].numpy()[i]],
|
|
128
|
+
confidence_array[i].numpy(),
|
|
129
|
+
)
|
|
130
|
+
for i in range(len(confidence_array))
|
|
131
|
+
]
|
|
132
|
+
decoded_prediction_with_confidence = list(
|
|
133
|
+
[(utils.decoder(tok), conf) for tok, conf in prediction_with_confidence[1:-1]]
|
|
134
|
+
)
|
|
135
|
+
decoded_prediction_with_confidence.append(prediction_with_confidence[-1])
|
|
136
|
+
return decoded_prediction_with_confidence
|
|
137
|
+
|
|
138
|
+
|
|
87
139
|
class DECIMER_Predictor(tf.Module):
|
|
88
140
|
"""This is a class which takes care of inference. It loads the saved checkpoint and the necessary
|
|
89
141
|
tokenizers. The inference begins with the start token (<start>) and ends when the end token(<end>)
|
|
@@ -116,10 +168,7 @@ class DECIMER_Predictor(tf.Module):
|
|
|
116
168
|
Returns:
|
|
117
169
|
output (tf.Tensor[tf.int64]): predicted output as an array.
|
|
118
170
|
"""
|
|
119
|
-
|
|
120
171
|
assert isinstance(Decoded_image, tf.Tensor)
|
|
121
|
-
if len(Decoded_image.shape) == 0:
|
|
122
|
-
Decoded_image = Decoded_image[tf.newaxis]
|
|
123
172
|
|
|
124
173
|
_image_batch = tf.expand_dims(Decoded_image, 0)
|
|
125
174
|
_image_embedding = encoder(_image_batch, training=False)
|
|
@@ -133,28 +182,29 @@ class DECIMER_Predictor(tf.Module):
|
|
|
133
182
|
|
|
134
183
|
output_array = tf.TensorArray(dtype=tf.int32, size=0, dynamic_size=True)
|
|
135
184
|
output_array = output_array.write(0, start_token)
|
|
185
|
+
confidence_array = tf.TensorArray(dtype=tf.float32, size=0, dynamic_size=True)
|
|
136
186
|
|
|
137
187
|
for t in tf.range(max_length):
|
|
138
188
|
output = tf.transpose(output_array.stack())
|
|
139
|
-
combined_mask = Transformer_decoder.
|
|
189
|
+
combined_mask = Transformer_decoder.create_masks_decoder(output)
|
|
140
190
|
|
|
141
191
|
# predictions.shape == (batch_size, seq_len, vocab_size)
|
|
142
|
-
prediction_batch
|
|
143
|
-
|
|
192
|
+
prediction_batch = transformer(
|
|
193
|
+
output, _image_embedding, training=False, look_ahead_mask=combined_mask
|
|
144
194
|
)
|
|
145
195
|
|
|
146
196
|
# select the last word from the seq_len dimension
|
|
147
197
|
predictions = prediction_batch[:, -1:, :] # (batch_size, 1, vocab_size)
|
|
148
198
|
|
|
149
199
|
predicted_id = tf.cast(tf.argmax(predictions, axis=-1), tf.int32)
|
|
150
|
-
|
|
200
|
+
confidence = predictions[0, 0, int(predicted_id[0, 0])]
|
|
151
201
|
output_array = output_array.write(t + 1, predicted_id[0])
|
|
152
|
-
|
|
202
|
+
confidence_array = confidence_array.write(t + 1, confidence)
|
|
153
203
|
if predicted_id == end_token:
|
|
154
204
|
break
|
|
155
205
|
output = tf.transpose(output_array.stack())
|
|
156
206
|
|
|
157
|
-
return output
|
|
207
|
+
return output, confidence_array.stack()
|
|
158
208
|
|
|
159
209
|
|
|
160
210
|
DECIMER = DECIMER_Predictor(encoder, tokenizer, transformer, MAX_LEN)
|