本练习的触发词将是“激活”。当我们每次听到说“激活”时,都会发出“叮咚”的声音。在本次作业结束时,我们也可以录制自己说话的片段,并让算法在检测到说“激活”时触发提示音。
在本次作业中我们将学习:
# Keras==2.2.5 tensorflow==1.15.0
!pip install pydub
import numpy as np
from pydub import AudioSegment
import random
import sys
import io
import os
import glob
import IPython
from td_utils import *
%matplotlib inline
在 raw_data 目录中,我们可以找到正面词、负面词和背景噪音的原始音频文件的子集。我们将使用这些音频文件合成数据集来训练模型。 “activate”目录包含人们说“activate”这个词的正面例子。 “否定”目录包含人们说“激活”以外的随机词的否定示例。每个录音有一个词。 “背景”目录包含 10 秒不同环境中的背景噪音剪辑,我们将使用这三种类型的记录(正/负/背景)来创建标记数据集,下面我们可以听一下录音示例:
IPython.display.Audio("./raw_data/activates/1.wav")
什么才是真正的录音?随着时间的推移,麦克风记录的气压变化很小,而正是这些气压的微小变化,我们的耳朵也会将其感知为声音。我们可以将录音视为一长串数字,用于测量麦克风检测到的微小气压变化。我们将使用以 44100 Hz(或 44100 赫兹)采样的音频。这意味着麦克风每秒为我们提供 44100 个数字。因此,一个 10 秒的音频剪辑由 441000 个数字表示(= $10 \times 44100 $)。
很难从音频的这种“原始”表示中确定是否说了“激活”这个词。为了帮助我们的序列模型更轻松地学习检测触发词,我们将计算音频的频谱图。频谱图告诉我们在某一时刻音频剪辑中存在多少不同的频率,下面我们生成一段音频的图谱:
x = graph_spectrogram("audio_examples/example_train.wav")
实现效果如下
上图表示每个频率(y 轴)在多个时间步长(x 轴)上的活跃程度。
由于语音数据难以获取和标记,因此我们将使用激活、底片和背景的音频剪辑来合成训练数据。录制大量带有随机“激活”的 10 秒音频剪辑非常慢。相反,更容易记录大量正面和负面的词,并分别记录背景噪音(或从免费在线资源下载背景噪音)。
要合成单个训练示例,我们将:
由于我们已将“激活”一词合成到背景剪辑中,因此我们确切地知道在 10 秒剪辑中“激活”出现的时间。稍后我们将看到,这也使生成标签 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 变得更加容易。
我们将使用pydub包来操作音频。 Pydub 将原始音频文件转换为 Pydub 数据结构列表(了解这里的细节并不重要)。 Pydub 使用 1ms 作为离散化间隔(1ms = 1/1000 秒),这就是为什么 10 秒剪辑总是使用 10,000 步表示的原因。
# Load audio segments using pydub
activates, negatives, backgrounds = load_raw_audio()
print("background len: " + str(len(backgrounds[0]))) # Should be 10,000, since it is a 10 sec clip
print("activate[0] len: " + str(len(activates[0]))) # Maybe around 1000, since an "activate" audio clip is usually around 1 sec (but varies a lot)
print("activate[1] len: " + str(len(activates[1]))) # Different "activate" clips can have different lengths
# background len: 10000
# activate[0] len: 721
# activate[1] len: 731
在背景上叠加正面/负面词:
给定一个 10 秒的背景剪辑和一个简短的音频剪辑(正面或负面的单词),我们需要将单词的简短音频剪辑“添加”或“插入”到背景上。为确保插入到背景中的音频片段不重叠,我们将跟踪先前插入的音频片段的时间。我们将在背景上插入多个正面/负面词的剪辑,并且不想在与之前添加的另一个剪辑重叠的地方插入“激活”或随机词。
为清楚起见,当我们将 1 秒的“激活”插入 10 秒的咖啡馆噪音片段时,最终会得到一个 10 秒的剪辑,听起来就像有人在咖啡馆里说“激活”,而“激活”叠加在背景咖啡馆噪音上,没有得到一个 11 秒的剪辑,后面我们将看到 pydub 如何执行此操作。
在叠加的同时创建标签:
还记得标签 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 表示某人是否刚刚说完“激活”。给定一个背景剪辑,我们可以为所有 t t t 初始化 y ⟨ t ⟩ = 0 y^{\langle t \rangle}=0 y⟨t⟩=0,因为该剪辑不包含任何“激活”。
当我们插入或覆盖“激活”剪辑时,还将更新 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 的标签,以便输出的 50 步现在具有目标标签 1。我们将训练 GRU 以检测何时有人完成说“激活”。例如,假设合成的“激活”剪辑在 10 秒音频中的 5 秒标记处结束——正好是剪辑的一半。回想一下 T y = 1375 T_y = 1375 Ty=1375,所以时间步长 $687 = $ int(1375*0.5)
对应于进入音频 5 秒的时刻。因此,我们将设置 y ⟨ 688 ⟩ = 1 y^{\langle 688 \rangle} = 1 y⟨688⟩=1。此外,如果 GRU 在此时刻之后的短时间内检测到“激活”,我们会非常满意,因此我们实际上将标签 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 的 **50 个连续值 **设置为 1。具体来说,我们有 y ⟨ 688 ⟩ = y ⟨ 689 ⟩ = ⋯ = y ⟨ 737 ⟩ = 1 y^{\langle 688 \rangle} = y^{\langle 689 \rangle} = \cdots = y^{\langle 737 \rangle} = 1 y⟨688⟩=y⟨689⟩=⋯=y⟨737⟩=1
这是合成训练数据的另一个原因:如上所述,生成这些标签 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 相对简单。相比之下,如果我们在麦克风上录制了 10 秒的音频,那么当“激活”完成时,一个人要听它并准确地手动标记会非常耗时。
下面是一个说明标签 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 的图,对于我们插入了“激活”、“无辜”、“激活”、“宝贝”的剪辑。请注意,正标签“1”是相关联的只有积极的话。
要实现训练集合成过程,我们将使用以下辅助函数。所有这些函数都将使用 1ms 的离散化间隔,因此 10 秒的音频总是被离散化为 10,000 步。
get_random_time_segment(segment_ms)
在我们的背景音频中获取一个随机的时间段is_overlapping(segment_time, existing_segments)
检查时间段是否与现有段重叠insert_audio_clip(background, audio_clip, existing_times)
使用 get_random_time_segment
和 is_overlapping
在我们的背景音频中随机插入一个音频片段insert_ones(y, segment_end_ms)
在单词“activate”之后将 1 插入到我们的标签向量 y 中函数** get_random_time_segment(segment_ms) **返回一个随机时间段,我们可以在其中插入持续时间为“segment_ms”的音频剪辑。
def get_random_time_segment(segment_ms):
"""
Gets a random time segment of duration segment_ms in a 10,000 ms audio clip.
Arguments:
segment_ms -- the duration of the audio clip in ms ("ms" stands for "milliseconds")
Returns:
segment_time -- a tuple of (segment_start, segment_end) in ms
"""
segment_start = np.random.randint(low=0, high=10000-segment_ms) # Make sure segment doesn't run past the 10sec background
segment_end = segment_start + segment_ms - 1
print("segment_time is [%d,%d]"%(segment_start,segment_end))
return (segment_start, segment_end)
接下来,假设我们已在 (1000,1800) 和 (3400,4500) 段插入音频剪辑。即,第一个片段从步骤 1000 开始,并在步骤 1800 结束。现在,如果我们考虑在 (3000,3600) 处插入一个新的音频剪辑,这是否与之前插入的片段之一重叠?在这种情况下,(3000,3600) 和 (3400,4500) 重叠,所以我们应该决定不在这里插入剪辑。
出于此函数的目的,将 (100,200) 和 (200,250) 定义为重叠,因为它们在时间步长 200 重叠。但是,(100,199) 和 (200,250) 不重叠。
练习:函数is_overlapping(segment_time, existing_segments)
来检查新的时间段是否与之前的任何段重叠。我们将需要执行 2 个步骤:
# GRADED FUNCTION: is_overlapping
def is_overlapping(segment_time, previous_segments):
"""
Checks if the time of a segment overlaps with the times of existing segments.
Arguments:
segment_time -- a tuple of (segment_start, segment_end) for the new segment
previous_segments -- a list of tuples of (segment_start, segment_end) for the existing segments
Returns:
True if the time segment overlaps with any of the existing segments, False otherwise
"""
segment_start, segment_end = segment_time
### START CODE HERE ### (≈ 4 line)
# Step 1: Initialize overlap as a "False" flag. (≈ 1 line)
overlap = False
# Step 2: loop over the previous_segments start and end times.
# Compare start/end times and set the flag to True if there is an overlap (≈ 3 lines)
for previous_start, previous_end in previous_segments:
if segment_start<=previous_end and segment_end>=previous_start:
overlap = True
### END CODE HERE ###
return overlap
进行测试,结果显示如下
overlap1 = is_overlapping((950, 1430), [(2000, 2550), (260, 949)])
overlap2 = is_overlapping((2305, 2950), [(824, 1532), (1900, 2305), (3424, 3656)])
print("Overlap 1 = ", overlap1) # False
print("Overlap 2 = ", overlap2) # True
现在,让我们使用之前的辅助函数在 10 秒的背景中随机插入一个新的音频片段,但要确保任何新插入的片段不与之前的片段重叠。
练习:实现 insert_audio_clip()
将音频剪辑叠加到背景 10 秒剪辑上。 我们将需要执行 4 个步骤:
# GRADED FUNCTION: insert_audio_clip
def insert_audio_clip(background, audio_clip, previous_segments):
"""
Insert a new audio segment over the background noise at a random time step, ensuring that the
audio segment does not overlap with existing segments.
Arguments:
background -- a 10 second background audio recording.
audio_clip -- the audio clip to be inserted/overlaid.
previous_segments -- times where audio segments have already been placed
Returns:
new_background -- the updated background audio
"""
# Get the duration of the audio clip in ms
segment_ms = len(audio_clip)
### START CODE HERE ###
# Step 1: Use one of the helper functions to pick a random time segment onto which to insert
# the new audio clip. (≈ 1 line)
segment_time = get_random_time_segment(segment_ms)
# Step 2: Check if the new segment_time overlaps with one of the previous_segments. If so, keep
# picking new segment_time at random until it doesn't overlap. (≈ 2 lines)
while is_overlapping(segment_time,previous_segments):
segment_time = get_random_time_segment(segment_ms)
# Step 3: Add the new segment_time to the list of previous_segments (≈ 1 line)
previous_segments.append(segment_time)
### END CODE HERE ###
# Step 4: Superpose audio segment and background
new_background = background.overlay(audio_clip, position = segment_time[0])
return new_background, segment_time
下面进行测试,结果显示如下
np.random.seed(5)
audio_clip, segment_time = insert_audio_clip(backgrounds[0], activates[0], [(3790, 4400)])
audio_clip.export("insert_test.wav", format="wav")
print("Segment Time: ", segment_time)
IPython.display.Audio("insert_test.wav")
# Segment Time: (2915, 3635)
最后,实现代码来更新标签 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩,假设我们刚刚插入了一个“激活”。在下面的代码中,y
是一个 (1,1375)
维向量,因为 T y = 1375 T_y = 1375 Ty=1375。
如果“激活”在时间步 t t t 结束,则设置 y ⟨ t + 1 ⟩ = 1 y^{\langle t+1 \rangle} = 1 y⟨t+1⟩=1 以及最多 49 个附加连续值。但是,请确保不会超出数组的末尾并尝试更新y[0][1375]
,因为有效索引是y[0][0]
到y[0][1374]
, 因为 T y = 1375 T_y = 1375 Ty=1375。因此,如果“激活”在步骤 1370 结束,我们将只得到 y[0][1371] = y[0][1372] = y[0][1373] = y[0][1374] = 1
练习:实现insert_ones()
,可以使用 for 循环。如果一个段以 segment_end_ms
结束(使用 10000 步离散化),将其转换为输出 y y y 的索引(使用 1375 1375 1375 步离散化),我们将使用以下公式:
segment_end_y = int(segment_end_ms * Ty / 10000.0)
# GRADED FUNCTION: insert_ones
def insert_ones(y, segment_end_ms):
"""
Update the label vector y. The labels of the 50 output steps strictly after the end of the segment
should be set to 1. By strictly we mean that the label of segment_end_y should be 0 while, the
50 followinf labels should be ones.
Arguments:
y -- numpy array of shape (1, Ty), the labels of the training example
segment_end_ms -- the end time of the segment in ms
Returns:
y -- updated labels
"""
# duration of the background (in terms of spectrogram time-steps)
segment_end_y = int(segment_end_ms * Ty / 10000.0)
# Add 1 to the correct index in the background label (y)
### START CODE HERE ### (≈ 3 lines)
for i in range(segment_end_y + 1, segment_end_y + 1 + 50):
if i < Ty:
y[0, i] = 1
### END CODE HERE ###
return y
下面我们进行测试,测试结果如下
arr1 = insert_ones(np.zeros((1, Ty)), 9700)
plt.plot(insert_ones(arr1, 4251)[0,:])
print("sanity checks:", arr1[0][1333], arr1[0][634], arr1[0][635])
最后,我们可以使用 insert_audio_clip
和 insert_ones
创建一个新的训练示例。
练习:实现create_training_example()
,我们需要执行以下步骤:
# GRADED FUNCTION: create_training_example
def create_training_example(background, activates, negatives):
"""
Creates a training example with a given background, activates, and negatives.
Arguments:
background -- a 10 second background audio recording
activates -- a list of audio segments of the word "activate"
negatives -- a list of audio segments of random words that are not "activate"
Returns:
x -- the spectrogram of the training example
y -- the label at each time step of the spectrogram
"""
# Set the random seed
np.random.seed(18)
# Make background quieter
background = background - 20
### START CODE HERE ###
# Step 1: Initialize y (label vector) of zeros (≈ 1 line)
y = np.zeros((1, Ty))
# Step 2: Initialize segment times as empty list (≈ 1 line)
previous_segments = []
### END CODE HERE ###
# Select 0-4 random "activate" audio clips from the entire list of "activates" recordings
number_of_activates = np.random.randint(0, 5)
random_indices = np.random.randint(len(activates), size=number_of_activates)
random_activates = [activates[i] for i in random_indices]
### START CODE HERE ### (≈ 3 lines)
# Step 3: Loop over randomly selected "activate" clips and insert in background
for random_activate in random_activates:
# Insert the audio clip on the background
background, segment_time = insert_audio_clip(background,random_activate,previous_segments)
# Retrieve segment_start and segment_end from segment_time
segment_start, segment_end = segment_time
# Insert labels in "y"
y = insert_ones(y,segment_end_ms=segment_end)
### END CODE HERE ###
# Select 0-2 random negatives audio recordings from the entire list of "negatives" recordings
number_of_negatives = np.random.randint(0, 3)
random_indices = np.random.randint(len(negatives), size=number_of_negatives)
random_negatives = [negatives[i] for i in random_indices]
### START CODE HERE ### (≈ 2 lines)
# Step 4: Loop over randomly selected negative clips and insert in background
for random_negative in random_negatives:
# Insert the audio clip on the background
background, _ = insert_audio_clip(background,random_negative,previous_segments)
### END CODE HERE ###
# Standardize the volume of the audio clip
background = match_target_amplitude(background, -20.0)
# Export new training example
file_handle = background.export("train" + ".wav", format="wav")
print("File (train.wav) was saved in your directory.")
# Get and plot spectrogram of the new recording (background with superposition of positive and negatives)
x = graph_spectrogram("train.wav")
return x, y
x, y = create_training_example(backgrounds[0], activates, negatives)
实现效果如下
现在我们可以聆听创建的训练示例并将其与上面生成的频谱图进行比较。
IPython.display.Audio("train.wav")
IPython.display.Audio("audio_examples/train_reference.wav")
最后,我们可以为生成的训练示例绘制相关标签如下
plt.plot(y[0])
我们现在已经实现了生成单个训练示例所需的代码,接下来我们使用这个过程来生成一个大的训练集。为了节省时间,我们已经生成了一组训练示例,直接调用
# Load preprocessed training examples
X = np.load("./XY_train/X.npy")
Y = np.load("./XY_train/Y.npy")
为了测试我们的模型,我们记录了一个包含 25 个示例的开发集。 虽然我们的训练数据是合成的,但我们希望使用与实际输入相同的分布来创建一个开发集。 因此,我们录制了 25 个 10 秒的音频片段,这些片段是人们说“激活”和其他随机词,并手工标记它们。 这遵循了之前描述的原则,即我们应该创建与测试集分布尽可能相似的开发集,这就是我们的开发集使用真实音频而不是合成音频的原因,接下来我们加载预处理的开发集示例
# Load preprocessed dev set examples
X_dev = np.load("./XY_dev/X_dev.npy")
Y_dev = np.load("./XY_dev/Y_dev.npy")
现在我们已经构建了一个数据集,接下来让我们编写和训练触发词检测模型!
该模型将使用一维卷积层、GRU 层和密集层, 让我们加载允许在 Keras 中使用这些层的包
from keras.callbacks import ModelCheckpoint
from keras.models import Model, load_model, Sequential
from keras.layers import Dense, Activation, Dropout, Input, Masking, TimeDistributed, LSTM, Conv1D
from keras.layers import GRU, Bidirectional, BatchNormalization, Reshape
from keras.optimizers import Adam
这是我们将使用的架构
该模型的一个关键步骤是一维卷积步骤(靠近图 3 的底部)。它输入5511步谱图,输出1375步输出,再经过多层进一步处理,得到最终的 T y = 1375 T_y=1375 Ty=1375步输出。该层的作用类似于 2D 卷积,即提取低级特征,然后可能生成更小维度的输出。
在计算上,一维卷积层也有助于加速模型,因为现在 GRU 只需要处理 1375 个时间步而不是 5511 个时间步。两个 GRU 层从左到右读取输入序列,然后最终使用密集 + sigmoid 层对 y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 进行预测。因为 y y y 是二进制值(0 或 1),我们在最后一层使用 sigmoid 输出来估计输出为 1 的机会,对应于刚刚说“激活”的用户。
请注意,我们使用单向 RNN 而不是双向 RNN。这对于触发词检测非常重要,因为我们希望能够在触发词被说出后几乎立即检测到。如果我们使用双向 RNN,我们将不得不等待整个 10 秒的音频被记录下来,然后才能判断音频剪辑的第一秒中是否说了“激活”。
模型的实现可以分为四个步骤:
步骤 1:CONV 层,使用 Conv1D()
来实现,有 196 个过滤器,过滤器大小为 15(kernel_size=15
),步长为 4。[参见文档。]
步骤2:第一个 GRU 层。要生成 GRU 层,可以使用:
X = GRU(单位 = 128,return_sequences = True)(X)
设置 return_sequences=True
确保所有 GRU 的隐藏状态都被馈送到下一层。请记住在 Dropout 和 BatchNorm 层中遵循这一点。
步骤 3:第二个 GRU 层。这类似于之前的 GRU 层(记住使用 return_sequences=True
),但是有一个额外的 dropout 层。
步骤4:创建一个时间分布的密集层如下:
X = TimeDistributed(Dense(1, activation = "sigmoid"))(X)
这会创建一个密集层,然后是一个 sigmoid,这样密集层使用的参数对于每个时间步都是相同的。 [参见文档。]
练习:实现model()
,架构如上图所示,实现代码如下:
# GRADED FUNCTION: model
def model(input_shape):
"""
Function creating the model's graph in Keras.
Argument:
input_shape -- shape of the model's input data (using Keras conventions)
Returns:
model -- Keras model instance
"""
X_input = Input(shape = input_shape)
### START CODE HERE ###
# Step 1: CONV layer (≈4 lines)
X = Conv1D(filters=196, kernel_size=15, strides=4)(X_input) # CONV1D
X = BatchNormalization()(X) # Batch normalization
X = Activation('relu')(X) # ReLu activation
X = Dropout(0.8)(X) # dropout (use 0.8)
# Step 2: First GRU Layer (≈4 lines)
X = GRU(units=128, return_sequences=True)(X) # GRU (use 128 units and return the sequences)
X = Dropout(0.8)(X) # dropout (use 0.8)
X = BatchNormalization()(X) # Batch normalization
# Step 3: Second GRU Layer (≈4 lines)
X = GRU(units=128, return_sequences=True)(X) # GRU (use 128 units and return the sequences)
X = Dropout(0.8)(X) # dropout (use 0.8)
X = BatchNormalization()(X) # Batch normalization
X = Dropout(0.8)(X) # dropout (use 0.8)
# Step 4: Time-distributed dense layer (≈1 line)
X = TimeDistributed(Dense(1, activation = "sigmoid"))(X) # time distributed (sigmoid)
### END CODE HERE ###
model = Model(inputs = X_input, outputs = X)
return model
生成模型
model = model(input_shape = (Tx, n_freq))
让我们打印模型摘要以跟踪形状
model.summary()
网络的输出形状为 (None, 1375, 1),而输入形状为 (None, 5511, 101)。 Conv1D 将频谱图的步数从 5511 减少到 1375。
触发词检测需要很长时间来训练。 为了节省时间,我们已经使用上面构建的架构在 GPU 上训练了大约 3 小时的模型,以及大约 4000 个示例的大型训练集,让我们加载模型
model = load_model('./models/tr_model.h5')
我们可以使用 Adam 优化器和二元交叉熵损失进一步训练模型,如下所示。 这将运行得很快,因为我们仅针对一个时期进行训练,并使用 26 个示例的小型训练集。
opt = Adam(lr=0.0001, beta_1=0.9, beta_2=0.999, decay=0.01)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=["accuracy"])
model.fit(X, Y, batch_size = 5, epochs=1)
可以看到训练过程如下
最后,让我们看看您的模型在开发集上的表现如何
loss, acc = model.evaluate(X_dev, Y_dev)
print("Dev set accuracy = ", acc)
# Dev set accuracy = 0.9451636075973511
这看起来很不错! 然而,准确度并不是这项任务的重要指标,因为标签严重偏向于 0,因此仅输出 0 的神经网络将获得略高于 90% 的准确度。 我们可以定义更有用的指标,例如 F1 分数或 Precision/Recall。 但我们不要在这里操心,而只是凭经验看看模型是如何做的。
现在我们已经为触发词检测构建了一个工作模型,让我们用它来进行预测。 此代码片段通过网络运行音频(保存在 wav 文件中)
def detect_triggerword(filename):
plt.subplot(2, 1, 1)
x = graph_spectrogram(filename)
# the spectogram outputs (freqs, Tx) and we want (Tx, freqs) to input into the model
x = x.swapaxes(0,1)
x = np.expand_dims(x, axis=0)
predictions = model.predict(x)
plt.subplot(2, 1, 2)
plt.plot(predictions[0,:,0])
plt.ylabel('probability')
plt.show()
return predictions
一旦我们估计了在每个输出步骤中检测到“激活”一词的概率,您就可以在概率高于某个阈值时触发“钟声”声音播放。 此外,在说“激活”之后,对于连续许多值, y ⟨ t ⟩ y^{\langle t \rangle} y⟨t⟩ 可能接近 1,但我们只想鸣响一次。 所以我们最多每 75 个输出步插入一次提示音。 这将有助于防止我们为单个“激活”实例插入两个钟声(这起到类似于计算机视觉中的非最大抑制的作用)
chime_file = "audio_examples/chime.wav"
def chime_on_activate(filename, predictions, threshold):
audio_clip = AudioSegment.from_wav(filename)
chime = AudioSegment.from_wav(chime_file)
Ty = predictions.shape[1]
# Step 1: Initialize the number of consecutive output steps to 0
consecutive_timesteps = 0
# Step 2: Loop over the output steps in the y
for i in range(Ty):
# Step 3: Increment consecutive output steps
consecutive_timesteps += 1
# Step 4: If prediction is higher than the threshold and more than 75 consecutive output steps have passed
if predictions[0,i,0] > threshold and consecutive_timesteps > 75:
# Step 5: Superpose audio and background using pydub
audio_clip = audio_clip.overlay(chime, position = ((i / Ty) * audio_clip.duration_seconds)*1000)
# Step 6: Reset consecutive output steps to 0
consecutive_timesteps = 0
audio_clip.export("chime_output.wav", format='wav')
让我们探索一下我们的模型如何处理来自开发集的两个看不见的音频片段。 让我们先听听两个开发集剪辑。
IPython.display.Audio("./raw_data/dev/1.wav")
IPython.display.Audio("./raw_data/dev/2.wav")
现在让我们在这些音频剪辑上运行模型,看看它是否在“激活”后添加了提示音!
filename = "./raw_data/dev/1.wav"
prediction = detect_triggerword(filename)
chime_on_activate(filename, prediction, 0.5)
IPython.display.Audio("./chime_output.wav")
filename = "./raw_data/dev/2.wav"
prediction = detect_triggerword(filename)
chime_on_activate(filename, prediction, 0.5)
IPython.display.Audio("./chime_output.wav")
录制一段 10 秒的音频片段,让您说出“激活”这个词和其他随机词,然后将其作为“myaudio.wav”上传到 Coursera 中心。 请务必将音频上传为 wav 文件。 如果您的音频以不同的格式(例如 mp3)录制,您可以在网上找到免费软件将其转换为 wav。 如果您的录音不是 10 秒,下面的代码将根据需要修剪或填充它以使其为 10 秒。
# Preprocess the audio to the correct format
def preprocess_audio(filename):
# Trim or pad audio segment to 10000ms
padding = AudioSegment.silent(duration=10000)
segment = AudioSegment.from_wav(filename)[:10000]
segment = padding.overlay(segment)
# Set frame rate to 44100
segment = segment.set_frame_rate(44100)
# Export as wav
segment.export(filename, format='wav')
将音频文件上传到 Coursera 后,将文件路径放在下面的变量中。
your_filename = "audio_examples/my_audio.wav"
preprocess_audio(your_filename)
IPython.display.Audio(your_filename) # listen to the audio you uploaded
最后,使用模型预测在 10 秒音频剪辑中说“激活”的时间,并触发提示音。 如果未正确添加哔声,请尝试调整 chime_threshold。
chime_threshold = 0.5
prediction = detect_triggerword(your_filename)
chime_on_activate(your_filename, prediction, chime_threshold)
IPython.display.Audio("./chime_output.wav")