![]() | ![]() | ![]() | ![]() |
Tutorial ini akan menunjukkan kepada Anda bagaimana membangun jaringan pengenalan suara dasar yang mengenali sepuluh kata berbeda. Penting untuk diketahui bahwa sistem pengenalan ucapan dan audio yang sebenarnya jauh lebih kompleks, tetapi seperti MNIST untuk gambar, ini akan memberi Anda pemahaman dasar tentang teknik yang terlibat. Setelah Anda menyelesaikan tutorial ini, Anda akan memiliki model yang mencoba untuk mengklasifikasikan klip audio satu detik sebagai "turun", "pergi", "kiri", "tidak", "kanan", "berhenti", "naik " dan ya".
Mempersiapkan
Impor modul dan dependensi yang diperlukan.
import os
import pathlib
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import tensorflow as tf
from tensorflow.keras.layers.experimental import preprocessing
from tensorflow.keras import layers
from tensorflow.keras import models
from IPython import display
# Set seed for experiment reproducibility
seed = 42
tf.random.set_seed(seed)
np.random.seed(seed)
Impor set data Perintah Pidato
Anda akan menulis skrip untuk mengunduh sebagian dari kumpulan data Perintah Ucapan . Dataset asli terdiri dari lebih dari 105.000 file audio WAV orang yang mengucapkan tiga puluh kata berbeda. Data ini dikumpulkan oleh Google dan dirilis di bawah lisensi CC BY, dan Anda dapat membantu menyempurnakannya dengan menyumbangkan suara Anda sendiri selama lima menit .
Anda akan menggunakan sebagian dari kumpulan data untuk menghemat waktu dengan pemuatan data. Ekstrak mini_speech_commands.zip
dan muat dengan menggunakan API tf.data
.
data_dir = pathlib.Path('data/mini_speech_commands')
if not data_dir.exists():
tf.keras.utils.get_file(
'mini_speech_commands.zip',
origin="http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip",
extract=True,
cache_dir='.', cache_subdir='data')
Downloading data from http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip 182083584/182082353 [==============================] - 2s 0us/step
Periksa statistik dasar tentang kumpulan data.
commands = np.array(tf.io.gfile.listdir(str(data_dir)))
commands = commands[commands != 'README.md']
print('Commands:', commands)
Commands: ['down' 'yes' 'left' 'right' 'no' 'go' 'stop' 'up']
Ekstrak file audio ke dalam daftar dan acak.
filenames = tf.io.gfile.glob(str(data_dir) + '/*/*')
filenames = tf.random.shuffle(filenames)
num_samples = len(filenames)
print('Number of total examples:', num_samples)
print('Number of examples per label:',
len(tf.io.gfile.listdir(str(data_dir/commands[0]))))
print('Example file tensor:', filenames[0])
Number of total examples: 8000 Number of examples per label: 1000 Example file tensor: tf.Tensor(b'data/mini_speech_commands/yes/fde2dee7_nohash_0.wav', shape=(), dtype=string)
Pisahkan file menjadi set pelatihan, validasi, dan pengujian menggunakan rasio 80:10:10, masing-masing.
train_files = filenames[:6400]
val_files = filenames[6400: 6400 + 800]
test_files = filenames[-800:]
print('Training set size', len(train_files))
print('Validation set size', len(val_files))
print('Test set size', len(test_files))
Training set size 6400 Validation set size 800 Test set size 800
Membaca file audio dan labelnya
File audio awalnya akan dibaca sebagai file biner, yang ingin Anda ubah menjadi tensor numerik.
Untuk memuat file audio, Anda akan menggunakan tf.audio.decode_wav
, yang mengembalikan audio yang dikodekan WAV sebagai Tensor dan kecepatan sampel.
File WAV berisi data deret waktu dengan sejumlah sampel per detik. Setiap sampel mewakili amplitudo sinyal audio pada waktu tertentu. Dalam sistem 16-bit, seperti file di mini_speech_commands
, nilainya berkisar dari -32768 hingga 32767. Kecepatan sampel untuk kumpulan data ini adalah 16kHz. Perhatikan bahwa tf.audio.decode_wav
akan menormalkan nilai ke kisaran [-1.0, 1.0].
def decode_audio(audio_binary):
audio, _ = tf.audio.decode_wav(audio_binary)
return tf.squeeze(audio, axis=-1)
Label untuk setiap file WAV adalah direktori induknya.
def get_label(file_path):
parts = tf.strings.split(file_path, os.path.sep)
# Note: You'll use indexing here instead of tuple unpacking to enable this
# to work in a TensorFlow graph.
return parts[-2]
Mari tentukan metode yang akan mengambil nama file dari file WAV dan mengeluarkan tupel yang berisi audio dan label untuk pelatihan yang diawasi.
def get_waveform_and_label(file_path):
label = get_label(file_path)
audio_binary = tf.io.read_file(file_path)
waveform = decode_audio(audio_binary)
return waveform, label
Anda sekarang akan menerapkan process_path
untuk membangun set pelatihan Anda untuk mengekstrak pasangan label audio dan memeriksa hasilnya. Anda akan membuat set validasi dan pengujian menggunakan prosedur serupa nanti.
AUTOTUNE = tf.data.AUTOTUNE
files_ds = tf.data.Dataset.from_tensor_slices(train_files)
waveform_ds = files_ds.map(get_waveform_and_label, num_parallel_calls=AUTOTUNE)
Mari kita periksa beberapa bentuk gelombang audio dengan labelnya yang sesuai.
rows = 3
cols = 3
n = rows*cols
fig, axes = plt.subplots(rows, cols, figsize=(10, 12))
for i, (audio, label) in enumerate(waveform_ds.take(n)):
r = i // cols
c = i % cols
ax = axes[r][c]
ax.plot(audio.numpy())
ax.set_yticks(np.arange(-1.2, 1.2, 0.2))
label = label.numpy().decode('utf-8')
ax.set_title(label)
plt.show()
Spectrogram
Anda akan mengubah bentuk gelombang menjadi spektogram, yang menunjukkan perubahan frekuensi dari waktu ke waktu dan dapat direpresentasikan sebagai gambar 2D. Hal ini dapat dilakukan dengan menerapkan short-time Fourier transform (STFT) untuk mengubah audio menjadi domain frekuensi waktu.
Transformasi Fourier ( tf.signal.fft
) mengubah sinyal menjadi frekuensi komponennya, tetapi kehilangan informasi sepanjang waktu. STFT ( tf.signal.stft
) membagi sinyal menjadi jendela waktu dan menjalankan transformasi Fourier pada setiap jendela, mempertahankan beberapa informasi waktu, dan mengembalikan tensor 2D tempat Anda dapat menjalankan konvolusi standar.
STFT menghasilkan larik bilangan kompleks yang mewakili besaran dan fase. Namun, Anda hanya memerlukan besaran untuk tutorial ini, yang dapat diturunkan dengan menerapkan tf.abs
pada keluaran tf.signal.stft
.
Pilih frame_length
dan frame_step
parameter seperti yang dihasilkan spektogram "image" hampir persegi. Untuk informasi lebih lanjut tentang pilihan parameter STFT, Anda dapat merujuk ke video ini tentang pemrosesan sinyal audio.
Anda juga ingin bentuk gelombang memiliki panjang yang sama, sehingga ketika Anda mengubahnya menjadi gambar spektogram, hasilnya akan memiliki dimensi yang sama. Ini dapat dilakukan hanya dengan mengisi nol klip audio yang lebih pendek dari satu detik.
def get_spectrogram(waveform):
# Padding for files with less than 16000 samples
zero_padding = tf.zeros([16000] - tf.shape(waveform), dtype=tf.float32)
# Concatenate audio with padding so that all audio clips will be of the
# same length
waveform = tf.cast(waveform, tf.float32)
equal_length = tf.concat([waveform, zero_padding], 0)
spectrogram = tf.signal.stft(
equal_length, frame_length=255, frame_step=128)
spectrogram = tf.abs(spectrogram)
return spectrogram
Selanjutnya, Anda akan menjelajahi datanya. Bandingkan bentuk gelombang, spektogram dan audio sebenarnya dari satu contoh dari dataset.
for waveform, label in waveform_ds.take(1):
label = label.numpy().decode('utf-8')
spectrogram = get_spectrogram(waveform)
print('Label:', label)
print('Waveform shape:', waveform.shape)
print('Spectrogram shape:', spectrogram.shape)
print('Audio playback')
display.display(display.Audio(waveform, rate=16000))
Label: yes Waveform shape: (13375,) Spectrogram shape: (124, 129) Audio playback
def plot_spectrogram(spectrogram, ax):
# Convert to frequencies to log scale and transpose so that the time is
# represented in the x-axis (columns).
log_spec = np.log(spectrogram.T)
height = log_spec.shape[0]
X = np.arange(16000, step=height + 1)
Y = range(height)
ax.pcolormesh(X, Y, log_spec)
fig, axes = plt.subplots(2, figsize=(12, 8))
timescale = np.arange(waveform.shape[0])
axes[0].plot(timescale, waveform.numpy())
axes[0].set_title('Waveform')
axes[0].set_xlim([0, 16000])
plot_spectrogram(spectrogram.numpy(), axes[1])
axes[1].set_title('Spectrogram')
plt.show()
/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/ipykernel_launcher.py:4: RuntimeWarning: divide by zero encountered in log after removing the cwd from sys.path. /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/ipykernel_launcher.py:8: MatplotlibDeprecationWarning: shading='flat' when X and Y have the same dimensions as C is deprecated since 3.3. Either specify the corners of the quadrilaterals with X and Y, or pass shading='auto', 'nearest' or 'gouraud', or set rcParams['pcolor.shading']. This will become an error two minor releases later.
Sekarang ubah dataset bentuk gelombang menjadi gambar spektrogram dan labelnya yang sesuai sebagai ID integer.
def get_spectrogram_and_label_id(audio, label):
spectrogram = get_spectrogram(audio)
spectrogram = tf.expand_dims(spectrogram, -1)
label_id = tf.argmax(label == commands)
return spectrogram, label_id
spectrogram_ds = waveform_ds.map(
get_spectrogram_and_label_id, num_parallel_calls=AUTOTUNE)
Periksa spektrogram "gambar" untuk sampel yang berbeda dari dataset.
rows = 3
cols = 3
n = rows*cols
fig, axes = plt.subplots(rows, cols, figsize=(10, 10))
for i, (spectrogram, label_id) in enumerate(spectrogram_ds.take(n)):
r = i // cols
c = i % cols
ax = axes[r][c]
plot_spectrogram(np.squeeze(spectrogram.numpy()), ax)
ax.set_title(commands[label_id.numpy()])
ax.axis('off')
plt.show()
/tmpfs/src/tf_docs_env/lib/python3.6/site-packages/ipykernel_launcher.py:4: RuntimeWarning: divide by zero encountered in log after removing the cwd from sys.path. /tmpfs/src/tf_docs_env/lib/python3.6/site-packages/ipykernel_launcher.py:8: MatplotlibDeprecationWarning: shading='flat' when X and Y have the same dimensions as C is deprecated since 3.3. Either specify the corners of the quadrilaterals with X and Y, or pass shading='auto', 'nearest' or 'gouraud', or set rcParams['pcolor.shading']. This will become an error two minor releases later.
Bangun dan latih modelnya
Sekarang Anda dapat membuat dan melatih model Anda. Namun sebelum Anda melakukannya, Anda harus mengulangi set pelatihan yang diproses sebelumnya pada set validasi dan pengujian.
def preprocess_dataset(files):
files_ds = tf.data.Dataset.from_tensor_slices(files)
output_ds = files_ds.map(get_waveform_and_label, num_parallel_calls=AUTOTUNE)
output_ds = output_ds.map(
get_spectrogram_and_label_id, num_parallel_calls=AUTOTUNE)
return output_ds
train_ds = spectrogram_ds
val_ds = preprocess_dataset(val_files)
test_ds = preprocess_dataset(test_files)
Batch set pelatihan dan validasi untuk pelatihan model.
batch_size = 64
train_ds = train_ds.batch(batch_size)
val_ds = val_ds.batch(batch_size)
Tambahkan operasi cache()
set data cache()
dan prefetch()
untuk mengurangi latensi baca saat melatih model.
train_ds = train_ds.cache().prefetch(AUTOTUNE)
val_ds = val_ds.cache().prefetch(AUTOTUNE)
Untuk model, Anda akan menggunakan jaringan neural konvolusional sederhana (CNN), karena Anda telah mengubah file audio menjadi gambar spektrogram. Model ini juga memiliki lapisan praproses tambahan berikut:
- Sebuah
Resizing
layer untuk mengurangi input agar model dapat berlatih lebih cepat. - Lapisan
Normalization
untuk menormalkan setiap piksel pada gambar berdasarkan mean dan deviasi standarnya.
Untuk lapisan Normalization
, metode adapt
pertama-tama perlu dipanggil pada data pelatihan untuk menghitung statistik agregat (yaitu mean dan deviasi standar).
for spectrogram, _ in spectrogram_ds.take(1):
input_shape = spectrogram.shape
print('Input shape:', input_shape)
num_labels = len(commands)
norm_layer = preprocessing.Normalization()
norm_layer.adapt(spectrogram_ds.map(lambda x, _: x))
model = models.Sequential([
layers.Input(shape=input_shape),
preprocessing.Resizing(32, 32),
norm_layer,
layers.Conv2D(32, 3, activation='relu'),
layers.Conv2D(64, 3, activation='relu'),
layers.MaxPooling2D(),
layers.Dropout(0.25),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(num_labels),
])
model.summary()
Input shape: (124, 129, 1) Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= resizing (Resizing) (None, 32, 32, 1) 0 _________________________________________________________________ normalization (Normalization (None, 32, 32, 1) 3 _________________________________________________________________ conv2d (Conv2D) (None, 30, 30, 32) 320 _________________________________________________________________ conv2d_1 (Conv2D) (None, 28, 28, 64) 18496 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 14, 14, 64) 0 _________________________________________________________________ dropout (Dropout) (None, 14, 14, 64) 0 _________________________________________________________________ flatten (Flatten) (None, 12544) 0 _________________________________________________________________ dense (Dense) (None, 128) 1605760 _________________________________________________________________ dropout_1 (Dropout) (None, 128) 0 _________________________________________________________________ dense_1 (Dense) (None, 8) 1032 ================================================================= Total params: 1,625,611 Trainable params: 1,625,608 Non-trainable params: 3 _________________________________________________________________
model.compile(
optimizer=tf.keras.optimizers.Adam(),
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'],
)
EPOCHS = 10
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=EPOCHS,
callbacks=tf.keras.callbacks.EarlyStopping(verbose=1, patience=2),
)
Epoch 1/10 100/100 [==============================] - 16s 47ms/step - loss: 1.9142 - accuracy: 0.2855 - val_loss: 1.2969 - val_accuracy: 0.5938 Epoch 2/10 100/100 [==============================] - 0s 4ms/step - loss: 1.2597 - accuracy: 0.5581 - val_loss: 0.8918 - val_accuracy: 0.7237 Epoch 3/10 100/100 [==============================] - 0s 4ms/step - loss: 0.9525 - accuracy: 0.6647 - val_loss: 0.7742 - val_accuracy: 0.7337 Epoch 4/10 100/100 [==============================] - 0s 4ms/step - loss: 0.7793 - accuracy: 0.7288 - val_loss: 0.6514 - val_accuracy: 0.7850 Epoch 5/10 100/100 [==============================] - 0s 4ms/step - loss: 0.6715 - accuracy: 0.7639 - val_loss: 0.5986 - val_accuracy: 0.8000 Epoch 6/10 100/100 [==============================] - 0s 4ms/step - loss: 0.5647 - accuracy: 0.7968 - val_loss: 0.5901 - val_accuracy: 0.8075 Epoch 7/10 100/100 [==============================] - 0s 4ms/step - loss: 0.5122 - accuracy: 0.8093 - val_loss: 0.5526 - val_accuracy: 0.8087 Epoch 8/10 100/100 [==============================] - 0s 4ms/step - loss: 0.4780 - accuracy: 0.8298 - val_loss: 0.5166 - val_accuracy: 0.8163 Epoch 9/10 100/100 [==============================] - 0s 4ms/step - loss: 0.4216 - accuracy: 0.8544 - val_loss: 0.4806 - val_accuracy: 0.8438 Epoch 10/10 100/100 [==============================] - 0s 4ms/step - loss: 0.3976 - accuracy: 0.8510 - val_loss: 0.4848 - val_accuracy: 0.8388
Mari kita periksa kurva kerugian pelatihan dan validasi untuk melihat bagaimana model Anda telah meningkat selama pelatihan.
metrics = history.history
plt.plot(history.epoch, metrics['loss'], metrics['val_loss'])
plt.legend(['loss', 'val_loss'])
plt.show()
Evaluasi kinerja set pengujian
Mari jalankan model di set pengujian dan periksa performa.
test_audio = []
test_labels = []
for audio, label in test_ds:
test_audio.append(audio.numpy())
test_labels.append(label.numpy())
test_audio = np.array(test_audio)
test_labels = np.array(test_labels)
y_pred = np.argmax(model.predict(test_audio), axis=1)
y_true = test_labels
test_acc = sum(y_pred == y_true) / len(y_true)
print(f'Test set accuracy: {test_acc:.0%}')
Test set accuracy: 84%
Tampilkan matriks kebingungan
Matriks konfusi berguna untuk melihat seberapa baik performa model pada setiap perintah dalam set pengujian.
confusion_mtx = tf.math.confusion_matrix(y_true, y_pred)
plt.figure(figsize=(10, 8))
sns.heatmap(confusion_mtx, xticklabels=commands, yticklabels=commands,
annot=True, fmt='g')
plt.xlabel('Prediction')
plt.ylabel('Label')
plt.show()
Jalankan inferensi pada file audio
Terakhir, verifikasi keluaran prediksi model menggunakan file audio masukan dari seseorang yang mengatakan "tidak". Seberapa baik performa model Anda?
sample_file = data_dir/'no/01bb6a2a_nohash_0.wav'
sample_ds = preprocess_dataset([str(sample_file)])
for spectrogram, label in sample_ds.batch(1):
prediction = model(spectrogram)
plt.bar(commands, tf.nn.softmax(prediction[0]))
plt.title(f'Predictions for "{commands[label[0]]}"')
plt.show()
Anda dapat melihat bahwa model Anda dengan jelas mengenali perintah audio sebagai "no."
Langkah selanjutnya
Tutorial ini menunjukkan bagaimana Anda dapat melakukan klasifikasi audio sederhana menggunakan jaringan neural konvolusional dengan TensorFlow dan Python.
Untuk mempelajari cara menggunakan pembelajaran transfer untuk klasifikasi audio, lihat klasifikasi Suara dengan tutorial YAMNet .
Untuk membuat aplikasi web interaktif Anda sendiri untuk klasifikasi audio, pertimbangkan untuk menggunakan TensorFlow.js - Pengenalan audio menggunakan codelab pembelajaran transfer .
TensorFlow juga memiliki dukungan tambahan untuk persiapan dan augmentasi data audio untuk membantu proyek berbasis audio Anda sendiri.