Error 'object not callable'

Hi so i was using gnn explainer in this GNN based model using OPENPOM model and pytorch’s GNNExplainer algorithm. But i was encountering error while generating explanation using the below code. Any help would be beneficial.

!wget https://raw.githubusercontent.com/ARY2260/openpom/main/openpom/data/curated_datasets/curated_GS_LF_merged_4983.csv

input_file = 'curated_GS_LF_merged_4983.csv' # or new downloaded file path
featurizer = GraphFeaturizer()
smiles_field = 'nonStereoSMILES' # column that contains SMILES
loader = dc.data.CSVLoader(tasks=TASKS,
feature_field=smiles_field,
featurizer=featurizer)
dataset = loader.create_dataset(inputs=[input_file])
n_tasks = len(dataset.tasks)
len(dataset)

randomstratifiedsplitter = dc.splits.RandomStratifiedSplitter()
train_dataset, test_dataset, valid_dataset = randomstratifiedsplitter.train_valid_test_split(dataset, frac_train = 0.8, frac_valid = 0.1, frac_test = 0.1, seed = 1)

print("train_dataset: ", len(train_dataset))
print("valid_dataset: ", len(valid_dataset))
print("test_dataset: ", len(test_dataset))
train_ratios = get_class_imbalance_ratio(train_dataset)
assert len(train_ratios) == n_tasks
learning_rate = dc.models.optimizers.ExponentialDecay(initial_rate=0.001, decay_rate=0.5, decay_steps=32*20, staircase=True)
model = MPNNPOMModel(n_tasks = n_tasks,
                            batch_size=128,
                            learning_rate=learning_rate,
                            class_imbalance_ratio = train_ratios,
                            loss_aggr_type = 'sum',
                            node_out_feats = 100,
                            edge_hidden_feats = 75,
                            edge_out_feats = 100,
                            num_step_message_passing = 5,
                            mpnn_residual = True,
                            message_aggregator_type = 'sum',
                            mode = 'classification',
                            number_atom_features = GraphConvConstants.ATOM_FDIM,
                            number_bond_features = GraphConvConstants.BOND_FDIM,
                            n_classes = 1,
                            readout_type = 'set2set',
                            num_step_set2set = 3,
                            num_layer_set2set = 2,
                            ffn_hidden_list= [392, 392],
                            ffn_embeddings = 256,
                            ffn_activation = 'relu',
                            ffn_dropout_p = 0.12,
                            ffn_dropout_at_input_no_act = False,
                            weight_decay = 1e-5,
                            self_loop = False,
                            optimizer_name = 'adam',
                            log_frequency = 32,
                            model_dir = './examples/experiments',
                            device_name='cuda')

explainer = Explainer(
    model=model,
    algorithm=GNNExplainer(epochs=200),
    explanation_type='model',
    node_mask_type='attributes',
    edge_mask_type='object',
    model_config=dict(
        mode='multiclass_classification',
        task_level='graph',
        return_type='log_probs',
    ),
)

batch = torch.zeros(data.x.size(0), dtype=torch.long)

with torch.no_grad():
    output = model(data.x, data.edge_index, batch)
print(f"Model output shape: {output.shape}")
print("Forward pass successful!")
print("Running explainer...")
exp = explainer(
    x=data.x,
    edge_index=data.edge_index,
    target=label_id,
    batch=batch
)
print(f"Explanation for '{TASKS[label_id]}' odor:")
print(f"Node importance: max={exp.node_mask.max().item():.3f}, min={exp.node_mask.min().item():.3f}")
print(f"Edge importance: max={exp.edge_mask.max().item():.3f}, min={exp.edge_mask.min().item():.3f}")
   
    # Optionally, if you need to visualize the results
print("Explanation generated successfully!") 

Outcome : 'MPNNPOMModel' object is not callable

while the explainer uses mode='multiclass_classification', and is set as multiclass, and the problem that OPENPOM solves is multi label, i was handling it already from extracting 571 smile strings that have only single label as their discriptor, so it sorts of represent multi class so considering that the dataset is already converted to PyG format , please anyone help how to resolve this error.

Hi Abhishek!

Perhaps ‘model’ isn’t a proper pytorch “model” derived from Module and doesn’t have
a proper forward() method.

Try printing out type (model).mro() to see what class model is and what classes it is
derived from. Try printing out dir (model) to see whether model has a forward method.
Try evaluating model.forward (without parentheses or function call) to see whether it’s
a “bound method.”

Consider:

>>> import torch
>>> torch.__version__
'2.7.0+cu128'
>>> lin = torch.nn.Linear (2, 3)
>>> type (lin).mro()
[<class 'torch.nn.modules.linear.Linear'>, <class 'torch.nn.modules.module.Module'>, <class 'object'>]
>>> 'forward' in dir (lin)
True
>>> lin ('incorrect_input_to_a_Linear')
Traceback (most recent call last):
...
    return F.linear(input, self.weight, self.bias)
           ~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: linear(): argument 'input' (position 1) must be Tensor, not str

Best.

K. Frank

hi thanks,
so for resolving the proper forward method i used a custom wrapper and then passed in the explainer

class MPNNPOMWrapper(Module):
    def __init__(self, dc_model, pyg_graphs, featurizer):
        super().__init__()
        self.dc_model = dc_model
        self.pyg_graphs = pyg_graphs
        self.featurizer = featurizer

    def forward(self, x, edge_index, batch):
        unique_graphs = batch.unique()
        selected_graphs = [self.pyg_graphs[i] for i in unique_graphs.tolist()]

        dc_graphs = []
        for g in selected_graphs:
            mol = Chem.MolFromSmiles(g.smiles)
            if mol is None:
                continue
            try:
                feats = self.featurizer.featurize([mol])
                if isinstance(feats, list) and len(feats) > 0 and feats[0] is not None:
                    dc_graphs.append(feats[0])
                else:
                    print(f"Warning: Featurization failed for SMILES: {g.smiles}, check featurization method")
                    from deepchem.feat.graph_data import GraphData
                    dc_graphs.append(GraphData(node_features=np.array([[0]]),  
                                             edge_index=np.array([[], []], dtype=np.int64),
                                             edge_features=np.empty((0, 1))))

            except Exception as e:
                print(f"Featurization failed for SMILES: {g.smiles} — {e}")
                from deepchem.feat.graph_data import GraphData
                dc_graphs.append(GraphData(node_features=np.array([[0]]), 
                                         edge_index=np.array([[], []], dtype=np.int64),
                                         edge_features=np.empty((0, 1))))
        if not dc_graphs:
            return torch.empty((0, self.dc_model.n_tasks), dtype=torch.float32) 

        batch_graph = BatchGraphData(dc_graphs)
        preds = self.dc_model.predict_on_batch(batch_graph)
        return torch.tensor(preds, dtype=torch.float32)

and when i used this and tried to generate the explanation

wrapper = MPNNPOMWrapper(dc_model=model, pyg_graphs=pyg_ds,featurizer=featurizer)

with torch.no_grad():
   output = wrapper(data.x, data.edge_index, batch)
   print(f"Model output shape: {output.shape}")

outcome was:

Warning: Featurization failed for SMILES: CCO, check featurization method
IndexError: tuple index out of range

maybe the fault is that when i converted the deepchem dataset to PyG format the featurisation
stopped working because the featuriser worked exclusively for DeepChem format. Maybe this is the issue now but idk how to handle this.
The featuriser that i am using is from openpom.feat.graph_featurizer import GraphFeaturizer
Can you please help resolving this @KFrank Thank you.