Category Archives: tutorial

Nkululeko: how to investigate correlations of specific features

As shown in this post, nkululeko can be used to investigate correlations of specific features with a target variable.

Now nkululeko can also be used to check on correlation between two real-valued acoustic features.

With the key regplot you can specify two features and optionally a target variable (if omitted, the ini-file target is used) like so:

[EXPL]
regplot = [['lld_mfcc3_sma3_median', 'lld_mfcc1_sma3_median'],
['lld_mfcc3_sma3_median', 'lld_F2frequency_sma3nz_median', 'age']]

The first tuple of features is related to the emotion target (default for this example data: emodb) and would produce this plot:

The second line states age as the target, which is a continuous target and thus will be grouped

Nkululeko: how to predict topics for your texts

With nkululeko since version 1.0.1 we integrated a text classification model. It's a so-called zero-shot model, which means you can define the categories you would like to have predicted by yourself.

Prerequisite for this is that your data is transcribed, i.e. there is a text column in your data.

Here is an example ini file how to use this on a transcripted version of emodb

[EXP]
root = ./examples/results
name = emodb_textclassifier
[DATA]
databases = ['emodb']
emodb = ./examples/results//exp_emodb_translate/results/all_predicted.csv
emodb.type = csv
emodb.split_strategy = random
labels = ['anger', 'happiness']
target = emotion
[FEATS]
type = ['os']
store_format = csv
[MODEL]
type = svm
[PREDICT]
targets = ['textclassification']
textclassifier.candidates = ["sadness", "anger", "neutral", "happiness", "fear", "disgust", "boredom"]

The output is a version with all columns and one with only the pewdicted emotions (from text)

file,start,end,classification_winner,sadness,anger,neutral,happiness,fear,disgust,boredom
./data/emodb/emodb/wav/12a01Fb.wav,0 days,0 days 00:00:01.863625,neutral,0.11576763540506363,0.1414959877729416,0.3593694567680359,0.05933323875069618,0.08951663225889206,0.12100014835596085,0.11351688951253891
./data/emodb/emodb/wav/12a01Wc.wav,0 days,0 days 00:00:02.358812500,neutral,0.12048673629760742,0.1446247100830078,0.25808465480804443,0.04279503598809242,0.0794658437371254,0.25803136825561523,0.09651164710521698

It makes sense that almost all predicted labels are neutral, because emodb was designed to have linguistically neutral emotional content.

Following the winner class are the logits for all candidate classes.

Nkululeko: how to compare classifiers, features and databases using multiple runs

With nkululeko since version 0.98 there is a functionality to compare the outcome for several runs across experiments.

Say, you would like to know if the difference between using acoustic (opensmile) features and linguistic embeddings (bert) as features for some classifier is significant. You could than use the outcomes of several runs from one MLP (multi layer perceptron) as tests that represent all possible runs (disclaimer: afaik this approach is disputable according to some statisticians).

You would set up your experiment like this:

[EXP]
...
runs = 10
epochs = 100
[FEATS]
type = ['bert']
#type = ['os']
#type = ['os', 'bert']
[MODEL]
type = mlp
...
patience = 5
[EXPL]
# turn on extensive statistical output
print_stats = True
[PLOT]
runs_compare = features

and run this three times, each time changing the feature type that is being used (bert, os, or the combination of both), so in the end you got a results folder three different run_results as text files in it.

Using this, nkululeko prints a plot that compares the three feature sets, here's a example (having used only 5 runs):

The title states the overall significance for all differences, as well as the largest one for pair-wise comparison. If you run-number is larger than 30, t-tests will be used instead of Mann-Whitney.

Nkululeko tutorial: voice of wellness workshop

Context

In Sep 2025, we did the Voice of wellness workshop.

In this post i try the nkululeko experiments i use for the tutorials there.

Prepare the Database

i use the Androids corpus, paper here

First thing you should probably do is check the data formats and re-sample if necessary.

[RESAMPLE]
# which of the data splits to re-sample: train, test or all (both)
sample_selection = all
replace = True
target = data_resampled.csv

Explore

Check the database distributions

python -m nkululeko.explore --config data/androids/exp.in

Transcribe and translate

transcribe Note! this should be done on a GPU

translate, no GPU required as it uses a Google service

Segment

Androids database samples are quite long sometimes.
It makes sense to check if approaches work better on shorter speech segments.

python -m nkululeko.segment --config data/androids/exp.ini

Filter the data

[DATA]
data.limit_samples_per_speaker = 8
data.filter = [['task', 'interview']]
check_size = 1000

Define splits

Either use pre-defined folds:

[MODEL]
logo=5

or, randomly define splits, but stratify them:

[DATA]
data.split_strategy = balanced
data.balance = {'depression':2, 'age':1, 'gender':1}
data.age_bins = 2

Add additional training data

More details here

[DATA]
databases = ['data', 'emodb']
data.split_strategy = speaker_split
# add German emotional data
emodb = ./data/emodb/emodb
# rename emotion to depression
emodb.colnames = {"emotion": "depression"}
# only use neutral and sad samples
emodb.filter = [["depression", ["neutral", "sadness"]]]
# map them to depression
emodb.mapping = {"neutral": "control", "sadness": "depressed"}
# and put everything to the training
emodb.split_strategy = train
target = depression
labels = ['depressed', 'control']

Nkululeko: ensemble learners with late fusion

With nkululeko since version 0.88.0 you can combine experiment results and report on the outcome, by using the ensemble module.

For example, you would like to know if the combination of expert features and learned embeddings works better than one of those. You could then do

python -m nkululeko.ensemble \
--method max_class \
tests/exp_emodb_praat_xgb.ini \
tests/exp_emodb_ast_xgb.ini \
tests/exp_emodb_wav2vec_xgb.in

(all in one line)
and would then get the results for a majority voting of the three results for Praat, AST and Wav2vec2 features.

Other methods are mean, max, sum, max_class, uncertainty_threshold, uncertainty_weighted, confidence_weighted:

  • majority_voting: The modality function for classification: predict the category that most classifiers agree on.
  • mean: For classification: compute the arithmetic mean of probabilities from all predictors for each labels, use highest probability to infer the label.
  • max: For classification: use the maximum value of probabilities from all predictors for each labels, use highest probability to infer the label.
  • sum: For classification: use the sum of probabilities from all predictors for each labels, use highest probability to infer the label.
  • max_class: For classification: compare the highest probabilities of all models across classes (instead of same class as in max_ensemble) and return the highest probability and the class
  • uncertainty_threshold: For classification: predict the class with the lowest uncertainty if lower than a threshold (default to 1.0, meaning no threshold), else calculate the mean of uncertainties for all models per class and predict the lowest.
  • uncertainty_weighted: For classification: weigh each class with the inverse of its uncertainty (1/uncertainty), normalize the weights per model, then multiply each class model probability with their normalized weights and use the maximum one to infer the label.
  • confidence_weighted: Weighted ensemble based on confidence (1-uncertainty), normalized for all samples per model. Like before, but use confidence (instead of inverse of uncertainty) as weights.

Nkululeko: export acoustic features

With nkululeko since version 0.85.0 the acoustic features for the test and the train (aka dev) set are exported to the project store.

If you specify the store_format:

[FEATS]
store_format = csv

they will be exported to CSV (comma separated value) files, else PKL (readable by python pickle module).
I.e. you store should then after execution of any nkululeko module that computes features the two files:

  • feats_test.csv
  • feats_train.csv

If you specified scaling the features:

[FEATS]
scale = standard # or speaker

you will have two additional files with features:

  • feats_test_scaled.csv
  • feats_train_scaled..csv

In contrast to the other feature stores, these contain the exact features that are used for training or feature importance exploration, so they might be combined from different feature types and selected via the features value. An example:

[FEATS]
type = ['praat', 'os']
features = ['speechrate_nsyll_dur', 'F0semitoneFrom27.5Hz_sma3nz_amean']
scale = standard
store_format = csv

results in the following feats_test.csv:

file,start,end,speechrate_nsyll_dur,F0semitoneFrom27.5Hz_sma3nz_amean
./data/emodb/emodb/wav/11b03Wb.wav,0 days,0 days 00:00:05.213500,4.028004219813945,34.42206
./data/emodb/emodb/wav/16b10Td.wav,0 days,0 days 00:00:03.934187500,3.0501850763340586,31.227554

....

How to use train, dev and test splits with Nkululeko

Usually in machine learning, you train your predictor on a train set, tune meta-parameters on a dev (development or validation set ) and evaluate on a test set.
With nkululeko, you have the following possiblities to split your data automaticfally: in addition, this entry describes how to use train/dev/test scenarios

How to Split Your Data

This tutorial explains different data splitting strategies in Nkululeko for supervised machine learning experiments. Based on the blog post by Felix Burkhardt.

Why Split Data?

In supervised machine learning, you typically need three kinds of datasets:

  1. Train data: To teach the model the relation between data and labels
  2. Dev data (development): To tune meta-parameters of your model (e.g., number of neurons, batch size, learning rate)
  3. Test data: To evaluate your model ONCE at the end to check on generalization

