Розробка систем розпізнавання емоцій за обличчям
Розпізнавання емоцій за мімікою - це завдання класифікації виразу обличчя на базові емоційні стани. Класична модель Екмана виділяє 7 універсальних емоцій: радість, грусть, гнів, страх, здивування, огида, нейтральність. Застосування: аналіз залучення при онлайн-навчанні, моніторинг задоволення клієнтів у call-центрі, дослідження UX, контроль стану водія.
Архітектура моделі
Конвеєр: виявлення обличчя → вирівнювання → класифікація емоцій.
import torch
import torch.nn as nn
import timm
import cv2
import numpy as np
from insightface.app import FaceAnalysis
class EmotionRecognizer:
def __init__(self, model_path: str):
# Виявлення та вирівнювання обличчя
self.detector = FaceAnalysis(allowed_modules=['detection'])
self.detector.prepare(ctx_id=0, det_size=(640, 640))
# Класифікатор емоцій
backbone = timm.create_model('efficientnet_b0', pretrained=False)
backbone.classifier = nn.Sequential(
nn.Dropout(0.3),
nn.Linear(backbone.num_features, 7)
)
backbone.load_state_dict(torch.load(model_path))
backbone.eval()
self.model = backbone
self.emotions = ['angry', 'disgust', 'fear', 'happy',
'neutral', 'sad', 'surprise']
self.transform = get_inference_transform()
@torch.no_grad()
def predict(self, image: np.ndarray) -> list[dict]:
faces = self.detector.get(image)
results = []
for face in faces:
x1, y1, x2, y2 = face.bbox.astype(int)
face_crop = image[y1:y2, x1:x2]
face_crop = cv2.resize(face_crop, (48, 48))
tensor = self.transform(face_crop).unsqueeze(0)
logits = self.model(tensor)
probs = torch.softmax(logits, dim=1).squeeze()
emotion_scores = {
self.emotions[i]: float(probs[i])
for i in range(7)
}
dominant = max(emotion_scores, key=emotion_scores.get)
results.append({
'bbox': [x1, y1, x2, y2],
'emotion': dominant,
'confidence': emotion_scores[dominant],
'all_scores': emotion_scores
})
return results
Набори даних та якість моделей
| Набір даних | Розмір | Умови | Класи |
|---|---|---|---|
| FER-2013 | 35k фото | Дикі | 7 |
| AffectNet | 1M фото | Дикі | 8 (+ презирство) |
| RAF-DB | 30k фото | Реальні | 7 + складні |
| CK+ | 593 відео | Лабораторні | 7 |
| SFEW | 1766 кадрів | Кіно | 7 |
Точність на FER-2013:
- EfficientNet-B0 fine-tuned: 73.1%
- Vision Transformer (ViT-B/16): 74.8%
- EfficientFace: 73.3%
Головна складність: мітки в публічних наборах даних суб'єктивні, люди не згідні в 30–40% випадків. 75% точність - це практична межа для FER-2013 через людське невідповідність.
Часова аналітика на відео
Покадрова класифікація нестабільна — емоція "мигтить" між кадрами. Рішення:
- Часова згладжування: ковзне середнє за 10–30 кадрів
- RNN/LSTM поверх frame-level класифікатора: враховує часову динаміку
- Агрегація за інтервалом: середня емоція за N-секундний інтервал для аналітики
from collections import deque
class TemporalEmotionTracker:
def __init__(self, window_size: int = 30):
self.window = deque(maxlen=window_size)
def update(self, emotion_scores: dict) -> dict:
self.window.append(emotion_scores)
# Усереднюємо по вікну
averaged = {}
for emotion in emotion_scores:
averaged[emotion] = sum(
frame[emotion] for frame in self.window
) / len(self.window)
return averaged
Обмеження та етичні аспекти
Важливо розуміти обмеження технології:
- Культурні відмінності у виразі емоцій (мімика варіюється між культурами)
- Нейтральне обличчя ≠ нейтральний емоційний стан
- Актерська мімика відрізняється від справжньої
Технологія не повинна використовуватися для прихованого моніторингу працівників без їхньої згоди. Production-системи завжди потребують юридичної згоди.
| Завдання | Графік |
|---|---|
| SDK для мобільного/веб додатка | 2–3 тижні |
| Аналітика залучення на відео | 3–5 тижнів |
| Кастомна модель на корпоративному наборі даних | 5–8 тижнів |







