2019-05-28 22:30:40 david_tym 阅读数 1022
  • 深度学习入门及如何转型AI领域

    深度学习入门转型视频教程,该课程主要告诉开发者如何快速入门深度学习。讲师是在机器学习,深度学习神经网络领域多年开发研究经验,精通算法原理与编程实践。曾完成过多项图像识别,目标识别,语音识别的实际项目,经验丰富。关注深度学习领域各种开源项目,如TensorFlow,Caffe,Torch等。喜欢理论与实践相结合的教学风格,课程编排由浅入深,体系清晰完整。

    2781 人正在学习 去看看 CSDN讲师

上篇博客(语音识别传统方法(GMM+HMM+NGRAM)概述)说到我们team要做语音识别相关的项目,而我们公司的芯片是用在终端上的,即我们要做终端上的语音识别。由于目前终端(如手机)上的CPU还不足够强劲,不能让语音识别的各种算法跑在终端上,尤其现在语音识别都是基于深度学习来做了,更加不能跑在终端上,所以目前主流的语音识别方案是声音采集和前处理在终端上做,语音识别算法则放在服务器(即云端)上跑。虽然这种方案有泄漏隐私(把终端上的语音数据发给服务器)和没有网络不能使用等缺点,但也是不得已而为之的,相信在不久的将来等终端上的CPU足够强劲了会把语音识别的所有实现都放在终端上的。

 

是不是意味着终端上做不了语音识别相关的算法了?其实也不是,语音唤醒功能是需要在终端上实现的。语音唤醒是指设定一个唤醒词,如Siri的“Hi Siri”,只有用户说了唤醒词后终端上的语音识别功能才会处于工作状态,否则处于休眠状态。这样做主要是为了降功耗,增加续航时间。目前很多终端都是靠电池供电的,对功耗很敏感,是不允许让语音识别功能一直处于工作状态的。为此我就对语音唤醒技术做了一番调研。依旧是看各种文档和博客,然后进行梳理和总结,形成PPT,给组内同学介绍。在此我也把PPT贴出来,给有需要或感兴趣的朋友看看,有什么不正确的也请指正。我的PPT中的一些图是用的文档或他人博客里的,谢谢这些原作者。以下就是我的关于语音唤醒技术的PPT。

 

 

2018-07-27 09:49:50 haoyutiangang 阅读数 1000
  • 深度学习入门及如何转型AI领域

    深度学习入门转型视频教程,该课程主要告诉开发者如何快速入门深度学习。讲师是在机器学习,深度学习神经网络领域多年开发研究经验,精通算法原理与编程实践。曾完成过多项图像识别,目标识别,语音识别的实际项目,经验丰富。关注深度学习领域各种开源项目,如TensorFlow,Caffe,Torch等。喜欢理论与实践相结合的教学风格,课程编排由浅入深,体系清晰完整。

    2781 人正在学习 去看看 CSDN讲师

Part 2: 触发字检测

关键词语音唤醒

触发字检测

欢迎来到这个专业课程的最终编程任务!

在本周的视频中,你了解了如何将深度学习应用于语音识别。在本作业中,您将构建一个语音数据集并实现触发字检测算法(有时也称为关键字检测或唤醒检测)。触发字检测技术,可以让亚马逊Alexa,Google Home,Apple Siri和百度DuerOS等设备在听到某个词语时进行唤醒。

本练习中,我们的触发词将是“Activate”。每当它听到你说“Activate”时,它就会发出“chiming”声音。在此作业结束时,您将能够录制自己正在讲话的片段,并在算法检测到您说出“chiming”时让算法触发一次钟声。

完成作业后,也许你还可以扩展到笔记本电脑上运行,这样每当你说“chiming”它启动你最喜欢的应用程序,或打开你家的网络连接灯,或触发一些其他事件。

image

本作业中,你将学到

  • 构建一个语音识别项目
  • 合成和处理音频记录以创建训练/开发测试集
  • 训练触发字检测模型并进行预测

导包

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

1 数据合成:创建语音数据集

首先为触发字检测算法构建一个数据集。 理想情况下,语音数据集尽可能接近您希望运行的应用程序。 在这种情况下,您希望在工作环境(图书馆,家庭,办公室,开放空间等)中检测到“activate”一词。 因此,您需要在不同的背景声音中混合使用正面词语(“activate”)和负面词语(除activate以外的随机词)。 我们来看看如何创建这样一个数据集。

1.1 聆听数据

