Hari Komunitas ML adalah 9 November! Bergabung dengan kami untuk update dari TensorFlow, JAX, dan lebih Pelajari lebih lanjut

Transfer pembelajaran dengan YAMNet untuk klasifikasi suara lingkungan

Lihat di TensorFlow.org Jalankan di Google Colab Lihat di GitHub Unduh buku catatan Lihat model Hub TF

YAMNet adalah jaringan saraf dalam pra-terlatih yang dapat memprediksi kejadian audio dari 521 kelas , seperti tawa, menggonggong, atau sirene.

Dalam tutorial ini Anda akan belajar bagaimana:

  • Muat dan gunakan model YAMNet untuk inferensi.
  • Buat model baru menggunakan penyematan YAMNet untuk mengklasifikasikan suara kucing dan anjing.
  • Evaluasi dan ekspor model Anda.

Impor TensorFlow dan perpustakaan lainnya

Mulai dengan memasang TensorFlow I / O , yang akan membuat lebih mudah bagi Anda untuk memuat file audio dari disk.

pip install tensorflow_io
import os

from IPython import display
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_io as tfio

Tentang YAMNet

YAMNet adalah jaringan saraf pra-terlatih yang mempekerjakan MobileNetV1 depthwise-dipisahkan arsitektur belit. Hal ini dapat menggunakan gelombang audio sebagai masukan dan membuat prediksi independen untuk masing-masing 521 kejadian audio dari AudioSet corpus.

Secara internal, model mengekstrak "bingkai" dari sinyal audio dan memproses kumpulan bingkai ini. Versi model ini menggunakan bingkai yang berdurasi 0,96 detik dan mengekstrak satu bingkai setiap 0,48 detik .

Model ini menerima 1-D float32 Tensor atau NumPy array yang berisi gelombang panjang sewenang-wenang, direpresentasikan sebagai single-channel (mono) sampel 16 kHz dalam rentang [-1.0, +1.0] . Tutorial ini berisi kode untuk membantu Anda mengonversi file WAV ke format yang didukung.

Model mengembalikan 3 output, termasuk skor kelas, embeddings (yang akan Anda gunakan untuk belajar transfer), dan log mel spektogram . Anda dapat menemukan rincian lebih lanjut di sini .

Salah satu penggunaan khusus YAMNet adalah sebagai ekstraktor fitur tingkat tinggi - keluaran embedding 1.024 dimensi. Anda akan menggunakan fitur masukan dasar (YAMNet) model dan memberi makan mereka ke dalam model dangkal Anda terdiri dari satu tersembunyi tf.keras.layers.Dense lapisan. Kemudian, Anda akan melatih jaringan pada sejumlah kecil data untuk klasifikasi audio tanpa memerlukan banyak data label dan melatih end-to-end. (Hal ini mirip dengan mentransfer belajar untuk klasifikasi citra dengan TensorFlow Hub untuk informasi lebih lanjut.)

Pertama, Anda akan menguji model dan melihat hasil klasifikasi audio. Anda kemudian akan membangun pipa pra-pemrosesan data.

Memuat YAMNet dari TensorFlow Hub

Anda akan menggunakan YAMNet pra-terlatih dari Tensorflow Hub untuk mengekstrak embeddings dari file suara.

Memuat model dari TensorFlow Hub sangat mudah: pilih model, copy URL-nya, dan menggunakan load fungsi.

yamnet_model_handle = 'https://tfhub.dev/google/yamnet/1'
yamnet_model = hub.load(yamnet_model_handle)

Dengan model dimuat, Anda dapat mengikuti YAMNet penggunaan dasar tutorial dan mendownload file WAV sampel untuk menjalankan kesimpulan.

testing_wav_file_name = tf.keras.utils.get_file('miaow_16k.wav',
                                                'https://storage.googleapis.com/audioset/miaow_16k.wav',
                                                cache_dir='./',
                                                cache_subdir='test_data')

