model_original.py
AI for Chemistry/code/chemprop_VP-main (1)/chemprop_VP-main/chemprop/models/model_original.py
from typing import List, Union, Tuple
import numpy as np
from rdkit import Chem
import torch
import torch.nn as nn
from .mpn import MPN
from chemprop.args import TrainArgs
from chemprop.features import BatchMolGraph
from chemprop.nn_utils import get_activation_function, initialize_weights
class MoleculeModel(nn.Module):
"""A :class:`MoleculeModel` is a model which contains a message passing network following by feed-forward layers."""
def __init__(self, args: TrainArgs):
"""
:param args: A :class:`~chemprop.args.TrainArgs` object containing model arguments.
"""
super(MoleculeModel, self).__init__()
self.classification = args.dataset_type == 'classification'
self.multiclass = args.dataset_type == 'multiclass'
self.fp_method = args.fp_method
self.output_size = args.num_tasks
if self.multiclass:
self.output_size *= args.multiclass_num_classes
if self.classification:
self.sigmoid = nn.Sigmoid()
if self.multiclass:
self.multiclass_softmax = nn.Softmax(dim=2)
self.create_encoder(args)
self.create_ffn(args)
initialize_weights(self)
def create_encoder(self, args: TrainArgs) -> None:
"""
Creates the message passing encoder for the model.
:param args: A :class:`~chemprop.args.TrainArgs` object containing model arguments.
"""
self.encoder = MPN(args)
if args.checkpoint_frzn is not None:
if args.freeze_first_only: # Freeze only the first encoder
for param in list(self.encoder.encoder.children())[0].parameters():
param.requires_grad = False
else: # Freeze all encoders
for param in self.encoder.parameters():
param.requires_grad = False
def create_ffn(self, args: TrainArgs) -> None:
"""
Creates the feed-forward layers for the model.
:param args: A :class:`~chemprop.args.TrainArgs` object containing model arguments.
"""
self.multiclass = args.dataset_type == 'multiclass'
if self.multiclass:
self.num_classes = args.multiclass_num_classes
if args.features_only:
first_linear_dim = args.features_size
else:
first_linear_dim = args.hidden_size * args.number_of_molecules
if args.use_input_features:
first_linear_dim += args.features_size
if args.atom_descriptors == 'descriptor':
first_linear_dim += args.atom_descriptors_size
dropout = nn.Dropout(args.dropout)
activation = get_activation_function(args.activation)
# Create FFN layers ['molecular', 'atomic', 'hybrid_dim0', 'hybrid_dim1']
if self.fp_method == 'molecular':
if args.ffn_num_layers == 1:
ffn = [
dropout,
nn.Linear(first_linear_dim, self.output_size)
]
else:
ffn = [
dropout,
nn.Linear(first_linear_dim, args.ffn_hidden_size)
]
for _ in range(args.ffn_num_layers - 2):
ffn.extend([
activation,
dropout,
nn.Linear(args.ffn_hidden_size, args.ffn_hidden_size),
])
ffn.extend([
activation,
dropout,
nn.Linear(args.ffn_hidden_size, self.output_size),
])
elif self.fp_method in ['atomic', 'hybrid_dim0']:
if args.ffn_num_layers == 1:
ffn = [
TimeDistributed_wrapper(dropout),
# TODO: important bias set to False !!
TimeDistributed_wrapper(
nn.Linear(first_linear_dim, self.output_size))
]
else:
ffn = [
TimeDistributed_wrapper(dropout),
# TODO: important bias set to False !!
TimeDistributed_wrapper(
nn.Linear(first_linear_dim, args.ffn_hidden_size))
]
for _ in range(args.ffn_num_layers - 2):
ffn.extend([
TimeDistributed_wrapper(activation),
TimeDistributed_wrapper(dropout),
# TODO: important bias set to False !!
TimeDistributed_wrapper(
nn.Linear(args.ffn_hidden_size, args.ffn_hidden_size)),
])
ffn.extend([
TimeDistributed_wrapper(activation),
TimeDistributed_wrapper(dropout),
# TODO: important bias set to False !!
TimeDistributed_wrapper(
nn.Linear(args.ffn_hidden_size, self.output_size, bias=True)),
#LambdaLayer(lambda x: torch.sum(x, 1)),
])
# If spectra model, also include spectra activation
if args.dataset_type == 'spectra':
if args.spectra_activation == 'softplus':
spectra_activation = nn.Softplus()
else: # default exponential activation which must be made into a custom nn module
class nn_exp(torch.nn.Module):
def __init__(self):
super(nn_exp, self).__init__()
def forward(self, x):
return torch.exp(x)
spectra_activation = nn_exp()
ffn.append(spectra_activation)
# Create FFN model
self.ffn = nn.Sequential(*ffn)
if args.checkpoint_frzn is not None:
if args.frzn_ffn_layers > 0:
# Freeze weights and bias for given number of layers
for param in list(self.ffn.parameters())[0:2*args.frzn_ffn_layers]:
param.requires_grad = False
def fingerprint(self,
batch: Union[List[List[str]], List[List[Chem.Mol]], List[List[Tuple[Chem.Mol, Chem.Mol]]], List[BatchMolGraph]],
features_batch: List[np.ndarray] = None,
atom_descriptors_batch: List[np.ndarray] = None,
atom_features_batch: List[np.ndarray] = None,
bond_features_batch: List[np.ndarray] = None,
fingerprint_type='MPN') -> torch.FloatTensor:
"""
Encodes the latent representations of the input molecules from intermediate stages of the model.
:param batch: A list of list of SMILES, a list of list of RDKit molecules, or a
list of :class:`~chemprop.features.featurization.BatchMolGraph`.
The outer list or BatchMolGraph is of length :code:`num_molecules` (number of datapoints in batch),
the inner list is of length :code:`number_of_molecules` (number of molecules per datapoint).
:param features_batch: A list of numpy arrays containing additional features.
:param atom_descriptors_batch: A list of numpy arrays containing additional atom descriptors.
:param fingerprint_type: The choice of which type of latent representation to return as the molecular fingerprint. Currently
supported MPN for the output of the MPNN portion of the model or last_FFN for the input to the final readout layer.
:return: The latent fingerprint vectors.
"""
if fingerprint_type == 'MPN':
return self.encoder(batch, features_batch, atom_descriptors_batch,
atom_features_batch, bond_features_batch)
elif fingerprint_type == 'last_FFN':
return self.ffn[:-1](self.encoder(batch, features_batch, atom_descriptors_batch,
atom_features_batch, bond_features_batch))
else:
raise ValueError(
f'Unsupported fingerprint type {fingerprint_type}.')
def forward(self,
batch: Union[List[List[str]], List[List[Chem.Mol]], List[List[Tuple[Chem.Mol, Chem.Mol]]], List[BatchMolGraph]],
features_batch: List[np.ndarray] = None,
atom_descriptors_batch: List[np.ndarray] = None,
atom_features_batch: List[np.ndarray] = None,
bond_features_batch: List[np.ndarray] = None,
sum_contribution=True) -> torch.FloatTensor:
"""
Runs the :class:`MoleculeModel` on input.
:param batch: A list of list of SMILES, a list of list of RDKit molecules, or a
list of :class:`~chemprop.features.featurization.BatchMolGraph`.
The outer list or BatchMolGraph is of length :code:`num_molecules` (number of datapoints in batch),
the inner list is of length :code:`number_of_molecules` (number of molecules per datapoint).
:param features_batch: A list of numpy arrays containing additional features.
:param atom_descriptors_batch: A list of numpy arrays containing additional atom descriptors.
:param atom_features_batch: A list of numpy arrays containing additional atom features.
:param bond_features_batch: A list of numpy arrays containing additional bond features.
:param sum_contribution: Return the summed value or the individual atomic contribution.
:return: The output of the :class:`MoleculeModel`, containing a list of property predictions
"""
hidden = self.encoder(batch, features_batch, atom_descriptors_batch,
atom_features_batch, bond_features_batch)
if self.fp_method == 'molecular':
output = self.ffn(hidden)
else:
output = self.ffn(hidden)
padding_target = hidden.sum(dim=2).view(
output.shape[0], -1, 1).bool().float()
output = output*padding_target
if sum_contribution:
output = torch.sum(output, 1)
else:
pass
# Don't apply sigmoid during training b/c using BCEWithLogitsLoss
if self.classification and not self.training:
output = self.sigmoid(output)
if self.multiclass:
# batch size x num targets x num classes per target
output = output.reshape((output.size(0), -1, self.num_classes))
if not self.training:
# to get probabilities during evaluation, but not during training as we're using CrossEntropyLoss
output = self.multiclass_softmax(output)
return output
class LambdaLayer(nn.Module):
def __init__(self, lambda_function):
super(LambdaLayer, self).__init__()
self.lambda_function = lambda_function
def forward(self, x):
return self.lambda_function(x)
class TimeDistributed_wrapper(nn.Module):
def __init__(self, module, batch_first=False):
super(TimeDistributed_wrapper, self).__init__()
self.module = module
self.batch_first = batch_first
def forward(self, x):
if len(x.size()) <= 2:
return self.module(x)
# Squash samples and timesteps into a single axis
# (samples * timesteps, input_size)
x_reshape = x.contiguous().view(-1, x.size(-1))
y = self.module(x_reshape)
# reshape Y
if self.batch_first:
# (samples, timesteps, output_size)
y = y.contiguous().view(x.size(0), -1, y.size(-1))
else:
# (timesteps, samples, output_size)
y = y.view(-1, x.size(1), y.size(-1))
return y
if __name__ == "__main__":
args = TrainArgs()
print(args)
Artículos relacionados
cosmo_to_s_profile_ver_1.1.1.py
cosmo_to_s_profile_ver_1.1.1.py — python source code from the AI for Chemistry learning materials (AI for Chemistry/2025_COSMO/data/s-profiles-all/List-9/cosmo_to_s_profile_ver_1.1.1.py).
Leer artículo →area.py
area.py — python source code from the AI for Chemistry learning materials (AI for Chemistry/2025_COSMO/data/s-profiles-all/area_volume-org/area.py).
Leer artículo →area_volume.py
area_volume.py — python source code from the AI for Chemistry learning materials (AI for Chemistry/2025_COSMO/data/s-profiles-all/area_volume-org/area_volume.py).
Leer artículo →volume.py
volume.py — python source code from the AI for Chemistry learning materials (AI for Chemistry/2025_COSMO/data/s-profiles-all/area_volume-org/volume.py).
Leer artículo →check_final_CID.py
check_final_CID.py — python source code from the AI for Chemistry learning materials (AI for Chemistry/2025_COSMO/data/s-profiles-all/check/check_final_CID.py).
Leer artículo →check_for_CID.py
check_for_CID.py — python source code from the AI for Chemistry learning materials (AI for Chemistry/2025_COSMO/data/s-profiles-all/check/check_for_CID.py).
Leer artículo →