LBM-EPFL / PeSTo

Geometric deep learning method to predict protein binding interfaces from a protein structure.
https://pesto.epfl.ch
Other
108 stars 15 forks source link

AttributeError: module 'numpy' has no attribute 'object' #8

Closed yzhang-github-pub closed 1 year ago

yzhang-github-pub commented 1 year ago

Dear authors,

Thanks for developing this very useful tool. I am testing PeSTo, and got an error with numpy:

_model/save/i_v4_1_2021-09-07_11-21/src/structure.py", line 99, in tag_hetatm_chains structure = tag_hetatmchains(structure) AttributeError: module 'numpy' has no attribute 'object'

This is perhaps a numpy version issue. When I created the environment, conda automatically installed numpy 1.25. Can you please tell the numpy version that works? Or even better specify versions in pesto.yml file? Thanks.

rubenalv commented 1 year ago

@yzhang-github-pub, I could create a working environment with the conda installation I submitted in https://github.com/LBM-EPFL/PeSTo/issues/2 My numpy version is 1.25.

With that environment I can run the example in PeSTo/ /apply_model.ipynb, which I have edited to convert into a python script below. I tested it running it interactively from PeSTo/ install folder, using the example in PeSTo/examples/issue_19_04_2023/2CUA_A.pdb, and compared the output with the website https://pesto.epfl.ch/. Note that for the results to match, you have to select "chainA" only on the website (if you select chainA and chainB, the PPI probabilities change).

import os
import sys
import h5py
import json
import numpy as np
import torch as pt
import pandas as pd
import matplotlib.pyplot as plt
from tqdm import tqdm
from glob import glob
from src.dataset import StructuresDataset, collate_batch_features, select_by_sid, select_by_interface_types
from src.data_encoding import encode_structure, encode_features, extract_topology, categ_to_resnames, resname_to_categ
from src.structure import data_to_structure, encode_bfactor, concatenate_chains, split_by_chain
from src.structure_io import save_pdb, read_pdb
from src.scoring import bc_scoring, bc_score_names
# data parameters
data_path = "examples/issue_19_04_2023"

# model parameters
save_path = "model/save/i_v4_1_2021-09-07_11-21"  # 91

# select saved model
model_filepath = os.path.join(save_path, 'model_ckpt.pt')
# add module to path
if save_path not in sys.path:
    sys.path.insert(0, save_path)

# load functions
from config import config_model, config_data
from data_handler import Dataset
from model import Model
# define device
device = pt.device("cpu")

# create model
model = Model(config_model)
# reload model
model.load_state_dict(pt.load(model_filepath, map_location=pt.device("cpu")))
# set model to inference
model = model.eval().to(device)
# find pdb files and ignore already predicted oins
pdb_filepaths = glob(os.path.join(data_path, "*.pdb"), recursive=True)
pdb_filepaths = [fp for fp in pdb_filepaths if "_i" not in fp]
# create dataset loader with preprocessing
dataset = StructuresDataset(pdb_filepaths, with_preprocessing=True)
# debug print
print(len(dataset))
# run model on all subunits
with pt.no_grad():
    for subunits, filepath in tqdm(dataset):
        # concatenate all chains together
        structure = concatenate_chains(subunits)
        # encode structure and features
        X, M = encode_structure(structure)
        #q = pt.cat(encode_features(structure), dim=1)
        q = encode_features(structure)[0]
        # extract topology
        ids_topk, _, _, _, _ = extract_topology(X, 64)
        # pack data and setup sink (IMPORTANT)
        X, ids_topk, q, M = collate_batch_features([[X, ids_topk, q, M]])
        # run model
        z = model(X.to(device), ids_topk.to(device), q.to(device), M.float().to(device))
        # for all predictions
        for i in range(z.shape[1]):
            # prediction
            p = pt.sigmoid(z[:,i])
            # encode result
            structure = encode_bfactor(structure, p.cpu().numpy())
            # save results
            output_filepath = filepath[:-4]+'_i{}.pdb'.format(i)
            save_pdb(split_by_chain(structure), output_filepath)
yzhang-github-pub commented 1 year ago

Thanks @rubenalv !

I compared my converted python script with yours. The difference was I re-organized the import statements so that all modules to be imported from git repo were in one block:

... _# select saved model model_filepath = os.path.join(save_path, 'model_ckpt.pt')

add module to path

if save_path not in sys.path: sys.path.insert(0, save_path)

from src.dataset import StructuresDataset, collate_batch_features, select_by_sid, select_by_interface_types from src.data_encoding import encode_structure, encode_features, extract_topology, categ_to_resnames, resname_to_categ from src.structure import data_to_structure, encode_bfactor, concatenate_chains, split_by_chain from src.structure_io import save_pdb, read_pdb from src.scoring import bc_scoring, bc_score_names

from config import config_model, config_data

from data_handler import Dataset # not used

from model import Model_

...

It seems moving "sys.path.insert(0, save_path)" before the 5 lines of "from src. import " caused the error. I still don't understand why, but the order seems to be important.

Thanks again.

rubenalv commented 1 year ago

@yzhang-github-pub, the sys.path.insert(0, save_path) line allows the modules within /main/model/save/i_v4_1_2021-09-07_11-21 to be loaded. In the script I wrote I use the modules in /main/model instead, so it may be that they are slightly different. I mention this because I had an issue with /main/src/structure_io.py and main/model/save/i_v4_1_2021-09-07_11-21/src/structure_io.py not being exactly the same when compared with linux diff. With one I could get a file identical to 2CUA_A_i0.pdb , and with the other .py I could not (again compared initially with diff), just because a difference in which columns each .py wrote in the pdb output. Glad you solved your issue!

lfkrapp commented 1 year ago

The chain naming in the PDB format is not ideal, especially for other molecules (HETATM) associated with the closest chain. There can be also multiple copies of the same chain name (symmetric structures). So I'm appending a tag to the chain name to indicate differentiate between those. I represent the chain name per atom using an array of string. However the unicode string in Numpy has a fixed length (cannot append to the string directly) and they removed the generic numpy.object type recent version of numpy.

I updated the code with a quick fix in commit d00b0d63f4c374cc942288b0319c36a3ea86fc0f

For now, it is using a buffer size of 10 characters for the chain name. The solution is not optimal but it should fix this issue in all cases.

The numpy.object version of the code works with numpy 1.23