| from typing import List, Dict, Optional |
| import torch |
| from lightning import seed_everything |
| from transformers_model.model import LitVanillaTransformer |
|
|
|
|
| |
| def load_forward_model( |
| ckpt_path: str, |
| vocab_path: str, |
| num_beams: int, |
| topn: int, |
| device: Optional[str] = None, |
| ) -> LitVanillaTransformer: |
| return load_lit_transformer_for_inference( |
| ckpt_path=ckpt_path, |
| vocab_path=vocab_path, |
| task="forward", |
| num_beams=num_beams, |
| topn=topn, |
| gen_max_length=256, |
| device=device, |
| ) |
|
|
|
|
| |
| def load_retro_model( |
| ckpt_path: str, |
| vocab_path: str, |
| num_beams: int, |
| topn: int, |
| device: Optional[str] = None, |
| ) -> LitVanillaTransformer: |
| return load_lit_transformer_for_inference( |
| ckpt_path=ckpt_path, |
| vocab_path=vocab_path, |
| task="forward", |
| num_beams=num_beams, |
| topn=topn, |
| gen_max_length=256, |
| device=device, |
| ) |
|
|
|
|
| |
| def load_lit_transformer_for_inference( |
| ckpt_path: str, |
| vocab_path: str, |
| task: str = "forward", |
| num_beams: int = 3, |
| topn: int = 3, |
| gen_max_length: int = 256, |
| device: Optional[str] = None, |
| ) -> LitVanillaTransformer: |
| """ |
| Load the trained LitVanillaTransformer exactly like LightningCLI would, |
| and prepare it for inference/generation. |
| """ |
| torch.set_float32_matmul_precision("high") |
|
|
| |
| if device is None: |
| device = "cuda" if torch.cuda.is_available() else "cpu" |
|
|
| |
| lit = LitVanillaTransformer.load_from_checkpoint( |
| ckpt_path, |
| vocab_path=vocab_path, |
| task=task, |
| num_beams=num_beams, |
| topn=topn, |
| max_length=gen_max_length, |
| device=device, |
| ) |
| lit.eval() |
|
|
| |
| target_device = torch.device(device) |
| lit.to(target_device) |
| if hasattr(lit, "model") and hasattr(lit.model, "config"): |
| lit.model.config.device = target_device |
|
|
| |
| return lit |
|
|
|
|
| |
| @torch.inference_mode() |
| def predict_smiles( |
| lit: LitVanillaTransformer, |
| inputs: List[str], |
| *, |
| batch_size: int = 512, |
| truncation: bool = True, |
| padding: str = "max_length", |
| data_max_length: int = 278, |
| seed: Optional[int] = 42, |
| ) -> List[Dict]: |
|
|
| |
| if seed is not None: |
| seed_everything(seed, workers=True) |
|
|
| |
| device = next(lit.parameters()).device |
|
|
| |
| tokenizer = lit.tokenizer |
|
|
| |
| all_results: List[Dict] = [] |
|
|
| |
| for start in range(0, len(inputs), batch_size): |
|
|
| |
| batch_src = inputs[start : start + batch_size] |
| print(f"DEBUG - batch_src: {batch_src}") |
|
|
| |
| encoder_input_ids = tokenizer( |
| batch_src, |
| truncation=truncation, |
| padding=padding, |
| max_length=data_max_length, |
| return_token_type_ids=False, |
| return_tensors="pt", |
| )["input_ids"] |
|
|
| |
| if device is not None: |
| encoder_input_ids = encoder_input_ids.to(device) |
|
|
| |
| outputs = lit.model.generate( |
| encoder_input_ids, |
| do_sample=False, |
| max_length=lit.hparams.max_length, |
| num_beams=lit.hparams.num_beams, |
| num_return_sequences=lit.hparams.topn, |
| return_dict_in_generate=True, |
| output_scores=True, |
| ) |
|
|
| |
| sequences = outputs.sequences |
| scores = outputs.sequences_scores |
|
|
| |
| pred_texts = tokenizer.batch_decode(sequences, skip_special_tokens=True) |
| pred_texts = tokenizer.batch_detokenize_smiles_string(pred_texts) |
|
|
| |
| src_texts_dec = tokenizer.batch_decode(encoder_input_ids, skip_special_tokens=True) |
| src_texts_dec = tokenizer.batch_detokenize_smiles_string(src_texts_dec) |
| print(f"DEBUG - src_texts_dec: {src_texts_dec}") |
|
|
| |
| topn = lit.hparams.topn |
|
|
| |
| batch_results: List[Dict] = [] |
|
|
| |
| for i, src in enumerate(src_texts_dec): |
|
|
| |
| start_j = i * topn |
|
|
| |
| for j in range(start_j, start_j + topn): |
|
|
| |
| item = { |
| "source": src, |
| "predicted_target": pred_texts[j], |
| "confidence": scores[j].item(), |
| } |
|
|
| |
| batch_results.append(item) |
|
|
| |
| all_results.extend(batch_results) |
|
|
| |
| return all_results |
|
|
|
|
| |
| @torch.inference_mode() |
| def reaction_likelihood( |
| lit: LitVanillaTransformer, |
| src_text: str, |
| tgt_text: str, |
| truncation: bool = True, |
| padding: str = "max_length", |
| data_max_length: int = 278, |
| ) -> float: |
| |
| model = lit.model |
| tokenizer = lit.tokenizer |
| device = next(model.parameters()).device |
|
|
| |
| src_ids = tokenizer( |
| src_text, |
| truncation=truncation, |
| padding=padding, |
| max_length=data_max_length, |
| return_tensors="pt", |
| ).input_ids.to(device) |
|
|
| |
| tgt_ids = tokenizer( |
| tgt_text, |
| truncation=truncation, |
| padding=padding, |
| max_length=data_max_length, |
| return_tensors="pt", |
| ).input_ids.to(device) |
|
|
| |
| outputs = model( |
| encoder_input_ids=src_ids, |
| decoder_input_ids=tgt_ids, |
| labels=tgt_ids, |
| return_dict=True, |
| ) |
|
|
| |
| loss = outputs.loss |
|
|
| |
| pad_id = tokenizer.pad_token_id |
| num_tokens = (tgt_ids != pad_id).sum() |
|
|
| |
| log_likelihood = -loss * num_tokens |
|
|
| |
| return float(torch.exp(log_likelihood)) |
|
|