你的朋友正在帮助你完成这个项目,并且他们已经去过遍布该地区的图书馆,咖啡馆,餐馆,家庭和办公室,以记录背景噪音,以及人们说正面/负面词汇的片段的片段。 这个数据集包括以各种口音说话的人。

在raw_data目录中,您可以找到正面单词,负面单词和背景噪音的原始音频文件的子集。 您将使用这些音频文件合成数据集来训练模型。 “activate”目录包含说“activate”的人的正面例子。 “negatives”目录包含说除“activate”以外的随机单词的反面例子。 每个音频记录有一个词。 “backgrounds”目录包含步同环境下的背景噪音的10s的剪辑。

聆听样例数据

IPython.display.Audio("./raw_data/activates/1.wav")
IPython.display.Audio("./raw_data/negatives/4.wav")
IPython.display.Audio("./raw_data/backgrounds/1.wav")

你将使用这三种类型的音频数据创建标签数据集。

1.2 从录音到声谱图

什么是录音? 麦克风随着时间的推移记录气压的微小变化,正是这些气压的微小变化让你的耳朵感觉到了声音。 你可以想象一个录音是一个长长的数字列表,用于测量麦克风检测到的微小气压变化。 我们将使用以44100赫兹采样的音频。 这意味着麦克风每秒给我们44100个数字。 因此,10秒音频剪辑由441000个数字(= 10×44100)表示。

从这个音频的原始数据表示中找出是否包含“activate”这个词是相当困难的。 为了帮助你的序列模型更容易学习检测触发字,我们将计算音频的谱图。 频谱图告诉我们一段时间内音频片段中存在多少不同的频率。

(如果你曾经学习过信号处理或傅里叶变换上的课程,频谱的计算时通过在原始音频信号上滑动窗口计算的,并使用傅立叶变换计算每个窗口中最活跃的频率。 如果你不理解前面的句子,也不用担心。)

让我们看一个例子:

IPython.display.Audio("audio_examples/example_train.wav")
x = graph_spectrogram("audio_examples/example_train.wav")

image

上面的图表表示每个频率(y轴)在各个时间步(x轴)上的活动情况。

image

上图是音频记录的频谱图,其中颜色显示的是不同时间点音频不同频率的程度。 绿色方块意味着音频片段中的某个频率更加活跃(声音更响亮); 蓝色方块表示较少的活动频率。

输出谱图的维度取决于谱图软件的超参数和输入的长度。 在本文中,我们将使用10秒音频剪辑作为我们培训示例的“标准长度”。 频谱图的时间步数将为5511.稍后你会看到频谱图作为输入X给带网络中,因此Tx = 5511。

_, data = wavfile.read("audio_examples/example_train.wav")
print("Time steps in audio recording before spectrogram", data[:,0].shape)
print("Time steps in input after spectrogram", x.shape)

# Time steps in audio recording before spectrogram (441000,)
# Time steps in input after spectrogram (101, 5511)

现在你可以定义:

Tx = 5511 # The number of time steps input to the model from the spectrogram
n_freq = 101 # Number of frequencies input to the model at each time step of the spectrogram

注意: 即使10秒作为我们默认的训练示例长度,也可以将10秒的时间离散为不同的数值。 你已经看过441000(原始音频)和5511(频谱图)。 在前一种情况下,每个时间步代表10/441000≈0.000023秒。 在第二种情况下,每个时间步代表10/5511≈0.0018秒。

对于10s的音频,关键的值有:

  • 441000(原始音频)
  • 5511=Tx(频谱图输出,也是神经网络输入的维度)
  • 10000(由pydub模块用于合成的音频)
  • 1375=Ty(即将构建的GRU的输出时间步的数量)

注意: 每一个样本恰好10秒的时间,被不同类型进行离散化。这些都是超参数,可以更改(除了441000,这是麦克风的功能)。 这里选择了语音系统标准范围内的值。

比如Ty = 1375意味着对于模型的输出,我们将10秒离散成1375个时间间隔(每个长度为10 /1375≈0.0072秒),并尝试预测这些时间间隔是否最近有人说过“activate”。

又如上面的10000这个数字,将10秒剪辑离散化为10/10000 = 0.001秒的间隔。 0.001秒也被称为1毫秒 所以当我们说按照1ms间隔进行离散化时,这意味着正在使用10,000步。

Ty = 1375 # The number of time steps in the output of our model

1.3 生成一个训练示例