print(testing_wav_file_name)
Downloading data from https://storage.googleapis.com/audioset/miaow_16k.wav
221184/215546 [==============================] - 0s 0us/step
./test_data/miaow_16k.wav

Anda akan memerlukan fungsi untuk memuat file audio, yang juga akan digunakan nanti saat bekerja dengan data pelatihan. (Pelajari lebih lanjut tentang membaca file audio dan label mereka di pengakuan audio yang sederhana .

# Utility functions for loading audio files and making sure the sample rate is correct.

@tf.function
def load_wav_16k_mono(filename):
    """ Load a WAV file, convert it to a float tensor, resample to 16 kHz single-channel audio. """
    file_contents = tf.io.read_file(filename)
    wav, sample_rate = tf.audio.decode_wav(
          file_contents,
          desired_channels=1)
    wav = tf.squeeze(wav, axis=-1)
    sample_rate = tf.cast(sample_rate, dtype=tf.int64)
    wav = tfio.audio.resample(wav, rate_in=sample_rate, rate_out=16000)
    return wav
testing_wav_data = load_wav_16k_mono(testing_wav_file_name)

_ = plt.plot(testing_wav_data)

# Play the audio file.
display.Audio(testing_wav_data,rate=16000)
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/parallel_for/pfor.py:2382: calling gather (from tensorflow.python.ops.array_ops) with validate_indices is deprecated and will be removed in a future version.
Instructions for updating:
The `validate_indices` argument has no effect. Indices are always validated on CPU and never validated on GPU.
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/ops/parallel_for/pfor.py:2382: calling gather (from tensorflow.python.ops.array_ops) with validate_indices is deprecated and will be removed in a future version.
Instructions for updating:
The `validate_indices` argument has no effect. Indices are always validated on CPU and never validated on GPU.
WARNING:tensorflow:Using a while_loop for converting IO>AudioResample
WARNING:tensorflow:Using a while_loop for converting IO>AudioResample

png

Muat pemetaan kelas

Sangat penting untuk memuat nama kelas yang dapat dikenali oleh YAMNet. File pemetaan hadir di yamnet_model.class_map_path() dalam format CSV.

class_map_path = yamnet_model.class_map_path().numpy().decode('utf-8')
class_names =list(pd.read_csv(class_map_path)['display_name'])

for name in class_names[:20]:
  print(name)
print('...')
Speech
Child speech, kid speaking
Conversation
Narration, monologue
Babbling
Speech synthesizer
Shout
Bellow
Whoop
Yell
Children shouting
Screaming
Whispering
Laughter
Baby laughter
Giggle
Snicker
Belly laugh
Chuckle, chortle
Crying, sobbing
...

Jalankan inferensi

YAMNet menyediakan skor kelas tingkat bingkai (yaitu, 521 skor untuk setiap bingkai). Untuk menentukan prediksi tingkat klip, skor dapat diagregasikan per kelas di seluruh bingkai (misalnya, menggunakan rata-rata atau agregasi maks). Hal ini dilakukan di bawah oleh scores_np.mean(axis=0) . Terakhir, untuk menemukan kelas dengan skor tertinggi di level klip, Anda mengambil maksimum 521 skor gabungan.

scores, embeddings, spectrogram = yamnet_model(testing_wav_data)
class_scores = tf.reduce_mean(scores, axis=0)
top_class = tf.argmax(class_scores)
inferred_class = class_names[top_class]

print(f'The main sound is: {inferred_class}')
print(f'The embeddings shape: {embeddings.shape}')
The main sound is: Animal
The embeddings shape: (13, 1024)

Kumpulan data ESC-50

The ESC-50 dataset ( Piczak 2015 ) adalah kumpulan berlabel dari 2.000 rekaman audio lima detik panjang lingkungan. Dataset terdiri dari 50 kelas, dengan 40 contoh per kelas.

Unduh kumpulan data dan ekstrak.

_ = tf.keras.utils.get_file('esc-50.zip',
                        'https://github.com/karoldvl/ESC-50/archive/master.zip',
                        cache_dir='./',
                        cache_subdir='datasets',
                        extract=True)
Downloading data from https://github.com/karoldvl/ESC-50/archive/master.zip
645701632/Unknown - 41s 0us/step

Jelajahi datanya

Metadata untuk setiap file ditentukan dalam file csv di ./datasets/ESC-50-master/meta/esc50.csv

dan semua file audio di ./datasets/ESC-50-master/audio/

Anda akan membuat panda DataFrame dengan pemetaan dan penggunaan yang memiliki pandangan yang lebih jelas dari data.

esc50_csv = './datasets/ESC-50-master/meta/esc50.csv'
base_data_path = './datasets/ESC-50-master/audio/'

pd_data = pd.read_csv(esc50_csv)
pd_data.head()

Saring datanya

Sekarang bahwa data yang disimpan dalam DataFrame , menerapkan beberapa perubahan:

  • Menyaring baris dan hanya menggunakan dipilih kelas - dog dan cat . Jika Anda ingin menggunakan kelas lain, di sinilah Anda dapat memilihnya.
  • Ubah nama file untuk memiliki path lengkap. Ini akan membuat loading lebih mudah nantinya.
  • Ubah target menjadi dalam rentang tertentu. Dalam contoh ini, dog akan tetap di 0 , tapi cat akan menjadi 1 bukan nilai aslinya dari 5 .
my_classes = ['dog', 'cat']
map_class_to_id = {'dog':0, 'cat':1}

filtered_pd = pd_data[pd_data.category.isin(my_classes)]

class_id = filtered_pd['category'].apply(lambda name: map_class_to_id[name])
filtered_pd = filtered_pd.assign(target=class_id)

full_path = filtered_pd['filename'].apply(lambda row: os.path.join(base_data_path, row))
filtered_pd = filtered_pd.assign(filename=full_path)

filtered_pd.head(10)

Muat file audio dan ambil embeddings

Di sini Anda akan menerapkan load_wav_16k_mono dan menyiapkan data WAV untuk model.

Ketika penggalian embeddings dari data WAV, Anda mendapatkan sebuah array dari bentuk (N, 1024) di mana N adalah jumlah frame yang YAMNet ditemukan (satu untuk setiap 0,48 detik dari audio).

Model Anda akan menggunakan setiap frame sebagai satu input. Oleh karena itu, Anda perlu membuat kolom baru yang memiliki satu frame per baris. Anda juga perlu memperluas label dan fold kolom yang tepat mencerminkan ini baris baru.

Diperluas fold kolom menjaga nilai-nilai asli. Anda tidak dapat mencampur bingkai karena, saat melakukan pemisahan, Anda mungkin memiliki bagian audio yang sama pada pemisahan yang berbeda, yang akan membuat langkah validasi dan pengujian Anda kurang efektif.

filenames = filtered_pd['filename']
targets = filtered_pd['target']
folds = filtered_pd['fold']

main_ds = tf.data.Dataset.from_tensor_slices((filenames, targets, folds))
main_ds.element_spec
(TensorSpec(shape=(), dtype=tf.string, name=None),
 TensorSpec(shape=(), dtype=tf.int64, name=None),
 TensorSpec(shape=(), dtype=tf.int64, name=None))
def load_wav_for_map(filename, label, fold):
  return load_wav_16k_mono(filename), label, fold

main_ds = main_ds.map(load_wav_for_map)
main_ds.element_spec
WARNING:tensorflow:Using a while_loop for converting IO>AudioResample
WARNING:tensorflow:Using a while_loop for converting IO>AudioResample
(TensorSpec(shape=<unknown>, dtype=tf.float32, name=None),
 TensorSpec(shape=(), dtype=tf.int64, name=None),
 TensorSpec(shape=(), dtype=tf.int64, name=None))
# applies the embedding extraction model to a wav data
def extract_embedding(wav_data, label, fold):
  ''' run YAMNet to extract embedding from the wav data '''
  scores, embeddings, spectrogram = yamnet_model(wav_data)
  num_embeddings = tf.shape(embeddings)[0]
  return (embeddings,
            tf.repeat(label, num_embeddings),
            tf.repeat(fold, num_embeddings))

# extract embedding
main_ds = main_ds.map(extract_embedding).unbatch()
main_ds.element_spec
(TensorSpec(shape=(1024,), dtype=tf.float32, name=None),
 TensorSpec(shape=(), dtype=tf.int64, name=None),
 TensorSpec(shape=(), dtype=tf.int64, name=None))

Pisahkan datanya

Anda akan menggunakan fold kolom untuk membagi dataset ke dalam kereta, validasi dan uji set.

ESC-50 diatur menjadi lima seragam berukuran cross-validasi fold s, sehingga klip dari sumber asli yang sama selalu dalam sama fold - cari tahu lebih dalam ESC: Dataset untuk Sound Lingkungan Klasifikasi kertas.

Langkah terakhir adalah menghapus fold kolom dari dataset karena Anda tidak akan menggunakannya selama pelatihan.

cached_ds = main_ds.cache()
train_ds = cached_ds.filter(lambda embedding, label, fold: fold < 4)
val_ds = cached_ds.filter(lambda embedding, label, fold: fold == 4)
test_ds = cached_ds.filter(lambda embedding, label, fold: fold == 5)

# remove the folds column now that it's not needed anymore
remove_fold_column = lambda embedding, label, fold: (embedding, label)

train_ds = train_ds.map(remove_fold_column)
val_ds = val_ds.map(remove_fold_column)
test_ds = test_ds.map(remove_fold_column)

train_ds = train_ds.cache().shuffle(1000).batch(32).prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.cache().batch(32).prefetch(tf.data.AUTOTUNE)
test_ds = test_ds.cache().batch(32).prefetch(tf.data.AUTOTUNE)

Buat model Anda

Anda melakukan sebagian besar pekerjaan! Berikutnya, menentukan sangat sederhana Sequential Model dengan satu lapisan tersembunyi dan dua output untuk mengenali kucing dan anjing dari suara.

my_model = tf.keras.Sequential([
    tf.keras.layers.Input(shape=(1024), dtype=tf.float32,
                          name='input_embedding'),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dense(len(my_classes))
], name='my_model')

my_model.summary()
WARNING:tensorflow:Please add `keras.layers.InputLayer` instead of `keras.Input` to Sequential model. `keras.Input` is intended to be used by Functional model.
WARNING:tensorflow:Please add `keras.layers.InputLayer` instead of `keras.Input` to Sequential model. `keras.Input` is intended to be used by Functional model.
Model: "my_model"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 512)               524800    
_________________________________________________________________
dense_1 (Dense)              (None, 2)                 1026      
=================================================================
Total params: 525,826
Trainable params: 525,826
Non-trainable params: 0
_________________________________________________________________
my_model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                 optimizer="adam",
                 metrics=['accuracy'])

