Convert v1 to v2#

Open In Colab

[1]:
# Install chemprop from GitHub if running in Google Colab
import os

if os.getenv("COLAB_RELEASE_TAG"):
    try:
        import chemprop
    except ImportError:
        !git clone https://github.com/chemprop/chemprop.git
        %cd chemprop
        !pip install .
        %cd examples

Import packages#

[2]:
import torch
from pprint import pprint
from pathlib import Path

from chemprop.utils.v1_to_v2 import convert_model_dict_v1_to_v2
from chemprop.models.model import MPNN

Change model paths here#

[3]:
chemprop_dir = Path.cwd().parent
model_v1_input_path =  chemprop_dir / "tests/data/example_model_v1_regression_mol.pt" # path to v1 model .pt file
model_v2_output_path = Path.cwd() / "converted_model.ckpt" # path to save the converted model .ckpt file

Load v1 model .pt file#

[4]:
model_v1_dict = torch.load(model_v1_input_path, weights_only=False)
[5]:
# Here are all the keys that is stored in v1 model
pprint(list(model_v1_dict.keys()))
['args',
 'state_dict',
 'data_scaler',
 'features_scaler',
 'atom_descriptor_scaler',
 'bond_descriptor_scaler',
 'atom_bond_scaler']
[6]:
# Here are the input arguments that is stored in v1 model
pprint(model_v1_dict['args'].__dict__)
{'activation': 'ReLU',
 'adding_bond_types': True,
 'adding_h': False,
 'aggregation': 'mean',
 'aggregation_norm': 100,
 'atom_constraints': [],
 'atom_descriptor_scaling': True,
 'atom_descriptors': None,
 'atom_descriptors_path': None,
 'atom_descriptors_size': 0,
 'atom_features_size': 0,
 'atom_messages': False,
 'atom_targets': [],
 'batch_size': 50,
 'bias': False,
 'bias_solvent': False,
 'bond_constraints': [],
 'bond_descriptor_scaling': True,
 'bond_descriptors': None,
 'bond_descriptors_path': None,
 'bond_descriptors_size': 0,
 'bond_features_size': 0,
 'bond_targets': [],
 'cache_cutoff': 10000,
 'checkpoint_dir': None,
 'checkpoint_frzn': None,
 'checkpoint_path': None,
 'checkpoint_paths': None,
 'class_balance': False,
 'config_path': None,
 'constraints_path': None,
 'crossval_index_dir': None,
 'crossval_index_file': None,
 'crossval_index_sets': None,
 'cuda': False,
 'data_path': '/Users/hwpang/Software/chemprop/tests/data/regression.csv',
 'data_weights_path': None,
 'dataset_type': 'regression',
 'depth': 3,
 'depth_solvent': 3,
 'device': device(type='cpu'),
 'dropout': 0.0,
 'empty_cache': False,
 'ensemble_size': 1,
 'epochs': 1,
 'evidential_regularization': 0,
 'explicit_h': False,
 'extra_metrics': [],
 'features_generator': None,
 'features_only': False,
 'features_path': None,
 'features_scaling': True,
 'features_size': None,
 'ffn_hidden_size': 300,
 'ffn_num_layers': 2,
 'final_lr': 0.0001,
 'folds_file': None,
 'freeze_first_only': False,
 'frzn_ffn_layers': 0,
 'gpu': None,
 'grad_clip': None,
 'hidden_size': 300,
 'hidden_size_solvent': 300,
 'ignore_columns': None,
 'init_lr': 0.0001,
 'is_atom_bond_targets': False,
 'keeping_atom_map': False,
 'log_frequency': 10,
 'loss_function': 'mse',
 'max_data_size': None,
 'max_lr': 0.001,
 'metric': 'rmse',
 'metrics': ['rmse'],
 'minimize_score': True,
 'mpn_shared': False,
 'multiclass_num_classes': 3,
 'no_adding_bond_types': False,
 'no_atom_descriptor_scaling': False,
 'no_bond_descriptor_scaling': False,
 'no_cache_mol': False,
 'no_cuda': False,
 'no_features_scaling': False,
 'no_shared_atom_bond_ffn': False,
 'num_folds': 1,
 'num_lrs': 1,
 'num_tasks': 1,
 'num_workers': 8,
 'number_of_molecules': 1,
 'overwrite_default_atom_features': False,
 'overwrite_default_bond_features': False,
 'phase_features_path': None,
 'pytorch_seed': 0,
 'quiet': False,
 'reaction': False,
 'reaction_mode': 'reac_diff',
 'reaction_solvent': False,
 'resume_experiment': False,
 'save_dir': '/Users/hwpang/Software/test_chemprop_v1_to_v2/fold_0',
 'save_preds': False,
 'save_smiles_splits': True,
 'seed': 0,
 'separate_test_atom_descriptors_path': None,
 'separate_test_bond_descriptors_path': None,
 'separate_test_constraints_path': None,
 'separate_test_features_path': None,
 'separate_test_path': None,
 'separate_test_phase_features_path': None,
 'separate_val_atom_descriptors_path': None,
 'separate_val_bond_descriptors_path': None,
 'separate_val_constraints_path': None,
 'separate_val_features_path': None,
 'separate_val_path': None,
 'separate_val_phase_features_path': None,
 'shared_atom_bond_ffn': True,
 'show_individual_scores': False,
 'smiles_columns': ['smiles'],
 'spectra_activation': 'exp',
 'spectra_phase_mask': None,
 'spectra_phase_mask_path': None,
 'spectra_target_floor': 1e-08,
 'split_key_molecule': 0,
 'split_sizes': [0.8, 0.1, 0.1],
 'split_type': 'random',
 'target_columns': None,
 'target_weights': None,
 'task_names': ['logSolubility'],
 'test': False,
 'test_fold_index': None,
 'train_data_size': 400,
 'undirected': False,
 'use_input_features': False,
 'val_fold_index': None,
 'warmup_epochs': 2.0,
 'weights_ffn_num_layers': 2}
