decimer 2.5.0__tar.gz → 2.7.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.5.0 → decimer-2.7.0}/DECIMER/DECIMER_EfficinetNetV2_Transfomer_Trainer.py +17 -19
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/Predictor_usingCheckpoints.py +11 -16
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/Repack_model.py +25 -20
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/Utils/Create_image_tokenizer.py +1 -1
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/__init__.py +29 -32
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/config.py +34 -33
- decimer-2.7.0/DECIMER/decimer.py +171 -0
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/efficientnetv2/effnetv2_configs.py +2 -1
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/efficientnetv2/effnetv2_model.py +1 -1
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/efficientnetv2/hparams.py +4 -1
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/efficientnetv2/utils.py +10 -4
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/utils.py +28 -20
- {decimer-2.5.0/decimer.egg-info → decimer-2.7.0}/PKG-INFO +3 -3
- {decimer-2.5.0 → decimer-2.7.0}/README.md +1 -1
- {decimer-2.5.0 → decimer-2.7.0/decimer.egg-info}/PKG-INFO +3 -3
- {decimer-2.5.0 → decimer-2.7.0}/decimer.egg-info/SOURCES.txt +1 -0
- decimer-2.7.0/decimer.egg-info/entry_points.txt +2 -0
- {decimer-2.5.0 → decimer-2.7.0}/decimer.egg-info/requires.txt +1 -1
- {decimer-2.5.0 → decimer-2.7.0}/setup.py +55 -52
- decimer-2.5.0/DECIMER/decimer.py +0 -162
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/Efficient_Net_encoder.py +0 -0
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/Transformer_decoder.py +0 -0
- {decimer-2.5.0 → decimer-2.7.0}/DECIMER/Utils/Create_TFrecord_From_pickles.py +0 -0
- {decimer-2.5.0 → decimer-2.7.0}/LICENSE +0 -0
- {decimer-2.5.0 → decimer-2.7.0}/decimer.egg-info/dependency_links.txt +0 -0
- {decimer-2.5.0 → decimer-2.7.0}/decimer.egg-info/top_level.txt +0 -0
- {decimer-2.5.0 → decimer-2.7.0}/setup.cfg +0 -0
|
@@ -76,8 +76,8 @@ AUTO = tf.data.experimental.AUTOTUNE
|
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
def decode_image(image_data):
|
|
79
|
-
"""Preprocess the input image for Efficient-Net and
|
|
80
|
-
|
|
79
|
+
"""Preprocess the input image for Efficient-Net and returned the
|
|
80
|
+
preprocessed image.
|
|
81
81
|
|
|
82
82
|
Args:
|
|
83
83
|
image_data (int array): Decoded image in 2D array
|
|
@@ -98,8 +98,8 @@ def decode_image(image_data):
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
def read_tfrecord(example):
|
|
101
|
-
"""Read a tf record file and decodes the image and text data
|
|
102
|
-
|
|
101
|
+
"""Read a tf record file and decodes the image and text data back into
|
|
102
|
+
original form.
|
|
103
103
|
|
|
104
104
|
Args:
|
|
105
105
|
example (tf.record): single entry from tf record file
|
|
@@ -124,7 +124,7 @@ numbers = re.compile(r"(\d+)")
|
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
def numericalSort(value):
|
|
127
|
-
"""Sorts the filenames numerically
|
|
127
|
+
"""Sorts the filenames numerically.
|
|
128
128
|
|
|
129
129
|
Args:
|
|
130
130
|
value (int): numerical value of the file name
|
|
@@ -138,7 +138,7 @@ def numericalSort(value):
|
|
|
138
138
|
|
|
139
139
|
|
|
140
140
|
def get_dataset(batch_size=BATCH_SIZE, buffered_size=BUFFER_SIZE, path=""):
|
|
141
|
-
"""Creates a batch of data rom a given dataset
|
|
141
|
+
"""Creates a batch of data rom a given dataset.
|
|
142
142
|
|
|
143
143
|
Args:
|
|
144
144
|
batch_size (int, optional): number of datapoints per batch. Defaults to BATCH_SIZE.
|
|
@@ -207,8 +207,8 @@ print("Training preparation\n")
|
|
|
207
207
|
|
|
208
208
|
|
|
209
209
|
def prepare_for_training(lr_config, encoder_config, transformer_config, verbose=0):
|
|
210
|
-
"""Preparte the model for training. initiate the learning rate, loss
|
|
211
|
-
and optimizer
|
|
210
|
+
"""Preparte the model for training. initiate the learning rate, loss
|
|
211
|
+
object, metrics and optimizer.
|
|
212
212
|
|
|
213
213
|
Args:
|
|
214
214
|
lr_config (int): values for learning rate configuration
|
|
@@ -239,20 +239,18 @@ def prepare_for_training(lr_config, encoder_config, transformer_config, verbose=
|
|
|
239
239
|
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(
|
|
240
240
|
name="train_accuracy", dtype=tf.float32
|
|
241
241
|
)
|
|
242
|
-
"""
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
validation_accuracy =
|
|
247
|
-
name="validation_accuracy", dtype=tf.float32
|
|
248
|
-
)
|
|
242
|
+
"""validation_loss = tf.keras.metrics.Mean(
|
|
243
|
+
|
|
244
|
+
name="validation_loss", dtype=tf.float32 ) validation_accuracy =
|
|
245
|
+
tf.keras.metrics.SparseCategoricalAccuracy(
|
|
246
|
+
name="validation_accuracy", dtype=tf.float32 )
|
|
249
247
|
"""
|
|
250
248
|
# Declare the learning rate schedule (try this as actual lr schedule and list...)
|
|
251
249
|
lr_scheduler = config.CustomSchedule(
|
|
252
250
|
transformer_config["d_model"], lr_config["warm_steps"]
|
|
253
251
|
)
|
|
254
252
|
|
|
255
|
-
#
|
|
253
|
+
# Instantiate an optimizer
|
|
256
254
|
optimizer = tf.keras.optimizers.Adam(
|
|
257
255
|
lr_scheduler, beta_1=0.9, beta_2=0.98, epsilon=1e-9
|
|
258
256
|
)
|
|
@@ -325,9 +323,9 @@ if ckpt_manager.latest_checkpoint:
|
|
|
325
323
|
start_epoch = int(ckpt_manager.latest_checkpoint.split("-")[-1])
|
|
326
324
|
|
|
327
325
|
|
|
328
|
-
# Main training step
|
|
326
|
+
# Main training step function
|
|
329
327
|
def train_step(image_batch, selfies_batch):
|
|
330
|
-
"""Main training step function
|
|
328
|
+
"""Main training step function.
|
|
331
329
|
|
|
332
330
|
Args:
|
|
333
331
|
image_batch (float array): Input image batch
|
|
@@ -378,7 +376,7 @@ def dist_train_step(image_batch, selfies_batch):
|
|
|
378
376
|
|
|
379
377
|
|
|
380
378
|
"""
|
|
381
|
-
# Main validation step
|
|
379
|
+
# Main validation step function
|
|
382
380
|
def validation_step(image_batch, selfies_batch):
|
|
383
381
|
|
|
384
382
|
selfies_batch_input = selfies_batch[:, :-1]
|
|
@@ -45,7 +45,7 @@ tokenizer = pickle.load(
|
|
|
45
45
|
|
|
46
46
|
max_length = 302
|
|
47
47
|
|
|
48
|
-
# Image
|
|
48
|
+
# Image parameters
|
|
49
49
|
IMG_EMB_DIM = (16, 16, 512)
|
|
50
50
|
IMG_EMB_DIM = (IMG_EMB_DIM[0] * IMG_EMB_DIM[1], IMG_EMB_DIM[2])
|
|
51
51
|
IMG_SHAPE = (512, 512, 3)
|
|
@@ -130,9 +130,8 @@ class DECIMER_Predictor(tf.Module):
|
|
|
130
130
|
self.max_length = max_length
|
|
131
131
|
|
|
132
132
|
def __call__(self, Decoded_image):
|
|
133
|
-
"""
|
|
134
|
-
|
|
135
|
-
Usage of predict_SMILES or predict_SMILES_with_confidence is recommended instead
|
|
133
|
+
"""Run the DECIMER predictor model when called. Usage of predict_SMILES
|
|
134
|
+
or predict_SMILES_with_confidence is recommended instead.
|
|
136
135
|
|
|
137
136
|
Args:
|
|
138
137
|
Decoded_image (_type_): output of config.decode_image
|
|
@@ -180,9 +179,8 @@ class DECIMER_Predictor(tf.Module):
|
|
|
180
179
|
|
|
181
180
|
|
|
182
181
|
def detokenize_output(predicted_array: tf.Tensor) -> str:
|
|
183
|
-
"""
|
|
184
|
-
|
|
185
|
-
string.
|
|
182
|
+
"""This function takes the predicted array of tokens and returns the
|
|
183
|
+
predicted SMILES string.
|
|
186
184
|
|
|
187
185
|
Args:
|
|
188
186
|
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
@@ -203,11 +201,10 @@ def detokenize_output_add_confidence(
|
|
|
203
201
|
predicted_array: tf.Tensor,
|
|
204
202
|
confidence_array: tf.Tensor,
|
|
205
203
|
) -> List[Tuple[str, float]]:
|
|
206
|
-
"""
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
value.
|
|
204
|
+
"""This function takes the predicted array of tokens as well as the
|
|
205
|
+
confidence values returned by the Transformer Decoder and returns a list of
|
|
206
|
+
tuples that contain each token of the predicted SMILES string and the
|
|
207
|
+
confidence value.
|
|
211
208
|
|
|
212
209
|
Args:
|
|
213
210
|
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
@@ -234,8 +231,7 @@ DECIMER = DECIMER_Predictor(encoder, tokenizer, transformer, MAX_LEN)
|
|
|
234
231
|
|
|
235
232
|
|
|
236
233
|
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
|
|
234
|
+
"""This function takes an image path (str) and returns a list of tuples
|
|
239
235
|
that contain each token of the predicted SMILES string and the confidence
|
|
240
236
|
level from the last layer of the Transformer decoder.
|
|
241
237
|
|
|
@@ -255,8 +251,7 @@ def predict_SMILES_with_confidence(image_path: str) -> List[Tuple[str, float]]:
|
|
|
255
251
|
|
|
256
252
|
|
|
257
253
|
def predict_SMILES(image_path: str) -> str:
|
|
258
|
-
"""
|
|
259
|
-
This function takes an image path (str) and returns the SMILES
|
|
254
|
+
"""This function takes an image path (str) and returns the SMILES
|
|
260
255
|
representation of the depicted molecule (str).
|
|
261
256
|
|
|
262
257
|
Args:
|
|
@@ -87,9 +87,8 @@ if ckpt_manager.latest_checkpoint:
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
def detokenize_output(predicted_array: tf.Tensor) -> str:
|
|
90
|
-
"""
|
|
91
|
-
|
|
92
|
-
string.
|
|
90
|
+
"""This function takes the predicted array of tokens and returns the
|
|
91
|
+
predicted SMILES string.
|
|
93
92
|
|
|
94
93
|
Args:
|
|
95
94
|
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
@@ -110,11 +109,10 @@ def detokenize_output_add_confidence(
|
|
|
110
109
|
predicted_array: tf.Tensor,
|
|
111
110
|
confidence_array: tf.Tensor,
|
|
112
111
|
) -> List[Tuple[str, float]]:
|
|
113
|
-
"""
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
value.
|
|
112
|
+
"""This function takes the predicted array of tokens as well as the
|
|
113
|
+
confidence values returned by the Transformer Decoder and returns a list of
|
|
114
|
+
tuples that contain each token of the predicted SMILES string and the
|
|
115
|
+
confidence value.
|
|
118
116
|
|
|
119
117
|
Args:
|
|
120
118
|
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
@@ -137,18 +135,22 @@ def detokenize_output_add_confidence(
|
|
|
137
135
|
|
|
138
136
|
|
|
139
137
|
class DECIMER_Predictor(tf.Module):
|
|
140
|
-
"""This is a class which takes care of inference.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
"""This is a class which takes care of inference.
|
|
139
|
+
|
|
140
|
+
It loads the saved checkpoint and the necessary tokenizers. The
|
|
141
|
+
inference begins with the start token (<start>) and ends when the
|
|
142
|
+
end token(<end>) is met. This class can only work with tf.Tensor
|
|
143
|
+
objects. The strings should get transformed into np.arrays before
|
|
144
|
+
feeding them into this class.
|
|
144
145
|
"""
|
|
145
146
|
|
|
146
147
|
def __init__(self, encoder, tokenizer, transformer, max_length):
|
|
147
|
-
"""Load the tokenizers, the maximum input and output length and the
|
|
148
|
+
"""Load the tokenizers, the maximum input and output length and the
|
|
149
|
+
model.
|
|
148
150
|
|
|
149
151
|
Args:
|
|
150
152
|
encoder (tf.keras.model): The encoder model
|
|
151
|
-
tokenizer (tf.keras.tokenizer): Output tokenizer, defines which
|
|
153
|
+
tokenizer (tf.keras.tokenizer): Output tokenizer, defines which character is assigned to what token
|
|
152
154
|
transformer (tf.keras.model): The transformer model
|
|
153
155
|
max_length (int): Maximum length of a string which can get predicted
|
|
154
156
|
"""
|
|
@@ -158,8 +160,8 @@ class DECIMER_Predictor(tf.Module):
|
|
|
158
160
|
self.max_length = max_length
|
|
159
161
|
|
|
160
162
|
def __call__(self, Decoded_image):
|
|
161
|
-
"""This
|
|
162
|
-
|
|
163
|
+
"""This function takes in the Decoded image as input and makes the
|
|
164
|
+
predicted list of tokens and return the tokens as tf.Tensor array.
|
|
163
165
|
Before feeding the input array we must define start and the end tokens.
|
|
164
166
|
|
|
165
167
|
Args:
|
|
@@ -211,7 +213,9 @@ DECIMER = DECIMER_Predictor(encoder, tokenizer, transformer, MAX_LEN)
|
|
|
211
213
|
|
|
212
214
|
|
|
213
215
|
class ExportDECIMERPredictor(tf.Module):
|
|
214
|
-
"""This class wraps the inference class into a module into tf.Module sub-
|
|
216
|
+
"""This class wraps the inference class into a module into tf.Module sub-
|
|
217
|
+
class, with a tf.function on the __call__ method.
|
|
218
|
+
|
|
215
219
|
So we could export the model as a tf.saved_model.
|
|
216
220
|
"""
|
|
217
221
|
|
|
@@ -221,9 +225,10 @@ class ExportDECIMERPredictor(tf.Module):
|
|
|
221
225
|
|
|
222
226
|
@tf.function
|
|
223
227
|
def __call__(self, Decoded_Image):
|
|
224
|
-
"""This
|
|
225
|
-
In the tf.function only the output sentence is returned.
|
|
226
|
-
|
|
228
|
+
"""This function calls the __call__function from the translator class.
|
|
229
|
+
In the tf.function only the output sentence is returned. Thanks to the
|
|
230
|
+
non-strict execution in tf.function any unnecessary values are never
|
|
231
|
+
computed.
|
|
227
232
|
|
|
228
233
|
Args:
|
|
229
234
|
sentence (tf.Tensor[tf.int32]): Input array in tf.Easgertensor format.
|
|
@@ -1,32 +1,29 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
"""
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
from .decimer import predict_SMILES, predict_SMILES_with_confidence
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""DECIMER V2.6.0 Python Package. ============================
|
|
3
|
+
|
|
4
|
+
This repository contains DECIMER-V2,Deep lEarning for Chemical ImagE Recognition) project
|
|
5
|
+
was launched to address the OCSR problem with the latest computational intelligence methods
|
|
6
|
+
to provide an automated open-source software solution.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Typical usage example:
|
|
10
|
+
|
|
11
|
+
from decimer import predict_SMILES
|
|
12
|
+
|
|
13
|
+
# Chemical depiction to SMILES translation
|
|
14
|
+
image_path = "path/to/imagefile"
|
|
15
|
+
SMILES = predict_SMILES(image_path)
|
|
16
|
+
print(SMILES)
|
|
17
|
+
|
|
18
|
+
For comments, bug reports or feature ideas,
|
|
19
|
+
please raise a issue on the Github repository.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
__version__ = "2.6.0"
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"DECIMER",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
from .decimer import predict_SMILES
|
|
@@ -20,9 +20,10 @@ TARGET_DTYPE = tf.float32
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def resize_byratio(image):
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
"""This function takes a Pillow Image object and will resize the image by
|
|
24
|
+
512 x 512 To upscale or to downscale the image LANCZOS resampling method is
|
|
25
|
+
used.
|
|
26
|
+
|
|
26
27
|
with the new pillow version the antialias is turned on when using LANCZOS.
|
|
27
28
|
Args: PIL.Image
|
|
28
29
|
Returns: PIL.Image
|
|
@@ -35,10 +36,10 @@ def resize_byratio(image):
|
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
def central_square_image(image):
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
so that the image has a square shape with the width/height of the longest side
|
|
39
|
+
"""This function takes a Pillow Image object and will add white padding so
|
|
40
|
+
that the image has a square shape with the width/height of the longest side
|
|
41
41
|
of the original image.
|
|
42
|
+
|
|
42
43
|
Args: PIL.Image
|
|
43
44
|
Returns: PIL.Image
|
|
44
45
|
"""
|
|
@@ -59,6 +60,7 @@ def central_square_image(image):
|
|
|
59
60
|
def delete_empty_borders(image):
|
|
60
61
|
"""This function takes a Pillow Image object, converts it to grayscale and
|
|
61
62
|
deletes white space at the borders.
|
|
63
|
+
|
|
62
64
|
Args: PIL.Image
|
|
63
65
|
Returns: PIL.Image
|
|
64
66
|
"""
|
|
@@ -132,8 +134,8 @@ def get_bnw_image(image):
|
|
|
132
134
|
|
|
133
135
|
|
|
134
136
|
def increase_contrast(image):
|
|
135
|
-
"""
|
|
136
|
-
|
|
137
|
+
"""This function increases the contrast of an image input.
|
|
138
|
+
|
|
137
139
|
Args: PIL.Image
|
|
138
140
|
Returns: PIL.Image
|
|
139
141
|
"""
|
|
@@ -153,8 +155,9 @@ def increase_contrast(image):
|
|
|
153
155
|
|
|
154
156
|
|
|
155
157
|
def get_resize(image):
|
|
156
|
-
"""
|
|
157
|
-
|
|
158
|
+
"""This function used to decide how to resize a given image without losing
|
|
159
|
+
much information.
|
|
160
|
+
|
|
158
161
|
Args: PIL.Image
|
|
159
162
|
Returns: PIL.Image
|
|
160
163
|
"""
|
|
@@ -172,8 +175,8 @@ def get_resize(image):
|
|
|
172
175
|
|
|
173
176
|
|
|
174
177
|
def increase_brightness(image):
|
|
175
|
-
"""
|
|
176
|
-
|
|
178
|
+
"""This function adjusts the brightness of the given image.
|
|
179
|
+
|
|
177
180
|
Args: PIL.Image
|
|
178
181
|
Returns: PIL.Image
|
|
179
182
|
"""
|
|
@@ -183,8 +186,8 @@ def increase_brightness(image):
|
|
|
183
186
|
|
|
184
187
|
|
|
185
188
|
def decode_image(image_path: str):
|
|
186
|
-
"""
|
|
187
|
-
|
|
189
|
+
"""Loads an image and preprocesses the input image in several steps to get
|
|
190
|
+
the image ready for DECIMER input.
|
|
188
191
|
|
|
189
192
|
Args:
|
|
190
193
|
image_path (str): path of input image
|
|
@@ -207,9 +210,7 @@ def decode_image(image_path: str):
|
|
|
207
210
|
|
|
208
211
|
|
|
209
212
|
class Config:
|
|
210
|
-
"""
|
|
211
|
-
Configuration class
|
|
212
|
-
"""
|
|
213
|
+
"""Configuration class."""
|
|
213
214
|
|
|
214
215
|
def __init__(
|
|
215
216
|
self,
|
|
@@ -227,11 +228,11 @@ class Config:
|
|
|
227
228
|
do_permute=False,
|
|
228
229
|
pretrained_weights=None,
|
|
229
230
|
):
|
|
230
|
-
"""This functions initializes the Efficient-Net V2 encoder with user
|
|
231
|
-
configurations.
|
|
231
|
+
"""This functions initializes the Efficient-Net V2 encoder with user
|
|
232
|
+
defined configurations.
|
|
232
233
|
|
|
233
234
|
Args:
|
|
234
|
-
image_embedding_dim (int): Embedding
|
|
235
|
+
image_embedding_dim (int): Embedding dimension of the input image
|
|
235
236
|
preprocessing_fn (method): Efficient Net preprocessing function for input image
|
|
236
237
|
backbone_fn (method): Calls Efficient-Net V2 as backbone for encoder
|
|
237
238
|
image_shape (int): Shape of the input image
|
|
@@ -257,9 +258,8 @@ class Config:
|
|
|
257
258
|
image_embedding_dim,
|
|
258
259
|
rate=0.1,
|
|
259
260
|
):
|
|
260
|
-
"""This functions initializes the Transformer model as decoder with
|
|
261
|
-
configurations.
|
|
262
|
-
|
|
261
|
+
"""This functions initializes the Transformer model as decoder with
|
|
262
|
+
user defined configurations.
|
|
263
263
|
|
|
264
264
|
Args:
|
|
265
265
|
vocab_len (int): Total number of words in the input vocabulary
|
|
@@ -267,7 +267,7 @@ class Config:
|
|
|
267
267
|
n_transformer_layers (int): Number of layers present in the transformer model
|
|
268
268
|
transformer_d_dff (int): Transformer feed forward upwards projection size
|
|
269
269
|
transformer_n_heads (int): Number of heads present in the transformer model
|
|
270
|
-
image_embedding_dim (int): Total number of dimension the image gets
|
|
270
|
+
image_embedding_dim (int): Total number of dimension the image gets embedded
|
|
271
271
|
dropout_rate (float, optional): Fraction of the input units to drop. Defaults to 0.1.
|
|
272
272
|
"""
|
|
273
273
|
self.transformer_config = dict(
|
|
@@ -281,7 +281,7 @@ class Config:
|
|
|
281
281
|
)
|
|
282
282
|
|
|
283
283
|
def initialize_lr_config(self, warm_steps, n_epochs):
|
|
284
|
-
"""This function sets the configuration to initialize learning rate
|
|
284
|
+
"""This function sets the configuration to initialize learning rate.
|
|
285
285
|
|
|
286
286
|
Args:
|
|
287
287
|
warm_steps (int): Number of steps The learning rate is increased
|
|
@@ -294,7 +294,7 @@ class Config:
|
|
|
294
294
|
|
|
295
295
|
|
|
296
296
|
class CustomSchedule(tf.keras.optimizers.schedules.LearningRateSchedule):
|
|
297
|
-
"""Custom schedule for learning rate used during training
|
|
297
|
+
"""Custom schedule for learning rate used during training.
|
|
298
298
|
|
|
299
299
|
Args:
|
|
300
300
|
tf (_type_): keras learning rate schedule
|
|
@@ -316,9 +316,9 @@ class CustomSchedule(tf.keras.optimizers.schedules.LearningRateSchedule):
|
|
|
316
316
|
|
|
317
317
|
|
|
318
318
|
def prepare_models(encoder_config, transformer_config, replica_batch_size, verbose=0):
|
|
319
|
-
"""This function is used to initiate the Encoder and the Transformer with
|
|
320
|
-
configs set by the user. After initiating the models this
|
|
321
|
-
and the optimizer.
|
|
319
|
+
"""This function is used to initiate the Encoder and the Transformer with
|
|
320
|
+
appropriate configs set by the user. After initiating the models this
|
|
321
|
+
function returns the Encoder,Transformer and the optimizer.
|
|
322
322
|
|
|
323
323
|
Args:
|
|
324
324
|
encoder_config ([type]): Encoder configuration set by user in the config class.
|
|
@@ -330,7 +330,7 @@ def prepare_models(encoder_config, transformer_config, replica_batch_size, verbo
|
|
|
330
330
|
[type]: Optimizer, Encoder model and the Transformer
|
|
331
331
|
"""
|
|
332
332
|
|
|
333
|
-
#
|
|
333
|
+
# Instantiate an optimizer
|
|
334
334
|
optimizer = tf.keras.optimizers.Adam(learning_rate=0.00051)
|
|
335
335
|
|
|
336
336
|
# Instantiate the encoder model
|
|
@@ -353,9 +353,10 @@ def prepare_models(encoder_config, transformer_config, replica_batch_size, verbo
|
|
|
353
353
|
|
|
354
354
|
# Downloads the model and unzips the file downloaded, if the model is not present on the working directory.
|
|
355
355
|
def download_trained_weights(model_url: str, model_path: str, verbose=1):
|
|
356
|
-
"""This function downloads the trained models and tokenizers to a default
|
|
357
|
-
After downloading the zipped file the function unzips the file
|
|
358
|
-
If the model exists on the default location this function
|
|
356
|
+
"""This function downloads the trained models and tokenizers to a default
|
|
357
|
+
location. After downloading the zipped file the function unzips the file
|
|
358
|
+
automatically. If the model exists on the default location this function
|
|
359
|
+
will not work.
|
|
359
360
|
|
|
360
361
|
Args:
|
|
361
362
|
model_url (str): trained model url for downloading.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import pickle
|
|
4
|
+
import sys
|
|
5
|
+
from typing import List
|
|
6
|
+
from typing import Tuple
|
|
7
|
+
|
|
8
|
+
import pystow
|
|
9
|
+
import tensorflow as tf
|
|
10
|
+
|
|
11
|
+
import DECIMER.config as config
|
|
12
|
+
import DECIMER.utils as utils
|
|
13
|
+
|
|
14
|
+
# Silence tensorflow model loading warnings.
|
|
15
|
+
logging.getLogger("absl").setLevel("ERROR")
|
|
16
|
+
|
|
17
|
+
# Silence tensorflow errors - not recommended if your model is not working properly.
|
|
18
|
+
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
|
|
19
|
+
|
|
20
|
+
# Set the absolute path
|
|
21
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
22
|
+
|
|
23
|
+
# Set model to run on default GPU and allow memory to grow as much as needed.
|
|
24
|
+
# This allows us to run multiple instances of inference in the same GPU.
|
|
25
|
+
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
|
|
26
|
+
gpus = tf.config.experimental.list_physical_devices("GPU")
|
|
27
|
+
for gpu in gpus:
|
|
28
|
+
tf.config.experimental.set_memory_growth(gpu, True)
|
|
29
|
+
|
|
30
|
+
# Set path
|
|
31
|
+
default_path = pystow.join("DECIMER-V2")
|
|
32
|
+
|
|
33
|
+
model_urls = {
|
|
34
|
+
"DECIMER": "https://zenodo.org/record/8300489/files/models.zip",
|
|
35
|
+
"DECIMER_HandDrawn": "https://zenodo.org/records/10781330/files/DECIMER_HandDrawn_model.zip",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_models(model_urls: dict):
|
|
40
|
+
"""Download and load models from the provided URLs.
|
|
41
|
+
|
|
42
|
+
This function downloads models from the provided URLs to a default location,
|
|
43
|
+
then loads tokenizers and TensorFlow saved models.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
model_urls (dict): A dictionary containing model names as keys and their corresponding URLs as values.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
tuple: A tuple containing loaded tokenizer and TensorFlow saved models.
|
|
50
|
+
- tokenizer (object): Tokenizer for DECIMER model.
|
|
51
|
+
- DECIMER_V2 (tf.saved_model): TensorFlow saved model for DECIMER.
|
|
52
|
+
- DECIMER_Hand_drawn (tf.saved_model): TensorFlow saved model for DECIMER HandDrawn.
|
|
53
|
+
"""
|
|
54
|
+
# Download models to a default location
|
|
55
|
+
model_paths = utils.ensure_models(default_path=default_path, model_urls=model_urls)
|
|
56
|
+
|
|
57
|
+
# Load tokenizers
|
|
58
|
+
tokenizer_path = os.path.join(
|
|
59
|
+
model_paths["DECIMER"], "assets", "tokenizer_SMILES.pkl"
|
|
60
|
+
)
|
|
61
|
+
tokenizer = pickle.load(open(tokenizer_path, "rb"))
|
|
62
|
+
|
|
63
|
+
# Load DECIMER models
|
|
64
|
+
DECIMER_V2 = tf.saved_model.load(model_paths["DECIMER"])
|
|
65
|
+
DECIMER_Hand_drawn = tf.saved_model.load(model_paths["DECIMER_HandDrawn"])
|
|
66
|
+
|
|
67
|
+
return tokenizer, DECIMER_V2, DECIMER_Hand_drawn
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
tokenizer, DECIMER_V2, DECIMER_Hand_drawn = get_models(model_urls)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def detokenize_output(predicted_array: int) -> str:
|
|
74
|
+
"""This function takes the predicted tokens from the DECIMER model and
|
|
75
|
+
returns the decoded SMILES string.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
predicted_array (int): Predicted tokens from DECIMER
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
(str): SMILES representation of the molecule
|
|
82
|
+
"""
|
|
83
|
+
outputs = [tokenizer.index_word[i] for i in predicted_array[0].numpy()]
|
|
84
|
+
prediction = (
|
|
85
|
+
"".join([str(elem) for elem in outputs])
|
|
86
|
+
.replace("<start>", "")
|
|
87
|
+
.replace("<end>", "")
|
|
88
|
+
)
|
|
89
|
+
return prediction
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def detokenize_output_add_confidence(
|
|
93
|
+
predicted_array: tf.Tensor,
|
|
94
|
+
confidence_array: tf.Tensor,
|
|
95
|
+
) -> List[Tuple[str, float]]:
|
|
96
|
+
"""This function takes the predicted array of tokens as well as the
|
|
97
|
+
confidence values returned by the Transformer Decoder and returns a list of
|
|
98
|
+
tuples that contain each token of the predicted SMILES string and the
|
|
99
|
+
confidence value.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
str: SMILES string
|
|
106
|
+
"""
|
|
107
|
+
prediction_with_confidence = [
|
|
108
|
+
(
|
|
109
|
+
tokenizer.index_word[predicted_array[0].numpy()[i]],
|
|
110
|
+
confidence_array[i].numpy(),
|
|
111
|
+
)
|
|
112
|
+
for i in range(len(confidence_array))
|
|
113
|
+
]
|
|
114
|
+
# remove start and end tokens
|
|
115
|
+
prediction_with_confidence_ = prediction_with_confidence[1:-1]
|
|
116
|
+
|
|
117
|
+
decoded_prediction_with_confidence = list(
|
|
118
|
+
[(utils.decoder(tok), conf) for tok, conf in prediction_with_confidence_]
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return decoded_prediction_with_confidence
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def predict_SMILES(
|
|
125
|
+
image_path: str, confidence: bool = False, hand_drawn: bool = False
|
|
126
|
+
) -> str:
|
|
127
|
+
"""Predicts SMILES representation of a molecule depicted in the given image.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
image_path (str): Path of chemical structure depiction image
|
|
131
|
+
confidence (bool): Flag to indicate whether to return confidence values along with SMILES prediction
|
|
132
|
+
hand_drawn (bool): Flag to indicate whether the molecule in the image is hand-drawn
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
str: SMILES representation of the molecule in the input image, optionally with confidence values
|
|
136
|
+
"""
|
|
137
|
+
chemical_structure = config.decode_image(image_path)
|
|
138
|
+
|
|
139
|
+
model = DECIMER_Hand_drawn if hand_drawn else DECIMER_V2
|
|
140
|
+
predicted_tokens, confidence_values = model(tf.constant(chemical_structure))
|
|
141
|
+
|
|
142
|
+
predicted_SMILES = utils.decoder(detokenize_output(predicted_tokens))
|
|
143
|
+
|
|
144
|
+
if confidence:
|
|
145
|
+
predicted_SMILES_with_confidence = detokenize_output_add_confidence(
|
|
146
|
+
predicted_tokens, confidence_values
|
|
147
|
+
)
|
|
148
|
+
return predicted_SMILES, predicted_SMILES_with_confidence
|
|
149
|
+
|
|
150
|
+
return predicted_SMILES
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def main():
|
|
154
|
+
"""This function take the path of the image as user input and returns the
|
|
155
|
+
predicted SMILES as output in CLI.
|
|
156
|
+
|
|
157
|
+
Agrs:
|
|
158
|
+
str: image_path
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
str: predicted SMILES
|
|
162
|
+
"""
|
|
163
|
+
if len(sys.argv) != 2:
|
|
164
|
+
print("Usage: {} $image_path".format(sys.argv[0]))
|
|
165
|
+
else:
|
|
166
|
+
SMILES = predict_SMILES(sys.argv[1])
|
|
167
|
+
print(SMILES)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
main()
|
|
@@ -63,7 +63,8 @@ class BlockDecoder(object):
|
|
|
63
63
|
return "_".join(args)
|
|
64
64
|
|
|
65
65
|
def decode(self, string_list):
|
|
66
|
-
"""Decodes a list of string notations to specify blocks inside the
|
|
66
|
+
"""Decodes a list of string notations to specify blocks inside the
|
|
67
|
+
network.
|
|
67
68
|
|
|
68
69
|
Args:
|
|
69
70
|
string_list: a list of strings, each string is a notation of block.
|
|
@@ -158,7 +158,10 @@ class Config(dict):
|
|
|
158
158
|
yaml.dump(self.as_dict(), f, default_flow_style=False)
|
|
159
159
|
|
|
160
160
|
def parse_from_str(self, config_str: Text) -> Dict[Any, Any]:
|
|
161
|
-
"""Parse a string like 'x.y=1,x.z=2' to nested dict {x: {y: 1, z:
|
|
161
|
+
"""Parse a string like 'x.y=1,x.z=2' to nested dict {x: {y: 1, z:
|
|
162
|
+
|
|
163
|
+
2}}.
|
|
164
|
+
"""
|
|
162
165
|
if not config_str:
|
|
163
166
|
return {}
|
|
164
167
|
config_dict = {}
|
|
@@ -192,7 +192,8 @@ class TpuBatchNormalization(tf.keras.layers.BatchNormalization):
|
|
|
192
192
|
super().__init__(fused=fused, **kwargs)
|
|
193
193
|
|
|
194
194
|
def _moments(self, inputs, reduction_axes, keep_dims):
|
|
195
|
-
"""Compute the mean and variance: it overrides the original
|
|
195
|
+
"""Compute the mean and variance: it overrides the original
|
|
196
|
+
_moments."""
|
|
196
197
|
shard_mean, shard_variance = super()._moments(
|
|
197
198
|
inputs, reduction_axes, keep_dims=keep_dims
|
|
198
199
|
)
|
|
@@ -222,7 +223,8 @@ class TpuBatchNormalization(tf.keras.layers.BatchNormalization):
|
|
|
222
223
|
|
|
223
224
|
|
|
224
225
|
class BatchNormalization(tf.keras.layers.BatchNormalization):
|
|
225
|
-
"""Fixed default name of BatchNormalization to match
|
|
226
|
+
"""Fixed default name of BatchNormalization to match
|
|
227
|
+
TpuBatchNormalization."""
|
|
226
228
|
|
|
227
229
|
def __init__(self, **kwargs):
|
|
228
230
|
if not kwargs.get("name", None):
|
|
@@ -371,7 +373,10 @@ def get_tpu_host_call(global_step, model_dir, iterations_per_loop):
|
|
|
371
373
|
return None # No summaries to write.
|
|
372
374
|
|
|
373
375
|
def host_call_fn(global_step, *args):
|
|
374
|
-
"""Training host call.
|
|
376
|
+
"""Training host call.
|
|
377
|
+
|
|
378
|
+
Creates summaries for training metrics.
|
|
379
|
+
"""
|
|
375
380
|
gs = global_step[0]
|
|
376
381
|
with tf.summary.create_file_writer(
|
|
377
382
|
model_dir, max_queue=iterations_per_loop
|
|
@@ -594,7 +599,8 @@ def restore_tf2_ckpt(model, ckpt_path_or_file, skip_mismatch=True, exclude_layer
|
|
|
594
599
|
|
|
595
600
|
|
|
596
601
|
class ReuableBackupAndRestore(tf.keras.callbacks.experimental.BackupAndRestore):
|
|
597
|
-
"""A BackupAndRestore callback that can be used across multiple
|
|
602
|
+
"""A BackupAndRestore callback that can be used across multiple
|
|
603
|
+
model.fit()s."""
|
|
598
604
|
|
|
599
605
|
def on_train_end(self, logs=None):
|
|
600
606
|
# don't delete the backup, so it can be used for future model.fit()s
|
|
@@ -63,27 +63,35 @@ def decoder(predictions):
|
|
|
63
63
|
return modified
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
model_url: str = "https://zenodo.org/record/8300489/files/models.zip",
|
|
69
|
-
):
|
|
70
|
-
"""Function to ensure model is present locally
|
|
66
|
+
def ensure_models(default_path: str, model_urls: dict) -> dict:
|
|
67
|
+
"""Function to ensure models are present locally.
|
|
71
68
|
|
|
72
|
-
Convenient function to ensure model
|
|
69
|
+
Convenient function to ensure model downloads before usage
|
|
73
70
|
|
|
74
71
|
Args:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"""
|
|
72
|
+
default_path (str): Default path for model data
|
|
73
|
+
model_urls (dict): Dictionary containing model names as keys and their corresponding URLs as values
|
|
78
74
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
75
|
+
Returns:
|
|
76
|
+
dict: A dictionary containing model names as keys and their local paths as values
|
|
77
|
+
"""
|
|
78
|
+
model_paths = {}
|
|
79
|
+
# Store st_size of each model
|
|
80
|
+
model_sizes = {
|
|
81
|
+
"DECIMER": 28080309,
|
|
82
|
+
"DECIMER_HandDrawn": 28080328,
|
|
83
|
+
}
|
|
84
|
+
for model_name, model_url in model_urls.items():
|
|
85
|
+
model_path = os.path.join(default_path, f"{model_name}_model")
|
|
86
|
+
if os.path.exists(model_path) and os.stat(
|
|
87
|
+
os.path.join(model_path, "saved_model.pb")
|
|
88
|
+
).st_size != model_sizes.get(model_name):
|
|
89
|
+
print(f"Working with model {model_name}")
|
|
90
|
+
shutil.rmtree(model_path)
|
|
91
|
+
config.download_trained_weights(model_url, default_path)
|
|
92
|
+
elif not os.path.exists(model_path):
|
|
93
|
+
config.download_trained_weights(model_url, default_path)
|
|
94
|
+
|
|
95
|
+
# Store the model path
|
|
96
|
+
model_paths[model_name] = model_path
|
|
97
|
+
return model_paths
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: decimer
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: DECIMER 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
|
+
Summary: DECIMER 2.6.0: Deep Learning for Chemical Image Recognition using Efficient-Net V2 + Transformer
|
|
5
5
|
Home-page: https://github.com/Kohulan/DECIMER-Image_Transformer
|
|
6
6
|
Author: Kohulan Rajan
|
|
7
7
|
Author-email: kohulan.rajan@uni-jena.de
|
|
@@ -111,7 +111,7 @@ print(SMILES)
|
|
|
111
111
|
- This project is licensed under the MIT License - see the [LICENSE](https://raw.githubusercontent.com/Kohulan/DECIMER-Image_Transformer/master/LICENSE?token=AHKLIF3EULMCUKCFUHIPBMDARSMDO) file for details
|
|
112
112
|
|
|
113
113
|
## Citation
|
|
114
|
-
- Rajan K, Brinkhaus HO, Agea MI, Zielesny A, Steinbeck C
|
|
114
|
+
- Rajan K, Brinkhaus HO, Agea MI, Zielesny A, Steinbeck C DECIMER.ai - An open platform for automated optical chemical structure identification, segmentation and recognition in scientific publications. Nat. Commun. 14, 5045 (2023). https://doi.org/10.1038/s41467-023-40782-0
|
|
115
115
|
- Rajan, K., Zielesny, A. & Steinbeck, C. DECIMER 1.0: deep learning for chemical image recognition using transformers. J Cheminform 13, 61 (2021). https://doi.org/10.1186/s13321-021-00538-8
|
|
116
116
|
|
|
117
117
|
## References
|
|
@@ -87,7 +87,7 @@ print(SMILES)
|
|
|
87
87
|
- This project is licensed under the MIT License - see the [LICENSE](https://raw.githubusercontent.com/Kohulan/DECIMER-Image_Transformer/master/LICENSE?token=AHKLIF3EULMCUKCFUHIPBMDARSMDO) file for details
|
|
88
88
|
|
|
89
89
|
## Citation
|
|
90
|
-
- Rajan K, Brinkhaus HO, Agea MI, Zielesny A, Steinbeck C
|
|
90
|
+
- Rajan K, Brinkhaus HO, Agea MI, Zielesny A, Steinbeck C DECIMER.ai - An open platform for automated optical chemical structure identification, segmentation and recognition in scientific publications. Nat. Commun. 14, 5045 (2023). https://doi.org/10.1038/s41467-023-40782-0
|
|
91
91
|
- Rajan, K., Zielesny, A. & Steinbeck, C. DECIMER 1.0: deep learning for chemical image recognition using transformers. J Cheminform 13, 61 (2021). https://doi.org/10.1186/s13321-021-00538-8
|
|
92
92
|
|
|
93
93
|
## References
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: decimer
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: DECIMER 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
|
+
Summary: DECIMER 2.6.0: Deep Learning for Chemical Image Recognition using Efficient-Net V2 + Transformer
|
|
5
5
|
Home-page: https://github.com/Kohulan/DECIMER-Image_Transformer
|
|
6
6
|
Author: Kohulan Rajan
|
|
7
7
|
Author-email: kohulan.rajan@uni-jena.de
|
|
@@ -111,7 +111,7 @@ print(SMILES)
|
|
|
111
111
|
- This project is licensed under the MIT License - see the [LICENSE](https://raw.githubusercontent.com/Kohulan/DECIMER-Image_Transformer/master/LICENSE?token=AHKLIF3EULMCUKCFUHIPBMDARSMDO) file for details
|
|
112
112
|
|
|
113
113
|
## Citation
|
|
114
|
-
- Rajan K, Brinkhaus HO, Agea MI, Zielesny A, Steinbeck C
|
|
114
|
+
- Rajan K, Brinkhaus HO, Agea MI, Zielesny A, Steinbeck C DECIMER.ai - An open platform for automated optical chemical structure identification, segmentation and recognition in scientific publications. Nat. Commun. 14, 5045 (2023). https://doi.org/10.1038/s41467-023-40782-0
|
|
115
115
|
- Rajan, K., Zielesny, A. & Steinbeck, C. DECIMER 1.0: deep learning for chemical image recognition using transformers. J Cheminform 13, 61 (2021). https://doi.org/10.1186/s13321-021-00538-8
|
|
116
116
|
|
|
117
117
|
## References
|
|
@@ -1,52 +1,55 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
import platform
|
|
3
|
-
|
|
4
|
-
import setuptools
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
platform.processor() == "arm" or platform.processor() == "i386"
|
|
8
|
-
) and platform.system() == "Darwin":
|
|
9
|
-
tensorflow_os = "tensorflow-macos>=2.10.0"
|
|
10
|
-
else:
|
|
11
|
-
tensorflow_os = "tensorflow
|
|
12
|
-
|
|
13
|
-
with open("README.md", "r") as fh:
|
|
14
|
-
long_description = fh.read()
|
|
15
|
-
|
|
16
|
-
setuptools.setup(
|
|
17
|
-
name="decimer",
|
|
18
|
-
version="2.
|
|
19
|
-
author="Kohulan Rajan",
|
|
20
|
-
author_email="kohulan.rajan@uni-jena.de",
|
|
21
|
-
maintainer="Kohulan Rajan, Otto Brinkhaus ",
|
|
22
|
-
maintainer_email="kohulan.rajan@uni-jena.de, otto.brinkhaus@uni-jena.de",
|
|
23
|
-
description="DECIMER 2.
|
|
24
|
-
long_description=long_description,
|
|
25
|
-
long_description_content_type="text/markdown",
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"Programming Language :: Python :: 3
|
|
44
|
-
"Programming Language :: Python :: 3.
|
|
45
|
-
"Programming Language :: Python :: 3.
|
|
46
|
-
"Programming Language :: Python :: 3.
|
|
47
|
-
"Programming Language :: Python :: 3
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
import setuptools
|
|
5
|
+
|
|
6
|
+
if (
|
|
7
|
+
platform.processor() == "arm" or platform.processor() == "i386"
|
|
8
|
+
) and platform.system() == "Darwin":
|
|
9
|
+
tensorflow_os = "tensorflow-macos>=2.10.0,<=2.15.0"
|
|
10
|
+
else:
|
|
11
|
+
tensorflow_os = "tensorflow>=2.12.0,<=2.15.0"
|
|
12
|
+
|
|
13
|
+
with open("README.md", "r") as fh:
|
|
14
|
+
long_description = fh.read()
|
|
15
|
+
|
|
16
|
+
setuptools.setup(
|
|
17
|
+
name="decimer",
|
|
18
|
+
version="2.7.0",
|
|
19
|
+
author="Kohulan Rajan",
|
|
20
|
+
author_email="kohulan.rajan@uni-jena.de",
|
|
21
|
+
maintainer="Kohulan Rajan, Otto Brinkhaus ",
|
|
22
|
+
maintainer_email="kohulan.rajan@uni-jena.de, otto.brinkhaus@uni-jena.de",
|
|
23
|
+
description="DECIMER 2.6.0: Deep Learning for Chemical Image Recognition using Efficient-Net V2 + Transformer",
|
|
24
|
+
long_description=long_description,
|
|
25
|
+
long_description_content_type="text/markdown",
|
|
26
|
+
entry_points={
|
|
27
|
+
"console_scripts": ["decimer = DECIMER.decimer:main"],
|
|
28
|
+
},
|
|
29
|
+
url="https://github.com/Kohulan/DECIMER-Image_Transformer",
|
|
30
|
+
packages=setuptools.find_packages(),
|
|
31
|
+
license="MIT",
|
|
32
|
+
install_requires=[
|
|
33
|
+
tensorflow_os,
|
|
34
|
+
"opencv-python",
|
|
35
|
+
"pystow",
|
|
36
|
+
"pillow-heif",
|
|
37
|
+
"efficientnet",
|
|
38
|
+
"selfies",
|
|
39
|
+
"pyyaml",
|
|
40
|
+
],
|
|
41
|
+
package_data={"DECIMER": ["repack/*.*", "efficientnetv2/*.*", "Utils/*.*"]},
|
|
42
|
+
classifiers=[
|
|
43
|
+
"Programming Language :: Python :: 3",
|
|
44
|
+
"Programming Language :: Python :: 3.5",
|
|
45
|
+
"Programming Language :: Python :: 3.6",
|
|
46
|
+
"Programming Language :: Python :: 3.7",
|
|
47
|
+
"Programming Language :: Python :: 3.8",
|
|
48
|
+
"Programming Language :: Python :: 3.9",
|
|
49
|
+
"Programming Language :: Python :: 3.10",
|
|
50
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
51
|
+
"License :: OSI Approved :: MIT License",
|
|
52
|
+
"Operating System :: OS Independent",
|
|
53
|
+
],
|
|
54
|
+
python_requires=">=3.5",
|
|
55
|
+
)
|
decimer-2.5.0/DECIMER/decimer.py
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import os
|
|
3
|
-
import pickle
|
|
4
|
-
import sys
|
|
5
|
-
from typing import List
|
|
6
|
-
from typing import Tuple
|
|
7
|
-
|
|
8
|
-
import pystow
|
|
9
|
-
import tensorflow as tf
|
|
10
|
-
|
|
11
|
-
import DECIMER.config as config
|
|
12
|
-
import DECIMER.utils as utils
|
|
13
|
-
|
|
14
|
-
# Silence tensorflow model loading warnings.
|
|
15
|
-
logging.getLogger("absl").setLevel("ERROR")
|
|
16
|
-
|
|
17
|
-
# Silence tensorflow errors - not recommended if your model is not working properly.
|
|
18
|
-
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
|
|
19
|
-
|
|
20
|
-
# Set the absolute path
|
|
21
|
-
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
22
|
-
|
|
23
|
-
# Set model to run on default GPU and allow memory to grow as much as needed.
|
|
24
|
-
# This allows us to run multiple instances of inference in the same GPU.
|
|
25
|
-
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
|
|
26
|
-
gpus = tf.config.experimental.list_physical_devices("GPU")
|
|
27
|
-
for gpu in gpus:
|
|
28
|
-
tf.config.experimental.set_memory_growth(gpu, True)
|
|
29
|
-
|
|
30
|
-
# Set path
|
|
31
|
-
default_path = pystow.join("DECIMER-V2")
|
|
32
|
-
|
|
33
|
-
# download models to a default location
|
|
34
|
-
utils.ensure_model(default_path=default_path)
|
|
35
|
-
|
|
36
|
-
# Load important pickle files which consists the tokenizers and the maxlength setting
|
|
37
|
-
tokenizer = pickle.load(
|
|
38
|
-
open(
|
|
39
|
-
os.path.join(
|
|
40
|
-
default_path.as_posix(), "DECIMER_model", "assets", "tokenizer_SMILES.pkl"
|
|
41
|
-
),
|
|
42
|
-
"rb",
|
|
43
|
-
)
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def main():
|
|
48
|
-
"""
|
|
49
|
-
This function take the path of the image as user input
|
|
50
|
-
and returns the predicted SMILES as output in CLI.
|
|
51
|
-
|
|
52
|
-
Agrs:
|
|
53
|
-
str: image_path
|
|
54
|
-
|
|
55
|
-
Returns:
|
|
56
|
-
str: predicted SMILES
|
|
57
|
-
|
|
58
|
-
"""
|
|
59
|
-
if len(sys.argv) != 2:
|
|
60
|
-
print("Usage: {} $image_path".format(sys.argv[0]))
|
|
61
|
-
else:
|
|
62
|
-
SMILES = predict_SMILES(sys.argv[1])
|
|
63
|
-
print(SMILES)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def detokenize_output(predicted_array: int) -> str:
|
|
67
|
-
"""
|
|
68
|
-
This function takes the predited tokens from the DECIMER model
|
|
69
|
-
and returns the decoded SMILES string.
|
|
70
|
-
|
|
71
|
-
Args:
|
|
72
|
-
predicted_array (int): Predicted tokens from DECIMER
|
|
73
|
-
|
|
74
|
-
Returns:
|
|
75
|
-
(str): SMILES representation of the molecule
|
|
76
|
-
"""
|
|
77
|
-
outputs = [tokenizer.index_word[i] for i in predicted_array[0].numpy()]
|
|
78
|
-
prediction = (
|
|
79
|
-
"".join([str(elem) for elem in outputs])
|
|
80
|
-
.replace("<start>", "")
|
|
81
|
-
.replace("<end>", "")
|
|
82
|
-
)
|
|
83
|
-
return prediction
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def detokenize_output_add_confidence(
|
|
87
|
-
predicted_array: tf.Tensor,
|
|
88
|
-
confidence_array: tf.Tensor,
|
|
89
|
-
) -> List[Tuple[str, float]]:
|
|
90
|
-
"""
|
|
91
|
-
This function takes the predicted array of tokens as well as the confidence values
|
|
92
|
-
returned by the Transformer Decoder and returns a list of tuples
|
|
93
|
-
that contain each token of the predicted SMILES string and the confidence
|
|
94
|
-
value.
|
|
95
|
-
|
|
96
|
-
Args:
|
|
97
|
-
predicted_array (tf.Tensor): Transformer Decoder output array (predicted tokens)
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
str: SMILES string
|
|
101
|
-
"""
|
|
102
|
-
prediction_with_confidence = [
|
|
103
|
-
(
|
|
104
|
-
tokenizer.index_word[predicted_array[0].numpy()[i]],
|
|
105
|
-
confidence_array[i].numpy(),
|
|
106
|
-
)
|
|
107
|
-
for i in range(len(confidence_array))
|
|
108
|
-
]
|
|
109
|
-
# remove start and end tokens
|
|
110
|
-
prediction_with_confidence_ = prediction_with_confidence[1:-1]
|
|
111
|
-
|
|
112
|
-
decoded_prediction_with_confidence = list(
|
|
113
|
-
[(utils.decoder(tok), conf) for tok, conf in prediction_with_confidence_]
|
|
114
|
-
)
|
|
115
|
-
decoded_prediction_with_confidence.append(prediction_with_confidence_[-1])
|
|
116
|
-
return decoded_prediction_with_confidence
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
# Load DECIMER model_packed
|
|
120
|
-
DECIMER_V2 = tf.saved_model.load(default_path.as_posix() + "/DECIMER_model/")
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
def predict_SMILES(image_path: str) -> str:
|
|
124
|
-
"""
|
|
125
|
-
This function takes an image path (str) and returns the SMILES
|
|
126
|
-
representation of the depicted molecule (str).
|
|
127
|
-
|
|
128
|
-
Args:
|
|
129
|
-
image_path (str): Path of chemical structure depiction image
|
|
130
|
-
|
|
131
|
-
Returns:
|
|
132
|
-
(str): SMILES representation of the molecule in the input image
|
|
133
|
-
"""
|
|
134
|
-
chemical_structure = config.decode_image(image_path)
|
|
135
|
-
predicted_tokens, _ = DECIMER_V2(chemical_structure)
|
|
136
|
-
predicted_SMILES = utils.decoder(detokenize_output(predicted_tokens))
|
|
137
|
-
return predicted_SMILES
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def predict_SMILES_with_confidence(image_path: str) -> List[Tuple[str, float]]:
|
|
141
|
-
"""
|
|
142
|
-
This function takes an image path (str) and returns a list of tuples
|
|
143
|
-
that contain each token of the predicted SMILES string and the confidence
|
|
144
|
-
level from the last layer of the Transformer decoder.
|
|
145
|
-
|
|
146
|
-
Args:
|
|
147
|
-
image_path (str): Path of chemical structure depiction image
|
|
148
|
-
|
|
149
|
-
Returns:
|
|
150
|
-
(List[Tuple[str, float]]): Tuples that contain the tokens and the confidence
|
|
151
|
-
values of the predicted SMILES
|
|
152
|
-
"""
|
|
153
|
-
decodedImage = config.decode_image(image_path)
|
|
154
|
-
predicted_tokens, confidence_values = DECIMER_V2(tf.constant(decodedImage))
|
|
155
|
-
predicted_SMILES_with_confidence = detokenize_output_add_confidence(
|
|
156
|
-
predicted_tokens, confidence_values
|
|
157
|
-
)
|
|
158
|
-
return predicted_SMILES_with_confidence
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if __name__ == "__main__":
|
|
162
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|