callback = tf.keras.callbacks.EarlyStopping(monitor='loss',
                                            patience=3,
                                            restore_best_weights=True)
history = my_model.fit(train_ds,
                       epochs=20,
                       validation_data=val_ds,
                       callbacks=callback)
Epoch 1/20
15/15 [==============================] - 5s 25ms/step - loss: 0.7833 - accuracy: 0.8000 - val_loss: 0.6789 - val_accuracy: 0.8687
Epoch 2/20
15/15 [==============================] - 0s 16ms/step - loss: 0.5082 - accuracy: 0.8958 - val_loss: 0.3775 - val_accuracy: 0.8813
Epoch 3/20
15/15 [==============================] - 0s 17ms/step - loss: 0.3210 - accuracy: 0.8750 - val_loss: 0.5043 - val_accuracy: 0.8750
Epoch 4/20
15/15 [==============================] - 0s 17ms/step - loss: 0.2146 - accuracy: 0.9021 - val_loss: 0.3757 - val_accuracy: 0.8750
Epoch 5/20
15/15 [==============================] - 0s 17ms/step - loss: 0.2113 - accuracy: 0.9062 - val_loss: 0.2740 - val_accuracy: 0.8750
Epoch 6/20
15/15 [==============================] - 0s 17ms/step - loss: 0.2672 - accuracy: 0.9167 - val_loss: 0.4483 - val_accuracy: 0.8750
Epoch 7/20
15/15 [==============================] - 0s 17ms/step - loss: 0.2386 - accuracy: 0.9333 - val_loss: 0.5775 - val_accuracy: 0.8687
Epoch 8/20
15/15 [==============================] - 0s 17ms/step - loss: 0.1639 - accuracy: 0.9229 - val_loss: 0.4539 - val_accuracy: 0.8750
Epoch 9/20
15/15 [==============================] - 0s 18ms/step - loss: 0.3539 - accuracy: 0.9250 - val_loss: 0.2091 - val_accuracy: 0.9187
Epoch 10/20
15/15 [==============================] - 0s 18ms/step - loss: 0.2705 - accuracy: 0.9271 - val_loss: 0.2505 - val_accuracy: 0.9062
Epoch 11/20
15/15 [==============================] - 0s 17ms/step - loss: 0.2582 - accuracy: 0.9312 - val_loss: 0.2182 - val_accuracy: 0.9250

