├── LICENSE ├── LCW-Using-ChemBERTa-2-For-Property_Prediction.py ├── LCW-Fine-Tuning-ChemBERTa-2.py ├── LCW-Using-ChemBERTa-2-For-Property_Prediction.ipynb └── LCW-Fine-Tuning-ChemBERTa-2.ipynb /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 jwoerner42 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LCW-Using-ChemBERTa-2-For-Property_Prediction.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # Released under MIT License 5 | # 6 | # Copyright (c) 2023 Andrew SID Lang, Oral Roberts University, U.S.A. 7 | # 8 | # Copyright (c) 2023 Jan HR Woerner, Oral Roberts University, U.S.A. 9 | # 10 | # Copyright (c) 2023 Wei-Khiong (Wyatt) Chong, Advent Polytech Co., Ltd, Taiwan. 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 13 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 14 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 15 | # permit persons to whom the Software is furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 18 | # Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 21 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 22 | # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | import torch 27 | from torch.utils.data import Dataset 28 | from transformers import AutoConfig, AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments 29 | import pandas as pd 30 | from transformers import pipeline 31 | from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error 32 | from scipy.stats import spearmanr 33 | import matplotlib.pyplot as plt 34 | import warnings 35 | 36 | warnings.filterwarnings("ignore", message="Was asked to gather along dimension 0, but all input tensors were scalars") 37 | output_directory = "./output" 38 | # max length of SMILES over both sets 39 | max_length = 195 40 | 41 | class Input(Dataset): 42 | def __init__(self, i_data, i_tokenizer, i_max_length): 43 | self.data = i_data 44 | self.tokenizer = i_tokenizer 45 | self.max_length = i_max_length 46 | 47 | def __len__(self): 48 | return len(self.data) 49 | 50 | def __getitem__(self, idx): 51 | i_smiles = self.data.iloc[idx]["Standardized_SMILES"] 52 | i_inputs = self.tokenizer(i_smiles, return_tensors="pt", padding='max_length', truncation=True, 53 | max_length=self.max_length) 54 | i_inputs["input_ids"] = i_inputs["input_ids"].squeeze(0) 55 | i_inputs["attention_mask"] = i_inputs["attention_mask"].squeeze(0) 56 | if "token_type_ids" in i_inputs: 57 | i_inputs["token_type_ids"] = i_inputs["token_type_ids"].squeeze(0) 58 | i_inputs["labels"] = torch.tensor(self.data.iloc[idx]["median_WS"], dtype=torch.float).unsqueeze(0) 59 | return i_inputs 60 | 61 | 62 | # retrieve the device to move the model to 63 | def get_device(): 64 | if torch.cuda.is_available(): 65 | dev = torch.device("cuda") 66 | print("Using NV GPU.") 67 | # The mps device in torch does repeatedly lead to a RuntimeError: Placeholder storage has not been allocated 68 | # on MPS device! 69 | #elif torch.backends.mps.is_available() and torch.backends.mps.is_built(): 70 | # dev = torch.device("mps") 71 | # print("Using M1 GPU.") 72 | else: 73 | print("No GPU available, using the CPU instead.") 74 | dev = torch.device("cpu") 75 | return dev 76 | 77 | 78 | # Predict properties for new SMILES strings 79 | def predict_smiles(u_smiles, dev): 80 | preds = [] 81 | for i_smiles in u_smiles: 82 | inputs = tokenizer(i_smiles, return_tensors="pt", padding='max_length', truncation=True, max_length=195).to(dev) 83 | # max_length=195 84 | with torch.no_grad(): 85 | outputs = model(**inputs) 86 | pred_property = outputs.logits.squeeze().item() 87 | preds.append(pred_property) 88 | r_mse = mean_squared_error(data["median_WS"], preds, squared=False) 89 | r2 = r2_score(data["median_WS"], preds) 90 | mae = mean_absolute_error(data["median_WS"], preds) 91 | correlation, p_value = spearmanr(data["median_WS"], preds) 92 | return r_mse, r2, mae, preds, correlation, p_value 93 | 94 | 95 | # display the results 96 | def display_results(dataset_type, in_r_mse, in_r2, in_mae, preds, correlation, p_val): 97 | print(dataset_type) 98 | print("N:", len(data["median_WS"])) 99 | print("R2:", in_r2) 100 | print("Root Mean Square Error:", in_r_mse) 101 | print("Mean Absolute Error:", in_mae) 102 | print("Spearman correlation:", correlation) 103 | print("p-value:", p_val) 104 | 105 | plt.scatter(data["median_WS"], preds) 106 | plt.xlabel("train['median_WS']") 107 | plt.ylabel("predictions") 108 | plt.title("Scatter Plot of " + dataset_type + " ['median_WS'] vs Predictions") 109 | plt.show() 110 | 111 | 112 | # assume test and predictions are two arrays of the same length 113 | # run it for train smiles data 114 | def run_prediction(prep_smiles, set_type, dev, is_saved): 115 | out_r_mse, out_r2, out_mae, predictions, correlation, p_value = predict_smiles(prep_smiles, dev) 116 | 117 | display_results(set_type, out_r_mse, out_r2, out_mae, predictions, correlation, p_value) 118 | if is_saved: 119 | results_df = pd.DataFrame({"actual_WS": test["median_WS"], "predicted_WS": predictions}) 120 | results_df.to_csv("testset_results.csv", index=False) 121 | 122 | # 123 | # LCW Using ChemBERTa-2 For Property Prediction main program 124 | # 125 | 126 | # Read in solubility data 127 | train_data = pd.read_csv('aqua_train.csv') 128 | test_data = pd.read_csv('aqua_test.csv') 129 | 130 | # pick out columns 131 | data = train_data[['Standardized_SMILES', 'median_WS']] 132 | test = test_data[['Standardized_SMILES', 'median_WS']] 133 | 134 | # Load a pretrained transformer model and tokenizer 135 | model_name = "DeepChem/ChemBERTa-77M-MTR" 136 | tokenizer = AutoTokenizer.from_pretrained(model_name) 137 | config = AutoConfig.from_pretrained(model_name) 138 | config.num_hidden_layers += 1 139 | model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=1) 140 | 141 | # move model to the device 142 | device = get_device() 143 | model.to(device) 144 | 145 | # Prepare the dataset for training 146 | train_dataset = Input(data, tokenizer, max_length) 147 | 148 | # Set up training arguments 149 | training_args = TrainingArguments( 150 | output_dir=output_directory, 151 | num_train_epochs=100, # Number of training epochs 152 | per_device_train_batch_size=86, # Batch size 153 | logging_steps=100, # Log training metrics every 100 steps 154 | optim="adamw_torch", # switch optimizer to avoid warning 155 | seed=123, # Set a random seed for reproducibility 156 | ) 157 | 158 | # Train the model 159 | trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset, ) 160 | trainer.train() 161 | trainer.save_model("./output") # save model to output folder 162 | 163 | # Create a prediction pipeline 164 | 165 | predictor = pipeline("text-classification", model=model, tokenizer=tokenizer) 166 | 167 | # Prepare new SMILES strings for prediction and run the model for test data 168 | test_smiles = test['Standardized_SMILES'] 169 | run_prediction(test_smiles, "TEST SET", device, False) 170 | 171 | # Prepare new SMILES strings for prediction and run the model for training data 172 | train_smiles = data['Standardized_SMILES'] 173 | run_prediction(train_smiles, "TEST SET", device, False) 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /LCW-Fine-Tuning-ChemBERTa-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | # 4 | # Released under MIT License 5 | # 6 | # Copyright (c) 2023 Andrew SID Lang, Oral Roberts University, U.S.A. 7 | # 8 | # Copyright (c) 2023 Jan HR Woerner, Oral Roberts University, U.S.A. 9 | # 10 | # Copyright (c) 2023 Wei-Khiong (Wyatt) Chong, Advent Polytech Co., Ltd, Taiwan. 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 13 | # documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 14 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 15 | # permit persons to whom the Software is furnished to do so, subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 18 | # Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 21 | # THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 22 | # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | 26 | import torch 27 | from torch.utils.data import Dataset 28 | from transformers import AutoConfig, AutoTokenizer, AutoModelForSequenceClassification 29 | from transformers import Trainer, TrainingArguments, TrainerCallback, pipeline 30 | import pandas as pd 31 | import warnings 32 | import numpy as np 33 | import evaluate 34 | from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error 35 | from scipy.stats import spearmanr 36 | import matplotlib.pyplot as plt 37 | 38 | warnings.filterwarnings("ignore", message="Was asked to gather along dimension 0, but all input tensors were scalars") 39 | # define the input file 40 | input_file: str = 'aqua.csv' 41 | output_directory: str = './output' 42 | # Define the maximum sequence length 43 | max_length = 195 44 | 45 | 46 | class MyData: 47 | def __init__(self, i_data): 48 | self.data = i_data 49 | 50 | def get_split(self, train_ratio=0.8, valid_ratio=0.1, seed=None): 51 | n = len(self.data) 52 | indices = np.arange(n) 53 | if seed is not None: 54 | np.random.seed(seed) 55 | np.random.shuffle(indices) 56 | train_size = int(train_ratio * n) 57 | valid_size = int(valid_ratio * n) 58 | train_indices = indices[:train_size] 59 | valid_indices = indices[train_size:train_size + valid_size] 60 | test_indices = indices[train_size + valid_size:] 61 | i_train_data = self.data.iloc[train_indices].reset_index(drop=True) 62 | i_valid_data = self.data.iloc[valid_indices].reset_index(drop=True) 63 | i_test_data = self.data.iloc[test_indices].reset_index(drop=True) 64 | return i_train_data, i_valid_data, i_test_data 65 | 66 | 67 | class Input(Dataset): 68 | def __init__(self, i_data, i_tokenizer, i_max_length): 69 | self.data = i_data 70 | self.tokenizer = i_tokenizer 71 | self.max_length = i_max_length 72 | 73 | def __len__(self): 74 | return len(self.data) 75 | 76 | def __getitem__(self, idx): 77 | smiles = self.data.iloc[idx]["Standardized_SMILES"] 78 | inputs = self.tokenizer(smiles, return_tensors="pt", padding='max_length', truncation=True, 79 | max_length=self.max_length) 80 | inputs["input_ids"] = inputs["input_ids"].squeeze(0) 81 | inputs["attention_mask"] = inputs["attention_mask"].squeeze(0) 82 | if "token_type_ids" in inputs: 83 | inputs["token_type_ids"] = inputs["token_type_ids"].squeeze(0) 84 | inputs["labels"] = torch.tensor(self.data.iloc[idx]["median_WS"], dtype=torch.float).unsqueeze(0) 85 | return inputs 86 | 87 | 88 | # Define a callback for printing validation loss 89 | class PrintValidationLossCallback(TrainerCallback): 90 | def on_evaluate(self, args, state, control, **kwargs): 91 | if state is not None and hasattr(state, 'eval_loss'): 92 | print(f"Validation loss: {state.eval_loss:.4f}") 93 | 94 | 95 | def compute_metrics(eval_pred): 96 | logits, labels = eval_pred 97 | predictions = np.argmax(logits, axis=-1) 98 | return metric.compute(predictions=predictions, references=labels) 99 | 100 | 101 | # Read in solubility data and split 102 | def read_solubility(filename: str): 103 | my_data = pd.read_csv(filename) 104 | # Create an instance of the MyData class 105 | my_data = MyData(my_data) 106 | # Split your data into training, validation, and testing sets 107 | train_data, valid_data, test_data = my_data.get_split(seed=123) 108 | # pick out columns 109 | r_data = train_data[['Standardized_SMILES', 'median_WS']] 110 | r_valid = valid_data[['Standardized_SMILES', 'median_WS']] 111 | r_test = test_data[['Standardized_SMILES', 'median_WS']] 112 | return r_data, r_valid, r_test 113 | 114 | 115 | # retrieve the device to move the model to 116 | def get_device(): 117 | if torch.cuda.is_available(): 118 | dev = torch.device("cuda") 119 | print("Using NV GPU.") 120 | # The mps device in torch does repeatedly lead to a RuntimeError: Placeholder storage has not been allocated 121 | # on MPS device! 122 | #elif torch.backends.mps.is_available(): 123 | # dev = torch.device("mps") 124 | # print("Using M1 GPU.") 125 | else: 126 | print("No GPU available, using the CPU instead.") 127 | dev = torch.device("cpu") 128 | return dev 129 | 130 | 131 | # Predict properties for new SMILES strings 132 | def predict_smiles(u_smiles, dev): 133 | preds = [] 134 | for i_smiles in u_smiles: 135 | # max_length=195 and move the inputs also to the device 136 | inputs = tokenizer(i_smiles, return_tensors="pt", padding='max_length', truncation=True, max_length=195).to(dev) 137 | with torch.no_grad(): 138 | outputs = model(**inputs) 139 | pred_property = outputs.logits.squeeze().item() 140 | preds.append(pred_property) 141 | r_mse = mean_squared_error(data["median_WS"], preds, squared=False) 142 | r2 = r2_score(data["median_WS"], preds) 143 | mae = mean_absolute_error(data["median_WS"], preds) 144 | correlation, p_value = spearmanr(data["median_WS"], preds) 145 | return r_mse, r2, mae, preds, correlation, p_value 146 | 147 | 148 | # display the results 149 | def display_results(dataset_type, in_r_mse, in_r2, in_mae, preds, correlation, p_val): 150 | print(dataset_type) 151 | print("N:", len(data["median_WS"])) 152 | print("R2:", in_r2) 153 | print("Root Mean Square Error:", in_r_mse) 154 | print("Mean Absolute Error:", in_mae) 155 | print("Spearman correlation:", correlation) 156 | print("p-value:", p_val) 157 | 158 | plt.scatter(data["median_WS"], preds) 159 | plt.xlabel("train['median_WS']") 160 | plt.ylabel("predictions") 161 | plt.title("Scatter Plot of " + dataset_type + " ['median_WS'] vs Predictions") 162 | plt.show() 163 | 164 | 165 | # assume test and predictions are two arrays of the same length 166 | # run it for prepared smiles data, set a string set_type for output, device, and a flag to save results 167 | def run_prediction(prep_smiles, set_type, dev, is_saved): 168 | out_r_mse, out_r2, out_mae, predictions, correlation, p_value = predict_smiles(prep_smiles, dev) 169 | display_results(set_type, out_r_mse, out_r2, out_mae, predictions, correlation, p_value) 170 | if is_saved: 171 | results_df = pd.DataFrame({"actual_WS": test["median_WS"], "predicted_WS": predictions}) 172 | results_df.to_csv("testset_results.csv", index=False) 173 | 174 | 175 | # 176 | # main program 177 | # 178 | 179 | # AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=1) 180 | # Load a pretrained transformer model and tokenizer 181 | model_name = "DeepChem/ChemBERTa-77M-MTR" 182 | tokenizer = AutoTokenizer.from_pretrained(model_name) 183 | config = AutoConfig.from_pretrained(model_name) 184 | config.num_hidden_layers += 1 185 | model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=1) 186 | 187 | # see if GPU and assign model (move model to the device) 188 | device = get_device() 189 | model.to(device) 190 | 191 | # Read and prepare the dataset for training 192 | data, valid, test = read_solubility(input_file) 193 | train_dataset = Input(data, tokenizer, max_length) 194 | validation_dataset = Input(valid, tokenizer, max_length) 195 | 196 | # Set up training arguments 197 | training_args = TrainingArguments( 198 | output_dir=output_directory, 199 | optim="adamw_torch", # switch optimizer to avoid warning 200 | num_train_epochs=100, # Train the model for 100 epochs 201 | per_device_train_batch_size=128, # Set the batch size to 128 202 | per_device_eval_batch_size=128, # Set the evaluation batch size to 128 203 | logging_steps=10, # Log training metrics every 100 steps 204 | eval_steps=10, # Evaluate the model every 100 steps 205 | save_steps=10, # Save the model every 100 steps 206 | seed=123, # Set the random seed for reproducibility 207 | evaluation_strategy="steps", # Evaluate the model every eval_steps steps 208 | load_best_model_at_end=True 209 | ) 210 | 211 | # Train the model 212 | trainer = Trainer( 213 | model=model, 214 | args=training_args, 215 | train_dataset=train_dataset, 216 | eval_dataset=validation_dataset, 217 | ) 218 | 219 | # Add the callback to the trainer 220 | trainer.add_callback(PrintValidationLossCallback()) 221 | 222 | metric = evaluate.load("accuracy") 223 | 224 | # Train the model 225 | trainer.train() 226 | 227 | # Save the model 228 | trainer.save_model("./output") 229 | 230 | # Create a prediction pipeline 231 | predictor = pipeline("text-classification", model=model, tokenizer=tokenizer) 232 | 233 | 234 | # Prepare new SMILES strings for prediction TRAINING-SET 235 | run_prediction(data['Standardized_SMILES'], "TRAINING SET", device, False) 236 | 237 | # Prepare new SMILES strings for prediction VALIDATION SET 238 | run_prediction(valid['Standardized_SMILES'], "VALIDATION SET", device, False) 239 | 240 | # Prepare new SMILES strings for prediction TEST SET 241 | run_prediction(test['Standardized_SMILES'], "TEST SET", device, True) 242 | -------------------------------------------------------------------------------- /LCW-Using-ChemBERTa-2-For-Property_Prediction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "e4fd208b-665e-4d83-861c-3c974f9a5ba4", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "# Released under MIT License\n", 11 | "#\n", 12 | "# Copyright (c) 2023 Andrew SID Lang, Oral Roberts University, U.S.A.\n", 13 | "#\n", 14 | "# Copyright (c) 2023 Jan HR Woerner, Oral Roberts University, U.S.A.\n", 15 | "#\n", 16 | "# Copyright (c) 2023 Wei-Khiong (Wyatt) Chong, Advent Polytech Co., Ltd, Taiwan.\n", 17 | "#\n", 18 | "# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n", 19 | "# documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n", 20 | "# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n", 21 | "# permit persons to whom the Software is furnished to do so, subject to the following conditions:\n", 22 | "#\n", 23 | "# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n", 24 | "# Software.\n", 25 | "#\n", 26 | "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n", 27 | "# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\n", 28 | "# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n", 29 | "# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", 30 | "\n", 31 | "import torch\n", 32 | "from torch.utils.data import Dataset\n", 33 | "from transformers import AutoConfig, AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments\n", 34 | "import pandas as pd\n", 35 | "import warnings\n", 36 | "warnings.filterwarnings(\"ignore\", message=\"Was asked to gather along dimension 0, but all input tensors were scalars\")" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "id": "948c5fef-6380-4fd2-b82d-3cb5c0b7d774", 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "class Input(Dataset):\n", 47 | " def __init__(self, i_data, i_tokenizer, i_max_length):\n", 48 | " self.data = i_data\n", 49 | " self.tokenizer = i_tokenizer\n", 50 | " self.max_length = i_max_length\n", 51 | "\n", 52 | " def __len__(self):\n", 53 | " return len(self.data)\n", 54 | "\n", 55 | " def __getitem__(self, idx):\n", 56 | " i_smiles = self.data.iloc[idx][\"Standardized_SMILES\"]\n", 57 | " i_inputs = self.tokenizer(i_smiles, return_tensors=\"pt\", padding='max_length', truncation=True, max_length=self.max_length)\n", 58 | " i_inputs[\"input_ids\"] = i_inputs[\"input_ids\"].squeeze(0)\n", 59 | " i_inputs[\"attention_mask\"] = i_inputs[\"attention_mask\"].squeeze(0)\n", 60 | " if \"token_type_ids\" in i_inputs:\n", 61 | " i_inputs[\"token_type_ids\"] = i_inputs[\"token_type_ids\"].squeeze(0)\n", 62 | " i_inputs[\"labels\"] = torch.tensor(self.data.iloc[idx][\"median_WS\"], dtype=torch.float).unsqueeze(0)\n", 63 | " return i_inputs" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 3, 69 | "id": "10d8698a-096a-4e15-b454-2f25a4a19dc3", 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "# Read in solubility data\n", 74 | "train_data = pd.read_csv('aqua_train.csv')\n", 75 | "test_data = pd.read_csv('aqua_test.csv')\n", 76 | "\n", 77 | "# pick out columns\n", 78 | "data = train_data[['Standardized_SMILES', 'median_WS']]\n", 79 | "test = test_data[['Standardized_SMILES', 'median_WS']]" 80 | ] 81 | }, 82 | { 83 | "cell_type": "code", 84 | "execution_count": 4, 85 | "id": "f290f56e-2ea6-45bc-90b7-cbe705dde78b", 86 | "metadata": {}, 87 | "outputs": [ 88 | { 89 | "name": "stderr", 90 | "output_type": "stream", 91 | "text": [ 92 | "Some weights of the model checkpoint at DeepChem/ChemBERTa-77M-MTR were not used when initializing RobertaForSequenceClassification: ['regression.out_proj.bias', 'regression.dense.bias', 'regression.dense.weight', 'norm_std', 'regression.out_proj.weight', 'norm_mean']\n", 93 | "- This IS expected if you are initializing RobertaForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", 94 | "- This IS NOT expected if you are initializing RobertaForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", 95 | "Some weights of RobertaForSequenceClassification were not initialized from the model checkpoint at DeepChem/ChemBERTa-77M-MTR and are newly initialized: ['classifier.dense.bias', 'classifier.out_proj.bias', 'classifier.dense.weight', 'classifier.out_proj.weight']\n", 96 | "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" 97 | ] 98 | }, 99 | { 100 | "name": "stdout", 101 | "output_type": "stream", 102 | "text": [ 103 | "Using GPU.\n" 104 | ] 105 | }, 106 | { 107 | "data": { 108 | "text/plain": [ 109 | "RobertaForSequenceClassification(\n", 110 | " (roberta): RobertaModel(\n", 111 | " (embeddings): RobertaEmbeddings(\n", 112 | " (word_embeddings): Embedding(600, 384, padding_idx=1)\n", 113 | " (position_embeddings): Embedding(515, 384, padding_idx=1)\n", 114 | " (token_type_embeddings): Embedding(1, 384)\n", 115 | " (LayerNorm): LayerNorm((384,), eps=1e-12, elementwise_affine=True)\n", 116 | " (dropout): Dropout(p=0.144, inplace=False)\n", 117 | " )\n", 118 | " (encoder): RobertaEncoder(\n", 119 | " (layer): ModuleList(\n", 120 | " (0-2): 3 x RobertaLayer(\n", 121 | " (attention): RobertaAttention(\n", 122 | " (self): RobertaSelfAttention(\n", 123 | " (query): Linear(in_features=384, out_features=384, bias=True)\n", 124 | " (key): Linear(in_features=384, out_features=384, bias=True)\n", 125 | " (value): Linear(in_features=384, out_features=384, bias=True)\n", 126 | " (dropout): Dropout(p=0.109, inplace=False)\n", 127 | " )\n", 128 | " (output): RobertaSelfOutput(\n", 129 | " (dense): Linear(in_features=384, out_features=384, bias=True)\n", 130 | " (LayerNorm): LayerNorm((384,), eps=1e-12, elementwise_affine=True)\n", 131 | " (dropout): Dropout(p=0.144, inplace=False)\n", 132 | " )\n", 133 | " )\n", 134 | " (intermediate): RobertaIntermediate(\n", 135 | " (dense): Linear(in_features=384, out_features=464, bias=True)\n", 136 | " (intermediate_act_fn): GELUActivation()\n", 137 | " )\n", 138 | " (output): RobertaOutput(\n", 139 | " (dense): Linear(in_features=464, out_features=384, bias=True)\n", 140 | " (LayerNorm): LayerNorm((384,), eps=1e-12, elementwise_affine=True)\n", 141 | " (dropout): Dropout(p=0.144, inplace=False)\n", 142 | " )\n", 143 | " )\n", 144 | " )\n", 145 | " )\n", 146 | " )\n", 147 | " (classifier): RobertaClassificationHead(\n", 148 | " (dense): Linear(in_features=384, out_features=384, bias=True)\n", 149 | " (dropout): Dropout(p=0.144, inplace=False)\n", 150 | " (out_proj): Linear(in_features=384, out_features=1, bias=True)\n", 151 | " )\n", 152 | ")" 153 | ] 154 | }, 155 | "execution_count": 4, 156 | "metadata": {}, 157 | "output_type": "execute_result" 158 | } 159 | ], 160 | "source": [ 161 | "# Load a pretrained transformer model and tokenizer\n", 162 | "model_name = \"DeepChem/ChemBERTa-77M-MTR\"\n", 163 | "tokenizer = AutoTokenizer.from_pretrained(model_name)\n", 164 | "config = AutoConfig.from_pretrained(model_name)\n", 165 | "config.num_hidden_layers += 1\n", 166 | "model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=1)\n", 167 | "\n", 168 | "#see if GPU\n", 169 | "if torch.cuda.is_available(): \n", 170 | " device = torch.device(\"cuda\")\n", 171 | " print(\"Using GPU.\")\n", 172 | "else:\n", 173 | " print(\"No GPU available, using the CPU instead.\")\n", 174 | " device = torch.device(\"cpu\")\n", 175 | "# move model to the device\n", 176 | "model.to(device) " 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 5, 182 | "id": "513419d0-426e-4fcd-869e-7cec0f16e078", 183 | "metadata": {}, 184 | "outputs": [ 185 | { 186 | "data": { 187 | "text/html": [ 188 | "\n", 189 | "
\n", 190 | " \n", 191 | " \n", 192 | " [2300/2300 09:37, Epoch 100/100]\n", 193 | "
\n", 194 | " \n", 195 | " \n", 196 | " \n", 197 | " \n", 198 | " \n", 199 | " \n", 200 | " \n", 201 | " \n", 202 | " \n", 203 | " \n", 204 | " \n", 205 | " \n", 206 | " \n", 207 | " \n", 208 | " \n", 209 | " \n", 210 | " \n", 211 | " \n", 212 | " \n", 213 | " \n", 214 | " \n", 215 | " \n", 216 | " \n", 217 | " \n", 218 | " \n", 219 | " \n", 220 | " \n", 221 | " \n", 222 | " \n", 223 | " \n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | " \n", 228 | " \n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | "
StepTraining Loss
1006.768000
2001.259400
3001.048200
4000.960900
5000.910400
6000.857300
7000.819500
8000.794000
9000.781700
10000.757900
11000.744500
12000.724800
13000.711800
14000.708500
15000.701500
16000.684800
17000.681700
18000.671000
19000.661500
20000.660700
21000.657900
22000.660900
23000.656800

" 296 | ], 297 | "text/plain": [ 298 | "" 299 | ] 300 | }, 301 | "metadata": {}, 302 | "output_type": "display_data" 303 | } 304 | ], 305 | "source": [ 306 | "# max length of SMILES over both sets\n", 307 | "max_length = 195\n", 308 | "# Prepare the dataset for training\n", 309 | "train_dataset = Input(data, tokenizer,max_length)\n", 310 | "\n", 311 | "# Set up training arguments\n", 312 | "training_args = TrainingArguments(\n", 313 | " output_dir=\"./output\",\n", 314 | " num_train_epochs=100, # Number of training epochs\n", 315 | " per_device_train_batch_size=86, # Batch size\n", 316 | " logging_steps=100, # Log training metrics every 100 steps\n", 317 | " optim=\"adamw_torch\", # switch optimizer to avoid warning\n", 318 | " seed=123, # Set a random seed for reproducibility\n", 319 | ")\n", 320 | "\n", 321 | " \n", 322 | "# Train the model\n", 323 | "trainer = Trainer(model=model, args=training_args, train_dataset=train_dataset,)\n", 324 | "trainer.train() \n", 325 | "trainer.save_model(\"./output\") # save model to output folder" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 10, 331 | "id": "d3ad4bb7-baa3-49f1-a041-7d27159a0cb7", 332 | "metadata": {}, 333 | "outputs": [], 334 | "source": [ 335 | "from transformers import pipeline\n", 336 | "# Create a prediction pipeline\n", 337 | "\n", 338 | "predictor = pipeline(\"text-classification\", model=model, tokenizer=tokenizer)\n", 339 | "\n", 340 | "# Prepare new SMILES strings for prediction\n", 341 | "test_smiles = test['Standardized_SMILES']\n", 342 | "\n", 343 | "# Predict properties for new SMILES strings\n", 344 | "predictions = []\n", 345 | "for smiles in test_smiles:\n", 346 | " inputs = tokenizer(smiles, return_tensors=\"pt\", padding='max_length', truncation=True, max_length=195).to(device)\n", 347 | " with torch.no_grad():\n", 348 | " outputs = model(**inputs)\n", 349 | " predicted_property = outputs.logits.squeeze().item()\n", 350 | " predictions.append(predicted_property)" 351 | ] 352 | }, 353 | { 354 | "cell_type": "code", 355 | "execution_count": 11, 356 | "id": "4eea13b3-f35f-4db9-a1a9-61f2d16faceb", 357 | "metadata": {}, 358 | "outputs": [ 359 | { 360 | "name": "stdout", 361 | "output_type": "stream", 362 | "text": [ 363 | "TEST SET\n", 364 | "N: 2552\n", 365 | "R2: 0.8054362061353308\n", 366 | "Root Mean Square Error: 1.0125407666739181\n", 367 | "Mean Absolute Error: 0.716880692248506\n", 368 | "Spearman correlation: 0.8995523590904071\n", 369 | "p-value: 0.0\n" 370 | ] 371 | }, 372 | { 373 | "data": { 374 | "image/png": "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\n", 375 | "text/plain": [ 376 | "