All of this is to prevent overfitting on your train and/or dev data. If you've used your test data for a while, you might need to find a new set, as chances are high that you overfitted on your test during experiments.

Rules for Good Data Splits

  • Train and dev can be from the same set, but the test set is ideally from a different database
  • If you don't have much data: use an 80/20/20% split
  • If you have masses of data: use only so much dev and test that your population seems covered
  • If you have really little data: use k-fold cross-validation for train and dev, but the test set should still be separate

Split Strategies in Nkululeko

Nkululeko offers several split strategies configured via split_strategy in the [DATA] section:

1. Specified Split

Use predefined train and test files. Ideal when you have a standard benchmark dataset with official splits.

Configuration:

[DATA]
emodb.split_strategy = specified
emodb.test_tables = ['emotion.categories.test.gold_standard']
emodb.train_tables = ['emotion.categories.train.gold_standard']

Example: exp_emodb_split_specified.ini

Run:

python -m nkululeko.train --config examples/exp_emodb_split_specified.ini

When to use:

  • You have official benchmark splits
  • You want reproducible comparisons with other research
  • Dataset provides predefined train/test files

2. Random Split

Randomly assign samples to train and test sets. Simple but doesn't guarantee speaker independence.

Configuration:

[DATA]
emodb.split_strategy = random
emodb.test_size = 20  # 20% for test

Example: exp_emodb_split_random.ini

Run:

python -m nkululeko.train --config examples/exp_emodb_split_random.ini

When to use:

  • Quick experiments
  • Large datasets where speaker overlap is less critical
  • When speaker information is not available

Caution: May lead to speaker overlap between train and test, resulting in optimistic performance estimates.


3. Speaker Split

Ensures speakers in train and test are different (speaker-independent evaluation). Critical for real-world generalization.

Configuration:

[DATA]
emodb.split_strategy = speaker_split
emodb.test_size = 20  # 20% for test

Example: exp_emodb_split_speaker.ini

Run:

python -m nkululeko.train --config examples/exp_emodb_split_speaker.ini

When to use:

  • Real-world deployment scenarios
  • You want to test generalization to unseen speakers
  • Gold standard for speaker-independent evaluation

Why it matters: Prevents the model from memorizing speaker characteristics, forcing it to learn genuine emotion patterns.


4. LOSO (Leave-One-Speaker-Out)

Cross-validation where each speaker is held out once as the test set. Tests generalization to every speaker.

Configuration:

[DATA]
emodb.split_strategy = speaker_split
emodb.test_size = 10  # Percentage for initial split

[MODEL]
logo = 10  # Number of speakers for LOSO cross-validation

Example: exp_emodb_split_loso.ini

Run:

python -m nkululeko.train --config examples/exp_emodb_split_loso.ini

When to use:

  • Small datasets with few speakers
  • You want robust speaker-independent evaluation
  • You need per-speaker performance analysis

How it works:

  • Uses speaker_split strategy to ensure speaker independence
  • The logo parameter specifies the number of speakers (folds)
  • For EmoDB with 10 speakers, logo = 10 means each fold leaves one speaker out (LOSO)
  • Trains 10 models, each testing on a different speaker

Note: Computationally expensive for datasets with many speakers. The number specified in logo should match the number of speakers in your dataset.


5. LOGO (Leave-One-Group-Out)

Cross-validation on the training data by leaving out one group at a time. Used for meta-parameter tuning.

Configuration:

[DATA]
emodb.split_strategy = random  # First split train/test
emodb.test_size = 20

[MODEL]
logo = 4  # Leave-One-Group-Out with 4 groups

Example: exp_emodb_split_logo.ini

Run:

python -m nkululeko.train --config examples/exp_emodb_split_logo.ini

When to use:

  • Tuning model hyperparameters
  • You want more robust validation than single train/dev split
  • Combined with another split strategy for train/test

6. K-Fold Cross-Validation

Splits training data into K folds and trains K models, using each fold as validation once.

Configuration:

[DATA]
emodb.split_strategy = random  # First split train/test
emodb.test_size = 20

[MODEL]
k_fold_cross = 5  # 5-fold cross-validation

Example: exp_emodb_split_kfold.ini

Run:

python -m nkululeko.train --config examples/exp_emodb_split_kfold.ini

When to use:

  • Small to medium datasets
  • You want robust performance estimates
  • Comparing different models or feature sets

Common values: 5-fold or 10-fold