由于语音数据很难获取和标记,因此您将使用正向、反向和背景的音频剪辑合成训练数据。 记录大量10秒的随机“activates”音频剪辑是很慢的。 相反,记录大量正向和反向词汇,并单独记录背景噪音(或从免费在线渠道下载背景噪音)更容易。

为了合成一个训练样本,你需要:

  • 随机选择一个10秒的背景音频剪辑
  • 随机将0-4个正向音频片段插入此10秒剪辑中
  • 随机将0-2个反向音频片段插入此10秒剪辑中

因为您已将“activates”一词合成到背景剪辑中,所以您确切知道10秒剪辑中何时出现“activates”。 稍后您会看到,这样也更容易生成标签y⟨t⟩。

您将使用pydub软件包来处理音频。 Pydub将原始音频文件转换为Pydub数据结构列表(这里了解细节并不重要)。 Pydub使用1ms作为离散化间隔(1ms是1毫秒= 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: 916
# activate[1] len: 1579

在背景上覆盖正面/负面的词语

给定一个10秒的背景剪辑和一个短的音频剪辑(正面或负面的单词),您需要能够将单词的短片段“添加”或“插入”背景。 为确保插入到背景上的音频片段不重叠,需要跟踪以前插入的音频片段的时间。 您将在背景中插入多个正面/负面单词剪辑,并且不希望插入有重叠。

为了清楚起见,当您在咖啡厅噪音的10秒剪辑中插入1秒“activate”时,最终会出现10秒的剪辑,听起来像某人在咖啡厅中说“activate”。你不会以11秒的剪辑结束。 稍后你会看到pydub如何让你做到这一点。

在插入的同时创建标签

回想一下,标签y⟨t⟩表示某人是否刚说完“activate”。给定一个背景剪辑,我们可以初始化所有t的y⟨t⟩= 0,因为该剪辑不包含任何“activate”。

当你插入或覆盖“activate”剪辑时,您还将更新yt的标签,以便输出的50个步骤具有目标标签1.您将训练GRU以检测某人何时说完“激活”。 例如,假设合成的“activate”剪辑在10秒音频中的5秒处结束 - 恰好在剪辑的中途。 回想一下Ty = 1375,所以时间步长687 = int(1375 * 0.5)对应于5秒进入音频的时刻。 所以,你会设置y688= 1。 此外,如果GRU在短时间内在任何时间内检测到“activate”,那么在此时刻之后,您会非常满意,所以我们实际上将标签yt的50个连续值设置为1.具体来说,我们有y688=y689==y737=1

这是合成训练数据的另一个原因:上面描述的生成这些标签y⟨t⟩比较简单;相反,如果在麦克风上录制了10秒的音频,那么听到该音频并且在“activate”完成时手动标记是非常耗时的。

下面是一张插图,展示了插入“activate”,“innocent”,“activate”,“baby” 的剪辑的标签y⟨t⟩。请注意,正面标签“1”仅与正面字词相关。

image

要实现训练集合成过程,您将使用以下辅助函数。 所有这些功能将使用1ms离散化间隔,所以10秒的音频总是被离散化为10,000步。

  1. get_random_time_segment(segment_ms) 从背景音频中获取随机时间片段
  2. is_overlapping(segment_time, existing_segments) 检查时间片是否与另一个时间片重叠
  3. insert_audio_clip(background, audio_clip, existing_times) 使用 get_random_time_segment 和 is_overlapping 在背景音频的随机时间处插入一个音频时间片
  4. 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

    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)

检查新的时间片是否与之前的任意时间片有重叠。这需要两步:
1. 创建”false”标签,稍后如果有重叠则置为”true”
2. 浏览之前插入时间片的开始和结束时间,比较与新时间片是否有重叠,如果有则将标签置为”true”。

for ....:
        if ... <= ... and ... >= ...:
            ...

提示:如果新的时间片在上一个时间片结束之前开始,或者在下一个时间片开始之后结束,都是有重叠。

# 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)
print("Overlap 2 = ", overlap2)

Overlap 1 =  False
Overlap 2 =  True

期待的输出

key value
Overlap 1 False
Overlap 2 True

现在,我们随机将一个新的音频片段插入到10秒的背景中,但要确保任何新插入的片段都不会与之前的片段重叠。

练习:实现 insert_audio_clip()

