Marina Kpamegan commited on
Commit
2b8147e
·
1 Parent(s): 87e9667

proba par classes

Browse files
.gitignore CHANGED
@@ -183,4 +183,5 @@ data/*
183
 
184
  # Mac
185
  .DS_Store
186
- .idea
 
 
183
 
184
  # Mac
185
  .DS_Store
186
+ .idea
187
+ wav2vec2_emotion/
src/model/feature_extractor.py CHANGED
@@ -1,6 +1,6 @@
1
  import torch
2
  from transformers import Wav2Vec2Model, Wav2Vec2Processor
3
- from src.config import MODEL_NAME, DEVICE
4
 
5
  processor = Wav2Vec2Processor.from_pretrained(MODEL_NAME)
6
  feature_extractor = Wav2Vec2Model.from_pretrained(MODEL_NAME).to(DEVICE)
 
1
  import torch
2
  from transformers import Wav2Vec2Model, Wav2Vec2Processor
3
+ from config import MODEL_NAME, DEVICE
4
 
5
  processor = Wav2Vec2Processor.from_pretrained(MODEL_NAME)
6
  feature_extractor = Wav2Vec2Model.from_pretrained(MODEL_NAME).to(DEVICE)
src/predict.py CHANGED
@@ -3,10 +3,9 @@ import os
3
  import torch
4
  import librosa
5
  import numpy as np
6
- from src.model.emotion_classifier import EmotionClassifier
7
- from src.utils.preprocessing import collate_fn
8
- from src.config import DEVICE, NUM_LABELS, BEST_MODEL_NAME
9
- import os
10
 
11
  # Charger le modèle entraîné
12
  feature_dim = 40 # Nombre de MFCCs utilisés
@@ -14,7 +13,10 @@ model = EmotionClassifier(feature_dim, NUM_LABELS).to(DEVICE)
14
  model.load_state_dict(torch.load(BEST_MODEL_NAME, map_location=DEVICE))
15
  model.eval() # Mode évaluation
16
 
17
- # Fonction pour prédire l’émotion d’un fichier audio
 
 
 
18
  def predict_emotion(audio_path, max_length=128):
19
  # Charger l’audio
20
  y, sr = librosa.load(audio_path, sr=16000)
@@ -35,17 +37,21 @@ def predict_emotion(audio_path, max_length=128):
35
  # Prédiction avec le modèle
36
  with torch.no_grad():
37
  logits = model(input_tensor)
 
38
  predicted_class = torch.argmax(logits, dim=-1).item()
39
 
40
- # Définition des labels
41
- LABELS = {0: "colère", 1: "neutre", 2: "joie"}
42
- return LABELS[predicted_class]
 
43
 
44
-
45
 
46
- #Exemple d'utilisation
47
  if __name__ == "__main__":
48
  base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "data"))
49
  audio_file = os.path.join(base_path, "colere", "c1ac.wav")
50
- emotion = predict_emotion(audio_file)
51
- print(f"🎤 L'émotion prédite est : {emotion}")
 
 
 
 
3
  import torch
4
  import librosa
5
  import numpy as np
6
+ from model.emotion_classifier import EmotionClassifier
7
+ from utils.preprocessing import collate_fn
8
+ from config import DEVICE, NUM_LABELS, BEST_MODEL_NAME
 
9
 
10
  # Charger le modèle entraîné
11
  feature_dim = 40 # Nombre de MFCCs utilisés
 
13
  model.load_state_dict(torch.load(BEST_MODEL_NAME, map_location=DEVICE))
14
  model.eval() # Mode évaluation
15
 
16
+ # Labels des émotions
17
+ LABELS = {0: "colère", 1: "neutre", 2: "joie"}
18
+
19
+ # Fonction pour prédire l’émotion d’un fichier audio avec probabilités
20
  def predict_emotion(audio_path, max_length=128):
21
  # Charger l’audio
22
  y, sr = librosa.load(audio_path, sr=16000)
 
37
  # Prédiction avec le modèle
38
  with torch.no_grad():
39
  logits = model(input_tensor)
40
+ probabilities = torch.nn.functional.softmax(logits, dim=-1).cpu().numpy().flatten() # Convertir en probabilités
41
  predicted_class = torch.argmax(logits, dim=-1).item()
42
 
43
+ # Associer les probabilités aux labels
44
+ probabilities_dict = {LABELS[i]: float(probabilities[i]) for i in range(NUM_LABELS)}
45
+
46
+ return LABELS[predicted_class], probabilities_dict
47
 
 
48
 
49
+ # Exemple d'utilisation
50
  if __name__ == "__main__":
51
  base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "data"))
52
  audio_file = os.path.join(base_path, "colere", "c1ac.wav")
53
+
54
+ predicted_emotion, probabilities = predict_emotion(audio_file)
55
+
56
+ print(f"🎤 L'émotion prédite est : {predicted_emotion}")
57
+ print(f"📊 Probabilités par classe : {probabilities}")
src/test_speech.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torchaudio
3
+ from transformers import Wav2Vec2Processor, Wav2Vec2ForSequenceClassification
4
+ import os
5
+
6
+ # 🔹 Paramètres
7
+ MODEL_NAME = "./wav2vec2_emotion" # Chemin du modèle sauvegardé
8
+ LABELS = ["colere", "joie", "neutre"] # Les classes
9
+
10
+ # 🔹 Charger le processeur et le modèle
11
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
12
+ processor = Wav2Vec2Processor.from_pretrained(MODEL_NAME)
13
+ model = Wav2Vec2ForSequenceClassification.from_pretrained(MODEL_NAME).to(device)
14
+ model.eval() # Mode évaluation
15
+
16
+
17
+ def predict_emotion(audio_path):
18
+ # Charger l'audio
19
+ waveform, sample_rate = torchaudio.load(audio_path)
20
+
21
+ # Prétraitement du son
22
+ inputs = processor(
23
+ waveform.squeeze().numpy(),
24
+ sampling_rate=sample_rate,
25
+ return_tensors="pt",
26
+ padding=True,
27
+ truncation=True,
28
+ max_length=32000 # Ajuste selon la durée de tes fichiers
29
+ )
30
+
31
+ # Envoyer les données sur le bon device (CPU ou GPU)
32
+ input_values = inputs["input_values"].to(device)
33
+
34
+ # Prédiction
35
+ with torch.no_grad():
36
+ logits = model(input_values).logits
37
+
38
+ # Trouver l'émotion prédite
39
+ predicted_class = torch.argmax(logits, dim=-1).item()
40
+
41
+ return LABELS[predicted_class] # Retourne le label correspondant
42
+
43
+ base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "data"))
44
+ audio_file = os.path.join(base_path, "colere", "c1ac.wav")
45
+ predicted_emotion = predict_emotion(audio_file)
46
+ print(f"🎙️ Émotion prédite : {predicted_emotion}")
47
+
48
+
49
+
src/train_speech.py CHANGED
@@ -1,169 +1,88 @@
1
- import os
2
  import torch
3
- import torch.nn as nn
4
- import torch.optim as optim
5
- import soundfile as sf
6
  import torchaudio
7
- import numpy as np
8
- from datasets import Dataset
9
- from transformers import Wav2Vec2Model, Wav2Vec2Processor
10
- from dotenv import load_dotenv
11
- from sklearn.metrics import accuracy_score
12
-
13
- # Charger .env pour Hugging Face API Key
14
- load_dotenv()
15
- HF_API_KEY = os.getenv("HF_API_KEY")
16
-
17
- if not HF_API_KEY:
18
- raise ValueError("Le token Hugging Face n'a pas été trouvé dans .env")
19
-
20
- # Définition des labels pour la classification des émotions
21
- LABELS = {"colere": 0, "neutre": 1, "joie": 2}
22
- NUM_LABELS = len(LABELS)
23
-
24
- # Charger le processeur et le modèle pour l'extraction de features
25
- model_name = "facebook/wav2vec2-large-xlsr-53-french"
26
- device = "cuda" if torch.cuda.is_available() else "cpu"
27
-
28
- processor = Wav2Vec2Processor.from_pretrained(model_name)
29
- feature_extractor = Wav2Vec2Model.from_pretrained(model_name).to(device)
30
-
31
- # Resampleur pour convertir en 16 kHz
32
- resampler = torchaudio.transforms.Resample(orig_freq=48_000, new_freq=16_000)
33
-
34
- # Définition du classifieur amélioré
35
- class EmotionClassifier(nn.Module):
36
- def __init__(self, feature_dim, num_labels):
37
- super(EmotionClassifier, self).__init__()
38
- self.fc1 = nn.Linear(feature_dim, 512)
39
- self.relu = nn.ReLU()
40
- self.dropout = nn.Dropout(0.3)
41
- self.fc2 = nn.Linear(512, num_labels)
42
-
43
- def forward(self, x):
44
- x = self.fc1(x)
45
- x = self.relu(x)
46
- x = self.dropout(x)
47
- return self.fc2(x)
48
-
49
- # Instancier le classifieur
50
- classifier = EmotionClassifier(feature_extractor.config.hidden_size, NUM_LABELS).to(device)
51
-
52
- # Charger les fichiers audio et leurs labels
53
  def load_audio_data(data_dir):
54
- data = []
55
- for label_name, label_id in LABELS.items():
56
- label_dir = os.path.join(data_dir, label_name)
57
- for file in os.listdir(label_dir):
58
- if file.endswith(".wav"):
59
- file_path = os.path.join(label_dir, file)
60
- data.append({"path": file_path, "label": label_id})
61
- return Dataset.from_list(data)
62
-
63
- # Chargement du dataset
64
- data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "data"))
65
- ds = load_audio_data(data_dir)
66
-
67
- # Charger les fichiers audio avec SoundFile et rééchantillonner à 16 kHz
68
- def preprocess_audio(batch):
69
- speech, sample_rate = sf.read(batch["path"], dtype="float32")
70
-
71
- if sample_rate != 16000:
72
- speech = torch.tensor(speech).unsqueeze(0)
73
- speech = resampler(speech).squeeze(0).numpy()
74
-
75
- batch["speech"] = speech.tolist() # Convertir en liste pour éviter les erreurs de PyArrow
76
- batch["sampling_rate"] = 16000
77
- return batch
78
-
79
-
80
- ds = ds.map(preprocess_audio)
81
-
82
- # Vérifier la distribution des longueurs des fichiers audio
83
- lengths = [len(sample["speech"]) for sample in ds]
84
- max_length = int(np.percentile(lengths, 95))
85
 
86
- # Transformer l'audio en features utilisables par le modèle
87
- def prepare_features(batch):
88
- features = processor(
89
- batch["speech"],
90
- sampling_rate=16000,
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  padding=True,
92
  truncation=True,
93
- max_length=max_length,
94
- return_tensors="pt"
95
  )
96
- batch["input_values"] = features.input_values.squeeze(0)
97
- batch["label"] = torch.tensor(batch["label"], dtype=torch.long)
98
- return batch
99
-
100
- ds = ds.map(prepare_features)
101
-
102
- # Diviser les données en train et test
103
- ds = ds.train_test_split(test_size=0.2)
104
- train_ds = ds["train"]
105
- test_ds = ds["test"]
106
-
107
- # Fonction d'entraînement avec sauvegarde du meilleur modèle
108
- def train_classifier(feature_extractor, classifier, train_ds, test_ds, epochs=20, batch_size=8):
109
- optimizer = optim.AdamW(classifier.parameters(), lr=2e-5, weight_decay=0.01)
110
- loss_fn = nn.CrossEntropyLoss()
111
-
112
- best_accuracy = 0.0 # Variable pour stocker la meilleure accuracy
113
-
114
- for epoch in range(epochs):
115
- classifier.train()
116
- total_loss, correct = 0, 0
117
- batch_count = 0
118
-
119
- for i in range(0, len(train_ds), batch_size):
120
- batch = train_ds[i: i + batch_size]
121
- optimizer.zero_grad()
122
-
123
- input_values = processor(
124
- batch["speech"],
125
- sampling_rate=16000,
126
- return_tensors="pt",
127
- padding=True,
128
- truncation=True,
129
- max_length=max_length
130
- ).input_values.to(device)
131
-
132
- with torch.no_grad():
133
- features = feature_extractor(input_values).last_hidden_state.mean(dim=1)
134
-
135
- logits = classifier(features)
136
- labels = torch.tensor(batch["label"], dtype=torch.long, device=device)
137
 
138
- if labels.numel() == 0:
139
- continue
140
-
141
- loss = loss_fn(logits, labels)
142
-
143
- loss.backward()
144
- optimizer.step()
145
-
146
- total_loss += loss.item()
147
- correct += (logits.argmax(dim=-1) == labels).sum().item()
148
- batch_count += 1
149
-
150
- train_acc = correct / len(train_ds)
151
-
152
- # Sauvegarde du modèle seulement si la précision s'améliore
153
- if train_acc > best_accuracy:
154
- best_accuracy = train_acc
155
- torch.save({
156
- "classifier_state_dict": classifier.state_dict(),
157
- "feature_extractor_state_dict": feature_extractor.state_dict(),
158
- "processor": processor
159
- }, "acc_model.pth")
160
- print(f"✅ Nouveau meilleur modèle sauvegardé ! Accuracy: {best_accuracy:.4f}")
161
-
162
- print(f"Epoch {epoch+1}/{epochs} - Loss: {total_loss/batch_count:.4f} - Accuracy: {train_acc:.4f}")
163
-
164
- return classifier
165
 
166
- # Entraînement
167
- trained_classifier = train_classifier(feature_extractor, classifier, train_ds, test_ds, epochs=20, batch_size=8)
 
168
 
169
- print("✅ Entraînement terminé, le meilleur modèle a été sauvegardé !")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import torch
 
 
 
2
  import torchaudio
3
+ import os
4
+ from datasets import Dataset, DatasetDict
5
+ from transformers import Wav2Vec2Processor, Wav2Vec2ForSequenceClassification, TrainingArguments, Trainer
6
+
7
+ # 🔹 Paramètres
8
+ MODEL_NAME = "facebook/wav2vec2-large-xlsr-53-french"
9
+ NUM_LABELS = 3 # Nombre de classes émotionnelles
10
+ BATCH_SIZE = 8
11
+ EPOCHS = 10
12
+ LEARNING_RATE = 1e-4
13
+ MAX_LENGTH = 32000 # Ajuste selon la durée de tes fichiers audio
14
+
15
+ # 🔹 Vérifier GPU dispo
16
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
17
+
18
+ # 🔹 Charger le processeur et le modèle
19
+ processor = Wav2Vec2Processor.from_pretrained(MODEL_NAME)
20
+ model = Wav2Vec2ForSequenceClassification.from_pretrained(
21
+ MODEL_NAME,
22
+ num_labels=NUM_LABELS,
23
+ problem_type="single_label_classification"
24
+ ).to(device)
25
+
26
+ # 🔹 Fonction pour charger les fichiers audio sans CSV
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  def load_audio_data(data_dir):
28
+ data = {"file_path": [], "label": []}
29
+ labels = ["colere", "joie", "neutre"] # Ajuste selon tes classes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ for label in labels:
32
+ folder_path = os.path.join(data_dir, label)
33
+ for file in os.listdir(folder_path):
34
+ if file.endswith(".wav"):
35
+ data["file_path"].append(os.path.join(folder_path, file))
36
+ data["label"].append(labels.index(label))
37
+
38
+ dataset = Dataset.from_dict(data)
39
+ train_test_split = dataset.train_test_split(test_size=0.2) # 80% train, 20% test
40
+ return DatasetDict({"train": train_test_split["train"], "test": train_test_split["test"]})
41
+
42
+ # 🔹 Prétraitement de l'audio
43
+ def preprocess_audio(file_path):
44
+ waveform, sample_rate = torchaudio.load(file_path)
45
+ inputs = processor(
46
+ waveform.squeeze().numpy(),
47
+ sampling_rate=sample_rate,
48
+ return_tensors="pt",
49
  padding=True,
50
  truncation=True,
51
+ max_length=MAX_LENGTH # ✅ Correction de l'erreur
 
52
  )
53
+ return inputs["input_values"][0] # Récupère les valeurs audio prétraitées
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ # 🔹 Charger et prétraiter le dataset
56
+ data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "data"))
57
+ ds = load_audio_data(data_dir)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ def preprocess_batch(batch):
60
+ batch["input_values"] = preprocess_audio(batch["file_path"])
61
+ return batch
62
 
63
+ ds = ds.map(preprocess_batch, remove_columns=["file_path"])
64
+
65
+ # 🔹 Définir les arguments d'entraînement
66
+ training_args = TrainingArguments(
67
+ output_dir="./wav2vec2_emotion",
68
+ evaluation_strategy="epoch",
69
+ save_strategy="epoch",
70
+ learning_rate=LEARNING_RATE,
71
+ per_device_train_batch_size=BATCH_SIZE,
72
+ per_device_eval_batch_size=BATCH_SIZE,
73
+ num_train_epochs=EPOCHS,
74
+ save_total_limit=2,
75
+ logging_dir="./logs",
76
+ logging_steps=10,
77
+ )
78
+
79
+ # 🔹 Définir le trainer
80
+ trainer = Trainer(
81
+ model=model,
82
+ args=training_args,
83
+ train_dataset=ds["train"],
84
+ eval_dataset=ds["test"],
85
+ )
86
+
87
+ # 🚀 Lancer l'entraînement
88
+ trainer.train()
src/utils/preprocessing.py CHANGED
@@ -3,8 +3,8 @@ import soundfile as sf
3
  import torch
4
  import torchaudio
5
  import numpy as np
6
- from src.model.feature_extractor import processor # type: ignore
7
- from src.config import DEVICE
8
 
9
  # Resampler pour convertir en 16kHz
10
  resampler = torchaudio.transforms.Resample(orig_freq=48_000, new_freq=16_000)
 
3
  import torch
4
  import torchaudio
5
  import numpy as np
6
+ from model.feature_extractor import processor # type: ignore
7
+ from config import DEVICE
8
 
9
  # Resampler pour convertir en 16kHz
10
  resampler = torchaudio.transforms.Resample(orig_freq=48_000, new_freq=16_000)