autoace / src /models.py
edwixx's picture
Deploy AutoAce voice tone dashboard
c3eb7ea verified
Raw
History Blame Contribute Delete
5.24 kB
import functools
import numpy as np
import torch
from .config import AST_MODEL, EMOTION_CAT_MODEL, EMOTION_DIM_MODEL, SEGMENTATION_MODEL, SR
from .logger import log
_AUDIOSET_TO_NOISE_TYPE = {
"Television": "TV",
"Music": "music",
"Musical instrument": "music",
"Vehicle": "traffic/road noise",
"Car": "traffic/road noise",
"Traffic noise, roadway noise": "traffic/road noise",
"Wind": "wind",
"Wind noise (microphone)": "wind",
"Typing": "keyboard typing",
"Computer keyboard": "keyboard typing",
"Conversation": "office chatter",
"Chatter": "office chatter",
"Speech": None,
"Crowd": "office chatter",
"Static": "static",
"White noise": "static",
"Hum": "mechanical noise",
"Mechanisms": "mechanical noise",
"Engine": "mechanical noise",
"Telephone bell ringing": "office chatter",
"Dog": "background chatter/animal noise",
"Silence": None,
}
@functools.lru_cache(maxsize=1)
def _silero_vad():
model, utils = torch.hub.load(
"snakers4/silero-vad", "silero_vad", trust_repo=True, onnx=False
)
return model, utils
def speech_segments(y: np.ndarray) -> list[tuple[float, float]] | None:
try:
model, utils = _silero_vad()
get_speech_timestamps = utils[0]
wav = torch.from_numpy(y)
ts = get_speech_timestamps(wav, model, sampling_rate=SR)
return [(t["start"] / SR, t["end"] / SR) for t in ts]
except Exception as e:
log.exception("speech_segments failed: %s", e)
return None
@functools.lru_cache(maxsize=1)
def _emotion_model():
import torch.nn as nn
from transformers import Wav2Vec2Processor
from transformers.models.wav2vec2.modeling_wav2vec2 import (
Wav2Vec2Model,
Wav2Vec2PreTrainedModel,
)
class RegressionHead(nn.Module):
def __init__(self, config):
super().__init__()
self.dense = nn.Linear(config.hidden_size, config.hidden_size)
self.dropout = nn.Dropout(config.final_dropout)
self.out_proj = nn.Linear(config.hidden_size, config.num_labels)
def forward(self, features):
x = self.dropout(features)
x = torch.tanh(self.dense(x))
x = self.dropout(x)
return self.out_proj(x)
class EmotionModel(Wav2Vec2PreTrainedModel):
def __init__(self, config):
super().__init__(config)
self.wav2vec2 = Wav2Vec2Model(config)
self.classifier = RegressionHead(config)
self.init_weights()
def forward(self, input_values):
hidden = self.wav2vec2(input_values)[0]
pooled = torch.mean(hidden, dim=1)
return pooled, self.classifier(pooled)
processor = Wav2Vec2Processor.from_pretrained(EMOTION_DIM_MODEL)
model = EmotionModel.from_pretrained(EMOTION_DIM_MODEL).eval()
return processor, model
def dimensional_emotion(y: np.ndarray) -> dict | None:
try:
processor, model = _emotion_model()
inputs = processor(y, sampling_rate=SR, return_tensors="pt")
with torch.no_grad():
_, logits = model(inputs["input_values"])
arousal, dominance, valence = logits[0].tolist()
return {"arousal": arousal, "dominance": dominance, "valence": valence}
except Exception as e:
log.exception("dimensional_emotion failed: %s", e)
return None
@functools.lru_cache(maxsize=1)
def _categorical_ser_pipeline():
from transformers import pipeline
return pipeline("audio-classification", model=EMOTION_CAT_MODEL, top_k=4)
def categorical_emotion(y: np.ndarray) -> list[dict] | None:
try:
clf = _categorical_ser_pipeline()
return clf({"array": y, "sampling_rate": SR})
except Exception as e:
log.exception("categorical_emotion failed: %s", e)
return None
@functools.lru_cache(maxsize=1)
def _ast_pipeline():
from transformers import pipeline
return pipeline("audio-classification", model=AST_MODEL, top_k=10)
def noise_tags(y: np.ndarray) -> list[dict] | None:
try:
clf = _ast_pipeline()
return clf({"array": y, "sampling_rate": SR})
except Exception as e:
log.exception("noise_tags failed: %s", e)
return None
def noise_type_from_tags(tags: list[dict], min_score: float = 0.15) -> str:
for tag in tags:
mapped = _AUDIOSET_TO_NOISE_TYPE.get(tag["label"])
if mapped and tag["score"] >= min_score:
return mapped
return ""
@functools.lru_cache(maxsize=1)
def _segmentation_inference():
from pyannote.audio import Inference, Model
model = Model.from_pretrained(SEGMENTATION_MODEL)
return Inference(model, step=2.5)
def overlap_seconds(path: str) -> float | None:
try:
inference = _segmentation_inference()
output = inference(path)
frame_speaker_count = output.data.sum(axis=-1)
overlap_frames = int((frame_speaker_count >= 2).sum())
frame_duration = output.sliding_window.duration / output.data.shape[1]
return float(overlap_frames * frame_duration)
except Exception as e:
log.exception("overlap_seconds failed: %s", e)
return None