Bartosz Pietrzak commited on
Commit
710c57b
1 Parent(s): 2ca2ca9

Final touches

Browse files
README.md ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Music Mind
2
+
3
+ [Aplikacja gradio projektu](https://huggingface.co/spaces/bjpietrzak/music_mind)
4
+
5
+ ## CI/CD Pipeline
6
+
7
+ ### 1. **Wybór modelu**
8
+
9
+ Jeśli model nie pochodzi z biblioteki HF, trzeba się upewnić, że jest kompatybilny z klasą Trainer biblioteki HuggingFace [LINK](https://huggingface.co/transformers/v3.3.1/training.html)
10
+
11
+ ### 2. **Wybór danych trenujących**
12
+
13
+ Jeżeli dataset pochodzi z biblioteki HuggingFace, można ten krok pominąć.
14
+
15
+ 1. Zbór uczący musi posiadać:
16
+ - Pliki muzyczne w formacie; znajdujące się w jednym folderze `[.ogg, .mp3 .wav]`
17
+ - Plik json zawierający oznaczenia dla wszystkich plików muzycznych:
18
+ ```json
19
+ {
20
+ "file1.ogg": "jazz",
21
+ "file2.ogg": "hiphop"
22
+ }
23
+ ```
24
+
25
+ 2. Aby dataset miał formę kompatybilną z trenerem HF, dostępny zbiór danych należy sparsować za pomocą skryptu `make_dataset.py` znajdującego się w katalogu `dl/` projektu:
26
+ ```bash
27
+ python make_dataset.py --dir sciezka/do/folderu/plikow/audio --file sciezka/do/pliku/json.json --output lokalizacja/wygenerowanego/datasetu
28
+ ```
29
+
30
+ ### 3. **Trening Modelu**
31
+
32
+ Trening modelu odbywa się poprzez skrypt `train.py` znajdujący się w katalogu `/dl/` projektu.
33
+
34
+ Skrupt posiada następujące flagi:
35
+ - learning_rate: Współczynnik uczenia podczas treningu modelu.
36
+ - train_eval_split: Stosunek danych trenujących do całego zbioru; reszta to dane walidacyjne.
37
+ - model_id: Identyfikator modelu z Hugging Face lub ścieżka do lokalnego modelu.
38
+ - num_epochs: Liczba epok treningowych.
39
+ - seed: Ziarno liczb losowych.
40
+ - save_dir: Ścieżka do katalogu wag tranowanego modelu.
41
+ - dataset: Nazwa/lokalizacja zbioru danych.
42
+
43
+ Przykładowe uruchomienie skryptu:
44
+
45
+ ```bash
46
+ python train_audio_model.py --model_id "facebook/wav2vec2-base-960h" --learning_rate 0.0001 --train_eval_split 0.8 --num_epochs 10 --seed 42 --save_dir "/path/to/save/models" --dataset "marsyas/gtzan"
47
+ ```
48
+
49
+ Wagi i pliki konfiguracyjne modelu zostaną zapisane w podanej ścierzce pod nazwą składającą się z parametrów uczenia np:
50
+
51
+ `/path/to/save/facebook-wav2vec2-base-960h-123-marsyas-gtzan-0.0001`
52
+
53
+ ### 4. **Wersjonowanie modelów**
54
+
55
+ Modele można przesyłać do repozytorium na huggingface.
56
+
57
+ Za pomocą skryptu `push_model.py`:
58
+
59
+ ```bash
60
+ python script_name.py --username your_username --model_dir /path/to/your/model --repo_name your_repo_name --private True
61
+ ```
62
+
63
+ lub za pomocą GUI strony HuggingFace [LINK](https://huggingface.co/new)
64
+
65
+ ![Widok strony początkowej zakładania repozytorium modelu](images/new.png)
66
+
67
+ ![Dodawanie plików do repozytorium](images/file_upload.png)
68
+
69
+ Na stronie:
70
+
71
+ ![alt text](images/new.png)
72
+
73
+ ![alt text](images/file_upload.png)
74
+
75
+ ### 6. **Aktualizowanie modelu w deployowanej aplikacji**
76
+
77
+ Po dodaniu modelu do repozytorium należy go wykorzystać w deployowanej aplikacji dostępnej pod tym adresem:
78
+
79
+ `git clone https://huggingface.co/spaces/bjpietrzak/music_mind`
80
+
81
+ W katalogu głównym projektu znajduje się plik `main.py`. W jego wnętrzu znajduje się zmienna config:
82
+
83
+ ```py
84
+ config = {
85
+ "sampling_rate": 16000,
86
+ "model": "bjpietrzak/distilhubert-gtzan-20-5e-5",
87
+ }
88
+ ```
89
+
90
+ Nazwę repozytorium, oraz model można umieścić w wartości klucza `model`, jeśli jest taka potrzeba, należy zmienić sampling rate.
91
+
92
+ Po zmianie zmian, należy je pchnąć:
93
+
94
+ ```bash
95
+ git push
96
+ ```
97
+
98
+ Zmiany powinny być widoczne po kilku minutach:
99
+
100
+ ![alt text](images/changes.png)
dl/make_dataset.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import argparse
4
+ import librosa
5
+ import pandas as pd
6
+
7
+ def parse_args():
8
+ parser = argparse.ArgumentParser()
9
+ parser.add_argument("--dir", type=str, help="Directory containing OGG audio files.")
10
+ parser.add_argument("--file", type=str, help="JSON file mapping filenames to classes.")
11
+ parser.add_argument('-o', '--output', type=str, default="output_dataset.csv", help="Output CSV file.")
12
+ return vars(parser.parse_args())
13
+
14
+ def load_audio_files(audio_dir, file_class_mapping):
15
+ data = []
16
+ for filename, class_label in file_class_mapping.items():
17
+ file_path = os.path.join(audio_dir, filename)
18
+ if os.path.exists(file_path):
19
+ audio, sr = librosa.load(file_path, sr=None)
20
+ data.append({
21
+ 'filename': filename,
22
+ 'audio': audio,
23
+ 'sampling_rate': sr,
24
+ 'label': class_label
25
+ })
26
+ return data
27
+
28
+ def main(args):
29
+ audio_dir = args['dir']
30
+ json_file = args['file']
31
+
32
+ with open(json_file, 'r') as f:
33
+ file_class_mapping = json.load(f)
34
+
35
+ dataset = load_audio_files(audio_dir, file_class_mapping)
36
+
37
+ df = pd.DataFrame(dataset)
38
+
39
+ df.to_csv(args['output'], index=False)
40
+
41
+ if __name__ == "__main__":
42
+ main(parse_args())
dl/push_model.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ from transformers import AutoModel, AutoTokenizer
3
+ from huggingface_hub import HfApi, HfFolder
4
+
5
+ def parse_args():
6
+ parser = argparse.ArgumentParser()
7
+ parser.add_argument("--username", type=str, required=True, help="Nazwa użytkownika Hugging Face.")
8
+ parser.add_argument("--model_dir", type=str, required=True, help="Ścieżka do zapisanego modelu.")
9
+ parser.add_argument("--repo_name", type=str, required=True, help="Nazwa repozytorium HuggingFace Hub.")
10
+ parser.add_argument("--private", type=bool, default=False, help="Flaga określająca, czy repozytorium powinno być prywatne.")
11
+ return parser.parse_args()
12
+
13
+ def main():
14
+ args = parse_args()
15
+ token = HfFolder.get_token()
16
+ if token is None:
17
+ raise ValueError("Token uwierzytelniający nie został znaleziony. Zaloguj się za pomocą CLI Hugging Face.")
18
+
19
+ model = AutoModel.from_pretrained(args.model_dir)
20
+ tokenizer = AutoTokenizer.from_pretrained(args.model_dir)
21
+
22
+ repo_url = HfApi().create_repo(
23
+ token=token,
24
+ name=args.repo_name,
25
+ organization=args.username,
26
+ private=args.private,
27
+ exist_ok=True
28
+ )
29
+
30
+ model.push_to_hub(args.repo_name, use_auth_token=token)
31
+ tokenizer.push_to_hub(args.repo_name, use_auth_token=token)
32
+
33
+ print(f"Model i tokajzer zostały wysłane do {repo_url}")
34
+
35
+ if __name__ == "__main__":
36
+ main()
dl/train.py CHANGED
@@ -4,6 +4,7 @@ from datasets import load_dataset, Audio
4
  from transformers import (AutoFeatureExtractor,
5
  AutoModelForAudioClassification, TrainingArguments,
6
  Trainer)
 
7
  import evaluate
8
  import random
9
 
@@ -11,15 +12,24 @@ import random
11
  accuracy_metric = evaluate.load("accuracy")
12
 
13
  def parse_args() -> dict:
14
- parser = argparse.ArgumentParser()
15
- parser.add_argument("--learning_rate", type=float, default=5e-5)
16
- parser.add_argument("--train_eval_split", type=float, default=0.9)
17
- parser.add_argument("--model_id", type=str, required=True)
18
- parser.add_argument("--num_epochs", type=int, default=20)
19
- parser.add_argument("--seed", type=int, default=42)
20
- parser.add_argument("--save_dir", type=str, default=".")
 
 
 
 
 
 
 
 
21
  return vars(parser.parse_args())
22
 
 
23
  def compute_metrics(eval_pred):
24
  predictions = np.argmax(eval_pred.predictions, axis=1)
25
  return accuracy_metric.compute(predictions=predictions,
@@ -29,7 +39,7 @@ def main(args: dict) -> None:
29
  random.seed(args["seed"])
30
  max_duration = 30.0
31
 
32
- gtzan = load_dataset("marsyas/gtzan", "all")
33
  gtzan = gtzan["train"].train_test_split(seed=42, shuffle=True,
34
  test_size=1 - args["train_eval_split"])
35
 
@@ -70,8 +80,10 @@ def main(args: dict) -> None:
70
  label2id=label2id,
71
  id2label=id2label)
72
 
 
 
73
  training_args = TrainingArguments(
74
- output_dir=args["save_dir"],
75
  evaluation_strategy="epoch",
76
  save_strategy="epoch",
77
  learning_rate=args["learning_rate"],
 
4
  from transformers import (AutoFeatureExtractor,
5
  AutoModelForAudioClassification, TrainingArguments,
6
  Trainer)
7
+ import os
8
  import evaluate
9
  import random
10
 
 
12
  accuracy_metric = evaluate.load("accuracy")
13
 
14
  def parse_args() -> dict:
15
+ parser = argparse.ArgumentParser(description="Skrypt do trenowania modelu klasyfikacji audio.")
16
+ parser.add_argument("--learning_rate", type=float, default=5e-5,
17
+ help="Współczynnik uczenia podczas treningu modelu.")
18
+ parser.add_argument("--train_eval_split", type=float, default=0.9,
19
+ help="Stosunek danych trenujących do całego zbioru; reszta to dane walidacyjne.")
20
+ parser.add_argument("--model_id", type=str, required=True,
21
+ help="Identyfikator modelu z Hugging Face lub ścieżka do lokalnego modelu.")
22
+ parser.add_argument("--num_epochs", type=int, default=20,
23
+ help="Liczba epok treningowych.")
24
+ parser.add_argument("--seed", type=int, default=42,
25
+ help="Ziarno liczb losowych.")
26
+ parser.add_argument("--save_dir", type=str, default=".",
27
+ help="Ścieżka do katalogu wag tranowanego modelu.")
28
+ parser.add_argument("--dataset", type=str, default="marsyas/gtzan",
29
+ help="Nazwa/lokalizacja zbioru danych.")
30
  return vars(parser.parse_args())
31
 
32
+
33
  def compute_metrics(eval_pred):
34
  predictions = np.argmax(eval_pred.predictions, axis=1)
35
  return accuracy_metric.compute(predictions=predictions,
 
39
  random.seed(args["seed"])
40
  max_duration = 30.0
41
 
42
+ gtzan = load_dataset(args["dataset"], "all")
43
  gtzan = gtzan["train"].train_test_split(seed=42, shuffle=True,
44
  test_size=1 - args["train_eval_split"])
45
 
 
80
  label2id=label2id,
81
  id2label=id2label)
82
 
83
+ dir_name = f"{args["model_id"]}-{args["seed"]}-{args["dataset"]}-{args['learning_rate']}".replace("/", "-")
84
+
85
  training_args = TrainingArguments(
86
+ output_dir=os.path.join(args["save_dir"], dir_name),
87
  evaluation_strategy="epoch",
88
  save_strategy="epoch",
89
  learning_rate=args["learning_rate"],
images/changes.png ADDED
images/file_upload.png ADDED
images/new.png ADDED