proz commited on
Commit
7eac826
·
verified ·
1 Parent(s): 8d7b532

Upload 11 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ # 1. Installation de libsndfile (obligatoire pour lire l'audio)
4
+ RUN apt-get update && apt-get install -y libsndfile1 && rm -rf /var/lib/apt/lists/*
5
+
6
+ # 2. Configuration du dossier de travail
7
+ WORKDIR /code
8
+
9
+ # 3. Installation des dépendances Python
10
+ COPY ./requirements.txt /code/requirements.txt
11
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
12
+
13
+ # 4. Copie du modèle et du code
14
+ # Important : on copie le dossier model_fr_onnx entier
15
+ COPY ./model_fr_onnx /code/model_fr_onnx
16
+ COPY ./app.py /code/app.py
17
+
18
+ # 5. Lancement du serveur (Port 7860 imposé par Hugging Face)
19
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, File, UploadFile, Form, HTTPException
2
+ from optimum.onnxruntime import ORTModelForCTC
3
+ from transformers import Wav2Vec2Processor
4
+ import torch
5
+ import librosa
6
+ import numpy as np
7
+ import io
8
+ from contextlib import asynccontextmanager
9
+
10
+ # --- CONFIGURATION ---
11
+ # Le dossier sera copié par le Dockerfile au même niveau que app.py
12
+ ONNX_MODEL_DIR = "model_fr_onnx"
13
+ ONNX_FILENAME = "model_quantized.onnx"
14
+
15
+ ai_context = {}
16
+
17
+ @asynccontextmanager
18
+ async def lifespan(app: FastAPI):
19
+ print("🚀 Chargement du modèle Français ONNX...")
20
+ try:
21
+ ai_context["processor"] = Wav2Vec2Processor.from_pretrained(ONNX_MODEL_DIR)
22
+ ai_context["model"] = ORTModelForCTC.from_pretrained(ONNX_MODEL_DIR, file_name=ONNX_FILENAME)
23
+ ai_context["vocab"] = ai_context["processor"].tokenizer.get_vocab()
24
+ print("✅ Modèle chargé.")
25
+ except Exception as e:
26
+ print(f"❌ Erreur critique : {e}")
27
+ yield
28
+ ai_context.clear()
29
+
30
+ app = FastAPI(lifespan=lifespan)
31
+
32
+ @app.get("/")
33
+ def home():
34
+ return {"status": "API is running", "help": "POST /transcribe to use"}
35
+
36
+ @app.post("/transcribe")
37
+ async def transcribe(
38
+ file: UploadFile = File(...),
39
+ allowed_phones: str = Form(...)
40
+ ):
41
+ if "model" not in ai_context:
42
+ raise HTTPException(status_code=500, detail="Model not loaded")
43
+
44
+ # Lecture Audio
45
+ audio_content = await file.read()
46
+ try:
47
+ speech, _ = librosa.load(io.BytesIO(audio_content), sr=16000)
48
+ except:
49
+ # Fallback si librosa n'aime pas le format direct, on peut essayer soundfile
50
+ import soundfile as sf
51
+ speech, _ = sf.read(io.BytesIO(audio_content))
52
+ if len(speech.shape) > 1: speech = speech[:, 0] # Stereo to mono
53
+ if _ != 16000: speech = librosa.resample(speech, orig_sr=_, target_sr=16000)
54
+
55
+ # Inférence
56
+ processor = ai_context["processor"]
57
+ inputs = processor(speech, sampling_rate=16000, return_tensors="pt", padding=True)
58
+
59
+ logits = ai_context["model"](inputs.input_values).logits
60
+
61
+ # Masquage Dynamique
62
+ user_allowed = [p.strip() for p in allowed_phones.split(',')]
63
+ technical_tokens = ["|", "[PAD]", "<s>", "</s>", "<pad>", "<unk>", "[UNK]"]
64
+ full_allowed = set(user_allowed + technical_tokens)
65
+
66
+ vocab = ai_context["vocab"]
67
+ mask = torch.ones(logits.shape[-1], dtype=torch.bool)
68
+
69
+ allowed_indices = [vocab[t] for t in full_allowed if t in vocab]
70
+ if allowed_indices:
71
+ mask[allowed_indices] = False
72
+
73
+ logits[:, :, mask] = -float('inf')
74
+
75
+ # Décodage
76
+ predicted_ids = torch.argmax(logits, dim=-1)
77
+ transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)[0]
78
+
79
+ return {"ipa": transcription}
model_fr_onnx/added_tokens.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ "</s>": 60,
3
+ "<s>": 59
4
+ }
model_fr_onnx/config.json ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_name_or_path": "model_fr_temp",
3
+ "activation_dropout": 0.0,
4
+ "adapter_attn_dim": null,
5
+ "adapter_kernel_size": 3,
6
+ "adapter_stride": 2,
7
+ "add_adapter": false,
8
+ "apply_spec_augment": true,
9
+ "architectures": [
10
+ "Wav2Vec2ForCTC"
11
+ ],
12
+ "attention_dropout": 0.1,
13
+ "bos_token_id": 59,
14
+ "classifier_proj_size": 256,
15
+ "codevector_dim": 256,
16
+ "contrastive_logits_temperature": 0.1,
17
+ "conv_bias": false,
18
+ "conv_dim": [
19
+ 512,
20
+ 512,
21
+ 512,
22
+ 512,
23
+ 512,
24
+ 512,
25
+ 512
26
+ ],
27
+ "conv_kernel": [
28
+ 10,
29
+ 3,
30
+ 3,
31
+ 3,
32
+ 3,
33
+ 2,
34
+ 2
35
+ ],
36
+ "conv_stride": [
37
+ 5,
38
+ 2,
39
+ 2,
40
+ 2,
41
+ 2,
42
+ 2,
43
+ 2
44
+ ],
45
+ "ctc_loss_reduction": "mean",
46
+ "ctc_zero_infinity": false,
47
+ "diversity_loss_weight": 0.1,
48
+ "do_stable_layer_norm": false,
49
+ "eos_token_id": 60,
50
+ "feat_extract_activation": "gelu",
51
+ "feat_extract_norm": "group",
52
+ "feat_proj_dropout": 0.1,
53
+ "feat_quantizer_dropout": 0.0,
54
+ "final_dropout": 0,
55
+ "freeze_feat_extract_train": true,
56
+ "hidden_act": "gelu",
57
+ "hidden_dropout": 0.1,
58
+ "hidden_size": 768,
59
+ "initializer_range": 0.02,
60
+ "intermediate_size": 3072,
61
+ "layer_norm_eps": 1e-05,
62
+ "layerdrop": 0.05,
63
+ "mask_channel_length": 10,
64
+ "mask_channel_min_space": 1,
65
+ "mask_channel_other": 0.0,
66
+ "mask_channel_prob": 0.0,
67
+ "mask_channel_selection": "static",
68
+ "mask_feature_length": 64,
69
+ "mask_feature_min_masks": 0,
70
+ "mask_feature_prob": 0.1024,
71
+ "mask_time_length": 10,
72
+ "mask_time_min_masks": 2,
73
+ "mask_time_min_space": 1,
74
+ "mask_time_other": 0.0,
75
+ "mask_time_prob": 0.5,
76
+ "mask_time_selection": "static",
77
+ "model_type": "wav2vec2",
78
+ "no_mask_channel_overlap": false,
79
+ "no_mask_time_overlap": false,
80
+ "num_adapter_layers": 3,
81
+ "num_attention_heads": 12,
82
+ "num_codevector_groups": 2,
83
+ "num_codevectors_per_group": 320,
84
+ "num_conv_pos_embedding_groups": 16,
85
+ "num_conv_pos_embeddings": 128,
86
+ "num_feat_extract_layers": 7,
87
+ "num_hidden_layers": 12,
88
+ "num_negatives": 100,
89
+ "output_hidden_size": 768,
90
+ "pad_token_id": 58,
91
+ "proj_codevector_dim": 256,
92
+ "tdnn_dilation": [
93
+ 1,
94
+ 2,
95
+ 3,
96
+ 1,
97
+ 1
98
+ ],
99
+ "tdnn_dim": [
100
+ 512,
101
+ 512,
102
+ 512,
103
+ 512,
104
+ 1500
105
+ ],
106
+ "tdnn_kernel": [
107
+ 5,
108
+ 3,
109
+ 3,
110
+ 1,
111
+ 1
112
+ ],
113
+ "torch_dtype": "float32",
114
+ "transformers_version": "4.38.2",
115
+ "use_weighted_layer_sum": false,
116
+ "vocab_size": 61,
117
+ "xvector_output_dim": 512
118
+ }
model_fr_onnx/model_quantized.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:790f5addf53746f55c42493dab3b042af5fae65c35cfd7871dd6edaac25bd4d1
3
+ size 95349071
model_fr_onnx/ort_config.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "one_external_file": true,
3
+ "opset": null,
4
+ "optimization": {},
5
+ "optimum_version": "1.17.0",
6
+ "quantization": {
7
+ "activations_dtype": "QUInt8",
8
+ "activations_symmetric": false,
9
+ "format": "QOperator",
10
+ "is_static": false,
11
+ "mode": "IntegerOps",
12
+ "nodes_to_exclude": [],
13
+ "nodes_to_quantize": [],
14
+ "operators_to_quantize": [
15
+ "Conv",
16
+ "MatMul",
17
+ "Attention",
18
+ "LSTM",
19
+ "Gather",
20
+ "Transpose",
21
+ "EmbedLayerNormalization"
22
+ ],
23
+ "per_channel": false,
24
+ "qdq_add_pair_to_weight": false,
25
+ "qdq_dedicated_pair": false,
26
+ "qdq_op_type_per_channel_support_to_axis": {
27
+ "MatMul": 1
28
+ },
29
+ "reduce_range": false,
30
+ "weights_dtype": "QUInt8",
31
+ "weights_symmetric": true
32
+ },
33
+ "transformers_version": "4.38.2",
34
+ "use_external_data_format": false
35
+ }
model_fr_onnx/preprocessor_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "do_normalize": true,
3
+ "feature_extractor_type": "Wav2Vec2FeatureExtractor",
4
+ "feature_size": 1,
5
+ "padding_side": "right",
6
+ "padding_value": 0.0,
7
+ "processor_class": "Wav2Vec2Processor",
8
+ "return_attention_mask": false,
9
+ "sampling_rate": 16000
10
+ }
model_fr_onnx/special_tokens_map.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ {
4
+ "content": "<s>",
5
+ "lstrip": false,
6
+ "normalized": true,
7
+ "rstrip": false,
8
+ "single_word": false
9
+ },
10
+ {
11
+ "content": "</s>",
12
+ "lstrip": false,
13
+ "normalized": true,
14
+ "rstrip": false,
15
+ "single_word": false
16
+ }
17
+ ],
18
+ "bos_token": "<s>",
19
+ "eos_token": {
20
+ "content": "</s>",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false
25
+ },
26
+ "pad_token": "[PAD]",
27
+ "unk_token": "[UNK]"
28
+ }
model_fr_onnx/tokenizer_config.json ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "57": {
4
+ "content": "[UNK]",
5
+ "lstrip": true,
6
+ "normalized": false,
7
+ "rstrip": true,
8
+ "single_word": false,
9
+ "special": false
10
+ },
11
+ "58": {
12
+ "content": "[PAD]",
13
+ "lstrip": true,
14
+ "normalized": false,
15
+ "rstrip": true,
16
+ "single_word": false,
17
+ "special": false
18
+ },
19
+ "59": {
20
+ "content": "<s>",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "60": {
28
+ "content": "</s>",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ }
35
+ },
36
+ "additional_special_tokens": [
37
+ "<s>",
38
+ "</s>"
39
+ ],
40
+ "bos_token": "<s>",
41
+ "clean_up_tokenization_spaces": true,
42
+ "do_lower_case": false,
43
+ "eos_token": "</s>",
44
+ "model_max_length": 1000000000000000019884624838656,
45
+ "pad_token": "[PAD]",
46
+ "processor_class": "Wav2Vec2Processor",
47
+ "replace_word_delimiter_char": " ",
48
+ "target_lang": null,
49
+ "tokenizer_class": "Wav2Vec2CTCTokenizer",
50
+ "unk_token": "[UNK]",
51
+ "word_delimiter_token": "|"
52
+ }
model_fr_onnx/vocab.json ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "1": 1,
3
+ "[PAD]": 58,
4
+ "[UNK]": 57,
5
+ "a": 2,
6
+ "b": 3,
7
+ "d": 4,
8
+ "e": 5,
9
+ "f": 6,
10
+ "h": 7,
11
+ "i": 8,
12
+ "j": 9,
13
+ "k": 10,
14
+ "l": 11,
15
+ "m": 12,
16
+ "n": 13,
17
+ "o": 14,
18
+ "p": 15,
19
+ "r": 16,
20
+ "s": 17,
21
+ "t": 18,
22
+ "u": 19,
23
+ "v": 20,
24
+ "w": 21,
25
+ "x": 22,
26
+ "y": 23,
27
+ "z": 24,
28
+ "|": 0,
29
+ "ç": 25,
30
+ "ð": 26,
31
+ "ø": 27,
32
+ "ŋ": 28,
33
+ "œ": 29,
34
+ "ɐ": 30,
35
+ "ɑ": 31,
36
+ "ɒ": 32,
37
+ "ɔ": 33,
38
+ "ə": 34,
39
+ "ɛ": 35,
40
+ "ɜ": 36,
41
+ "ɡ": 37,
42
+ "ɣ": 38,
43
+ "ɨ": 39,
44
+ "ɪ": 40,
45
+ "ɬ": 41,
46
+ "ɲ": 42,
47
+ "ɹ": 43,
48
+ "ɾ": 44,
49
+ "ʁ": 45,
50
+ "ʃ": 46,
51
+ "ʊ": 47,
52
+ "ʌ": 48,
53
+ "ʍ": 49,
54
+ "ʒ": 50,
55
+ "ʔ": 51,
56
+ "ʲ": 52,
57
+ "ː": 53,
58
+ "̃": 54,
59
+ "β": 55,
60
+ "θ": 56
61
+ }
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ python-multipart
3
+ uvicorn
4
+ torch
5
+ numpy
6
+ librosa
7
+ optimum
8
+ onnxruntime
9
+ transformers