Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7193f40
feat: Add SAMO BERT emotion detection model
d-ulker Sep 10, 2025
56a5d15
fix: resolve variable reference errors in SAMO BERT emotion classifier
d-ulker Sep 10, 2025
3c73dd8
fix: Address all 7 code review comments
d-ulker Sep 10, 2025
3a5e7d9
Feat/dl add emotion detection enhancements
deepsource-autofix[bot] Sep 10, 2025
6db18df
fix: Address Gemini Code Assist review comments
d-ulker Sep 10, 2025
2d51cfc
Merge branch 'feat/dl-add-emotion-detection-enhancements' of github.c…
d-ulker Sep 10, 2025
b9ac502
fix: Address Copilot AI review comments
d-ulker Sep 10, 2025
ff3bbb9
fix: Remove unused variable 'loss_fn' in run_all_tests function
d-ulker Sep 10, 2025
6054ffc
fix: Resolve all PYL-W0621 variable shadowing issues
d-ulker Sep 10, 2025
bfa15ba
fix: Resolve PYL-W0640 cell variable defined in loop
d-ulker Sep 10, 2025
47365ce
fix: Convert all f-string logging calls to lazy % formatting (PYL-W1203)
d-ulker Sep 10, 2025
6f73ac6
fix: Complete PYL-W1203 logging optimization in enhanced_bert_classif…
d-ulker Sep 10, 2025
fa1a1dd
fix: Address Sourcery AI code quality suggestions
d-ulker Sep 10, 2025
4c5e9d2
fix: resolve line length violations and temperature parameter bug
d-ulker Sep 10, 2025
fddb258
refactor: replace threshold constants with getter functions
d-ulker Sep 10, 2025
19e47a3
fix: correct import path for emotion labels
d-ulker Sep 10, 2025
a3a8c6e
fix: move class weights tensor to model device
d-ulker Sep 10, 2025
04436cb
refactor: move emotion labels import to top of file
d-ulker Sep 10, 2025
ca09ca1
fix: resolve remaining line length violations in enhanced_config.py
d-ulker Sep 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 186 additions & 0 deletions configs/samo_emotion_detection_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
# SAMO-DL Emotion Detection Configuration
# Optimized parameters for journal entry emotion analysis

# Model Configuration
model:
name: "bert-base-uncased" # Robust BERT model for emotion understanding
device: null # Auto-detect (CPU/GPU)

# Emotion Detection Parameters
emotion_detection:
# Number of emotion categories (27 GoEmotions + neutral)
num_emotions: 28

# Prediction threshold for binary classification
prediction_threshold: 0.6 # Updated from 0.5 for better calibration

# Temperature scaling for calibrated predictions
temperature: 1.0

# Top-k emotions to return per prediction
top_k: 5

# Model Architecture
architecture:
# BERT configuration
hidden_dropout_prob: 0.3 # Dropout for BERT hidden layers
attention_probs_dropout_prob: 0.3 # Dropout for attention layers

# Classification head configuration
classifier_dropout_prob: 0.5 # Dropout for classification layers

# Freezing strategy
freeze_bert_layers: 6 # Number of BERT layers to freeze initially

# Class balancing
use_class_weights: true # Enable class weight balancing

# Training Configuration
training:
# Batch sizes
train_batch_size: 16
eval_batch_size: 32

# Learning rates
bert_learning_rate: 2e-5 # Lower LR for BERT (fine-tuning)
classifier_learning_rate: 5e-4 # Higher LR for classification head

# Training epochs
num_epochs: 10
warmup_steps: 100

# Gradient settings
max_grad_norm: 1.0
gradient_accumulation_steps: 1

# Early stopping
early_stopping_patience: 3
early_stopping_threshold: 0.01

# Data Processing
data:
# Text processing
max_length: 512 # Maximum sequence length
truncation: true
padding: "max_length"

# Data augmentation
enable_augmentation: false # Disable for now, can be enabled later

# Validation split
validation_split: 0.2
test_split: 0.1

# Evaluation Configuration
evaluation:
# Metrics to compute
metrics:
- "precision"
- "recall"
- "f1_micro"
- "f1_macro"
- "accuracy"

# Evaluation threshold (lowered to capture more predictions)
threshold: 0.2

# Top-k evaluation
top_k_evaluation: true
top_k_values: [1, 3, 5]

# Logging and Monitoring
logging:
level: "INFO"
log_interval: 100 # Log every N steps
save_interval: 1000 # Save checkpoint every N steps

# TensorBoard logging
enable_tensorboard: true
log_dir: "logs/emotion_detection"

# Model Saving
model_saving:
# Save directory
save_dir: "models/emotion_detection"

# Save best model based on metric
save_best_metric: "f1_macro"

# Save checkpoints
save_checkpoints: true
checkpoint_interval: 1 # Save every N epochs

# Performance Optimization
performance:
# Mixed precision training
use_amp: true # Automatic Mixed Precision

# Data loading
num_workers: 4
pin_memory: true

# Memory optimization
gradient_checkpointing: false # Can be enabled for memory savings

# Inference optimization
use_torchscript: false # Can be enabled for faster inference