将一个新的音频片段插入到10秒的背景中,你需要完成4步:

  1. 以毫秒为单位获取随机时间段。
  2. 确保时间段与前面的任何时间段都不重叠;如果重叠,则返回步骤1并选择新的时间段。
  3. 将新时间段添加到现有时间段列表中,以跟踪插入的所有时间段。
  4. 使用pydub将音频剪辑覆盖在背景上(我们已经为你实现了这个方法)。
# 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:  (2254, 3169)

期待的输出

key value
Segment Time (2254, 3169)
# Expected audio
IPython.display.Audio("audio_examples/insert_reference.wav")

最后,假设你刚刚插入一个“activate”,实现代码来更新标签yt。在下面的代码中,y是一个(1,1375)维向量,因为Ty = 1375。

如果“activate”在时间步骤t结束,则设置yt+1=y<t+2>=y<t+50>个连续值,但是Ty = 1375,注意 y<t+m> 不能越界。

练习:实现 insert_ones()

你可以使用for循环。
(如果你是python的slice操作的专家,也可以使用切片来将其向量化)。
如果一个段在segment_end_ms处结束(使用10000步离散化),将其转换为输出y的索引(使用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+51):
        if i < Ty:
            y[0, i] = 1.0
    ### 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])

# sanity checks: 0.0 1.0 0.0

image

最后,你可以使用insert_audio_clip 和 insert_ones 创建一个新的训练样本。

练习:实现 create_training_example()
  1. 将标签向量y初始化为零值的(1,Ty)numpy数组
  2. 将已存在时间片集合初始化为空列表。
  3. 随机选择0至4个“activate”音频剪辑,并将其插入10秒剪辑,记着将标签插入标签向量y中的正确位置
  4. 随机选择0到2个负面音频片段,并将它们插入10秒片段。
# 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)
    ### 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, _ = background, segment_time = 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)

# File (train.wav) was saved in your directory.

image

现在你可以听一下你创建的新样本,和上面的频谱比较一下。

IPython.display.Audio("train.wav")

期待的输出

IPython.display.Audio("audio_examples/train_reference.wav")

最后,你可以将生成的训练样本的相关标签绘制成图。

plt.plot(y[0])

image

1.4 全部训练集

你现在已经实现了生成单个训练样本所需的代码。 我们将使用这个过程来生成一个大的训练集。 为了节省时间,我们已经生成了一组训练示例。

# Load preprocessed training examples
X = np.load("./XY_train/X.npy")
Y = np.load("./XY_train/Y.npy")

1.5 开发测试集

为了测试我们的模型,我们记录了25个样本的开发集。 虽然我们的训练数据是合成的,但我们希望创建一个与实际输入相同分布的开发集。 因此,我们记录了人们说“activate”和其他随机单词的25个10秒钟音频剪辑,并手工进行标记。 这遵循在课程3中描述的原则,我们应该创建一个尽可能与测试集分布相似的开发集; 这就是为什么我们的开发测试集使用真实而不是合成音频。

# Load preprocessed dev set examples
X_dev = np.load("./XY_dev/X_dev.npy")
Y_dev = np.load("./XY_dev/Y_dev.npy")

2 模型

现在建立好了数据集,让我们写一个训练触发字检测的模型吧。

该模型将使用一维卷积层,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

# Using TensorFlow backend.

2.1 构建模型

下面是将要使用的模型结构。 花点时间看看模型,看看它是否有意义。

image

该模型的一个关键步骤是1维卷积步骤(靠近图的底部)。 它输入5511步频谱,并输出一个1375步输出,然后再进行多层处理得到最终的Ty = 1375步输出。 该层的作用类似于您在课程4中看到的提取低级特征的2D卷积,然后可能会生成较小维度的输出。

从计算角度而言,1-D 卷积层有助于加速模型,因为现在GRU仅处理1375个时步而不是5511个时间步。 两个GRU层从左到右读取输入序列,然后最终使用密集+sigmoid层对yt进行预测。 因为y是二进制值(0或1),所以我们在最后一层使用sigmoid输出来估计输出为1的机会,对应于刚刚说过“activate”的用户。

注意: 我们使用单向RNN而不是双向RNN。 这对于触发字检测非常重要,因为我们希望能够在它说出后立即检测触发字。 如果我们使用双向RNN,我们必须等待整个10秒的音频被记录下来,然后才能确定音频片段的第一秒是否有“activate”。

