SSCD / inference.py
dashakoryakovskaya's picture
Update inference.py
d910def verified
Raw
History Blame Contribute Delete
6.34 kB
# coding: utf-8
import os, logging
import random
from pathlib import Path
from typing import Dict, List
import numpy as np
import torch
from utils.config_loader import ConfigLoader
from torch.utils.data import ConcatDataset, DataLoader
from tqdm import tqdm
import whisper
from models.models import MultiModalFusionModelWithAblation
from vlm.feature_extractor import PretrainedVLMEmbeddingExtractor
from text.feature_extractor import PretrainedTextEmbeddingExtractor
def transform_matrix(matrix):
threshold1 = 1 - 1 / 7
threshold2 = 1 / 7
mask1 = matrix[:, 0] >= threshold1
result = np.zeros_like(matrix[:, 1:])
transformed = (matrix[:, 1:] >= threshold2).astype(int)
result[~mask1] = transformed[~mask1]
return result
def process_predictions(pred_emo):
pred_emo = torch.nn.functional.softmax(pred_emo, dim=1).cpu().detach().numpy()
pred_emo = transform_matrix(pred_emo).tolist()
return pred_emo
def aggregate(feats, average: bool = True):
if feats is None:
return None
if isinstance(feats, torch.Tensor):
if average and feats.ndim == 3:
feats = feats.mean(dim=1) # → [B, D]
return feats.squeeze()
if isinstance(feats, dict):
return {
key: aggregate(val, average)
for key, val in feats.items()
}
raise TypeError(f"Unsupported feature type: {type(feats)}")
def transcribe_audio(audio_path):
whisper_model = whisper.load_model("base")
try:
result = whisper_model.transcribe(audio_path, fp16=False)
return result.get('text', '')
except Exception as e:
logging.error(f"Transcription failed: {e}")
return ""
def stack_core_feats(feat_dict: dict, modal: str) -> torch.Tensor:
parts = [feat_dict[k] for k in ["last_emo_encoder_features", "last_per_encoder_features"] if k in feat_dict]
return torch.cat(parts)
def custom_collate_fn(batch):
filtered_batch = []
for sample in batch:
if sample is None or "features" not in sample:
continue
modalities = sample["features"].keys()
has_all_modalities = all(sample["features"].get(m) is not None for m in modalities)
if has_all_modalities:
filtered_batch.append(sample)
if not filtered_batch:
return None
features = {} # modality → Tensor([B, D])
metas = {} # modality → dict списков «побочных» полей (логиты)
modalities = filtered_batch[0]["features"].keys()
emo_pred = {}
per_pred = {}
for m in modalities:
core_vecs = []
emo_logits = []
per_logits = []
for sample in filtered_batch:
core_vecs.append(stack_core_feats(sample["features"][m], m))
emo_logits.append(sample["features"][m]["emotion_logits"])
per_logits.append(sample["features"][m]["personality_scores"])
features[m] = torch.stack(core_vecs)
emo_pred[m] = torch.stack(emo_logits)
per_pred[m] = torch.stack(per_logits)
return {
"features": features,
"emotion_logits": emo_pred,
"personality_scores": per_pred,
}
def predict(video_path):
base_config = ConfigLoader("config_copy.toml")
vlm_feature_extractor = PretrainedVLMEmbeddingExtractor(device=base_config.device)
text_feature_extractor = PretrainedTextEmbeddingExtractor(device=base_config.device)
modality_extractors = {
"video": vlm_feature_extractor,
"text": text_feature_extractor,
}
ablation_config = {}
if not base_config.single_task:
modality_combinations = [
[], # 0 use all modalities
# Single modalities
["text"], # 1
["video"] # 2
]
components = [
-1,
"disable_graph_attn",
"disable_cross_attn",
"disable_emo_logit_proj",
"disable_pkl_logit_proj",
"disable_guide_emo",
"disable_guide_pkl",
]
ablation_config = (
{
"disabled_modalities": modality_combinations[base_config.id_ablation_type_by_modality],
components[base_config.id_ablation_type_by_component]: True
}
if components[base_config.id_ablation_type_by_component] != -1
else {"disabled_modalities": modality_combinations[base_config.id_ablation_type_by_modality]}
)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = MultiModalFusionModelWithAblation(
hidden_dim=base_config.hidden_dim,
num_heads=base_config.num_transformer_heads,
dropout=base_config.dropout,
emo_out_dim=7,
pkl_out_dim=5,
device=device,
ablation_config=ablation_config,
attention=base_config.attention
).to(device)
checkpoint = torch.load("multimodal_model.pt", map_location=device)
state_dict = checkpoint["model_state_dict"] if "model_state_dict" in checkpoint else checkpoint
model.load_state_dict(state_dict)
model.eval()
entry = {
"video_path": video_path,
"features": {},
}
try:
video_feats = modality_extractors["video"].extract(video_path=video_path, saved=False)
entry["features"]["video"] = aggregate(video_feats, True)
except Exception as e:
logging.warning(f"Video extract error {video_path}: {e}")
entry["features"]["video"] = None
try:
txt_raw = transcribe_audio(video_path)
text_feats = modality_extractors["text"].extract(txt_raw)
entry["features"]["text"] = aggregate(text_feats, True)
except Exception as e:
logging.warning(f"Text extract error {txt_raw}: {e}")
entry["features"]["text"] = None
outputs = model(custom_collate_fn([entry]))
preds_emo = None
preds_per = None
if outputs.get('emotion_logits') is not None:
preds_emo = list(torch.nn.functional.softmax(outputs['emotion_logits'], dim=1).cpu().detach().numpy())
if outputs.get('personality_scores') is not None:
preds_per = list(outputs['personality_scores'].cpu().detach().numpy())
return preds_emo, preds_per