ASR-микросервис: whisper-large-v3-russian + Qwen3-ASR-1.7B

This commit is contained in:
n1kt4r committed 2026-08-30 15:47:44 +05:00
commit 11a646604b
8 files changed
+247

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
# ASR-сервис: конфигурация
# Устройство распознавания
DEVICE=cuda
# Активность моделей
LOAD_WHISPER=true
LOAD_QWEN=true
# Путь к whisper-large-v3-russian (локальный checkpoint)
WHISPER_MODEL_PATH=/home/neo/models/whisper-russian
# Модель Qwen3-ASR (автоматическая загрузка с HuggingFace)
QWEN_MODEL_NAME=Qwen/Qwen3-ASR-1.7B
+17
View File
@@ -0,0 +1,17 @@
# Python
__pycache__/
*.pyc
*.egg-info/
.venv/
venv/
# ASR-сервис
.env
# Модели (локальные, не коммитить)
models/
# Данные
*.wav
*.mp3
*.m4a
+44
View File
@@ -0,0 +1,44 @@
# ASR-микросервис для calls24.ru
# Распознавание речи: whisper-large-v3-russian + Qwen3-ASR-1.7B
FastAPI-сервис распознавания речи для анализа записанных звонков.
Две модели: Qwen3-ASR-1.7B (новее, точнее) и whisper-large-v3-russian.
## Структура
```
app/
├── __init__.py
├── main.py # FastAPI app, роуты
├── engine.py # Движки ASR (whisper + qwen3)
└── config.py # Pydantic Settings
```
## Запуск
```bash
cd /home/neo/workspace/asr-microservice
~/gigaam-env/bin/python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
```
## API
- `GET /health` — статус моделей
- `POST /transcribe` — загрузка аудиофайла, параметр `model` (qwen3|whisper)
### Пример
```bash
curl -X POST "http://localhost:8000/transcribe?model=qwen3" \
-F "file=@/tmp/test_silero.wav"
```
## Конфиг
Переменные окружения (см. .env.example):
- `DEVICE` — cuda / cpu
- `WHISPER_MODEL_PATH` — путь к checkpoint whisper
- `QWEN_MODEL_NAME` — модель Qwen3-ASR
## Зависимости
```bash
~/gigaam-env/bin/python -m pip install fastapi uvicorn python-multipart librosa soundfile
```
Модели уже скачаны:
- whisper: `/home/neo/models/whisper-russian`
- qwen3: `Qwen/Qwen3-ASR-1.7B` (автоматическая загрузка)
View File
Whitespace-only changes.
+26
View File
@@ -0,0 +1,26 @@
"""Конфигурация ASR-микросервиса."""
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
# Модель whisper-large-v3-russian
WHISPER_MODEL_PATH: str = "/home/neo/models/whisper-russian"
# Модель Qwen3-ASR-1.7B
QWEN_MODEL_NAME: str = "Qwen/Qwen3-ASR-1.7B"
# Устройство: cuda / cpu
DEVICE: str = "cuda"
# Режим загрузки моделей при старте
LOAD_WHISPER: bool = True
LOAD_QWEN: bool = True
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
settings = Settings()
+65
View File
@@ -0,0 +1,65 @@
"""Движки распознавания речи: whisper-large-v3-russian и Qwen3-ASR-1.7B."""
import warnings
import logging
import librosa
import numpy as np
import soundfile as sf
import torch
import torchaudio
from transformers import WhisperForConditionalGeneration, WhisperProcessor
from qwen_asr import Qwen3ASRModel
from .config import settings
logger = logging.getLogger(__name__)
class WhisperModel:
"""whisper-large-v3-russian (transformers)."""
def __init__(self, device):
self.model = WhisperForConditionalGeneration.from_pretrained(
settings.WHISPER_MODEL_PATH
)
self.processor = WhisperProcessor.from_pretrained(settings.WHISPER_MODEL_PATH)
self.device = device
self.model.to(device)
def transcribe(self, audio_path):
wav, sr = sf.read(audio_path)
wav = wav.mean(axis=1) if wav.ndim > 1 else wav
wav16 = torchaudio.functional.resample(
torch.from_numpy(np.asarray(wav, dtype=np.float32)), sr, 16000
)
inputs = self.processor(
wav16, sampling_rate=16000, return_tensors="pt"
)
with torch.no_grad():
ids = self.model.generate(inputs["input_features"].to(self.device))
return self.processor.batch_decode(ids.cpu().numpy(), skip_special_tokens=True)
class QwenModel:
"""Qwen3-ASR-1.7B (qwen-asr)."""
def __init__(self, device):
self.model = Qwen3ASRModel.from_pretrained(settings.QWEN_MODEL_NAME)
self.device = device
def transcribe(self, audio_path):
result = self.model.transcribe(audio_path)
return result.get("text") if isinstance(result, dict) else result
def load_models(device):
"""Загрузить все активные модели."""
models = {}
if settings.LOAD_WHISPER:
logger.info("Загрузка whisper-large-v3-russian...")
models["whisper"] = WhisperModel(device)
if settings.LOAD_QWEN:
logger.info("Загрузка Qwen3-ASR-1.7B...")
models["qwen3"] = QwenModel(device)
return models
+72
View File
@@ -0,0 +1,72 @@
"""ASR-микросервис: whisper-large-v3-russian + Qwen3-ASR-1.7B для calls24."""
import logging
from fastapi import FastAPI, UploadFile
from fastapi.responses import JSONResponse
from pydantic import BaseModel
from starlette.middleware.cors import CORSMiddleware
from .config import settings
from .engine import load_models
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(title="ASR Microservice", version="1.0.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
_models = {}
@app.on_event("startup")
async def startup_event():
import torch
device = torch.device(settings.DEVICE)
global _models
_models = load_models(device)
logger.info(f"Модели загружены: {list(_models.keys())}")
@app.get("/health")
async def health():
return {"status": "ok", "models": list(_models.keys())}
@app.post("/transcribe")
async def transcribe(file: UploadFile, model: str = "qwen3"):
"""Распознать загруженный аудиофайл (model в query-параметре)."""
import os
import tempfile
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".wav")
content = await file.read()
tmp.write(content)
tmp.close()
if model not in _models:
return JSONResponse(
status_code=400,
content={"error": f"Неизвестная модель: {model}. Доступны: {list(_models.keys())}"},
)
try:
result = _models[model].transcribe(tmp.name)
return {"model": model, "text": result}
except Exception as e:
logger.error(f"Ошибка распознавания: {e}")
return JSONResponse(status_code=500, content={"error": str(e)})
finally:
os.unlink(tmp.name)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
+10
View File
@@ -0,0 +1,10 @@
fastapi
uvicorn
python-multipart
pydantic-settings
transformers
torch
torchaudio
librosa
soundfile
qwen-asr