通过以下四步来实现模型:

  1. 卷积层
  2. 第一个GRU层

    X = GRU(units = 128, return_sequences = True)(X)
    • 设置return_sequences=True确保所有GRU的隐藏状态都被传送到下一层。
    • 记住这一层后面紧跟着Dropout和BatchNorm。
  3. 第二个GRU层
    • 与第一个GRU层类似,只是有一个额外的dropout层
  4. 创建时间分布的dense层

    X = TimeDistributed(Dense(1, activation = "sigmoid"))(X)
    • 这将创建一个dense层,后跟一个sigmoid,以便用于dense层的参数对于每个时间步都是相同的。[See documentation.]
练习:实现 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(196, 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()

# _________________________________________________________________
# Layer (type)                 Output Shape              Param #   
# =================================================================
# input_1 (InputLayer)         (None, 5511, 101)         0         
# _________________________________________________________________
# conv1d_1 (Conv1D)            (None, 1375, 196)         297136    
# _________________________________________________________________
# batch_normalization_1 (Batch (None, 1375, 196)         784       
# _________________________________________________________________
# activation_1 (Activation)    (None, 1375, 196)         0         
# _________________________________________________________________
# dropout_1 (Dropout)          (None, 1375, 196)         0         
# _________________________________________________________________
# gru_1 (GRU)                  (None, 1375, 128)         124800    
# _________________________________________________________________
# dropout_2 (Dropout)          (None, 1375, 128)         0         
# _________________________________________________________________
# batch_normalization_2 (Batch (None, 1375, 128)         512       
# _________________________________________________________________
# gru_2 (GRU)                  (None, 1375, 128)         98688     
# _________________________________________________________________
# dropout_3 (Dropout)          (None, 1375, 128)         0         
# _________________________________________________________________
# batch_normalization_3 (Batch (None, 1375, 128)         512       
# _________________________________________________________________
# dropout_4 (Dropout)          (None, 1375, 128)         0         
# _________________________________________________________________
# time_distributed_1 (TimeDist (None, 1375, 1)           129       
# =================================================================
# Total params: 522,561
# Trainable params: 521,657
# Non-trainable params: 904
# _________________________________________________________________

期待的输出

key value
Total params 522,561
Trainable params 521,657
Non-trainable params 904

网络的输入是(无,5511,101),输出是(无,1375,1)。Conv1D将频谱图中5511步减少到1375步。

2.2 适应模型

触发字检测需要很长时间来训练。 为了节省时间,我们已经使用上面构建的模型结构在GPU上训练了约3小时的模型,并使用大约4000个样本进行了训练。 让我们加载这个模型。

model = load_model('./models/tr_model.h5')

您可以使用Adam优化器和二元交叉熵损失进一步训练模型,如下所示。
执行速度很快,因为我们只训练一个epoch,并且只有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)

# Epoch 1/1
# 26/26 [==============================] - 29s - loss: 0.0728 - acc: 0.9806

2.3 测试你的模型

最后,我们看看模型在开发测试集上表现如何。

loss, acc = model.evaluate(X_dev, Y_dev)
print("Dev set accuracy = ", acc)

# 25/25 [==============================] - 4s
# Dev set accuracy =  0.944989085197

看起来很不错! 然而,对于这项任务来说,准确性并不是一个很好的指标,因为标签严重倾斜到0,所以只输出0的神经网络的准确性会略高于90%。 我们可以定义更多有用的指标,如F1分数或Precision / Recall。 但是,这里我们不要烦恼,只是凭经验去看模型是如何工作的。

3 做预测

现在您已经构建了触发字检测的工作模型,让我们用它来进行预测。 此代码段通过网络运行音频(保存在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

计算出在每个输出步骤检测到“activate”这个词的概率,当概率超过某个阈值时,你就可以触发“chiming”声音。
此外,在“activate”之后,对于连续的许多值,yt可能接近1,但我们只想要一次铃声。
所以我们每75个输出步骤最多插入一次“chiming”声音。
这将有助于防止我们为单个“chiming”实例插入两个“chiming”声音。
(这起到类似于计算机视觉非最大抑制的作用。)

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

3.3 在开发测试集上进行测试

让我们来看看我们的模型在开发集中的两个看不见的音频片段上表现如何。 让我们先听两个开发者设定的剪辑。

IPython.display.Audio("./raw_data/dev/1.wav")
IPython.display.Audio("./raw_data/dev/1.wav")

现在让我们在这些音频剪辑上运行模型,看看它是否在“activate”之后添加了“chiming”声音!

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

谨记

  • 数据合成是为语音问题创建大型训练集的有效方式,特别是触发词检测。
  • 在将音频数据传送到RNN,GRU或LSTM之前,使用频谱图和可选的1D conv层是一个常见的预处理步骤。
  • 可以使用端到端的深度学习方法来构建非常有效的触发字检测系统。

恭喜你完成了最后的作业!

感谢你一直坚持到最后以及为学习深度学习所付出的所有努力。 我们希望你喜欢这门课程!

4 试验你自己的例子!(可选)

录制10秒的音频片段,说出“activate”和其他随机单词,然后将其上传到Coursera中心作为myaudio.wav。 确保将音频上传为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秒音频片段中何时有”activate”并触发“chiming”声音。 如果没有正确添加“chiming”声音,请尝试调整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")
2020-03-24 16:16:11 Pandade520 阅读数 215
  • 深度学习入门及如何转型AI领域

    深度学习入门转型视频教程,该课程主要告诉开发者如何快速入门深度学习。讲师是在机器学习,深度学习神经网络领域多年开发研究经验,精通算法原理与编程实践。曾完成过多项图像识别,目标识别,语音识别的实际项目,经验丰富。关注深度学习领域各种开源项目,如TensorFlow,Caffe,Torch等。喜欢理论与实践相结合的教学风格,课程编排由浅入深,体系清晰完整。

    2781 人正在学习 去看看 CSDN讲师

摘自清华语音和语言技术中心

KWS评价指标

1)、唤醒率: 在测试过程中被系统正确唤醒的次数除以总的测试次数。
2)、虚警率: 在测试过程中系统不被唤醒的次数除以总的测试次数。
3)、实时率: 系统的反应速度。
4)、功耗水平: 低功耗是系统性能的一个重要指标,与用户体验紧密相关。

方法流程

关键词唤醒分为两个阶段:检测阶段、决策阶段。
第一个阶段:检测阶段。即系统手机关键词在给定的句子中的信息。第二阶段:决策阶段。即系统根据检测阶段得到的置信向量判断关键词是否出现在语音中,并给出关键词出现的位置

关键词唤醒和LVCSR

LVCSR(大词汇量语音识别任务)是将一段连续语音识别成文字。该任务的HMM-GMM系统原理是为每个音素进行建模,将输入语音的没帧识别成音素的状态。LVCSR任务的DNN方法输入是每帧语音信号特征向量,输出是每帧对应的因素,本质上是一个多分类任务。
KWS任务可以被视为是LVCSR任务的子问题,不同的是KWS任务只需要在一段连续语音中提取关键字的信息。
实际上关键词唤醒问题的解决方法和LVCSR在一定程度上是相通的。

关键词唤醒的难点

最主要的问题是低功耗和高计算需求的不平衡。实际落地需要系统尽可能减小运算量和降低功耗,但同时不能降低系统的稳定性。

模型方法

1、Query-by-Example方法
最早尝试的方法之一,将关键词当做example,将听到的语音和关键词进行比对的放发,是一种基于模板匹配的方法。具体实现包括两个步骤:
(1)训练步骤
先选择合适的方法对将语音格式的关键词表示为易于比较的模板格式(例如特
征向量或网格形式等),完成对关键词的建模。
(2)测试步骤
将待检测的目标语音转化成和模板相同的格式,然后和关键词模板进行匹配计算相似度。在过去几十年对query-by -example方法的探索中,研究热点是如何选择合适的特征表示方法[3]对关键词进行建模,而模板比对主要采用动态时间规整(dynamic time warping) [4]的算法,它采用了动态规划的思想。在自动语音识别的孤立词识别任务中,也采用了类似query-by-example的方法。将给定的词汇创建模板,将待识别语音和模板比对,相似度最大的即为识别结果。DTW方法在孤立词识别任务中也能取得较好的效果。query-by- example方法不再是KWS问题的主流方法,因此此处不再做过多展开。
2、Keyword/Filler隐马尔科夫系统
基于隐马尔可夫模型的keyword/filler系统[5]是目前的主流方法之一,它和语音识别中早期使用的HMM-GMM系统原理相似。其思想都是用HMM模型对待识别的语音的子字单元进行建模。最大的不同是在解码器中,LVCSR方法的解码器中包含了词典中所有的单词,而关键词唤醒任务的keyword/iller系统则将语音分为关键词(keyword) 和非关键词(iller) 两类进行建模。如图1所示,
在这里插入图片描述keyword建模采用精细建模方法,在词级、音素级或状态级上对关键词进行建模; fllr建 模采用粗放建模的方法,对除关键词之外的任意词语和噪音进行建模。这样建模的好处是大大缩小了解码空间,使解码速度变快,提高系统的实时率。
在这里插入图片描述
如图2所示为基于HMM系统的keyword-iller的一一个实例, 来自于Amazon的文章[6]。该解码系统由keyword(alexa)路径和iller路径构成,fller路径 又分为非语音(Non-speech,NSP)路径和语音(speech,SP) 路径。对关键词采用三状态HMM对音素进行建模,而fller采用单状态HMM进行建模,有趣的是该隐马尔可夫模型的发射概率由TDNN进行建模。

3、LVCSR方法
一种简单粗暴的想法是直接用LVCSR语音识别系统解决关键词检索唤醒问题[7]。待识别语音通过LVCSR系统给识别为-一个个单词或字,再进一步进行关键词检索。基于LVCSR系统方法的一个缺点是OOV问题。如果关键词不在词典中,那KWS问题将无法进行。基于LVCSR的关键词唤醒虽然能取得较好的效果,但是计算需求量大,在训练资源充足的时候系统稳定性较强,但是在移动设备.上连续运行LVCSR系统通常是不切实际的。

4、Deep KWS端到端系统
目前最为流行的方法是基于神经网络的端到端关键词唤醒系统,在论文[8]中被首次提出。如图3所示为deep KWS系统的具体结构。该系统分为三个模块:
(1)特征提取模块。对原始语音信号进行特征提取,通常做法是提取每帧的特征向量。
(2)神经网络模块。输入是语音的特征向量。输出是关键词和非关键词的后验概率。
(3)后验值处理模块。由于神经网络输出的后验值是带有噪音的。在这一-步骤中,对后验值以一定窗长进行平滑。平滑后的后验值超过一-定的阅值,则认为被唤醒。
在这里插入图片描述

关键词唤醒和嵌入式系统

目前在嵌入式设备商的关键词唤醒系统优化主要分为两类:一是通过减少神经网络参数量的方法提高系统在设备上的性能(模型裁剪、模型压缩)。二是针对硬件平台的优化。

小结

本节介绍了关键词唤醒的概念、技术难点和解决方法。模型方法包括Query-by-Example通过DTW算法进行模板匹配的方法; Keyword/Filler系统基于隐马尔可夫模型对关键词和非关键词进行建模的方法:基于LVCSR的关键词唤醒方法以及DeepKWS端到端输出后验概率的方法。目前工程应用上多采用Keyword/Filler系统和Deep KWS系统,在保证系统准确率的同时降低系统能耗并提升系统的反应速度。
关于关键词唤醒系统在嵌入式设备端的部署,主要介绍了两种方法。一是通过减少模型本身的复杂度降低运算量,二是通过运算加速库针对硬件平台进行优化,提高运算速度。

2019-04-20 17:16:17 zhinengxuexi 阅读数 91
  • 深度学习入门及如何转型AI领域

    深度学习入门转型视频教程,该课程主要告诉开发者如何快速入门深度学习。讲师是在机器学习,深度学习神经网络领域多年开发研究经验,精通算法原理与编程实践。曾完成过多项图像识别,目标识别,语音识别的实际项目,经验丰富。关注深度学习领域各种开源项目,如TensorFlow,Caffe,Torch等。喜欢理论与实践相结合的教学风格,课程编排由浅入深,体系清晰完整。

    2781 人正在学习 去看看 CSDN讲师

智能语音的应用从初期的手机助手形态(语音拨号、语音导航、语音搜索、语音听写等),发展到今天软硬一体的远讲降噪、语音唤醒、声纹识别、语用计算、流式交互等等。

从当前的语音识别技术发展看来,基础理论和技术框架已经基本成熟,目前主要是基于深度神经网络的识别架构,再加上大规模的真实数据训练。目前在手机端的近讲语音识别错误率可以做到3%以内,在电话语音识别错误率可以做到6%以内,基本上接近或超过人工识别的水平。语义理解和知识图谱等认知技术,目前还没有形成通用的技术框架,主要是针对具体垂直领域进行优化,这部分有待突破性进展。

目前语音交互技术面临最大的挑战还是来自产业应用层面:

首先是针对专业领域的优化,比如在医疗领域,通常专用词汇比较多,让我们普通人去听的时候可能都写不下来,只有经过专业训练的医生和护士才能准确记录;影视节目查询、商品搜索、路径导航等方面也同样需要做针对性的优化。

