Downloads · 30 days
0
Lekhansh/bc-multilabel-classifier
bc-multilabel-classifier is a text classification model from Lekhansh. Use it when you need a label for a piece of text. It is set up for transformers. The card lists the license as apache-2.0.
This model is a fine-tuned version of answerdotai/ModernBERT-base for multilabel classification of Motivational Interviewing (MI) behavioral codes. It classifies utterances into three non-mutually-exclusive categories…
Downloads · 30 days
0
Access
Public
Updated Dec 27, 2025
Parameters
149M
298 MB on disk
Likes
0
Public
Click a slice to open those files.
.safetensors298 MB · 99%
How the weights are stored.
BF16149M · 100%
From the Hugging Face model README
This model is a fine-tuned version of answerdotai/ModernBERT-base for multilabel classification of Motivational Interviewing (MI) behavioral codes. It classifies utterances into three non-mutually-exclusive categories used in behavioral coding of therapeutic conversations.
Developed by: Lekhansh
Model type: Multilabel Text Classification
Language: English
Base model: answerdotai/ModernBERT-base
License: Apache 2.0
This model is designed for automated behavioral coding in Motivational Interviewing contexts, predicting three types of MI-consistent and MI-inconsistent behaviors:
The model was evaluated on a held-out test set of 3,235 coded utterances.
| Metric | Score |
|---|---|
| Exact Match Accuracy | 85.63% |
| Hamming Loss | 0.0579 |
| F1 Macro | 86.66% |
| F1 Micro | 92.46% |
| Precision Macro | 86.53% |
| Precision Micro | 93.47% |
| Recall Macro | 86.84% |
| Recall Micro | 91.48% |
Exact Match: Percentage of examples where all labels are predicted correctly Hamming Loss: Average fraction of labels that are incorrectly predicted (lower is better)
| Label | F1 Score | Precision | Recall | Accuracy |
|---|---|---|---|---|
| Adherent | 74.29% | 74.47% | 74.10% | 90.26% |
| Non-Adherent | 89.32% | 87.34% | 91.39% | 98.98% |
| Neutral | 96.39% | 97.77% | 95.04% | 93.38% |
The training data exhibits class imbalance, addressed through positive class weighting:
Hardware:
Hyperparameters:
| Parameter | Value |
|---|---|
| Learning Rate | 6e-5 |
| Batch Size (per device) | 12 |
| Gradient Accumulation | 2 steps |
| Effective Batch Size | 24 |
| Max Sequence Length | 3000 tokens |
| Epochs | 20 (early stopped at epoch 14) |
| Weight Decay | 0.01 |
| Warmup Ratio | 0.1 |
| LR Scheduler | Cosine |
| Optimizer | AdamW |
| Dropout | 0.1 |
Training Features:
Loss Function: Binary Cross-Entropy with Logits Loss (BCEWithLogitsLoss) with per-label positive class weights
The model uses a custom architecture on top of ModernBERT:
ModernBERT-base (encoder)
→ [CLS] token extraction
→ Dropout (0.1)
→ Linear layer (hidden_size → 3)
→ Sigmoid activation (applied during inference)
import torch
from transformers import AutoTokenizer, AutoModel
import torch.nn as nn
# Define the model class
class MultiLabelBERTModel(nn.Module):
def __init__(self, model_name, num_labels=3, dropout=0.1):
super().__init__()
self.bert = AutoModel.from_pretrained(model_name)
self.dropout = nn.Dropout(dropout)
self.classifier = nn.Linear(self.bert.config.hidden_size, num_labels)
self.num_labels = num_labels
def forward(self, input_ids, attention_mask):
outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
pooled_output = outputs.last_hidden_state[:, 0, :] # [CLS] token
pooled_output = self.dropout(pooled_output)
logits = self.classifier(pooled_output)
return logits
# Load model and tokenizer
model_name = "Lekhansh/bc-multilabel-classifier"
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Initialize model architecture
model = MultiLabelBERTModel(model_name, num_labels=3)
# Load trained weights
# Note: You'll need to load the weights from the saved model
model.eval()
# Prepare input
text = "That's a wonderful goal you've set for yourself."
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=3000)
# Get predictions
with torch.no_grad():
logits = model(inputs['input_ids'], inputs['attention_mask'])
probs = torch.sigmoid(logits)
predictions = (probs > 0.5).int()
# Interpret results
labels = ['adherent', 'non_adherent', 'neutral']
print(f"Text: {text}")
print("\nPredictions:")
for i, label in enumerate(labels):
if predictions[0][i]:
print(f" ✓ {label} (confidence: {probs[0][i]:.2%})")
def predict_multilabel(texts, model, tokenizer, threshold=0.5):
"""
Predict multiple labels for each text with confidence scores.
Args:
texts: List of input texts
model: The multilabel classification model
tokenizer: The tokenizer
threshold: Probability threshold for positive prediction (default: 0.5)
Returns:
List of dicts with predictions and probabilities
"""
inputs = tokenizer(
texts,
return_tensors="pt",
truncation=True,
max_length=3000,
padding=True
)
with torch.no_grad():
logits = model(inputs['input_ids'], inputs['attention_mask'])
probs = torch.sigmoid(logits)
labels = ['adherent', 'non_adherent', 'neutral']
results = []
for i in range(len(texts)):
predictions = (probs[i] > threshold).int()
result = {
'text': texts[i],
'labels': {},
'probabilities': {}
}
for j, label in enumerate(labels):
result['labels'][label] = bool(predictions[j])
result['probabilities'][label] = float(probs[i][j])
results.append(result)
return results
# Example usage
utterances = [
"I hear you saying that you want to change but you're not sure how.",
"You need to stop making excuses and just do it.",
"How many cigarettes do you smoke per day?"
]
results = predict_multilabel(utterances, model, tokenizer)
for r in results:
print(f"\nText: {r['text'][:60]}...")
print("Predicted labels:")
for label in ['adherent', 'non_adherent', 'neutral']:
status = "✓" if r['labels'][label] else "✗"
conf = r['probabilities'][label]
print(f" {status} {label}: {conf:.2%}")
# Adjust threshold for precision/recall trade-off
def predict_with_custom_threshold(text, model, tokenizer, thresholds):
"""
Predict with different thresholds for each label.
Args:
thresholds: Dict with keys 'adherent', 'non_adherent', 'neutral'
"""
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=3000)
with torch.no_grad():
logits = model(inputs['input_ids'], inputs['attention_mask'])
probs = torch.sigmoid(logits)
labels_list = ['adherent', 'non_adherent', 'neutral']
predictions = {}
for i, label in enumerate(labels_list):
threshold = thresholds.get(label, 0.5)
predictions[label] = {
'predicted': bool(probs[0][i] > threshold),
'probability': float(probs[0][i]),
'threshold': threshold
}
return predictions
# Example: Higher threshold for adherent (higher precision)
custom_thresholds = {
'adherent': 0.6,
'non_adherent': 0.5,
'neutral': 0.5
}
result = predict_with_custom_threshold(
"What are your thoughts on reducing your drinking?",
model,
tokenizer,
custom_thresholds
)
# Output format
{
"adherent": 0 or 1,
"non_adherent": 0 or 1,
"neutral": 0 or 1
}
# Example: An utterance can have multiple labels
# "I hear that you're struggling, and I believe you can overcome this."
# → adherent=1, non_adherent=0, neutral=0
Training was conducted using mixed precision to optimize resource usage. Exact carbon footprint was not measured.
If you use this model in your research, please cite:
@misc{lekhansh2025bcmultilabel,
author = {Lekhansh},
title = {Behavioral Coding Multilabel Classifier for Motivational Interviewing},
year = {2025},
publisher = {HuggingFace},
howpublished = {\url{https://huggingface.co/Lekhansh/bc-multilabel-classifier}}
}
For more information on Motivational Interviewing behavioral coding:
Lekhansh