Download cas9/data/cas9_pre_batching.py from ChatterjeeLab/pCoMole: direct link, hf CLI and curl.
- Browser
- Download file 5.19 kB
-
https://huggingface.co/ChatterjeeLab/pCoMole/resolve/main/cas9/data/cas9_pre_batching.py
- Command line
-
hf download hf://ChatterjeeLab/pCoMole/cas9/data/cas9_pre_batching.py
-
curl -L -o cas9_pre_batching.py https://huggingface.co/ChatterjeeLab/pCoMole/resolve/main/cas9/data/cas9_pre_batching.py
5.19 kB
| import os | |
| import random | |
| import pandas as pd | |
| from datasets import Dataset, DatasetDict | |
| # import selfies as sf | |
| # from rdkit import Chem | |
| # from rdkit import RDLogger | |
| # RDLogger.DisableLog('rdApp.*') | |
| import sys | |
| from transformers import EsmTokenizer | |
| # --------------------------------------------------------------------- | |
| # CONFIG | |
| # --------------------------------------------------------------------- | |
| # Set PROJECT_ROOT or edit paths below | |
| PROJECT_ROOT = os.environ.get('PROJECT_ROOT', 'path/to/project') | |
| FASTA_PATH = os.path.join(PROJECT_ROOT, 'path/to/protein2pam_db_v1.0/cas9_full.fasta') | |
| OUTPUT_DIR = os.path.join(PROJECT_ROOT, 'data/cas9/cas9_dataset_esm2_tokenized_under_1125') | |
| MAX_TOKENS_PER_BATCH = 8192 | |
| TRAIN_RATIO, VAL_RATIO, TEST_RATIO = 0.8, 0.1, 0.1 | |
| # ------------------------------------------------------------ | |
| # 1) Load sequences from FASTA file | |
| # ------------------------------------------------------------ | |
| from Bio import SeqIO | |
| sequences = [] | |
| print("Loading sequences from FASTA and validating…") | |
| for record in SeqIO.parse(FASTA_PATH, "fasta"): | |
| s = str(record.seq) | |
| # skip empty sequences | |
| if not s: | |
| continue | |
| sequences.append(s) | |
| # remove all duplicates | |
| sequences = list(set(sequences)) | |
| ## filter out any sequence that contains non-natural amino acids | |
| print(f"len(sequences) before filtering: {len(sequences)}") | |
| sequences = [ | |
| seq for seq in sequences | |
| if all(aa in "ACDEFGHIKLMNPQRSTVWY" for aa in seq) and len(seq) <= 1125 | |
| ] | |
| print(f"len(sequences) after filtering: {len(sequences)}") | |
| print(f"Average length of sequences: {sum(len(seq) for seq in sequences) / len(sequences)}") | |
| # ---- Initialize ESM tokenizer ---- | |
| tokenizer = EsmTokenizer.from_pretrained("facebook/esm2_t33_650M_UR50D") | |
| print(f"Vocabulary Size: {tokenizer.vocab_size}") | |
| # shuffle before splitting | |
| random.shuffle(sequences) | |
| n = len(sequences) | |
| n_train = int(n * TRAIN_RATIO) | |
| n_val = int(n * VAL_RATIO) | |
| train_sequences = sequences[:n_train] | |
| val_sequences = sequences[n_train:n_train + n_val] | |
| test_sequences = sequences[n_train + n_val:] | |
| # ------------------------------------------------------------ | |
| # 2) helper: tokenize sequences using the ESM tokenizer | |
| # ------------------------------------------------------------ | |
| def tokenize_sequences(sequences): | |
| enc = {"input_ids": [], "attention_mask": []} | |
| for s in sequences: | |
| res = tokenizer(s, add_special_tokens=True) | |
| enc["input_ids"].append(res["input_ids"]) | |
| enc["attention_mask"].append(res["attention_mask"]) | |
| return enc | |
| # ------------------------------------------------------------ | |
| # 3) build batched dataset (token-based batching: groups sequences by length, packs up to max_tokens) | |
| # ------------------------------------------------------------ | |
| from tqdm import tqdm | |
| def build_batched_dataset(sequences, max_tokens=8192): | |
| # tokenize all first | |
| toks = tokenize_sequences(sequences) | |
| input_ids_list = toks["input_ids"] | |
| attn_mask_list = toks["attention_mask"] | |
| # add lengths | |
| items = [] | |
| for ids, mask in tqdm(zip(input_ids_list, attn_mask_list), total=len(input_ids_list), desc="Collecting items with length"): | |
| items.append({ | |
| "input_ids": ids, | |
| "attention_mask": mask, | |
| "length": len(ids), | |
| }) | |
| # sort by length | |
| items.sort(key=lambda x: x["length"]) | |
| batched_input_ids = [] | |
| batched_attention_masks = [] | |
| batched_lengths = [] | |
| batched_batch_sizes = [] | |
| i = 0 | |
| n = len(items) | |
| while i < n: | |
| L = items[i]["length"] | |
| # how many of length L can we pack? | |
| max_bs = max_tokens // L | |
| if max_bs < 1: | |
| max_bs = 1 | |
| # collect up to max_bs items with same length | |
| cur_ids = [] | |
| cur_masks = [] | |
| taken = 0 | |
| j = i | |
| while j < n and items[j]["length"] == L and taken < max_bs: | |
| cur_ids.append(items[j]["input_ids"]) # length L | |
| cur_masks.append(items[j]["attention_mask"]) # length L | |
| j += 1 | |
| taken += 1 | |
| # now this is one batch: shape (B, L) | |
| batched_input_ids.append(cur_ids) | |
| batched_attention_masks.append(cur_masks) | |
| batched_lengths.append(L) | |
| batched_batch_sizes.append(taken) | |
| i = j | |
| ds = Dataset.from_dict({ | |
| "input_ids": batched_input_ids, # list of (B, L) | |
| "attention_mask": batched_attention_masks, | |
| "seq_length": batched_lengths, | |
| "batch_size": batched_batch_sizes, | |
| }) | |
| return ds | |
| # ------------------------------------------------------------ | |
| # 4) build batched datasets for each split | |
| # ------------------------------------------------------------ | |
| train_ds = build_batched_dataset(train_sequences, max_tokens=MAX_TOKENS_PER_BATCH) | |
| val_ds = build_batched_dataset(val_sequences, max_tokens=MAX_TOKENS_PER_BATCH) | |
| test_ds = build_batched_dataset(test_sequences, max_tokens=MAX_TOKENS_PER_BATCH) | |
| dsdict = DatasetDict( | |
| { | |
| "train": train_ds, | |
| "validation": val_ds, | |
| "test": test_ds, | |
| } | |
| ) | |
| os.makedirs(OUTPUT_DIR, exist_ok=True) | |
| dsdict.save_to_disk(OUTPUT_DIR) | |
| print(f"saved to {OUTPUT_DIR}") | |