Skip to content

Repository files navigation

FENNEC_logo

FENNEC

Fluorescence Event Neural Network for Evaluating and Classifying bursts

FENNEC is a neural network that classifies single-molecule FRET bursts directly from raw photon timestamps. It performs three simultaneous binary classification tasks for each burst:

  1. Dynamics: did the FRET efficiency change during the burst (dynamic) or remain constant (static)?
  2. Bleaching: did the acceptor photobleach during the burst?
  3. Blinking: did the acceptor undergo triplet blinking during the burst?

The model is trained on simulated bursts generated with parameters calibrated from experimental data.

We're pleased to release FENNEC to the community, and we're excited to see how people put it to use!

- Bob and Joel

Installation

git clone https://github.com/jacrossley/FENNEC.git
cd FENNEC
conda env create -f environment.yml   # creates the 'fennec' environment
conda activate fennec
pip install -e .

Quick start

# 1. Calibrate simulation parameters to your data
python scripts/calibrate.py data/myfile.hdf5

# 2. Train a model
python scripts/train.py

# 3. Classify experimental data
python scripts/infer.py --file data/myfile.hdf5

Note: You don't need to train your own model to get started. A pre-trained model is provided, so you can skip straight to inference and run it on your own data file:

python scripts/infer.py --file data/myfile.hdf5

Before running inference, edit configs/instrument_config.toml to match your own instrument excitation periods and the detector channels used for donor and acceptor photons.

Configuration

FENNEC uses two TOML files and a calibration JSON:

  • configs/train_config.toml contains simulation, training, calibration, and classification-threshold settings.
  • configs/instrument_config.toml contains correction factors, excitation periods, and the detector channels used for donor and acceptor photons.
  • The calibration JSON is generated from experimental data by scripts/calibrate.py.

Photon rate and burst duration distributions, background rates, and rate correlation in [simulation] are reference examples. These values, together with the burst intensity profiles, come from the selected calibration JSON. By default, [calibration].source = "latest" selects the latest JSON in data/calibration/; source can also point to a specific file.

To use a different training configuration:

python scripts/train.py --config configs/my_experiment.toml

Calibration uses the instrument configuration together with the burst search settings in the training configuration. Alternative files can be supplied with:

python scripts/calibrate.py data/myfile.hdf5 \
    --instrument-config configs/my_instrument.toml \
    --train-config configs/my_experiment.toml

Each training run is saved under trained_models/ with its model weights, metadata, training configuration, and calibration JSON. To select a run for inference:

python scripts/infer.py --file data/myfile.hdf5 \
    --model trained_models/fennec_YYYYMMDD_HHMMSS/ \
    --instrument-config configs/my_instrument.toml

If --model is omitted, the latest run is used. Inference reads the model and burst search settings and classification thresholds saved with that run.

Model architecture

The model takes a sequence of up to 1000 photons per example. Each photon is represented by three features:

[log1p(time_us), excitation, emission]

Input shape:

(batch, max_photons=1000, 3)

The model uses a WaveNet-inspired gated dilated convolutional neural network to process each photon sequence. A first linear layer projects each photon from 3 features to 64 channels:

Linear(3 → 64)

The sequence then passes through 9 gated dilated convolution blocks with dilations:

1, 2, 4, 8, 16, 32, 64, 128, 256

Each block computes:

signal = tanh(Conv1d(x, 64 → 64, kernel_size=3, dilation=d))
gate   = sigmoid(Conv1d(x, 64 → 64, kernel_size=3, dilation=d))

h = signal × gate        # gated block output

skip += Conv1d(h, 64 → 128, kernel_size=1)
x     = Conv1d(h, 64 → 64, kernel_size=1) + x

One convolution produces a candidate activation using tanh, while a second produces gate values between 0 and 1 using sigmoid. Their element-wise product, h, is the gated activation. The residual stream, x, remains at 64 channels throughout all nine blocks. Each block sends h through two separate 1×1 convolutional projections:

  1. Residual path
    A 1×1 convolution projects h to 64 channels. The result is added to x and passed to the next block.

  2. Skip path
    A separate 1×1 convolution projects h to 128 channels and adds it to a running sum.

The nine 128-channel skip projections are summed and passed to the output head. This lets the final prediction use features from every dilation scale, from local photon patterns at small dilations to longer-range patterns at large dilations.

After the 9 blocks, the accumulated skip features are passed through a small convolutional output head:

ReLU
Conv1d(128 → 128, kernel_size=1)
ReLU
Conv1d(128 → 128, kernel_size=1)

This output head mixes the skip features collected from all dilation scales. The model then applies masked average pooling across the photon positions, excluding padding, to produce a single 128-dimensional representation for each burst.

The pooled 128-dimensional representation is projected into a shared 256-dimensional embedding:

Linear(128 → 256)
ReLU

Finally, three independent linear heads predict the target parameters:

                    shared embedding: 256
                             │
        ┌────────────────────┼────────────────────┐
        ▼                    ▼                    ▼
Linear(256 → 2)      Linear(256 → 2)      Linear(256 → 2)
  Dynamics             Bleaching             Blinking

The model has approximately 400k parameters.

The receptive field is 1023 photons, covering the full input sequence of up to 1000 photons.

Tensor shape summary

Here, B is the batch size (256 is the default). The number 1000 is the padded sequence length, set by the max_photons parameter in configs/train_config.toml. Each burst is represented as up to 1000 photon positions, with padding used for shorter bursts.

Input photons                   (B, 1000, 3)
Mask                            (B, 1000)

Linear projection               (B, 1000, 3) → (B, 64, 1000)

9 gated dilated blocks:
    hᵢ = tanh(conv_dᵢ(xᵢ)) × sigmoid(conv_dᵢ(xᵢ))

    xᵢ₊₁ = xᵢ + residual_projection(hᵢ)   (B, 64, 1000)
    sᵢ   = skip_projection(hᵢ)            (B, 128, 1000)

Sum skip outputs across blocks  (B, 128, 1000)
Output convolutions             (B, 128, 1000)
Masked average over photons     (B, 128)
Shared embedding                (B, 256)
Three 2-output heads            3 × (B, 2)

Acknowledgement

If you use this code or data, please cite the associated publication. FENNEC uses FRETBursts for background estimation and dual-channel burst search (DCBS), so please also cite:

  • FRETBursts software: Ingargiola, A., Lerner, E., Chung, S., Weiss, S. & Michalet, X. (2016). FRETBursts: An Open Source Toolkit for Analysis of Freely-Diffusing Single-Molecule FRET. PLOS ONE, 11(8), e0160716. doi:10.1371/journal.pone.0160716
  • Original DCBS method: Nir, E. et al. (2006). Shot-Noise Limited Single-Molecule FRET Histograms: Comparison between Theory and Experiments. Journal of Physical Chemistry B, 110(44), 22103–22124. doi:10.1021/jp063483n

About

A Python toolkit for classifying single-molecule FRET bursts directly from raw photon timestamps using deep learning. It covers burst simulation, training of a dilated convolutional network, and inference.

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages