File size: 5,348 Bytes
111d270 848dcb6 0b12001 111d270 a7ff6bf 111d270 e2dbbbe ad553a5 e2dbbbe 0b12001 c4c625d 0b12001 c4c625d 0b12001 38688e0 c4c625d e2dbbbe d1e7bbb e2dbbbe c4c625d 111d270 0b12001 38688e0 62fd606 111d270 0b12001 111d270 0b12001 111d270 dacb5e3 111d270 e2dbbbe 111d270 dacb5e3 111d270 dacb5e3 111d270 454364c 111d270 e2dbbbe 2753942 4c450f8 2753942 111d270 | 1 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | from transformers import Pipeline
import torch
import torch.nn.functional as F
import numpy as np
from transformers import Pipeline
import torch
import torch.nn.functional as F
import re
class SmilesDiffusionPipe(Pipeline):
def __init__(self, model, voc, **kwargs):
super().__init__(model=model, **kwargs)
self.voc = voc
def restore_halogen(self, smiles):
"""Restores halogens back to their proper form after decoding."""
return smiles.replace('L', 'Cl').replace('R', 'Br')
def replace_halogen(self, smiles):
return smiles.replace('Cl', 'L').replace('Br', 'R')
def tokenize_smiles(self, smiles):
regex = r'(\[[^\[\]]{1,6}\])'
smiles = self.replace_halogen(smiles)
char_list = re.split(regex, smiles)
tokenized = []
for char in char_list:
if char.startswith('['):
tokenized.append(char)
else:
tokenized.extend(list(char))
return tokenized
def encode(self, char_list):
smiles_matrix = np.zeros(len(char_list), dtype=np.float32)
for i, char in enumerate(char_list):
if char not in self.voc.vocab:
raise ValueError(f"Token '{char}' not in vocabulary.")
smiles_matrix[i] = self.voc.vocab[char]
return smiles_matrix
def decode(self, matrix):
"""Takes an array of indices (can be list of tensors or ints) and returns the corresponding SMILES"""
matrix = [int(i) for i in matrix] # This handles tensors from PyTorch
# Using tokenizer's decode method to convert token IDs to text
smiles = self.voc.decode(matrix)
smiles = self.restore_halogen(smiles) # Restore halogens after decoding
return smiles
def _sanitize_parameters(self, **kwargs):
preprocess_kwargs = {}
postprocess_kwargs = {}
if "steps" in kwargs:
preprocess_kwargs["steps"] = kwargs["steps"]
if "k" in kwargs:
preprocess_kwargs["k"] = kwargs["k"]
return preprocess_kwargs, {}, postprocess_kwargs
def preprocess(self, smiles_str, steps=30, k=1):
tokenized = self.tokenize_smiles(smiles_str)
encoded = self.encode(tokenized)
return (encoded, steps, k)
def _forward(self, inputs):
encoded, steps, k = inputs
return self.unmask_partial_smiles(encoded, self.model, self.voc, steps, k)
def postprocess(self, result):
return result
def topk_sample(self, probs, k=5):
topk_probs, topk_indices = torch.topk(probs, k)
topk_probs = topk_probs / topk_probs.sum(dim=-1, keepdim=True)
sampled = torch.multinomial(topk_probs, 1)
return topk_indices.gather(-1, sampled)
def unmask_partial_smiles(self, input_ids, model, voc, steps=30, k=1):
model.eval()
with torch.no_grad():
sequences = torch.tensor(input_ids, dtype=torch.long, device=self.device).unsqueeze(0)
total_tokens = sequences.size(1)
original_num_masked = (sequences == voc.vocab['[MASK]']).sum().item()
print(f"Original number of masked tokens: {original_num_masked}")
if total_tokens == 0:
raise ValueError("Encoded SMILES has no tokens.")
if original_num_masked == 0:
print("No masked tokens found. Returning original SMILES.")
return self.decode(input_ids)
filled_tokens_confidence = {}
for step in range(steps):
t = torch.tensor([(sequences == voc.vocab['[MASK]']).sum().item() / total_tokens], device=self.device)
logits = model(sequences, t=t)
probs = F.softmax(logits, dim=-1)
mask_indices = (sequences == voc.vocab['[MASK]']).nonzero(as_tuple=True)[1]
if len(mask_indices) == 0:
print("All tokens filled.")
break
confidences = []
filled_indices = []
for idx in mask_indices:
token_probs = probs[0, idx]
topk_token = self.topk_sample(token_probs, k)
confidence = token_probs[topk_token].item()
sequences[0, idx] = topk_token
confidences.append(confidence)
filled_indices.append(idx)
filled_tokens_confidence[idx] = confidence
avg_conf = sum(confidences) / len(confidences)
for idx, conf in zip(filled_indices, confidences):
if conf < avg_conf:
sequences[0, idx] = voc.vocab['<MASK>']
filled_tokens_confidence[idx] = None
sorted_filled_tokens = sorted(
((idx, conf) for idx, conf in filled_tokens_confidence.items() if conf is not None),
key=lambda x: x[0]
)
for idx, confidence in sorted_filled_tokens:
print(f"Final confidence for token at index {idx}: {confidence:.3f}")
decoded = self.decode([
token for token in sequences[0].tolist()
if token not in (voc.vocab.get('<s>'), voc.vocab.get('</s>'))
]).replace(" ", "")
return decoded |