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

التعرف البسيط على الصوت: التعرف على الكلمات الأساسية

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

يوضح هذا البرنامج التعليمي كيفية المعالجة المسبقة لملفات الصوت في شكل WAV وبناء وتدريب الأساسي التعرف على الكلام التلقائي نموذج (ASR) للاعتراف كلمات مختلفة عشرة. سوف تستخدم جزء من مجموعة البيانات الكلام أوامر ( اردن، 2018 )، الذي يحتوي على قصيرة (ثانية واحدة أو أقل) مقاطع صوتية من الأوامر، مثل "القاعدة"، "الذهاب"، "اليسار" أو "لا"، " صحيح "،" توقف "،" أعلى "و" نعم ".

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

اقامة

استيراد الوحدات والتبعيات الضرورية. علما بأن عليك أن تكون باستخدام سيبورن عن التصور في هذا البرنامج التعليمي.

import os
import pathlib

import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import tensorflow as tf

from tensorflow.keras import layers
from tensorflow.keras import models
from IPython import display

# Set the seed value for experiment reproducibility.
seed = 42
tf.random.set_seed(seed)
np.random.seed(seed)

قم باستيراد مجموعة بيانات أوامر الكلام المصغرة

لتوفير الوقت مع تحميل البيانات ، ستعمل باستخدام إصدار أصغر من مجموعة بيانات أوامر الكلام. و رقة العمل الأصلي يتكون من أكثر من 105،000 الملفات الصوتية في شكل ملف صوتي WAV (الموجي) من الناس يقولون 35 كلمات مختلفة. تم جمع هذه البيانات بواسطة Google وتم إصدارها بموجب ترخيص CC BY.

تحميل واستخراج mini_speech_commands.zip الملف الذي يحتوي على أصغر قواعد البيانات الكلام أوامر مع tf.keras.utils.get_file :

DATASET_PATH = 'data/mini_speech_commands'

data_dir = pathlib.Path(DATASET_PATH)
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 [==============================] - 6s 0us/step
182091776/182082353 [==============================] - 6s 0us/step

يتم تخزين مقاطع صوتية ورقة العمل في ثمانية مجلدات المقابلة لكل أمر الكلام: no ، yes ، down ، go ، left ، up ، right ، و stop :

commands = np.array(tf.io.gfile.listdir(str(data_dir)))
commands = commands[commands != 'README.md']
print('Commands:', commands)
Commands: ['yes' 'go' 'left' 'right' 'up' 'down' 'stop' 'no']

استخراج مقاطع صوتية في قائمة تسمى filenames ، وخلط ما يلي:

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/up/05739450_nohash_2.wav', shape=(), dtype=string)

تقسيم filenames في التدريب والتحقق منها واختبار مجموعات باستخدام نسبة 80:10:10، على التوالي:

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

اقرأ الملفات الصوتية وتسمياتها

في هذا القسم ، ستقوم بمعالجة مجموعة البيانات مسبقًا ، مما يؤدي إلى إنشاء موترات مفكوكة لأشكال الموجة والتسميات المقابلة. لاحظ أن:

  • يحتوي كل ملف WAV على بيانات سلاسل زمنية مع عدد محدد من العينات في الثانية.
  • تمثل كل عينة من السعة للإشارة السمعية في ذلك الوقت تحديدا.
  • في 16 بت نظام، مثل ملفات WAV في مجموعة البيانات مصغرة الكلام أوامر، وتتراوح قيم السعة من -32،768 إلى 32،767.
  • و معدل العينة حول هذه البينات هو 16KHZ.

شكل الموتر إرجاعها بواسطة tf.audio.decode_wav هو [samples, channels] ، حيث channels هي 1 أحادية أو 2 لستيريو. تحتوي مجموعة بيانات أوامر الكلام المصغرة على تسجيلات أحادية فقط.

test_file = tf.io.read_file(DATASET_PATH+'/down/0a9f9af7_nohash_0.wav')
test_audio, _ = tf.audio.decode_wav(contents=test_file)
test_audio.shape
TensorShape([13654, 1])