" 377 | ] 378 | }, 379 | "metadata": { 380 | "needs_background": "light" 381 | }, 382 | "output_type": "display_data" 383 | } 384 | ], 385 | "source": [ 386 | "from sklearn.metrics import mean_squared_error\n", 387 | "from sklearn.metrics import r2_score\n", 388 | "from sklearn.metrics import mean_absolute_error\n", 389 | "\n", 390 | "r_mse = mean_squared_error(test[\"median_WS\"], predictions, squared=False)\n", 391 | "r2 = r2_score(test[\"median_WS\"], predictions)\n", 392 | "mae = mean_absolute_error(test[\"median_WS\"], predictions)\n", 393 | "\n", 394 | "print(\"TEST SET\")\n", 395 | "print(\"N:\", len(test[\"median_WS\"]))\n", 396 | "print(\"R2:\", r2)\n", 397 | "print(\"Root Mean Square Error:\", r_mse)\n", 398 | "print(\"Mean Absolute Error:\", mae)\n", 399 | "\n", 400 | "from scipy.stats import spearmanr\n", 401 | "\n", 402 | "# assume test and predictions are two arrays of the same length\n", 403 | "correlation, p_value = spearmanr(test[\"median_WS\"], predictions)\n", 404 | "\n", 405 | "print(\"Spearman correlation:\", correlation)\n", 406 | "print(\"p-value:\", p_value)\n", 407 | "\n", 408 | "import matplotlib.pyplot as plt\n", 409 | "\n", 410 | "plt.scatter(test[\"median_WS\"], predictions)\n", 411 | "plt.xlabel(\"Test_Set Median Water Solubility\")\n", 412 | "plt.ylabel(\"Predictions\")\n", 413 | "plt.title(\"Scatter Plot of Test_Set Median Water Solubility vs Predictions\")\n", 414 | "plt.show()" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": 12, 420 | "id": "36823b0f-4d30-4536-a182-7041c3e56642", 421 | "metadata": {}, 422 | "outputs": [], 423 | "source": [ 424 | "# Prepare new SMILES strings for prediction\n", 425 | "train_smiles = data['Standardized_SMILES']\n", 426 | "\n", 427 | "# Predict properties for new SMILES strings\n", 428 | "train_predictions = []\n", 429 | "for smiles in train_smiles:\n", 430 | " inputs = tokenizer(smiles, return_tensors=\"pt\", padding='max_length', truncation=True, max_length=195).to(device) \n", 431 | " with torch.no_grad():\n", 432 | " outputs = model(**inputs)\n", 433 | " predicted_property = outputs.logits.squeeze().item()\n", 434 | " train_predictions.append(predicted_property)" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": 13, 440 | "id": "b9115caa-d8d9-44e9-9abe-941d082f6ce2", 441 | "metadata": {}, 442 | "outputs": [ 443 | { 444 | "name": "stdout", 445 | "output_type": "stream", 446 | "text": [ 447 | "TRAINING SET\n", 448 | "N: 7655\n", 449 | "R2: 0.871320792914473\n", 450 | "Root Mean Square Error: 0.8098064745023944\n", 451 | "Mean Absolute Error: 0.595570092893068\n", 452 | "Spearman correlation: 0.9277674381474756\n", 453 | "p-value: 0.0\n" 454 | ] 455 | }, 456 | { 457 | "data": { 458 | "image/png": "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\n", 459 | "text/plain": [ 460 | "
" 461 | ] 462 | }, 463 | "metadata": { 464 | "needs_background": "light" 465 | }, 466 | "output_type": "display_data" 467 | } 468 | ], 469 | "source": [ 470 | "rmse = mean_squared_error(data[\"median_WS\"], train_predictions, squared=False)\n", 471 | "r2 = r2_score(data[\"median_WS\"], train_predictions)\n", 472 | "mae = mean_absolute_error(data[\"median_WS\"], train_predictions)\n", 473 | "\n", 474 | "print(\"TRAINING SET\")\n", 475 | "print(\"N:\", len(data[\"median_WS\"]))\n", 476 | "print(\"R2:\", r2)\n", 477 | "print(\"Root Mean Square Error:\", rmse)\n", 478 | "print(\"Mean Absolute Error:\", mae)\n", 479 | "\n", 480 | "# assume test and predictions are two arrays of the same length\n", 481 | "correlation, p_value = spearmanr(data[\"median_WS\"], train_predictions)\n", 482 | "\n", 483 | "print(\"Spearman correlation:\", correlation)\n", 484 | "print(\"p-value:\", p_value)\n", 485 | "\n", 486 | "import matplotlib.pyplot as plt\n", 487 | "\n", 488 | "plt.scatter(data[\"median_WS\"], train_predictions)\n", 489 | "plt.xlabel(\"Training_Set Median Water Solubility\")\n", 490 | "plt.ylabel(\"Predictions\")\n", 491 | "plt.title(\"Scatter Plot of Training_Set Median Water Solubility vs Predictions\")\n", 492 | "plt.show()" 493 | ] 494 | }, 495 | { 496 | "cell_type": "code", 497 | "execution_count": null, 498 | "id": "e4544830-cc4f-4eee-a692-cfd99cd9072e", 499 | "metadata": {}, 500 | "outputs": [], 501 | "source": [] 502 | } 503 | ], 504 | "metadata": { 505 | "kernelspec": { 506 | "display_name": "Python 3 (ipykernel)", 507 | "language": "python", 508 | "name": "python3" 509 | }, 510 | "language_info": { 511 | "codemirror_mode": { 512 | "name": "ipython", 513 | "version": 3 514 | }, 515 | "file_extension": ".py", 516 | "mimetype": "text/x-python", 517 | "name": "python", 518 | "nbconvert_exporter": "python", 519 | "pygments_lexer": "ipython3", 520 | "version": "3.9.5" 521 | } 522 | }, 523 | "nbformat": 4, 524 | "nbformat_minor": 5 525 | } 526 | -------------------------------------------------------------------------------- /LCW-Fine-Tuning-ChemBERTa-2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "e4fd208b-665e-4d83-861c-3c974f9a5ba4", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "# Released under MIT License\n", 11 | "#\n", 12 | "# Copyright (c) 2023 Andrew SID Lang, Oral Roberts University, U.S.A.\n", 13 | "#\n", 14 | "# Copyright (c) 2023 Jan HR Woerner, Oral Roberts University, U.S.A.\n", 15 | "#\n", 16 | "# Copyright (c) 2023 Wei-Khiong (Wyatt) Chong, Advent Polytech Co., Ltd, Taiwan.\n", 17 | "#\n", 18 | "# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated\n", 19 | "# documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the\n", 20 | "# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to\n", 21 | "# permit persons to whom the Software is furnished to do so, subject to the following conditions:\n", 22 | "#\n", 23 | "# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the\n", 24 | "# Software.\n", 25 | "#\n", 26 | "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO\n", 27 | "# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS\n", 28 | "# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n", 29 | "# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", 30 | "\n", 31 | "import torch\n", 32 | "from torch.utils.data import Dataset\n", 33 | "from transformers import AutoConfig, AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments, TrainerCallback\n", 34 | "import pandas as pd\n", 35 | "import warnings\n", 36 | "warnings.filterwarnings(\"ignore\", message=\"Was asked to gather along dimension 0, but all input tensors were scalars\")" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 2, 42 | "id": "5d899440-4b67-43fe-bad7-555924feeed4", 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "class MyData:\n", 47 | " def __init__(self, i_data):\n", 48 | " self.data = i_data\n", 49 | " \n", 50 | " def get_split(self, train_ratio=0.8, valid_ratio=0.1, seed=None):\n", 51 | " n = len(self.data)\n", 52 | " indices = np.arange(n)\n", 53 | " if seed is not None:\n", 54 | " np.random.seed(seed)\n", 55 | " np.random.shuffle(indices)\n", 56 | " train_size = int(train_ratio * n)\n", 57 | " valid_size = int(valid_ratio * n)\n", 58 | " train_indices = indices[:train_size]\n", 59 | " valid_indices = indices[train_size:train_size+valid_size]\n", 60 | " test_indices = indices[train_size+valid_size:]\n", 61 | " i_train_data = self.data.iloc[train_indices].reset_index(drop=True)\n", 62 | " i_valid_data = self.data.iloc[valid_indices].reset_index(drop=True)\n", 63 | " i_test_data = self.data.iloc[test_indices].reset_index(drop=True)\n", 64 | " return i_train_data, i_valid_data, i_test_data" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 3, 70 | "id": "948c5fef-6380-4fd2-b82d-3cb5c0b7d774", 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "class Input(Dataset):\n", 75 | " def __init__(self, i_data, i_tokenizer, i_max_length):\n", 76 | " self.data = i_data\n", 77 | " self.tokenizer = i_tokenizer\n", 78 | " self.max_length = i_max_length\n", 79 | "\n", 80 | " def __len__(self):\n", 81 | " return len(self.data)\n", 82 | "\n", 83 | " def __getitem__(self, idx):\n", 84 | " smiles = self.data.iloc[idx][\"Standardized_SMILES\"]\n", 85 | " inputs = self.tokenizer(smiles, return_tensors=\"pt\", padding='max_length', truncation=True, max_length=self.max_length)\n", 86 | " inputs[\"input_ids\"] = inputs[\"input_ids\"].squeeze(0)\n", 87 | " inputs[\"attention_mask\"] = inputs[\"attention_mask\"].squeeze(0)\n", 88 | " if \"token_type_ids\" in inputs:\n", 89 | " inputs[\"token_type_ids\"] = inputs[\"token_type_ids\"].squeeze(0)\n", 90 | " inputs[\"labels\"] = torch.tensor(self.data.iloc[idx][\"median_WS\"], dtype=torch.float).unsqueeze(0)\n", 91 | " return inputs" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 4, 97 | "id": "d2f6243f-76f2-463d-8087-ac80742fb0a3", 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "# Read in solubility data\n", 102 | "my_data = pd.read_csv('aqua.csv')\n", 103 | "\n", 104 | "# Create an instance of the MyData class\n", 105 | "my_data = MyData(my_data)\n", 106 | "\n", 107 | "# Split your data into training, validation, and testing sets\n", 108 | "train_data, valid_data, test_data = my_data.get_split(seed = 123)\n", 109 | "\n", 110 | "# pick out columns\n", 111 | "data = train_data[['Standardized_SMILES', 'median_WS']]\n", 112 | "valid = valid_data[['Standardized_SMILES', 'median_WS']]\n", 113 | "test = test_data[['Standardized_SMILES', 'median_WS']]" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "id": "f290f56e-2ea6-45bc-90b7-cbe705dde78b", 120 | "metadata": {}, 121 | "outputs": [ 122 | { 123 | "name": "stderr", 124 | "output_type": "stream", 125 | "text": [ 126 | "Some weights of the model checkpoint at DeepChem/ChemBERTa-77M-MTR were not used when initializing RobertaForSequenceClassification: ['regression.dense.weight', 'regression.out_proj.weight', 'regression.out_proj.bias', 'norm_mean', 'norm_std', 'regression.dense.bias']\n", 127 | "- This IS expected if you are initializing RobertaForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).\n", 128 | "- This IS NOT expected if you are initializing RobertaForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).\n", 129 | "Some weights of RobertaForSequenceClassification were not initialized from the model checkpoint at DeepChem/ChemBERTa-77M-MTR and are newly initialized: ['classifier.dense.bias', 'classifier.out_proj.weight', 'classifier.dense.weight', 'classifier.out_proj.bias']\n", 130 | "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" 131 | ] 132 | }, 133 | { 134 | "name": "stdout", 135 | "output_type": "stream", 136 | "text": [ 137 | "Using GPU.\n" 138 | ] 139 | }, 140 | { 141 | "data": { 142 | "text/plain": [ 143 | "RobertaForSequenceClassification(\n", 144 | " (roberta): RobertaModel(\n", 145 | " (embeddings): RobertaEmbeddings(\n", 146 | " (word_embeddings): Embedding(600, 384, padding_idx=1)\n", 147 | " (position_embeddings): Embedding(515, 384, padding_idx=1)\n", 148 | " (token_type_embeddings): Embedding(1, 384)\n", 149 | " (LayerNorm): LayerNorm((384,), eps=1e-12, elementwise_affine=True)\n", 150 | " (dropout): Dropout(p=0.144, inplace=False)\n", 151 | " )\n", 152 | " (encoder): RobertaEncoder(\n", 153 | " (layer): ModuleList(\n", 154 | " (0-2): 3 x RobertaLayer(\n", 155 | " (attention): RobertaAttention(\n", 156 | " (self): RobertaSelfAttention(\n", 157 | " (query): Linear(in_features=384, out_features=384, bias=True)\n", 158 | " (key): Linear(in_features=384, out_features=384, bias=True)\n", 159 | " (value): Linear(in_features=384, out_features=384, bias=True)\n", 160 | " (dropout): Dropout(p=0.109, inplace=False)\n", 161 | " )\n", 162 | " (output): RobertaSelfOutput(\n", 163 | " (dense): Linear(in_features=384, out_features=384, bias=True)\n", 164 | " (LayerNorm): LayerNorm((384,), eps=1e-12, elementwise_affine=True)\n", 165 | " (dropout): Dropout(p=0.144, inplace=False)\n", 166 | " )\n", 167 | " )\n", 168 | " (intermediate): RobertaIntermediate(\n", 169 | " (dense): Linear(in_features=384, out_features=464, bias=True)\n", 170 | " (intermediate_act_fn): GELUActivation()\n", 171 | " )\n", 172 | " (output): RobertaOutput(\n", 173 | " (dense): Linear(in_features=464, out_features=384, bias=True)\n", 174 | " (LayerNorm): LayerNorm((384,), eps=1e-12, elementwise_affine=True)\n", 175 | " (dropout): Dropout(p=0.144, inplace=False)\n", 176 | " )\n", 177 | " )\n", 178 | " )\n", 179 | " )\n", 180 | " )\n", 181 | " (classifier): RobertaClassificationHead(\n", 182 | " (dense): Linear(in_features=384, out_features=384, bias=True)\n", 183 | " (dropout): Dropout(p=0.144, inplace=False)\n", 184 | " (out_proj): Linear(in_features=384, out_features=1, bias=True)\n", 185 | " )\n", 186 | ")" 187 | ] 188 | }, 189 | "execution_count": 5, 190 | "metadata": {}, 191 | "output_type": "execute_result" 192 | } 193 | ], 194 | "source": [ 195 | "#AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=1)\n", 196 | "\n", 197 | "# Load a pretrained transformer model and tokenizer\n", 198 | "model_name = \"DeepChem/ChemBERTa-77M-MTR\"\n", 199 | "tokenizer = AutoTokenizer.from_pretrained(model_name)\n", 200 | "config = AutoConfig.from_pretrained(model_name)\n", 201 | "config.num_hidden_layers += 1\n", 202 | "model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=1)\n", 203 | "\n", 204 | "#see if GPU\n", 205 | "if torch.cuda.is_available(): \n", 206 | " device = torch.device(\"cuda\")\n", 207 | " print(\"Using GPU.\")\n", 208 | "else:\n", 209 | " print(\"No GPU available, using the CPU instead.\")\n", 210 | " device = torch.device(\"cpu\")\n", 211 | "# move model to the device\n", 212 | "model.to(device) " 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": 6, 218 | "id": "513419d0-426e-4fcd-869e-7cec0f16e078", 219 | "metadata": {}, 220 | "outputs": [ 221 | { 222 | "data": { 223 | "text/html": [ 224 | "\n", 225 | "
\n", 226 | " \n", 227 | " \n", 228 | " [1600/1600 11:03, Epoch 100/100]\n", 229 | "
\n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | " \n", 294 | " \n", 295 | " \n", 296 | " \n", 297 | " \n", 298 | " \n", 299 | " \n", 300 | " \n", 301 | " \n", 302 | " \n", 303 | " \n", 304 | " \n", 305 | " \n", 306 | " \n", 307 | " \n", 308 | " \n", 309 | " \n", 310 | " \n", 311 | " \n", 312 | " \n", 313 | " \n", 314 | " \n", 315 | " \n", 316 | " \n", 317 | " \n", 318 | " \n", 319 | " \n", 320 | " \n", 321 | " \n", 322 | " \n", 323 | " \n", 324 | " \n", 325 | " \n", 326 | " \n", 327 | " \n", 328 | " \n", 329 | " \n", 330 | " \n", 331 | " \n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 414 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | " \n", 443 | " \n", 444 | " \n", 445 | " \n", 446 | " \n", 447 | " \n", 448 | " \n", 449 | " \n", 450 | " \n", 451 | " \n", 452 | " \n", 453 | " \n", 454 | " \n", 455 | " \n", 456 | " \n", 457 | " \n", 458 | " \n", 459 | " \n", 460 | " \n", 461 | " \n", 462 | " \n", 463 | " \n", 464 | " \n", 465 | " \n", 466 | " \n", 467 | " \n", 468 | " \n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " \n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | " \n", 544 | " \n", 545 | " \n", 546 | " \n", 547 | " \n", 548 | " \n", 549 | " \n", 550 | " \n", 551 | " \n", 552 | " \n", 553 | " \n", 554 | " \n", 555 | " \n", 556 | " \n", 557 | " \n", 558 | " \n", 559 | " \n", 560 | " \n", 561 | " \n", 562 | " \n", 563 | " \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | " \n", 568 | " \n", 569 | " \n", 570 | " \n", 571 | " \n", 572 | " \n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | " \n", 631 | " \n", 632 | " \n", 633 | " \n", 634 | " \n", 635 | " \n", 636 | " \n", 637 | " \n", 638 | " \n", 639 | " \n", 640 | " \n", 641 | " \n", 642 | " \n", 643 | " \n", 644 | " \n", 645 | " \n", 646 | " \n", 647 | " \n", 648 | " \n", 649 | " \n", 650 | " \n", 651 | " \n", 652 | " \n", 653 | " \n", 654 | " \n", 655 | " \n", 656 | " \n", 657 | " \n", 658 | " \n", 659 | " \n", 660 | " \n", 661 | " \n", 662 | " \n", 663 | " \n", 664 | " \n", 665 | " \n", 666 | " \n", 667 | " \n", 668 | " \n", 669 | " \n", 670 | " \n", 671 | " \n", 672 | " \n", 673 | " \n", 674 | " \n", 675 | " \n", 676 | " \n", 677 | " \n", 678 | " \n", 679 | " \n", 680 | " \n", 681 | " \n", 682 | " \n", 683 | " \n", 684 | " \n", 685 | " \n", 686 | " \n", 687 | " \n", 688 | " \n", 689 | " \n", 690 | " \n", 691 | " \n", 692 | " \n", 693 | " \n", 694 | " \n", 695 | " \n", 696 | " \n", 697 | " \n", 698 | " \n", 699 | " \n", 700 | " \n", 701 | " \n", 702 | " \n", 703 | " \n", 704 | " \n", 705 | " \n", 706 | " \n", 707 | " \n", 708 | " \n", 709 | " \n", 710 | " \n", 711 | " \n", 712 | " \n", 713 | " \n", 714 | " \n", 715 | " \n", 716 | " \n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | " \n", 743 | " \n", 744 | " \n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 817 | " \n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 822 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 827 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | " \n", 843 | " \n", 844 | " \n", 845 | " \n", 846 | " \n", 847 | " \n", 848 | " \n", 849 | " \n", 850 | " \n", 851 | " \n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " \n", 856 | " \n", 857 | " \n", 858 | " \n", 859 | " \n", 860 | " \n", 861 | " \n", 862 | " \n", 863 | " \n", 864 | " \n", 865 | " \n", 866 | " \n", 867 | " \n", 868 | " \n", 869 | " \n", 870 | " \n", 871 | " \n", 872 | " \n", 873 | " \n", 874 | " \n", 875 | " \n", 876 | " \n", 877 | " \n", 878 | " \n", 879 | " \n", 880 | " \n", 881 | " \n", 882 | " \n", 883 | " \n", 884 | " \n", 885 | " \n", 886 | " \n", 887 | " \n", 888 | " \n", 889 | " \n", 890 | " \n", 891 | " \n", 892 | " \n", 893 | " \n", 894 | " \n", 895 | " \n", 896 | " \n", 897 | " \n", 898 | " \n", 899 | " \n", 900 | " \n", 901 | " \n", 902 | " \n", 903 | " \n", 904 | " \n", 905 | " \n", 906 | " \n", 907 | " \n", 908 | " \n", 909 | " \n", 910 | " \n", 911 | " \n", 912 | " \n", 913 | " \n", 914 | " \n", 915 | " \n", 916 | " \n", 917 | " \n", 918 | " \n", 919 | " \n", 920 | " \n", 921 | " \n", 922 | " \n", 923 | " \n", 924 | " \n", 925 | " \n", 926 | " \n", 927 | " \n", 928 | " \n", 929 | " \n", 930 | " \n", 931 | " \n", 932 | " \n", 933 | " \n", 934 | " \n", 935 | " \n", 936 | " \n", 937 | " \n", 938 | " \n", 939 | " \n", 940 | " \n", 941 | " \n", 942 | " \n", 943 | " \n", 944 | " \n", 945 | " \n", 946 | " \n", 947 | " \n", 948 | " \n", 949 | " \n", 950 | " \n", 951 | " \n", 952 | " \n", 953 | " \n", 954 | " \n", 955 | " \n", 956 | " \n", 957 | " \n", 958 | " \n", 959 | " \n", 960 | " \n", 961 | " \n", 962 | " \n", 963 | " \n", 964 | " \n", 965 | " \n", 966 | " \n", 967 | " \n", 968 | " \n", 969 | " \n", 970 | " \n", 971 | " \n", 972 | " \n", 973 | " \n", 974 | " \n", 975 | " \n", 976 | " \n", 977 | " \n", 978 | " \n", 979 | " \n", 980 | " \n", 981 | " \n", 982 | " \n", 983 | " \n", 984 | " \n", 985 | " \n", 986 | " \n", 987 | " \n", 988 | " \n", 989 | " \n", 990 | " \n", 991 | " \n", 992 | " \n", 993 | " \n", 994 | " \n", 995 | " \n", 996 | " \n", 997 | " \n", 998 | " \n", 999 | " \n", 1000 | " \n", 1001 | " \n", 1002 | " \n", 1003 | " \n", 1004 | " \n", 1005 | " \n", 1006 | " \n", 1007 | " \n", 1008 | " \n", 1009 | " \n", 1010 | " \n", 1011 | " \n", 1012 | " \n", 1013 | " \n", 1014 | " \n", 1015 | " \n", 1016 | " \n", 1017 | " \n", 1018 | " \n", 1019 | " \n", 1020 | " \n", 1021 | " \n", 1022 | " \n", 1023 | " \n", 1024 | " \n", 1025 | " \n", 1026 | " \n", 1027 | " \n", 1028 | " \n", 1029 | " \n", 1030 | " \n", 1031 | " \n", 1032 | " \n", 1033 | " \n", 1034 | " \n", 1035 | " \n", 1036 | " \n", 1037 | " \n", 1038 | " \n", 1039 | " \n", 1040 | "
StepTraining LossValidation Loss
1014.49850013.903141
2013.31250012.314187
3011.3317009.889343
408.8333006.695904
505.9927004.188548
604.1795003.679473
703.3948002.733828
802.6909002.194749
902.1848001.792031
1001.9166001.556690
1101.6070001.459312
1201.4609001.370523
1301.3925001.330363
1401.2731001.267703
1501.3301001.248693
1601.2054001.236013
1701.1710001.196895
1801.2225001.181159
1901.1322001.157773
2001.1120001.164091
2101.1167001.133514
2201.1236001.133295
2301.0493001.128167
2401.0811001.099577
2501.0824001.096789
2600.9867001.089766
2701.0498001.079090
2801.0274001.067783
2901.0011001.057811
3001.0063001.100570
3100.9958001.029109
3201.0064001.044250
3301.0225001.036337
3400.9642001.032447
3500.9618001.010790
3600.9727001.015232
3700.9902001.014361
3800.9370001.014328
3900.9167001.013889
4000.9608000.983203
4100.9571000.980814
4200.9205000.981581
4300.9354000.983257
4400.8955000.974684
4500.9294000.982432
4600.9262000.971062
4700.9049000.969826
4800.9017000.960682
4900.9368000.976769
5000.8808000.964954
5100.9036000.949082
5200.9097000.966911
5300.8355000.936071
5400.9125000.960301
5500.8818000.935671
5600.8624000.946876
5700.8508000.935555
5800.9047000.928763
5900.8761000.926278
6000.8518000.938022
6100.8859000.917941
6200.8761000.938434
6300.8433000.916394
6400.8366000.931034
6500.8551000.909021
6600.8451000.922578
6700.8425000.908290
6800.8610000.914329
6900.8254000.920769
7000.8277000.896255
7100.8434000.894783
7200.8193000.904708
7300.8170000.909307
7400.8046000.910911
7500.8334000.887669
7600.8167000.900026
7700.8294000.894980
7800.8009000.889096
7900.8044000.896905
8000.8029000.894861
8100.8297000.900829
8200.7979000.890006
8300.7856000.893044
8400.7854000.884947
8500.8031000.903455
8600.7813000.896731
8700.7941000.903011
8800.7994000.877687
8900.7959000.900194
9000.7949000.883582
9100.7847000.887545
9200.7801000.894659
9300.7707000.909069
9400.7759000.883565
9500.8200000.876190
9600.7659000.899084
9700.7636000.884935
9800.7802000.904533
9900.7738000.880672
10000.7754000.875259
10100.7567000.905909
10200.7661000.888669
10300.7523000.898266
10400.7826000.879773
10500.7493000.881599
10600.7850000.883525
10700.7195000.879436
10800.7968000.876413
10900.7442000.889036
11000.7744000.877228
11100.7509000.879265
11200.7590000.879474
11300.7231000.883581
11400.7979000.891133
11500.7259000.875201
11600.7631000.874155
11700.7286000.876161
11800.7651000.879099
11900.7441000.873605
12000.7592000.879335
12100.7704000.874104
12200.7210000.877102
12300.7536000.882011
12400.7492000.874405
12500.7401000.882769
12600.7593000.884418
12700.7265000.873274
12800.7464000.872972
12900.7682000.877476
13000.7047000.882903
13100.7474000.873165
13200.7202000.869180
13300.7886000.879084
13400.7152000.875005
13500.7080000.871279
13600.7599000.879987
13700.7391000.873613
13800.7508000.866602
13900.7474000.874252
14000.7535000.870742
14100.7340000.870454
14200.7501000.865231
14300.7710000.871395
14400.7178000.876452
14500.7427000.872083
14600.7150000.867632
14700.7325000.870068
14800.7231000.870656
14900.7464000.871050
15000.7384000.869421
15100.7432000.867668
15200.7276000.871007
15300.7134000.874482
15400.6977000.874198
15500.7375000.873622
15600.7382000.872050
15700.7352000.872152
15800.7259000.872730
15900.7275000.872686
16000.7151000.872661

" 1041 | ], 1042 | "text/plain": [ 1043 | "" 1044 | ] 1045 | }, 1046 | "metadata": {}, 1047 | "output_type": "display_data" 1048 | } 1049 | ], 1050 | "source": [ 1051 | "from transformers import TrainerCallback\n", 1052 | "import numpy as np\n", 1053 | "import evaluate\n", 1054 | "\n", 1055 | "# Define the maximum sequence length\n", 1056 | "max_length = 195\n", 1057 | "\n", 1058 | "# Prepare the dataset for training\n", 1059 | "train_dataset = Input(data, tokenizer, max_length)\n", 1060 | "validation_dataset = Input(valid, tokenizer, max_length)\n", 1061 | "\n", 1062 | "# Set up training arguments\n", 1063 | "training_args = TrainingArguments(\n", 1064 | " output_dir=\"./output\",\n", 1065 | " optim=\"adamw_torch\", # switch optimizer to avoid warning\n", 1066 | " num_train_epochs=100, # Train the model for 100 epochs\n", 1067 | " per_device_train_batch_size=128, # Set the batch size to 128\n", 1068 | " per_device_eval_batch_size=128, # Set the evaluation batch size to 128\n", 1069 | " logging_steps=10, # Log training metrics every 100 steps\n", 1070 | " eval_steps=10, # Evaluate the model every 100 steps\n", 1071 | " save_steps=10, # Save the model every 100 steps\n", 1072 | " seed=123, # Set the random seed for reproducibility\n", 1073 | " evaluation_strategy=\"steps\", # Evaluate the model every eval_steps steps\n", 1074 | " load_best_model_at_end=True\n", 1075 | ")\n", 1076 | "\n", 1077 | "# Train the model\n", 1078 | "trainer = Trainer(\n", 1079 | " model=model,\n", 1080 | " args=training_args,\n", 1081 | " train_dataset=train_dataset,\n", 1082 | " eval_dataset=validation_dataset,\n", 1083 | ")\n", 1084 | "\n", 1085 | "\n", 1086 | "# Define a callback for printing validation loss\n", 1087 | "class PrintValidationLossCallback(TrainerCallback):\n", 1088 | " def on_evaluate(self, args, state, control, **kwargs):\n", 1089 | " if state is not None and hasattr(state, 'eval_loss'):\n", 1090 | " print(f\"Validation loss: {state.eval_loss:.4f}\")\n", 1091 | "\n", 1092 | "# Add the callback to the trainer\n", 1093 | "trainer.add_callback(PrintValidationLossCallback())\n", 1094 | "\n", 1095 | "\n", 1096 | "\n", 1097 | "metric = evaluate.load(\"accuracy\")\n", 1098 | "\n", 1099 | "def compute_metrics(eval_pred):\n", 1100 | " logits, labels = eval_pred\n", 1101 | " predictions = np.argmax(logits, axis=-1)\n", 1102 | " return metric.compute(predictions=predictions, references=labels)\n", 1103 | "\n", 1104 | "# Train the model\n", 1105 | "trainer.train()\n", 1106 | "\n", 1107 | "# Save the model\n", 1108 | "trainer.save_model(\"./output\")" 1109 | ] 1110 | }, 1111 | { 1112 | "cell_type": "code", 1113 | "execution_count": 7, 1114 | "id": "8b2bc259-724d-42bb-8756-670d7ba21854", 1115 | "metadata": {}, 1116 | "outputs": [], 1117 | "source": [ 1118 | "from sklearn.metrics import mean_squared_error\n", 1119 | "from sklearn.metrics import r2_score\n", 1120 | "from sklearn.metrics import mean_absolute_error\n", 1121 | "from scipy.stats import spearmanr\n", 1122 | "import matplotlib.pyplot as plt\n", 1123 | "from transformers import pipeline\n", 1124 | "\n", 1125 | "# Create a prediction pipeline\n", 1126 | "predictor = pipeline(\"text-classification\", model=model, tokenizer=tokenizer)" 1127 | ] 1128 | }, 1129 | { 1130 | "cell_type": "code", 1131 | "execution_count": 8, 1132 | "id": "dd930758-128a-4097-811e-76ff0fba6c7c", 1133 | "metadata": {}, 1134 | "outputs": [ 1135 | { 1136 | "name": "stdout", 1137 | "output_type": "stream", 1138 | "text": [ 1139 | "TRAINING SET\n", 1140 | "N: 8165\n", 1141 | "R2: 0.8702982901268843\n", 1142 | "Root Mean Square Error: 0.8173305996108375\n", 1143 | "Mean Absolute Error: 0.5987025186064614\n", 1144 | "Spearman correlation: 0.930919173416945\n", 1145 | "p-value: 0.0\n" 1146 | ] 1147 | }, 1148 | { 1149 | "data": { 1150 | "image/png": "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\n", 1151 | "text/plain": [ 1152 | "

" 1153 | ] 1154 | }, 1155 | "metadata": { 1156 | "needs_background": "light" 1157 | }, 1158 | "output_type": "display_data" 1159 | } 1160 | ], 1161 | "source": [ 1162 | "# Prepare new SMILES strings for prediction\n", 1163 | "train_smiles = data['Standardized_SMILES']\n", 1164 | "\n", 1165 | "# Predict properties for new SMILES strings\n", 1166 | "predictions = []\n", 1167 | "for smiles in train_smiles:\n", 1168 | " inputs = tokenizer(smiles, return_tensors=\"pt\", padding='max_length', truncation=True, max_length=195).to(device) #max_length=195\n", 1169 | " with torch.no_grad():\n", 1170 | " outputs = model(**inputs)\n", 1171 | " predicted_property = outputs.logits.squeeze().item()\n", 1172 | " predictions.append(predicted_property)\n", 1173 | "\n", 1174 | "r_mse = mean_squared_error(data[\"median_WS\"], predictions, squared=False)\n", 1175 | "r2 = r2_score(data[\"median_WS\"], predictions)\n", 1176 | "mae = mean_absolute_error(data[\"median_WS\"], predictions)\n", 1177 | "\n", 1178 | "print(\"TRAINING SET\")\n", 1179 | "print(\"N:\", len(data[\"median_WS\"]))\n", 1180 | "print(\"R2:\", r2)\n", 1181 | "print(\"Root Mean Square Error:\", r_mse)\n", 1182 | "print(\"Mean Absolute Error:\", mae)\n", 1183 | "\n", 1184 | "# assume test and predictions are two arrays of the same length\n", 1185 | "correlation, p_value = spearmanr(data[\"median_WS\"], predictions)\n", 1186 | "\n", 1187 | "print(\"Spearman correlation:\", correlation)\n", 1188 | "print(\"p-value:\", p_value)\n", 1189 | "\n", 1190 | "plt.scatter(data[\"median_WS\"], predictions)\n", 1191 | "plt.xlabel(\"train['median_WS']\")\n", 1192 | "plt.ylabel(\"predictions\")\n", 1193 | "plt.title(\"Scatter Plot of Training['median_WS'] vs Predictions\")\n", 1194 | "plt.show()" 1195 | ] 1196 | }, 1197 | { 1198 | "cell_type": "code", 1199 | "execution_count": 9, 1200 | "id": "6cf01e38-b1ad-42d8-b18f-10722d1a4e98", 1201 | "metadata": {}, 1202 | "outputs": [ 1203 | { 1204 | "name": "stdout", 1205 | "output_type": "stream", 1206 | "text": [ 1207 | "VALIDATION SET\n", 1208 | "N: 1020\n", 1209 | "R2: 0.8346196871525771\n", 1210 | "Root Mean Square Error: 0.9301778088403094\n", 1211 | "Mean Absolute Error: 0.6894259539831057\n", 1212 | "Spearman correlation: 0.9164363654548762\n", 1213 | "p-value: 0.0\n" 1214 | ] 1215 | }, 1216 | { 1217 | "data": { 1218 | "image/png": "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\n", 1219 | "text/plain": [ 1220 | "
" 1221 | ] 1222 | }, 1223 | "metadata": { 1224 | "needs_background": "light" 1225 | }, 1226 | "output_type": "display_data" 1227 | } 1228 | ], 1229 | "source": [ 1230 | "# Prepare new SMILES strings for prediction\n", 1231 | "valid_smiles = valid['Standardized_SMILES']\n", 1232 | "\n", 1233 | "# Predict properties for new SMILES strings\n", 1234 | "predictions = []\n", 1235 | "for smiles in valid_smiles:\n", 1236 | " inputs = tokenizer(smiles, return_tensors=\"pt\", padding='max_length', truncation=True, max_length=195).to(device) #max_length=195\n", 1237 | " with torch.no_grad():\n", 1238 | " outputs = model(**inputs)\n", 1239 | " predicted_property = outputs.logits.squeeze().item()\n", 1240 | " predictions.append(predicted_property)\n", 1241 | "\n", 1242 | "r_mse = mean_squared_error(valid[\"median_WS\"], predictions, squared=False)\n", 1243 | "r2 = r2_score(valid[\"median_WS\"], predictions)\n", 1244 | "mae = mean_absolute_error(valid[\"median_WS\"], predictions)\n", 1245 | "\n", 1246 | "print(\"VALIDATION SET\")\n", 1247 | "print(\"N:\", len(valid[\"median_WS\"]))\n", 1248 | "print(\"R2:\", r2)\n", 1249 | "print(\"Root Mean Square Error:\", r_mse)\n", 1250 | "print(\"Mean Absolute Error:\", mae)\n", 1251 | "\n", 1252 | "# assume test and predictions are two arrays of the same length\n", 1253 | "correlation, p_value = spearmanr(valid[\"median_WS\"], predictions)\n", 1254 | "\n", 1255 | "print(\"Spearman correlation:\", correlation)\n", 1256 | "print(\"p-value:\", p_value)\n", 1257 | "\n", 1258 | "plt.scatter(valid[\"median_WS\"], predictions)\n", 1259 | "plt.xlabel(\"validation['median_WS']\")\n", 1260 | "plt.ylabel(\"predictions\")\n", 1261 | "plt.title(\"Scatter Plot of Validation['median_WS'] vs Predictions\")\n", 1262 | "plt.show()" 1263 | ] 1264 | }, 1265 | { 1266 | "cell_type": "code", 1267 | "execution_count": 10, 1268 | "id": "4eea13b3-f35f-4db9-a1a9-61f2d16faceb", 1269 | "metadata": {}, 1270 | "outputs": [ 1271 | { 1272 | "name": "stdout", 1273 | "output_type": "stream", 1274 | "text": [ 1275 | "TEST SET\n", 1276 | "N: 1022\n", 1277 | "R2: 0.8223617048517198\n", 1278 | "Root Mean Square Error: 0.9380008871807847\n", 1279 | "Mean Absolute Error: 0.6807156079688436\n", 1280 | "Spearman correlation: 0.8991562895604099\n", 1281 | "p-value: 0.0\n" 1282 | ] 1283 | }, 1284 | { 1285 | "data": { 1286 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYAAAAEXCAYAAACkpJNEAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAAsTAAALEwEAmpwYAABBvklEQVR4nO2de5hdZXnof+9MdpI9iWYSidUMCUHUpCKSkChotDWoREVwKpeI6Cl6hKPWKinGBuVoUKwp0YKtba1atadgDBqcgtgGLQE1GDAhE2KEVBSSuPESTQYIM5A9M+/5Y601WbNnXfde+/7+nmc/M+v+rb3X+t7ve6+iqhiGYRjtR0e9G2AYhmHUBxMAhmEYbYoJAMMwjDbFBIBhGEabYgLAMAyjTTEBYBiG0aaYADAMw2hTTAC0CSKyVkRuqNG1lonIz0XkiIj01uKavmvPFxF1r31ZLa/tXv9rInKN+/+rRGRvrduQBSIyxf0Oi979NCMi8oiIvNb9/yMi8uUyz7NHRF6dZdsaARMAJYjIK0XkbhF5TEQOichWEXlphee8RER+VLLua1m/WO45j7ov7iER+Z6ILCzjPGMvTZl8Avi8qk5X1b6Scx/xfUZFZMi3fHEZbb1TRN4dsKlbVb/o7vNqEbmznBupBFX9oaouqNb5ReQiEXmgZN33Qtatcf8Pfb7d5/RrbtufVtXpwI3Var97Tb/APuI+e2uqcS1V/RtVDXpWSts04d1U1ZNV9c5qtKuemADwISLPBL4D/AMwC+gBrgaerme7ghCRSSGbrnVf3OOB3wFfq1mjjnECsCdogysUprtt3A+c41tX1c6mBfkBsFBEZsPYM3EqkC9Z93LgBw3+fHe7z8RFwMdE5PWlO0Q880aZmAAYzwsBVHWDqo6o6pCq3q6q93s7iMilIvKAiDwhIj8TkdPc9WtE5Be+9X/mrv9j4AvAy90RzoCrmrgY+LC77lZ33zkisklEDorIwyLyAd9114rIt0TkBhF5HLgk6kZUdRD4OvDioO0icq47rR1wR9F/7K7/d2AecKvbtg+HHH+piDzkjiJvEZE57vpfAM/zHT8l7kt3j+vwfYd/EJGbRGSWu22qe99/cNv7ExH5IxH5FPAq4PPutT6f8FoqIu8TR031hIh8UkROckfGj7vXnuzb/00i0u9e+24ReYlv22IRuc89z0Zgqm/bq0XkV77lwGfE3XaJiPxIRD4jIofd3/8NUfehqgXgl8CfuKtOwxG8d5Ws6wB+QoLnOynuszrk/Ua+7+L3IpITkeeLyF3uTOP37ncTi6r+2L2HF3vfn4j8tYj8Bvhq1HPituEdIrLP3fbRkjaPU4PKsdnQgIgccH+DsHfTr0qaIiLXi8ij7ud67zn3tfkKEfmdiPxaRN7pu+Yb3d/+CREpiMiH0n73maKq9nE/wDOBPwD/BrwBmFmy/QKgALwUEOD5wAm+bXNwXraVwJPAc91tlwA/KjnX14BrfMsdwA7gY8BknE70l8AKd/taoAj0uvvmA9o/dk5gOo4A+KHv+Bvc/1/otu91QA74MPAQMNnd/gjw2ojv6Uzg9zidyxScEeUPfNsjjw/aD/ggsA1n5jIF+Bdgg7vt/wC3Al1AJ7AEeKa77U7g3b5zzgcUmBRxXQX+w/29T8YZAf+3+53PAH4G/Lm772KcmdTp7rX/3G33FPd32gescr/H893fyPsNXg38quT5iXpGisCl7nXeCzwKSMx3+FXgc+7/H8JRv11asu6OJM93yPnHnqmAbXcAl/qW1wNfcP/fAHzUvdepwCtDzjH2e+G8U8uAQeA17vc3DPyt+33nY56TFwFHcITfFODv3OO9Z2wtx96BE4AncGYcOeBZwKKwe2b8s/oJtw3PBmYDdwOf9P3mw+4+OeCN7v3MdLf/GniV+/9M4LR69nk2A/Chqo8Dr8R5IL8EHHRHt3/k7vJuHBXLT9ThIVXd5x77TVV9VFVHVXUj8HPgZSku/1Jgtqp+QlWPquov3Ta81bfPj1W1z73GUMh5PiQiAzgd+nSCZworgdtU9XuqWgQ+g/NyvSJhWy8GvqKq96nq08CVODOc+QmPD+I9wEdV9VfuOdcC54sz7S/ivKDPV2fkusP9rSrhWlV9XFX3AD8FblfVX6rqY8B/4nT8AJcB/6Kq97jX/jccgXGG+8kB16tqUVW/hTPSDiTBM7JPVb+kqiM4nfRzgT8KOpcP/2j/VcAP3Y9/3V3u9eOe77R8HacDRUQE51n9urutiNPJzlHVp1T1R8GnGOP3wCHgy8AaVf1vd/0o8HF1bBJDRD8n5wPfUdUfuNv+r3t8EG8Dvq/ObKioqn9Q1f6E930x8AlV/Z2qHsRRo73Dt73obi+q6ndxhNIC37YXicgzVfWwqt6X8JpVwQRACar6gKpeoqrH46hP5gDXu5vnAr8IOk5E/pdPTTDgHntcikufAMzxjnfP8RHGdwAHEpznM6rararPUdVzVTWovXNwRq4AqOqoe+6ehG0tPf4Izsgy6fFBnAB823fvDwAjOPf/78Bm4BvulPtaEclVcC2A3/r+HwpYnu5r1xUlv8tcnO9gDlBQdzjnso8QEjwjv/H+UUeFh68dYfwAeImIzMQRSD9W1QeB57rrXunu45036vlOyyYcwf9cHIEziiN8wJlVCnCvOKrGd8Wc6zhVnamqf6yqf+9bf1BVn/ItRz0nc/C9I6r6JM5zGUTou5yAcc+/+/8c3/IfVHXYtzzIsd/xPJxZwT5XRfbyMtuQCSYAInBfpK9xTI9+ADipdD8ROQFnRPV+4Fmq2o0zqhTvVEGnL1k+ADzsdt7e5xmq+saIY8rlUZwXyWu/4LwQhYTXKT1+Gs4IvRB6RDwHgDeU3P9UVS24I6mrVfVFOLOUNwH/K2FbK+UA8KmSdnWp6gac6XyP+/15zAs6SYJnpCzcmeKjODOV/a4wBvixu246jroi6NjS5zvttQ8Dt+PMKN8GfMMThqr6G1W9VFXn4Kjw/klEnl/OZUqWQ58TnN9jrrejiHThPJdBBL7LIdcsZdzzj/ObPxpzjHNiR3vwZhz1UR9wU5LjqoUJAB8istA13hzvLs/FmeJ6L9CXcVQsS8Th+e6LPQ3noTnoHvdOxr9UvwWOF59h0V33PN/yvcATrsErLyKdIvJiqdAFNYSbgLNF5DXuSPoKHLXG3SFtK2UD8E4RWeQav/4GuEdVH6mgTV8APuV+n4jIbBF5s/v/chE5RUQ6gcdxptHe1D6urZXyJeA9InK6+5tPE5GzReQZOJ3sMPABcQyfbyFc7Rf3jFTCD4G/4tjoG+BH7rrtnrowwfNdDl/HEcbnc0z9g4hc4F0HOIxz72HqmDSEPifAt4A3ucbdyTh6+LA+7kbgtSJyoYhMEpFnicgid1uS5/8q99rH4djtYmNsRGSyiFwsIjNc1evjZPOdlI0JgPE8gWPsu0dEnsR5MX6K00Giqt8EPoXzoD+BI8FnqerPgM/idAi/BU4BtvrOeweOZ8NvROT37rp/xdEFDohIn6v3fROwCHgYRyf6ZRyjZKao6l7g7TjG298D5+C4Yx51d/k0zgM+EOSloKrfx9GvbsIZdZ3EeFtFOXwOuAW4XUSewPnuT3e3PQfn5X4cZ8p/F45ayDvufHE8Z/6ejFHV7ThG1c/jdGQP4dpV3O/rLe7yIZyR8M0h54l7RirhLpwRpV/P/kN33Q986yKf7zK5BXgB8BtV3eVb/1L3OkfcfT7ozlYqJfQ5ce05f4Hzfv4a5/f6VdBJVHU/jirmCpzfrh/HhRZK3s2Aw68BtgP3A7uB+9x1SXgH8Ig4nnzvwbEn1A0Zr740jObGHRnuBZ4CVqvql+rcpKbEndn9FsfIfa2qXl3nJhlVwASAYRhGm2KRdYbR4LhqlCDeoKo/DNlmGLHYDMAwDKNNaaoZwHHHHafz58+vdzMMwzCaih07dvxeVWeXrm8qATB//ny2b99e72YYhmE0FSISGKBobqCGYRhtigkAwzCMNsUEgGEYRptiAsAwDKNNMQFgGIbRpjSVF5BhGEat6dtZYP3mvTw6MMSc7jyrVyygd3Elmc8bBxMAhmEYIfTtLHDlzbsZKo4AUBgY4sqbdwO0hBAwFZBhGEYI6zfvHev8PYaKI6zfvLdOLcoWEwCGYRghPDoQXHk1bH2zYSogwzCMEOZ05ykEdPZzuvNlna9vZ4G1t+xhYKgIwMyuHB8/5+S6qZNsBmAYhhHC6hULyOc6x63L5zpZvWJByBHh9O0ssPqbu8Y6f4DDg0VWf2sXfTsrqaZaPiYADMMwQuhd3MOn33IKPd15BOjpzvPpt5xS1oh9/ea9FEcnZl8ujmjdbAqmAjIMw4igd3FPxSqavp2FQFWSR9S2amICwDAMo0qU6vzD6JAaNagEEwCGYbQM1QzaSnvu0hiCKEYV5q+5jZ4aB5qZADAMoyWoZtBWOecOiiGIo9aBZmYENgyjJahm0Faac/ftLLBs3R1l6/VrGWhmMwDDMFqCcoK2kqp1kp47jdoniloFmtkMwDCMliAsOCtsvddZFwaGUI6pX4J88pOeuxy1T5o2Z40JAMMwWoK0QVtp1DrLF06opx64PouRe7mBZuVgAsAwjJYgbdBWGpXRlgcPBu5bur7SkfvMrlzZgWblYDYAwzDKIguXy6zdNpMEbXnXnBiT6xDUiScVFqtXLGD1t3ZRHAk7ezRdkyfVNC+QCQDDMFKThctlPXLtxxlphWB1T9KkcNv3HSq784faZxk1AWAYRmqi9OdJO+8szpGWOCOtApt2FFh6wqxxbVi9YsEEwSE4QuukK7/LiCrd+VxsxG8cQbOPaga3mQ3AMIzUZJEnvx659pOcO8gQ7LcvgNP5e+P8EXX+q7TzDzL+pvFUKgcTAIZhpCaty2WafTtEMk2P7AVmnbjmNjokWdKdIEHRu7iHrWvOpKc7H2o/qIQg42+1K5KZADAMIzVZ5MkPOgc4I+qsRrmlI2hvtB5HlCCrxgylpztfUQBaudRVAIjI60Vkr4g8JCJr6tkWwzCSk0WefO8cnQGj8mqmcADoFEGA7nyOXOfE6w8eHQ4VQFkHaeU6JFRwZjHTiqJuRmAR6QT+EXgd8CvgJyJyi6r+rF5tMgwjOVnkye9d3MOqjf2B27IY5YadY1SVh9edDQSnbD48WAz1SFq+cDY3bNtfcds8Jk/qCP0eg4zPWQaK1XMG8DLgIVX9paoeBb4BvLmO7TEMow5UOsr16/iXrbtj3Mg9ybl7F/cwbcrEsXDYLCQsKKxcnjw6EjrbyLIiWRCiCXViWSMi5wOvV9V3u8vvAE5X1feX7HcZcBnAvHnzluzbt6/mbTUMo3oE+ebnc52JOrqgYz0PnZ7uPMsXzmbTjsK47bkOYfrUSQwMFsfcKldt7A817HaKJLYdlEtPd56ta86s2vlFZIeqLi1d3/BGYFX9oqouVdWls2cH5+MwDKN5qWSUG6Tj97rqwsAQm3YUOG9Jzzj3zeKocniwOM6tckY+F3qNanf+UPsAMI96BoIVgLm+5ePddYZhtDhBwU3ljIDjOs6h4sg4fX1QVz5UHGFqroN8rjOTTJ7lMDVXn7F4PWcAPwFeICInishk4K3ALXVsj2EYVaZvZ4HFn7idyzf2ZxLclJU3zOHBIsHiIVtmdgXPNIaKo1zVt7vq1y+lbgJAVYeB9wObgQeAm1R1T73aYxhGdfH09U5nO54ot88oI+/qFQvIqp76UHE0ozMFI8DOj50VWgB+wz0Hqnr9IOpqA1DV76rqC1X1JFX9VD3bYhhGdYnLwxOkzolLhdC7uKcG4/Zs8GYroyENroWtoZSGNwIbhtGYRI3Mg4irkRukzkmSCqEn48CsauD33Q8KfItaX01MABiGkZpykpRFdXBhwU1hRt7CwNCY0AlLKVErpkzqGOfBdP3KRVy/clGoV9NFp88NPE/Y+mpi6aANw0jN1bfuSZ3KOUrFEeb2GZaHHxwh8Fc39TMjn2OoOFITf/0gRkc1MEVz2Pew9IRZfH3bfvwWhw53fa2xGYBhGKno21kINOSCM2IPUw2FecCEJUKD8IRxHqPKWFvq0fmDE1eQJm/R+s17KTU3j7rra43NAAzDSEVURzUjnwus8rV93yGOPDUceIy/qEpPScET7+/6zXtjbQj1xK+qiivgUo86CGHYDMAwjFREdVQiBKqGNtxzgGKY+wvHRu9BtgQvD38j4xmwk9hGqp3hMw0mAAzDSEVYR9WdzzEQohpKo54ZKo5wxU27JqiQuiPSNVSDfK6DrgQRuvlcJ8sXzmbZuju4fGN/rNdSFrUUssIEgGG0EGldM8shrANbe+7JocIhrYvjiOqEEfTac0+uaYc1a9oUZk6bErjNqyfQ053nvCU9bNpRiFRR+WdN1c7wmQazARhGi1CaGdPrPCHcI6Uc/Hr5ID13UGZPr5MsJ9eON4JevWIB0iHhkVQZE6Xq8tcTWLbujtj7KhWMWdRSyAITAIbRIkQFTWXd2YR1YFHCYekJs8o25j46MMT6zXsZqVHnD8c67aD2+jv0OONtvdQ7STABYBgtQqN4l0QJh97FPYE5/GOR+EjiLMl1HivTGFeRa0Y+N66amJ9Sr6ZGwwSAYbQIYUFTWXqXxLk4JsHbv7QMYxS1dvH3Zhpx6q6+nQWeeHqie2uuQ1h/wakN2/F7mAAwjBYhy/qxQR09kJmNoXdxD+s3700sAMpFcEboIoQGrwUxqoypzqL09R+5+f5AtVRUnd9GwgSAYbQIcaPVpIQZk6fmOjK1MdRCpTOnO8+jA0PMyOfIdQrFkeRTCS/fUNh32bezwGBICuknj9ansExa6lYTuByWLl2q27dvr3czDKPqZKFqKZdl6+4oq3P2cvH49d7efRQGhsa2ezV7G53SdpbWKY77nh5xvYQagbCawDYDMIwGo1bunGGUOzL3R/NevrGfj9x8P8VRHRt1e9sbrfPPdUhglHLpmtLZTpRxvdZBa+VigWCG0WAkyYFfTcKCtgRSpV0eLI6mUrnUg57uPOsvOHVchx2WtA7Gd/pRxvWjwyNVDcbLCpsBGEaDUW93zrC0DYqTttlTTTV21x6PZyAPMvIuuvr2QAP1nO78OLVWmDrLsw3UevaWFpsBGEaDUe9kYWEVtgRYtbEfgOtWLmoaNYefmV05xP07ZVIHqzb2Txil9+0s8OTRYNfO5QtnjyV7g2TqrFrO3tJiAsAwGox6JwtbvWIBuYDK5ep+CgNDrP7mrkD/90amO59j58fO4rqVi3iqOMrAUDEwY+f6zXsDVVfTp05iy4MHy0pnUY9Uz0kwAWAYDUZpsrCo0Wq1rj99arR2uDiqNU3LUClesjoIt7Fc7n6/YUbwgcFi2R15PVI9J8FsAIbRgISlTaiGTjnI5TQsrXMzUpqOIaoTj9LrR+UGinJtbeRcQDYDMIwGphKPoCSpocMKmHRHeMI0E50iY4nkvPuPG40rTofux+vEw9RzF58xb9yMrTufq3uq5yTYDMAwyqQWwVrlegQFzRwu39jP2lv2jFOFBI1mh4ojTJnUQT7XWZa+u5EorTQGwSkzSlGczjvot92+7xAb7jnAiCqdIpy3pIdrek+p+r1UAxMAhlEGtQrWKjfBW9DMAWBgqMjqb+4CIdJH33OBrGH6/arjzZy88pJRqal7uvOBZSj7dhbYtKMwJlhGVNm0o8DSE2Y17Cg/ClMBGUYZ1CpYK0jlkOsQBo8OR6p2omYI/ujcOFql8/fwvhevzvD1Kxel8riqd5Be1pgAMIwyqFWwVqlHUHc+B25mS09nv2pjP1f17R53XKN6ndSKsGjmoMpcacoz1jtIL2tMBWQYKenbWaDDTWxWSjU6Xn+k6rJ1d0yIUFXgxm37x6khkui5m5kOcTr5oBw+XgnKjT85MG6mk+uUseLtpbr9pOqbWtRcqCV1mQGIyHoReVBE7heRb4tIdz3aYRhp8XT/QZ1/Ldz9wkaaChPUEFNzrTvBf9vp81h/wamBI/2h4gi33f/rCX6ZIyPKxp8cmODxlCauot5BellTrxnA94ArVXVYRP4WuBL46zq1xTASE2Zc7RQpy90vrSdR2AgUnA7txDW3MSOf48mjww2fiK0Sbty2H3CKswcRVPxlFBgt+U7S1DPwfquh4sg4v/+puQ627ztUt/TdlVAXAaCqt/sWtwHn16MdhpGWsBH4qGpmhVe27zvElgcPTqjGlaSgukLVq2w1AgrcsG0/HVJ5ucgk+vvS38p/ycODRW5wBRI0fgI4P41gA3gXsDFso4hcBlwGMG/evFq1yWgj0ozCs9QBh3mU3Lht/1gH4+XdiXPbbFeCvJTyuU6mTOpILAiT/HZhM78wKqmUVkuqpiQUke+LyE8DPm/27fNRYBi4Mew8qvpFVV2qqktnz55dreYabUpYJGyYXjhLHXCUPt9PmNtmp8iEiNV2p0OclNVrzz050H021zn+G0v625Xj5dMMnkFVmwGo6mujtovIJcCbgNdoM9WlNFqKKL/uoNFbXN3dLGYTSRlV5eF1Z7P4E7enKnjeyngzgrDfKWhdklF6Ob9VM3gG1UUFJCKvBz4M/KmqDtajDYYB5fl1h7kNpo0ODnLVTFMv1+tgbPg0Hk94h/1O5ahl0rrVNotnUL38xD4PPAP4noj0i8gX6tQOo83JsvhK2ijRoCCki8+YNyEXfwdEqi4eawOjbxoKA0Nlpc2OSp4XGJAXQSMngPMjzaR9Wbp0qW7fvr3ezTBaiNJROzidazkv8IlrbgscvQvw8LqzE7dn9bd2TQhgWvnSuWOeQTPyOUSc/PRzuvM8+fRwW3j+pCXJ7xhV3jHu+LDaAWF5hOqJiOxQ1aWl61s3UsRoC5KkPI4iTSqAuGtlMZsIqkZVHFE23HOA1SsWcN3KRTw9PDouFURQ+UIjPkeP3wEAJqre4o5vhaCwRnADNYyyyCojZ5JUAEmuFaQnTtshhNkeRlS58ubdTM11TFAztbN76MyuHE8VRxhyi7CXEmXLSeLaGWcL8s7TbAFgHiYAjKYlrQdPLa41ZdKxDnpmV46Pn3NyqrZEeZsMFUdaNrdPOQiw82NnRZZx7BDhxDW3BXbOSdw042ZvafIINSKmAjKallpmZoy7ljdD8OvinwoZlUYRpFYol3yuk8mdzRcpEJbJsxSvc476vUdUQ+M74jr30tlbperGRsQEgNG0ZOnBU+m1ssoT79kkKqU7n+O0eTM42mTqoVyHcNHpc2OFoL8mQkdCgVH6ewQJW+9MpbagtAGDzYIJAKNpqaURLu5aWc5GslApDAwV2fqLQxWfp9YMq3Ljtv1MmdTBzK5jdXXf7qu5W1oTISgzaxj+3yPIAeC6lYt4ZN3ZbF1z5rjfodUKwXiYDcBoWmpphPNfqzAwRKfIuA6g1fLE1wuvLx8YKpLPdXLdykUTfs9FV98emhpjVJU53XkGjw4HRkcHFYRJ8ry0WiEYDxMARlNTSyOcd50gb6DzlvSwaUehLA+goPQRXbkOBsuwIbQSQUb2vp2F0JgHLzWGt1+lHll+WlXAmwrICKUVjV6VEqYK2PLgwVSlBT3CdMtvWXI8Hc1nv82c0hF2lMrF3xmnLfUYRyv4/AdhMwAjkKx87FuNKFVAObORMIFyw7b9dOU6GCqOJs4N1Ir4O/W+nYXIhGylnXGWs8NW8PkPIpEAEJEPAl8FngC+DCwG1pQUdjFaiFr62DcTM/K5QBVEuaqAKB1yM6qAlp00iz2PPpFJagr/CNsbkIQxsytX9eey2X3+g0g6A3iXqn5ORFYAM4F3AP8OmABoUVrV6FUJfTsLgWkXch3C6hULQlNB+/PNdLrF5Hvc7ZWmhG40knge5TqE6VMnBRpp/YZc/wg7Kmo3n+vk4+ecXFnD25SkAsDTRr4R+HdV3SOS0PnWaEpa1ehVCUF5egCmT3Veo7Dyjn7jsOeyGGU8bmW68znWnut01mmS8EUNPJol82YjktQIvENEbscRAJtF5Bk4NZaNFqVVjV6VENYJDQwWQ1VmG+45ENq5e9vPW9JDTxsI1pldOaZNmcSqjf2s37x37L6TGGnDBh493Xnr/Csg6QzgfwOLgF+q6qCIPAt4Z9VaZdSdVjV6VULUrCgqiVsUI6ps2lEYi/4tTQXdKuQ6hccGi2Nqn8LAEBvvPcD6C05N9EylSbSXpipbu5NIAKjqqIj8FniRiJjnUJvQCkavJJ1B0g5j+cLZ4wq2w7FOyNPxl+Lp/KMYKo5w9a17HD126/X99HTnOfTk0xPTXI8qa2/Zk+gZSzogMe+1dCT1AvpbYCXwM8ATwQr8oErtMoyKSdIZJO0w+nYW2LSjMK5/FuC8JY6Q3L7v0AThIMAZz5vJffsfi9XxHx4scuXN91McbR0J4Nfpz19zW+A+abyF/AMST2iv2tg/ThiY91o6ko7me4EFqvp0FdtiGJmSpDNI2mEE7afAlgcPBgoHb/u9Dx+eUM4xjLCc9o3OzK4cqk5nXurlVI1ON0pom/daOpIKgF8COcAEgNE0JOkMknYYUftFuSgWR3XcqL6D1vGe6Mp18LNPviHRvjO7coFunzO7omvrBhEltM17LR1JvYAGgX4R+RcR+XvvU82GGUalJEkXHbWPPxVGWMrhKANwEK3S+QMMDY8mTg/y8XNOnjATynVKrP9+UDqSKGFs3mvpSCoAbgE+CdwN7PB9DKNhSdIZhOWELwwMsWpj/1iOniBDrneudh1dqpI4J37v4h7Wn3/qOLfP9edHewCF5UmakQ+eNcxxXUKzzAHU6ogmzKUtIpOBF7qLe1W18ljvlCxdulS3b99e68saTcxVfbvZcM8BRlTpFKfYyDW94wuu+CN1hWhHHH+k6vKFs9ny4MHMInnjrt2odIrw2QuTuXOmIazUo1MHeDRxEJkBIrJDVZeWrk80AxCRVwM/B/4R+Cfgf0TkT7JsoGFkjWec9UbvI6rcsG0/iz9x+7hRa+/iHrauOZOe7nxsB+ylHF69YgGbdkQnJ0tDkms3Kl7B+qyzxUYF3tkoPxuSGoE/C5ylqnsBROSFwAZgSbUaZhiVEmacdVwuJ7p6pikSHmX4TYM3cgVYtbG/4YRAT3eegcGjPHk0+l79nlNZBWJFGXRbIUalEUhqA8h5nT+Aqv4PjleQYTQsUR26v5qXZ2iM63z99oMsRv5duY6xkevaW/Y0VOefz3Vy/cpFbF1zZmzn7/HowFCmtXPNoFt9kgqA7SLyZRF5tfv5EmDKeKOh6Y5xMSztsIIoLRIOjm46C2ZOmzI2Ys4ifXIldOdzFatU5nTnM62dawbd6pNUBfRe4C+AD7jLP8SxBRhGwxLn39DdleOKm3aFpmrwMleGRQ3Hkc910CESOoIuDAyFGjprzcBQkf6PnxW4rTukBkIpq1csYNXG/sBt5QZimaqnuiSaAajq06r6d6r6FvdznUUFG43OYxGdVq5TOPLUcGSenmlTJsVGA0fxwCffQHfX5NDtnrtpIyA4Ai7I737tuSeTi6lP2Z13CrKEucQqWFnRBiRyBiAiN6nqhSKymwAPNVV9SdVaZhgVEmZE7BRh2uRJsaPa0lFrms66UyS2hGEj6fwVuHxj/zhX1MLAEJdv7Cef62Dly+aOubyWuqvmc51jOf6DsnbiO58lZmss4mYAH3T/vgk4J+BTESJyhYioiBxX6bkMo5QwI+JnLzw1cnbgUVqPNk0FJM81stkKuwcJpaHiKBvvPcDqFQt4ZN3ZXLdyUahe3q+3D6Jce0AWBM1u2p1IAaCqv3b/fZ+q7vN/gPdVcmERmQucBeyv5DyGEUaUETEuerfU22T95r2pR+xDxRFaJblncVTHOu7exT1jEdBeLqSguIow2ecZ32vZGWfpndRKJDUCvw7465J1bwhYl4brgA8D/1HBOYwWoJoFPMKMiEGqCk+1EZTJslF09fXEU4klTaEdpoKbkc/VPGe/pYkOJnIGICLvdfX/C0Xkft/nYWB3uRcVkTcDBVXdlWDfy0Rku4hsP3jwYLmXNBqUoJHZqo39XNVX9uOViKDZwXUrF/GIG+W7fvPecaPTzjJLYHeH5K1pBNLek5eDJ6mrZ5gKToTMXEWTYmmig4mbAXwd+E/g08Aa3/onVPVQ1IEi8n3gOQGbPgp8BEf9E4uqfhH4Iji5gJIcYzQPYXn2b9y2n6UnzKrq6Ky0ytT6zXsnFHH3RqdxVb2CEOBNpz6XDfceYKTBdEH+COSkrq1PHh2OzcbpJ6yKV9auokmwNNHBRAoAVX0MeExEPgccUtUnAETkmSJyuqreE3Hsa4PWi8gpwInALnFGIMcD94nIy1T1N2Xeh9GkhL30ClWfngepMkqreoEzOk1S2rGUi8+Yx5YHDzZE59+ddwqyh6nZouIhPIojmjrnfpAKLqx8ZjU74zQ1hduJpJHA/wwc8S0fcdelRlV3q+qzVXW+qs4HfgWcZp1/exL10ld7eh42+whiRDWVF9DMrhzX9J7SELYDz01z65ozeXjd2Wxdc+a4Trl3cQ+fvfDURPeXRc79eqR4sKjiYJIagUV9eaPdIvFWHN6oGE8lENTxVnt6nlbAKMcMxXGpmwcGi2Ouo/UY//vTVicxqofVNS6luyuXuEB71LUqOb6UpE4EFlU8kcQlIUXkAxwb9b8Pp0xkxbizAKNNCet4ajE9D1NlRKElf8PomtzJFTftqkrnL8ArTprFffsHQusIe2mr0+DVSYgSAkeecuwAlXamWXXGST2SjGCSqoDeA7wCKOCobE4HLqtWo4z24preUyKDi6rF8oWzq3buJ4+OlGU4jiOf6+S6lYu4YOk8iFDalDt72vLgwUih5Y8HaASyTD7XjiSaAajq74C3VrktRhuT5YgwqWphy4PN51bs79zCPHcqmT0lUYs1kuukuXdWRlwuoA+r6rUi8g8E5wL6QMBhhlEX0qoDmrWTiGt3JbOnJGqxRnKdNPfOyohTAT3g/t3O+GLwVhTeaDjC1AGXb+wPTDfQrJ3EnO58aK2DHrdaVrkEeej4aTTXSSsaUxlxcQC3un//rTbNMWpBNVMv1Br/vUTprv2zAQj3RW908rlOli+czcZ7D0zYluuUiju+Ug+dGfkcIo5XUyM+K1l7FLUbohGGKhG5lQiHB1U9txqNCmPp0qW6fbsVIquEoKImXlRos700aQu0gBMQ9fTwaCb1fOMQiS9KkwYvR1GY8OrO50KLuhjtjYjsUNWlpevjVECfwSkI/zAwBHzJ/RwBfpF1I43q02xeE1FZI8spzD4wVKxJ55/rFK67cFFm5+vpzo8FcIXZAJKkuDYMP3HpoO9S1buAZaq6UlVvdT9vA15VmyYaWdJMXhNxKXwbqc2d4kT/em6s688/ld7FPWUnkfMjME61E2a7aFabhlE/kgaCTROR56nqLwFE5ERgWvWaZVSLWnpNVGpriEvhmzaQK5/rZGqug8OD5Y2Uo6J6RxSeKo5y3cpF4+4xi1gABdbesoerb93DwGCR7q4cuQ6h6MsxZIZPoxySBoKtAu4UkTtF5C5gC3B51VplVI1aeU1kUYAjbrayesWCyPw10yZ3jo3AO0U4b0kPZ7/kuYmv7yfXIVx8xrzIfYI8jsIqY6VlYKjI4cEiCo4AE0fnb3ltjEpIGgj2XyLyAmChu+pBKwrfnNTKayKLAhxxsxUvjcQN2yYWlevsEI4Oj46NwEdU2bSjwNRcsjFPhzCumldxVLl5x6+Y2ZWLnUH4PY6WL5wd2L5KKY4o06ZMMqOvURGJBICIdAF/BZygqpeKyAtEZIGqfqe6zTOqQS2SYmVha6gkhe/IqFJq6h0qjiQ2AAdlcB4sjvLU8Ci5TqE4Eq3aiTOse8naOspIM+3RSDYQozlJqgL6KnAUeLm7XACuqUqLjJYgC0NlkhS+G+6Z6A9fTUYVpk2exMyQQCw/hYGhUBuFl6ztsxeeGqiSS1JJzIy+RqUkNQKfpKorReQiAFUdFMnAvcFoCsox5saN3oPOCcGqqbBr9e0sVCXhWhwDQ8WKSz12iIxl1dy+7xAb7jnAiOqYrWLpCbMiYxzM6GtkQWQg2NhOIncDrwG2quppInISsEFVX1btBvqxQLDaU0ngWJjgCAvg6uyQcdWz/NcpPdfyhbPHlW5MQq5DGB7VuuTnDyKf6+S8JT0T7sNfrrFZInKNxiYsECypAHgdcBXwIuB2YBlwiaremXE7IzEBUHuWrbsjUI3hBSZlec4gvOjXUoFRr0IrWRNWarKS7zcLWildiBEuAGJVQCLSAcwE3gKcgfPufVBVf595K42GoxqBY2mOLQwMBdarbYXOH8LjBOpp4LUiK+1DrBFYVUeBD6vqH1T1NlX9jnX+7UM1ok7THCtkE0zVqIRFCtfTwNts6UKM8knqBfR9EfmQiMwVkVnep6otM+pCae6d5QtnZx44FhfA5dGsah4hvGP3k891ctHpcxsunXEzpQsxKiOpAFiJUwf4LpzaAN7HaCGConc37Shw3pKeTMs19i7u4eIz5k0QArkOGZdPp96df7l5fMLcO3OdMiF695reU2JdXaMS4lWDsNmHQk2ub9SOpEbgPI4AeCXOc/BD4AuqWtMhgRmBq0s1DL5RxBka0xiLs8abfZQzC3nELcaehSG1Hum749JsN2v68HamUi+gm4DHgRvdVW8DZqjqhZm2MgYTANXlxDW3BXZ2gjOqrQZeJ1kYGBrziOnxxQWkzfcfRNJOPGmnHxUJ3CnCLz79xtRtDKPWQtnD/7sEUW8vJSMd5dYD8Hixqr5bVbe4n0uBF2fbRKPe1DrNsF/lBMeMvYWBIVZ/axdrb9nDUHFkXEK3tHSKcN3KRbHH5jqc/ZKonoojSj4kp9BFp89N3cYowvTu1Z4Z9S7uYeuaM0NtNWYPaA2SCoD7ROQMb0FETsdsADWlFnrgWtdXjSroUhxRBtwCJyOqoQbTOKZMElZt7I/1JJo+dVJksZVSniqO8vYz5o0TTm8/Yx7X9J6Sqn1xhAlfgZro4q32QGuTNBXEEuBuEfHSGs4D9orIbkBV9SVVaZ0B1M4vu9b1VdOMIoeKI2x58CCnzZvB1l8cSnzcYHE00X4DbobPpDUG5nTnuab3lMw7/FJWr1jAqo39E2YlCqkyq1Zy/XIT8hmNT1IB8PqqtsKIJIvUyknJOlNolCE0bUGXwsBQ1VQP3og2qMMrJYvi65DMSNy7uIfLN/YHHl8LNYwVXW9tktYD2FfthhjhNKtfdtzMJUln6ycsbUKl+Ee0XscWFH08RgZNSDOr66lhFbcgapE+3KgPSW0ARh1pVj1sXESpP90zxBt5q9H5d+dzTM11sMpXyat3cQ+jEdcqjmrFUbFpom1rbZsx2oe6CQAR+UsReVBE9ojItfVqRzPQrB1AmHrHv97zNnnEDZ6KEgJZFFj3053P8fTw6FipRX/ZyjjhWunsK82sLkldBMMoh6Q2gEwRkeXAm4FTVfVpEXl2PdrRLDSrHjZMZRPUkXsqkahRvucNVGlcADh6fBFCR+GrVyxg9Td3jSu87qfS2VdcuctSTA1jVIO6CADgvcA6r66wqv6uTu1oGmppnM2KsM7cW+9vQ5LSiF6AmHdMd1eOI08Nh3bSkSihtX0fHRiid3EPV9+6J3AfgYpnX+ZdYzQC9VIBvRB4lYjcIyJ3ichLw3YUkctEZLuIbD948GANm9i6BOX88VQfWdITMprt6c5PaENc5x/kedM1eRIrXzY3sQ3BT5TQ8EbhAyECQqnc/dbUOkYjULUZgIh8H3hOwKaPutedhVNf4KXATSLyPA3IS6GqXwS+CE4qiGq1t50ox600zYzBn0agNK2CN8qNCgIrZWZXjo+fczLABM+ZTTsK4zrOuDw2SVi+cDYQrqYJE2xpMbWOUW+qNgNQ1deq6osDPv8B/Aq4WR3uBUaB46rVFmM8ad1K08wYStM7eLl1YPwoN4kRNdchXL9yETs/dha9i3tCBdfVt+4ZWw4aWaet37vlQWem2azGd8NISr1sAH3AcmCLiLwQmAxYkZkakdYAmWbGELRv0LQtURBYiUYnTGgcHixyVd9utjx4MHCGknZW4LWrWY3vhpGURNlAM7+oyGTgK8Ai4CjwIVW9I+44ywaaDWlTDIdlCYVjqY+T7AvHMm1253M8eXQ4NKumH7/xN2nkcOn9pDE4l5vRsxw1mQkWoxZUmg00U1T1qKq+3VUJnZak8zeyI60BMk1Csjj3SK/bHRgqgjJWACYKT+U0/1nJde+lQVVevIFXrCXqmuUEnJWrJqumEd4w4rBI4CYh62yg/g5x65ozI0efYSUcvYRkpfsmzdhZHFUODxaZ051nZle0nn6oOMLdKZLAQbjKyKtIFkanSOrvN01kr9XcNRoFEwBNQL1HjL2Le0LVOkEqmakhufLDKAwMhfrk+0k7Lg+ajXiC9MZt+wlr5ohq6u83jWG9WXM7Ga2HCYAmIOmIsZo1A8JcH/1qIE9QJenMw8gq2UOQt06pII3KFJ12RJ4mX1Mj5naqdd1hozEwAdAERFWF8l7Was8SkqiB0vj2h+GN8isVBOctmehjn7Z9aUbkaVxGG829tN4zTKN+1MsN1EhBlMtkYWCI1d90UheXBrdmUTPA760SpoLxOsq4DjPXKUybPGms0lcU/tq8Pd15Dj35NEMJi7sAbNpRYOkJs8bde1oVS5oReRqX0UZzL61lvQmjsTAB0ATE5c2PSmtQiV45qf+811FGCaoeXyfXt7MQmWjNw+v8t645kxPX3Jaq7UEdWJoCNOWMyNNE9jZSFLDZJNoXUwE1AaV589NQOopNo+tNojLxd5Rhqo3rVy4a52nUu7iH6VOTjT08NdeMkGjeKFVRaQcW1L5ch4x5IHm5hNotL08j2iSM2lCXQLBysUAwWLbujrKDoa7q282N2/aPU+V4apnHhooTVBFRQV0CgaqLpAFOcQFjpeQ6BXT8bCef6+S8JT1suOdAoO/+zK4cXZMnjWsLNI7qpVFIGxhoNB9hgWCmAmoykpZR7BSZEAlb2vkDFEd0TCdfWpYwKhna1jVnBl43qWojbT3g4ojS4Rvud+dzrD33ZHoX97D0hFkTvpNcp3DkqeExj6TCwBCXb+wfSyxnHdsxGs0mYdQOEwBNhv9lLQwMIQKlg9+g0dv6zXsTjbj9uvNq5qwPOneuQ5g+dVKoG6nfZPD08DGDcFAH9uTTw4HG5sODxdDau+1MI9kkjNphAqAJ8V7UoJmAf2TsJ41Bz9s37cgwTP3jXz8jn0PEybXf3ZUDdMy7Z/rUSXz8nJMT5fwpNfKWdmBRRmPzcDEMBxMATUqYgXbalEmBHVsalcsct2CLvzO/buWiyA6zVI/sqZO27zvEph2FsfX+UXnpSN8bnZ+3pGfcMWFECbW4+y0MDHHimttM3WG0NeYF1GB4Xjrz19zGSVd+l/kh3jppXffCcvR0lLjR5HOdLF84O3VgUJgv+YZ7DqQKvhoqjrDlwYPjktWFVfqK8lJJkpPIgp6MdsdmAA1E6Sja82wpNc5CeUXFYaI6J2hdVGDQ9n2HxrxuOkW46PS5XNN7Suhou5zMml5N3qh8/nG2CO/YtbfsiQ08M5WQ0a6YG2gDEefi6fe+qabrXloXzWUnzeLuXxxKnawtjCAvo0ry5yeJZoZw11bDaHbMDbQJiDPU+rdX03UvrYvm1pRpmqMIG9lX4qXiPzZKyPpVQt5xhtHKmABoEPp2FmIrVZWqd6rluhfm/llJordOEUZVx3kBzenOs3zh7NBSjmm5qm93oHoq7t5KMZWQ0S6YAGgAPHVOnL588OgwfTsLVe+YwmYXq27qnxBzkJRRVR4uKR+ZJVf17eaGbfvHlkdUx5b9QqD03uIS3BlGK2MCoAFImqY4yyCmOJ166eyib2eh7M4fqp9XZsM9B0LXl84CkqiELA+O0Q6YAGgA0ow2s0rxHOSzD0zo9NMUYg/Dr9evVjH0sNlT3KyqmtHOhtHoWBxAA5B2tFmpeiJJhTF/kZBK8GfWrGbhkbBYgbD1Hv5Mq0L7ZQI12hubATQASRO8eVSqnkgSRFZpdS+BCdHD1Sw8ctHpc8fZADxGVFm27o7ImYblwTHaFZsBNABBo9DuiPz3laonkuR/r3SWoUy0U1Sz8Mg1vafw9jPmBY74LdrXMIIxAdAg9C7uYeuaM3l43dlsXXMma889eUIqAwEuPmNexaPVJDVpK51lBBWvqXbhkaUnzOI5M6YGbktb5N0w2gETAA1K0KzgupWLJni0ZHXuUr13VC4db4zdnc9NyCUETi7+WhdDT2KzMNdOwxiP2QAamCS66TReNWn2La070OkGqXl/e7rzDB4dnlCIHmDaZOexWrbujsBrVcMLKInNwlw7DWM8JgCamKTunGn2jcrpX3p8GANDRS7f2D+2XHqtahhc40b35tppGBMxFVATk8SdM82+QW6al2/sZ/EnbmftLXsq8gqqtg4+anRvrp2GEUxdZgAisgj4AjAVGAbep6r31qMt1aJaAU9+0njVVOL6GVaiMS3V1MGHBXRZx28Y4dRrBnAtcLWqLgI+5i63DNUMePKTxqumFq6fcVRTB28BXYaRnnrZABR4pvv/DODROrWjKlQz4MlPmjQGSfZNmwY6DbXQwVtA13hqMQs1mpt6zQAuB9aLyAHgM8CVdWpHVahmwJOfNKNeb19/gNnU3PifP66M4syuXGyZxiC68zkbjdeYWs1CjeamajMAEfk+8JyATR8FXgOsUtVNInIh8K/Aa0POcxlwGcC8efOq1NpsSVuusRLSjnqfPDo89v/hwSKrv7VrwnmCyijmc518/JyTx/Y5cc1tsdfqsVFn3ajVLNRobupSElJEHgO6VVVFRIDHVPWZccc1S0nIapZrLLc9UVk9Z3bl2PmxswKPCVMfpClfadSesLKeAlWty2A0Jo1WEvJR4E+BO4EzgZ/XqR1VoZoBT2np21lg9bd2URwJF/RBXj5xM4u4BHaFgaGaFK8xgqnlLNRoXuolAC4FPicik4CncFU8rUSjGCSvvnVPZOdfLt69XXHTrtCc+1Zbt35YnQMjCXURAKr6I2BJPa7dbiT14T9xzW2pZyrefmEzAdM5149GmoUajYulgjAAxnmKQPJRu7efP/WDH0vAVj8aZRZqNC6WCqLFCasrEEY5KRt6F/cEpn8G0zkbRiNjAqDJ6NtZYNm6OzhxzW0sW3dHrF/32nNPJheUszmCuFF7UBuqmerZMIzqYAKgiQgK7lm1sZ+r+naHHtO7uIf1F5ya6jpRo/awACPAUjEYRpNRlziAcmmWOIBqEeV7353P8dhQMdTYF+e37xEXrxB2HvP7N4zGJSwOwGYATUSUamZgqBgZ8h+monn7GfNSjdprlebCMIzqY15ATUTSZG1B7pdZuQVagJFhtA4mAJqI1SsWsGpjf2CIfylBI/Is3ALjAowsA6VhNA8mAJqI3sU9bN93iBu37Y8VAtUakUfNJNKUqKwEEzKGkQ0mAJqMa3pPYekJs8Y6wO6uHEeeGqboq85ebffLsJlELTJQ1krIGEY7YAKgCSntgBtlRFwLA7GlOTaM7DAB0AI0Ssh/LQzE5oVkGNlhbqBGZtQiGjhNHWTDMKIxAWBkRi0Ks1vKCcPIDlMBlUGj6NwbkWqroyzNsWFkhwmAlJgXSv1pFJuHYTQ7LS8Ash6tmxeKYRitQksLgGqM1hvJC8VUUYZhVEJLG4GjRuthxOXbbxQvlLC0zHH1AQzDMDxaWgCkHa0n6VQbxQulHOFmGIbhp6UFQNrRepJOtRaujkloJFWUYRjNSUvbAOIyV5aStFNtBC8US8tsGEaltPQMIO1ovVH0+0loFFWUYRjNS0vPACDdaD3tjKGeWECUYRiV0vICIA3N1qk2girKMIzmxQRACdapGobRLrS0DcAwDMMIxwSAYRhGm2ICwDAMo00xAWAYhtGmmAAwDMNoU0RV692GxIjIQWBfHZtwHPD7Ol4/C1rhHsDuo9FohftohXuA4Ps4QVVnl+7YVAKg3ojIdlVdWu92VEIr3APYfTQarXAfrXAPkO4+TAVkGIbRppgAMAzDaFNMAKTji/VuQAa0wj2A3Uej0Qr30Qr3ACnuw2wAhmEYbYrNAAzDMNoUEwCGYRhtigmAGETkAhHZIyKjIrK0ZNuVIvKQiOwVkRX1amNaRGSRiGwTkX4R2S4iL6t3m8pFRP5SRB50f6Nr692eShCRK0REReS4erclLSKy3v0d7heRb4tId73blAYReb37Hj8kImvq3Z5yEJG5IrJFRH7mvg8fjDvGBEA8PwXeAvzAv1JEXgS8FTgZeD3wTyLSOfHwhuRa4GpVXQR8zF1uOkRkOfBm4FRVPRn4TJ2bVDYiMhc4C9hf77aUyfeAF6vqS4D/Aa6sc3sS4763/wi8AXgRcJH7fjcbw8AVqvoi4AzgL+LuwwRADKr6gKruDdj0ZuAbqvq0qj4MPAQ0y0hagWe6/88AHq1jWyrhvcA6VX0aQFV/V+f2VMJ1wIdxfpumQ1VvV9Vhd3EbcHw925OSlwEPqeovVfUo8A2c97upUNVfq+p97v9PAA8AkcVNTACUTw9wwLf8K2K+7AbicmC9iBzAGTU3zWithBcCrxKRe0TkLhF5ab0bVA4i8magoKq76t2WjHgX8J/1bkQKmvldDkRE5gOLgXui9rOKYICIfB94TsCmj6rqf9S6PVkQdU/Aa4BVqrpJRC4E/hV4bS3bl5SY+5gEzMKZ7r4UuElEnqcN6Nsccx8fwVH/NDRJ3hMR+SiOKuLGWrbNOIaITAc2AZer6uNR+5oAAFS1nM6vAMz1LR/vrmsIou5JRP4f4BmIvgl8uSaNKoOY+3gvcLPb4d8rIqM4ibAO1qp9SQm7DxE5BTgR2CUi4DxH94nIy1T1NzVsYixx74mIXAK8CXhNIwrhCBr6XU6DiORwOv8bVfXmuP1NBVQ+twBvFZEpInIi8ALg3jq3KSmPAn/q/n8m8PM6tqUS+oDlACLyQmAyTZbNUVV3q+qzVXW+qs7HUT+c1midfxwi8nocG8a5qjpY7/ak5CfAC0TkRBGZjOPccUud25QacUYQ/wo8oKp/l+QYmwHEICJ/BvwDMBu4TUT6VXWFqu4RkZuAn+FMef9CVUfq2dYUXAp8TkQmAU8Bl9W5PeXyFeArIvJT4Cjw50028mwlPg9MAb7nzmS2qep76tukZKjqsIi8H9gMdAJfUdU9dW5WOSwD3gHsFpF+d91HVPW7YQdYKgjDMIw2xVRAhmEYbYoJAMMwjDbFBIBhGEabYgLAMAyjTTEBYBiG0aaYADAMw2hTTAAYDYeIdIvI+8o89nIR6fItj7hpr+e4y49k1MzS697ppQsXke9mlQ7Z/S7+4Ab5ICIvd1NGH+8uzxCRQyLSISJnuHmR+kXkARFZ6+5zie//VSKyX0Q+n0X7jObGBIDRiHQDZQkAnER3Xb7lIVVdpKo1y3iqqm9U1YGMzjUA/Br4Y3fVK4Cd7l9w8iDdq6qjwL8Bl7lpvl8M3BRwvutwUoAbhgkAoyFZB5zkjmTXi8hqEfmJW2zkagARmSYit4nILhH5qYisFJEPAHOALSKyJeTcB93j57sFTL4mIv8jIjeKyGtFZKuI/NwrkuNe5ysicq+I7HQzdyIieRH5hjvS/jaQ9y4gIo+IW9RFRPpEZIdboOMy3z5HRORTbvu3icgfRXwfd3Osw38FTupo//JW9/9n4wgLVHVEVX/mrh8CjkR/5UZboqr2sU9DfYD5wE/d/88CvggIzoDlO8CfAOcBX/IdM8P9+whwnG/9kYhrDAOnuOfdgZNaQnBywfe5+/0N8Hb3/26cYifTgL/CSRkA8BL3XEtL2wDMcv/mcYoLPctdVuAc9/9rgasivo8/911rJzAV+JG7/D2c5GvgjOwPA98G/g8wNeR8lwCfr/fvbJ/6f2wGYDQ6Z7mfncB9wEKcxHu7gdeJyN+KyKtU9bEyzv2wOsnYRoE9wH+rqrrnnu+7/ho3t8qdOJ3vPBwhdAOAqt4P3B9yjQ+IyC6cIilz3baDk7voO+7/O3zXC+Ju4BVu0sFHVPUpnNxf04EluDnfVfUTwFLgduBtwH8l+RKM9sWSwRmNjgCfVtV/mbBB5DTgjcA1IvLfbgeYhqd9/4/6lkc59m4IcJ6WVIVzbbLRDRd5NU6dhZer6qCI3IkjQACKrrABGCHiXVTVn7tG5XOAH7urdwDvxBEIR3z7/gL4ZxH5EnBQRJ6lqn+IbazRltgMwGhEngCe4f6/GXiXO9pFRHpE5NmuV8+gqt4ArAdOCzg2CzYDf+nzwlnsrv8BzigbEXkxjhqolBnAYbfzX4hjsC2XbTg1HDwB8GMcg7en/0dEzpZjkukFOIJloIJrGi2OzQCMhkNV/+AaY3+KU1rw68CP3b7tCPB24Pk4ZS1HgSJOfWBw7AX/JSKPquryDJrzSeB64H4R6QAexil68s/AV0XkAZzaqzsCjv0v4D3uPntxOvFy2Yoz29nuLv8YeB6OesjjHcB1IjKIY5O4WJsnRblRBywdtNHSiMgRVZ1e73Y0EuJU7lqqqu+vd1uM+mIqIKPVedwfCNbuiMgq4Eogslas0R7YDMAwGgRxCqpfULL6m6r6qXq0x2h9TAAYhmG0KaYCMgzDaFNMABiGYbQpJgAMwzDaFBMAhmEYbcr/B391xZiEvFSJAAAAAElFTkSuQmCC\n", 1287 | "text/plain": [ 1288 | "
" 1289 | ] 1290 | }, 1291 | "metadata": { 1292 | "needs_background": "light" 1293 | }, 1294 | "output_type": "display_data" 1295 | } 1296 | ], 1297 | "source": [ 1298 | "# Prepare new SMILES strings for prediction\n", 1299 | "test_smiles = test['Standardized_SMILES']\n", 1300 | "\n", 1301 | "# Predict properties for new SMILES strings\n", 1302 | "predictions = []\n", 1303 | "for smiles in test_smiles:\n", 1304 | " inputs = tokenizer(smiles, return_tensors=\"pt\", padding='max_length', truncation=True, max_length=195).to(device) #max_length=195\n", 1305 | " with torch.no_grad():\n", 1306 | " outputs = model(**inputs)\n", 1307 | " predicted_property = outputs.logits.squeeze().item()\n", 1308 | " predictions.append(predicted_property)\n", 1309 | "\n", 1310 | "r_mse = mean_squared_error(test[\"median_WS\"], predictions, squared=False)\n", 1311 | "r2 = r2_score(test[\"median_WS\"], predictions)\n", 1312 | "mae = mean_absolute_error(test[\"median_WS\"], predictions)\n", 1313 | "\n", 1314 | "print(\"TEST SET\")\n", 1315 | "print(\"N:\", len(test[\"median_WS\"]))\n", 1316 | "print(\"R2:\", r2)\n", 1317 | "print(\"Root Mean Square Error:\", r_mse)\n", 1318 | "print(\"Mean Absolute Error:\", mae)\n", 1319 | "\n", 1320 | "# assume test and predictions are two arrays of the same length\n", 1321 | "correlation, p_value = spearmanr(test[\"median_WS\"], predictions)\n", 1322 | "\n", 1323 | "print(\"Spearman correlation:\", correlation)\n", 1324 | "print(\"p-value:\", p_value)\n", 1325 | "\n", 1326 | "plt.scatter(test[\"median_WS\"], predictions)\n", 1327 | "plt.xlabel(\"test['median_WS']\")\n", 1328 | "plt.ylabel(\"predictions\")\n", 1329 | "plt.title(\"Scatter Plot of Test['median_WS'] vs Predictions\")\n", 1330 | "plt.show()" 1331 | ] 1332 | }, 1333 | { 1334 | "cell_type": "code", 1335 | "execution_count": 12, 1336 | "id": "7bf98da0-79d3-4e63-9350-ea6c33ed7fc0", 1337 | "metadata": {}, 1338 | "outputs": [], 1339 | "source": [ 1340 | "# Save results\n", 1341 | "results_df = pd.DataFrame({\"actual_WS\": test[\"median_WS\"], \"predicted_WS\": predictions})\n", 1342 | "results_df.to_csv(\"testset_results.csv\", index=False)" 1343 | ] 1344 | } 1345 | ], 1346 | "metadata": { 1347 | "kernelspec": { 1348 | "display_name": "Python 3 (ipykernel)", 1349 | "language": "python", 1350 | "name": "python3" 1351 | }, 1352 | "language_info": { 1353 | "codemirror_mode": { 1354 | "name": "ipython", 1355 | "version": 3 1356 | }, 1357 | "file_extension": ".py", 1358 | "mimetype": "text/x-python", 1359 | "name": "python", 1360 | "nbconvert_exporter": "python", 1361 | "pygments_lexer": "ipython3", 1362 | "version": "3.9.5" 1363 | } 1364 | }, 1365 | "nbformat": 4, 1366 | "nbformat_minor": 5 1367 | } 1368 | --------------------------------------------------------------------------------