Wizualizowanie i przekształcanie danych
- 15 min
Po zapoznaniu się z nieprzetworzonym kształtem fali następnym krokiem jest przekształcenie dźwięku w reprezentację, która jest przydatna do klasyfikacji.
Kształt fali pokazuje amplitudę w czasie.
Spektrogram pokazuje zawartość częstotliwości w czasie. Aby utworzyć spektrogram, obliczasz krótkoterminową transformację Fouriera (STFT) w małych nakładających się oknach kształtów fali. TensorFlow udostępnia tę operację przez tf.signal.stft.
W starszych wersjach tego przepływu pracy spectrogramy zostały zapisane jako pliki PNG, a następnie załadowane z powrotem jako obrazy. Zaktualizowane podejście zachowuje spektrogramy jako tensory. Pozwala to uniknąć dodatkowych operacji we/wy na plikach, unika przekształcania rozmiaru obrazów i pasuje do bieżącego wzorca samouczka dźwiękowego TensorFlow.
Przygotowywanie binarnego zestawu danych audio
Zacznij od pobrania tych samych mini poleceń mowy z poprzedniej lekcji. Poniższy kod pobiera zestaw danych, jeśli nie jest jeszcze obecny, weryfikuje skrót archiwum, lokalizuje wyodrębniony folder dla układów Keras 2 i Keras 3, a następnie kopiuje tylko no foldery i yes do mniejszego katalogu binarnego zestawu danych.
import pathlib
import shutil
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
SOURCE_DATASET_PATH = pathlib.Path("data/mini_speech_commands")
ALT_SOURCE_DATASET_PATH = pathlib.Path("data/mini_speech_commands_extracted/mini_speech_commands")
BINARY_DATASET_PATH = pathlib.Path("data/speech_commands_yes_no")
MINI_SPEECH_COMMANDS_SHA256 = "49650f2341b26d886b46b3f4fb8fed59e30300b17550f1ee4a768b3106cf93a0"
if not SOURCE_DATASET_PATH.exists() and not ALT_SOURCE_DATASET_PATH.exists():
tf.keras.utils.get_file(
"mini_speech_commands.zip",
origin="https://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip",
file_hash=MINI_SPEECH_COMMANDS_SHA256,
hash_algorithm="sha256",
extract=True,
cache_dir=".",
cache_subdir="data",
)
if ALT_SOURCE_DATASET_PATH.exists():
SOURCE_DATASET_PATH = ALT_SOURCE_DATASET_PATH
for label in ("no", "yes"):
target_dir = BINARY_DATASET_PATH / label
target_dir.mkdir(parents=True, exist_ok=True)
for source_file in (SOURCE_DATASET_PATH / label).glob("*.wav"):
target_file = target_dir / source_file.name
if not target_file.exists():
shutil.copy2(source_file, target_file)
Oczekiwane dane wyjściowe: Ten kod konfiguracji nie wyświetla danych wyjściowych.
data/speech_commands_yes_no Tworzy katalog z jednym podkatalogiem dla no i jeden dla yes. TensorFlow 2.16 i nowsze używają Keras 3, który wyodrębnia archiwum do <archive>_extracted podkatalogu. Moduł pomocniczy obsługuje oba układy, aby reszta modułu działała w dowolnej obsługiwanej wersji biblioteki TensorFlow.
Ładowanie plików audio jako zestawów danych TensorFlow
Użyj tf.keras.utils.audio_dataset_from_directory polecenia , aby utworzyć zestawy danych TensorFlow na podstawie struktury katalogów. Pliki mini komend głosowych są próbkowane przy 16 kHz, dlatego output_sequence_length=16000 wybiera stałe ramy czasowe 16 000 próbek na klip, co stanowi dokładnie jedną sekundę dźwięku przy tej częstotliwości próbkowania. Pliki krótsze niż 16 000 próbek są dopełniane zerami, a pliki dłuższe niż 16 000 próbek są obcinane. Argument output_sequence_length nie zmienia próbkowania dźwięku zarejestrowanego w innej częstotliwości próbkowania; ponowne próbkowanie oznacza zmianę liczby próbek reprezentujących każdą sekundę dźwięku. Jeśli dostosowujesz ten przepływ pracy do plików zarejestrowanych z inną częstotliwością próbkowania, przeskaluj je do 16 kHz przed załadowaniem lub użyj opcji sampling_rate i tensorflow-io modułu ładującego, a następnie sprawdź, czy kształty wynikowych tensorów nadal pasują do danych wejściowych modelu.
Kod jawnie ustawia label_mode="int", ponieważ model używa etykiet klas całkowitych z kategoriową entropią krzyżową z rozrzedzeniem w następnej jednostce. Nazwy klas są wnioskowane z nazw folderów w kolejności alfanumerycznej.
SEED = 42
BATCH_SIZE = 64
tf.random.set_seed(SEED)
np.random.seed(SEED)
train_ds, validation_ds = tf.keras.utils.audio_dataset_from_directory(
directory=BINARY_DATASET_PATH,
label_mode="int",
batch_size=BATCH_SIZE,
validation_split=0.2,
subset="both",
seed=SEED,
output_sequence_length=16000,
)
label_names = np.array(train_ds.class_names)
print("Label names:", label_names)
Oczekiwane dane wyjściowe: Moduł ładujący znajduje około 2000 plików WAV w klasach no i yes . W przypadku podziału weryfikacji na 20 procent około 1600 przykładów jest używanych do trenowania i około 400 na potrzeby weryfikacji.
Found 2000 files belonging to 2 classes.
Using 1600 files for training.
Using 400 files for validation.
Label names: ['no' 'yes']
Zachowaj zestaw testów oddzielony od zestawu sprawdzania poprawności. Moduł ładujący nie tworzy oddzielnego podziału testowego, więc poniższy kod rozpakowuje dane przechowywane i dzieli poszczególne klipy na segment weryfikacyjny i segment testowy. Odblokuj przed fragmentowaniem, aby podział odbywa się na poziomie klipu, a nie na poziomie partii. Buforuj dane blokady, aby walidacja i fragmenty testowe nie wielokrotnie dekodować tych samych plików WAV.
holdout_ds = validation_ds.unbatch().cache()
val_ds = holdout_ds.shard(num_shards=2, index=0).batch(BATCH_SIZE)
test_ds = holdout_ds.shard(num_shards=2, index=1).batch(BATCH_SIZE)
Oczekiwane dane wyjściowe: Ten kod nie wyświetla danych wyjściowych. Model używa train_ds do trenowania, val_ds dostrajania podczas trenowania i test_ds do ostatecznej oceny.
Note
Dataset.shard wybiera elementy z zestawu danych. Ponieważ ten kod wywołuje unbatch() przed shard()elementem , elementy fragmentu to pojedyncze klipy audio, a nie całe partie. Z około 400 klipów holdout, val_ds i test_ds każdy zawiera około 200 klipów po przetworzeniu.
Note
Ten moduł stosuje kompaktowy wzorzec podziału zaczerpnięty z samouczka TensorFlow, co pozwala utrzymać przykład w niewielkiej skali. Aby dokładnie ocenić pełny zestaw danych poleceń mowy, użyj train, validation i test podziałów dostarczonych przez TensorFlow Datasets lub podziel według identyfikatora mówiącego, aby klipy z tego samego mówiącego nie pojawiały się zarówno w danych treningowych, jak i ewaluacyjnych.
Zestaw danych zwraca tensory dźwięku z wymiarem kanału. Ponieważ te pliki są mono, usuń dodatkowy wymiar kanału przed utworzeniem spectrogramów.
def squeeze(audio, labels):
audio = tf.squeeze(audio, axis=-1)
return audio, labels
train_ds = train_ds.map(squeeze, num_parallel_calls=tf.data.AUTOTUNE)
val_ds = val_ds.map(squeeze, num_parallel_calls=tf.data.AUTOTUNE)
test_ds = test_ds.map(squeeze, num_parallel_calls=tf.data.AUTOTUNE)
for example_audio, example_labels in train_ds.take(1):
print("Audio batch shape:", example_audio.shape)
print("Label batch shape:", example_labels.shape)
Oczekiwane dane wyjściowe: Każda partia audio zawiera maksymalnie 64 1-sekundowe kształty fali.
Audio batch shape: (64, 16000)
Label batch shape: (64,)
Wizualizowanie kształtów fali
Poniższy kod wykreśli jeden kształt fali z partii szkoleniowej.
example_waveform = example_audio[0]
example_label = label_names[example_labels[0].numpy()]
plt.figure(figsize=(12, 4))
plt.plot(example_waveform.numpy())
plt.title(f"Waveform for '{example_label}'")
plt.xlabel("Sample")
plt.ylabel("Amplitude")
plt.xlim([0, 16000])
plt.show()
Oczekiwane dane wyjściowe: Wykres przedstawia amplitudę ponad 16 000 próbek dla jednego no lub yes klipu audio.
Tworzenie spectrogramów
StFT konwertuje kształt fali z domeny czasu na reprezentację częstotliwości czasu. Model używa tylko amplitudy wartości STFT, więc kod zastosowuje tf.abs do złożonego wyniku STFT. Ostatni wiersz dodaje wymiar kanału, aby warstwy konwolucyjne mogły przetwarzać spektrogram jako dane wejściowe przypominające obrazy.
def get_spectrogram(waveform):
spectrogram = tf.signal.stft(
waveform,
frame_length=255,
frame_step=128,
)
spectrogram = tf.abs(spectrogram)
spectrogram = spectrogram[..., tf.newaxis]
return spectrogram
example_spectrogram = get_spectrogram(example_waveform)
print("Spectrogram shape:", example_spectrogram.shape)
Oczekiwane dane wyjściowe: Jedna sekunda dźwięku (16 000 próbek), długość ramki 255 i krok ramki 128, kształt spektrogramu to (124, 129, 1). Pierwszy wymiar to liczba ram czasowych: floor((16000 - 255) / 128) + 1 = 124. Ta formuła ma zastosowanie, ponieważ tf.signal.stft pozostawia pad_end ustawione na False domyślnie, więc nie dodaje dodatkowej częściowej ramki na końcu. Drugi wymiar to liczba przedziałów częstotliwości: tf.signal.stft dopełnia zerami każdą ramkę do następnej potęgi dwóch dla FFT (256 w tym przypadku), a następnie zwraca fft_length / 2 + 1 = 129 niezależne przedziały. Ostateczny wymiar to oś kanałów dodana dla modelu splotowego.
Spectrogram shape: (124, 129, 1)
Wizualizowanie spektrogramu
Użyj skali logarytmicznej do wyświetlania, aby ułatwić wyświetlanie cichszych składników częstotliwości. Dodaj niewielką wartość epsilon przed wywołaniem np.log, aby wartości zerowe nie stały się ujemną nieskończonością.
def plot_spectrogram(spectrogram, ax):
if len(spectrogram.shape) > 2:
spectrogram = np.squeeze(spectrogram, axis=-1)
log_spec = np.log(spectrogram.T + np.finfo(float).eps)
height = log_spec.shape[0]
width = log_spec.shape[1]
time_steps = np.arange(width)
frequency_bins = np.arange(height)
ax.pcolormesh(time_steps, frequency_bins, log_spec)
ax.set_xlabel("Time frame")
ax.set_ylabel("Frequency bin")
fig, axes = plt.subplots(2, figsize=(12, 8))
axes[0].plot(example_waveform.numpy())
axes[0].set_title("Waveform")
axes[0].set_xlim([0, 16000])
plot_spectrogram(example_spectrogram.numpy(), axes[1])
axes[1].set_title("Spectrogram")
plt.suptitle(example_label.title())
plt.show()
Oczekiwane dane wyjściowe: Pierwszy wykres przedstawia kształt fali. Drugi wykres przedstawia spektrogram z przedziałami czasu na osi poziomej, przedziałami częstotliwości na osi pionowej i intensywnością koloru reprezentującą wielkość.
Tworzenie zestawów danych spektrogramów
Mapuj zestawy danych falowych na zestawy danych spektrogramów. Buforowanie i wstępne pobieranie zestawów danych zmniejsza opóźnienia w potoku wejściowym podczas procesu trenowania.
def make_spectrogram_dataset(dataset):
return dataset.map(
map_func=lambda audio, label: (get_spectrogram(audio), label),
num_parallel_calls=tf.data.AUTOTUNE,
)
train_spectrogram_ds = make_spectrogram_dataset(train_ds)
val_spectrogram_ds = make_spectrogram_dataset(val_ds)
test_spectrogram_ds = make_spectrogram_dataset(test_ds)
train_spectrogram_ds = train_spectrogram_ds.cache().shuffle(1000, seed=SEED).prefetch(tf.data.AUTOTUNE)
val_spectrogram_ds = val_spectrogram_ds.cache().prefetch(tf.data.AUTOTUNE)
test_spectrogram_ds = test_spectrogram_ds.cache().prefetch(tf.data.AUTOTUNE)
for spectrograms, labels in train_spectrogram_ds.take(1):
print("Spectrogram batch shape:", spectrograms.shape)
print("Label batch shape:", labels.shape)
Oczekiwane dane wyjściowe: Zestaw danych gotowy do modelu zawiera partie tensorów spektrogramu i etykiet całkowitych.
Spectrogram batch shape: (64, 124, 129, 1)
Label batch shape: (64,)
Sprawdź swoją wiedzę
Opinia
Czy ta strona była pomocna?
Nie
Potrzebujesz pomocy dotyczącej tego tematu?
Chcesz spróbować użyć asystenta Ask Learn, aby wyjaśnić ten temat lub uzyskać instrukcje, które go dotyczą?