الآن ، دعنا نحدد وظيفة تعالج ملفات الصوت WAV الأولية لمجموعة البيانات في موترات صوتية:

def decode_audio(audio_binary):
  # Decode WAV-encoded audio files to `float32` tensors, normalized
  # to the [-1.0, 1.0] range. Return `float32` audio and a sample rate.
  audio, _ = tf.audio.decode_wav(contents=audio_binary)
  # Since all the data is single channel (mono), drop the `channels`
  # axis from the array.
  return tf.squeeze(audio, axis=-1)

حدد وظيفة تنشئ تسميات باستخدام الدلائل الأصلية لكل ملف:

  • تقسيم مسارات الملفات في tf.RaggedTensor الصورة (التنسورات مع الأبعاد مع خشنة شرائح التي قد يكون لها أطوال مختلفة).
def get_label(file_path):
  parts = tf.strings.split(
      input=file_path,
      sep=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]

تعريف آخر المساعد فونكأيشن- get_waveform_and_label يضع المفتى كل ذلك معا:

  • الإدخال هو اسم ملف صوت WAV.
  • الإخراج عبارة عن مجموعة تحتوي على موترات الصوت والتسمية الجاهزة للتعلم تحت الإشراف.
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

قم ببناء مجموعة التدريب لاستخراج أزواج التسمية الصوتية:

ستنشئ مجموعات التحقق والاختبار باستخدام إجراء مماثل لاحقًا.

AUTOTUNE = tf.data.AUTOTUNE

files_ds = tf.data.Dataset.from_tensor_slices(train_files)

waveform_ds = files_ds.map(
    map_func=get_waveform_and_label,
    num_parallel_calls=AUTOTUNE)

دعنا نرسم بعض أشكال الموجات الصوتية:

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

بي إن جي

تحويل أشكال الموجة إلى مخططات طيفية

يتم تمثيل أشكال الموجة في مجموعة البيانات في المجال الزمني. بعد ذلك، عليك تحويل الطول الموجي من إشارات الوقت المجال في إشارات وقت التردد المجال عن طريق حساب لوقت قصير تحويل فورييه (STFT) لتحويل الطول الموجي باسم الطيفية ، والتي تظهر التغييرات تردد مع مرور الوقت ويمكن أن تكون ممثلة كصور ثنائية الأبعاد. ستقوم بتغذية صور المخطط الطيفي في شبكتك العصبية لتدريب النموذج.

تحويل فورييه ( tf.signal.fft ) يحول إشارة إلى الترددات المكونة لها، ولكن يفقد كل المعلومات في الوقت. وعلى سبيل المقارنة، STFT ( tf.signal.stft ) يقسم إشارة إلى ويندوز من الوقت ويدير تحويل فورييه على كل نافذة، والحفاظ على بعض المعلومات في الوقت، وإرجاع موتر 2D التي يمكن تشغيلها تلافيف القياسية على.

قم بإنشاء وظيفة مفيدة لتحويل أشكال الموجة إلى مخططات طيفية:

  • يجب أن تكون أشكال الموجة من نفس الطول ، بحيث عندما تقوم بتحويلها إلى مخططات طيفية ، يكون للنتائج أبعاد متشابهة. ويمكن القيام بذلك ببساطة عن طريق صفر حشوة مقاطع الصوتية التي هي الثانية أقصر من واحد (باستخدام tf.zeros ).
  • عندما يدعو tf.signal.stft ، اختر frame_length و frame_step المعلمات بحيث الطيفية "صورة" ولدت هي مربع تقريبا. لمزيد من المعلومات حول اختيار المعلمات STFT، الرجوع إلى هذا الفيديو كورسيرا على معالجة الإشارة الصوتية وSTFT.
  • ينتج STFT مجموعة من الأعداد المركبة التي تمثل الحجم والمرحلة. ومع ذلك، في هذا البرنامج التعليمي سوف تستخدم فقط حجم، والتي يمكن أن تجنيها من خلال تطبيق tf.abs على إخراج tf.signal.stft .
