proz commited on
Commit
3789ef8
·
verified ·
1 Parent(s): ba25791

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +26 -0
  2. app.py +101 -0
  3. download_model.py +8 -0
  4. requirements.txt +9 -0
Dockerfile ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # Installation des dépendances système audio
4
+ RUN apt-get update && apt-get install -y \
5
+ libsndfile1 \
6
+ ffmpeg \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ WORKDIR /app
10
+
11
+ # Installation des librairies Python
12
+ COPY requirements.txt .
13
+ RUN pip install --no-cache-dir -r requirements.txt
14
+
15
+ # Téléchargement du modèle PENDANT le build
16
+ COPY download_model.py .
17
+ RUN python download_model.py
18
+
19
+ # Copie du code de l'application
20
+ COPY . .
21
+
22
+ # Droits d'accès pour Hugging Face
23
+ RUN chmod -R 777 /app
24
+
25
+ # Démarrage
26
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
2
+ from transformers import Wav2Vec2Processor, Wav2Vec2ForCTC
3
+ import torch
4
+ import librosa
5
+ import io
6
+ import numpy as np
7
+ from contextlib import asynccontextmanager
8
+
9
+ # Configuration
10
+ MODEL_ID = "Cnam-LMSSC/wav2vec2-french-phonemizer"
11
+ ai_context = {}
12
+
13
+ @asynccontextmanager
14
+ async def lifespan(app: FastAPI):
15
+ print("🚀 Chargement du modèle Cnam avec Masque...")
16
+ try:
17
+ # Chargement du processeur et du modèle
18
+ processor = Wav2Vec2Processor.from_pretrained(MODEL_ID)
19
+ model = Wav2Vec2ForCTC.from_pretrained(MODEL_ID)
20
+ model.eval() # Mode lecture seule (plus rapide)
21
+
22
+ ai_context["processor"] = processor
23
+ ai_context["model"] = model
24
+
25
+ # On stocke le vocabulaire pour le masque (ex: 'a': 12, 'b': 14)
26
+ ai_context["vocab"] = processor.tokenizer.get_vocab()
27
+
28
+ print("✅ Modèle prêt et vocabulaire indexé.")
29
+ except Exception as e:
30
+ print(f"❌ Erreur critique : {e}")
31
+ yield
32
+ ai_context.clear()
33
+
34
+ app = FastAPI(lifespan=lifespan)
35
+
36
+ @app.get("/")
37
+ def home():
38
+ return {"status": "Model Cnam Masked is running"}
39
+
40
+ @app.post("/transcribe")
41
+ async def transcribe(
42
+ file: UploadFile = File(...),
43
+ allowed_phones: str = Form(...) # Ce champ est OBLIGATOIRE
44
+ ):
45
+ if "model" not in ai_context:
46
+ raise HTTPException(status_code=500, detail="Modèle non chargé")
47
+
48
+ # 1. Lecture Audio avec Librosa (force 16kHz)
49
+ try:
50
+ content = await file.read()
51
+ # On utilise io.BytesIO pour lire depuis la mémoire sans fichier temporaire
52
+ audio_array, _ = librosa.load(io.BytesIO(content), sr=16000)
53
+ except Exception as e:
54
+ raise HTTPException(status_code=400, detail=f"Erreur fichier audio: {str(e)}")
55
+
56
+ # 2. Préparation Modèle
57
+ processor = ai_context["processor"]
58
+ model = ai_context["model"]
59
+
60
+ inputs = processor(audio_array, sampling_rate=16000, return_tensors="pt", padding=True)
61
+
62
+ # 3. Calcul des Logits (Probabilités brutes avant décision)
63
+ with torch.no_grad():
64
+ logits = model(inputs.input_values).logits
65
+
66
+ # --- 4. APPLICATION DU MASQUE BINAIRE ---
67
+
68
+ # On récupère la liste demandée (ex: "a,i,o")
69
+ requested_phones = [p.strip() for p in allowed_phones.split(',') if p.strip()]
70
+
71
+ if requested_phones:
72
+ vocab = ai_context["vocab"]
73
+
74
+ # Tokens techniques indispensables pour que le CTC fonctionne (silence, padding...)
75
+ # Le modèle Cnam utilise '|' comme séparateur de mot/silence
76
+ technical_tokens = ["<pad>", "<s>", "</s>", "<unk>", "|", "[PAD]", "[UNK]"]
77
+
78
+ # On construit l'ensemble des tokens autorisés
79
+ full_allowed_set = set(requested_phones + technical_tokens)
80
+
81
+ # On trouve leurs positions numériques (ID) dans le cerveau du modèle
82
+ allowed_indices = [vocab[t] for t in full_allowed_set if t in vocab]
83
+
84
+ if allowed_indices:
85
+ # Création du masque : Par défaut, tout est interdit (-Infini)
86
+ mask = torch.full((logits.shape[-1],), float('-inf'))
87
+
88
+ # On ouvre les portes seulement pour les indices autorisés (0.0)
89
+ mask[allowed_indices] = 0.0
90
+
91
+ # On applique le masque aux logits
92
+ logits = logits + mask
93
+
94
+ # 5. Décodage final (Argmax)
95
+ predicted_ids = torch.argmax(logits, dim=-1)
96
+ transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)[0]
97
+
98
+ return {
99
+ "ipa": transcription,
100
+ "allowed_used": allowed_phones
101
+ }
download_model.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from transformers import Wav2Vec2Processor, Wav2Vec2ForCTC
2
+
3
+ MODEL_ID = "Cnam-LMSSC/wav2vec2-french-phonemizer"
4
+
5
+ print(f"⬇️ Téléchargement du modèle {MODEL_ID}...")
6
+ processor = Wav2Vec2Processor.from_pretrained(MODEL_ID)
7
+ model = Wav2Vec2ForCTC.from_pretrained(MODEL_ID)
8
+ print("✅ Modèle téléchargé et mis en cache.")
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ python-multipart
4
+ torch --extra-index-url https://download.pytorch.org/whl/cpu
5
+ transformers
6
+ librosa
7
+ soundfile
8
+ numpy
9
+ scipy