# SAMO-Specific Optimizations
samo_optimizations:
# Journal entry specific settings
journal_entry_mode: true

# Emotional context awareness
context_awareness: true

# Multi-label prediction
multi_label_mode: true

# Confidence calibration
calibration_enabled: true

# Emotion intensity scaling
intensity_scaling: true

# Error Handling
error_handling:
# Retry settings
max_retries: 3
retry_delay: 1.0

# Fallback behavior
fallback_to_cpu: true
graceful_degradation: true

# Logging errors
log_errors: true
error_log_file: "logs/emotion_detection_errors.log"

# Security and Privacy
security:
# Input sanitization
sanitize_input: true

# Output filtering
filter_sensitive_emotions: false # Can be enabled for privacy

# Rate limiting
rate_limit_requests: 1000 # Requests per minute

# Data privacy
anonymize_predictions: false # Can be enabled for privacy

# Development and Debugging
development:
# Debug mode
debug_mode: false

# Verbose logging
verbose: false

# Test mode
test_mode: false

# Profiling
enable_profiling: false
profile_steps: 100
Binary file not shown.
Binary file not shown.
135 changes: 135 additions & 0 deletions src/models/emotion_detection/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""
Configuration module for SAMO Emotion Detection System

This module provides centralized configuration management for the emotion
detection system, ensuring consistency across training and evaluation.
"""

from typing import Dict, Any
from dataclasses import dataclass

# Default configuration values
DEFAULT_CONFIG = {
"model": {
"name": "bert-base-uncased",
"num_emotions": 28,
"hidden_dropout_prob": 0.3,
"classifier_dropout_prob": 0.5,
"freeze_bert_layers": 6,
"temperature": 1.0,
},
"training": {
"batch_size": 16,
"learning_rate": 2e-5,
"num_epochs": 10,
"weight_decay": 0.01,
},
"evaluation": {
"threshold": 0.2, # Default evaluation threshold
"top_k": 5,
},
"prediction": {
"threshold": 0.6, # Default prediction threshold
"max_length": 512,
}
}


def get_evaluation_threshold() -> float:
"""Get current evaluation threshold from global config."""
return _config.evaluation_threshold


def get_prediction_threshold() -> float:
"""Get current prediction threshold from global config."""
return _config.prediction_threshold


@dataclass
class EmotionDetectionConfig:
"""Configuration class for emotion detection system."""

# Model configuration
model_name: str = "bert-base-uncased"
num_emotions: int = 28
hidden_dropout_prob: float = 0.3
classifier_dropout_prob: float = 0.5
freeze_bert_layers: int = 6
temperature: float = 1.0

# Training configuration
batch_size: int = 16
learning_rate: float = 2e-5
num_epochs: int = 10
weight_decay: float = 0.01

# Evaluation configuration
evaluation_threshold: float = 0.2
top_k: int = 5

# Prediction configuration
prediction_threshold: float = 0.6
max_length: int = 512

@classmethod
def from_dict(cls, config_dict: Dict[str, Any]) -> 'EmotionDetectionConfig':
"""Create config from dictionary."""
return cls(**config_dict)

def to_dict(self) -> Dict[str, Any]:
"""Convert config to dictionary."""
return {
"model_name": self.model_name,
"num_emotions": self.num_emotions,
"hidden_dropout_prob": self.hidden_dropout_prob,
"classifier_dropout_prob": self.classifier_dropout_prob,
"freeze_bert_layers": self.freeze_bert_layers,
"temperature": self.temperature,
"batch_size": self.batch_size,
"learning_rate": self.learning_rate,
"num_epochs": self.num_epochs,
"weight_decay": self.weight_decay,
"evaluation_threshold": self.evaluation_threshold,
"top_k": self.top_k,
"prediction_threshold": self.prediction_threshold,
"max_length": self.max_length,
}


def get_default_config() -> EmotionDetectionConfig:
"""Get default configuration."""
return EmotionDetectionConfig()


def get_config_from_dict(config_dict: Dict[str, Any]) -> EmotionDetectionConfig:
"""Get configuration from dictionary with defaults."""
default_config = get_default_config()

# Update with provided values
for key, value in config_dict.items():
if hasattr(default_config, key):
setattr(default_config, key, value)

return default_config
Comment on lines +105 to +114

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Config update ignores nested YAML structure; implement nested → flat mapping

Passing a YAML-like dict with sections (model/training/evaluation/prediction) won’t update the dataclass fields. Add flattening so nested keys map to attributes.

Apply this diff:

 def get_config_from_dict(config_dict: Dict[str, Any]) -> EmotionDetectionConfig:
     """Get configuration from dictionary with defaults."""
-    default_config = get_default_config()
-
-    # Update with provided values
-    for key, value in config_dict.items():
-        if hasattr(default_config, key):
-            setattr(default_config, key, value)
-
-    return default_config
+    cfg = get_default_config()
+
+    # Map nested sections to dataclass fields
+    section_map = {
+        "model": {
+            "name": "model_name",
+            "num_emotions": "num_emotions",
+            "hidden_dropout_prob": "hidden_dropout_prob",
+            "classifier_dropout_prob": "classifier_dropout_prob",
+            "freeze_bert_layers": "freeze_bert_layers",
+            "temperature": "temperature",
+        },
+        "training": {
+            "batch_size": "batch_size",
+            "learning_rate": "learning_rate",
+            "num_epochs": "num_epochs",
+            "weight_decay": "weight_decay",
+        },
+        "evaluation": {
+            "threshold": "evaluation_threshold",
+            "top_k": "top_k",
+        },
+        "prediction": {
+            "threshold": "prediction_threshold",
+            "max_length": "max_length",
+        },
+    }
+
+    # Apply nested updates
+    for section, mapping in section_map.items():
+        data = config_dict.get(section)
+        if isinstance(data, dict):
+            for k, v in data.items():
+                attr = mapping.get(k)
+                if attr and hasattr(cfg, attr):
+                    setattr(cfg, attr, v)
+
+    # Apply any flat overrides
+    for key, value in config_dict.items():
+        if not isinstance(value, dict) and hasattr(cfg, key):
+            setattr(cfg, key, value)
+
+    return cfg
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def get_config_from_dict(config_dict: Dict[str, Any]) -> EmotionDetectionConfig:
"""Get configuration from dictionary with defaults."""
default_config = get_default_config()
# Update with provided values
for key, value in config_dict.items():
if hasattr(default_config, key):
setattr(default_config, key, value)
return default_config
def get_config_from_dict(config_dict: Dict[str, Any]) -> EmotionDetectionConfig:
"""Get configuration from dictionary with defaults."""
cfg = get_default_config()
# Map nested sections to dataclass fields
section_map = {
"model": {
"name": "model_name",
"num_emotions": "num_emotions",
"hidden_dropout_prob": "hidden_dropout_prob",
"classifier_dropout_prob": "classifier_dropout_prob",
"freeze_bert_layers": "freeze_bert_layers",
"temperature": "temperature",
},
"training": {
"batch_size": "batch_size",
"learning_rate": "learning_rate",
"num_epochs": "num_epochs",
"weight_decay": "weight_decay",
},
"evaluation": {
"threshold": "evaluation_threshold",
"top_k": "top_k",
},
"prediction": {
"threshold": "prediction_threshold",
"max_length": "max_length",
},
}
# Apply nested updates
for section, mapping in section_map.items():
data = config_dict.get(section)
if isinstance(data, dict):
for k, v in data.items():
attr = mapping.get(k)
if attr and hasattr(cfg, attr):
setattr(cfg, attr, v)
# Apply any flat overrides
for key, value in config_dict.items():
if not isinstance(value, dict) and hasattr(cfg, key):
setattr(cfg, key, value)
return cfg
🤖 Prompt for AI Agents
In src/models/emotion_detection/config.py around lines 105 to 114, the current
get_config_from_dict only updates top-level keys and ignores nested YAML
sections (model/training/evaluation/prediction). Modify the function to flatten
one-level (or recursive) nested dicts into a single mapping where nested keys
map to dataclass attribute names (e.g., config_dict["model"]["lr"] -> "lr"),
then iterate that flattened mapping and setattr on default_config for keys that
exist; ignore unknown keys. Ensure nested dict values override defaults and
preserve non-dict top-level keys.



# Global configuration instance
_config = get_default_config()


def get_config() -> EmotionDetectionConfig:
"""Get current configuration."""
return _config


def update_config(config_dict: Dict[str, Any]) -> None:
"""Update global configuration."""
global _config
_config = get_config_from_dict(config_dict)

Comment on lines +126 to +130

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

update_config discards existing values; perform in-place shallow merge.
Rebuilding from defaults nukes prior updates.

-def update_config(config_dict: Dict[str, Any]) -> None:
-    """Update global configuration."""
-    global _config
-    _config = get_config_from_dict(config_dict)
+def update_config(config_dict: Dict[str, Any]) -> None:
+    """Update global configuration in place (shallow merge)."""
+    global _config
+    for key, value in config_dict.items():
+        if hasattr(_config, key):
+            setattr(_config, key, value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def update_config(config_dict: Dict[str, Any]) -> None:
"""Update global configuration."""
global _config
_config = get_config_from_dict(config_dict)
def update_config(config_dict: Dict[str, Any]) -> None:
"""Update global configuration in place (shallow merge)."""
global _config
for key, value in config_dict.items():
if hasattr(_config, key):
setattr(_config, key, value)
🤖 Prompt for AI Agents
In src/models/emotion_detection/config.py around lines 122 to 126, update_config
currently replaces the global _config with get_config_from_dict(config_dict),
discarding prior values; change it to perform an in-place shallow merge: compute
updates = get_config_from_dict(config_dict), ensure _config is initialized
(e.g., to get_default_config() or an empty dict if None), then iterate
updates.items() and assign each key into _config ( _config[key] = value ) so
existing keys not present in updates are preserved; keep the function signature
and avoid reassigning the _config reference.


def reset_config() -> None:
"""Reset to default configuration."""
global _config
_config = get_default_config()
Loading
Loading