Mari kita jalankan evaluate metode pada data uji hanya untuk memastikan tidak ada overfitting.

loss, accuracy = my_model.evaluate(test_ds)

print("Loss: ", loss)
print("Accuracy: ", accuracy)
5/5 [==============================] - 0s 4ms/step - loss: 0.6575 - accuracy: 0.8125
Loss:  0.657511293888092
Accuracy:  0.8125

Anda melakukannya!

Uji model Anda

Selanjutnya, coba model Anda pada embedding dari pengujian sebelumnya menggunakan YAMNet saja.

scores, embeddings, spectrogram = yamnet_model(testing_wav_data)
result = my_model(embeddings).numpy()

inferred_class = my_classes[result.mean(axis=0).argmax()]
print(f'The main sound is: {inferred_class}')
The main sound is: cat

Simpan model yang dapat langsung mengambil file WAV sebagai input

Model Anda berfungsi saat Anda memberikannya embeddings sebagai input.

Dalam skenario dunia nyata, Anda ingin menggunakan data audio sebagai input langsung.

Untuk melakukan itu, Anda akan menggabungkan YAMNet dengan model Anda menjadi satu model yang dapat Anda ekspor untuk aplikasi lain.

Untuk membuatnya lebih mudah untuk menggunakan hasil model, lapisan akhir akan menjadi reduce_mean operasi. Saat menggunakan model ini untuk penyajian (yang akan Anda pelajari nanti di tutorial), Anda memerlukan nama lapisan terakhir. Jika Anda tidak mendefinisikannya, TensorFlow akan otomatis menentukan satu tambahan yang membuatnya sulit untuk diuji, karena akan terus berubah setiap kali Anda melatih model. Saat menggunakan operasi TensorFlow mentah, Anda tidak dapat menetapkan nama untuknya. Untuk mengatasi masalah ini, Anda akan membuat lapisan kustom yang berlaku reduce_mean dan menyebutnya 'classifier' .