def get_spectrogram(waveform):
  # Zero-padding for an audio waveform with less than 16,000 samples.
  input_len = 16000
  waveform = waveform[:input_len]
  zero_padding = tf.zeros(
      [16000] - tf.shape(waveform),
      dtype=tf.float32)
  # Cast the waveform tensors' dtype to float32.
  waveform = tf.cast(waveform, dtype=tf.float32)
  # Concatenate the waveform with `zero_padding`, which ensures all audio
  # clips are of the same length.
  equal_length = tf.concat([waveform, zero_padding], 0)
  # Convert the waveform to a spectrogram via a STFT.
  spectrogram = tf.signal.stft(
      equal_length, frame_length=255, frame_step=128)
  # Obtain the magnitude of the STFT.
  spectrogram = tf.abs(spectrogram)
  # Add a `channels` dimension, so that the spectrogram can be used
  # as image-like input data with convolution layers (which expect
  # shape (`batch_size`, `height`, `width`, `channels`).
  spectrogram = spectrogram[..., tf.newaxis]
  return spectrogram

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

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: up
Waveform shape: (11606,)
Spectrogram shape: (124, 129, 1)
Audio playback

الآن ، حدد وظيفة لعرض مخطط طيفي:

def plot_spectrogram(spectrogram, ax):
  if len(spectrogram.shape) > 2:
    assert len(spectrogram.shape) == 3
    spectrogram = np.squeeze(spectrogram, axis=-1)
  # Convert the frequencies to log scale and transpose, so that the time is
  # represented on the x-axis (columns).
  # Add an epsilon to avoid taking a log of zero.
  log_spec = np.log(spectrogram.T + np.finfo(float).eps)
  height = log_spec.shape[0]
  width = log_spec.shape[1]
  X = np.linspace(0, np.size(spectrogram), num=width, dtype=int)
  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()

بي إن جي

الآن ، حدد دالة تقوم بتحويل مجموعة بيانات شكل الموجة إلى مخططات طيفية والتسميات المقابلة لها كمعرفات عدد صحيح:

def get_spectrogram_and_label_id(audio, label):
  spectrogram = get_spectrogram(audio)
  label_id = tf.argmax(label == commands)
  return spectrogram, label_id

خريطة get_spectrogram_and_label_id عبر عناصر مجموعة البيانات مع Dataset.map :

spectrogram_ds = waveform_ds.map(
  map_func=get_spectrogram_and_label_id,
  num_parallel_calls=AUTOTUNE)

افحص مخططات الطيف للحصول على أمثلة مختلفة لمجموعة البيانات:

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(spectrogram.numpy(), ax)
  ax.set_title(commands[label_id.numpy()])
  ax.axis('off')

plt.show()

بي إن جي

بناء وتدريب النموذج

كرر مجموعة التدريب قبل المعالجة على مجموعات التحقق والاختبار:

def preprocess_dataset(files):
  files_ds = tf.data.Dataset.from_tensor_slices(files)
  output_ds = files_ds.map(
      map_func=get_waveform_and_label,
      num_parallel_calls=AUTOTUNE)
  output_ds = output_ds.map(
      map_func=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_size = 64
train_ds = train_ds.batch(batch_size)
val_ds = val_ds.batch(batch_size)

إضافة Dataset.cache و Dataset.prefetch عمليات للحد من الكمون للقراءة في حين أن التدريب على نموذج:

train_ds = train_ds.cache().prefetch(AUTOTUNE)
val_ds = val_ds.cache().prefetch(AUTOTUNE)

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

بك tf.keras.Sequential سوف نموذج استخدام طبقات تجهيزها Keras التالية:

ل Normalization طبقة، في adapt سيكون بحاجة الى طريقة أول من دعا بيانات التدريب من أجل حساب إحصائيات مجمعة (أي، المتوسط والانحراف المعياري).

for spectrogram, _ in spectrogram_ds.take(1):
  input_shape = spectrogram.shape
print('Input shape:', input_shape)
num_labels = len(commands)

# Instantiate the `tf.keras.layers.Normalization` layer.
norm_layer = layers.Normalization()
# Fit the state of the layer to the spectrograms
# with `Normalization.adapt`.
norm_layer.adapt(data=spectrogram_ds.map(map_func=lambda spec, label: spec))

model = models.Sequential([
    layers.Input(shape=input_shape),
    # Downsample the input.
    layers.Resizing(32, 32),
    # Normalize.
    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 (Normalizatio  (None, 32, 32, 1)        3         
 n)                                                              
                                                                 
 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
_________________________________________________________________

قم بتكوين نموذج Keras باستخدام مُحسِّن آدم وخسارة الانتروبيا المتقاطعة:

model.compile(
    optimizer=tf.keras.optimizers.Adam(),
    loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    metrics=['accuracy'],
)

قم بتدريب النموذج على مدى 10 فترات لأغراض العرض التوضيحي:

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 [==============================] - 13s 40ms/step - loss: 1.7404 - accuracy: 0.3823 - val_loss: 1.3372 - val_accuracy: 0.5550
Epoch 2/10
100/100 [==============================] - 0s 4ms/step - loss: 1.1776 - accuracy: 0.5903 - val_loss: 0.9747 - val_accuracy: 0.6600
Epoch 3/10
100/100 [==============================] - 0s 4ms/step - loss: 0.9027 - accuracy: 0.6778 - val_loss: 0.8096 - val_accuracy: 0.7150
Epoch 4/10
100/100 [==============================] - 0s 4ms/step - loss: 0.7647 - accuracy: 0.7316 - val_loss: 0.7325 - val_accuracy: 0.7625
Epoch 5/10
100/100 [==============================] - 0s 4ms/step - loss: 0.6492 - accuracy: 0.7658 - val_loss: 0.6697 - val_accuracy: 0.7688
Epoch 6/10
100/100 [==============================] - 0s 5ms/step - loss: 0.5796 - accuracy: 0.7937 - val_loss: 0.6108 - val_accuracy: 0.7875
Epoch 7/10
100/100 [==============================] - 0s 4ms/step - loss: 0.5261 - accuracy: 0.8117 - val_loss: 0.5708 - val_accuracy: 0.8087
Epoch 8/10
100/100 [==============================] - 0s 4ms/step - loss: 0.4747 - accuracy: 0.8267 - val_loss: 0.5429 - val_accuracy: 0.8163
Epoch 9/10
100/100 [==============================] - 0s 4ms/step - loss: 0.4427 - accuracy: 0.8428 - val_loss: 0.5379 - val_accuracy: 0.8175
Epoch 10/10
100/100 [==============================] - 0s 4ms/step - loss: 0.3927 - accuracy: 0.8637 - val_loss: 0.5320 - val_accuracy: 0.8213

دعنا نرسم منحنيات فقدان التدريب والتحقق من الصحة للتحقق من تحسن نموذجك أثناء التدريب:

metrics = history.history
plt.plot(history.epoch, metrics['loss'], metrics['val_loss'])
plt.legend(['loss', 'val_loss'])
plt.show()

بي إن جي

تقييم أداء النموذج

قم بتشغيل النموذج على مجموعة الاختبار وتحقق من أداء النموذج:

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%

اعرض مصفوفة الارتباك

استخدام مصفوفة الارتباك للتحقق من مدى جيدا نموذج لم يصنف كل من الأوامر في مجموعة الاختبار:

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

بي إن جي

تشغيل الاستدلال على ملف صوتي

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

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

بي إن جي

كما يوحي الإخراج ، يجب أن يتعرف نموذجك على الأمر الصوتي على أنه "لا".

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

يوضح هذا البرنامج التعليمي كيفية إجراء تصنيف صوتي بسيط / التعرف التلقائي على الكلام باستخدام شبكة عصبية تلافيفية مع TensorFlow و Python. لمعرفة المزيد ، ضع في اعتبارك الموارد التالية: