Skip to content

Supervised modification detection

Use a supplied fine-tuned classifier to call its trained modification classes from contextual PoreFM representations. RNA classifiers are available for m6A/inosine and pseudouridine; see Models for supported classes. The first recipe scores A positions in the called read sequence and writes both read-level Parquet predictions and a BAM with modification tags.

First follow input and model preparation. You need basecalls.bam, pod5/, and reference.fa with its .fai. The command downloads and caches WGLab/PoreFM-RNA_m6A-Inosine@v0.1.0. See Models to prefetch it for offline use.

basecalls.bam + POD5
        |
        v
  signal/sequence chunks
        |
        v
  fine-tuned classifier <--- model.pt (weights + class metadata)
        |
        +--> output.bam                 MM/ML modification tags
        `--> prediction Parquets -----> class counts/fractions at each site
                              reference.fa supplies aligned coordinates

Basic usage

Provide the sample directly on the command line:

porefm detect-modification \
  --bam_path basecalls.bam --pod5 pod5/ \
  --read_based --ref reference.fa --motif 'A 0' \
  --model WGLab/PoreFM-RNA_m6A-Inosine@v0.1.0 \
  --output_format both \
  --output outputs/supervised

--read_based selects motifs in basecalls, and --ref retains available genomic coordinates for aligned reads. A 0 selects the A itself. Use a selector appropriate to the released model's target base. For an SG-tagged signal BAM, omit --pod5.

The checkpoint supplies preprocessing settings, class labels, the canonical base, and modification codes. --output_format both writes predictions and a BAM with modification tags. For a local checkpoint, replace --model with --model-path models/supervised/model.pt; --model_load_path remains supported.

Without --read_based, targets come from the reference: supply --ref and either --motif or --pos_file. Use --contigs chr1 chr2 (also --chrom) to restrict contigs. Direct sample options accept hyphenated spellings too, such as --bam-path, --read-based, and --pos-file.

Relative input paths are resolved from the directory where you run the command. run_manifest.json records the direct options and their normalized defaults in arguments.resolved_input, alongside the model release provenance.

Pseudouridine detection

Use the pseU model to classify canonical U versus pseudouridine in RNA004 reads:

porefm detect-modification \
  --bam_path basecalls.bam --pod5 pod5/ \
  --read_based --ref reference.fa --motif 'T 0' \
  --model WGLab/PoreFM-RNA_pseU@v0.1.0 \
  --output_format both \
  --output outputs/supervised-pseu

RNA uridine is encoded as T in BAM sequences, so target T 0 even though the model's canonical class is named U. The saved class order is U, pseU; the checkpoint supplies base T and modification code 17802 for BAM tags. Keep each classifier's output in its own directory. The HeLa walkthrough shows both models and plots their calls together.

JSON input alternative

JSON configs remain supported. Save the following as sample.json for the same sample and A-base target selection as the first command:

{
  "config_type": "supervised",
  "schema_version": 1,
  "labelled_samples": [
    {
      "bam_path": "basecalls.bam",
      "pod5": "pod5/",
      "read_based": true,
      "ref": "reference.fa",
      "motif": "A 0"
    }
  ]
}

labelled_samples selects prediction targets; known modification labels are not required for inference.

porefm detect-modification \
  --bam_config sample.json \
  --model WGLab/PoreFM-RNA_m6A-Inosine@v0.1.0 \
  --output_format both \
  --output outputs/supervised

Use either --bam_config or direct sample settings. They share validation and defaults, and cannot be mixed. The global --read_based override for JSON configs remains supported. JSON is useful for multiple samples with different settings or tagged label sources. Multiple SG BAMs with shared settings can also be passed as --bam_path first.bam second.bam; POD5 input requires one BAM file. See the input option mapping for additional filters and polishing options.

Read the results

outputs/supervised/
|-- output.bam
|-- checkpoint_*_data.parquet  # Position-level logits and probabilities
|-- checkpoint_*_meta.parquet  # Read identities and alignment metadata
|-- run_manifest.json
`-- *.pfmi                    # Automatic BAM read-ID index, if needed

Keep each data/meta pair together. The BAM produced from POD5 input is unsorted. For coordinate-based viewers or downstream tools, create a sorted, indexed copy:

samtools sort -@ 4 -o outputs/supervised/calls.sorted.bam \
  outputs/supervised/output.bam
samtools index outputs/supervised/calls.sorted.bam

After inference finishes, summarize the Parquets without loading a model:

from pathlib import Path
from porefm.outputs import open_output

Path("summaries").mkdir(exist_ok=True)
calls = open_output("outputs/supervised")
calls.summarize_loci(
    include_class_counts=True,
    mean_probabilities=True,
    output_path="summaries/supervised.parquet",
    threads=4,
)

Each reference site has called_count, count_LABEL, and freq_LABEL columns using the checkpoint's actual labels. Frequencies count the most probable class per call; mean probabilities are a separate statistic. Retain canonical and modified calls when computing fractions. These are model call fractions, not automatically calibrated modification stoichiometry.

See processing outputs for previews, coordinates, and filtering.

Optional settings

Expanded command with window, filtering, and resource settings

Use this alternative when you want explicit signal windows and more loader workers. It also disables the default minimum read quality and length filters, so its retained calls can differ from the basic command.

--size, --step, and --num_trim set signal windows and edge trimming. --batch_size, --reads_per_chunk, and loader settings control resource use; reduce the batch size if GPU memory is limiting. --checkpoint_batch_step controls prediction writes, not model checkpoints.

porefm detect-modification \
  --bam_path basecalls.bam --pod5 pod5/ \
  --read_based --ref reference.fa --motif 'A 0' \
  --model WGLab/PoreFM-RNA_m6A-Inosine@v0.1.0 \
  --size 4096 --step 3400 --num_trim 8 \
  --checkpoint_batch_step 500 --reads_per_chunk 10 \
  --dataloader_workers 8 --batch_size 256 --prefetch_factor 2 \
  --min_qscore 0 --min_read_length 0 \
  --output_format both \
  --output outputs/supervised