| --- |
| license: mit |
| --- |
| |
| # Model Card for Omni-DNA |
|
|
| ## Requirement |
|
|
| ```bash |
| pip install datasets ai2-olmo |
| ``` |
|
|
| ## Overview |
|
|
| Omni-DNA is a **cross-modal, multi-task genomic foundation model** designed to generalize across diverse genomic tasks. Unlike previous Genomic Foundation Models (GFMs), which require separate fine-tuning for each task, Omni-DNA leverages **auto-regressive transformer-based training** and **multi-task fine-tuning**, enabling a single model to perform a wide range of genomic tasks with **state-of-the-art** performance. |
|
|
| Omni-DNA models range from **20M to 1B** parameters and support tasks such as **sequence annotation, regulatory element classification, acetylation/methylation prediction, and DNA2Function/DNA2Image mapping**. |
|
|
|
|
| ## Multi-Task Performance |
|
|
| | Histone Mark | Accuracy | F1 Score | Matthews Correlation | Precision | Recall | |
| |-------------|----------|----------|----------------------|-----------|--------| |
| | H3 | 0.8778 | 0.8775 | 0.7558 | 0.8787 | 0.8771 | |
| | H4 | 0.8919 | 0.8897 | 0.7808 | 0.8934 | 0.8873 | |
| | H3K9ac | 0.8543 | 0.8531 | 0.7069 | 0.8522 | 0.8548 | |
| | H3K14ac | 0.9540 | 0.9532 | 0.9065 | 0.9526 | 0.9538 | |
| | H4ac | 0.9396 | 0.9392 | 0.8785 | 0.9390 | 0.9395 | |
| | H3K4me1 | 0.7926 | 0.7912 | 0.5825 | 0.7909 | 0.7916 | |
| | H3K4me2 | 0.8664 | 0.8623 | 0.7248 | 0.8611 | 0.8638 | |
| | H3K4me3 | 0.9046 | 0.9041 | 0.8084 | 0.9049 | 0.9034 | |
| | H3K36me3 | 0.8581 | 0.8571 | 0.7142 | 0.8571 | 0.8570 | |
| | H3K79me3 | 0.8651 | 0.8641 | 0.7291 | 0.8660 | 0.8631 | |
|
|
| ## Model Description |
|
|
| <!-- - **Developed by:** Anonymous Authors --> |
| - **Supported by:** Microsoft Research Asia |
| - **Model type:** Auto-regressive transformer-based genomic model |
| - **License:** mit |
| - **Date cutoff:** 2024 |
| - **Contact:** Research inquiries at `zl6222@ic.ac.uk` |
|
|
| ## Model Sources |
|
|
| - **Paper:** [Omni-DNA: Scaling Auto-Regressive Transformer to Multi-Tasking Genomic Foundation Model](https://arxiv.org/abs/2502.03499) |
| - **Codebase:** https://github.com/Zehui127/Omni-DNA |
| - **Dataset:** Pretrained on **300B nucleotides** from multi-species genome datasets |
|
|
| ## Capabilities |
|
|
| Omni-DNA is trained to perform **multiple genomic tasks** including: |
|
|
| - **Regulatory Element Classification:** Enhancer/promoter/splice site detection |
| - **Histone Modification Prediction:** Acetylation and methylation state identification |
| - **Genomic Function Annotation:** DNA-to-text mapping (DNA2Function) |
| - **Cross-modal Learning:** DNA-to-image mapping (DNA2Image) |
| - **Multi-task Learning:** A single model can solve multiple tasks simultaneously |
|
|
| ## Usage |
|
|
| ### Inference on 10 tasks at the same time |
|
|
| ```python |
| import argparse |
| import os |
| import torch |
| import re |
| import numpy as np |
| import sklearn |
| from tqdm import tqdm |
| from datasets import load_dataset, DatasetDict |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
| |
| def parse_args(): |
| parser = argparse.ArgumentParser(description="Run DNA task inference with a specified model and tokenizer.") |
| parser.add_argument( |
| "--model_tokenizer_path", |
| type=str, |
| default="zehui127/Omni-DNA-Multitask", # Set default value |
| help="Path to the pretrained model and tokenizer. Default: zehui127/Omni-DNA-Multitask" |
| ) |
| return parser.parse_args() |
| |
| def load_model_and_tokenizer(model_tokenizer_path): |
| tokenizer = AutoTokenizer.from_pretrained(model_tokenizer_path) |
| model = AutoModelForCausalLM.from_pretrained(model_tokenizer_path).to('cuda') |
| return model, tokenizer |
| |
| def generate(message, task_type, model, tokenizer, sample_num=1): |
| tokenized_message = tokenizer([message], return_tensors='pt', return_token_type_ids=False, add_special_tokens=True).to('cuda') |
| response = model.generate(**tokenized_message, max_new_tokens=sample_num, do_sample=False) |
| reply = tokenizer.batch_decode(response, skip_special_tokens=False)[0].replace(" ", "") |
| return extract_label(reply, task_type) |
| |
| def extract_label(message, task_type): |
| task_type = '[MASK]' |
| answer = message.split(task_type)[1] |
| match = re.search(r'\d+', answer) |
| return match.group() if match else None |
| |
| def load_and_format_dataset(): |
| raw_dataset = load_dataset("zehui127/Omni-DNA-dataset-nt-downstream-multitask") |
| dataset = raw_dataset['test'] |
| |
| def formatting_prompts_func(example): |
| output_texts = [f"{instr}[MASK]" for instr in example['instruction']] |
| labels = [output[-1] for output in example['output']] |
| task_types = example['task'] |
| return {'formatted_text': output_texts, 'label': labels, 'task_type': task_types} |
| |
| formatted_dataset = dataset.map(formatting_prompts_func, batched=True, remove_columns=dataset.column_names, desc="Formatting dataset") |
| return formatted_dataset |
| |
| def group_by_task_type(dataset): |
| task_types = set(dataset['task_type']) |
| task_datasets = DatasetDict() |
| |
| for task_type in task_types: |
| filtered_dataset = dataset.filter(lambda x: x['task_type'] == task_type, num_proc=1, desc=f"Filtering {task_type} examples") |
| if len(filtered_dataset) > 0: |
| task_datasets[task_type] = filtered_dataset |
| print(f"\nTask type '{task_type}': {len(filtered_dataset)} examples") |
| |
| return task_datasets |
| |
| def calculate_metrics(predictions, labels): |
| valid_mask = labels != -100 |
| valid_predictions = predictions[valid_mask] |
| valid_labels = labels[valid_mask] |
| return { |
| "accuracy": sklearn.metrics.accuracy_score(valid_labels, valid_predictions), |
| "f1": sklearn.metrics.f1_score(valid_labels, valid_predictions, average="macro", zero_division=0), |
| "matthews_correlation": sklearn.metrics.matthews_corrcoef(valid_labels, valid_predictions), |
| "precision": sklearn.metrics.precision_score(valid_labels, valid_predictions, average="macro", zero_division=0), |
| "recall": sklearn.metrics.recall_score(valid_labels, valid_predictions, average="macro", zero_division=0), |
| } |
| |
| def inference(dataset, model, tokenizer): |
| predictions, labels = [], [] |
| |
| for element in tqdm(dataset): |
| prediction = generate(element['formatted_text'], element['task_type'], model, tokenizer) |
| sample_num = 2 |
| while prediction is None: |
| prediction = generate(element['formatted_text'], element['task_type'], model, tokenizer, sample_num) |
| sample_num += 1 |
| if sample_num >= 20: |
| prediction = '0' |
| print("Warning: No valid result") |
| break |
| predictions.append(int(str(prediction)[0])) |
| labels.append(int(element['label'])) |
| |
| return calculate_metrics(np.array(predictions), np.array(labels)) |
| |
| def main(): |
| args = parse_args() |
| model, tokenizer = load_model_and_tokenizer(args.model_tokenizer_path) |
| formatted_dataset = load_and_format_dataset() |
| task_specific_datasets = group_by_task_type(formatted_dataset) |
| |
| tasks = ['H3', 'H4', 'H3K9ac', 'H3K14ac', 'H4ac', 'H3K4me1', 'H3K4me2', 'H3K4me3', 'H3K36me3', 'H3K79me3'] |
| |
| for task in tasks: |
| print(f"==========={task}=========") |
| dataset_test = task_specific_datasets.get(task, None) |
| if dataset_test: |
| print(inference(dataset_test, model, tokenizer)) |
| else: |
| print(f"No data for task {task}") |
| |
| if __name__ == "__main__": |
| main() |
| |
| ``` |
|
|