مساعدة في حماية الحاجز المرجاني العظيم مع TensorFlow على Kaggle تاريخ التحدي

نقل التعلم مع YAMNet لتصنيف الصوت البيئي

عرض على TensorFlow.org تشغيل في Google Colab عرض على جيثب تحميل دفتر انظر نموذج TF Hub

YAMNet هو عبارة عن شبكة العصبية العميقة المدربين قبل أن يمكن التنبؤ الأحداث الصوتية من 521 فئات ، مثل الضحك، ينبح، أو صفارة إنذار.

ستتعلم في هذا البرنامج التعليمي كيفية:

  • تحميل واستخدام نموذج YAMNet للاستدلال.
  • قم ببناء نموذج جديد باستخدام حفلات الزفاف YAMNet لتصنيف أصوات القطط والكلاب.
  • تقييم وتصدير النموذج الخاص بك.

استيراد TensorFlow ومكتبات أخرى

تبدأ من خلال تركيب TensorFlow I / O ، الأمر الذي سيجعل من الأسهل بالنسبة لك لتحميل الملفات الصوتية من القرص.

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

حول YAMNet

YAMNet هي شبكة المدربين قبل العصبية التي توظف MobileNetV1 -depthwise انفصال العمارة الإلتواء. ويمكن أن تستخدم الموجي الصوت كمدخل وتقديم تنبؤات مستقلة لكل من 521 الأحداث الصوت من AudioSet الإحضار.

داخليًا ، يستخرج النموذج "إطارات" من الإشارة الصوتية ويعالج دفعات من هذه الإطارات. يستخدم هذا الإصدار من النموذج إطارات يبلغ طولها 0.96 ثانية ويستخرج إطارًا واحدًا كل 0.48 ثانية.

يقبل نموذج 1-D float32 التنسور أو نمباي مجموعة تحتوي على الموجي من طول التعسفي، ممثلة في قناة واحدة (أحادية) عينات 16 كيلو هرتز في حدود [-1.0, +1.0] . يحتوي هذا البرنامج التعليمي على رمز لمساعدتك في تحويل ملفات WAV إلى التنسيق المدعوم.

نموذج بإرجاع 3 المخرجات، بما في ذلك عشرات الطبقة، التضمينات (التي ستستخدمها من أجل التعلم نقل)، وسجل ميل الطيفية . يمكنك العثور على مزيد من التفاصيل هنا .

أحد الاستخدامات المحددة لـ YAMNet هو كمستخرج ميزة عالية المستوى - إخراج التضمين 1،024 بعدًا. سوف تستخدم ميزات إدخال نموذج قاعدة (YAMNet) وإطعامهم في نموذج ضحالة بك تتكون من واحد مخبأة tf.keras.layers.Dense طبقة. بعد ذلك، سوف تدريب الشبكة على كمية صغيرة من البيانات لتصنيف الصوت دون الحاجة إلى الكثير من البيانات وصفت وتدريب نهاية إلى نهاية. (هذا هو مماثل ل نقل التعلم لتصنيف الصور مع TensorFlow محور لمزيد من المعلومات).

أولاً ، ستختبر النموذج وترى نتائج تصنيف الصوت. ستقوم بعد ذلك بإنشاء خط أنابيب المعالجة المسبقة للبيانات.

تحميل YAMNet من TensorFlow Hub

كنت تسير على استخدام YAMNet تدريب قبل من Tensorflow محور لاستخراج التضمينات من ملفات الصوت.

تحميل نموذج من TensorFlow محور واضح وصريح: اختيار النموذج، نسخ URL لها، واستخدام load وظيفة.

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

مع نموذج تحميل، يمكنك اتباع YAMNet استخدام الأساسية تعليمي وتحميل ملف WAV عينة لتشغيل الاستدلال.

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

ستحتاج إلى وظيفة لتحميل الملفات الصوتية ، والتي سيتم استخدامها أيضًا لاحقًا عند العمل مع بيانات التدريب. (مزيد من المعلومات حول قراءة ملفات الصوت والملصقات الخاصة بها في اعتراف الصوت بسيط .

# 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

بي إن جي

تحميل تعيين الفصل

من المهم تحميل أسماء الفئات التي تستطيع YAMNet التعرف عليها. ملف التعيين موجود في yamnet_model.class_map_path() في شكل 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
...

تشغيل الاستدلال

يوفر YAMNet درجات على مستوى الإطار (أي 521 درجة لكل إطار). من أجل تحديد تنبؤات مستوى القصاصة ، يمكن تجميع الدرجات لكل فئة عبر الإطارات (على سبيل المثال ، باستخدام متوسط ​​أو أقصى تجميع). ويتم ذلك دون كتبها scores_np.mean(axis=0) . أخيرًا ، للعثور على فئة أعلى الدرجات على مستوى القصاصة ، تحصل على الحد الأقصى من مجموع الدرجات البالغ 521.

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)

مجموعة بيانات ESC-50

و ESC-50 مجموعة البيانات ( Piczak، 2015 ) هي عبارة عن مجموعة وصفت من 2000 تسجيلات صوتية خمسة الثانية بيئية طويلة. تتكون مجموعة البيانات من 50 فئة ، مع 40 مثالًا لكل فصل.

قم بتنزيل مجموعة البيانات واستخرجها.

_ = 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

استكشف البيانات

يتم تحديد البيانات الوصفية لكل ملف في ملف CSV في ./datasets/ESC-50-master/meta/esc50.csv

