2019-09-09 21:27:29 ACBC12345 阅读数 531
  • 深度学习入门及如何转型AI领域

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

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

雷锋网AI科技评论编者按:人工合成人类语音被称为语音合成。这种基于机器学习的技术适用于文本转换语音(text-to-speech)、音乐生成、语音生成、语音支持设备、导航系统以及为视障人士提供无障碍服务。

在这篇文章中,我们将研究基于深度学习而进行的研究或模型框架。

在我们正式开始之前,我们需要简要概述一些特定的、传统的语音合成策略:拼接和参数化。

拼接方法,需要使用大型数据库中的语音来拼接生成新的可听语音。在需要不同语音风格的情况下,必须使用新的音频数据库,这极大的限制了这种方法的可扩展性。

参数化方法则是用一条记录下的人的声音以及一个含参函数,通过调节函数参数来改变语音。

这两种方法代表了传统的语音合成方法。现在让我们来看看使用深度学习的新方法。为了探索当前流行的语音合成方法,我们研究了这些:

WaveNet: 原始音频生成模型Tacotron:端到端的语音合成Deep Voice 1: 实时神经文本语音转换Deep Voice 2: 多说话人神经文本语音转换Deep Voice 3: 带有卷积序列学习的尺度文本语音转换Parallel WaveNet: 快速高保真语音合成利用小样本的神经网络语音克隆VoiceLoop: 通过语音循环进行语音拟合与合成利用梅尔图谱预测上的条件WaveNet进行自然TTS合成WaveNet: 原始音频生成模型

这篇文章的作者来自谷歌。他们提出了一种能产生原始音频波的神经网络。他们的模型是完全概率的和自回归的,在英语和汉语的text-to-speech上都取得了最先进的结果。

文章链接: https://arxiv.org/abs/1609.03499

图1

WaveNET是基于PixelCNN的音频生成模型,它能够产生类似于人类发出的声音。

图2

在这个生成模型中,每个音频样本都以先前的音频样本为条件。条件概率用一组卷积层来建模。这个网络没有池化层,模型的输出与输入具有相同的时间维数。

图3

在模型架构中使用临时卷积可以确保模型不会违反数据建模的顺序。在该模型中,每个预测语音样本被反馈到网络上用来帮助预测下一个语音样本。由于临时卷积没有周期性连接,因此它们比RNN训练地更快。

使用临时卷积的主要挑战之一是,它们需要很多层来增加感受野。为了解决这一难题,作者使用了加宽的卷积。加宽的卷积使只有几层的网络能有更大的感受野。模型使用了Softmax分布对各个音频样本的条件分布建模。

图4

这个模型在多人情景的语音生成、文本到语音的转换、音乐音频建模等方面进行了评估。测试中使用的是平均意见评分(MOS),MOS可以评测声音的质量,本质上就是一个人对声音质量的评价一样。它有1到5之间的数字,其中5表示质量最好。

图5

下图显示了1-5级waveNet的语音质量

图6

Tacotron:端到端的语音合成

这篇文章的作者来自谷歌。 Tacotron是一种端到端的生成性文本转化语音的模型,可直接从文本和音频对合形成语音。Tacotron在美式英语上获得3.82分的平均得分。Tacotron是在帧级生成语音,因此比样本级自回归的方法更快。

文章链接:https://arxiv.org/abs/1703.10135

这个模型是在音频和文本对上进行的训练,因此它可以非常方便地应用到新的数据集上。Tacotron是一个seq2seq模型,该模型包括一个编码器、一个基于注意力的解码器以及一个后端处理网络(post-processing net)。如下框架图所示,该模型输入字符,输出原始谱图。然后把这个谱图转换成波形图。

图7

下图显示了CBHG模块的结构。它由1-D卷积滤波器,highway networks和双向GRU(Gated Recurrent Unit)组成。

图8

将字符序列输入编码器,编码器将提取出文本的顺序表示。每个字符被表示为一个独热向量嵌入到连续向量中。然后加入非线性变换,再然后加上一个dropout,以减少过度拟合。这在本质上减少了单词的发音错误。

模型所用的解码器是基于内容注意力的tanh解码器。然后使用Griffin-Lim算法生成波形图。该模型使用的超参数如下所示。

图9

下图显示了与其他替代方案相比,Tacotron的性能优势。

图10

Deep Voice 1: 实时神经文本到语音合成

这篇文章的作者来自百度硅谷人工智能实验室。Deep Voice是一个利用深度神经网络开发的文本到语音的系统.

文章链接:https://arxiv.org/abs/1702.07825

它有五个重要的组成模块:

定位音素边界的分割模型(基于使用连接时间分类(CTC)损失函数的深度神经网络);字母到音素的转换模型(字素到音素是在一定规则下产生单词发音的过程);音素持续时间预测模型;基频预测模型;音频合成模型(一个具有更少参数的WaveNet变体)。

图11

字母到音素模型将英文字符转换为音素。分割模型识别每个音素在音频文件中开始和结束的位置。音素持续时间模型预测音素序列中每个音素的持续时间。

基频模型预测音素是否发声。音频合成模型则综合了字母到音素转换模型、音素持续时间模型、基频预测模型等的输出进行音频合成。

以下是它与其他模型的对比情况

图12

Deep Voice 2: 多说话人神经文本语音转换

这篇文章是百度硅谷人工智能实验室在Deep Voice上的二次迭代。他们介绍了一种利用低维可训练说话人嵌入来增强神经文本到语音的方法,这可以从单个模型产生不同的声音。

该模型与DeepVoice 1有类似的流水线,但它在音频质量上却有显著的提高。该模型能够从每个说话人不到半个小时的语音数据中学习数百种独特的声音。

文章链接:https://arxiv.org/abs/1705.08947

作者还介绍了一种基于WaveNet的声谱到音频的神经声码器,并将其与Taco tron结合,代替Griffin-Lim音频生成。这篇文章的重点是处理多个说话人而每个说话人的数据有非常少的情况。模型的架构类似于Deep Voice 1,训练过程如下图所示。

图13

Deep Voice 2和Deep Voice 1之间的主要区别在于音素持续时间模型和频率模型的分离。 Deep Voice 1有一个用于联合预测音素持续时间和频率曲线的单一模型; 而在Deep Voice 2中,则先预测音素持续时间,然后将它们用作频率模型的输入。

Deep Voice 2中的分割模型使用一种卷积递归结构(采用连接时间分类(CTC)损失函数)对音素对进行分类。Deep Voice 2的主要修改是在卷积层中添加了大量的归一化和残余连接。它的发声模型是基于WaveNet架构的。

从多个说话人合成语音,主要通过用每个说话人的单个低维级说话人嵌入向量增强每个模型来完成的。说话人之间的权重分配,则是通过将与说话人相关的参数存储在非常低维的矢量中来实现。

递归神经网络(RNN)的初始状态由说话人声音的嵌入产生。采用均匀分布的方法随机初始化说话人声音的嵌入,并用反向传播对其进行联合训练。说话人声音的嵌入包含在模型的多个部分中,以确保能考虑到每个说话人的声音特点。

图14

接下来让我们看看与其他模型相比它的性能如何

图15

Deep Voice 3: 利用卷积序列学习将文本转换为语音

文章链接:https://arxiv.org/abs/1710.07654

这篇文章的作者提出了一种全卷积字符到谱图的框架,可以实现完全并行计算。该框架是基于注意力的序列到序列模型。这个模型在LibriSpeech ASR数据集上进行训练。

这个模型的结构能够将字符、音素、重音等文本特征转换成不同的声码器参数,其中包括Mel波段光谱图、线性比例对数幅度谱图、基频谱图、谱包络图和非周期性参数。然后将这些声码器参数作为音频波形合成模型的输入。

图16

模型的结构由以下几个部分组成:

编码器:一种全卷积编码器,可将文本特征转换为内部学习表示。解码器:一种全卷积因果解码器,以自回归的方式解码学习表示。转换器:一种全卷积后处理网络,可预测最终的声码器参数。对于文本预处理,作者的处理方式包括:大写文本输入字符,删除标点符号,以句号或问号结束每句话,并用表示停顿长度的特殊字符替换空格。

下图是该模型与其他替代模型的性能比较。

图17

Parallel WaveNet: 快速高保真语音合成

这篇文章的作者来自谷歌。他们引入了一种叫做概率密度蒸馏的方法,它从一个训练过的WaveNet中训练一个并行前馈网络。该方法是通过结合逆自回归流(IAFS)和波形网(WaveNet)的最佳特征构建的。这些特征代表了WaveNet的有效训练和IAF网络的有效采样。

文章链接:https://arxiv.org/abs/1711.10433

为了进行有效训练,作者使用一个已经训练过的WaveNet作为“老师”,并行WaveNet‘学生’向其学习。目的是为了让学生从老师那里学到的分布中匹配自己样本的概率。

图18

作者还提出了额外的损失函数,以指导学生生成高质量的音频流:

功率损失函数:确保使用语音不同频带的功率,就像人在说话一样。感知损失函数:针对这种损失函数,作者尝试了特征重构损失函数(分类器中特征图之间的欧氏距离)和风格损失函数(Gram矩阵之间的欧氏距离)。他们发现风格损失函数会产生更好的效果。无论条件向量如何,对比度损失会惩罚有高可能性的波形。下图显示了这个模型的性能:

图19

利用小样本的神经网络语音克隆

据雷锋网了解,这篇文章的作者来自百度研究院。他们引入了一个神经语音克隆系统,它可以通过学习从少量音频样本合成一个人的声音。

系统使用的两种方法是说话人自适应和说话人编码。说话人自适应是通过对多个说话人的声音生成模型进行微调来实现的,而说话人编码则是通过训练一个单独的模型来直接推断一个新的嵌入到多个说话人语音生成模型。

文章链接:https://arxiv.org/abs/1802.06006v3

本文采用Deep Voice 3作为多说话人模型的基线。所谓声音克隆,即提取一个说话人的声音特征,并根据这些特征来生成给定的文本所对应的音频。

生成音频的性能指标决定于语音的自然度和说话人声音的相似度。作者提出了一种说话人编码方法,该方法能够从未曾见过的说话人音频样本中预测说话人声音嵌入。

图20

下面是声音克隆的性能:

图21

图22

VoiceLoop: 通过语音循环进行语音拟合与合成

这篇文章的作者来自Facebook AI研究院。他们引入了一种神经文本到语音(TTS)技术,可以将文本从野外采集的声音转换为语音。

文章链接:https://arxiv.org/abs/1707.06588

VoiceLoop的灵感来源于一种称为语音循环的工作记忆模型,它能在短时间内保存语言信息。它由两部分组成,其一是一个不断被替换的语音存储(phonological store),其二是一个在语音存储中保持长期表达(longer-term representations)的预演过程。

Voiceloop将移动缓冲区视作矩阵,从而来构造语音存储。句子表示为音素列表。然后从每个音素解码一个短向量。通过对音素的编码进行加权并在每个时间点对它们求和来生成当前的上下文向量。

使VoiceLoop脱颖而出的一些属性包括:使用内存缓冲区而不是传统的RNN,所有进程之间的内存共享,以及使用浅层、全连接的网络进行所有计算。

图23

下图显示了模型与其他替代方案相比的性能表现

图24

图25

利用梅尔图谱预测上的条件WaveNet进行自然TTS合成

作者来自谷歌和加州大学伯克利分校。他们引入了Tacotron 2,这是一种用于文本语音合成的神经网络架构。

文章链接:https://arxiv.org/abs/1712.05884

它由一个循环的的序列到序列特征预测网络组成,该网络将字符嵌入到梅尔标度图谱中。然后是一个修改后的WaveNet模型,这个模型充当声码器,利用频谱图来合成时域波。模型的平均意见评分(MOS)为4.53分。

图26

这个模型结合了Tacconon和WaveNet的最佳特点。下面是它与其他模型的性能对比:

图27

雷锋网小结:

现在的语音合成技术发展很快,我们希望能够尽快追赶上最前沿的研究。以上这几篇文章是当前语音合成领域最重要的进展代表,论文、以及其代码实现都可在网上找到,期待你能去下载下来进行测试,并能够获得期望的结果。

让我们一起创造一个丰富多彩的语音世界。

原文链接:

https://heartbeat.fritz.ai/a-2019-guide-to-speech-synthesis-with-deep-learning-630afcafb9dd

2018-05-29 11:41:08 qq_41185868 阅读数 10413
  • 深度学习入门及如何转型AI领域

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

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

Python之GUI:基于Python的GUI界面设计的一套AI课程学习(机器学习、深度学习、大数据、云计算等)推荐系统(包括语音生成、识别等前沿黑科技)

导读
基于Python的GUI界面设计的一套AI课程学习(机器学习、深度学习、大数据、云计算等)推荐系统,(包括目标检测、人脸识别,语音生成、识别等前沿黑科技)。本款系统软件归Jason Niu本人所有,严禁盗用,谢谢合作!

 

 

目录

输出结果

核心代码


 

 

输出结果


 

 

核心代码

def niu_read_docx(filename):
     doc=docx.Document(filename)
     fulltext=[]
     for para in doc.paragraphs:
         fulltext.append(para.text)
     return '\n'.join(fulltext)

class dialog(QDialog, Ui_dialog):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(dialog, self).__init__(parent)
        self.setupUi(self)
        
        time.sleep(2) #使开机画面停留2秒 
    
    @pyqtSlot()
    def on_pushButton_clicked(self):
        """
        Slot documentation goes here.
        """
        my_str_2=self.lineEdit_2.text()
        my_str_3=self.lineEdit_3.text()
        my_str_4=self.lineEdit_4.text()
        
        print(my_str, my_str_2, my_str_3, my_str_4)
        Button1_1=QMessageBox.information(self, u'提示信息框', u'输入的s所有信息已经存储到数据库中!', ) #提示对话信息框
        print('OK')
        self.close() #关闭当前界面

    
    @pyqtSlot()
    def on_pushButton_2_clicked(self):
        """
        Slot documentation goes here.
        """
        print('Cancel')

class MainWindow(QMainWindow, Ui_MainWindow):
    """
    Class documentation goes here.
    """
    def __init__(self, parent=None):
        """
        Constructor
        @param parent reference to the parent widget
        @type QWidget
        """
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        self.graphicsView.mousePressEvent=self.my_clicked
        
    def my_clicked(self, e):
        print('自定义的点击事件函数')
        webbrowser.open('www.baidu.com') #默认浏览器打开指定网址

    @pyqtSlot()
    def on_pushButton_clicked(self):
        """
        Slot documentation goes here.
        """
        print(self.textBrowser.toPlainText()) 
    
    @pyqtSlot()
    def on_pushButton_2_clicked(self):
        """
        Slot documentation goes here.
        这是测试按钮的槽函数
        """
        self.lineEdit.setText( "")  #清除单行文本框内容
        self.lineEdit_2.setText( "")
        self.textBrowser.setText( "") #清除多行文本框内容
        print("清除登陆账号信息!")
    
    @pyqtSlot()
    def on_pushButton_3_clicked(self):
        """
        Slot documentation goes here.
        """
        my_str=self.lineEdit.text()+":"+self.lineEdit_2.text() #获取单行文本框的内容
        self.textBrowser.append(my_str) #向多行文本添加内容
        print(my_str)
        
    @pyqtSlot()
    def on_pushButton_4_clicked(self):
        """
        Slot documentation goes here.
        该按钮命令调用的预定义的函数,退出的命令
        """

    @pyqtSlot()
    def on_pushButton_5_clicked(self):
        """
        Slot documentation goes here.
        """
        Button5=QMessageBox.question(self, u'提示信息框', u'是否全部保存到数据库中?')
        if Button5==0:
            print('全部保存中……')
        else:
            print('没有保存')
            
        
    @pyqtSlot()
    def on_pushButton_6_clicked(self):
        """
        Slot documentation goes here.
        """
        Button6=QMessageBox.warning(self, u'提警告信息框', u'没有警告信息,请继续输入!')
        
    @pyqtSlot()
    def on_pushButton_7_clicked(self):
        """
        Slot documentation goes here.
        """
        Button7=QMessageBox.critical(self, u'严重警告!', u'没有严重警告信息,请继续输入!')
   

    @pyqtSlot()
    def on_pushButton_9_clicked(self):
        """
        Slot documentation goes here.
        """
        self.graphicsView.setStyleSheet("border-image: url(:/im/image/AI (4).jpg);")
        
        

 
#    @pyqtSlot(QUrl)
    def on_textBrowser_anchorClicked(self, p0):
        """
        Slot documentation goes here.
        
        @param p0 DESCRIPTION
        @type QUrl
        """

    @pyqtSlot()
    def on_radioButton_clicked(self):
        """
        Slot documentation goes here.
        """
        print('同时选择其他三个首个radioButton')
        self.radioButton_12.setChecked(True)
        self.radioButton_16.setChecked(True)
        self.radioButton_20.setChecked(True)
        self.label_4.setStyleSheet("border-image: url(:/im/image/AI (4).jpg);")

 

 


 

2017-09-12 13:34:52 qq_36852006 阅读数 784
  • 深度学习入门及如何转型AI领域

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

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

一、深度学习在语音合成中的应用

语音合成主要采用波形拼接合成和统计参数合成两种方式。波形拼接语音合成需要有足够的高质量发音人录音才能够合成高质量的语音,它在工业界中得到了广泛使用。统计参数语音合成虽然整体合成质量略低,但是在发音人语料规模有限的条件下,优势更为明显。在上一期我们重点介绍了深度学习在统计参数语音合成中的应用,本期将和大家分享基于波形拼接的语音合成系统,围绕 Siri 近期推出的语音合成系统展开介绍,它是一种混合语音合成系统,选音方法类似于传统的波形拼接方法,它利用参数合成方法来指导选音,本质上是一种波形拼接语音合成系统。

单元选择是波形拼接语音合成系统的基本难题,需要在没有明显错误的条件下将合适的基元组合在一起。语音合成系统通常分为前端和后端两个部分,前端模块对于提高语音合成系统的表现力起到非常重要的作用。前端模块将包含数字、缩写等在内的原始文本正则化,并对各个词预测读音,解析来自文本的句法、节奏、重音等信息。因此,前端模块高度依赖于语言学信息。后端通过语言学特征预测声学参数,模型的输入是数值化的语言学特征。模型的输出是声学特征,例如频谱、基频、时长等。在合成阶段,利用训练好的统计模型把输入文本特征映射到声学特征,然后用来指导选音。在选音过程中需要重点考虑以下两个准则:(1)候选基元和目标基元的特征必须接近;(2)相邻两个基元的边界处必须自然过渡。可以通过计算目标代价和拼接代价评估这两个准则;然后通过维特比算法计算最优路径确定最终的候选基元;最后通过波形相似重叠相加算法找出最佳拼接时刻,因此生成平滑且连续合成语音。

Siri 的 TTS 系统的目标是训练一个基于深度学习的统一模型,该模型能自动准确地预测数据库中单元的目标成本和拼接成本。因此该方法使用深度混合密度模型来预测特征值的分布。这种网络结构结合了常规的深度神经网络和高斯混合模型的优势,即通过 DNN 对输入和输出之间的复杂关系进行建模,并且以概率分布作为输出。系统使用了基于 MDN 统一的目标和拼接模型,该模型能预测语音目标特征(谱、基频、时长)和拼接成本分布,并引导基元的搜索。对于元音,有时语音特征相对稳定,而有些时候变化又非常迅速,针对这一问题,模型需要能够根据这种变化性对参数作出调整,因此在模型中使用嵌入方差解决这一问题。系统在运行速度、内存使用上具有一定优势,使用快速预选机制、单元剪枝和计算并行化优化了它的性能,可以在移动设备上运行。

 

二、深度学习在语音增强中的应用

通过语音增强可以有效抑制各种干扰信号,增强目标语音信号;有效的语音增强算法一方面可以提高语音可懂度和话音质量,另一方面有助于提高语音识别和声纹识别的鲁棒性。经典的语音增强方法包括谱减法、维纳滤波法、最小均方误差法,上述方法基于一些数学假设,在真实环境下难以有效抑制非平稳噪声的干扰。基于盲分离的非负矩阵分解方法也得到了一定关注,但是这类方法计算复杂度相对较高;近年来,基于深度学习的语音增强方法得到了越来越多的关注,接下来重点介绍几种典型的基于深度学习的语音增强方法。

1. 预测幅值谱信息

这类方法通过深层神经网络模型建立带噪语音和干净语音谱参数之间的映射关系,模型的输入是带噪语音的幅值谱相关特征,模型的输出是干净语音的幅值谱相关特征,通过深层神经网络强大的非线性建模能力重构安静语音的幅值谱相关特征;神经网络模型结构可以是 DNN/BLSTM-RNN/CNN 等;相比于谱减、最小均方误差、维纳滤波等传统方法,这类方法可以更为有效的利用上下文相关信息,对于处理非平稳噪声具有明显的优势。

 

2. 预测屏蔽值信息

采用这类方法建模时模型的输入可以是听觉域相关特征,模型的输出是二值型屏蔽值或浮点型屏蔽值,最常用的听觉域特征是 Gamma 滤波器相关特征,这种方法根据听觉感知特性将音频信号分成不同子带提取特征参数;对于二值型屏蔽值,如果某个时频单元能量是语音主导,则保留该时频单元能量,如果某个时频单元能量是噪声主导,则将该时频单元能量置零;采用这种方法的优势是,共振峰位置处的能量得到了很好的保留,而相邻共振峰之间波谷处的能量虽然失真误差较大,但是人耳对这类失真并不敏感;因此通过这种方法增强后的语音具有较高的可懂度;浮点值屏蔽是在二值型屏蔽基础上进一步改进,目标函数反映了对各个时频单元的抑制程度,进一步提高增强后语音的话音质量和可懂度。

3. 预测复数谱信息

目前主流的语音增强方法更多的关注于对幅值谱相关特征的增强而保留原始语音的相位谱,随着信噪比的降低相位谱失真对听感的影响明显增强,在低信噪比条件下,有效的相位重构方法可以有助于提高语音可懂度;一种典型的相位重构方法是利用基音周期线索对浊音段的相位进行有效修复,但是这类方法无法有效估计清音段的相位信息;复数神经网络模型可以对复数值进行非线性变换,而语音帧的复数谱能够同时包含幅值谱信息和相位谱信息,可以通过复数神经网络建立带噪语音复数谱和干净语音复数谱的映射关系,实现同时对幅值信息和相位信息的增强。

4. PIT 说话人分离

通过说话人分离技术可以将混叠语音中不同的说话人信息有效分离出来,已有的基于深度学习的说话人分离模型受限于说话人,只能分离出特定说话人的声音;采用具有置换不变性的训练方法得到的说话人分离模型不再受限于特定说话人;这种方法通过自动寻找分离出的信号和标注的声源之间的最佳匹配来优化语音增强目标函数;模型的输入是混叠语音的谱参数特征,模型的输出包含多个任务,每个任务对应一个说话人;在训练过程中,对于训练集中一个样本内,每个任务固定对应某个说话人;可以采用 BLSTM-RNN 模型结构建模。

5. DeepClustering 说话人分离

基于深度聚类的说话人分离方法是另一种说话人无关的分离模型,这种方法通过把混叠语音中的每个时频单元结合它的上下文信息映射到一个新的空间,并在这个空间上进行聚类,使得在这一空间中属于同一说话人的时频单元距离较小可以聚类到一起;将时频单元映射到新的空间跟词矢量抽取的思想有些类似,可以通过 k 均值聚类的方法对时频单元分组,然后计算二值型屏蔽值分离出不同说话人的语音,也可以通过模糊聚类的方法描述不同的时频单元,然后计算浮点型屏蔽值后分离混叠语音。基于深层聚类的方法和基于 PIT 的方法有着各自的优势,为了更有效的解决问题,可能需要将两种方法有效的结合。

6. 基于对抗网络的语音增强

在深度学习生成模型方面的最新突破是生成对抗网络,GAN 在计算机视觉领域生成逼真图像上取得巨大成功,可以生成像素级、复杂分布的图像。GAN 还没有广泛应用于语音生成问题。本文介绍一种基于对抗网络的语音增强方法。这种方法提供了一种快速增强处理方法,不需要因果关系,没有 RNN 中类似的递归操作;直接处理原始音频的端到端方法,不需要手工提取特征,无需对原始数据做明显假设;从不同说话者和不同类型噪声中学习,并将它们结合在一起形成相同的共享参数,使得系统简单且泛化能力较强。

语音增强问题是由输入含噪信号得到增强信号,这种方法通过语音增强 GAN 实现,其中生成网络用于增强。它的输入是含噪语音信号和潜在表征信号,输出是增强后的信号。将生成器设计为全部是卷积层(没有全连接层),这么做可以减少训练参数从而缩短了训练时间。生成网络的一个重要特点是端到端结构,直接处理原始语音信号,避免了通过中间变换提取声学特征。在训练过程中,鉴别器负责向生成器发送输入数据中真伪信息,使得生成器可以将其输出波形朝着真实的分布微调,从而消除干扰信号。

 

三、总结

本文围绕着近年来深度学习在语音合成和语音增强问题中的新方法展开介绍,虽然语音合成和语音增强需要解决的问题不同,但是在建模方法上有很多相通之处,可以相互借鉴。深度学习方法在语音转换、语音带宽扩展等领域也有着广泛的应用,感兴趣的读者可以关注这一领域最新的研究成果。虽然深度学习的快速发展推动了智能语音产品的落地,但是仍有些问题不能依赖于深度学习方法彻底解决,例如提高合成语音的表现力、提高增强后语音的可懂度,需要在对输入输出特征的物理含义深入理解的基础上,有效的表征信息,选择合适的方法进行建模。

刘斌:中科院自动化所博士,极限元资深智能语音算法专家,中科院-极限元智能交互联合实验室核心技术人员,在国际顶级会议上发表多篇文章,获得多项关于语音及音频领域的专利,具有丰富的工程经验,擅长语音信号处理和深度学习,提供有效的技术解决方案。


2018-12-21 00:45:04 weixin_37993251 阅读数 270
  • 深度学习入门及如何转型AI领域

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

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

本文为《Python深度学习》的学习笔记。

第7章 生成式深度学习

  • 使用LSTM生成文本
  • 实现DeepDream
  • 实现神经风格迁移
  • 变分自编码器
  • 了解生成式对抗网络

8.1 使用LSTM生成文本

8.1.1 生成式循环网络简史

Schmidhuber:LSTM 1997
循环升级完了已经被成功用于音乐生成、对话生成、图像生成、语音合成和分子设计。

8.1.2 如何生产序列数据

使用前面的标记作为输入,迅雷一个网络来预测序列中接下来一个或多个标记。字符级的神经语言模型

8.1.3 采样策略的重要性

贪婪采样
随机采样

# 8-1 对于不同的softmax温度,对概率分布重新加权
import numpy as np

def reweight_distribution(original_distribution, temperature = 0.5):
    distribution = np.log(original_distribution) / temperature
    distribution = np.exp(distribution)
    return distribution / np.sum(distribution)

更高的温度会得到熵更大的采样分布。

8.1.4 实现字符级的LSTM文本生成

  1. 准备数据
# 8-2 下载并解析初始文本文件
import keras
import numpy as np

path = keras.utils.get_file(
    'nietzsche.txt',
    origin='https://s3.amazonaws.com/text-datasets/nietzsche.txt')
text = open(path).read().lower()
print('Corpus length:', len(text))

选长度为maxlen的序列,进行one-hot编码

# 8-3 将字符序列向量化
maxlen = 60 # 提取60个字符组成的序列

step = 3 # 每3个字符采集一个新序列

sentences = [] # 保存所提取的序列

next_chars = [] # 保存目标

for i in range(0, len(text) - maxlen, step):
    sentences.append(text[i: i + maxlen])
    next_chars.append(text[i + maxlen])
    
print('Number of sentences:', len(sentences))

# 将唯一字符映射在列表中
chars = sorted(list(set(text)))
print('Unique characters:', len(chars))
char_indices = dict((char, chars.index(char)) for char in chars)

# one-hot编码
print('Vectorization')
x = np.zeros((len(sentences), maxlen, len(chars)), dtype = np.bool)
y = np.zeros((len(sentences), len(chars)), dtype = np.bool)
for i, sentence in enumerate(sentences):
    for t, char in enumerate(sentence):
        x[i, t, char_indices[char]] = 1
    y[i, char_indices[next_chars[i]]] = 1

Number of sentences: 200278
Unique characters: 58
Vectorization

  1. 构建网络
# 8-4 用于预测下一个字符的单层LSTM模型
from keras import layers

model = keras.models.Sequential()
model.add(layers.LSTM(128, input_shape=(maxlen, len(chars))))
model.add(layers.Dense(len(chars), activation='softmax'))

经过one-hot编码,使用categorical_crossentropy作为损失。

# 8-5 模型编译配置
optimizer = keras.optimizers.RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)
  1. 训练语言模型并从中采样
  • 给定已经生成的文本,从模型中得到下一个字符的概率分布
  • 根据某个温度对分布进行重新加权
  • 根据重新加权后的分布对下一个字符进行随机采样
  • 将新字符添加到文本末尾

最后,循环反复训练并生成文本。

# 8-7 文本生成循环
import random
import sys

for epoch in range(1, 60):
    print('epoch', epoch)
    # Fit the model for 1 epoch on the available training data
    model.fit(x, y,
              batch_size=128,
              epochs=1)

    # Select a text seed at random
    start_index = random.randint(0, len(text) - maxlen - 1)
    generated_text = text[start_index: start_index + maxlen]
    print('--- Generating with seed: "' + generated_text + '"')

    for temperature in [0.2, 0.5, 1.0, 1.2]:
        print('------ temperature:', temperature)
        sys.stdout.write(generated_text)

        # We generate 400 characters
        for i in range(400):
            sampled = np.zeros((1, maxlen, len(chars)))
            for t, char in enumerate(generated_text):
                sampled[0, t, char_indices[char]] = 1.

            preds = model.predict(sampled, verbose=0)[0]
            next_index = sample(preds, temperature)
            next_char = chars[next_index]

            generated_text += next_char
            generated_text = generated_text[1:]

            sys.stdout.write(next_char)
            sys.stdout.flush()
        print()

8.2 DeepDream

8.2.1 用keras实现DeepDream

Keras中有许多预训练的卷积神经网络。这里使用Keras内置的Incetion V3模型。

# 8-8 加载预训练的InceptionV3模型
from keras.applications import inception_v3
from keras import backend as K

# We will not be training our model,
# so we use this command to disable all training-specific operations
K.set_learning_phase(0)

# Build the InceptionV3 network.
# The model will be loaded with pre-trained ImageNet weights.
model = inception_v3.InceptionV3(weights='imagenet',
                                 include_top=False)

计算损失(loss)

# 8-9 设置DeepDream配置
# Dict mapping layer names to a coefficient
# quantifying how much the layer's activation
# will contribute to the loss we will seek to maximize.
# Note that these are layer names as they appear
# in the built-in InceptionV3 application.
# You can list all layer names using `model.summary()`.
layer_contributions = {
    'mixed2': 0.2,
    'mixed3': 3.,
    'mixed4': 2.,
    'mixed5': 1.5,
}

接下来定义一个包含损失的张量,在激活的L2范数的加权求和。

# 8-10 定义需要最大化的损失
# Get the symbolic outputs of each "key" layer (we gave them unique names).
layer_dict = dict([(layer.name, layer) for layer in model.layers])

# Define the loss.
loss = K.variable(0.)
for layer_name in layer_contributions:
    # Add the L2 norm of the features of a layer to the loss.
    coeff = layer_contributions[layer_name]
    activation = layer_dict[layer_name].output

    # We avoid border artifacts by only involving non-border pixels in the loss.
    scaling = K.prod(K.cast(K.shape(activation), 'float32'))
    loss += coeff * K.sum(K.square(activation[:, 2: -2, 2: -2, :])) / scaling

设置梯度上升过程

# 8-11 梯度上升过程
# This holds our generated image
dream = model.input

# Compute the gradients of the dream with regard to the loss.
grads = K.gradients(loss, dream)[0]

# Normalize gradients.
grads /= K.maximum(K.mean(K.abs(grads)), 1e-7)

# Set up function to retrieve the value
# of the loss and gradients given an input image.
outputs = [loss, grads]
fetch_loss_and_grads = K.function([dream], outputs)

def eval_loss_and_grads(x):
    outs = fetch_loss_and_grads([x])
    loss_value = outs[0]
    grad_values = outs[1]
    return loss_value, grad_values

def gradient_ascent(x, iterations, step, max_loss=None):
    for i in range(iterations):
        loss_value, grad_values = eval_loss_and_grads(x)
        if max_loss is not None and loss_value > max_loss:
            break
        print('...Loss value at', i, ':', loss_value)
        x += step * grad_values
    return x

下面对图片进行梯度上升

# 8-12 在多个连续尺度上运行梯度上升
import numpy as np

# Playing with these hyperparameters will also allow you to achieve new effects

step = 0.01  # Gradient ascent step size
num_octave = 3  # Number of scales at which to run gradient ascent
octave_scale = 1.4  # Size ratio between scales
iterations = 20  # Number of ascent steps per scale

# If our loss gets larger than 10,
# we will interrupt the gradient ascent process, to avoid ugly artifacts
max_loss = 10.

# Fill this to the path to the image you want to use
base_image_path = 'C:/Users/adward/Desktop/deeplearning_with_python/Deeplearning_with_python/original_photo_deep_dream.jpg'

# Load the image into a Numpy array
img = preprocess_image(base_image_path)

# We prepare a list of shape tuples
# defining the different scales at which we will run gradient ascent
original_shape = img.shape[1:3]
successive_shapes = [original_shape]
for i in range(1, num_octave):
    shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shape])
    successive_shapes.append(shape)

# Reverse list of shapes, so that they are in increasing order
successive_shapes = successive_shapes[::-1]

# Resize the Numpy array of the image to our smallest scale
original_img = np.copy(img)
shrunk_original_img = resize_img(img, successive_shapes[0])

for shape in successive_shapes:
    print('Processing image shape', shape)
    img = resize_img(img, shape)
    img = gradient_ascent(img,
                          iterations=iterations,
                          step=step,
                          max_loss=max_loss)
    upscaled_shrunk_original_img = resize_img(shrunk_original_img, shape)
    same_size_original = resize_img(original_img, shape)
    lost_detail = same_size_original - upscaled_shrunk_original_img

    img += lost_detail
    shrunk_original_img = resize_img(original_img, shape)
    save_img(img, fname='dream_at_scale_' + str(shape) + '.png')

save_img(img, fname='final_dream.png')

辅助函数SciPy

# 8-13 辅助函数
import scipy
from keras.preprocessing import image

def resize_img(img, size):
    img = np.copy(img)
    factors = (1,
               float(size[0]) / img.shape[1],
               float(size[1]) / img.shape[2],
               1)
    return scipy.ndimage.zoom(img, factors, order=1)


def save_img(img, fname):
    pil_img = deprocess_image(np.copy(img))
    scipy.misc.imsave(fname, pil_img)


def preprocess_image(image_path):
    # Util function to open, resize and format pictures
    # into appropriate tensors.
    img = image.load_img(image_path)
    img = image.img_to_array(img)
    img = np.expand_dims(img, axis=0)
    img = inception_v3.preprocess_input(img)
    return img


def deprocess_image(x):
    # Util function to convert a tensor into a valid image.
    if K.image_data_format() == 'channels_first':
        x = x.reshape((3, x.shape[2], x.shape[3]))
        x = x.transpose((1, 2, 0))
    else:
        x = x.reshape((x.shape[1], x.shape[2], 3))
    x /= 2.
    x += 0.5
    x *= 255.
    x = np.clip(x, 0, 255).astype('uint8')
    return x
from matplotlib import pyplot as plt

plt.imshow(deprocess_image(np.copy(img)))
plt.show()

8.3 神经风格迁移

将参考图像的风格应用于目标图像。
loss = distance(style(reference_image) - style(generated_image)) + distance(content(original_image) - content(generated_image))
在这里插入图片描述

8.3.1 内容损失

网络更靠近底部的层包含关于图片的局部信息,靠近顶部的层则包含更加全局。

8.3.2 风格损失

Gram matrix——特征图的内积

8.3.3 用Keras实现神经风格迁移

VGG19

# 8-14 定义初始变量
from keras.preprocessing.image import load_img, img_to_array

# This is the path to the image you want to transform.
target_image_path = 'C:/Users/adward/Desktop/deeplearning_with_python/Deeplearning_with_python/original_photo_deep_dream.jpg'
# This is the path to the style image.
style_reference_image_path = 'C:/Users/adward/Desktop/deeplearning_with_python/Deeplearning_with_python/popova.jpg'

# Dimensions of the generated picture.
width, height = load_img(target_image_path).size
img_height = 400
img_width = int(width * img_height / height)

需要一些辅助函数,对VGG19卷积神经网络的图像进行加载、预处理和后处理。

# 8-15 辅助函数
import numpy as np
from keras.applications import vgg19

def preprocess_image(image_path):
    img = load_img(image_path, target_size=(img_height, img_width))
    img = img_to_array(img)
    img = np.expand_dims(img, axis=0)
    img = vgg19.preprocess_input(img)
    return img

def deprocess_image(x):
    # Remove zero-center by mean pixel
    x[:, :, 0] += 103.939
    x[:, :, 1] += 116.779
    x[:, :, 2] += 123.68
    # 'BGR'->'RGB'
    x = x[:, :, ::-1]
    x = np.clip(x, 0, 255).astype('uint8')
    return x

下面构建VGG19网络。

# 8-16 加载预训练的VGG19网络,应用于三张图片
from keras import backend as K

target_image = K.constant(preprocess_image(target_image_path))
style_reference_image = K.constant(preprocess_image(style_reference_image_path))

# This placeholder will contain our generated image
combination_image = K.placeholder((1, img_height, img_width, 3))

# We combine the 3 images into a single batch
input_tensor = K.concatenate([target_image,
                              style_reference_image,
                              combination_image], axis=0)

# We build the VGG19 network with our batch of 3 images as input.
# The model will be loaded with pre-trained ImageNet weights.
model = vgg19.VGG19(input_tensor=input_tensor,
                    weights='imagenet',
                    include_top=False)
print('Model loaded.')

定义内容损失(均方误差)和风格损失(gram_matrix矩阵)。

# 8-17 内容损失
def content_loss(base, combination):
    return K.sum(K.square(combination - base))
# 8-18 风格损失
def gram_matrix(x):
    features = K.batch_flatten(K.permute_dimensions(x, (2, 0, 1)))
    gram = K.dot(features, K.transpose(features))
    return gram


def style_loss(style, combination):
    S = gram_matrix(style)
    C = gram_matrix(combination)
    channels = 3
    size = img_height * img_width
    return K.sum(K.square(S - C)) / (4. * (channels ** 2) * (size ** 2))

再添加第三个——总变差损失,对生成的组合图形进行操作,避免图形过度像素化,理解为正则化损失。

# 总变差损失
def total_variation_loss(x):
    a = K.square(
        x[:, :img_height - 1, :img_width - 1, :] - x[:, 1:, :img_width - 1, :])
    b = K.square(
        x[:, :img_height - 1, :img_width - 1, :] - x[:, :img_height - 1, 1:, :])
    return K.sum(K.pow(a + b, 1.25))

需要最小化的损失是这三项损失的加权平均。计算内容损失,只是用一个靠顶部的层,即block5_conv2层;风格损失则需要添加总变差损失。

# 8-20 定义需要最小化的最终损失
# Dict mapping layer names to activation tensors
outputs_dict = dict([(layer.name, layer.output) for layer in model.layers])
# Name of layer used for content loss
content_layer = 'block5_conv2'
# Name of layers used for style loss
style_layers = ['block1_conv1',
                'block2_conv1',
                'block3_conv1',
                'block4_conv1',
                'block5_conv1']
# Weights in the weighted average of the loss components
total_variation_weight = 1e-4
style_weight = 1.
content_weight = 0.025

# Define the loss by adding all components to a `loss` variable
loss = K.variable(0.)
layer_features = outputs_dict[content_layer]
target_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]
loss += content_weight * content_loss(target_image_features,
                                      combination_features)
for layer_name in style_layers:
    layer_features = outputs_dict[layer_name]
    style_reference_features = layer_features[1, :, :, :]
    combination_features = layer_features[2, :, :, :]
    sl = style_loss(style_reference_features, combination_features)
    loss += (style_weight / len(style_layers)) * sl
loss += total_variation_weight * total_variation_loss(combination_image)

这里我们重新设置梯度下降过程。

# 8-21 设置梯度下降过程
# Get the gradients of the generated image wrt the loss
grads = K.gradients(loss, combination_image)[0]

# Function to fetch the values of the current loss and the current gradients
fetch_loss_and_grads = K.function([combination_image], [loss, grads])


class Evaluator(object):

    def __init__(self):
        self.loss_value = None
        self.grads_values = None

    def loss(self, x):
        assert self.loss_value is None
        x = x.reshape((1, img_height, img_width, 3))
        outs = fetch_loss_and_grads([x])
        loss_value = outs[0]
        grad_values = outs[1].flatten().astype('float64')
        self.loss_value = loss_value
        self.grad_values = grad_values
        return self.loss_value

    def grads(self, x):
        assert self.loss_value is not None
        grad_values = np.copy(self.grad_values)
        self.loss_value = None
        self.grad_values = None
        return grad_values

evaluator = Evaluator()
# 8-22 风格迁移循环
from scipy.optimize import fmin_l_bfgs_b
from scipy.misc import imsave
import time

result_prefix = 'style_transfer_result'
iterations = 20

# Run scipy-based optimization (L-BFGS) over the pixels of the generated image
# so as to minimize the neural style loss.
# This is our initial state: the target image.
# Note that `scipy.optimize.fmin_l_bfgs_b` can only process flat vectors.
x = preprocess_image(target_image_path)
x = x.flatten()
for i in range(iterations):
    print('Start of iteration', i)
    start_time = time.time()
    x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x,
                                     fprime=evaluator.grads, maxfun=20)
    print('Current loss value:', min_val)
    # Save current generated image
    img = x.copy().reshape((img_height, img_width, 3))
    img = deprocess_image(img)
    fname = result_prefix + '_at_iteration_%d.png' % i
    imsave(fname, img)
    end_time = time.time()
    print('Image saved as', fname)
    print('Iteration %d completed in %ds' % (i, end_time - start_time))
from matplotlib import pyplot as plt

# Content image
plt.imshow(load_img(target_image_path, target_size=(img_height, img_width)))
plt.figure()

# Style image
plt.imshow(load_img(style_reference_image_path, target_size=(img_height, img_width)))
plt.figure()

# Generate image
plt.imshow(img)
plt.show()

在这里插入图片描述

8.4 用变分自编码器生成图像

8.4.1 从图像的潜在空间采样

在这里插入图片描述
这里想找到一个低维的潜在空间,其中任一点都能映射为一张逼真的图像。叫做生成器和解码器。
在这里插入图片描述

8.4.2 图像编辑的概念向量

给定一个图像的潜在空间,可能潜在点z是某张人脸的嵌入表示。

8.4.3 变分自编码器

# Encode the input into a mean and variance parameter
z_mean, z_log_variance = encoder(input_img)

# Draw a latent point using a small random epsilon
z = z_mean + exp(z_log_variance) * epsilon

# Then decode z back to an image
reconstructed_img = decoder(z)

# Instantiate a model
model = Model(input_img, reconstructed_img)

# Then train the model using 2 losses:
# a reconstruction loss and a regularization loss

使用和重构损失和正则化损失。

# 8-23 VAE编码器网络
import keras
from keras import layers
from keras import backend as K
from keras.models import Model
import numpy as np

img_shape = (28, 28, 1)
batch_size = 16
latent_dim = 2  # Dimensionality of the latent space: a plane

input_img = keras.Input(shape=img_shape)

x = layers.Conv2D(32, 3,
                  padding='same', activation='relu')(input_img)
x = layers.Conv2D(64, 3,
                  padding='same', activation='relu',
                  strides=(2, 2))(x)
x = layers.Conv2D(64, 3,
                  padding='same', activation='relu')(x)
x = layers.Conv2D(64, 3,
                  padding='same', activation='relu')(x)
shape_before_flattening = K.int_shape(x)

x = layers.Flatten()(x)
x = layers.Dense(32, activation='relu')(x)

z_mean = layers.Dense(latent_dim)(x)
z_log_var = layers.Dense(latent_dim)(x)

接下来生成潜在空间。

# 8-24 潜在空间采样的函数
def sampling(args):
    z_mean, z_log_var = args
    epsilon = K.random_normal(shape=(K.shape(z_mean)[0], latent_dim),
                              mean=0., stddev=1.)
    return z_mean + K.exp(z_log_var) * epsilon

z = layers.Lambda(sampling)([z_mean, z_log_var])
# 8-25 VAE解码器网络,将潜在空间点映射为图像
# This is the input where we will feed `z`.
decoder_input = layers.Input(K.int_shape(z)[1:])

# Upsample to the correct number of units
x = layers.Dense(np.prod(shape_before_flattening[1:]),
                 activation='relu')(decoder_input)

# Reshape into an image of the same shape as before our last `Flatten` layer
x = layers.Reshape(shape_before_flattening[1:])(x)

# We then apply then reverse operation to the initial
# stack of convolution layers: a `Conv2DTranspose` layers
# with corresponding parameters.
x = layers.Conv2DTranspose(32, 3,
                           padding='same', activation='relu',
                           strides=(2, 2))(x)
x = layers.Conv2D(1, 3,
                  padding='same', activation='sigmoid')(x)
# We end up with a feature map of the same size as the original input.

# This is our decoder model.
decoder = Model(decoder_input, x)

# We then apply it to `z` to recover the decoded `z`.
z_decoded = decoder(z)

编写一个自定义损失

# 8=26 用于计算VAE损失的自定义层
class CustomVariationalLayer(keras.layers.Layer):

    def vae_loss(self, x, z_decoded):
        x = K.flatten(x)
        z_decoded = K.flatten(z_decoded)
        xent_loss = keras.metrics.binary_crossentropy(x, z_decoded)
        kl_loss = -5e-4 * K.mean(
            1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
        return K.mean(xent_loss + kl_loss)

    def call(self, inputs):
        x = inputs[0]
        z_decoded = inputs[1]
        loss = self.vae_loss(x, z_decoded)
        self.add_loss(loss, inputs=inputs)
        # We don't use this output.
        return x

# We call our custom layer on the input and the decoded output,
# to obtain the final model output.
y = CustomVariationalLayer()([input_img, z_decoded])

最后,实例化训练。

# 8-27 训练VAE
from keras.datasets import mnist

vae = Model(input_img, y)
vae.compile(optimizer='rmsprop', loss=None)
vae.summary()

# Train the VAE on MNIST digits
(x_train, _), (x_test, y_test) = mnist.load_data()

x_train = x_train.astype('float32') / 255.
x_train = x_train.reshape(x_train.shape + (1,))
x_test = x_test.astype('float32') / 255.
x_test = x_test.reshape(x_test.shape + (1,))

vae.fit(x=x_train, y=None,
        shuffle=True,
        epochs=10,
        batch_size=batch_size,
        validation_data=(x_test, None))

在这里插入图片描述

# 8-28 从二维潜在空间中采样一组点的网络,并解码为图像
import matplotlib.pyplot as plt
from scipy.stats import norm

# Display a 2D manifold of the digits
n = 15  # figure with 15x15 digits
digit_size = 28
figure = np.zeros((digit_size * n, digit_size * n))
# Linearly spaced coordinates on the unit square were transformed
# through the inverse CDF (ppf) of the Gaussian
# to produce values of the latent variables z,
# since the prior of the latent space is Gaussian
grid_x = norm.ppf(np.linspace(0.05, 0.95, n))
grid_y = norm.ppf(np.linspace(0.05, 0.95, n))

for i, yi in enumerate(grid_x):
    for j, xi in enumerate(grid_y):
        z_sample = np.array([[xi, yi]])
        z_sample = np.tile(z_sample, batch_size).reshape(batch_size, 2)
        x_decoded = decoder.predict(z_sample, batch_size=batch_size)
        digit = x_decoded[0].reshape(digit_size, digit_size)
        figure[i * digit_size: (i + 1) * digit_size,
               j * digit_size: (j + 1) * digit_size] = digit

plt.figure(figsize=(10, 10))
plt.imshow(figure, cmap='Greys_r')
plt.show()

在这里插入图片描述

8.5 生成式对抗网络

生成器网路(generator network)
判别器网路(discriminatory network)

8.5.1 GAN的简要实现流程

  1. generator网络将行政为(latent_dim,)的向量映射到形状为(32,32,3)的图像。
  2. discriminator网络将形状为(32,32,3)的图像映射到一个二进制分数,用于评估图像为真的概率。
  3. gan网络将g网络和d网络连接在一起:gan(x) = d(g(x))
  4. 使用真、假标签的真假图像样本来训练判别器
  5. 训练生成器,使用gan模型的损失相对于生成器权重的梯度。训练生成器来欺骗判别器。

8.5.2 大量技巧

调节GAN过程比较困难,下面是一些技巧:

  1. 使用tanh作为生成器最后一层的激活,而不用sigmoid
  2. 使用正太分布对潜在空间的点进行采样,而不用均匀分布
  3. 随机性能提高稳健性。训练GAN得到一个动态平衡。在判别器中使用dropout,想判别器的标签加入随机噪声。
  4. 稀疏的梯度会妨碍训练。使用LeakyReLu代替ReLU激活
  5. 使用Conv2DTranspose,内核大小要被步幅大小整除

8.5.3 生成器

# 8-29 GAN生成器网络
import keras
from keras import layers
import numpy as np

latent_dim = 32
height = 32
width = 32
channels = 3

generator_input = keras.Input(shape=(latent_dim,))

# First, transform the input into a 16x16 128-channels feature map
x = layers.Dense(128 * 16 * 16)(generator_input)
x = layers.LeakyReLU()(x)
x = layers.Reshape((16, 16, 128))(x)

# Then, add a convolution layer
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)

# Upsample to 32x32
x = layers.Conv2DTranspose(256, 4, strides=2, padding='same')(x)
x = layers.LeakyReLU()(x)

# Few more conv layers
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(256, 5, padding='same')(x)
x = layers.LeakyReLU()(x)

# Produce a 32x32 1-channel feature map
x = layers.Conv2D(channels, 7, activation='tanh', padding='same')(x)
generator = keras.models.Model(generator_input, x)
generator.summary()

在这里插入图片描述

8.5.4 判别器

接受一张候选图像(真实或者合成的)作为输入,然后划分成“生成图像”或者是“来自训练集的真实图像”。

# 8-30 GAN判别器网络
discriminator_input = layers.Input(shape=(height, width, channels))
x = layers.Conv2D(128, 3)(discriminator_input)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128, 4, strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Flatten()(x)

# One dropout layer - important trick!
x = layers.Dropout(0.4)(x)

# Classification layer
x = layers.Dense(1, activation='sigmoid')(x)

discriminator = keras.models.Model(discriminator_input, x)
discriminator.summary()

# To stabilize training, we use learning rate decay
# and gradient clipping (by value) in the optimizer.
discriminator_optimizer = keras.optimizers.RMSprop(lr=0.0008, clipvalue=1.0, decay=1e-8)
discriminator.compile(optimizer=discriminator_optimizer, loss='binary_crossentropy')

在这里插入图片描述

8.5.5 对抗网络

# 8-31 对抗网络
# Set discriminator weights to non-trainable
# (will only apply to the `gan` model)
discriminator.trainable = False

gan_input = keras.Input(shape=(latent_dim,))
gan_output = discriminator(generator(gan_input))
gan = keras.models.Model(gan_input, gan_output)

gan_optimizer = keras.optimizers.RMSprop(lr=0.0004, clipvalue=1.0, decay=1e-8)
gan.compile(optimizer=gan_optimizer, loss='binary_crossentropy')

8.5.6 如何训练DCGAN

# 8-32 实现GAN的训练
import os
from keras.preprocessing import image

# Load CIFAR10 data
(x_train, y_train), (_, _) = keras.datasets.cifar10.load_data()

# Select frog images (class 6)
x_train = x_train[y_train.flatten() == 6]

# Normalize data
x_train = x_train.reshape(
    (x_train.shape[0],) + (height, width, channels)).astype('float32') / 255.

iterations = 10000
batch_size = 20
save_dir = '/home/ubuntu/gan_images/'

# Start training loop
start = 0
for step in range(iterations):
    # Sample random points in the latent space
    random_latent_vectors = np.random.normal(size=(batch_size, latent_dim))

    # Decode them to fake images
    generated_images = generator.predict(random_latent_vectors)

    # Combine them with real images
    stop = start + batch_size
    real_images = x_train[start: stop]
    combined_images = np.concatenate([generated_images, real_images])

    # Assemble labels discriminating real from fake images
    labels = np.concatenate([np.ones((batch_size, 1)),
                             np.zeros((batch_size, 1))])
    # Add random noise to the labels - important trick!
    labels += 0.05 * np.random.random(labels.shape)

    # Train the discriminator
    d_loss = discriminator.train_on_batch(combined_images, labels)

    # sample random points in the latent space
    random_latent_vectors = np.random.normal(size=(batch_size, latent_dim))

    # Assemble labels that say "all real images"
    misleading_targets = np.zeros((batch_size, 1))

    # Train the generator (via the gan model,
    # where the discriminator weights are frozen)
    a_loss = gan.train_on_batch(random_latent_vectors, misleading_targets)
    
    start += batch_size
    if start > len(x_train) - batch_size:
      start = 0

    # Occasionally save / plot
    if step % 100 == 0:
        # Save model weights
        gan.save_weights('gan.h5')

        # Print metrics
        print('discriminator loss at step %s: %s' % (step, d_loss))
        print('adversarial loss at step %s: %s' % (step, a_loss))

        # Save one generated image
        img = image.array_to_img(generated_images[0] * 255., scale=False)
        img.save(os.path.join(save_dir, 'generated_frog' + str(step) + '.png'))

        # Save one real image, for comparison
        img = image.array_to_img(real_images[0] * 255., scale=False)
        img.save(os.path.join(save_dir, 'real_frog' + str(step) + '.png'))
import matplotlib.pyplot as plt

# Sample random points in the latent space
random_latent_vectors = np.random.normal(size=(10, latent_dim))

# Decode them to fake images
generated_images = generator.predict(random_latent_vectors)

for i in range(generated_images.shape[0]):
    img = image.array_to_img(generated_images[i] * 255., scale=False)
    plt.figure()
    plt.imshow(img)
    
plt.show()
2019-07-12 16:53:40 wangwei19871103 阅读数 303
  • 深度学习入门及如何转型AI领域

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

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

深度学习-李宏毅GAN学习之改进序列生成器

序列生成器

很多情况下,我们需要的生成器是序列模型,比如语音识别,机器翻译,机器人聊天,今天我们要用GAN的技术来改进他们,他们都可以看成是一个条件生成器,输入一个信息,根据这个信息输出一个信息:
在这里插入图片描述

机器人聊天问题

举个机器人聊天的例子,传统做法就是训练一个编码器,解码器,输入一段文字,希望输出的跟给定的文字越接近越好,这里的例子训练的输入时How are you输出是I’m good,我们希望模型输出的是I’m good.的概率越大越好,但是事实上可能会出现Not badI’m John两种回答,以人来判断,回答前者是好的,但是机器来判断是后者,因为里面包含I’m,符合最大似然估计的标准,这里补充个知识,最大似然函数就等价于最小交叉熵。
在这里插入图片描述

改进序列模型

在这里插入图片描述
有另种方式改进序列模型,一种是强化学习,一种是GAN,其实这里强化学习也可以算是GAN的一种特殊形式。先讲讲强化学习的做法:
在这里插入图片描述
如上图,让机器来跟人对话,然后根据机器的输出,人来给奖励,好的回答给正的,不好的给负的。机器希望能得到奖励分数越大越好。
在这里插入图片描述
上图就是机器希望最大化的奖励的期望,根据的是策略梯度的算法。输入一个c,输出一个奖励,然后把输入和输出给人来打分,希望奖励R(c,x)越大越好。以后的公式把输入c换成了h,就改了字母,其他一样,结构如下:
在这里插入图片描述
我们希望找出一组参数θ,使得生成的x和输入的h的奖励R(h,c)越高越好。即θ使希望R的期望越大越好,期望公式意思就是在给定输入h的情况下得到x的概率乘以相应的奖励累加起来,再乘以输入h出现的概率,再累加起来,即是在输入h,生成x,R(h,x)的期望,公式可能比较复杂,但是分开来还是好理解的,就是期望的概念,可能性乘以结果,累加起来。只是输入h有多重可能性,在输入h下生成x也有多重可能性,即h服从P(h),x服从Pθ(x|h),合起来写就转换成下面的式子。
在这里插入图片描述
期望怎么算呢,理论上要遍历所有的h,所有的x,但是实际上做不到啊,只能通过采样的方式啦,h和x我们都是可以采样到的,我们可以采样很多的(h,x)的样本。那就变成:
在这里插入图片描述
但是貌似参数θ不见了,其实他就是决定(h,x)的样本啦,在采样的时候已经藏进去了,不同的θ对应不同的样本。那问题来了,θ不见了,那这个式子怎么做优化呢,我们可以先算梯度,再算期望啦,先做恒等变换,凑出θ项:
在这里插入图片描述
然后根据log(f)=f/flog(f)=f'/f得到:

在这里插入图片描述

最终得到:在这里插入图片描述
这样我们就可以用梯度上升做优化:
在这里插入图片描述
从上面可以看到,如果奖励是正的,我们希望这样的匹配概率越大越好,如果是负的,希望越小越好。
归结起来就是这样啦,下面的c就是上面的h,这个就不改了,知道就好,要注意的是,每次θ更新后,需要重新去采样数据,因为所有的x是基于θ产生的,而且奖励是跟人互动的,收集数据的成本很高,所以这个环节要仔细点,不然可能会出问题:
在这里插入图片描述
然后我们来看一下最大似然估计和强化学习对这个的区别:
在这里插入图片描述
可以看到强化学习比最大似然估计多了个R,可以看做权重,也就是说每个数据对都是有权重的,最大似然估计的权重都是1.因为强化学习的数据对中的x都是机器自己生产的,回答是有好有坏的,有了权重之后,可以给好的数据正的权重,给不好的数据负的权重。这里有讨论一个为什么R是有正有负的,因为对于某个数据对的概率来说是在0-1之间的,如果有数据对的概率提高了,那必定有其他的概率下降了,不可能所有的都提高。还有就是都是正的理论上是没问题,但是这样就必须要要穷举所有的情况,把一些概率调低,但是也是正的,实际上做不到穷举所有情况,所以又正有负是比较好的做法,如果你的R都是正的,那减去某个数,让他有正有负训练起来比较好点。

在这里插入图片描述
上图说明了,在真实的情况里,我们不可能用人来跟机器进行强化学习,我们用两个机器自己来相互训练,但是这样就有个问题,如何评价结果的好坏,即如何评估R(h,x),所以我们引入了GAN。

在这里插入图片描述
引入GAN之后,本来需要人来给的奖励交给了判别器,判别器是经过很多人类对话训练的,能分辨出哪些好与不好,类似CGAN的思想,把输入和输出一起给判别器,只要Chatbot生成器能骗过判别器,那就是好的回答,算法如下:
在这里插入图片描述
算法还是很清楚的,跟训练GAN一样,但是会有个问题,因为我们的生成模型是序列模型,在输入给判别器之前是进行采样的,所以判别器更新的时候没办法微分,因为采样对结果的影响不确定,有没想记得EAV好像也右类似的情况,后来用了个小技巧可以微分了。
在这里插入图片描述
那如何来来解决呢,我们有三种方法:
在这里插入图片描述

有种叫Gumbel-softmax具体的可以去找资料看看是怎么解决的:
在这里插入图片描述
第二种Continuous Input for Discriminator:
直接不需要采样的过程,直接给单词中的分布,产生连续的数据,这样就可以微分(这里我也不是很明白):
在这里插入图片描述
但是这样也是会有问题的,因为你从分布里取出来的数据是连续的,而判别器里学到的真实的数据是one-hot的,生成的是连续的,所以判别器的准则可能就变成了one-hot是真,其他是假,就很容易判别出生成器的东西都是假的,然后生成器就会学着把连续的变成one-hot了,随便找一维变1,其他0,也不管语义了,能骗过判别器就好,这样的生成器不是我们要的。但是有个办法或许可以解决,就是用WGAN,因为他对模型有个约束1-Lipschitz,可以看这篇文章。这样或许判别器比较不容易找出两个之间的差别,容易练起来。
在这里插入图片描述
第三种方法就是强化学习,将判别器的任务看成一个强化学习的奖励函数,同样对好的输入,输出都是越大越好:
在这里插入图片描述
用强化学习的方式来训练生成器,只是把奖励函数换成了判别器的输出,目标一样,然后判别器用很多人的对话和生成器产生的对话来训练:
在这里插入图片描述
我们生成器每一步在做训练的时候就是换成强化学习的策略梯度,只是把奖励换成了判别器的输出:
在这里插入图片描述
在这里插入图片描述
我们看到上面的例子,如果回答了'I don't know',应该是要调低这个的几率的,但是把这个分解开,用语言的序列模型可以看到第一项是生成'I'的几率也被下调了,其实第一个生成'I'是合理的,后面两项是不合理的,这样训练可能就有问题啦,不过实际上我们采样的数据够多的话,不会有问题,因为还是有很多正的样本的,比如:
在这里插入图片描述
这个的时候几率是上升的,刚好和前面有问题的抵消了,但是也只是在样本够多的理论情况下。有没有解决方案呢,当然有啦,但是貌似计算量会比较大:
在这里插入图片描述
上图的就是说,既然可能会出现整体句子出现几率被下降而影响里面的词出现几率下降,那能不能就研究每个词的几率呢,就是上面的变换,根据每次词出现的好坏来影响个别词出现的几率,然后增加好的词的几率,降低不好的词的几率,当然都是符合条件概率的,即是那个语言模型。还有个方法有兴趣的可以看看:
在这里插入图片描述
再来看下最大似然估计和GAN产生出来的实验数据:
在这里插入图片描述
会发现,最大似然的回答比较模糊,可能是因为他要去最大可能性的去迎合所有的好样本,所以回答就比较模糊,跟学习很多张图片,最大似然他们的结果一样,会模糊。而GAN的话就比较长了,因为他要更加符合真实的分布特征,但是具体是长的句子好还是短的呢,不好说。

总结
总的来说,就是用GAN来强化序列模型,可以减少人力投入,另外强化学习的用途也是很广的。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵,图片来自李宏毅课件,侵删。

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