class ReduceMeanLayer(tf.keras.layers.Layer):
  def __init__(self, axis=0, **kwargs):
    super(ReduceMeanLayer, self).__init__(**kwargs)
    self.axis = axis

  def call(self, input):
    return tf.math.reduce_mean(input, axis=self.axis)
saved_model_path = './dogs_and_cats_yamnet'

input_segment = tf.keras.layers.Input(shape=(), dtype=tf.float32, name='audio')
embedding_extraction_layer = hub.KerasLayer(yamnet_model_handle,
                                            trainable=False, name='yamnet')
_, embeddings_output, _ = embedding_extraction_layer(input_segment)
serving_outputs = my_model(embeddings_output)
serving_outputs = ReduceMeanLayer(axis=0, name='classifier')(serving_outputs)
serving_model = tf.keras.Model(input_segment, serving_outputs)
serving_model.save(saved_model_path, include_optimizer=False)
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
INFO:tensorflow:Assets written to: ./dogs_and_cats_yamnet/assets
INFO:tensorflow:Assets written to: ./dogs_and_cats_yamnet/assets
tf.keras.utils.plot_model(serving_model)

png

Muat model yang Anda simpan untuk memverifikasi bahwa itu berfungsi seperti yang diharapkan.

reloaded_model = tf.saved_model.load(saved_model_path)

