| |
| |
| |
| |
|
|
| import logging |
| from typing import Tuple, Dict |
| import torch |
| from torch import nn |
| from esm.data import Alphabet |
| from utils.tensor import add_eos_bos, is_1hot_tensor |
| from torch import nn |
| from torch.nn import functional as F |
| from abc import ABC, abstractmethod |
| import hydra |
| import os |
|
|
| from utils.linear_projection import LinearProjectionDistogramModel |
|
|
| logger = logging.getLogger(__name__) |
|
|
| PDB_LOADER_PARAM_REGISTRY = { |
| 'LinearProjectionDist-1A': { |
| "DMIN": 2.5, |
| "DMAX": 20.0, |
| "DBINS": 17, |
| "ABINS": 17, |
| "PHI_BINS": 7, |
| "WMIN": 0.8, |
| "LMIN": 150, |
| "LMAX": 400, |
| "EPOCHS": 10, |
| "NCPU": 8, |
| "SLICE": "CONT", |
| "contact_bin_cutoff": (0, 5) |
| }, |
| } |
|
|
|
|
| class WrapStruct(nn.Module, ABC): |
| """ |
| Wraps structure models used by Designer. |
| Companion of WrapLM. |
| """ |
| def __init__(self, model, vocab, has_eos=False): |
| super().__init__() |
| self.model = model |
| self.vocab = vocab |
| self.has_eos = has_eos |
|
|
| @abstractmethod |
| def forward(self, seq1h, pos_idx=None): |
| raise NotImplementedError |
|
|
| @property |
| def msa_based(self): |
| return False |
|
|
|
|
| def load(vocab: Alphabet) -> Tuple[nn.Module, Dict]: |
| |
| pdb_loader_params = PDB_LOADER_PARAM_REGISTRY['LinearProjectionDist-1A'] |
| model = load_model() |
| has_eos = True |
| return WrapStructModel(model, vocab, has_eos), pdb_loader_params |
|
|
|
|
| def load_model(): |
| model_url_path = 'https://dl.fbaipublicfiles.com/fair-esm/examples/lm_design/linear_projection_model.pt' |
| local_model_path = hydra.utils.to_absolute_path('./linear_projection_model.pt') |
| if not os.path.exists(local_model_path): |
| logger.info(f'Downloading linear projection model from {model_url_path} to {local_model_path}') |
| os.system(f'wget {model_url_path} -O {local_model_path}') |
| |
| |
| state = torch.load(local_model_path, map_location='cpu') |
| |
| model = LinearProjectionDistogramModel() |
| model_state = state['model'] |
| model.load_state_dict(model_state) |
| from esm.pretrained import esm2_t33_650M_UR50D |
| base_model, alphabet = esm2_t33_650M_UR50D() |
| model.base_model = base_model |
| return model |
| |
|
|
| class WrapStructModel(WrapStruct): |
| def forward(self, seq1h, pos_idx=None): |
| assert is_1hot_tensor(seq1h) |
| B, L, K = seq1h.shape |
| |
| eos_idx = self.vocab.eos_idx if self.has_eos else None |
| seq1h = add_eos_bos(seq1h, bos_idx=self.vocab.cls_idx, eos_idx=eos_idx) |
| |
| src_lengths = torch.full((B,), L + 1 + int(self.has_eos)).to(seq1h).long() |
|
|
| |
| |
| |
| |
| embed_dim = self.model.base_model.embed_tokens.weight.size(0) |
| if embed_dim != K: |
| seq1h = torch.cat([ |
| seq1h, |
| torch.zeros(B, L+1, embed_dim - K).to(seq1h) |
| ], -1) |
|
|
| |
| out_dict = self.model(seq1h, src_lengths=src_lengths) |
| return self._process_output(out_dict) |
|
|
| @property |
| def lm(self): |
| return self.model.base_model |
|
|
|
|
| @staticmethod |
| def _process_output(out_dict): |
| for k in ('logits', 'theta_logits', 'phi_logits', 'omega_logits'): |
| out_dict[k] = out_dict[k].permute(0, 2, 3, 1).contiguous() |
| |
| if 'logits' in out_dict: |
| out_dict['dist_logits'] = out_dict['logits'] |
| del out_dict['logits'] |
| for k in [k for k in out_dict if k.endswith('_logits')]: |
| targetname = k[:-len('_logits')] |
| out_dict[f'p_{targetname}'] = F.softmax(out_dict[k], -1) |
| return out_dict |
|
|