#!/usr/bin/env python # coding: utf-8 # # Generative Pre-Training from Molecules import os #os.environ["CUDA_VISIBLE_DEVICES"] = ['1',"2"] from pprint import pprint import sys sys.path.append('/root/autodl-tmp/wjm/iupac-gpt') from tqdm import tqdm try: import iupac_gpt as gpt except ImportError: import sys sys.path.extend([".."]) # Parent directory stores `smiles_gpt` package. import iupac_gpt as gpt import torch # For demonstration purposes, we use only 10K subset of PubChem data made available by # [ChemBERTa](https://arxiv.org/abs/2010.09885) developers. The original model was pretrained # on the first 5M compounds with the following hyperparameters: # ```python # hyperparams = {"batch_size": 128, "max_epochs": 2, "max_length": 512, # "learning_rate": 5e-4, "weight_decay": 0.0, # "adam_eps": 1e-8, "adam_betas": (0.9, 0.999), # "scheduler_T_max": 150_000, "final_learning_rate": 5e-8, # "vocab_size": 1_000, "min_frequency": 2, "top_p": 0.96, # "n_layer": 4, "n_head": 8, "n_embd": 512} # ``` # Tokenizer, model, optimizer, scheduler, and trainer hyperparameters. hyperparams = {"batch_size": 128, "max_epochs": 10, "max_length": 1280, "learning_rate": 5e-4, "weight_decay": 0.0, "adam_eps": 1e-8, "adam_betas": (0.9, 0.999), "scheduler_T_max": 1_000, "final_learning_rate": 5e-8, "vocab_size": 1491, "min_frequency": 2, "top_p": 0.96, "n_layer": 8, "n_head": 8, "n_embd": 256} gpus = [0,1,2] # Specify either a list of GPU devices or an integer (0 for no GPU). num_workers = 32 # Number of dataloader worker processes. # ## Tokenization # # `smiles_gpt.SMILESBPETokenizer` first splits SMILES strings into characters, runs # byte-pair encoding, and augments the resulting list with `""` (beginning-of-SMILES) and # `""` (end-of-SMILES) special tokens. `smiles_gpt.SMILESAlphabet` stores 72 possible # characters as an initial vocabulary. device = 'gpu' train_dataloader,iupac_tokenizer = gpt.get_data_loader(is_train=1,dataset_filename = './pubchem_iupac_smile_gpt_1bw.csv') pbar = tqdm(train_dataloader) #train_dataloader.cuda() ''' for inputs in pbar: src_label = Variable(inputs["labels"].to(device)) inputs = prepare_input(inputs,device) src = Variable(inputs["input_ids"].to(device)) #self.tokenizer._convert_token_to_id print(src[:,:].shape,src_label) ''' tokenizer = iupac_tokenizer #start mark 2, end mark 1, pad 0 iupac_string = "2-amino-9-[4-hydroxy-3-(hydroxymethyl)-2-methylidenecyclopentyl]-1H-purin-6-one" iupac_encoded = tokenizer(iupac_string) iupac_encoded['input_ids'] = [2]+iupac_encoded['input_ids'] iupac_merges = [tokenizer.decode(i) for i in iupac_encoded['input_ids']] #iupac_encoded['attention_mask'] print(iupac_encoded['input_ids']) print(iupac_merges) print(tokenizer.unk_token_id,tokenizer.eos_token_id,tokenizer.unk_token,tokenizer.eos_token,tokenizer.vocab_size) #2 1 1491 # ## Data Module batch = next(iter(pbar)) # ## GPT-2 Model # # Now we load HuggingFace # [`GPT2LMHeadModel`](https://huggingface.co/transformers/model_doc/gpt2.html#gpt2lmheadmodel) # with the configuration composed of previously # defined model hyperparameters. The model processes mini-batch of input ids and labels, then # returns predictions and cross-entropy loss between labels and predictions. from transformers import GPT2Config, GPT2LMHeadModel config = GPT2Config(vocab_size=tokenizer.vocab_size, bos_token_id=tokenizer.unk_token_id, eos_token_id=tokenizer.eos_token_id, n_layer=hyperparams["n_layer"], n_head=hyperparams["n_head"], n_embd=hyperparams["n_embd"], n_positions=hyperparams["max_length"], n_ctx=hyperparams["max_length"]) model = GPT2LMHeadModel(config) #model= torch.nn.DataParallel(model.cuda(),device_ids=gpus,output_device=gpus[0]) outputs = model(**batch) print(outputs.keys()) #['loss', 'logits', 'past_key_values'] # ## Trainer # # GPT-2 is trained with autoregressive language modeling objective: # $$ # P(\boldsymbol{s}) = P(s_1) \cdot P(s_2 | s_1) \cdots P(s_T | s_1, \ldots, s_{T-1}) = # \prod_{t=1}^{T} P(s_t | s_{j < t}), # $$ # where $\boldsymbol{s}$ is a tokenized (encoded) SMILES string, $s_t$ is a token from pretrained # vocabulary $\mathcal{V}$. # # We use `pytorch_lightning.Trainer` to train GPT-2. Since `Trainer` requires lightning modules, # we import our # [`smiles_gpt.GPT2LitModel`](https://github.com/sanjaradylov/smiles-gpt/blob/master/smiles_gpt/language_modeling.py#L10) # wrapper that implements training phases for # `GPT2LMHeadModel`, configures an `Adam` optimizer with `CosineAnnealingLR` scheduler, and # logs average perplexity every epoch. # In[8]: from pytorch_lightning import Trainer from pytorch_lightning.callbacks.early_stopping import EarlyStopping checkpoint = "../checkpoints/iupac" trainer = Trainer( gpus=gpus, max_epochs=hyperparams["max_epochs"], callbacks=[EarlyStopping("ppl", 0.1, 3)], #[EarlyStopping("ppl", 0.2, 2)] auto_lr_find=False, # Set to True to search for optimal learning rate. auto_scale_batch_size=False, # Set to True to scale batch size # accelerator="dp" # Uncomment for GPU training. accelerator="gpu", #devices=4, strategy="ddp" ) lit_model = gpt.GPT2LitModel( model, batch_size=hyperparams["batch_size"], learning_rate=hyperparams["learning_rate"], final_learning_rate=hyperparams["final_learning_rate"], weight_decay=hyperparams["weight_decay"], adam_eps=hyperparams["adam_eps"], adam_betas=hyperparams["adam_betas"], scheduler_T_max=hyperparams["scheduler_T_max"], save_model_every=1, checkpoint=checkpoint) trainer.fit(lit_model, train_dataloader) #model.module.save_pretrained('./pretrained') model.save_pretrained('./pretrained') # ## Interpretability # # [BertViz](https://github.com/jessevig/bertviz) inspects attention heads of transformers # capturing specific patterns in data. Each head can be representative of some syntactic # or short-/long-term relationships between tokens. # In[9]: import torch from bertviz import head_view input_ids_list = iupac_encoded['input_ids'] model = GPT2LMHeadModel.from_pretrained(checkpoint, output_attentions=True) attention = model(torch.LongTensor(input_ids_list))[-1] tokens = [tokenizer.decode(i) for i in input_ids_list] print(input_ids_list,attention,tokens) # Don't worry if a snippet is not displayed---just rerun this cell. head_view(attention, tokens) from bertviz import model_view # Don't worry if a snippet is not displayed---just rerun this cell. model_view(attention, tokens) # ## Sampling # # Finally, we generate novel SMILES strings with top-$p$ sampling$-$i.e., sampling from the # smallest vocabulary subset $\mathcal{V}^{(p)} \subset \mathcal{V}$ s.t. it takes up the most # probable tokens whose cumulative probability mass exceeds $p$, $0 < p < 1$. Model # terminates the procedure upon encountering `""` or reaching maximum number # `hyperparams["max_length"]`. Special tokens are eventually removed. import tqdm model.eval() # Set the base model to evaluation mode. generated_smiles_list = [] n_generated = 30000 for _ in tqdm.tqdm(range(n_generated)): # Generate from "" so that the next token is arbitrary. smiles_start = torch.LongTensor([[tokenizer.unk_token_id]]) # Get generated token IDs. generated_ids = model.generate(smiles_start, max_length=hyperparams["max_length"], do_sample=True,top_p=hyperparams["top_p"], repetition_penalty=1.2, pad_token_id=tokenizer.eos_token_id) # Decode the IDs into tokens and remove "" and "". generated_smiles = tokenizer.decode(generated_ids[0], skip_special_tokens=True) generated_smiles_list.append(generated_smiles) print(generated_smiles_list[:10]) import numpy as np import pandas as pd df2 = pd.DataFrame(generated_smiles_list, columns=['iupac']) df2.to_csv("iupacGPT2-gen30K.csv",index=None,mode='a')