[7]:
# Here are the state_dict that is stored in v1 model
pprint(list(model_v1_dict['state_dict'].keys()))
['encoder.encoder.0.cached_zero_vector',
 'encoder.encoder.0.W_i.weight',
 'encoder.encoder.0.W_h.weight',
 'encoder.encoder.0.W_o.weight',
 'encoder.encoder.0.W_o.bias',
 'readout.1.weight',
 'readout.1.bias',
 'readout.4.weight',
 'readout.4.bias']

Convert loaded v1 model dictionary into v2 model dictionary#

[8]:
model_v2_dict = convert_model_dict_v1_to_v2(model_v1_dict)
[9]:
# Here are all the keys in the converted model
pprint(list(model_v2_dict.keys()))
['epoch',
 'global_step',
 'pytorch-lightning_version',
 'state_dict',
 'loops',
 'callbacks',
 'optimizer_states',
 'lr_schedulers',
 'hparams_name',
 'hyper_parameters']
[10]:
# Here are all the keys in the converted state_dict
pprint(list(model_v2_dict['state_dict'].keys()))
['message_passing.W_i.weight',
 'message_passing.W_h.weight',
 'message_passing.W_o.weight',
 'message_passing.W_o.bias',
 'predictor.ffn.0.0.weight',
 'predictor.ffn.0.0.bias',
 'predictor.ffn.1.2.weight',
 'predictor.ffn.1.2.bias',
 'predictor.output_transform.mean',
 'predictor.output_transform.scale',
 'predictor.criterion.task_weights']
[11]:
# Here are all the keys in the converted hyper_parameters
pprint(list(model_v2_dict['hyper_parameters'].keys()))
['batch_norm',
 'metrics',
 'warmup_epochs',
 'init_lr',
 'max_lr',
 'final_lr',
 'message_passing',
 'agg',
 'predictor']

Save#

[12]:
torch.save(model_v2_dict, model_v2_output_path)

Load converted model#

[13]:
mpnn = MPNN.load_from_checkpoint(model_v2_output_path)
[14]:
# now visually check the converted model is what is expected
mpnn
[14]:
MPNN(
  (message_passing): BondMessagePassing(
    (W_i): Linear(in_features=147, out_features=300, bias=False)
    (W_h): Linear(in_features=300, out_features=300, bias=False)
    (W_o): Linear(in_features=433, out_features=300, bias=True)
    (dropout): Dropout(p=0.0, inplace=False)
    (tau): ReLU()
    (V_d_transform): Identity()
    (graph_transform): Identity()
  )
  (agg): MeanAggregation()
  (bn): Identity()
  (predictor): RegressionFFN(
    (ffn): MLP(
      (0): Sequential(
        (0): Linear(in_features=300, out_features=300, bias=True)
      )
      (1): Sequential(
        (0): ReLU()
        (1): Dropout(p=0.0, inplace=False)
        (2): Linear(in_features=300, out_features=1, bias=True)
      )
    )
    (criterion): MSE(task_weights=[[1.0]])
    (output_transform): UnscaleTransform()
  )
  (X_d_transform): Identity()
  (metrics): ModuleList(
    (0): RMSE(task_weights=[[1.0]])
    (1): MSE(task_weights=[[1.0]])
  )
)