其次是相对复杂的口音和噪声环境,包括全国各地的口音和方言、远讲、噪声、混响等,都是比较复杂的。声音传播能量是随距离平方成反比关系的,传播距离增加一倍能量就衰减到四分之一,在五米外采集的语音能量,和在一米采集能量相比就相差25倍,但噪声并没有因为距离而降低,加上距离远之后,房间会出现多次反射和叠加,还会形成混响效果,信噪比会下降很多,都会对识别效果造成影响,这些是需要麦克风阵列结合语音声学模型去解决的问题。

第三是低功耗、低成本、高可靠的问题,智能家电需要满足绿色环保标准,需要确保24小时误唤醒低于1次,尽量不出现误操作,要实现大规模量产出货,还要降低整体方案的成本。面向物联网的智能语音交互方案,如果功耗成本下不来就不能普及,智能手表、手环等依赖电池的穿戴设备,功耗和成本问题就更加严重。

最后是语音交互设计问题,这是语音技术产品化的重中之重,现在的物联网设备,大到汽车、空调、机器人,小到玩具、穿戴设备等,有大有小、有带屏幕和不带屏幕、涉及不同的应用领域,交互方式差异非常大,需要针对性优化语义理解和知识图谱。

语音交互是未来物联网的重要交互手段,这是我们为什么把物联网列为语音技术落地主战场的原因。有了智能语音交互基础之后,我们还要做好精准和个性化的内容和服务,这才是用户真正想要的。

改变用户的使用方式

在智能语音最早进入市场时,用户会觉得比较新奇,当时用户是需要对着话筒,在近讲和安静情况下比较配合才能使用(最初还需要用户先念一段话来训练模型),所以大家觉得语音识别和人工智能技术不靠谱。

最近这几年,随着深度学习技术和大数据的发展,智能语音技术的进步已经超出了很多用户的想象,用户可以无需预先训练,在真实应用场景下实现相对自然的语音交互。现阶段用户对语音交互的接受度在逐步提升,随着语音应用和服务的日臻完善,让用户会逐渐形成习惯,越来越接受这种交互方式,机器则可以通过后台数据的不断快速迭代提高精度,用户就越来越喜欢用。

人工智能、大数据、云计算和物联网的未来发展值得重视,均为前沿产业,多智时代专注于人工智能和大数据的入门和科谱,在此为你推荐几篇优质好文:
这才是人们需要的AI语音助手
http://www.duozhishidai.com/article-12812-1.html
AI语音对话,什么时候能够摆脱智障?
http://www.duozhishidai.com/article-7553-1.html
智能语音技术革命的前世与今生
http://www.duozhishidai.com/article-2416-1.html


多智时代-人工智能大数据学习入门网站|人工智能、大数据、物联网云计算的学习交流网站

多智时代-人工智能大数据学习入门网站|人工智能、大数据、云计算、物联网的学习服务的好平台
  
2016-03-30 16:26:03 happytofly 阅读数 1379
  • 深度学习入门及如何转型AI领域

    深度学习入门转型视频教程,该课程主要告诉开发者如何快速入门深度学习。讲师是在机器学习,深度学习神经网络领域多年开发研究经验,精通算法原理与编程实践。曾完成过多项图像识别,目标识别,语音识别的实际项目,经验丰富。关注深度学习领域各种开源项目,如TensorFlow,Caffe,Torch等。喜欢理论与实践相结合的教学风格,课程编排由浅入深,体系清晰完整。

    2781 人正在学习 去看看 CSDN讲师

环形五麦克风阵列360°全平面拾音角度,远场拾音距离可达5米。

功能特性包括:

  • 二维空间拾音
  • 抗噪能力增强
  • 远距拾取音频
  • 支持连续唤醒
  • 支持回声消除
  • 支持语音打断

噪声、混响、干扰和回声是声学信号处理需要解决的问题。讯飞采用声源定位技术,利用麦克风阵列用基于TDOA(Time Difference Of Arrival,到达时间差)计算声源距离阵列的角度和距离,实现对目标声源的跟踪。再通过波束形成技术,即在期望方向上有效地形成一个波束,仅拾取波束的信号,从而达到同时提取声源和抑制噪声的目的。

图片描述

图片描述

原文:http://www.pieeco.com/zb/keda.html?from=groupmessage&isappinstalled=0

Wake Up AI

阅读数 48

没有更多推荐了,返回首页