وجميع الملفات الصوتية هي في ./datasets/ESC-50-master/audio/

سوف تقوم بإنشاء الباندا DataFrame مع تعيين واستخدام ذلك لدينا رؤية أكثر وضوحا من البيانات.

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()

تصفية البيانات

والآن بعد أن يتم تخزين البيانات في DataFrame ، وتطبيق بعض التحولات:

  • تصفية الصفوف واستخدام فقط في اختيار الطبقات - dog و cat . إذا كنت ترغب في استخدام أي فئات أخرى ، فهذا هو المكان الذي يمكنك اختيارهم فيه.
  • قم بتعديل اسم الملف ليحصل على المسار الكامل. هذا سيجعل التحميل أسهل لاحقًا.
  • تغيير الأهداف لتكون ضمن نطاق معين. في هذا المثال، dog سيبقى في 0 ، ولكن cat سوف تصبح 1 بدلا من القيمة الأصلية من 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)

قم بتحميل الملفات الصوتية واسترجاع حفلات الزفاف

هنا سوف تقوم بتطبيق load_wav_16k_mono وإعداد البيانات WAV للنموذج.

عند استخراج التضمينات من البيانات WAV، يمكنك الحصول على مجموعة واسعة من شكل (N, 1024) حيث N هو عدد الإطارات التي YAMNet وجدت (واحد لكل 0.48 ثانية من الصوت).

سيستخدم نموذجك كل إطار كمدخل واحد. لذلك ، تحتاج إلى إنشاء عمود جديد يحتوي على إطار واحد لكل صف. تحتاج أيضا إلى توسيع التسميات و fold عامود لمناسبة تعكس هذه الصفوف الجديدة.

وسعت fold العمود يحافظ على القيم الأصلية. لا يمكنك مزج الإطارات لأنه عند إجراء الانقسامات ، قد ينتهي بك الأمر إلى وجود أجزاء من نفس الصوت على تقسيمات مختلفة ، مما يجعل خطوات التحقق من الصحة والاختبار أقل فعالية.

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))

تقسيم البيانات

سيتم استخدام fold عمود لتقسيم مجموعة البيانات في القطار، والتحقق من صحة واختبار مجموعات.

يتم ترتيب ESC-50 إلى خمس موحد الحجم عبر التحقق من صحة fold الصورة، بحيث مقاطع من نفس المصدر الأصلي دائما في نفس fold - معرفة المزيد في ESC: مجموعة بيانات لتصنيف الصوت البيئة الورق.

الخطوة الأخيرة هي لإزالة fold عمود من مجموعة البيانات منذ كنت لا تنوي استخدام أثناء التدريب.

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)

قم بإنشاء نموذجك

لقد قمت بمعظم العمل! بعد ذلك، تعريف بسيط جدا متسلسل نموذج مع طبقة مخفية واحدة واثنين من النواتج الاعتراف القطط والكلاب من الأصوات.

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

دعونا تشغيل evaluate الأسلوب على بيانات الاختبار فقط للتأكد من ليس هناك 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

أنت فعلت ذلك!

اختبر نموذجك

بعد ذلك ، جرب النموذج الخاص بك على التضمين من الاختبار السابق باستخدام YAMNet فقط.

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

احفظ نموذجًا يمكنه أخذ ملف WAV كمدخلات مباشرة

يعمل نموذجك عندما تقدم له الزخارف كمدخلات.

في سيناريو العالم الحقيقي ، سترغب في استخدام البيانات الصوتية كمدخل مباشر.

للقيام بذلك ، ستقوم بدمج YAMNet مع النموذج الخاص بك في نموذج واحد يمكنك تصديره لتطبيقات أخرى.

لجعله أسهل للاستخدام نتيجة للنموذج، فإن الطبقة النهائية تكون reduce_mean العملية. عند استخدام هذا النموذج للخدمة (والذي ستتعرف عليه لاحقًا في البرنامج التعليمي) ، ستحتاج إلى اسم الطبقة النهائية. إذا لم تحدد واحدًا ، فسيقوم TensorFlow تلقائيًا بتحديد تعريف تدريجي يجعل من الصعب اختباره ، حيث سيستمر التغيير في كل مرة تقوم فيها بتدريب النموذج. عند استخدام عملية TensorFlow خام ، لا يمكنك تعيين اسم لها. ولمعالجة هذه المسألة، سوف تقوم بإنشاء طبقة المخصصة التي ينطبق reduce_mean والذي يطلق عليه '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)

بي إن جي

قم بتحميل النموذج المحفوظ للتحقق من أنه يعمل على النحو المتوقع.

reloaded_model = tf.saved_model.load(saved_model_path)

وللحصول على الاختبار النهائي: بالنظر إلى بعض البيانات الصوتية ، هل يُرجع نموذجك النتيجة الصحيحة؟

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

إذا كنت ترغب في تجربة نموذجك الجديد على إعداد تقديم ، يمكنك استخدام توقيع "serve_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

(اختياري) بعض الاختبارات الأخرى

النموذج جاهز.

دعنا نقارنها بـ YAMNet في مجموعة بيانات الاختبار.

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]

بي إن جي

# 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)

الخطوات التالية

لقد أنشأت نموذجًا يمكنه تصنيف الأصوات من الكلاب أو القطط. مع نفس الفكرة ومجموعة بيانات مختلفة يمكنك أن تجرب، على سبيل المثال، بناء معرف الصوتية الطيور بناء على الغناء.

شارك مشروعك مع فريق TensorFlow على وسائل التواصل الاجتماعي!