VoxProfile
Predicts a speaker's gender and age decade from a short audio clip. A pretrained speaker-embedding encoder (TitaNet-L) feeds two lightweight classification heads, so both attributes come out of a single forward pass.
- Gender: binary classification (
Male/Female) - Age: 6-way decade classification (
10s20s30s40s50s60s+)
Code: furence-ai/VoxProfile
Architecture
- Raw waveform goes through a pretrained TitaNet-L speaker-recognition encoder, producing a 192-dim speaker embedding (encoder frozen by default; top layers can be unfrozen, or adapted with LoRA, instead).
- The embedding feeds two independent MLP heads, one producing a single gender logit and the other 6 age-decade logits.
- Both losses are combined and trained jointly:
loss = BCEWithLogitsLoss(gender) + λ × CrossEntropyLoss(age)
Performance
Evaluated on a 624-sample held-out test set.
| Metric | Value |
|---|---|
| Gender accuracy | 98.9% |
| 12-class joint accuracy (gender AND age decade both correct) | 68.8% |
| Age decade | n | MAE | Decade acc |
|---|---|---|---|
| 10s | 97 | 0.825 | 60.8% |
| 20s | 113 | 0.212 | 79.6% |
| 30s | 108 | 0.565 | 47.2% |
| 40s | 97 | 0.567 | 45.4% |
| 50s | 73 | 0.247 | 82.2% |
| 60s+ | 136 | 0.257 | 94.9% |
Gender is nearly solved (well under 1% error). Age confusion concentrates in adjacent decades (30s↔40s, 20s↔30s), while the extremes — 10s and 60s+ — separate cleanly from the rest.
Training data
Utterances with gender and age labels were drawn from several Korean-language AIHub corpora (free conversation, meeting recordings, speaker-recognition audio), cleaned, and split by speaker into train/val/test.
Usage
Requires the model code from the GitHub repo
(voxprofile/model.py, voxprofile/config.py) — this repo hosts only the
trained weights.
import torch
from voxprofile.config import TrainConfig
from voxprofile.model import AgeGenderModel
from huggingface_hub import hf_hub_download
cfg = TrainConfig()
ckpt_path = hf_hub_download(repo_id="furence-ai/VoxProfile", filename="model.pt")
head_cfg = cfg.head_config()
model = AgeGenderModel(
encoder_name=cfg.encoder_name,
encoder_out_dim=cfg.encoder_out_dim,
hidden_dims=head_cfg["hidden_dims"],
dropout=head_cfg["dropout"],
unfreeze_encoder_layers=head_cfg.get("unfreeze_encoder_layers", 0),
emb_dropout=cfg.emb_dropout,
)
model.load_state_dict(torch.load(ckpt_path, map_location="cpu"))
model.eval()