Dan untuk tes terakhir: diberikan beberapa data suara, apakah model Anda mengembalikan hasil yang benar?

reloaded_results = reloaded_model(testing_wav_data)
cat_or_dog = my_classes[tf.argmax(reloaded_results)]
print(f'The main sound is: {cat_or_dog}')
The main sound is: cat

Jika Anda ingin mencoba model baru Anda pada penyiapan penayangan, Anda dapat menggunakan tanda tangan 'serving_default'.

serving_results = reloaded_model.signatures['serving_default'](testing_wav_data)
cat_or_dog = my_classes[tf.argmax(serving_results['classifier'])]
print(f'The main sound is: {cat_or_dog}')
The main sound is: cat

(Opsional) Beberapa pengujian lagi

Modelnya sudah siap.

Mari kita bandingkan dengan YAMNet pada dataset pengujian.

test_pd = filtered_pd.loc[filtered_pd['fold'] == 5]
row = test_pd.sample(1)
filename = row['filename'].item()
print(filename)
waveform = load_wav_16k_mono(filename)
print(f'Waveform values: {waveform}')
_ = plt.plot(waveform)

display.Audio(waveform, rate=16000)
./datasets/ESC-50-master/audio/5-212454-A-0.wav
WARNING:tensorflow:Using a while_loop for converting IO>AudioResample
WARNING:tensorflow:Using a while_loop for converting IO>AudioResample
Waveform values: [-8.8849301e-09  2.6603255e-08 -1.1731625e-08 ... -1.3478296e-03
 -1.0509168e-03 -9.1038318e-04]

png

# Run the model, check the output.
scores, embeddings, spectrogram = yamnet_model(waveform)
class_scores = tf.reduce_mean(scores, axis=0)
top_class = tf.argmax(class_scores)
inferred_class = class_names[top_class]
top_score = class_scores[top_class]
print(f'[YAMNet] The main sound is: {inferred_class} ({top_score})')

reloaded_results = reloaded_model(waveform)
your_top_class = tf.argmax(reloaded_results)
your_inferred_class = my_classes[your_top_class]
class_probabilities = tf.nn.softmax(reloaded_results, axis=-1)
your_top_score = class_probabilities[your_top_class]
print(f'[Your model] The main sound is: {your_inferred_class} ({your_top_score})')
[YAMNet] The main sound is: Animal (0.9570276141166687)
[Your model] The main sound is: dog (0.9999711513519287)

Langkah selanjutnya

Anda telah membuat model yang dapat mengklasifikasikan suara dari anjing atau kucing. Dengan ide yang sama dan dataset yang berbeda Anda dapat mencoba, misalnya, membangun identifier akustik burung berdasarkan nyanyian mereka.

Bagikan proyek Anda dengan tim TensorFlow di media sosial!