Download model/Protenix-pxd/protenix/data/compute_esm.py from OneScience-Group/PXDesign: direct link, hf CLI and curl.
- Browser
- Download file 7.84 kB
-
https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/Protenix-pxd/protenix/data/compute_esm.py
- Command line
-
hf download hf://OneScience-Group/PXDesign/model/Protenix-pxd/protenix/data/compute_esm.py
-
curl -L -o compute_esm.py https://huggingface.co/OneScience-Group/PXDesign/resolve/main/model/Protenix-pxd/protenix/data/compute_esm.py
7.84 kB
| # Copyright 2024 ByteDance and/or its affiliates. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import argparse | |
| import os | |
| import pandas as pd | |
| import torch | |
| from esm import FastaBatchedDataset, pretrained | |
| from tqdm.auto import tqdm | |
| ESM_CONFIG = { | |
| "esm2-3b": { | |
| "type": "esm2", | |
| "model_path": "esm2_t36_3B_UR50D.pt", | |
| "emb_dim": 2560, | |
| "n_layers": 36, | |
| }, | |
| "esm2-3b-ism": { | |
| "type": "esm2", | |
| "model_path": "esm2_t36_3B_UR50D_ism.pt", | |
| "emb_dim": 2560, | |
| "n_layers": 36, | |
| }, # https://www.biorxiv.org/content/10.1101/2024.11.08.622579v2 | |
| } | |
| def _load_esm2_model(model_path): | |
| if os.path.exists(model_path): | |
| model, alphabet = pretrained.load_model_and_alphabet_local(model_path) | |
| else: | |
| model, alphabet = pretrained.load_model_and_alphabet( | |
| os.path.splitext(os.path.basename(model_path))[0] | |
| ) | |
| return model, alphabet | |
| def load_esm_model(model_name, local_esm_dir="release_data/checkpoint"): | |
| local_model_path = os.path.join(local_esm_dir, ESM_CONFIG[model_name]["model_path"]) | |
| if os.path.exists(local_model_path): | |
| print("Try to load ESM language model from ", local_model_path) | |
| if "ism" in model_name and not os.path.exists(local_model_path): | |
| raise RuntimeError( | |
| f"esm2-3b-ism model: {local_model_path} does not exist \n" | |
| + "this model can not be download from fair-esm, \n" | |
| + "download it from https://af3-dev.tos-cn-beijing.volces.com/release_model/esm2_t36_3B_UR50D_ism.pt" | |
| ) | |
| if model_name.startswith("esm2"): | |
| model, alphabet = _load_esm2_model(local_model_path) | |
| model.eval() | |
| if torch.cuda.is_available(): | |
| model = model.cuda() | |
| return model, alphabet | |
| def _check_files_exist(save_dir, labels): | |
| return all( | |
| [os.path.exists(os.path.join(save_dir, label + ".pt")) for label in labels] | |
| ) | |
| def compute_ESM_embeddings( | |
| model_name, | |
| model, | |
| alphabet, | |
| labels, | |
| sequences, | |
| save_dir, | |
| toks_per_batch=4096, | |
| truncation_seq_length=1022, | |
| ): | |
| if model_name.startswith("esm2"): | |
| embeddings = compute_esm2_embeddings( | |
| model, | |
| alphabet, | |
| labels, | |
| sequences, | |
| save_dir, | |
| toks_per_batch, | |
| truncation_seq_length, | |
| ) | |
| return embeddings | |
| # Adapt from Corso, Gabriele, et al. "Diffdock: Diffusion steps, twists, and turns for molecular docking." | |
| # URL: https://github.com/gcorso/DiffDock/blob/main/utils/inference_utils.py | |
| def compute_esm2_embeddings( | |
| model, | |
| alphabet, | |
| labels, | |
| sequences, | |
| save_dir, | |
| toks_per_batch=4096, | |
| truncation_seq_length=1022, | |
| ): | |
| dataset = FastaBatchedDataset(labels, sequences) | |
| batches = dataset.get_batch_indices(toks_per_batch, extra_toks_per_seq=1) | |
| data_loader = torch.utils.data.DataLoader( | |
| dataset, | |
| collate_fn=alphabet.get_batch_converter(truncation_seq_length), | |
| batch_sampler=batches, | |
| ) | |
| repr_layer = model.num_layers | |
| embeddings = {} | |
| with torch.no_grad(): | |
| for batch_idx, (labels, strs, toks) in enumerate(tqdm(data_loader)): | |
| print( | |
| f"Processing {batch_idx + 1} of {len(batches)} batches ({toks.size(0)} sequences)" | |
| ) | |
| if _check_files_exist(save_dir, labels): | |
| continue | |
| if torch.cuda.is_available(): | |
| toks = toks.to(device="cuda", non_blocking=True) | |
| out = model(toks, repr_layers=[repr_layer], return_contacts=False) | |
| representation = out["representations"][repr_layer].to(device="cpu") | |
| for i, label in enumerate(labels): | |
| truncate_len = min(truncation_seq_length, len(strs[i])) | |
| embeddings[label] = representation[i, 1 : truncate_len + 1].clone() | |
| save_path = os.path.join(save_dir, label + ".pt") | |
| torch.save(embeddings[label], save_path) | |
| return embeddings | |
| def pdb_sequences_iterator( | |
| input_path="./scripts/msa/data/pdb_seqs/pdb_seq.csv", | |
| save_path="./scripts/msa/data/pdb_seqs/pdb_labels_seqs.csv", | |
| start_id=0, | |
| end_id=-1, | |
| ): | |
| if os.path.exists(save_path): | |
| df_seq = pd.read_csv(save_path) | |
| else: | |
| df = pd.read_csv(input_path) | |
| # Protein only | |
| df = df[df["mol_type"] == "protein"] | |
| # Sequence name | |
| df["pdb_entity_id"] = df["pdb_id"] + "_" + df["entity_id"].astype(str) | |
| # Group by 'seq' | |
| df_seq = ( | |
| df.groupby("seq")["pdb_entity_id"] | |
| .apply(lambda x: ",".join(x)) | |
| .reset_index() | |
| ) | |
| # Use the first pdb_entity_id as the label | |
| df_seq["seq_label"] = df_seq["pdb_entity_id"].apply(lambda x: x.split(",")[0]) | |
| assert df_seq["seq_label"].nunique() == len(df_seq) | |
| # Get a part id | |
| df_seq["part_id"] = df_seq["pdb_entity_id"].apply(lambda x: x[1:3]) | |
| df_seq.to_csv(save_path) | |
| if end_id == -1: | |
| end_id = len(df_seq) | |
| df_seq = df_seq[start_id:end_id] | |
| part_counts = dict(df_seq["part_id"].value_counts()) | |
| for part_id, count in part_counts.items(): | |
| df_part = df_seq[df_seq["part_id"] == part_id] | |
| print(f"Part {part_id}: {len(df_part)} sequences.") | |
| yield part_id, df_part["seq_label"].tolist(), df_part["seq"].tolist() | |
| def process_pdb_dataset( | |
| model_name, root_save_dir, pdb_seq_path, pdb_seq_label_path, start_id=0, end_id=-1 | |
| ): | |
| model, alphabet = load_esm_model(model_name) | |
| seq_iterator = pdb_sequences_iterator( | |
| pdb_seq_path, pdb_seq_label_path, start_id, end_id | |
| ) | |
| error_parts = [] | |
| for part_id, labels, sequences in seq_iterator: | |
| save_dir = os.path.join(root_save_dir, f"{part_id}") | |
| if not os.path.exists(save_dir): | |
| os.makedirs(save_dir) | |
| print(f"[{part_id}] Generating ESM language model embeddings") | |
| lm_embeddings = compute_ESM_embeddings( | |
| model_name, | |
| model, | |
| alphabet, | |
| labels, | |
| sequences, | |
| save_dir, | |
| truncation_seq_length=4094, | |
| toks_per_batch=16384, | |
| ) | |
| print(f"[{part_id}] Processed {len(lm_embeddings)} sequences in total. Done!") | |
| print("Error parts: ", error_parts) | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--model_name", type=str, choices=list(ESM_CONFIG.keys())) | |
| parser.add_argument("--start_id", type=int, default=0) | |
| parser.add_argument("--end_id", type=int, default=-1) | |
| args = parser.parse_args() | |
| save_dir = f"./esm_embeddings/{args.model_name}" | |
| pdb_seq_path = "./scripts/msa/data/pdb_seqs/pdb_seq.csv" | |
| pdb_seq_label_path = "./scripts/msa/data/pdb_seqs/pdb_labels_seqs.csv" | |
| if not os.path.exists(save_dir): | |
| print("Make dir: ", save_dir) | |
| os.makedirs(save_dir) | |
| process_pdb_dataset( | |
| args.model_name, | |
| save_dir, | |
| pdb_seq_path, | |
| pdb_seq_label_path, | |
| args.start_id, | |
| args.end_id, | |
| ) | |
| if __name__ == "__main__": | |
| main() | |