7. Column Split

Split by the values of an arbitrary column (e.g. recording location, session, or any other metadata) instead of by speaker or a random percentage. Useful when your train/test distinction is defined by something other than speaker identity, such as recording site or acquisition device.

Configuration:

[DATA]
emodb.columns = ["age", "gender"]  # the split column must be listed here, or it isn't loaded
emodb.split_strategy = column
emodb.split_column = gender
emodb.train_vals = ['male']
emodb.test_vals = ['female']

Example: exp_emodb_split_column.ini

Run:

python -m nkululeko.train --config examples/exp_emodb_split_column.ini

When to use:

  • Your train/test distinction comes from metadata other than speaker (location, session, recording device, ...)
  • You want to evaluate generalization across a specific, known confound (e.g. train on one recording site, test on another)

How it works:

  • split_column names the column to split on; train_vals/test_vals (also spelled train_values/test_values) list which of that column's values go to which split
  • For a train/dev/test experiment, also set dev_vals
  • Rows whose value is in none of the configured lists are excluded from every split
  • The value lists must be pairwise disjoint -- the same value can't be assigned to two splits (this is validated and raises an error if violated)

Note: the split column must already be loaded into the dataframe via DATA.*db_name*.columns -- this applies even to columns that feel "standard", like gender.


Exercise 1: Compare Split Strategies

Try all split methods with EmoDB using OpenSMILE features and XGBoost:

# 1. Specified split
python -m nkululeko.train --config examples/exp_emodb_split_specified.ini

# 2. Random split
python -m nkululeko.train --config examples/exp_emodb_split_random.ini

# 3. Speaker split
python -m nkululeko.train --config examples/exp_emodb_split_speaker.ini

# 4. LOSO
python -m nkululeko.train --config examples/exp_emodb_split_loso.ini

# 5. LOGO
python -m nkululeko.train --config examples/exp_emodb_split_logo.ini

# 6. 5-fold cross-validation
python -m nkululeko.train --config examples/exp_emodb_split_kfold.ini

# 7. Column split
python -m nkululeko.train --config examples/exp_emodb_split_column.ini

Question: Which split strategy gives the best performance? Why?

Expected findings:

  • Random split typically gives the highest performance (but least realistic)
  • Speaker split / LOSO give more conservative (realistic) performance
  • K-fold / LOGO provide robust estimates with confidence intervals

Exercise 2: Detecting Overfitting

Run a neural network experiment to visualize when overfitting starts:

Configuration: exp_emodb_split_overfitting.ini

python -m nkululeko.train --config examples/exp_emodb_split_overfitting.ini

This configuration:

  • Uses an MLP with layers {l1: 1024, l2: 64}
  • Trains for 200 epochs
  • Plots epoch progression and identifies the best model

What to look for:

  1. Open the epoch progression plot in examples/results/exp_emodb_split_overfitting/images/
  2. Find where training loss continues decreasing but validation loss starts increasing
  3. That's where overfitting begins!

Comparison Table

Split Strategy Speaker Independent Use Case Computational Cost Realism
Specified Depends on dataset Benchmark comparison Low Varies
Random ❌ No Quick experiments Low Low
Speaker Split ✅ Yes Real-world deployment Low High
LOSO ✅ Yes Small datasets, per-speaker analysis High Very High
LOGO Configurable Hyperparameter tuning Medium Medium
K-Fold Configurable Robust evaluation Medium-High Medium
Column Depends on column chosen Splitting by a known metadata confound (location, session, device, ...) Low Varies

Best Practices

For Small Datasets (< 1000 samples)

  1. Use k-fold cross-validation (k=5 or k=10) on train+dev
  2. Keep a separate test set that you evaluate ONLY ONCE
  3. Consider LOSO if you have < 20 speakers

For Medium Datasets (1000-10,000 samples)

  1. Use speaker split with 80/10/10 (train/dev/test)
  2. Ensure different speakers in each split
  3. Use k-fold on training data for hyperparameter tuning

For Large Datasets (> 10,000 samples)

  1. Simple random split or speaker split works well
  2. Dev and test sets can be smaller (e.g., 5% each)
  3. Focus on ensuring the test set covers the population diversity

General Tips

  • Always keep test data separate until final evaluation
  • ✅ Use speaker split for realistic performance estimates
  • ✅ Use cross-validation for robust hyperparameter tuning
  • Never tune on test data
  • ❌ Don't evaluate on test data multiple times (you'll overfit!)

Advanced: Balanced Splits

For imbalanced datasets, use stratified or balanced splits:

[DATA]
emodb.split_strategy = balanced
emodb.test_size = 20
# Stratify by multiple variables with weights
balance = {'emotion':2, 'age':1, 'gender':1}
age_bins = 2
size_diff_weight = 1

See exp_emodb_split.ini for a complete example.


References


Summary

Choosing the right split strategy is crucial for reliable machine learning experiments:

  • For benchmarking: Use specified splits
  • For real-world deployment: Use speaker split or LOSO
  • For quick experiments: Use random split
  • For small datasets: Use k-fold cross-validation
  • For hyperparameter tuning: Use LOGO or k-fold
  • For splitting by a known metadata confound: Use column split

Remember: Your test set performance is only meaningful if it represents the real-world scenario your model will face!

Nkululeko: how to bin/discretize your feature values

With nkululeko since version 0.77.8 you have the possibility to convert all feature values into the discreet classes low, mid and high

Simply state

[FEATS]
type = ['praat']
scale = bins
store_format = csv

in your config to use Praat features.
With the store format stated as csv you will be able to look at the train and test features in the store folder.

The binning will be done based on the 33 and 66 percent of the training feature values.

Nkululeko: compare several databases

With nkululeko since version 0.77.7 there is a new interface named multidb which lets you compare several databases.

You can state their names in the [EXP] section and they will then be processed one after each other and against each other, the results are stored in a file called heatmap.png in the experiment folder.

!Mind YOU NEED TO OMIT THE PROJECT NAME!

Here is an example for such an ini.file:

[EXP]
root = ./experiments/emodbs/
#  DON'T give it a name, 
# this will be the combination 
# of the two databases: 
# traindb_vs_testdb
epochs = 1
databases = ['emodb', 'polish']
[DATA]
root_folders = ./experiments/emodbs/data_roots.ini
target = emotion
labels = ['neutral', 'happy', 'sad', 'angry']
[FEATS]
type = ['os']
[MODEL]
type = xgb

you can (but don't have to), state the specific dataset values in an external file like above.
data_roots.ini:

[DATA]
emodb = ./data/emodb/emodb
emodb.split_strategy = specified
emodb.test_tables = ['emotion.categories.test.gold_standard']
emodb.train_tables = ['emotion.categories.train.gold_standard']
emodb.mapping = {'anger':'angry', 'happiness':'happy', 'sadness':'sad', 'neutral':'neutral'}
polish = ./data/polish_emo
polish.mapping = {'anger':'angry', 'joy':'happy', 'sadness':'sad', 'neutral':'neutral'}
polish.split_strategy = speaker_split
polish.test_size = 30

Withe respect to the mapping, you can also specify super categories, by giving a list as a source category. Here's an example:

emodb.mapping = {'anger, sadness':'negative', 'happiness': 'positive'}
labels = ['negative', 'positive']

Call it with:

python -m nkululeko.multidb --config my_conf.ini

The default behavior is that all databases are used as a whole when being test or training database. If you would rather like the splits to be used, you can add a flag for this:

[EXP]
use_splits = True

Here's a result with two databases:

and this is the same experiment, but with augmentations:

In order to add augmentation, simply add an [AUGMENT] section:

[EXP]
root = ./experiments/emodbs/augmented/
epochs = 1
databases = ['emodb', 'polish']
[DATA]
--
[AUGMENT]
augment = ['traditional', 'random_splice']
[FEATS]
...

In order to add an additional training database to all experiments, you can use:

[CROSSDB]
train_extra = [meta, emodb]

, to add two databases to all training data sets,
where meta and emodb should then be declared in the root_folders file

Nkululeko: generate a latex/pdf report

With nkululeko since version 0.66.3, a report document formatted in Latex and compiled as a PDF file can automatically be generated, basically as a compilation of the images that are generated.
There is a dedicated REPORT section in the config file for this, here is an example:

[REPORT]
# should the report be shown in the terminal at the end?
show = False 
# should a latex/pdf file be printed? if so, state the filename
latex = emodb_report
# name of the experiment author (default "anon")
author = Felix
# title of the report (default "report")
title = EmoDB

NOTE:
with each run of a nkululeko module in the same experiment environment, the details of the report will be added.
So a typical use would be, to first run the general module and than more specialized ones:

# first run a segmentation 
python -m nkululeko.segment --config myconf.ini 
# then rename the data-file in the config.ini and
# run some data exploration
python -m nkululeko.explore --config myconf.ini 
# then run a machine learning experiment
python -m nkululeko.nkululeko --config myconf.ini 

Each run will add some contents to the report