TensorFlow2-深度学习项目-全-
TensorFlow2 深度学习项目(全)
一、入门:安装和故障排除
为了充分利用这本书,你需要满足以下先决条件:
-
安装 Python 3,Python 的最新版本
-
安装 Jupyter 笔记本
-
安装 TensorFlow 2.0
-
硬安装
-
安装 NumPy
-
安装科学软件
-
安装 Matplotlib
-
安装熊猫
-
安装 Scikit-Learn
本章将帮助你安装所有必要的软件包。它还针对安装过程中可能出现的一些常见错误提供了故障排除提示。
Note
为这些项目创建单独的虚拟环境是一种很好的做法。在安装这里提到的包之前,创建一个虚拟环境并激活它。
安装 Python 3
Python 是一种通用解释型、命令式、面向对象的高级编程语言。它是最古老的编程语言之一。然而,随着机器学习的出现,Python 获得了新生。它已经成为机器学习和深度学习的流行工具。目前,Python 有两个不同的版本——Python 2 和 Python 3。
本书所有项目都使用 Python 3,所以最好确保安装了。
方法 1:从 Python 官方网站直接安装
这种方法适用于 Windows、Linux 和 macOS X 系统。这是标准的安装方法,你可以直接从官方网站下载 Python,然后安装到你的系统上。
-
Go to
https://www.python.org/
and select the Downloads tab. A drop-down menu will appear (see Figure 1-1).图 1-1
Python 官方网站
-
在下拉菜单的右侧,您将看到适用于您的特定系统的最新 Python 版本。第一个按钮提供最新版本的 Python 3。一旦你点击它,下载将开始。
-
下载完成后,双击下载栏中的包。这将启动安装过程。
-
In the dialog box that pops up, select Continue (see Figure 1-2).
图 1-2
Python 安装的介绍窗口
-
In the new dialog box, you will be presented with important information regarding the changes made to Python (see Figure 1-3). Once again, select Continue.
图 1-3
Python 安装的自述窗口
-
Now you will be shown the license agreement for using Python. Select Continue (see Figure 1-4).
图 1-4
Python 安装的许可窗口
-
将出现一个迷你对话框,要求您同意列出的条款和条件。选择同意。
-
选择新 Python 安装的文件路径。
-
Select the type of installation. See Figure 1-5.
图 1-5
Python 安装的安装类型窗口
-
Finally, you will be told how much memory will be used on your system. Select Install (see Figure 1-6).

图 1-6
Python 安装的存储警告窗口
-
该对话框将显示一个进度条,指示安装完成了多少。这应该只需要几分钟,取决于您系统的内存和速度。
-
Once the installation is complete, you will be presented with a dialog box indicating that the installation was successful, as shown in Figure 1-7.

图 1-7
Python 安装的摘要窗口
- To test the installation, open the terminal (see Figure 1-8), type
python 3
, and press Enter.

图 1-8
在终端中运行 Python
-
现在输入命令
print ("Hello World!")
并按回车键。 -
This will print the words
'Hello World!'
in the terminal, as shown in Figure 1-9.

图 1-9
测试 Python 安装
故障排除提示
在 Linux 系统上,你可能会得到一个错误信息,说pip
需要ssl
,如图 1-10 所示。
图 1-10
PIP 需要 SSL 警告
要安装ssl
,请使用以下命令:
sudo apt-get install libssl-dev openssl
完成后,使用以下命令:
sudo make install
Python 现在应该已经成功安装在您的系统上了。
在 Windows 系统上,请按照下列步骤操作:
-
打开终端测试安装。
-
键入
python —version
并按回车键。 -
Python 安装的版本应该会显示出来。
方法 2:使用 Anaconda
这种方法适用于 Windows 和 Linux 系统。Anaconda 是一个桌面图形用户界面(GUI ),允许您启动应用程序并轻松管理 conda 包、环境和通道,而无需使用命令行。
-
Download and install Anaconda from
https://www.anaconda.com/distribution/
. See Figure 1-11.图 1-11
Anaconda 的官方网站
-
选择 Python 3 的图形安装程序,因为它是最容易使用的。
-
In the pop-up window, select Continue. See Figure 1-12.
图 1-12
Anaconda 安装的介绍窗口
-
Read the important information and click Continue. See Figure 1-13.
图 1-13
Anaconda 安装的“自述”窗口
-
Read the license and click Continue to accept it. See Figure 1-14.
图 1-14
Anaconda 安装的许可窗口
-
选择“仅为我安装”(除非您是为所有用户安装),然后单击“下一步”。
-
Select a destination folder to install Anaconda and click the Next button. See Figure 1-15.
图 1-15
Anaconda 安装的“选择目标”窗口
-
In the dialog box, select Add Anaconda to My PATH Environment Variable, as this will automatically create the path in the bash file. See Figure 1-16.
图 1-16
Anaconda 安装的高级选项窗口
-
选择是否将 Anaconda 注册为默认 Python 版本。除非您计划安装和运行多个版本的 Anaconda,或者多个版本的 Python,否则请接受默认设置并选中此框。
-
Click the Install button. The installation will begin, as shown in Figure 1-17.

图 1-17
Anaconda 安装的安装窗口
-
单击下一步按钮。
-
Once the installation is complete, click the Anaconda icon to run Anaconda.

图 1-18
蟒蛇图标
- Create a new environment by clicking Environments, as shown in Figure 1-19.

图 1-19
环境按钮
- Next, click the Create button, as shown in Figure 1-20.

图 1-20
“创建”按钮
- Enter the name of the new environment and select Python 3.7 as the default language. See Figure 1-21.

图 1-21
选择 Python 3.7(本书出版时 Python 的最新版本)
- To test the installation, type
source activate vin
in the terminal, wherevin
is the name of your environment. See Figure 1-22.

图 1-22
从终端激活新创建的环境
- To test the Python installation, type
python 3
and press Enter. See Figure 1-23.

图 1-23
运行 Python
- Now type
"Hello World!"
and press Enter. This will print the words'Hello World!'
in the terminal, as shown in Figure 1-24.

图 1-24
在终端中测试 Python
故障排除提示
确保您选择了创建路径参数的选项,因为您必须将 Python 和 conda 添加到您的环境变量中。如果您在安装过程中错过了这一步,您可以通过在命令提示符下键入setx
命令来手动完成,如下所示:
-
类型
SETX PATH "%PATH%; C:\ Users\user_name\Anaconda\Scripts; C:\ Users\user_name\Anaconda
。 -
用您系统上的用户名替换
user_name
。 -
关闭当前的命令提示符并打开一个新的命令提示符。
-
尝试在命令提示符下键入
python
和conda
来查看路径是否被保存。
请注意,直接安装方法适用于所有系统。如果从官方 Python 网站直接安装给你带来麻烦,使用brew
作为替代方法:
-
Type
$ brew update
. See Figure 1-25.图 1-25
在终端中更新 brew
-
Then type
$ brew install python3
. See Figure 1-26.图 1-26
在终端中打开 Python
安装 Python 之后,您就可以安装 Jupyter Notebook 了,您将使用它来记录本书中的项目。
安装 Jupyter 笔记本电脑
Jupyter 笔记本是开源软件。这是一个机器学习的便捷工具。你可以把它用于工作中的项目,或者自己修改机器学习概念,因为它提供了一种很好的方式来记录你的工作,以便其他人可以轻松地理解和复制你的项目。在本书中,我们将使用 Jupyter 笔记本。
属国
- python3
方法 1:使用 PIP 安装包
PIP 是 Python 包或模块的包管理器。Python 版及更高版本默认包含 PIP。
-
Open the terminal and type
pip install jupyter notebook
. See Figure 1-27.图 1-27
使用终端安装 jupiter 笔记本
-
安装将需要几秒钟时间。
-
To test the installation, type
jupyter notebook
at the command prompt in the terminal. See Figure 1-28.图 1-28
通过终端运行 Jupyter 笔记本
-
Information about the notebook will be shown in the terminal. Then the Jupyter Notebook dashboard will open in the browser. See Figure 1-29.
图 1-29
Jupyter 笔记本界面
故障排除提示
有时您可能需要在命令提示符下键入jupyter-notebook
来启动它。
方法 2:使用 Anaconda
如果你安装了 Anaconda,Jupyter 笔记本已经为你安装好了。您可以使用以下步骤打开 Anaconda 来启动 Jupyter Notebook:
-
打开 Anaconda Navigator 仪表板。
-
Click Jupyter Notebook. See Figure 1-30.
图 1-30
巨蟒之灾中的朱庇特笔记本
将打开一个新的 web 浏览器,其中包含 Jupyter 仪表板。
故障排除提示
确保 Anaconda 目录PATH
已经正确地添加到环境变量部分。如果没有添加这个路径,您需要定位 Anaconda 目录/文件路径,并手动将其添加到环境变量。
现在你已经安装了 Jupyter 笔记本,你准备安装 TensorFlow 2.0。
安装 TensorFlow 2.0
TensorFlow 是一个面向研究和生产的开源机器学习库,由 Google 开发。它有一个陡峭的学习曲线,但随着目前处于测试阶段的 2.0 版本的推出,TensorFlow 变得更加用户友好。本书中的所有项目我们都将使用 TensorFlow 2.0。
属国
- python3
方法 1:使用 PIP 安装包
PIP 提供了一种简单的方法,用一行代码就可以安装 TensorFlow。
-
Open the terminal and type
pip install tensorflow==2.0.0
. See Figure 1-31.图 1-31
通过终端安装 TensorFlow 2.0
-
安装将需要几秒钟时间。
-
在 Jupyter 中打开一个新笔记本。
-
To test the installation, type
import tensorflow
in a new cell, as shown in Figure 1-32.图 1-32
通过 Jupyter 笔记本测试 TensorFlow 2.0 安装
-
现在输入
tensorflow.__version__
。 -
运行手机。
-
TensorFlow 安装的版本将显示在单元格下。
故障排除提示
建议为 TensorFlow 创建一个新的虚拟环境。我们可以使用 virtualenv 或 Anaconda 来创建一个新环境。
方法 2:使用 Anaconda
-
Open the terminal and type
conda create -n tf_env tensorflow
. See Figure 1-33.图 1-33
使用 Anaconda 安装 TensorFlow 2.0
-
设置需要几秒钟。
-
Now type
conda activate tf_env
. See Figure 1-34.图 1-34
用 TensorFlow 2.0 激活新创建的环境
故障排除提示
使用 Anaconda 时,确保新的 conda 环境安装了 Python 和 TensorFlow 2.0。
现在,您已经安装了 TensorFlow 2.0,可以安装 Keras 了。
安装 Keras
Keras 是一个高级神经网络 API,用 Python 编写,能够运行在 TensorFlow 之上。TensorFlow 2.0 中的更新使其严重依赖 Keras。
属国
-
python3
-
TensorFlow 2.0
使用 PIP 安装包
我们使用 PIP 来安装 Keras,因为它可以在任何系统上工作,并且只需要一行代码。
-
Open the terminal and type
pip install keras
. See Figure 1-35.图 1-35
通过终端安装 Keras
-
安装将需要几秒钟时间。
-
在 Jupyter 中打开一个新笔记本。
-
要测试安装,请在新的单元格中键入
import keras
。 -
Now type
pip list | grep Keras
and run the cell. See Figure 1-36.图 1-36
通过 Jupyter 笔记本测试 Keras 安装
-
Keras 安装的版本将显示在单元格下。
故障排除提示
如果 TensorFlow 不是 Keras 安装的默认后端,您可以通过以下步骤手动更改它:
-
在您的系统上找到
keras.backend.__init__.py
文件。 -
注释环境变量
import
。
既然已经安装了 Keras,就可以安装基本的 Python 库了。
安装 Python 库
在本书中,我们将使用基本的 Python 库进行数据预处理。轻松安装所有 Python 库的最佳方式是使用 PIP。
安装 NumPy
NumPy 是 Python 编程语言的一个库。它增加了对大型多维数组和矩阵的支持,以及对这些数组进行操作的大量高级数学函数和工具。它是用 Python 和 C 写的,由 BSD 分发。
属国
- python3
使用 PIP 安装包
-
Open the terminal and type
pip install numpy
. See Figure 1-37.图 1-37
通过终端安装 NumPy
-
安装将需要几秒钟时间。
-
在 Jupyter 中打开一个新笔记本。
-
要测试安装,请在新的单元格中键入
import numpy
。 -
Now type
numpy.version.version
and run the cell. See Figure 1-38.图 1-38
通过 Jupyter 笔记本测试 NumPy
-
您的 NumPy 安装版本将显示在单元格下。
故障排除提示
-
如果出现错误信息,请尝试
sudo pip install -U numpy
命令。 -
使用 pip3。
既然已经安装了 NumPy,就可以安装 SciPy 了。
安装 SciPy
SciPy 是一个用于科学和技术计算的免费开源 Python 库。SciPy 包含用于优化、线性代数、积分、插值、特殊函数、FFT(快速傅立叶变换)、信号和图像处理、ODE 求解器以及科学和工程中常见的其他任务的模块。它是用 Python、Fortran、C 和 C++编写的,并在 BSD-new 许可下发布。
属国
-
python3
-
NumPy
使用 PIP 安装包
-
Open the terminal and type
pip install scipy
. See Figure 1-39.图 1-39
通过终端安装 SciPy
-
安装将需要几秒钟时间。
-
在 Jupyter 中打开一个新笔记本。
-
要测试安装,请在新的单元格中键入
import scipy
。 -
Now type
scipy.version.version
and run the cell. See Figure 1-40.图 1-40
通过 Jupyter 笔记本测试 SciPy 安装
-
SciPy 安装的版本将显示在单元格下。
故障排除提示
-
如果出现错误信息,请尝试
sudo pip install -U scipy
命令。 -
使用 pip3。
既然已经安装了 SciPy,就可以安装 Matplotlib 了。
安装 Matplotlib
Matplotlib 是 Python 编程语言和 NumPy 数字数学扩展的绘图库。Matplotlib 提供了一个面向对象的 API,用于使用通用 GUI 工具包将绘图嵌入到应用程序中。
属国
-
python3
-
NumPy
-
我的天啊
使用 PIP 安装包
-
Open the terminal and type
pip install matplotlib
. See Figure 1-41.图 1-41
通过终端安装 Matplotlib
-
安装将需要几秒钟时间。
-
在 Jupyter 中打开一个新笔记本。
-
要测试安装,请在新的单元格中键入
import matplotlib
。 -
Now type
matplotlib.version.version
and run the cell. See Figure 1-42.图 1-42
通过 Jupyter 笔记本测试 Matplotlib 安装
-
Matplotlib 安装的版本将显示在单元格下。
故障排除提示
-
如果出现错误信息,请尝试
sudo pip install -U matplotlib
命令。 -
使用 pip3。
现在您已经安装了 Matplotlib,您可以开始安装 Pandas 了。
安装熊猫
Pandas 是一个为 Python 编程语言编写的软件库,用于数据操作和分析。它提供数据结构和操作来操作数字表和时间序列。
它是在三条款 BSD 许可下发布的自由软件。它是用 Python 和 C 写的,原作者是 Wes McKinney。
属国
-
python3
-
NumPy
-
我的天啊
-
Matplotlib
使用 PIP 安装包
-
Open the terminal and type
pip install pandas
. The installation will take a few seconds. See Figure 1-43.图 1-43
通过终端的熊猫装置
-
在 Jupyter 中打开一个新笔记本。
-
要测试安装,请在新的单元格中键入
import pandas
。 -
Now type
pandas.__version__
and run the cell. See Figure 1-44.图 1-44
通过 Jupyter 笔记本测试熊猫
-
Pandas 安装的版本将显示在单元格下。
故障排除提示
-
如果出现错误信息,请尝试
sudo pip install -U pandas
命令。 -
使用 pip3。
现在您已经安装了 Pandas,您已经准备好安装 Scikit- Learn 了。
安装 Scikit-学习
Scikit-Learn 是 Python 编程语言的免费软件机器学习库。它具有各种分类、回归和聚类算法,包括支持向量机。
它适用于 Linux、macOS 和 Windows,并在 BSD 许可下发布。
属国
-
python3
-
NumPy
-
我的天啊
-
Matplotlib
-
熊猫
使用 PIP 安装包
-
Open the terminal and type
pip install sklearn
. See Figure 1-45.图 1-45
通过终端安装 Scikit-Learn
-
安装将需要几秒钟时间。
-
在 Jupyter 中打开一个新笔记本。
-
要测试安装,请在新的单元格中键入
import sklearn
。 -
Now type
sklearn.__version__
and run the cell. See Figure 1-46.图 1-46
通过 Jupyter 笔记本测试 Scikit-Learn 安装
-
该单元格下将显示
sklearn
安装的版本。
故障排除提示
-
如果出现错误信息,请尝试
sudo pip install -U sklearn
命令。 -
使用 pip3。
安装 Scikit-Learn 后,您就可以使用 TensorFlow 2.0 学习深度学习的基础知识了。
摘要
在本章中,您学习了如何安装所有必要的软件包,以便在后面的章节中学习。
二、感知器
在我们开始深度学习并讨论如何使用神经网络之前,我们需要了解什么是神经网络。特别是,我们需要了解神经网络的最基本单元,感知器。一个感知器是神经网络的基本单元,其建模方式与人脑中的神经元非常相似。
生物神经元
生物神经元是人工神经元的灵感来源。通过模仿生物神经元的工作方式,人类已经使机器能够独立思考。生物神经元是人类神经系统的基本结构和功能单位(见图 2-1 )。人脑中大约有 860 亿个神经元,约占所有脑细胞的 10%。它们相互连接形成一个网络,参与处理和传输化学和电子信号。细胞核处理从树突接收的信息。轴突是神经元用来发送信息的电缆。每个神经元都由以下部分组成:
图 2-1
基本神经元结构
-
细胞体(又称细胞体
-
树突
-
轴突
这让我们清楚地了解了生物神经元是如何工作的。现在,让我们比较一下生物神经元和人工神经元的工作方式。
人工神经元
一个人工神经元是一个数学函数,模仿生物神经元的工作。每个神经元接受输入,分别加权,求和,然后通过一个非线性函数产生输出。每个神经元都有一种叫做激活信号的内部状态。每个神经元通过连接链路连接到另一个神经元。
人工神经元的组件包括以下内容:
-
输入信号
-
砝码
-
偏见
-
净投入
-
激活功能
-
输出信号
最简单的人工神经元叫做感知器。我们来详细看看感知器。
感觉
感知器是一种算法,用于二进制分类器的监督学习。感知器算法学习输入信号的权重,以便绘制线性决策边界。
这是一种分类算法,它基于将一组权重与特征向量相结合的线性预测函数来进行所有预测。
有两种类型的感知器:
-
单层感知器,处理能力有限。
-
多层感知器或前馈神经网络具有更强的处理能力,因为它们包含两层或更多层。
为了更好地了解感知器是如何工作的,让我们来讨论一下感知器学习规则。
感知机学习规则
感知器学习规则规定,算法将自动学习最佳权重系数。感知器接收多个输入信号,如果输入信号的总和超过某个阈值,它要么发送信号,要么不返回输出。在监督学习和分类的情况下,这可以用于预测样本的类别。
在我们开始创建感知机之前,我们需要知道如何“激活”它,以帮助它正确处理数据并获得有用的输出。让我们来看看基本的激活功能。
激活功能的类型
为了简单起见,我们在本章中介绍了三个最常用的激活功能:
-
Sigmoid 函数
-
ReLU 函数
-
Softmax 函数
Sigmoid 激活函数
Sigmoid 函数是具有 Sigmoid 曲线的数学函数,也称为“S”曲线(见图 2-2 )。这是逻辑函数的一种特殊情况,导致值的概率在 0 和 1 之间。
图 2-2
S 形曲线
当我们希望关注概率映射而不是输入参数的精确值时,这是一个有用的激活函数。
Sigmoid 函数的优点如下:
-
它特别适用于我们必须预测概率作为输出的模型。既然任何事物的概率只存在于 0 到 1 的范围内,那么 Sigmoid 就是正确的选择。
-
我们可以在任意两点找到 s 形曲线的斜率。这意味着函数是可微的。
-
这个函数是单调的,但它的导数不是。这意味着函数以这样一种方式变化,它要么从不减少,要么从不增加,但 Sigmoid 函数的导数不是单调的。
Sigmoid 函数的限制如下:
-
对于高度负输入,Sigmoid 输出接近于零。
-
这可能是神经网络训练中的一个问题,并且可能导致学习缓慢以及模型在训练期间陷入局部最小值。
-
由于消失梯度问题,Sigmoid 函数不能用于多层网络。
ReLU 函数
整流器或 ReLU(整流线性单元)允许我们消除人工神经网络中的负值,因为它是分段线性函数。如果为正,则直接输出输入;否则将给出0
作为输出(见图 2-3 )。
图 2-3
ReLU 激活功能
ReLU 函数的优点包括:
-
允许在大型和复杂数据集上更快和有效地训练深度神经架构。
-
神经网络中仅约 50%单元的稀疏激活(因为负值被消除)。
-
有效的梯度传播,这意味着没有消失或爆炸梯度问题。
-
只有比较、加法或乘法的高效计算。
-
伸缩性好。
ReLU 功能的限制包括:
-
在零点不可微。这意味着接近零的值可能会产生不一致或难以处理的结果。
-
非零居中。这造成了数据的不对称性(因为只处理正值),导致数据处理不均衡。
-
输出值没有限制,并且在传递大值时会导致计算问题。
-
当学习率太高时,ReLU 神经元会变得不活跃并“死亡”
Softmax 函数
Softmax 或归一化指数函数是逻辑函数的推广,它输出结果属于某个类集的概率。它将任意实值的 K 维向量转换为范围(0,1)内加起来等于 1 的 K 维实值向量(见图 2-4 )。它类似于神经网络末端的分类逻辑。
图 2-4
Softmax 激活功能
Softmax 功能的优点包括:
-
突出显示最大值。
-
抑制明显低于最大值的值。
-
训练和预测都很快。
Softmax 功能的缺点包括:
- 它不支持空拒绝,所以如果需要的话,我们需要用一个特定的空类来训练算法。
现在你已经知道什么是感知器,并介绍了它所需的激活功能,让我们看看感知器是如何工作的。
行动中的感知
感知器过程由四个阶段组成(见图 2-5 )。接下来的几节将逐一讨论。
图 2-5
感知器处理输入
阶段 1:输入的前向传播
感知器接受特征形式的输入,并对其进行处理以预测输出。将该输出与标签进行比较,以测量误差。这被称为正向传播。
图中感知器的输入由[x1,x2......x (n)]
表示,其中x
代表特征值,n
代表特征总数。在图 2-5 中,我们取 1 为常数,这样它可以乘以偏差而不改变其值。
接下来,计算净输入。
第二阶段:净投入的计算
砝码
权重是训练模型时随时间计算的值。我们用[w1,w2,.....w(n)]
来表示感知器的权重。
感知器算法的权重是使用梯度下降从训练数据中估计的。
偏见
在代数术语中,偏差允许分类器在指定的方向上将其决策边界向左或向右平移一个恒定的距离。这种偏差有助于更快、更好地训练模型。
在图 2-5 所示的例子中,偏置用w0表示。
净投入
净输入是连续发生的两次计算的结果:
-
首先是每个权重
[w(n)]
与其相关特征值[x(n)]
的乘积。 -
然后将偏差
[w
0]
添加到乘积中。
现在我们有了网络输入,是时候应用激活函数了。
第三阶段:激活功能
激活函数为感知器提供非线性。在当前示例中,我们使用 Sigmoid 函数作为激活函数。Sigmoid 函数是为实输入值定义的,并且在每个点上具有非负导数。输出介于 0 和 1 之间。
Sigmoid 函数表示为f(x) = 1/1+e^-x
。
至此,前向传播完成,但感知器和人工神经元的美妙之处通常在于反向传播过程,在此过程中会发生重新计算。
阶段 4:反向传播
误差反向传播有时简称为 back-propagation 。它是一种用于使用梯度下降的人工神经网络的监督学习的机制。
梯度下降是一种优化算法,用于找到使成本函数最小化的函数参数值。该方法计算误差函数相对于神经网络权重的梯度。误差计算是通过比较输出和标签来完成的。
预测过程接受输入,并根据其过去的“训练经验”,使用内部状态生成最可能的输出在每次迭代期间计算损失函数,并使用优化函数将其最小化,直到实现收敛。损失函数是对模型计算中的误差的计算。最优化是最小化损失的方法。
梯度的计算通过网络反向进行。这意味着首先计算神经网络最后一层输出的梯度,最后计算第一层的梯度。
来自一个层的梯度的部分计算被重新用于计算前一层的梯度。这种反向信息流允许有效计算每层的梯度。
这种优化算法的工作方式是每次向模型显示一个训练实例。该模型对训练实例进行预测,计算误差,并且更新该模型以便减少下一次预测的误差。该过程可用于找到模型中的一组权重,该组权重导致模型在训练数据上的最小误差。偏差以类似的方式更新,只是没有输入,因为它与特定的输入值无关。
让我们通过使用 TensorFlow 2.0 创建我们自己的感知器来更好地理解感知器的工作原理。
项目描述
在本教程中,我们将使用感知器对结构化数据进行分类。这些是在我们的感知机中会发生的步骤:
-
特征和权重被视为矩阵并相乘。
-
然后将矩阵乘法的乘积加到偏差上。
-
使用损失函数计算误差,并进行优化。
图 2-6 提供了一个简单的例子来帮助显示个人感知机是如何工作的。
图 2-6
感知器流程图
重要术语
-
目标/标签:函数的预期输出。
-
损失函数:计算每次迭代的误差。误差计算为预期输出值和预测值之间的差值。
-
优化器:它最小化损失函数。
-
迭代:要进行训练的次数。
-
混淆矩阵:它将预测值与目标值进行比较,用一个矩阵表示正确和不正确观测值的个数。
必需的库
我们将与以下库合作:
-
TensorFlow 2.0
-
硬
-
NumPy
-
我的天啊
-
熊猫
-
Matplotlib
-
Scikit-Learn
程序
在本教程中,我们将通过使用 TensorFlow 分类结构化数据来实现感知器。
第一步。导入库
首先导入必要的库。
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import feature_column #reformats structured data for ease in calculations
from tensorflow.keras import layers #to create the layer in the neural network.
from sklearn.model_selection import train_test_split #splits the data for us
from sklearn.metrics import confusion_matrix #calculates the confusion matrix
from sklearn.metrics import accuracy_score #calculates the accuracy score
import matplotlib.pyplot as plt
%matplotlib inline
#so that plots remain within the cell
第二步。声明参数
在本教程中,我们将两个特征作为输入。这里,units
表示当前层中存在的神经元的数量。
Number_of_features=2
Number_of_units=1 #indicates number of neurons
第三步。宣布权重和偏差
使用tf.Variable()
初始化权重变量,并将其命名为weights
。使用tf.zeros()
将weights
变量设置为0
。接下来,使用tf.Variable()
初始化偏差变量,并将其命名为bias
。使用tf.zeros()
将bias
变量设置为0
。
weight=tf.Variable(tf.zeros([Number_of_features,Number_of_units])) #initializing to zero
bias=tf.Variable(tf.zeros([Number_of_units]))#Initializing to zero
第四步。定义感知器函数
使用def perceptron(x)
定义感知器功能。在函数内部,I
使用tf.matmul(x,weight)
计算x
和权重的矩阵乘积,然后使用tf.add(bias)
将乘积加到偏差上。tf.sigmoid(I)
使用 Sigmoid 激活函数计算输出。
def perceptron(x):
I=tf.add(tf.matmul(x,weight),bias)
output=tf.sigmoid(I)
return output
第五步。定义损失函数和优化器
个体损失函数被计算为标签y
的 Sigmoid 交叉熵的缩减平均值,其中对数是perceptron(x)
函数的输出。
Sigmoid 交叉熵用于量化两个概率分布之间的差异,在这种情况下,0
和1
。使用tf.optimizers.Adam(.01)
初始化optimizer
并使用 Adam 优化器:
individual_loss=lambda: abs(tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y,logits=perceptron(x))))
optimizer=tf.keras.optimizers.Adam(.01)
第六步。读入数据
使用pd.read_csv('data.csv')
读入数据。然后使用dataframe.head()
查看数据帧的前几行,检查数据是否被正确读取。
dataframe = pd.read_csv('data.csv')
dataframe.head()
第七步。标签的可视化
为了熟悉这些数据,您可以使用plt.scatter()
对标记的数据进行快速可视化。
plt.scatter(dataframe.x1,dataframe.x2,c=dataframe.label)
输出如图 2-7 所示。
图 2-7
标签散点图
第八步。准备输入
使用.as_matrix()
将输入数据x_input
格式化为矩阵,使用.as_matrix()
将标签y_label
格式化为矩阵。这将从数据集中分离出输入和标注。
x_input=dataframe[['x1','x2']].as_matrix()
y_label=dataframe[['label']].as_matrix()
#View the input matrix
x_input
第九步。初始化变量
使用tf.Variable()
将x
初始化为变量。和 dataframe 对象x_input
。
#Initialize the variable x
x=tf.Variable(x_input)
使用x=tf.cast()
将x
的datatype
更改为float32
。
#Change the datatype of x to 'float32'
x=tf.cast(x,tf.float32)
使用tf.Variable()
和 dataframe 对象y_label
将y
初始化为变量。
#Create the variable y
y=tf.Variable(y_label)
使用tf.cast()
将y
的datatype
更改为float32
。
#Change the datatype of y to 'float32'
y=tf.cast(y,tf.float32)
第十步。训练模型
使用for
循环训练模型。使用in range(1000):
将迭代次数设置为1000
。使用optimizer.minimize(individual_loss,[weight,bias])
将损耗降至最低。
for i in range(1000):
optimizer.minimize(individual_loss,[weight,bias])
第十一步。权重和偏差的新值
通过使用tf.print(weight,bias)
打印它们的值,查看新的重量和偏差。
tf.print(weight,bias)
以下是输出:
[[-2..42329407]
[1.85045445]] [1.74988687]]
第十二步。查看最终损失
通过使用以下公式计算来查看最终损失:
final_loss=tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y,logits=perceptron(x)))
现在使用tf.print(final_loss)
打印最终损失的值。
final_loss=tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y,logits=perceptron(x)))
tf.print(final_loss)
以下是输出:
0.534005582
第十三步。使用训练好的模型进行预测
使用ypred=perceptron(x)
和ypred=tf.round(ypred)
四舍五入输出值,使其成为1
或0
。
ypred=perceptron(x)
ypred=tf.round(ypred) #Round off the output value to 1 or 0, to make the comparison with the target easier.
第十四步。评估模型
使用accuracy_score(y, ypred)
计算准确度分数。这表明在将预测输出与目标进行比较后,模型的准确性。
accuracy_score(y, ypred)
以下是输出:
1.0
使用confusion_matrix(y, ypred)
生成混淆矩阵。如果所有的观察值都落在矩阵的对角线内,其余的值都是0
,那么这个模型就是 100%准确的。
confusion_matrix(y, ypred)
Note
在 TensorFlow 中初始化变量时,始终使用大写V
。
现在你知道感知器是如何工作的了。在下一章,你将学习一组感知器如何以人工神经网络的形式一起工作。
摘要
以下是您在本章中学到的内容:
-
人工神经元是模仿生物神经元的数学函数。
-
感知器是神经网络的基本单元,某些计算使用它来检测输入数据中的功能或商业智能。它映射其输入
x
,该输入乘以学习的权重系数,并生成输出值f(x)
。 -
感知器学习规则规定,算法将自动学习最佳权重系数。
-
多层感知器,或具有两层或更多层的前馈神经网络,具有更强的处理能力,也可以处理非线性模式。
-
感知器接受特征作为输入。
-
每个感知器都有权重和偏差。
-
当输入在输入层被馈送并被连续处理直到它们到达输出层时,发生正向传播。
-
当计算最后一层的梯度时,发生反向传播,并且每个部分计算被馈送到前一层,直到到达神经网络的第一层。
-
常用的激活函数有 Sigmoid、ReLU 和 Softmax。
-
加权求和是每个权重相乘后得到的值的总和。
-
Sigmoid 函数是具有 Sigmoid 曲线的数学函数。
-
整流器,或 ReLU(整流线性单元),允许我们消除人工神经网络中的负值。
-
Softmax 或标准化指数函数是逻辑函数的推广。
-
在本章的项目中,您学习了如何对二元分类问题进行预测,如何优化一组权重,如何计算准确度得分,以及如何查看混淆矩阵。
三、神经网络
这里有一些值得思考的问题:如果单个神经元足够强大,可以执行二进制分类,就像你在上一章看到的那样,那么神经元的集合会强大多少?这就是我们在本章将要发现的。几个神经元一起构成了一个神经网络。在本教程中,我们将创建一个多层神经元,然后用它对 MNIST 数据集进行分类。在 Keras 中,一次迭代被称为一个时期。让我们更详细地研究神经网络。
什么是神经网络?
神经网络也称为人工神经网络(ANN)。它包含被称为神经元的互连节点层。神经网络利用一系列算法来检测数据集中的潜在关系。神经网络适应内部和外部参数。网络生成最佳可能输出,而不必修改输出标准。神经网络基于自适应学习。他们使用几个原则,包括基于梯度的训练、模糊逻辑和贝叶斯方法。人工神经网络用于序列和模式识别系统、数据处理、机器人以及许多其他系统。
神经网络组件
神经网络由以下主要组件组成:
-
输入层:输入到神经网络的输入。
-
隐藏层:包含任意数量的神经元,取决于我们想要达到的目标。
-
输出层:得到的最终输出。
神经网络的优势
神经网络具有以下优势:
-
当神经网络的一个元素失效时,它将继续发挥作用。这是由于它的并行性。
-
神经网络不需要为看不见的数据重新编程,因为它是一个自适应的智能系统。
-
神经网络擅长解决复杂的问题。
-
神经网络比传统网络具有更强的容错能力。在不丢失存储数据的情况下,网络可以在其任何组件中再生故障。
神经网络的缺点
神经网络有以下挫折:
-
大型神经网络需要大量的处理时间。
-
神经网络的架构不同于微处理器的架构和历史,所以它们必须被仿真。
-
增加中子的数量会增加网络的复杂性。
-
成功训练网络需要最佳的数据量。
-
确定层数以及每层神经元的数量需要一些反复试验。这很费时间。
我们在理论上知道什么是神经网络,但是在实践中呢?让我们来看看神经网络是如何工作的。
神经网络如何工作
正如你在前一章看到的,神经元内发生的过程是:
-
正向传播
输入➤计算净输入➤激活函数应用➤输出
-
反向传播
输出➤激活函数应用于净输入➤输入的➤计算
现在让我们将这个过程扩展到神经网络中的大量神经元。
让我们以一个有十个神经元的浅层神经网络为例。在这种类型的网络中,只有一个隐藏层(见图 3-1 )。
图 3-1
使用 MNIST 的浅层神经网络示例
正向传播
输入被馈送到隐藏层中的每个神经元。这意味着在隐藏层中,每个神经元都有自己的一套权重和一个偏好。整个过程是正向的,因此称为正向传播。以下是这三层的细分:
-
输入层:根据图 3-1 ,有四个输入。这里,x1、xn-1 和 xn 是变量。我们把一个作为常数,这样它就不会影响偏差。
-
隐含层:在图 3-1 中,有四个输入,所以每个神经元有四个权值和单个偏置。激活函数应用于每个神经元的净输入。根据图表,我们从隐藏层得到十个输出。每个神经元都有一个。在这种情况下,我们使用 Softmax 激活函数。
-
输出层:我们得到十个类别,每个类别对应一个数字,y1,y2..… y10。
Note
根据预期输出的数量,隐藏层可以具有 Sigmoid 函数或 Softmax 函数。如果需要两个输出,则使用 Sigmoid。如果预期有两个以上的输出,则使用 Softmax。
对于具有多个隐藏层的神经网络,在层之间使用的最流行的激活函数是 ReLU 函数。
一旦输入被馈入并被正向处理,就该检查并调整它们,以便通过反向传播获得更好的结果。
反向传播
我们在前一章看到,反向传播发生在神经网络的每一层。每层中每个神经元的权重和偏置被调整,直到实现收敛。该过程从输出层移动到隐藏层,最后移动到输入层。
有了这些关于神经网络的知识,让我们看看最常见的神经网络类型。
神经网络的类型
在前面的例子中,我们学习了什么是浅层神经网络。现在,让我们快速看一下神经网络是如何大致分类的。
每个神经网络都有几种变体和组合。所以,实际上有几种神经网络可用。对于本书中的项目,我们主要关注人工神经网络的大类。
前馈神经网络
在前馈神经网络中,数据仅在一个方向上从第一层向前移动,直到到达输出节点(见图 3-2 )。这也称为波前传播波,通常通过使用分类激活函数来实现。没有反向传播,它可以有一个单一的层或多个隐藏层。
前馈神经网络的用途包括:
图 3-2
样本前馈神经网络(HC 表示隐藏单元)
-
人脸识别
-
计算机视觉
卷积神经网络
卷积神经网络(CNN)是多层神经元的正则化版本,包含一个或多个卷积层(见图 3-3 )。这些层可以完全互连,也可以汇集在一起。卷积层对输入使用卷积运算。
CNN 的用途包括:
图 3-3
样本卷积神经网络(K 表示核,C/P 表示卷积或池,HC 表示隐藏单元)
-
图像和视频识别
-
自然语言处理
-
推荐系统
循环神经网络(RNN)
循环神经网络(RNN)保存特定层的输出,并将其作为输入进行反馈。循环神经网络过程在第一层之后的层中开始。每个节点在计算和执行操作时充当一个存储单元,因此在反向传播期间,它可以将以前的值与新值进行比较(见图 3-4 )。rnn 将在第九章中详细讨论。
RNN 的用途包括文本到语音转换技术。
图 3-4
样本循环神经网络(RC 表示递归细胞)
径向基函数神经网络
RBNN 考虑任意点相对于中心的距离。它应用径向基函数作为其激活函数(见图 3-5 )。有两层。在内层,特征与径向基函数相结合。那么当在下一时间步中计算相同的输出时,考虑这些特征的输出。这样,它实现了线性可分性。
RBNN 的用途包括电力恢复系统。
图 3-5
样本径向基函数神经网络(HC 表示隐藏单元)
现在你知道了神经网络的基本类别。当我们在本书的项目中工作时,我们将使用神经网络,它们属于这里讨论的子类。
项目描述
在这个项目中,我们使用单层神经网络对一组手写数字进行分类。我们首先将二维矩阵中的数据展平为一维数组。然后我们用 Softmax 激活函数把它输入神经网络。输出是一个从 0 到 9 的数字。图 3-6 显示了该过程的流程图。
图 3-6
对 MNIST 数据使用简单神经网络的流程图
扁平化数据
MNIST 的数据完全由手写数字的二维图像组成。这意味着数据是二维矩阵的形式。这对于神经网络来说不容易处理。所以我们通过把它转换成一维数据来“扁平化”。二维矩阵被转换成一维数组。这样,神经网络可以更好地处理数据,并给出更好的结果。参见图 3-7 。
图 3-7
拼合 MNIST 数据
关于数据集
名称: MNIST 数据库
内容:手写数字的 MNIST 数据库具有 60,000 个样本的训练集和 10,000 个样本的测试集。它是可从 MNIST 获得的更大集合的子集。数字已经过大小标准化,并在固定大小的图像中居中。
来源: http://yann.lecun.com/exdb/mnist/
必需的库
我们的项目需要以下库:
-
硬
-
TensorFlow 2.0
神经网络体系结构
我们将使用一个简单的神经网络架构,由以下部分组成:
-
输入层:数据作为特征输入到输入层。神经元计算网络输入函数。
-
输出层:这一层包含十个神经元,用于数据的每个可能类别。
-
激活功能: Softmax
-
损失函数:稀疏分类交叉熵
-
优化器:亚当
-
度量:准确度
程序
按照以下步骤创建一个多层神经元:
-
导入库。使用
import
命令导入 TensorFlow 2.0 和 Keras。 -
读入数据。Keras 加载了一组数据集。MNIST 是其中之一。所以可以直接从 Keras 加载。然后将数据分成两组,分别用于测试和训练。
#import libraries.
import keras
import tensorflow as tf
mnist=tf.keras.datasets.mnist
#split data
(x_train, y_train), (x_test, y_test) = mnist.load_data()
我们的输出应该是这样的。
- 数据处理。MNIST 数据集由二维图像组成。为了方便起见,将它们展平成一维图像。
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
- 创建神经网络。使用
Sequential
模块创建一个单独的隐藏层。这个隐藏层有十个神经元。我们将使用的激活函数是 Softmax。接下来,您可以定义优化器。我们将使用 Adam 优化器,因为我们只是对数据进行基本的分类。我们将损失函数定义为稀疏分类交叉熵,因为输出只能是十个可能类别中的一个。为了测试这个模型的能力,使用accuracy
指标。
x_train, x_test = x_train / 255.0, x_test / 255.0
- 训练模型。使用
fit
模块,训练新创建的模型。设置模型的迭代。首先,将其设置为三个历元,并检查准确度分数。
digit = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
#compile model
digit.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
digit.fit(x_train, y_train, epochs=3)
我们的输出应该如下所示:
- 评估模型。使用
evaluate
模块,测试模型。测试完成后,检查准确度分数。只需三次迭代,您就可以获得 92%的准确率。
Train on 60000 samples
Epoch 1/3
60000/60000 [==============================] - 3s 47us/sample - loss: 0.4670 - acc: 0.8788
Epoch 2/3
60000/60000 [==============================] - 3s 44us/sample - loss: 0.3036 - acc: 0.9151
Epoch 3/3
60000/60000 [==============================] - 3s 45us/sample - loss: 0.2834 - acc: 0.9204
<tensorflow.python.keras.callbacks.History at 0x7f8709047358>
digit.evaluate(x_test, y_test)
输出应该类似于以下内容:
- 进一步的实验。尝试更改历元数,看看它如何影响精确度。
10000/10000 [==============================] - 0s 26us/sample - loss: 0.2765 - acc: 0.9229
[0.2764906617820263, 0.9229]
摘要
以下是您在本章中学到的所有内容的回顾:
-
神经网络也称为人工神经网络(ANN)。
-
神经网络基于自适应学习。
-
神经网络具有并行性。
-
在不丢失存储数据的情况下,网络能够在其任何组件中再生故障。
-
大型神经网络需要大量的处理时间。
-
增加中子的数量会增加网络的复杂性。
-
成功训练网络需要最佳的数据量。
-
确定层数以及每层神经元的数量需要一些反复试验。这很费时间。
-
根据预期输出的数量,隐藏层可以具有 Sigmoid 函数或 Softmax 函数。如果有两个预期输出,则使用 Sigmoid。如果有两个以上的预期输出,则使用 Softmax。
-
前馈神经网络是一种 ANN,其中节点之间的连接不形成循环。
-
循环神经网络应用反向传播。
-
RBNN 是一个单层神经网络,其激活函数是径向基函数。它实现了线性分离。
-
数据扁平化包括将其从一种格式转换为另一种格式。例如,在这个项目中,我们通过将 MNIST 数据从二维矩阵转换为一维数组来将其扁平化。
-
通过第一个项目,您学习了如何处理大型数据集,如何将数据集拆分为测试数据集和定型数据集,以及如何为模型准备数据。
-
您现在应该很清楚如何构造一个简单的神经网络,如何使用训练数据对其进行训练,以及如何使用测试数据对其进行测试。
参考
本章中使用的资源如下:
-
www.linkedin.com/pulse/artificial-neural-networks-advantages-disadvantages-maad-m-mijwel/
优缺点: -
神经网络的类型:
www.digitalvidya.com/blog/types-of-neural-networks/
四、情感分析
在前一章中,我们学习了不同类型的神经网络。在这一章中,我们学习长短期记忆(LSTM)神经网络。本章中的项目旨在简化,以帮助我们适应 TensorFlow 2.0。我们将首先了解什么是 LSTM 以及我们为什么需要它。然后我们将看看 LSTM 是如何工作的。之后,我们将学习什么是情感分析及其各种类型。我们还将学习如何保存一个训练好的模型并重新加载它,这样我们就可以保存模型并在将来的项目中重用它们。请记住,我们只在这个项目中介绍保存和重新加载一个训练好的模型的过程;但是,您在本书或您自己的书中处理的任何项目都可以保存和重新加载。
LSM 评论
让我们从理论上了解什么是 LSTM 开始。LSTM 能够学习长期依赖关系。它保持一个相对恒定的误差,允许递归网络通过多个步骤继续学习,以远程链接原因和结果。该误差通过时间和层反向传播。图 4-1 显示了一个基本的 LSTM,其中 M 表示存储单元。第一列神经元是输入层,最后一列神经元是输出层。
图 4-1
基本 LSTM 结构
LSTM 包含门控细胞中循环网络正常流动之外的信息。它本质上是一个存储单元,可以存储、写入或读取信息。
该单元通过模拟门决定存储什么以及何时允许读和写特权。这些门通过 Sigmoids 的逐元素乘法来实现,这些乘法都在 0-1 的范围内。模拟具有可微分的优点,这使得它适合于反向传播。
通过进行猜测、反向传播误差和通过梯度下降调整权重的迭代过程,单元学习何时允许数据进入、离开或删除。
这是“消失梯度”问题的完美解决方案。当我们尝试使用基于梯度的优化技术来训练神经网络模型时,会出现消失梯度问题。
在反向传播过程中,梯度随着通过神经网络向后移动而减小。这意味着初始层中的神经元与后面层中的神经元相比学习非常慢。网络中的初始层是训练最慢的。训练过程花费非常长的时间,并且模型的预测精度降低。
LSTM 通过在遗忘之门激活和梯度计算之间建立联系来解决这个问题。这种连接为信息流创建了一条通过遗忘门的路径,从而确保信息不会被遗忘。
LSTM 的应用包括:
-
手写识别
-
语音识别
-
疾病预测
-
音乐创作
现在您已经知道了什么是 LSTM,以及它在现实生活中的应用,让我们来看看 LSTM 中的过程。
LSTM 是如何工作的
理解 LSTM 的最好方式是把它想象成一个链状结构,有两个非重复模块和一个重复模块。(参见图 4-2 )。在这种结构中,重复模块有三个门。它们是输入门、输出门、和遗忘门。细胞状态以最小的线性相互作用连接整个链。
LSTMs 使用一种称为门的结构来调节进出单元的数据流。它们的工作方式类似于条件门(and
、or
、xor
等)。).基于一组条件,他们决定允许哪些信息通过大门。这些条件由 LSTM 决定,不需要明确编程。
-
遗忘门:遗忘门的输出通过将矩阵中的一个位置乘以零来告诉单元状态要遗忘哪些信息。如果遗忘门的输出为
1
,则信息保持在单元状态。 -
输入门:该门决定哪些信息应该进入单元格状态。这里重要的组件是激活函数。输入门有一个范围为
[0,1]
的 Sigmoid 函数。单元状态的等式是先前单元状态之间的求和,因此 Sigmoid 函数本身只会增加存储器,而不能移除存储器。如果您只能在[0,1]
之间添加一个浮点数,则该数字永远不会为零。这使得它无法忘记。这就是输入调制门具有tanh
激活功能的原因。tanh
有一个[-1, 1]
范围,允许细胞状态遗忘记忆。 -
输出门:该门考虑了所有可能的值,并利用一个 Sigmoid 激活函数来决定哪些信息应该进入下一个隐藏状态。
LSTM 的输入是三维数组的形式。其中第一维表示批量大小,第二维表示我们输入序列的时间步数,第三维表示一个输入序列中的单元数。
图 4-2
对 LSTM 运作的内部观察
-
S
(t-1)
是细胞状态 -
f
(gt)
是忘年门 -
i
(gt)
是输入门 -
o
(gt)
是输出门
阶段 1:LSTM 决定从小区状态中丢弃什么信息。这个决定是由使用 Sigmoid 激活函数的遗忘门层做出的。这意味着将分配一个在0
和1
之间的值。值1
代表“存储”,值0
代表“丢弃”
假设权重为W
fg
,偏差为b
fg
。现在根据图表,我们有两个输入— n
(t-1)
和a
。两个值都将乘以权重(W
fg
),然后结果乘积将被添加到偏差(b
fg
)。最后,Sigmoid 激活函数将应用于结果。
它可以用数学方法推导如下:
- f gt =乙状结肠(W fg 。[n (t-1) ,a] + b fg
阶段 2:根据剩余的信息,LSTM 决定在单元状态中存储什么新信息。这分两步完成:
-
输入门图层使用 Sigmoid 函数来决定哪些值将被更新。假设权重为
W
ig
,偏差为b
ig
。i gt =乙状结肠(W ig 。[n (t-1) ,a] + b ig
-
一个
tanh
层创建了一个新值的矢量。假设权重为W
c
,偏差为b
c
。S ?? = tanh( W c 。[n (t-1) ,a] + b c
阶段 3:我们现在结合这两个步骤的结果来创建单元状态的更新。旧的单元格状态S
(t-1)
乘以f
gt
。然后我们把结果加到(i
gt
* S
??
)
的乘积上。
- S??=(fgt* St-1)+(Igt* S??
阶段 4:最后,LSTM 决定输出。这分两步完成:
-
输出层使用 Sigmoid 层来决定单元状态的哪些部分将作为输出。假设权重为
W
c
,偏差为b
c
:o gt = Sigmoid( W og 。[n (t-1) 、a] + b og
-
结果通过一个
tanh
激活函数,得到-1
和1
之间的值。然后将结果乘以 Sigmoid 门的输出。nt= ogtSt
LSTM 中的图层
为了设计有效的 LSTM,仅仅知道 LSTM 是如何工作的是不够的。我们还需要了解哪些图层可用于设计 LSTM。它可以有以下类型的层:
-
嵌入层:用于为进来的词创建词向量。它位于输入层和 LSTM 层之间。嵌入层的输出是 LSTM 层的输入。
-
LSTM 层:循环神经网络层,将序列作为输入,可以返回序列或矩阵。
-
剔除层:一种正则化技术,包括在训练期间的每次更新时将输入单元的一部分设置为
0
,以防止过拟合。 -
密集层:全连接层,其中每个输入节点连接到每个输出节点。
-
激活层:决定 LSTM 使用哪个激活函数来计算节点的输出。
既然您已经了解了 LSTM 的工作原理,您就可以在项目中将其付诸实践了。
项目描述
在这个项目中,我们将使用亚马逊评论上的 LSTM 来识别句子中的情感,并确定它们的极性。本质上,我们会发现一个评论是积极的还是消极的。深度学习模型需要数字数据作为输入。由于我们正在进行文本分类,我们需要将文本数据转换成数字向量。为此,我们将使用一种叫做单词嵌入的方法。这种方法将每个单词编码成一个 n 维稠密向量,其中相似的单词会有相似的编码。为此,我们将使用 Keras 嵌入层。
我们首先只取总评论的 20%,并创建我们将使用的主要数据框架。这是为了避免我们的系统过载,并可能导致系统崩溃。然后,我们通过删除标点符号和数字、将所有大写字母转换为小写字母以及删除单个字符来清理数据。为了准备模型的数据,我们将其分为 70%用于训练集,30%用于测试集。在此之后,我们将新清理的数据转换为数组,并使用标记器来帮助模型理解每个单词。为了避免数组大小的不兼容问题,我们使用填充。我们继续创建模型并训练它。最后,我们通过检查模型的准确性来评估模型的性能。该过程如图 4-3 所示。
图 4-3
LSTM 情绪分析项目工作流程
关于数据集
名称: Amazon_Review_Polarity
内容: 656MB (688,340,758 字节)亚马逊上负面、正面和中性评论的压缩档案
来源 : https://drive.google.com/open?id=0Bz8a_Dbh9QhbaW12WVVZS2drcnM
创作人 : 张翔
Note
建议您为所有项目创建一个粗略的流程图,甚至是您自己正在做的项目,以帮助您更好地组织项目。
理解情感分析
情感分析(见图 4-4 )是文本的上下文挖掘,识别和提取源材料中的主观信息。然而,社交媒体流的分析通常仅限于基本的情感分析和基于计数的指标。
图 4-4
情感分析是在各种在线资源上进行的
情感分析是分析文本数据并将观点分类为消极、积极或中立的自动化过程。情感分析是最常用的文本分类工具;其应用包括:
-
社交媒体监控
-
品牌监控
-
客户之声(VoC)
-
客户服务
-
市场调查
-
自动分析调查回复、产品评论和社交媒体评论
我们可以提取表达式的其他属性,例如:
-
极性:当说话者表达肯定或否定的意见时
-
话题:正在谈论的事情
-
意见持有人:表达意见的个人或实体
情感分析可以应用于不同层次的范围:
-
文档级情感分析获取完整文档或段落的情感。
-
句子级情感分析获取单个句子的情感。
-
子句子级情感分析获取句子内子表达的情感。
在我们开始我们的项目之前,我们需要理解一些概念,比如不同类型的情感分析以及它们在现实生活中的应用。然后,我们需要理解一次性编码,因为我们将把它应用到我们的数据集。一旦我们设计了我们的模型,我们还需要测试它以确保它表现良好。
情感分析的类型
有许多类型的情绪分析和 SA 工具,从关注极性(积极、消极、中性)的系统到检测感觉和情绪(愤怒、快乐、悲伤等)的系统。)或者识别意图。
细粒度情感分析
通过将情绪的基本类别(积极、中立或消极意见)扩展到以下类别,我们可以更精确地了解意见的极性水平:
-
非常积极
-
积极的
-
中立的
-
否定
-
非常消极
这通常被称为细粒度情感分析。
情感检测
情绪检测旨在检测快乐、沮丧、愤怒、悲伤等情绪。许多情绪检测系统求助于词典(即单词列表和它们传达的情绪)或复杂的机器学习算法。求助于词汇的一个缺点是,人们表达情感的方式千差万别。一些通常表达愤怒的词也可能表达快乐。
基于方面的情感分析
通常,当分析受试者的情绪时,我们可能不仅对人们谈论产品时是正面、中性还是负面感兴趣,还会对人们谈论产品的哪些特定方面或特征感兴趣。
意图分析
意图分析主要检测人们想对文本做什么,而不是人们对文本说什么(见图 4-5 )。有时,可以从文本中推断出意图动作,但其他时候,推断意图需要上下文知识。
图 4-5
一个对 tweet 进行意图分析的简单例子
多语言情感分析
多语言情感分析是一项艰巨的任务。它涉及大量的数据预处理。这些资源中的大部分可以在线获得(例如,情感词典),但是许多其他资源必须被创建(例如,翻译的语料库或噪声检测算法)。可用资源的使用需要大量的编码经验,并且可能需要很长时间来实现。
另一种方法是自动检测语言,然后为我们选择的语言训练一个定制模型(如果文本不是用英语写的),最后执行分析。
情感分析算法
有许多方法和算法来实现情感分析系统,并且它们可以被分类如下:
-
基于规则的系统根据一组手工制作的规则执行情感分析。通常,基于规则的方法定义了一组识别主观性、极性或观点主题的规则。这些规则可以使用各种输入,例如:
-
传统的自然语言处理技术,如词干提取、标记化、词性标注和语法分析。
-
其他资源,如词典(即单词和短语列表)。这个系统非常幼稚,因为它没有考虑单词在序列中是如何组合的。
-
-
自动系统依靠机器学习技术从数据中学习。
-
混合系统结合了基于规则和自动化的方法。
用于评估的情感分析指标
一旦我们设计了我们的模型,我们需要测试它,看看它对情感分析的效果如何。我们可以通过使用下面几节中描述的某些指标来做到这一点。
交叉验证
这包括将训练数据分成一定数量的训练折叠(75%的训练数据)和相同数量的测试折叠(25%的训练数据)。然后,我们使用训练折叠来训练分类器,并对照测试折叠对其进行测试,以获得性能指标。该过程重复多次,并计算每个指标的平均值。
如果我们的测试集总是相同的,我们可能会过度适应该测试集,这意味着我们可能会根据给定的数据集调整我们的分析,以至于我们无法正确地分析不同的数据集。
精确
这度量了在被预测(正确和不正确)属于给定类别的所有文本中,该文本被预测为属于该类别的程度。
回忆
这测量文本如何在所有文本中被正确地预测为属于给定类别,其中应该被预测为属于该类别的。我们输入分类器的数据越多,召回率就越高。
准确
这度量了从语料库中的文本中正确预测了多少文本(包括属于某个类别和不属于该类别)。
最常见的是,精确度和召回率被用来衡量性能,因为精确度本身并不能说明一个分类器的好坏。
混合方法
当我们将各种情感分析方法和技术结合在一起时,我们得到了混合的情感分析方法和技术。
影响模型性能的参数
影响我们模型性能的因素是参数,下面讨论最常见的参数。
主观性和语气
主客观文本的检测与分析其语气同样重要。事实上,所谓的客观文本并不包含明确的情感。所有的谓词(形容词、动词和一些名词)在创造情感的方式上不应该被同等对待。
背景和极性
所有的话语都是在语境中产生的。脱离上下文分析情感变得相当困难。然而,如果没有明确提到上下文,机器就无法学习上下文。如果我们要考虑文本产生的至少一部分上下文,就需要大量的预处理或后处理。
讽刺和挖苦
字面意思和预期意思(即反语)之间的差异以及更具侮辱性或荒谬性的反语(即讽刺)通常会将积极情绪转变为消极情绪,而消极或中性情绪可能会转变为积极情绪。然而,发现讽刺和挖苦需要对文本产生的背景进行大量的分析,因此,很难自动完成。
比较
情感分析仍然不够先进,不足以理解比较。这类陈述容易出错。
定义中性情绪
定义我们的类别——在这种情况下,是中立的标签——是这个问题最重要的部分之一。由于标记数据要求标记标准一致,因此必须对问题进行良好的定义。
中性标签可能包含:
-
客观文本
-
无关信息
-
包含愿望的文本
Tokenizer
记号赋予器用于对文本语料库(我们正在处理的文本数据)进行矢量化,方法是将文本转换为整数序列(每个整数都是字典中记号的索引)或向量,其中每个记号的系数可以是二进制的或基于字数的。
Note
0
是一个保留索引,不会分配给任何单词。
通过将verbose
设置为0
、1
或2
,您可以决定如何显示每个时期的训练进度。
-
verbose=0
什么都不会给你看(沉默)。 -
verbose=1
会显示一个动画进度条,如下所示:
verbose=2
会提到这样的历元数:
H5 文件
为了保存我们的模型,我们需要将重量存储在 H5 文件中。它是以分层数据格式(HDF)保存的数据文件。它包含科学数据的多维数组。
JSON 文件
当保存我们的模型时,JSON 文件存储了关于层的信息和每个层使用的数量,以便可以很容易地重新加载。JSON 文件以 JavaScript Object Notation (JSON)格式存储简单的数据结构和对象,这是一种标准的数据交换格式。它主要用于在 web 应用程序和服务器之间传输数据。JSON 文件是轻量级的、基于文本的、可读的,并且可以使用文本编辑器进行编辑。
你现在应该对这个项目有一个清晰的了解,并且已经学习了一些新的术语。我们可以开始了。
必需的库
对于这个项目,您将使用在本书第一章中安装的基本库。此外,还有一些我们需要的其他库。以下是该项目所需的所有库的列表:
-
NumPy(安装说明见第一章)
-
TensorFlow(安装说明见第一章)
-
熊猫(安装说明见第一章)
-
Keras(安装说明见第一章)
-
Scikit-Learn(安装说明见第一章)
-
re(内置 Python 库)
看起来我们已经拥有了这个项目所需要的所有库。让我们把重点放在我们的项目中想要使用的层的类型和每种类型的层的数量上。
LSM 体系结构
我们将在这个项目中使用的模型是一个简单的 LSTM。我们希望专注于识别基本情绪,包括积极的情绪(高兴、兴奋等)。)和消极(难过、担心等。).我们不会陷入被动攻击或类似的复杂情绪。图 4-6 是该 LSTM 结构的直观表示。
图 4-6
用于情感分析的 LSTM 架构
为了更好地理解我们的模型是如何工作的,让我们来看看 LSTM 的“蓝图”。我们只处理了 20%的数据集,所以我们的模型不需要太复杂。我们的模型将包括以下内容:
-
嵌入层- 1
-
LSM 层- 1
-
密集层- 1
我们将使用 Keras 嵌入层来训练我们自己的自定义单词嵌入模型。该层用随机权重初始化,并被定义为网络的第一个隐藏层。LSTM 层分别学习什么是积极情绪和消极情绪。然后,它存储这些信息,这样它就可以识别数据并将其分类为积极情绪或消极情绪。最后,密集层是一个完全连接的层,确保模型学习良好并提高其准确性。
-
激活函数:我们将使用 Softmax 函数,因为它可以识别情绪并进行分类。
-
损失函数:模型通过损失函数进行学习。这是一种评估特定算法对给定数据的处理效果的方法。如果预测与实际结果偏离太多,损失函数就会很大。如果偏差较小,损失函数将较小。我们将使用二进制交叉熵,也称为 Sigmoid 交叉熵损失。它是 Sigmoid 激活和交叉熵损失的组合。它独立于每个矢量分量(类),这意味着为每个输出矢量分量计算的损耗不受其他分量值的影响。这使得它非常适合多标签分类,在这种分类中,属于某个类的元素不应该影响另一个类的决策。它被称为二元交叉熵损失,因为它为每个类建立了一个二元分类问题。
-
优化器:Adam 优化器是大多数项目的理想选择。本章中的项目相当简单,所以 Adam 优化器很好。
-
度量:为了检查我们的模型是如何工作的,我们现在只测量准确性。
我们已经规划好了我们的架构。剩下的就是实现模型了。
程序
让我们回顾一下我们将在这个项目中使用的代码。
第一步。导入库
首先为这个项目导入必要的库。
import tensorflow as tf
import numpy as np
import pandas as pd
import re
import keras
from keras import Model
from tensorflow.keras.layers import Flatten,LSTM, Dense, Flatten, Embedding
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from keras_preprocessing.text import Tokenizer
from keras.initializers import glorot_uniform
from sklearn import model_selection
第二步。加载数据
加载数据,数据为.csv
格式。然后使用file.readlines()
阅读评论中的每一行。
#Read in data
with open('train.csv', 'r') as file:
text = file.readlines()
现在您使用 Pandas 创建一个名为x_train
的空数据帧。这将用于仅存储分析所需的数据。
#create empty dataframe
x_train = pd.DataFrame()
您需要初始化两个列表。列表将评论中的所有单词存储为字符串。使用一个for
循环,你可以指出单词被一个空格分开。然后,循环检测评论中的单词,并将它们存储为字符串。最后,这些列表被转换成序列,准备存储在数据帧中。第一个系列命名为consumer_review
,第二个系列命名为polarity_label
。此时,数据已经加载到系统中。现在是为模型准备数据的时候了。
# fill in dataframe
word=[]
label=[]
for n in text:
n=n.split()
label.append(1) if n[0] =="__label__2" else label.append(0)
word.append(" ".join(n[1:]))
x_train['consumer_review'] = word
x_train['polarity_label'] = label
#view dataframe
x_train
第三步。准备数据
正如项目描述中提到的,我们现在只使用总数据的 20%。因此,您使用sklearn
中的model_selection.train_test_split
函数来表明,在整个数据集中,您只希望使用其中的 20%。
#use only 20% of data to avoid overloading your system. You can reduce or increase this number according to your convenience.
_, x_set,_, y_set = \
model_selection.train_test_split(x_train['consumer_review'],
x_train['polarity_label'], test_size=0.02)
第四步。清理数据
计算机不能像人类那样阅读文本。因此,您需要重新格式化数据集中的评论,使它们更易于系统理解。这意味着您需要删除大写、标点和冠词(a、an 和 the)。为此,您将定义一个自定义函数。
#data cleaning function
def data_prep(in_tex):
# Remove punctuations and numbers
out_tex = re.sub('[^a-zA-Z]', ' ', in_tex)
# Convert upper case to lower case
out_tex="".join(list(map(lambda x:x.lower(),out_tex)))
# Remove single character
out_tex= re.sub(r"\s+[a-zA-Z]\s+", ' ', out_tex)
return out_tex
将这个新清理的数据集存储在名为text_set
的新列表中:
#create new list with clean data
text_set=[]
for reviews in list(x_set):
text_set.append(data_prep(reviews))
现在,您创建一个新的数据帧来存储干净的数据。如果您使用与以前相同的数据帧,原始数据将被干净的数据覆盖。
x_train= pd.DataFrame()
x_train['consumer_review'] = text_set
x_train['polarity_label'] = list(y_set)
这些数据是干净的,但是仍然需要更多的预处理。首先将其分为 70%用于训练数据集,30%用于测试数据集。您像以前一样使用来自sklearn
的相同的split
函数。
#split data into 70% train and 30% test
x_train, x_test, y_train, y_test = \
model_selection.train_test_split(x_train['consumer_review'],
x_train['polarity_label'], test_size=0.30)
为了应用标记器,您需要将列表转换成数组。这样更容易处理。
#convert to array
x_train=np.array(x_train.values.tolist())
x_test=np.array(x_test.values.tolist())
y_train=np.array(y_train.values.tolist())
y_test=np.array(y_test.values.tolist())
应用记号赋予器。
#tokenizer
tokenizer = Tokenizer()
tokenizer.fit_on_texts(x_train)
word_index=tokenizer.word_index
total_size = len(word_index)+1
print(total_size)
输出:
22259
将文本输入转换为序列。这是模型最容易处理的形式。
#text to sequence
x_train = tokenizer.texts_to_sequences(x_train)
x_test = tokenizer.texts_to_sequences(x_test)
为了避免由于数组大小造成的不兼容性,您必须向数据添加填充。
#add padding to ensure the same length
max_length = 100
x_train = pad_sequences(x_train, padding="post", maxlen=max_length)
x_test = pad_sequences(x_test, padding="post", maxlen=max_length)
第五步。构建模型
使用 Keras 嵌入层、LSTM 层和密集层构建模型。
#Create Model
model = Sequential()
model.add(Embedding(total_size, 20, input_length=max_length))
model.add(LSTM(32,dropout=0.2, recurrent_dropout=0.2))
model.add(Dense(1, activation="sigmoid"))
第六步。编译模型
接下来,您指定要使用的优化器是 Adam,损失函数是二进制交叉熵,您想要关注的度量是准确性。
#compile
model.compile(optimizer='adam', loss="binary_crossentropy", metrics=['acc'])
print(model.summary())
第七步。训练模型
现在,通过将批处理大小设置为 128,将时期设置为 5,将详细设置为 1 来训练模型。这些设置可以微调,以获得更好的效果。
model.fit(x_train, y_train, batch_size=128, epochs=5, verbose=1, validation_data=(x_test, y_test))
第八步。保存模型(可选)
如果需要,可以保存模型的当前状态以备将来使用。
model.save("model.h5")
第九步。导入预训练模型(可选)
此代码用于重新加载您训练的模型,以便您可以在相同或不同的数据集上使用它。
model = keras.models.load_model("model.h5")
您已经成功完成了情感分析。精确度相当不错,但是如果你想把它投入使用,这个模型肯定需要更多的训练。
进一步测试
你在这个项目里做的情感分析真的很简单。它有很大的实验空间。以下是一些修改和通过这个项目学习更多的想法:
-
添加更多 LSTM 图层
-
添加更密集的层
-
尝试不同的优化功能
解决纷争
以下是一些可能会出现但很容易修复的常见错误:
-
"Warning: No
read.me
file"
。这表明package.json
文件丢失。package.json
文件跟踪项目的依赖关系。在安装模块之前,首先使用npm init
,因为它会为您创建package.json
文件。 -
"Error: Missing DLL"
。此错误消息可能意味着以下情况之一:"Cannot execute binary file"
。有两种方法可以解决此错误:-
您的 Windows 版本没有更新。您必须确保您使用的是最新版本的 Windows。
-
该项目没有对
C:\Users\\AppData\Local\Microsoft\Windows\ or C:\Users\\AppData\Local\Temp
文件夹的完全访问权限。最好使用admin
帐户,它拥有对所有文件的完全访问权限,或者向管理员请求访问权限。 -
通过将
-s
选项传递给ln
命令,后跟目标文件和链接名称,创建一个符号链接。 -
不要用
apt-get
安装,因为这不是一回事。
-
-
"npm WARN package.json chat@4.5.0 No repository field"
。
npm WARN package.json chat@4.5.0 No README data
npm ERR! Windows_NT 6.1.7600
node_modules\\npm\\bin\\npm-cli.js" "install"
npm ERR! node v4.5.0
npm ERR! npm v2.15.9
npm ERR! code ENOENT
npm ERR! errno ENOENT
npm ERR! syscall getaddrinfo
npm ERR! enoent getaddrinfo ENOENT registry.npmjs.org:443
npm ERR! enoent This is most likely not a problem with npm itself
npm ERR! enoent and is related to npm not being able to find a file.
npm ERR! enoent
npm ERR! Please include the following file with any support request:
npm ERR! C:\xampp\htdocs\chat\npm-debug.log. "
这些错误表明您已经安装了 NodeJS。如果 Keras Tokenizer 导致问题,请使用以下替代方法:
from keras.preprocessing import text
tokenizer = text.Tokenizer(...)
Note
不导入也不暴露文本子模块。所以有时候可能会导致错误。但是,可以使用本节中讨论的修复方法来处理此错误。
摘要
以下是您在本章中学到的所有内容的快速回顾:
-
LSTM 能够学习长期依赖关系。LSTM 的应用包括手写识别、语音识别、疾病预测和音乐创作。
-
模拟具有可微分的优点,这使得它适合于反向传播。
-
LSTMs 是“消失梯度”问题的完美解决方案,当我们试图使用基于梯度的优化技术来训练神经网络模型时,就会出现这种问题。
-
LSTM 中的门是遗忘门、输入门和输出门。
-
LSTM 决定从单元状态中丢弃什么信息。这个决定是由“忘记门层”做出的,它使用了一个 Sigmoid 激活函数。
-
准确性是衡量我们的机器学习模型在预测给定观察的正确类别方面有多好的一个指标。不要在不平衡的问题上使用准确性。
-
该模型通过损失函数进行学习。这是一种评估特定算法对给定数据的处理效果的方法。如果预测与实际结果偏离太多,损失函数就很大。如果偏差小,损失函数也小。
-
情感分析是文本的上下文挖掘,从源材料中识别和提取主观信息。然而,社交媒体流的分析通常局限于基本的情感分析和基于计数的指标。情感分析是分析文本数据并将观点分类为消极、积极或中立的自动化过程。这是最常见的文本分类工具。
-
通过细粒度的情感分析,我们可以更精确地了解意见的极性水平,方法是将积极、中立和消极意见的基本类别扩展到以下类别:非常积极、积极、中立、消极和非常消极。
-
有许多方法和算法来实现情感分析系统,并且它们被分类为基于规则的、自动的和混合的。
-
交叉验证包括将训练数据分成一定数量的训练折叠(75%的训练数据)和相同数量的测试折叠(25%的训练数据)。然后,使用训练折叠来训练分类器,并根据测试折叠对其进行测试,以获得性能指标。该过程重复多次,并计算每个指标的平均值。如果测试集总是相同的,您将冒过度适应该测试集的风险,这意味着您可能会根据给定的数据集调整您的分析,以至于无法分析不同的数据集。
-
精度衡量被预测(正确和不正确)属于给定类别的所有文本中,该文本被正确预测为属于该类别的程度。
-
召回衡量在所有文本中应该被预测属于给定类别的文本被正确预测为属于该类别的程度。我们输入分类器的数据越多,召回率就越高。
参考
本章中使用的资源如下:
进一步阅读
你有兴趣了解本章中的一些主题吗?这里有一些很棒的链接可以查看:
-
关于 LSTM:
https://colah.github.io/posts/2015-08-Understanding-LSTMs/
-
用 Keras 嵌入单词:
https://machinelearningmastery.com/use-word-embedding-layers-deep-learning-keras/
-
极性和基于主题的情感分析的区别:
https://blog.bitext.com/polarity-topic-sentiment-analysis
-
文字的极性和强度:
www.aclweb.org/anthology/W18-3306/
-
情感分析极性:
www.kdnuggets.com/2018/08/emotion-sentiment-analysis-practitioners-guide-nlp-5.html
-
保存和加载预训练模型:
www.tensorflow.org/tutorials/keras/save_and_load
五、音乐生成
每个人都喜欢音乐。不管我们的心情或偏好如何,总有一首歌适合我们所有人。考虑到这一点,在本章中,我们将使用一种叫做门控递归单元(GRU)的神经网络来生成一首歌曲。别担心,你不需要成为一名专业的音乐家来使用深度学习生成音乐。
GRU 概述
GRUs 旨在解决标准循环神经网络(RNN)带来的消失梯度问题。当梯度变得越来越小时,消失梯度问题出现在深度神经网络中,这阻止了权重改变其值。
为了解决梯度消失的问题,GRUs 使用了一个更新门和一个重置门。这些门是决定什么信息应该被传递到输出的两个向量。他们可以被训练储存很久以前的信息,而不会随着时间的推移而抹去。他们还可以删除与预测无关的信息。
与 RNN 之前拥有的四个节点的简单神经网络不同,GRU 拥有一个包含多种操作的单元。GRU 将信息向前传递到几个时间段,以便影响未来的时间段。换句话说,该值会在内存中存储一段时间。在一个临界点,它被取出并与当前状态一起使用,以便在将来的某个日期进行更新。GRU 网络涉及的参数更少,这使得它们的训练速度更快。网络学会如何使用门来保护它的记忆,这样它就能够在更长的时间内进行长期预测。类似于传统的 RNNs,GRU 网络在每个时间步产生一个输出,这个输出用于使用梯度下降来训练网络。
GRU 网络的应用包括:
-
复调音乐造型
-
语音信号建模
-
手写识别
-
语音识别
-
人类基因组的研究
-
股票市场分析
GRU 是如何工作的
GRU 是循环神经网络的门控版本。隐藏状态用于传输信息。GRU 是直观的,因为它能够确定将多少过去的信息传递给未来,而无需显式编程。与 LSTM 不同,GRU 只有两个门:
-
更新门:更新门帮助模型确定有多少过去的信息(来自以前的时间步骤)需要传递给未来。这真的很强大,因为该模型可以决定复制过去的所有信息,并消除梯度消失问题的风险。
-
重置门:本质上,该门用于从模型中决定要忘记多少过去的信息。
图 5-1 显示了一个 GRU 的内部工作方式,这里Rg(t)
是复位门,Ug(t)
是更新门。
图 5-1
在 GRU 里面
起重机实习
让我们来看看 GRU 每个阶段的过程。记住,GRU 的输入是三维数组的形式。
第一阶段
电流存储器的功能非常像一个门,但它集成在复位门中,用于在输入中引入一些非线性,并使输入为零均值。此外,它减少了先前信息对当前信息的影响,而当前信息会传递到未来。请遵循以下步骤:
-
引入新的存储内容,使用复位门存储过去的相关信息。输入
nt
乘以权重W
和Hs(t-1
)
Rgt
。 -
计算复位门
Rgt
和Rgt * Hs(t-1)
之间的哈达玛乘积。这将决定从之前的时间步骤中删除什么。 -
根据输入到矢量中的输入,
Rgt
矢量被赋予一个接近于0
或1
的值。 -
将第 1 步和第 2 步的结果相加。
-
应用非线性激活功能
tanh
。数学表示如下:Hst' = tanh(W【Rg】t* Hs(t-1),n t )
第二阶段
本质上,该模型使用重置门来决定忘记多少过去的信息。它类似于 LSTM 循环单元中输入门和遗忘门的组合。总误差由所有时间步的误差总和给出。类似地,该值可以计算为每个时间步长的梯度总和。请遵循以下步骤:
-
将输入的
nt
乘以权重Wrg
,将Hst-1
乘以Wrg
。 -
计算步骤 1 中的乘积之和。
-
对结果应用 Sigmoid。
模型可以学习将向量 Rg t 设置为接近0
或1
。如果向量Rgt
接近0
,当前内容的大部分将被忽略,因为它与我们的预测无关。同时,由于在这个时间步Rgt
将接近0
,因此1-Rgt
将接近1
,从而允许保留大部分过去的信息。
数学表示如下:
- Rg t =乙状结肠(Wrg【Hst-1,n t )
第 3 阶段
更新门是模型可以决定从过去复制所有信息并消除消失梯度问题的风险的地方。请遵循以下步骤:
-
将输入的
nt
乘以权重Wug
,将Hst-1
乘以Wug
。 -
计算步骤 1 中的乘积之和。
-
步骤 3:对结果应用 Sigmoid。
模型可以学习将向量 Ug t 设置为接近0
或1
。如果向量Ugt
接近0
,当前内容的大部分将被忽略,因为它与我们的预测无关。同时,由于Ugt
在这个时间步将接近0
,(1-Ugt
)将接近1
,允许保留大部分过去的信息。
数学表示如下:
- Ug t =乙状结肠(W ug Hs t-1 ,n t )
第四阶段
当前时间步长的存储器是 GRU 的最后一级。网络计算保存当前单元信息的向量Hst
,并将其传递给网络。
为此,更新门确定从当前存储器内容Hst
收集什么,以及从先前步骤Hs(t-1)
收集什么。这是按如下方式完成的:
-
将逐元素乘法应用于更新门
Ugt
和Hs(t-1
)
。 -
对
(1-Rg
t
)
和Hs
t
’应用逐元素乘法。 -
将第 1 步和第 2 步的结果相加。
' Hst=(1-Rgt)(Hst-1)+Ugt Hst'
格鲁层
为了设计有效的 GRU,我们可以利用以下类型的层:
-
嵌入层:用于为进来的词创建词向量。它位于输入层和 GRU 层之间。嵌入层的输出是 GRU 层的输入。
-
GRU 层:循环神经网络层,将序列作为输入,可以返回序列或矩阵。
-
丢弃层:一种正则化技术,包括在训练期间的每次更新时将输入单元的一部分设置为 0,以防止过拟合。
-
密集层:全连接层,其中每个输入节点连接到每个输出节点。
-
激活层:决定 GRU 使用哪个激活函数来计算节点的输出。
Note
keras.layers.CuDNNGRU
提供由 cuDNN 支持的快速 GRU 实施。NVIDIA CUDA 深度神经网络库(cuDNN)是一个 GPU 加速的深度神经网络图元库。cuDNN 为标准例程提供了高度优化的实现,例如前向和后向卷积、池化、规范化和激活层。
比较 GRU 和 LSTM
乍一看,LSTMs 和 GRUs 似乎非常相似。但是,需要注意两者之间的细微差别:
-
GRU 只有两个大门,而 LSTM 有三个。
-
LSTMs 控制内存内容(单元状态)的公开,而 gru 将整个单元状态公开给网络中的其他单元。LSTM 单元具有独立的输入和遗忘门,而 GRU 通过其复位门同时执行这两种操作。
-
LSTM 给了我们最大的控制,因此,更好的结果。它确实带来了更多的复杂性和更高的运营成本。
与 LSTM 相比,GRU 使用的训练参数更少,因此使用的内存更少,执行速度更快,训练速度更快,而 LSTM 在使用较长序列的数据集上更准确。
你现在知道什么是 GRU 以及它与 LSTM 有什么不同了。是时候看看如何将 GRU 变成音乐 DJ 了。
项目描述
在这个项目中,我们将创建一个 GRU,它将根据给定的音符序列生成音乐。一旦模型被训练,当一组随机的音符被提供作为输入时,它应该能够预测接下来的音符序列。数据集由 MIDI 文件组成,我们从中提取音乐成分作为模型的特征。GRU 模型需要以下两个对象来生成音乐:
-
音符对象:包含关于音高、八度和偏移的信息。
-
和弦对象:一组音符的容器。
然后使用一键编码的过程将音符和和弦对象转换成整数向量。这使得模型能够学习各种音乐作品中的模式。然后,该预测将从整数向量重新转换为音符,并保存为 MIDI 文件。目标是获得一个听起来尽可能与原曲相似的 MIDI 文件。
![img/488562_1_En_5_Fig2_HTML.jpg
图 5-2
GRU 音乐项目工作流程
关于数据集
名称: 13 万个 MIDI 文件集合
流派包括:流行音乐、古典音乐(钢琴/小提琴/吉他)、电子音乐、电子游戏和电影/电视主题
来源: www.reddit.com/r/datasets/comments/3akhxy/the_largest_midi_collection_on_the_internet/
创作人:迷笛侠
在你开始创作音乐之前,你需要理解一些术语和概念。
重要术语和概念
在开始这个项目之前,这里有一些你应该知道的方便的术语和概念。
图 5-3
一键编码工作流程
-
注:表示音乐声音的符号。它用乐谱表示声音的音高和音长。
-
和弦:同时演奏的两个或两个以上音符的组合。
-
音高:声音的频率,或者说高低。用字母 A 到 g 表示。
-
偏移:音符在乐曲中的位置。
-
八度音程:占据两个音之间(包括两个音在内)间隔的一系列八个音,其中一个音的振动频率是另一个音的两倍或一半。
-
流:music 21 对象的基本容器;在 Music21 中,对象可以基于从该容器的开始的偏移来按时间排序和/或放置。
-
MIDI 文件:(读作“mid-ee”):乐器数字接口文件的缩写。它有两个扩展--。MID 或. MIDI。它不包含实际的音频,只包含诸如音高、和弦、音符等信息。
-
分类交叉熵:用于单标签分类的损失函数。这是指只有一个类别适用于每个数据点。
-
RMSprop 优化器:类似于梯度下降算法,但是有动量。它将振动限制在垂直方向。
-
One-hot 编码:将分类变量转换为整数或 0 和 1 的向量的过程。例如,在图 5-3 中,我们有一个包含元素 A、B、C 和 A 的数据集。目前它们是字符。然而,我们的模型只能接受数值。因此,我们使用一键编码将字符转换为向量,每个向量有一行三列来表示每个类别。对于本例,映射是最后一列表示类别 A,中间一列表示类别 B,第一列表示类别 c。因此,1 用于指示变量属于哪个类别。
最后,你已经准备好开始这个项目了。让我们从准备这个项目的包开始。
必需的库
让我们看看这个项目所需库的列表。
-
操作系统(内置 Python)
-
Os.path(内置 Python)
-
随机(内置 Python 2 及更高版本)
-
Shutil(内置 Python 库)
-
NumPy(安装说明见第一章)
-
熊猫(安装说明见第一章)
-
Matplotlib(安装说明见第一章)
-
TensorFlow(安装说明见第一章)
-
Keras(安装说明见第一章)
-
Music21(本章中的安装说明)
看起来我们需要做的就是安装 Music21 和 Random2。让我们现在做那件事。
安装说明
在第一章,我们安装了每个项目所需的标准库。这些是这个特定项目中使用的附加库的安装说明。
使用画中画
为了确保我们可以安装这些库,而不用考虑我们的系统,我们将使用 Python 包 PIP。
-
在终端中使用以下命令安装 Music21。
-
在终端中使用以下命令检查 Music21 的安装。
Pip3 install Music21
- 在终端中使用以下命令安装 Random2。
Pip3 show Music21
- 在终端中使用以下命令检查 Random2 的安装。
Pip3 install random2
图 5-4
MuseScore 官方网站
- 安装 MuseScore。因为我们正在处理 MIDI 文件,我们需要软件来打开和查看这些文件。前往官网
https://musescore.org/en
(见图 5-4 )。MuseScore 是开源免费的。该网站会自动检测您使用的系统,并推荐合适的 MuseScore 版本。只需点击免费下载按钮。
Pip3 show random2
此后,根据您使用的操作系统,说明会有所不同。
使用 Windows
在 Windows 中,请按照下列步骤操作:
-
找到下载文件的位置,双击安装程序开始安装。
-
单击每个屏幕上的下一步按钮,直到出现安装按钮。点击此按钮,MuseScore 将被安装。
-
一旦弹出完成按钮,就意味着 MuseScore 已经成功安装。退出安装程序。
-
进入开始菜单,选择所有程序➤ MuseScore,启动 MuseScore。
使用 macOS
在 macOS 中,请按照下列步骤操作:
-
找到文件(通常在
Downloads
文件夹中)并运行它。将出现 MuseScore 图标。 -
将图标拖至
Applications
文件夹。它可能会询问管理员密码;输入密码,然后点按“鉴定”。安装将开始。 -
要运行 MuseScore,导航至
Applications
文件夹并点击 MuseScore 图标。
使用 Linux
在 Linux 中,请遵循以下步骤:
-
在终端类型中:
-
然后按回车键。
-
出现提示时,按 Y,然后再次返回。将安装 MuseScore。
-
MuseScore 可以通过
application
菜单打开,或者通过在终端中键入musescore
并按回车键打开。
sudo apt-get install musescore
安装故障排除
以下是一些可能会出现但很容易修复的常见错误:
- 确保所有安装都是最新的。
Note
random2
提供了 Python 2.7 的随机模块的 Python 3 移植版本。它也被移植到 Python 2.6 中。
-
尽量不要手动安装任何东西。请改用 pip 安装程序。
-
如果你得到一个“不在 sudoers 文件中”的错误,可以通过编辑文件来修复。您将需要在终端中
su
到根目录,然后运行visudo
并搜索看起来像root ALL=(ALL) ALL
的一行。复制那一行,用用户名替换root
。然后保存文件。或者,您可以在“用户”偏好设置面板中将用户设置为管理员。然后,您将拥有管理员权限。 -
在 Windows 中,在使用 pip 之前,必须将 Python 路径添加到环境变量中。
这些是你可能遇到的最常见的问题。为了获得更多的帮助,有一个谷歌小组,用户可以在那里发布关于 Music21 的问题。该组的链接可以在本章末尾的“参考资料”部分找到。
GRU 建筑
要使用 GRU 生成音乐,您需要一个具有正确层数和正确顺序的模型。图 5-5 显示了我们将用于这个项目的模型。
图 5-5
该项目的 GRU 建筑
让我们来看看 GRU 的“蓝图”。该模型将由四层组成:
-
GRU 层 - 3
-
脱落层- 3
-
密集层- 2
-
激活层- 1
GRU 层学习音乐作品中的模式。它们利用复位门和更新门来提取模式并“记忆”它。dropout 层完全按照它所暗示的那样工作。通过“丢弃”模型学习的部分模式,我们避免了过拟合模型。最后,密集层是一个完全连通的层,可以提高模型的准确性。
-
Activation function: 我们将使用 Softmax 函数,因为它非常适合于音乐作品中组件的识别和分类。
-
损失函数:由于我们是把这个项目当做一个分类问题来处理,分类交叉熵损失函数会给我们最好的结果。它是专门为分类而设计的。
-
优化器:我们使用 RMSprop 优化器,因为它有伪曲率信息。此外,它可以处理随机目标。这两个特性对于小批量学习很重要,这是我们的模型使用的过程。
Note
嵌入层通常用于文本分析。所以我们不会在模型中使用它。
程序
您终于准备好实现 GRU 了。让我们打开一个新的 Jupyter 笔记本并开始工作。
第一步。导入库
为此项目导入必要的库。
import tensorflow as tf
from music21 import converter, instrument, note, chord,stream
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import os
import random
from keras.layers.core import Dense, Activation, Flatten
from keras.layers import GRU,Convolution1D,Convolution2D, Flatten, Dropout, Dense
from keras import utils as np_utils
from tensorflow.keras import layers
设置文件路径,以便 Jupyter 笔记本可以访问该数据集。使用os.chdir
命令并输入文件路径。
os.chdir("//Users//vinitasilaparasetty//Downloads//Project 1- GRU//Input//music_files")#enter the file path from your system.
声明将用于特征提取的变量:
musical_note = []
offset = []
instrumentlist=[]
第二步。加载数据
创建filenames
变量,该变量使用随机模块随机选择并加载 MIDI 文件。我们可以设置随机选择的文件数量;在这种情况下,我们选择了5
。创建musiclist
变量,加载数据集中所有的 MIDI 文件。
filenames = random.sample(os.listdir('D:\\Proj\\vinita\\Project 1- GRU\Input\\music_files\\'), 5) #Only 5 files are taken at random
musiclist = os.listdir('D:\\Proj\\vinita\\Project 1- GRU\Input\\music_files\\')
第三步。特征抽出
因为音符的最重要部分可以使用音高的字符串符号重新创建,所以使用音符对象的字符串符号来附加每个音符对象的音高。请遵循以下步骤:
-
使用
string_midi = converter.parse(r1)
解析注释并初始化parsednotes = None
变量。 -
使用
parts = instrument.partitionByInstrument(string_midi)
检测仪器类型,并使用instrumentlist.append(parts.parts[0].getInstrument().instrumentName)
更新仪器计数器。 -
使用
if parts: parsednotes = parts.parts[0].recurse()
提取尖锐音符。 -
使用
else: parsednotes = string_midi.flat.notes
提取平音符。 -
使用
for element in parsednotes: offset.append(element.offset)
提取偏移量。 -
使用
if isinstance(element, note.Note):
和musical_note.append(str(element.pitch))
开始向musical_note[ ]
数组添加音符。 -
用一个
.
来分隔每个音符:elif isinstance(element, chord.Chord):
和musical_note.append('.'.join(str(n) for n in element.normalOrder))
。
for file in filenames:
matching = [s for s in musiclist if file.split('_')[0] in s]
print(matching)
r1 = matching[random.randint(1, len(matching))]
string_midi = converter.parse(r1)
parsednotes = None
parts = instrument.partitionByInstrument(string_midi)
instrumentlist.append(parts.parts[0].getInstrument().instrumentName)
if parts: # file has instrument parts
parsednotes = parts.parts[0].recurse()
else: # file has flat notes
parsednotes = string_midi.flat.notes
for element in parsednotes: #detect offsets
offset.append(element.offset)
if isinstance(element, note.Note):
musical_note.append(str(element.pitch))
elif isinstance(element, chord.Chord):
musical_note.append('.'.join(str(n) for n in element.normalOrder))
第四步。探索性数据分析
使用以下方法计算 MIDI 文件中独特乐器的数量:
pd.Series(instrumentlist).value_counts()
pd.Series(instrumentlist).value_counts()
使用以下方法计算音符和和弦的数量:
pd.Series(musical_note).value_counts()
pd.Series(musical_note).value_counts()
使用以下命令可视化 MIDI 文件中的偏移:
offset = [float(item) for item in offset]
offset = [float(item) for item in offset]
plt.plot(offset)
plt.show() # this shows that offset is normally started from 0 for each musical file
输出应该类似于图 5-6 。
图 5-6
生成偏移的散点图
第五步。数据准备(输入)
使用变量sequence_length = 100
变量将前 100 个音符序列作为输入。
现在我们来整理一下笔记:
-
在
musical_note[ ]
数组中,按升序使用pitchcategory = sorted(set(item for item in musical_note))
。 -
然后通过将 MIDI 文件中的音符映射到整数来使用一个热编码。
-
使用以下内容创建一个字典来保存映射的注释:
-
通过声明
model_input_original = []
变量来创建模型的输入。 -
使用
model_output = []
收集模型的输出。 -
将作为输入序列输入的音符分开:
for i in range(0, len(musical_note) - sequence_length, 1): sequence_in = musical_note[i:i + sequence_length]
-
将预期输出中的注释分开:
sequence_out = musical_note[i + sequence_length]
-
将输入序列添加到字典中:
model_input_original.append([note_encoding[char] for char in sequence_in])
-
添加音符的输出序列:
model_output.append(note_encoding[sequence_out])
note_encoding = dict((note, number) for number, note in enumerate(pitchcategory))
MIDI 文件的组成模式存储在n_patterns = len(model_input_original)
变量中。现在我们已经有了输入,是时候对它进行整形,以便它可以在模型上使用。使用下面的行来改变数据的形状。
model_input = np.reshape(model_input_original, (n_patterns, sequence_length, 1))
在重塑数据之后,是时候对其进行标准化,以获得准确的结果,避免在训练过程中出现问题。
sequence_length = 100
# Arranging notes and chords in ascending order
pitchcategory = sorted(set(item for item in musical_note))
# One hot encoding
note_encoding = dict((note, number) for number, note in enumerate(pitchcategory))
model_input_original = []
model_output = []
# Prepare input and output data for model
for i in range(0, len(musical_note) - sequence_length, 1):
sequence_in = musical_note[i:i + sequence_length]
sequence_out = musical_note[i + sequence_length]
model_input_original.append([note_encoding[char] for char in sequence_in])
model_output.append(note_encoding[sequence_out])
n_patterns = len(model_input_original)
# converting data for compatibility with GRU
model_input = np.reshape(model_input_original, (n_patterns, sequence_length, 1))
# standardizing model input data
model_output = np_utils.to_categorical(model_output)
Len_Notes = model_output.shape[1]
model_input = model_input / float(Len_Notes)
第六步。构建模型
您现在已经准备好创建 GRU 模型了。请遵循以下步骤:
-
使用
model_GRU = tf.keras.models.Sequential()
初始化模型。 -
使用
model_GRU.add(layers.GRU(16,input_shape=(model_input.shape[1], model_input.shape[2]),return_sequences=True))
添加第一个 GRU 层。该层在数据经过处理后返回值。 -
使用
model_GRU.add(layers.Dropout(0.3))
添加脱落层。 -
这是其次是第二个 GRU 层使用
model_GRU.add(layers.GRU(64, return_sequences=True))
。 -
使用
model_GRU.add(layers.Dropout(0.3))
添加第二个脱落层。 -
使用
model_GRU.add(layers.GRU(64))
添加最后的 GRU 层。 -
使用
model_GRU.add(layers.Dense(16))
添加第一个密集层。 -
使用
model_GRU.add(layers.Dropout(0.3))
添加最终的脱落层。 -
使用
model_GRU.add(layers.Dense(Len_Notes))
,你已经为最终的密集层做好了准备。 -
现在你只需要添加最后一层,也就是激活层。使用
model_GRU.add(layers.Activation('softmax'))
将 Softmax 设置为激活功能。 -
要完成模型的设置,使用
model_GRU.compile(loss='categorical_crossentropy', optimizer="rmsprop")
添加编译特性。在这里,您将损失函数设置为分类交叉熵,将优化器设置为rmsprop
。 -
现在使用
model_GRU.fit(model_input, model_output, epochs=30, batch_size=64)
为模型设置时期和批量大小。对于第一次运行,您将从 30 个时期和 6 个批量开始。这可以在以后更改以改进模型。 -
为了确保模型的架构是正确的,您可以使用
model_GRU.summary()
来获得模型结构的概述。
model_GRU = tf.keras.models.Sequential()
model_GRU.add(layers.GRU(16,input_shape=(model_input.shape[1], model_input.shape[2]),return_sequences=True))
model_GRU.add(layers.Dropout(0.3))
model_GRU.add(layers.GRU(64, return_sequences=True))
model_GRU.add(layers.Dropout(0.3))
model_GRU.add(layers.GRU(64))
model_GRU.add(layers.Dense(16))
model_GRU.add(layers.Dropout(0.3))
model_GRU.add(layers.Dense(Len_Notes))
model_GRU.add(layers.Activation('softmax'))
model_GRU.compile(loss='categorical_crossentropy', optimizer="rmsprop")
model_GRU.summary() #Displays model architecture
第七步。训练模型
按照以下步骤训练模型:
-
使用
int_to_note = dict((number, note) for number, note in enumerate(pitchcategory))
为笔记初始化一个新的字典。 -
使用
pattern = model_input_original[0]
初始化一个新数组来检测 MIDI 文件中的模式。 -
使用
prediction_output = [ ]
初始化一个新数组来存储预测的音符。
# initializing data for model prediction
int_to_note = dict((number, note) for number, note in enumerate(pitchcategory))
pattern = model_input_original[0]
prediction_output = []
model_GRU.fit(model_input, model_output, epochs=30, batch_size=64)
第八步。预报
通过使用 for note_index in range(500)
并遵循以下步骤,您可以预测接下来的 500 个音符:
-
必须使用
prediction_input = np.reshape(pattern, (1, len(pattern), 1))
对预测输入进行整形,以与 GRU 兼容。 -
需要使用
prediction_input = prediction_input / float(Len_Notes)
对输入进行标准化。 -
接下来,使用
prediction_GRU = model_GRU.predict(prediction_input, verbose=0)
获得预测音符。 -
使用
index_GRU = np.argmax(prediction_GRU)
找到频率最高的音符。 -
使用以下方法将整数重新转换为相应的音符:
-
使用以下命令将注释添加到数组中:
index = index_GRU
result = int_to_note[index]
prediction_output.append(result)
pattern = np.append(pattern,index)
pattern = pattern[1:len(pattern)]
# generate 500 notes
for note_index in range(500):
prediction_input = np.reshape(pattern, (1, len(pattern), 1))
prediction_input = prediction_input / float(Len_Notes)
prediction_GRU = model_GRU.predict(prediction_input, verbose=0)
index_GRU = np.argmax(prediction_GRU)
index = index_GRU
result = int_to_note[index]
prediction_output.append(result)
pattern = np.append(pattern,index)
pattern = pattern[1:len(pattern)]
第九步。数据准备(偏移)
按照以下步骤准备偏移:
-
使用
offlen = len(offset)
找到偏移的长度。 -
现在使用
DifferentialOffset = (max(offset)-min(offset))/len(offset)
找到偏移的微分。 -
创建使用
offset2 = offset.copy()
收集的偏移的副本。 -
使用
output_notes = [ ]
初始化输出音符数组,并使用i = 0
将计数器设置为零。 -
现在使用
offset = [ ]
初始化偏移量数组,并使用initial = 0
将计数器设置为零。 -
使用以下公式开始计算偏移量:
for i in range(len(offset2)): offset.append(initial) initial = initial+DifferentialOffset
-
现在是时候检测音符和和弦了。使用
i=0
重置计数器,确保计数器为零。 -
使用以下内容搜索句号,以检测每个音符开始的时间:
for pattern in prediction_output: if ('.' in pattern) or pattern.isdigit():
-
使用以下方法将和弦从弦中分离出来,并将其添加到音符数组中:
notes_in_chord = pattern.split('.') notes = [ ]
-
使用以下方法检测音符并将其分开:
```py
for check_note in notes_in_chord:
gen_note = note.Note(int(check_note))
gen_note.storedInstrument = instrument.Guitar()
notes.append(gen_note)
gen_chord = chord.Chord(notes)
```
- 使用以下方法检测偏移:
```py
gen_chord.offset = offset[i]
output_notes.append(gen_chord)
```
- 为了防止异常,请使用以下代码:
```py
else:
gen_note = note.Note(pattern)
gen_note.offset = offset[i]
gen_note.storedInstrument = instrument.Guitar()
output_notes.append(gen_note)
i=i+1
```
```py
# prepare notes , chords and offset separately
offlen = len(offset)
DifferentialOffset = (max(offset)-min(offset))/len(offset)
offset2 = offset.copy()
output_notes = []
i = 0
offset = []
initial = 0
for i in range(len(offset2)):
offset.append(initial)
initial = initial+DifferentialOffset
# Differentiate notes and chords
i=0
for pattern in prediction_output:
if ('.' in pattern) or pattern.isdigit():
notes_in_chord = pattern.split('.')
notes = []
for check_note in notes_in_chord:
gen_note = note.Note(int(check_note))
gen_note.storedInstrument = instrument.Guitar()
notes.append(gen_note)
gen_chord = chord.Chord(notes)
gen_chord.offset = offset[i]
output_notes.append(gen_chord)
else:
gen_note = note.Note(pattern)
gen_note.offset = offset[i]
gen_note.storedInstrument = instrument.Guitar()
output_notes.append(gen_note)
i=i+1
```
第十步。将输出存储为 MIDI 文件
按照以下步骤将输出存储为 MIDI 文件:
-
使用
os.chdir('D:\\Proj\\vinita\\Project 1- GRU\Output\\')
指定存储 MIDI 文件的文件路径。 -
使用
midi_stream = stream.Stream(output_notes) #create stream
创建带有输出的流。 -
使用下面的代码创建一个 MIDI 文件:
midi_stream.write('midi', fp='GRU_output.mid') #create MIDI file using stream
os.chdir('D:\\Proj\\vinita\\Project 1- GRU\Output\\') #Specify file path to store the MIDI file.
midi_stream = stream.Stream(output_notes) #create stream
midi_stream.write('midi', fp='GRU_output.mid') #create MIDI file using stream
我们现在可以使用 MuseScore 查看 MIDI 文件(参见图 5-7 )。将原始音乐作品与生成的输出进行比较,看看 GRU 的表现如何。
图 5-7
使用 MuseScore 查看生成的输出
进一步测试
这里有一些想法可以尝试,并从这个项目中学到更多:
-
尝试增加和减少 GRU 中的层数。
-
尝试增加下降图层。
-
尝试增加和减少历元的数量,以查看其对模型结果的影响。
解决纷争
以下是您可能遇到的一些常见错误,这些错误很容易修复:
-
当您开始定型模型时,可能会出现以下错误:
WARNING: Logging before flag parsing goes to stderr.
不要担心这个。不会影响你的节目。
-
如果您使层有状态,请记住指定大小。
-
确保所有路径文件都是正确的。
-
数据量相当大,可能会耗尽 RAM。若要避免此问题,请减少定型集中的文件数量。
-
当测试其他数据集时,如果识别乐器有问题,请确保数据集包含在单个乐器上演奏的音乐片段。
-
使用
.show('midi')
在 Jupyter 笔记本中直接播放 MIDI 文件。
摘要
以下是你在本章中学到的所有内容的回顾。
-
GRU 能够学习长期依赖,而不是香草 RNN。
-
在反向传播过程中,梯度随着通过神经网络向后移动而减小。这就是所谓的消失梯度问题。
-
gru 的架构比 LSTMs 简单,训练速度也更快。
-
GRUs 的应用包括复调音乐建模、语音信号建模、手写识别、语音识别、人类基因组研究和股票市场分析。
-
GRU 有两个门:复位门和更新门。
-
GRU 中的不同图层包括嵌入图层、GRU 图层、下降图层和密集图层。
-
我们可以将 RNN 层设置为有状态的,这意味着为一批中的样本计算的状态将在下一批中作为样本的初始状态重用。
-
GRU 的输入是三维数组的形式。第一个维度表示批量大小,第二个维度表示我们输入到序列中的时间步长数,第三个维度表示一个输入序列中的单元数。
-
电流存储器的功能非常像一个门,但它集成在复位门中,用于在输入中引入一些非线性,并使输入为零均值。此外,它减少了以前的信息对传递到未来的当前信息的影响。
-
模型使用重置门来决定要忘记多少过去的信息。它类似于 LSTM 循环单元中输入门和遗忘门的组合。
-
更新门是模型可以决定从过去复制所有信息并消除消失梯度问题的风险的地方。
-
当前时间步长的存储器是 GRU 的最后一级。网络计算 Hs t ,这是一个向量,它保存当前单元的信息,并将其传递给网络。
-
分类交叉熵是用于单标签分类的损失函数。这是指每个数据点仅适用一个类别。
-
RMSprop 优化器类似于梯度下降算法,但具有动量。它限制垂直方向的振动。
-
一键编码是一个过程,通过该过程,分类变量被转换成可以提供给 ML 算法的形式,以在预测中做得更好。
参考
本章中使用的参考资料如下:
资源
以下是对你的项目有帮助的附加材料的链接。
-
数据集:
-
音乐疑难解答帮助 21:
-
MuseScore 的疑难解答帮助:
进一步阅读
有兴趣了解本章中涉及的一些主题吗?这里有一些很棒的链接可以查看:
-
GRU 如何解决渐变消失问题:
-
关于 MIDI 文件的更多信息:
-
关于音乐理论的更多信息:
-
关于音乐的官方文件 21:
-
关于 cuDNN GRU 的官方文件:
-
在各种类型的 rnn 中有状态:
-
关于一键编码的更多信息:
https://medium.com/@vinitasilaparasetty/what-is-one-hot-encoding-ffd381f9a8a2
https://hackernoon.com/what-is-one-hot-encoding-why-and-when-do-you-have-to-use-it-e3c6186d008f
-
关于 MuseScore 的更多信息:
六、图像上色
图像上色是将颜色添加到原始黑白图像的过程。这意味着艺术家需要计划配色方案,然后花时间费力地手动填充颜色。目前选择的工具是 Photoshop 或类似工具。一张照片可能需要一个月的时间来上色。在这一章中,我们将实现一个简单的卷积神经网络(CNN)模型来理解图像着色是如何工作的。
CNN 模型的灵感来源于人类的视觉。人类的眼睛扫描一个物体,大脑很快地获取该物体的独特特征,以便“识别”它。CNN 模仿这种扫描图像并识别图像的不同特征来识别它的行为。这使它成为影像数据集的理想选择。我们先来看看人类的视觉是如何工作的。然后我们将它与 CNN 模型的工作方式进行比较。
人类视觉评论
图 6-1 展示了人类视觉的全过程,需要大脑和眼睛同时协同工作。
虽然视觉始于眼睛,但对我们所见的解释发生在大脑中,即初级视觉皮层。当我们看到一个物体时,我们眼中的光感受器通过视神经向初级视觉皮层发送信号,在那里处理输入。
我们能够认出我们生活中看到的所有物体和人。大脑中神经元和连接的复杂层次结构在记忆和标记物体的过程中起着重要作用。
就像一个孩子学习识别物体一样,我们需要对数百万张带标签的图片引入一种算法,然后它才能概括输入,并对它从未见过的图像做出预测。计算机将物体以数字的形式形象化。每幅图像都可以用一组二维数字来表示,称为像素。
在视觉中,单个感觉神经元的感受域是视网膜上的特定区域,其中某些东西会激活神经元。每个感觉神经元细胞都有相似的感受野,它们的感受野是重叠的。
等级观念在大脑中起着重要作用。信息以连续的顺序存储在模式序列中。位于大脑最外层的新皮层,分层存储信息。它储存在皮层柱中,或新皮层中统一组织的神经元群中。
图 6-1
人类视觉
你现在应该对人类视觉的工作原理有了基本的了解。让我们看看如何通过使用 CNN 模拟人类视觉,使机器能够“识别”物体。
计算机视觉评论
计算机视觉是一个跨学科的科学领域,旨在使机器能够像人类一样看待世界,并以类似的方式感知世界。它寻求自动完成人类视觉系统可以完成的任务,主要使用 CNN。
CNN 的架构类似于人类大脑中神经元的连接模式,并受到视觉皮层组织的启发。单个神经元只在视野的一个有限区域对刺激做出反应,这个区域被称为感受野。这些区域的集合重叠覆盖了整个可视区域。
卷积神经网络(CNN)是一种深度学习算法,可以接受输入图像,为图像中的各个方面/对象分配重要性(可学习的权重和偏差),并能够区分彼此。
卷积有三个优点:
-
稀疏相互作用
-
参数共享
-
等变表示
CNN 可以通过应用相关的过滤器成功地捕捉图像中的空间和时间依赖性。由于所涉及的参数数量的减少和权重的可重用性,该架构对图像数据集执行更好的拟合。换句话说,网络可以被训练来理解图像的复杂度。
CNN 的作用是将图像简化成一种更容易处理的形式,而不丢失对做出好的预测至关重要的特征。当我们希望设计一个不仅擅长学习特征,而且可扩展到大规模数据集的架构时,这一点非常重要。
CNN 的应用包括:
-
图像和视频识别
-
图像分析和分类
-
媒体娱乐
-
推荐系统
-
自然语言处理
现在您已经知道了 CNN 是什么,以及它在现实生活中的应用,让我们来看看 CNN 内部的过程。
CNN 是如何工作的
CNN 的隐藏层通常由输入层、卷积层、汇集层和全连接层组成(见图 6-2 )。每一层都通过可微函数将一个体积转换成另一个体积。
图 6-2
CNN 的标准架构
输入层
当计算机看到一幅图像(以一幅图像作为输入)时,它将看到一个像素值数组。根据图像的分辨率和大小,它将看到 R x C x 3 的张量,其中 R 表示行,C 表示列,3 表示 RGB 值。这些数字中的每一个都被赋予一个从 0 到 255 的值,该值描述了该点的像素强度。这些数字是计算机仅有的输入。
卷积层:内核
卷积层对两个信号进行操作:
-
一维的
-
二维(它接受两幅图像作为输入,产生第三幅图像作为输出)
数学上,卷积层可以表示如下:
特征图:f
输入:i
内核:k
在卷积层的第一部分中执行卷积运算所涉及的元素被称为内核/滤波器。卷积运算的目的是提取低层特征,如边缘、颜色、梯度方向等。,来自输入图像。
想象一下,我们在一个黑暗的房间里,只用一个手电筒就可以观察周围的环境。手电筒让我们一次只能看到房间的一小部分。
CNN 以类似的方式工作。手电筒被称为滤光器,手电筒覆盖的照明区域被称为感受野。
过滤器以一定的步幅值向右移动,直到解析完整的宽度。然后,它以相同的步幅值向下滑动或卷积到图像的开头(左侧),并重复该过程,直到遍历整个图像。
在图像具有多个通道(例如,RGB)的情况下,内核具有与输入图像相同的深度。将所有结果与偏差相加,以产生挤压的单深度通道卷积特征输出。
该操作有两种结果:
-
有效填充:与输入相比,卷积特征的维数减少。
-
相同填充:维度要么增加,要么保持不变。
从第二卷积层向前,输入是从第一层产生的激活图。因此,输入的每一层基本上都描述了原始图像中某些低级特征出现的位置。现在,当我们在其上应用一组过滤器时(通过第二个卷积层),输出将是代表更高级特征的激活。当我们通过网络和更多的卷积层时,我们会得到代表越来越复杂功能的激活图。随着我们深入网络,过滤器开始有越来越大的感受域,这意味着它们能够考虑来自原始输入量的更大区域的信息。(换句话说,它们对更大的像素空间区域更敏感。)
Note
滤镜的深度必须与图像的深度相同。
上采样层
上采样层是一个简单的层,没有权重,会使输入的维度加倍。在传统卷积层之后,它可以用于生成模型。
DepthwiseConv2D
深度方向可分离卷积仅执行深度方向空间卷积的第一步(分别作用于每个输入通道)。
汇集层
汇集是一个基于样本的离散化过程。目标是对输入表示(图像、隐藏层输出矩阵等)进行下采样。),因此减少了它的维数,并允许对装仓的子区域中包含的特征进行假设。
与卷积层类似,汇集层负责减小卷积要素的空间大小。它也称为缩减像素采样层。这是为了降低处理数据所需的计算能力。通过降维,参数或权重的数量减少了 75%,从而降低了计算成本。此外,它对于提取旋转和位置不变的主要特征是有用的,因此保持了有效训练模型的过程。
池化的工作方式是在特征图上放置一个较小的矩阵,并在该框中选取最大值。矩阵在整个特征图中从左到右移动,每次选取某个值。然后这些值形成一个新的矩阵,称为汇集特征图。
共有三种类型的池:
-
Max pooling: 返回内核覆盖的图像部分的最大值。
-
最大池也作为噪音抑制剂。它完全丢弃有噪声的激活,并且在降维的同时执行去噪。最大池比平均池表现好得多。Max pooling 通过选择最大值同时减小图像的大小来保留主要特征。这有助于减少过拟合。
-
Min pooling: 选择批次的最小像素值。
-
Average pooling: 返回内核覆盖的图像部分的所有值的平均值。平均池简单地执行维数减少作为噪声抑制机制。
全连接层
添加全连接层是学习由卷积层的输出表示的高级特征的非线性组合的(通常)廉价方式。这一步由输入层、全连接层和输出层组成。完全连接层类似于人工神经网络中的隐藏层,但在这种情况下,它是完全连接的。输出层是我们获得预测类的地方。信息通过网络传递,计算预测误差。然后,误差通过系统反向传播,以改进预测。它计算类分数并输出大小等于类数量的一维数组。
现在您已经了解了 CNN 是如何工作的,您已经准备好将它应用到项目中了。
项目描述
在这个项目中,我们收集了一组彩色图像。这是我们的基准,以便我们知道我们希望我们的模型达到什么结果。然后我们将这些彩色图像转换成灰度。我们将灰度图像分成训练集和测试集。然后,我们将训练集图像及其相应的彩色图像输入到我们的模型(VGG-16 和 CNN 的组合)中,以“学习”图像是如何着色的。然后,为了测试这个模型,我们给它输入一个灰度图像。它自己给这些图像添加颜色。目标是让模型添加颜色,使其看起来令人信服,并尽可能接近原始图像。流程图见图 6-3 。
图 6-3
图像上色流程图
关于数据集
名称: Colornet
来源: www.floydhub.com/emilwallner/datasets/colornet
创建者:埃米尔·沃纳
重要术语
彩色空间
我们需要知道的第一件重要的事情是使用了 Lab 色彩空间。原因很简单:在 RGB 等色彩空间中,我们需要学习三个不同的通道,而在 Lab 中,我们只需要学习两个。通道 L 表示亮度,其值介于 0(暗)和 100(亮)之间。通道 a 和 b 分别是红绿和蓝黄范围之间的轴位置。
Lab 编码的图像有一个灰度层。然后,它将三个颜色层合二为一。这意味着我们可以在最终预测中使用原始灰度图像。那么,我们只有两个渠道可以预测。
使用 Lab 颜色空间的一个很好的原因是它可以保持光强度值的分离。黑白图片可以被视为 L 通道,模型在进行预测时不必学习如何保持正确的光强(如果使用 RGB,则必须这样做)。该模型将只学习如何给图像着色,让它专注于重要的事情。
该模型输出 AB 值,然后可以将其应用于黑白图像以获得彩色版本。
真彩色值从-128 到 128,这是 Lab 色彩空间中的默认间隔。将它们除以 128,它们也落在-1 比 1 的区间内。这使我们能够比较预测的误差。
在计算出最终误差后,网络更新滤波器以减少总误差。网络保持在这个循环中,直到误差尽可能低。1.0/255 表示我们使用的是 24 位 RGB 颜色空间。这意味着我们对每个颜色通道使用 0-255 个数字。这是颜色的标准尺寸,产生 1670 万种颜色组合。
图像上色
黑白图像可以用像素网格来表示。每个像素都有一个与其亮度相对应的值。值的范围是 0-255,其中 0 表示黑色,255 表示白色。单通道中的值 0 表示该层中没有颜色。如果所有颜色通道的值都为 0,则图像像素为黑色。
彩色图像由三层组成:
-
红色层
-
绿色层
-
蓝色层
图层不仅决定颜色,还决定亮度。例如,为了获得白色,我们需要每种颜色的平均分布。因此,彩色图像使用这三层对颜色和对比度进行编码。
对于上色任务,网络需要找到将灰度图像与彩色图像联系起来的特征。我们正在寻找将灰度值网格与三种颜色网格联系起来的特征。
我们有一个输入的灰度层,我们想预测两个颜色层,Lab 中的 ab 。为了创建最终的彩色图像,我们将包括用于输入的 L/灰度图像,从而创建一个 Lab 图像。
为了将一层变成两层,我们使用卷积滤波器。每个滤镜决定了我们在图片中看到的内容。他们可以突出显示或删除某些内容,以从图片中提取信息。网络可以从一个过滤器创建一个新的图像,或者将几个过滤器组合成一个图像。
对于卷积神经网络,每个滤波器都会自动调整,以帮助实现预期的结果。我们将从堆叠数百个过滤器开始,然后将它们缩小到两层,即 a 和 b 层。
-
输入是代表黑白图像的网格。
-
它输出两个带有颜色值的网格。
-
在输入和输出值之间,我们创建过滤器将它们连接在一起,这是一个卷积神经网络。
当我们训练网络时,我们使用彩色图像。我们将 RGB 颜色转换到 Lab 颜色空间。黑白层是我们的输入,两个彩色层是输出。
我们有黑白输入,或过滤器,以及来自神经网络的预测。
我们将预测值映射到相同区间内的真实值。这样,我们可以比较值。间隔从-1 到 1。为了映射预测值,我们使用了一个tanh
激活函数。对于我们给tanh
函数的任何值,它将返回-1 到 1。
我们的神经网络发现了将灰度图像与其彩色版本联系起来的特征。
过程是这样的:
-
首先,我们寻找简单的图案:一条对角线,全黑像素,等等。
-
我们在每个方块中寻找相同的图案,并移除不匹配的像素。
-
如果我们再次扫描图像,我们会看到我们已经检测到的相同的小图案。为了更好地理解图像,我们将图像尺寸缩小了一半。
-
我们仍然只有一个 3×3 的过滤器来扫描每张图像。但是通过将新的 9 个像素与较低级别的过滤器相结合,我们可以检测到更复杂的图案。
-
一个像素组合可能形成一个半圆、一个小点或一条线。同样,我们从图像中反复提取相同的模式。这一次,我们生成 128 个新的过滤图像。
如前所述,我们从低级特征开始,比如边。离输出更近的图层组合成图案,再组合成细节,最终转化成人脸。
神经网络以试错的方式运行。它首先对每个像素进行随机预测。基于每个像素的误差,它通过网络反向工作以改进特征提取。
它开始针对产生最大误差的情况进行调整。在这种情况下,是要不要上色以及定位不同的对象。然后它把所有的物体都涂成棕色。它是与所有其他颜色最相似的颜色,因此产生的误差最小。
与其他视觉网络的主要区别在于像素位置的重要性。给网络着色时,图像大小或比例在整个网络中保持不变。在其他网络中,图像越接近最终图层就越失真。
分类网络中的最大池层增加了信息密度,但也扭曲了图像。它只重视信息,而不重视图像的布局。当给网络着色时,我们使用步长 2,将宽度和高度减半。这也增加了信息密度,但不会扭曲图像。
另外两个区别是对图层进行上采样和保持图像比例。分类网络只关心最终的分类。因此,当图像在网络中传输时,它们会不断降低图像的大小和质量。着色网络保持图像比例。这是通过添加白色填充来实现的。否则,每个卷积层都会切割图像。这是用*padding='same'*
参数完成的。为了将图像的大小加倍,着色网络使用了上采样层。
填充本质上使得由滤波器核产生的特征图与原始图像具有相同的大小。这对于深度 CNN 非常有用,因为我们不希望输出减少,这样我们在网络的末端只剩下一个 2×2 的区域来预测我们的结果。
Note
人类只能感知 200-1000 万种颜色,所以用更大的颜色空间没有太大意义。
VGG-16
VGG-16 是一种卷积神经网络(CNN)架构,被认为是一种优秀的视觉模型架构(见图 6-4 )。该模型具有带 3x3 过滤器和跨度为 1 的卷积层。它始终使用相同的填充和最大池层(2x2 过滤器和步幅为 2 的)。在整个架构中,它始终遵循卷积层和最大池层的这种安排。最后,它有两个 fc(全连接层),后跟一个 Softmax 优化功能。这个网络是一个相当大的网络,它有大约 1.38 亿个参数。
该模型在 ImageNet 中取得了 92.7%的前五名测试准确率,ImageNet 是一个包含属于 1,000 个类别的超过 1,400 万张图像的数据集。它优于 AlexNet,因为它用多个 3×3 内核大小的滤波器一个接一个地替换了大内核大小的滤波器(第一和第二卷积层中分别为 11 和 5 个)。它在 ImageNet 数据库中的超过一百万张图像上进行了训练。该网络有 16 层,可以将图像分为 1000 种对象类别,如键盘、鼠标、铅笔和许多动物。
图 6-4
VGG-16 体系结构
MAPE 损失函数
平均绝对百分比误差(MAPE),也称为平均绝对百分比偏差(MAPD),是统计学中预测方法预测准确性的一种度量。
你现在应该对这个项目有了一个清晰的了解,并且已经学习了一些新的术语,所以让我们继续。
必需的库
对于这个项目,我们将使用您在本书第一章中安装的基本库。然而,我们还需要一些额外的库。此项目需要以下库:
-
操作系统(内置 Python2 和更高版本)
-
NumPy(安装说明见第一章)
-
熊猫(安装说明见第一章)
-
Matplotlib(安装说明见第一章)
-
TensorFlow(安装说明见第一章)
-
Keras(安装说明见第一章)
-
PIL(安装说明在本章中)
-
数学(内置 Python2 和更高版本)
-
随机(内置 Python2 和更高版本)
-
cv2(安装说明在本章中)
-
Scikit-Image(安装说明在本章中)
安装说明
在第一章,我们安装了每个项目所需的标准库。这些是这个特定项目中使用的附加库的安装说明。为了确保我们可以安装这些库而不用考虑我们的系统,我们将使用名为 PIP 的 Python 包。
安装 PIL
PIL 是 Python 图像库,最初由 Fredrik Lundh 及其贡献者开发。这个库可以用来处理图像。自 2009 年以来,PIL 没有任何发展。所以建议你用枕头代替。
Pillow 是 PIL 的一个分支,由 Alex Clark 和贡献者创建和维护。它以 PIL 电码为基础,然后演变成更好的 PIL 版本。它增加了对打开、操作和保存许多不同图像文件格式的支持。它的很多功能都和最初的 PIL 一样。
Note
截至本书出版时,根据官方网站,PIL 图书馆的状态如下:“当前的免费版本是 PIL 1.1.7。此版本支持 Python 1.5.2 和更新版本,包括 2.5 和 2.6。3 的版本。x 稍后会发布。”Python3 用户可以安装一个名为 Pillow 的分叉版本。
在终端中使用以下命令安装 Pillow。
Pip3 install Pillow
然后在终端中使用以下命令来检查安装。
Pip3 show pillow
PIL 故障排除
-
或者,您也可以从官方网站
http://www.pythonware.com/products/pil/
下载,手动安装 PIL。 -
卸载任何过时版本的 PIL。
-
Pillow 及以后版本不支持
import image
命令。用from PIL import image
代替。 -
枕头 2.1.0 及以后版本不支持
import _imaging
。用from PIL.Image import core as _imaging
代替。
安装 CV2
在终端中使用以下命令安装 CV2:
Pip3 install opencv
然后在终端中使用以下命令检查 CV2 的安装:
Pip3 show cv2
CV2 故障排除
opencv
有四个不同的包,你应该只选择其中一个。不要在同一环境中安装多个不同的软件包。
安装 Scikit-Image
在终端中使用以下命令安装scikit-image
。
Pip3 install scikit-image
然后在终端中使用以下命令检查scikit-image
的安装。
Pip3 show scikit-image
Scikit 故障排除-图像
-
确保 PIP 已升级。
-
确保您有一个可用的最新 C 编译器。
-
如果在系统上直接安装时出现错误,请尝试使用虚拟环境。
-
有时候
skimage
会给出一个假的错误。要禁用来自skimage
的错误和警告,导出值为0
或False
的环境变量SKIMAGE_TEST_STRICT_WARNINGS
,并运行如下测试:
export SKIMAGE_TEST_STRICT_WARNINGS=False
pytest --pyargs skimage
现在,您应该已经拥有了这个项目所需的所有库。让我们把重点放在我们想要在这个项目中使用的层的类型和每种类型的层的数量上。
CNN+VGG-16 架构
要使用 CNN+VGG-16 对灰度图像进行着色,我们需要首先加载 VGG-16 模型,这是 TensorFlow 2.0 环境的一部分。这省去了我们从头构建它的麻烦。然后,我们开始构建 CNN,并插入 VGG-16 作为 CNN 的第二层。CNN 的第一层对输入的图像进行整形,这样它们就可以很容易地输入到 VGG-16 中。从那里,输入继续通过 CNN。图 6-5 显示了我们将用于本项目的模型。
图 6-5
本项目 CNN+VGG-16 的架构
让我们来看看 CNN+VGG-16 的“蓝图”,更好地了解这两种模式是如何结合的。我们的模型将包括以下内容:
-
输入层- 1
-
密集层- 2
-
向上采样 2D - 6
-
DepthwiseConv2D - 3
-
激活层- 3
-
辍学层- 3
-
平均池 2D - 1
我们将使用一个 Keras 输入图层将数据重塑为三个通道:亮度(黑到白)、a(绿到红)和 b(蓝到黄)。密集层是完全连接的,并确保模型中使用了图像中的所有值。
Upsampling2D 层只是将输入的维度加倍。它在输出中重复行和列(由输入提供)。该层没有参数或模型权重,因为它不学习任何东西;它只是加倍了输入。默认情况下,UpSampling2D 将使每个输入维度加倍。此外,默认情况下,UpSampling2D 图层将使用最近邻算法来填充新的行和列。这对于我们的模型是理想的,这样有价值的图像数据在被模型处理时不会丢失。
DepthwiseConv2D 层执行深度方向可分离卷积,这只是深度方向空间卷积的第一步(它分别作用于每个输入通道)。
dropout 层会删除训练过程中获得的一些值,这对于避免过拟合模型至关重要。
AveragePooling2D 层通过将输入划分为矩形池区域并计算每个区域的平均值来执行下采样。
对于激活函数,由于其梯度的不饱和性,我们将使用 ReLU,这大大加快了随机梯度下降的收敛速度。
对于损失函数,我们将使用 MAPE,如“重要术语”一节所述。由于其尺度无关性和可解释性的优势,它可以帮助我们确定我们的模型工作得有多好。
Adam 优化器是大多数项目使用的标准优化器,包括这个项目。
我们已经规划好了我们的架构。剩下的就是实现模型了。
程序
按照以下步骤构建这个项目。
第一步。导入库
我们通过导入必要的库来开始这个项目。
# Importing Libraries
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.layers import Dense, Dropout, Input, InputLayer, Conv2D,UpSampling2D,DepthwiseConv2D
from tensorflow.keras.layers import Flatten,MaxPooling2D,Conv2DTranspose, AveragePooling2D
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.models import Model,Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import layers
from tensorflow.keras.preprocessing.image import img_to_array,load_img
from PIL import Image
from tensorflow.keras.utils import plot_model
from math import ceil
import random
import cv2
from skimage import io, color
设置文件路径,以便 Jupyter 笔记本可以访问数据集。使用os.path
命令并输入文件路径。
os.path=("Macintosh HD/Users/vinitasilaparasetty□/Downloads/")
第二步。将图像转换为灰度
然后我们将所有的图像转换成灰度,这样我们就有了一组彩色图像和它们相应的灰度版本。
-
random.sample()
是 Python 中random
模块的内置函数,返回从序列中选择的特定长度的项目列表。它用于无替换的随机抽样。 -
cv2.cvtColor(rgb, cv2.COLOR_BGR2LAB)
是 CV2 中的一个函数,用于将颜色空间从 RGB 转换到 LAB。 -
cv2.split(lab_image)
将图像分割成三个通道。
Note
cv2.split()
使用更多的计算时间。NumPy 索引是一个很好的选择,但是结果可能不太准确。
rootdir = os.getcwd()
filenames = random.sample(os.listdir('D:\\Proj\\vinita\\colornet\\'), 500)
lspace=[]
abspace=[]
for file in filenames:
rgb = io.imread(file)
lab_image = cv2.cvtColor(rgb, cv2.COLOR_BGR2LAB) #convert colors space from RGB to LAB
l_channel,a_channel,b_channel = cv2.split(lab_image)
lspace.append(l_channel)
replot_lab=np.zeros((256, 256, 2))
replot_lab[:,:,0] = a_channel
replot_lab[:,:,1] = b_channel
abspace.append(replot_lab)
transfer = cv2.merge([l_channel, a_channel, b_channel])
transfer = cv2.cvtColor(transfer.astype("uint8"), cv2.COLOR_LAB2BGR)
lspace=np.asarray(lspace) #convert to array
abspace=np.asarray(abspace) #convert to array
第三步。加载数据
将lspace
加载为X
,将abspace
加载为Y
。lspace
表示图像的亮度,abspace
是a
和b
通道的组合值,它们位于红绿和蓝黄范围之间的轴上。
X = np.load("lspace100.npy")
Y = np.load("abspace100.npy")
第四步。构建模型
我们准备创建 CNN+VGG-16。使用下面的代码。
model6 = VGG16(weights='imagenet',include_top=False,input_shape=(256, 256, 3))
model = Sequential()
model.add(InputLayer(input_shape=(X.shape[1], X.shape[2], 1)))
model.add(layers.Dense(units=3))
model.add(Model(inputs=model6.inputs, outputs=model6.layers[-10].output))
model.add(UpSampling2D((2, 2)))
model.add(UpSampling2D((2, 2)))
model.add(DepthwiseConv2D(32, (2, 2), activation="tanh", padding="same"))
model.add(UpSampling2D((2, 2)))
model.add(DepthwiseConv2D(32, (2, 2), activation="tanh", padding="same"))
model.add(layers.ReLU(0.3))
model.add(layers.Dropout(0.4))
model.add(UpSampling2D((2, 2)))
model.add(UpSampling2D((2, 2)))
model.add(DepthwiseConv2D(2, (2, 2), activation="tanh", padding="same"))
model.add(layers.ReLU(0.3))
model.add(layers.Dropout(0.2))
model.add(UpSampling2D((2, 2)))
model.add(layers.ReLU(0.3))
model.add(layers.Dropout(0.2))
model.add(AveragePooling2D(pool_size = (2, 2)))
model.add(layers.Dense(units=2))
print(model.summary())
第五步。设置模型参数
为了完成 CNN 的架构,我们通过创建一个函数来设置优化器和损失函数,如下所示:
def adam_optimizer():
return Adam(lr=0.001, beta_1=0.99, beta_2=0.999)
model.compile(loss='mape', optimizer=adam_optimizer())
第六步。数据准备
在将数据输入模型之前,我们需要对其进行整形,以便使用以下代码将值输入适当的通道:
X=((X.reshape(X.shape[0],X.shape[1],X.shape[2],1)))
X=(X-255)/255
Y=(Y-255)/255
trainsize= ceil(0.8 * X.shape[0])
testsize= ceil(0.2 * X.shape[0])+1
train_inp=X[:trainsize,]
test_inp=X[testsize:,]
train_out=Y[:trainsize,]
test_out=Y[testsize:,]
第七步。训练模型
我们现在可以训练模型:
model.fit(x=train_inp, y=train_out, batch_size=10, epochs=5)
第八步。获得预测
现在我们的模型已经训练好了,它已经学会了如何给图像添加颜色,使它看起来尽可能自然。让我们通过使用以下代码将新的灰度图像输入模型来检查结果:
train_pred = model.predict(train_inp)
test_pred = model.predict(test_inp)
train_random=random.randint(1,trainsize)
test_random=random.randint(1,testsize)
check=np.interp(train_pred, (train_pred.min(), train_pred.max()), (0,255))
check1=np.interp(test_pred, (test_pred.min(), test_pred.max()), (0,255))
l_channel=test_inp[20]*255
a_channel=check1[20,:,:,0]
b_channel=check1[20,:,:,1]
transfer = cv2.merge([l_channel, a_channel, b_channel])
transfer = cv2.cvtColor(transfer.astype("uint8"), cv2.COLOR_LAB2BGR)
第九步。查看结果
让我们来看看我们的图像着色模型的结果。
plt.imshow(transfer)
需要考虑的几点:
-
我们在这个项目中使用的数据集只有彩色图像。我们需要将图像转换为灰度,以便得到一组彩色图像和一组灰度图像。
-
大多数在线数据集只包含彩色图像,因此我们必须手动将图像转换为灰度,然后才能使用它们来训练模型。
-
因为大多数训练数据都非常相似,所以网络很难区分不同的对象。它会调整不同的棕色调,但无法生成更细微的颜色。
解决纷争
以下是您可能会遇到的一些常见错误,这些错误很容易修复:
-
如果 PIL 安装给您带来了麻烦,请尝试卸载它,然后使用以下命令进行升级:
-
VGG 模型相当大,需要相当大的内存,所以使用有大量内存的系统或者使用云。
-
如果显示此警告,您可以忽略它,因为它不会影响程序:
pip uninstall PIL
pip install PIL —upgrade
WARNING:tensorflow:From /usr/local/lib/python3.6
/dist-packages/tensorflow/python/framework
/op_def_library.py:263: colocate_with
(from tensorflow.python.framework.ops) is
deprecated and will be removed in a future version.
Instructions for updating: Collocations handled
automatically by placer.
进一步测试
这里有一些想法可以尝试,并从这个项目中学到更多:
-
尝试单独使用 VGG-16,看看结果有什么不同。
-
试着只用彩色图像训练模型,看看模型表现如何。
-
尝试其他色彩空间,看看结果如何变化。
-
尝试最小/最大池,而不是平均池。
-
移除上采样层,看看它如何影响结果。
-
移除脱落层,查看结果有何不同。
摘要
下面是你在本章中学到的所有内容的快速回顾。
-
计算机将物体以数字的形式形象化。每幅图像都可以用一组二维数字来表示,称为像素。
-
CNN 可以通过应用相关的过滤器成功地捕捉图像中的空间和时间依赖性。
-
滤镜的深度必须与图像的深度相同。
-
有三种类型的池:最小池,最大池,平均池。
-
对于 RGB,我们需要学习三个不同的通道,而在实验室中,我们只需要学习两个。
-
Lab 颜色空间保持光强度值分开。真彩色值从-128 到 128,这是 Lab 色彩空间中的默认间隔。将它们除以 128,它们也落在-1 比 1 的区间内。
-
通道 L 表示亮度,其值介于 0(暗)和 100(亮)之间。
-
像素值范围为 0 - 255,其中 0 表示黑色,255 表示白色。
-
平均绝对百分比误差(MAPE),也称为平均绝对百分比偏差(MAPD),是统计学中预测方法预测准确性的一种度量。
参考
本章中使用的参考资料如下:
-
www.ncbi.nlm.nih.gov/pmc/articles/PMC1359523/pdf/jphysiol01247-0121.pdf
-
https://docs.w3cub.com/tensorflow~python/tf/keras/layers/depthwiseconv2d/
-
www.statisticshowto.datasciencecentral.com/mean-absolute-percentage-error-mape/
进一步阅读
有兴趣了解本章中涉及的一些主题吗?这里有一些很棒的链接可以查看:
-
色彩空间:
https://programmingdesignsystems.com/color/color-models-and-color-spaces/index.html
-
人类视觉:
-
计算机视觉:
七、图像去模糊
在前一章中,我们讨论了图像着色,这是使用 Photoshop 等工具完成的。现在让我们来谈谈 Photoshop 通常用于的另一项任务,但我们可以使用神经网络来自动化这项任务。在这一章中,我们将讨论图像去模糊。我们将在这个项目中使用一个与 VGG-16 结合的生成性对抗网络(GAN)。首先,我们将了解什么是 GAN 以及它是如何工作的。然后,我们将仔细看看什么是图像去模糊。
什么是甘?
生成对抗网络(GANs)是一类强大的神经网络,与无监督学习一起使用。gan 由两个相互竞争的神经网络模型组成,能够分析、捕获和复制数据集中的变化。
GANs 由两种成分组合而成。一个称为生成器,它生成新的数据实例,而另一个称为鉴别器,评估实例的真实性。换句话说,鉴别器决定它检查的每个数据实例是否属于实际的训练数据集。
即使在原始数据中引入少量噪声,主流神经网络也经常会对事物进行错误分类。这是因为大多数模型从有限的数据中学习,这使它们容易过拟合。此外,输入和输出之间的映射几乎是线性的。各种类别之间的分离边界似乎是线性的,但实际上,它们是由线性组成的,即使特征空间中某个点的微小变化也可能导致数据错误分类。
gan 的类型
一些常用的方法如下:
-
香草甘:这是最简单的一种甘。这里,生成器和鉴别器是简单的多层感知器。算法真的很简单;它试图使用随机梯度下降来优化数学方程。
-
条件 GAN(CGAN):CGAN 可以描述为一种深度学习方法,其中一些条件参数被放置到位。将一个附加参数 y 添加到生成器中,以生成相应的数据。标签也被用作鉴别器的输入,以帮助区分真实数据和伪造生成的数据。
-
深度卷积 GAN(DCGAN):DCGAN 是 GAN 最成功的实现之一。它由神经网络代替多层感知器组成。ConvNets 的实现没有最大池。它们被卷积步幅所取代。此外,这些层没有完全连接。
-
拉普拉斯金字塔 GAN (LAPGAN): 拉普拉斯金字塔是一种线性可逆图像表示,由一组带通图像组成,间隔一个倍频程,加上一个低频残差。这种方法使用多个生成器和鉴别器网络以及不同级别的拉普拉斯金字塔。主要使用这种方法是因为它可以产生非常高质量的图像。在金字塔的每一层对图像进行下采样,然后在每一层反向放大,图像从条件 GAN 获取一些噪声,直到达到其原始大小。
-
超分辨率 GAN(SRGAN):SRGAN 是一种设计 GAN 的方法,其中深度神经网络与对抗网络一起使用,以产生更高分辨率的图像。它有助于优化放大原始低分辨率图像以增强其细节,同时最大限度地减少误差。
gan 的应用包括:
-
生成图像数据集的示例
-
生成人脸照片
-
生成逼真的照片
-
生成卡通人物
-
图像到图像的翻译
-
文本到图像的翻译
-
语义图像到照片的翻译
-
人脸正面视图生成
现在,您应该了解了什么是 GAN,以及它在现实生活中的应用。让我们看看 GAN 内部的过程。
GAN 是如何工作的
gan 可分为两个主要部分,发生器(第一部分)和鉴别器(第二部分)(见图 7-1 )。
图 7-1
氮化镓的组成部分
生成模型
生成模型根据概率模型描述了数据是如何生成的。它捕捉数据的分布,并以这样一种方式进行训练,即它试图最大化鉴别器出错的概率。
生成器模型将固定长度的随机向量作为输入,并在域中生成样本。该向量从高斯分布中随机抽取,并且该向量用于生成过程的种子。在训练之后,这个多维向量空间中的点将对应于问题域中的点,形成数据分布的压缩表示。
这个向量空间被称为潜在空间,或者由潜在变量组成的向量空间。潜在变量,或隐藏变量,是那些对一个领域很重要但不能直接观察到的变量。
在 GANs 的情况下,生成器模型将意义应用于所选潜在空间中的点,使得从潜在空间中提取的新点可以被提供给生成器模型作为输入,并用于生成新的不同的输出示例。在训练之后,生成器模型被保留并用于生成新的样本。见图 7-2 。
图 7-2
发电机过程的流程图
发电机内的过程
鉴别器空闲时,发电机被训练。G(z)给出了与实际输入相同的形状。例如,如果 10*10 的图像是真实输入,那么 G(z)产生相同的形状,但是我们希望我们的生成器最大化伪数据,并使用最大化真实数据同时最小化伪数据的鉴别器。
-
max v(di)= en ~ rdata(n)[logd(n)]+ey ~ gy(y)[log(1-d(g(z)]]
迪
G 损失 = log (1-Di(G(y)))
其中:
G 损耗 =发电机损耗
Di =鉴别器
G(y) =发电机的输出
y =噪声矢量
步骤 1: 发电机 Ge 根据其噪声计算损耗,作为 Ge 损耗。
第二步:反向传播
- V (Di,Ge)= En ~ Rdata(n)【logD(n)】+ Ey ~ G(y)【log(1-Di(G(y)))】
其中:
Ge =发电机
Di =鉴别器
n =来自 Rdata(n)的训练样本
D(n) =鉴频器的输出
G(y) =发电机的输出
y =噪声矢量
一旦我们得到这两个损耗,我们计算关于它们的参数的梯度,并通过它们的网络独立地反向传播,以从损耗中学习(调整关于损耗的参数)。)
这种方法重复几个时期,然后手动检查准确性。如果看起来可以接受,那么就停止训练;否则,它将被允许继续运行几个纪元。
鉴别器模型
鉴别器是一个正常的分类模型。鉴别器的目标是估计接收到的样本来自训练数据而不是来自生成器的概率。鉴别器模型从域中取一个例子作为输入(真实的或生成的),并预测一个真实或虚假的二元类标签(生成的样本)。见图 7-3 。
真实的例子来自训练数据集。生成的示例由生成器模型输出。在训练过程之后,鉴别器模型被丢弃,因为我们对生成器感兴趣。
图 7-3
鉴别器过程的流程图
Note
发生器和鉴别器都经过各自的反馈回路。
鉴别者试图使其报酬 V(Di,Ge)最小化,而生产者试图使其损失最大化。
鉴别器内的过程
鉴别器在发电机空闲时进行训练。在此阶段,网络仅向前传播,不进行向后传播。在这个阶段,鉴别器在假生成的数据以及真实数据上被训练,以查看它是否能够正确地预测它们。
在鉴别器被生成器生成的假数据训练后,我们可以得到它的预测,并使用结果来训练生成器。这导致了一个比前一个状态更好的结果,所以我们可以尝试欺骗鉴别器。它以介于0
(代表真实)和1
(代表虚假)之间的数字的形式返回概率。正如我们在下面的公式中看到的,D(x)和 D(G(z))给出了一个介于 0 和 1 之间的分数:
-
Min V Ge Ey ~ py(y)【log(1-Di(Ge(y)))】
-
葛
-
DL 实 =日志(di(n))假 =日志(1-D(G)))
-
DL = DL 实数 + DL 假 = >日志(D(n)) +日志(1-D(G(y)))
其中:
Di =鉴别器
n =来自 Rdata(n)的训练样本
D(n) =鉴频器的输出
G(y) =发电机的输出
y =噪声矢量
DL 真实 =真实样本的损失
DL fake =丢失虚假/生成的数据
步骤 1: 我们从随机分布中取出一些噪声,并将其馈送给 Ge 生成器,以产生伪 n(标签 y=0) → (n,y)输入-标签对。
第二步:我们取这个假对和真对 n(标号 y =1)交替馈给 Di 鉴别器。
第三步:Di 鉴别器是一个二元分类神经网络,所以它运行两次。它计算假 n 和真 n 的损失,并将它们合并为最终损失,Di 损失。
训练 GAN 的技巧:
-
训练鉴别器时,保持发生器值不变。训练发生器时,保持鉴别器不变。每个人都应该针对静态的对手进行训练。这使得生成器能够更好地读取它必须学习的梯度。
-
GAN 的每一边都可以压倒另一边。如果鉴别器太好,它将返回非常接近
0
或1
的值,发生器将难以读取梯度。如果生成器太好,它将持续利用鉴别器中的弱点,导致假阴性。这可以通过母语英语教师各自的学习速度来缓解。
项目描述
在这个项目中,我们将训练一个 GAN 接受模糊的照片作为输入,消除模糊,并返回清晰的,消除障碍的图像作为输出。甘需要很长时间来训练。因此,在这个项目中,我们将我们的定制 GAN 与预训练的 VGG-16 模型相结合。这将节省计算资源和时间,并给我们更好的结果。该过程如图 7-4 所示。
图 7-4
图像去模糊流程图
关于数据集
名称: CERTH 图像模糊数据集
内容:
训练集:
-
630 张无失真图像(原件)
-
220 张自然模糊的图像(在拍照时模糊)
-
150 张人工模糊图像(使用软件模糊)
由“自然模糊”集和“人工模糊”集组成的评估集。
自然模糊集:
-
589 个不失真的图像
-
411 自然模糊的图像
人造模糊集:
-
30 幅不失真的图像
-
450 张人工模糊的图像
来源: https://mklab.iti.gr/results/certh-image-blur-dataset/
创建者: E. Mavridaki 和 V. Mezaris
重要术语和概念
图像去模糊
图像去模糊是去除图像模糊的过程。模糊通常是由散焦像差、运动模糊和高斯模糊等引起的。
当一个像素的值受到相邻像素的影响时,就会出现图像模糊。
散焦
散焦是一种模糊,因为主要元素不仅出现在活动的像素上,还出现在相邻像素上。这可能是由于焦距调整不当或缺少对焦元件造成的。
运动模糊
运动模糊也是一种模糊,因为相同的信号与物体一样落在不同的接收器单元上,或者物体正在运动。
目标是从模糊的图像中恢复清晰的图像。
盘旋
在数学上,这个过程表示如下:
- BI = SI*BK
其中 BI 是模糊的输入图像。我们需要找到清晰的图像(SI)和模糊的内核(BK)。当我们把它们相乘时,叫做卷积。我们说 SI 与 BK 卷积生成模糊图像(BI ),其中 BK 是模糊。
模糊(BK)通常被建模为点扩展函数,并与假设的清晰图像(SI)卷积以获得模糊图像(BI),其中清晰图像(SI)和点扩展函数(BK)都是未知的。
这是一个逆问题的例子。在几乎所有的情况下,模糊图像中没有足够的信息来唯一地确定似乎合理的原始图像,这使得它成为一个不适定的问题。此外,模糊图像包含额外的噪声,这使得确定原始图像的任务变得复杂。这通常通过使用正则化项来试图消除不合理的解决方案来解决。
反褶积
图像去模糊问题可以分成两个不同的问题:
-
盲解卷积:这包括恢复点扩散函数(PSF)。在多图像 PSF 估计方法中,通过图像序列跟踪对象,或者通过使用多个模糊图像或一个模糊噪声图像对,问题在数学上被约束为变得越来越不病态。在单幅图像的点扩散函数估计中,物体的模糊边缘代表了局部运动信息的来源。在全局水平上,将整个图像的梯度与已知的一般估计进行比较可以帮助推断 PSF。
-
非盲解卷积:这包括使用已知的 PSF 恢复初始估计。非盲解卷积方法解决了最小化附加噪声在用已知 PSF 去模糊中的巨大影响、消除源自近似 PSF 估计的伪像以及截断改变的图像中的数据的问题。
注意un bur 是不正确的技术术语。去模糊是正确的技术术语。
GAN 架构
cov1
层的输入是固定大小的 224x224 RGB 图像。图像通过一堆卷积( conv )层,在这里使用的过滤器具有 3×3 的非常小的感受野(这是捕捉左/右、上/下和中心概念所需的最小尺寸)。
在其中一种配置中,它还利用 1×1 卷积滤波器,可视为输入通道的线性变换(后跟非线性)。卷积步距固定为一个像素;conv 图层输入的空间填充使得空间分辨率在卷积后保持不变(即,3×3 conv 图层的填充为一个像素)。
空间池由五个最大池图层执行,这五个图层位于一些 conv 图层之后(并非所有 conv 图层都遵循最大池)。最大池化在 2×2 像素窗口上执行,步长为 2。
三个全连接(FC)层跟随一个卷积层堆栈(在不同的架构中具有不同的深度)。前两层各有 4096 个通道,第三层执行 1000 路 ILSVRC 分类,因此包含 1000 个通道(每个类别一个通道)。
最后一层是 Softmax 层。全连接层的配置在所有网络中都是相同的。所有隐藏层都配备了整流(ReLU)非线性。
必需的库
对于这个项目,我们将使用您在本书第一章中安装的基本库。以下是该项目所需的所有库的列表:
-
NumPy(安装说明见第一章)
-
操作系统(内置 Python 2 及更高版本)
-
熊猫(安装说明见第一章)
-
Matplotlib(安装说明见第一章)
-
Keras(安装说明见第一章)
-
TensorFlow(安装说明见第一章)
-
PIL(安装说明见第六章)
-
随机(内置 Python 2 及更高版本)
-
数学(内置 Python 2 及更高版本)
看起来我们已经拥有了这个项目所需要的所有库。让我们把重点放在我们想要在这个项目中使用的层的类型和每种类型的层的数量上。
GAN 架构
让我们来看看 GAN 的“蓝图”。我们的模型将由以下部分组成,如图 7-5 所示。
发电机
-
卷积 2D 层:4
-
脱落层:2
-
上采样 2D : 1
-
致密层:1
-
激活层:2
-
激活功能:泄漏 ReLU
-
损失函数:二元交叉熵
鉴别器
-
卷积 2D 层:4
-
卷积 2D 转置:1
-
最大池 2D: 1
-
脱落层:3
-
致密层:4
-
激活层:3
-
激活功能:泄漏 ReLU
-
损失函数:二元交叉熵
图 7-5
GAN 模型
程序
本节概述了用于构建这个项目的步骤和代码。
第一步。导入库
通过导入必要的库来开始项目。
import numpy as np
import os
import pandas as pd
import matplotlib.pyplot as plt
import keras
import tensorflow as tf
from tensorflow.keras.layers import Dense, Dropout, Input, InputLayer, Conv2D,UpSampling2D , Flatten,MaxPooling2D,Conv2DTranspose
from tensorflow.keras.models import Model,Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import layers
from PIL import Image
import random
from math import ceil
设置文件路径,以便 Jupyter 笔记本可以访问数据集。使用os.chdir
命令并输入文件路径。
os.chdir('CERTH_ImageBlurDataset') #enter file path of dataset
os.curdir #enter dataset directory
现在,我们将查看数据集的内容:
os.listdir() #list contents of current directory
以下是输出:
['Artificially-Blurred', 'Naturally-Blurred', 'Undistorted']
从输出中,您可以看到数据集中有三个文件夹。
Note
为了简单起见,我们将人工模糊和自然模糊的图像称为“假的”,而未失真的图像称为“真实的”
第二步。数据集准备
使用init_size=100
一次取 100 个文件。然后使用folders=os.listdir()
开始浏览数据集的内容。
init_size=100
folders=os.listdir()
然后声明将用于对数据集排序的变量
filelist=[]#Keeps track of files.
fake_data=[]#stores the distorted images.
real_data=[]#stores the undistorted image.
创建for
循环来挑选假图像。im
变量一个接一个地打开每个图像。然后,为了方便和统一,图像被调整为 50x50。请记住,模型不能直接处理图像。因此,您需要将图像转换为数组。现在,使用缩小滤波器进一步简化阵列。然后将数组添加到fake_data
,如下所示。
for i in folders[0:3]:
files=os.listdir(i)
for j in files:
im = Image.open(i+'\\'+j) # opening each image
width = 50 #setting width of image
height = 50 #setting height of image
im5 = im.resize((width, height), Image.ANTIALIAS) #resizing image
x=np.asarray(im5) #convert image to array
x =(x-x.mean())/255.0# best down-sizing filter
fake_data.append(x)
创建for
循环来整理真实图像。这个过程类似于前面的代码块,除了这一次,您使用的是“真实的”图像。
for i in folders[2:]:
files1=os.listdir(i)
for j in files1:
im = Image.open(i+'\\'+j)
width = 50
height = 50
im5 = im.resize((width, height), Image.LANCZOS)
x=np.asarray(im5)
x =(x-x.mean())/255.0# best down-sizing filter
real_data.append(x)
第三步。探索性数据分析
让我们先来看看“假”图像。通过设置interpolation='nearest'
,如果显示分辨率与图像分辨率不同(这是最常见的情况),它只显示图像,而不尝试在像素之间进行插值。这将导致图像中的像素显示为多个像素的正方形。
fake_data= np.asarray(fake_data) #convert image to array
plt.imshow(fake_data[70], interpolation="nearest")
对于输出,您将得到第 70 个位置的图像,如图 7-6 所示。
Note
图 7-6 和 7-7 可能与您在运行项目时看到的不同。不要担心;这只是由于各种因素,如你正在工作的系统。
图 7-6
第 70 个位置的假图像
现在,您可以查看“真实”图像:
real_data= np.asarray(real_data) #convert image to array
plt.imshow(real_data[50], interpolation="nearest")
对于输出,您将获得“真实”图像,它位于第 50 个位置,如图 7-7 所示。
图 7-7
第 50 个位置的真实图像
第四步。构建模型
接下来,为 Adam 优化器定义一个带有参数的函数。学习率(lr)
、beta_1
和beta_2
都设置为默认值。记住beta_1
和beta_2
必须在0
和1
之间。
Note
因为我们没有将amsgrad
设置为FALSE
,所以我们使用 Adam 优化器的 AMSGrad 变体。
def adam_optimizer():
return Adam(lr=0.001, beta_1=0.9, beta_2=0.999)
使用create_generator()
功能构建 GAN 的发生器。
def create_generator():
generator=tf.keras.models.Sequential()
generator.add(InputLayer(input_shape=(50,100,100)))
generator.add(Conv2D(32, (2, 2), activation="tanh", padding="same", strides=2))
generator.add(layers.LeakyReLU(0.6))
generator.add(layers.Dropout(0.4))
generator.add(Conv2D(32, (2, 2), activation="tanh", padding="same"))
generator.add(layers.LeakyReLU(0.3))
generator.add(layers.Dropout(0.2))
generator.add(Conv2D(32, (3, 3), activation="tanh", padding="same"))
generator.add(UpSampling2D((2, 1)))
generator.add(Conv2D(3, (5, 5), activation="tanh", padding="same"))
generator.add(layers.Dense(units=3, activation="tanh"))
generator.compile(loss='binary_crossentropy', optimizer=adam_optimizer())
return generator
创建生成器并查看摘要,以确保所有层都已定义。
g=create_generator()
g.summary()
以下是输出结果:
_______________________________________________________________
Layer (type) Output Shape Param #
===============================================================
conv2d_8 (Conv2D) (None, 25, 50, 32) 12832
_______________________________________________________________
leaky_re_lu_5 (LeakyReLU) (None, 25, 50, 32) 0
_______________________________________________________________
dropout_5 (Dropout) (None, 25, 50, 32) 0
_______________________________________________________________
conv2d_9 (Conv2D) (None, 25, 50, 32) 4128
_______________________________________________________________
leaky_re_lu_6 (LeakyReLU) (None, 25, 50, 32) 0
_______________________________________________________________
dropout_6 (Dropout) (None, 25, 50, 32) 0
_______________________________________________________________
conv2d_10 (Conv2D) (None, 25, 50, 32) 9248
_______________________________________________________________
up_sampling2d_1 (UpSampling2 (None, 50, 50, 32) 0
_______________________________________________________________
conv2d_11 (Conv2D) (None, 50, 50, 3) 2403
_______________________________________________________________
dense_4 (Dense) (None, 50, 50, 3) 12
===============================================================
Total params: 28,623
Trainable params: 28,623
Non-trainable params: 0
_______________________________________________________________
生成器的摘要应该是这样的。
接下来,使用create_discriminator()
功能构建 GAN 的鉴频器。
def create_discriminator():
discriminator=tf.keras.models.Sequential()
discriminator.add(InputLayer(input_shape=(50,50,3)))
discriminator.add(Conv2D(10, (2, 2), activation="tanh", padding="same", strides=2))
discriminator.add(layers.Dense(units=100))
discriminator.add(layers.LeakyReLU(0.2))
discriminator.add(layers.Dropout(0.3))
discriminator.add(Conv2D(10, (3, 3), activation="tanh", padding="same", strides=2))
discriminator.add(layers.Dense(units=50))
discriminator.add(layers.LeakyReLU(0.2))
discriminator.add(layers.Dropout(0.3))
discriminator.add(Conv2D(10, (3, 3), activation="tanh", padding="same", strides=2))
discriminator.add(layers.Dense(units=25))
discriminator.add(layers.LeakyReLU(0.2))
discriminator.add(layers.Dropout(0.3))
discriminator.add(Conv2D(1, (4, 4), activation="sigmoid", padding="same", strides=3))
#discriminator.add(Conv2D(1, (4, 4), activation="tanh", padding="same"))
discriminator.add(MaxPooling2D(pool_size = (2, 3)))
#discriminator.add(Conv2DTranspose(1, (2,2), strides=(2,2)))
discriminator.add(Flatten())
discriminator.compile(loss='binary_crossentropy', optimizer=adam_optimizer())
return discriminator
创建鉴别器并查看摘要,以确保所有层都已定义。
d =create_discriminator()
d.summary()
以下是输出结果:
_______________________________________________________________
Layer (type) Output Shape Param #
===============================================================
conv2d_12 (Conv2D) (None, 25, 25, 10) 130
_______________________________________________________________
dense_5 (Dense) (None, 25, 25, 100) 1100
_______________________________________________________________
leaky_re_lu_7 (LeakyReLU) (None, 25, 25, 100) 0
_______________________________________________________________
dropout_7 (Dropout) (None, 25, 25, 100) 0
_______________________________________________________________
conv2d_13 (Conv2D) (None, 13, 13, 10) 9010
_______________________________________________________________
dense_6 (Dense) (None, 13, 13, 50) 550
_______________________________________________________________
leaky_re_lu_8 (LeakyReLU) (None, 13, 13, 50) 0
_______________________________________________________________
dropout_8 (Dropout) (None, 13, 13, 50) 0
_______________________________________________________________
conv2d_14 (Conv2D) (None, 7, 7, 10) 4510
_______________________________________________________________
dense_7 (Dense) (None, 7, 7, 25) 275
_______________________________________________________________
leaky_re_lu_9 (LeakyReLU) (None, 7, 7, 25) 0
_______________________________________________________________
dropout_9 (Dropout) (None, 7, 7, 25) 0
_______________________________________________________________
conv2d_15 (Conv2D) (None, 3, 3, 1) 401
_______________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 1, 1, 1) 0
_______________________________________________________________
flatten_1 (Flatten) (None, 1) 0
===============================================================
Total params: 15,976
Trainable params: 15,976
Non-trainable params: 0
_______________________________________________________________
鉴别器的摘要应该是这样的。
现在使用create_gan(discriminator, generator)
功能组合 VGG-16。
def create_gan(discriminator, generator):
d.trainable=False #This enables us to treat the model as a combination of our custom GAN and the VGG16
gan_input = Input(shape=(None,100,100)) #set the input shape.
x = g(gan_input)
gan_output= d(x)
gan= Model(inputs=gan_input, outputs=gan_output)
gan.compile(loss='binary_crossentropy', optimizer="adam")
return gan
使用create_gan(d,g)
创建 GAN 并查看摘要,以确保所有参数均已定义。
gan = create_gan(d,g)
gan.summary() #view the structure to ensure it is correct.
以下是输出:
_______________________________________________________________
Layer (type) Output Shape Param #
===============================================================
input_7 (InputLayer) (None, None, 100, 100) 0
_______________________________________________________________
sequential_2 (Sequential) multiple 28623
_______________________________________________________________
sequential_3 (Sequential) multiple 15976
===============================================================
Total params: 44,599
Trainable params: 28,623
Non-trainable params: 15,976
_______________________________________________________________
0
1
GAN 的摘要应该是这样的。
第五步。输入准备
这里需要将i
计数器初始化为0
,将名为epoch_num
的纪元号初始化为1
,将名为batches
的批号初始化为2
。
然后,在for
循环中,您随机抽取数据样本,并使用tf.cast()
将其转换为类型float32
。
i=0 #set counter
epoch_num=1 #set number of epochs
batches=2 #set number of batches
for epoch in range(epoch_num):
i=i+1
for index in range(batches):
# [Batch Preparation]
print(index)
noise= np.random.normal(0,1, [batches,50,100,100])
noise = tf.cast(noise, tf.float32)
现在,您需要使用生成器为模型生成假输入。例如,您可以使用np.ones(gen_images.shape[0])
生成一个只包含一个数组的伪输出:
# Generate fake inputs
gen_images = g.predict(x=noise,steps=10)
y_gen = np.ones(gen_images.shape[0])
使用np.random.randint()
创建一组新的随机选择的伪造和真实图像:
ran_real_image =real_data[np.random.randint(low=0,high=real_data.shape[0],size=batches)] #get random set of real images
使用以下选项选择随机实像:
ran_fake_image =real_data[np.random.randint(low=0,high=real_data.shape[0],size=batches)]#get random set of fake images
构建不同批次的真假数据。使用np.concatenate([ran_real_image, ran_fake_image])
将随机生成的假图像和真图像组合成一组新图像。
#Construct different batches of real and fake data
X= np.concatenate([ran_real_image, ran_fake_image])
y_combined=np.zeros(2*batches)
y_combined[:batches]=0.9
d.trainable=True #Train only the custom GAN.
d.train_on_batch(X, y_combined)
noise= np.random.randint(0,1, [batches,50,100,100])
noise = tf.cast(noise, tf.float32)
y_gen = np.ones(batches)
d.trainable=False
gan.train_on_batch(noise, y_gen)
noise= np.random.randint(0,1, [batches,50,100,100])
noise = tf.cast(noise, tf.float32)
gen_images = g.predict(x=noise,steps=10)
gen_images = gen_images
第六步。查看图像
一旦模型训练完毕,您就可以查看图像了。我们将以 20x20 的尺寸查看它们,并将插值设置为nearest
。由于我们不需要轴来查看图像,我们将使用plt.axis('off')
将其设置为off
。
dim=(20,20)
figsize=(20,20)
plt.figure(figsize=figsize)
for i in range(gen_images.shape[0]):
plt.subplot(dim[0], dim[1], i+1)
plt.imshow(gen_images[i]*256, interpolation="nearest")
plt.axis('off')
plt.tight_layout()
os.chdir('output')
plt.savefig('actual'+str(i)+'.png')
第七步。保存结果
现在,首先使用os.chdir('output')
设置文件路径,将结果直接保存到系统中。然后使用result.save('actual.png')
命名图像文件。
# Send Output to folder
result = Image.fromarray((gen_images[5]*256).astype(np.uint8)) #obtain array form of image.
os.chdir('output') #specify the file path to save the image.
result.save('actual.png') #save the image
a=(gen_images[5]*255.0).astype(np.uint8) #Unsigned Integers of 8 bits. A uint8 data type contains all whole numbers from 0 to 255.
解决纷争
这里有一些快速解决你在这个项目中可能遇到的问题的方法。
-
确保在正确的时间将模型设置为
trainable
。 -
如果结果不令人满意,请在训练前尝试洗牌。
-
批量过大会降低模型的泛化能力。尝试使用小批量。
-
由于我们正在使用 VGG-16,这是一个相关的模型,请确保您在训练时使用与模型相同的规范化和预处理。
-
过多的调整会导致网络严重不足。减少正则化,例如丢失、批量范数、权重/偏差 L2 正则化等。
-
在开始做出有意义的预测之前,网络可能需要更多的时间来训练。如果损失在稳步减少,让它多训练一些。
进一步测试
这里有一些想法可以尝试,并从这个项目中学到更多:
-
试着移除预训练的 VGG-16 模型,看看仅使用定制 GAN 的结果如何。(警告:GANs 需要很长的训练时间,大约 24 小时以上。因此,只有当您的系统能够处理训练时间时,才测试这一点。)
-
在生成器中添加/移除层,并查看这如何影响结果。
-
在鉴别器中添加/移除层,并查看这会如何影响结果。
-
改变时代。
-
尝试只使用 VGG-16,看看结果如何比较 GAN。
摘要
下面是你在本章中学到的所有内容的快速回顾。
-
生成对抗网络(GANs)是一类强大的神经网络,用于无监督学习。
-
GANs 由两部分组成——一个发生器和一个鉴别器。生成器生成新的数据实例(通常是图像)。鉴别器评估图像的真实性。
-
主流神经网络的输入和输出之间的映射几乎是线性的。
-
香草甘真的很简单;它试图使用随机梯度下降来优化数学方程。
-
条件 GAN 可以被描述为一种深度学习方法,其中一些条件参数,例如附加参数 y 和标签被放置到位。
-
深度卷积 GAN 是 GAN 最成功的实现之一。它由神经网络代替多层感知器组成。它不使用最大池,并且各层没有完全连接。
-
拉普拉斯金字塔 GAN 是一种线性图像表示,由一组相隔一个倍频程的带通图像和一个低频残差组成。
-
超分辨率 GAN 是一种深度神经网络,与对抗网络一起使用,以产生更高分辨率的图像。
-
GANs 用于生成图像数据集的示例、生成人脸的照片、生成逼真的照片、生成卡通人物、图像到图像的翻译、文本到图像的翻译、语义图像到照片的翻译以及面部正面视图的生成。
-
生成模型根据概率模型描述了数据是如何生成的。它捕捉数据的分布,并以这样一种方式进行训练,即它试图最大化鉴别器出错的概率。
-
鉴别器是一个正常的分类模型。鉴别器模型从域中取一个例子作为输入(真实的或生成的),并预测真实或虚假的二进制类别标签(生成的样本)。
-
训练鉴别器时,保持发生器值不变;并且在训练生成器时,保持鉴别器不变。每个人都应该针对静态的对手进行训练。这使得生成器能够更好地读取它必须学习的梯度。
参考
本章中使用的参考资料如下:
-
https://machinelearningmastery.com/what-are-generative-adversarial-networks-gans/
-
https://towardsdatascience.com/step-by-step-vgg16-implementation-in-keras-for-beginners-a833c686ae6c
-
https://pathmind.com/wiki/generative-adversarial-network-gan
进一步阅读
有兴趣了解本章中涉及的一些主题吗?这里有一些很棒的链接可以查看:
-
图像去模糊:
-
AMSGrad 版亚当:
https://openreview.net/forum?id=ryQu7f-RZ
八、图像处理
你认为你有发现篡改图像的敏锐眼光吗?现在,如果图像被操纵得如此之好,以至于一般人很容易被愚弄呢?神经网络可以帮助找到图像的细微特征,并识别哪些是真实的,哪些是经过修改的。这叫做图像取证 。
在这个项目中,我们将使用 CNN。由于我们已经在第六章中讨论了 CNN,我们将直接进入项目描述并从那里开始。
项目描述
在这个项目中,我们将使用 CNN 来寻找图像异常,如相同的像素补丁,以检测哪些图像被篡改。特别是,该数据集由使用复制-移动技术 伪造的图像组成。模型在地面真实图像上训练。
我们使用来自数据集的“未修改 JPEG 压缩的原始图像”文件作为我们的原始图像,我们称之为real_data
。我们使用来自数据集的“多次粘贴的副本”文件作为我们的操作数据,我们将称之为tampered_data
。
重要术语和概念
相机会在拍摄图像的瞬间创建一个数字水印或数字签名。通过检查该数字水印的值,可以检测到随后进行的任何修改。图像编辑软件工具的易用性和普及性使得任何人都可以轻松地更改图像内容或创建新图像,而不会留下任何明显的篡改痕迹。现有软件允许个人创建逼真的计算机图形和混合生成的视觉内容,观众通常会发现这些内容与照片图像难以区分。
多媒体取证
多媒体取证来源于经典的法医学,因为它使用科学方法从物理或数字证据中获取可证明的事实。多媒体取证工具的任务是通过利用数字成像和多媒体安全研究方面的现有知识,揭露多媒体内容在其生命的每一步中留下的痕迹。
它依赖于图像历史的每个阶段——从采集过程到以压缩格式存储,再到任何后处理操作——都会在数据上留下独特的痕迹,就像一种数字指纹。然后,可以识别数字图像的来源,并通过检测与数字内容内在相关的这些特征的存在、不存在或不一致来确定它是真实的还是被修改的。
通常,用于检测被操纵图像的方法可以分为以下几种:
-
主动方法:为了评估可信度,我们利用从源(即,在摄像机中)获得的数据。主动方法可以进一步分为两个部分:数据隐藏方法(例如水印)和数字签名方法。
-
被动/盲目方法:通过这种方法,我们尝试仅使用我们掌握的数字内容进行评估。它在没有任何保护技术和没有使用任何关于图像的先验信息的情况下工作。盲法使用图像函数和伪造品可以给图像添加特定的可检测变化的事实。
重建数字图像的历史是为了验证其原创性和评估其质量。当图像来源已知时,验证图像相对容易。然而,在实际情况中,几乎没有关于图像的信息。因此,调查者需要使用盲法来认证图像历史。
Note
数码相机配有水印芯片或数字签名芯片,可以使用相机本身内置的私钥轻松利用这些芯片。该芯片在将相机拍摄的每张照片存储到存储卡之前都会对其进行认证。
数字图像的历史可以由几个步骤组成,分为四个主要阶段:采集、编码、编辑和保存。这些步骤如图 8-1 所示。
图 8-1
数字图像的历史
获得物ˌ获得
在采集期间,来自摄像机拍摄的真实场景的光线被摄像机传感器(CCD 或 CMOS)上的透镜聚焦。这是产生数字图像信号的地方。在到达传感器之前,光被 CFA(滤色器阵列)过滤,CFA 是传感器上的一层薄膜,它选择性地允许光的特定成分穿过它到达传感器。每个像素只有一种特定的主色(红色、绿色或蓝色),这是聚集的。通过去马赛克过程,传感器输出被连续插值以获得每个像素的所有三种主要颜色,从而获得数字彩色图像。获得的信号经过额外的相机内处理,包括白平衡、色彩处理、图像锐化、对比度增强和伽马校正。
编码
处理后的信号存储在照相机存储器中;为了节省存储,在大多数相机中,图像是有损压缩的,对于商业设备,JPEG 格式通常是首选格式。有损图像压缩是对数字图像执行的最常见操作之一。这是因为便于处理少量数据来存储和/或传输。事实上,大多数数码相机在拍摄后会直接压缩每张照片。
编辑
可以对生成的图像进行后处理,例如,增强或修改其内容。任何图像编辑都可以在图像的生命周期内应用于图像:最常用的编辑方法是几何变换(旋转、缩放等。)、模糊、锐化、对比度调整、图像拼接(使用图像的一个或多个部分的部分来合成图像)以及克隆(或复制-移动,同一图像的一部分的复制)。
Note
拼接可能比复制-移动伪造更常见,因为它更灵活,允许创建内容与原始图像完全不同的图像。
节约
编辑后,图像通常以 JPEG 格式保存,因此会发生再压缩。操作痕迹被称为指纹或脚印,分为以下几类:
-
采集指纹:由于特定的光学系统、图像传感器和相机软件,数字采集设备中的每个组件都会修改输入,并在最终的图像输出中留下固有的指纹。图像采集流水线对于大多数商业上可获得的设备是常见的;然而,由于每个步骤都是根据特定制造商的选择来执行的,所以轨迹可能取决于特定的相机品牌和/或型号。这意味着相机的每个阶段都会引入缺陷或内在的图像规律性,从而在最终图像中留下指示性的足迹,这些足迹代表了相机类型的签名,甚至是图像中的单个设备的签名。这些工件中不一致的存在可以作为篡改的证据。
-
编码指纹:有损压缩不可避免地会留下自己的特征足迹,这些特征足迹与具体的编码架构有关。图像编码伪像中不一致的存在可以作为篡改的证据。
-
编辑指纹:应用于数字图像的每一种处理,即使在视觉上无法察觉,也会修改其属性,从而根据所使用的处理留下特殊的痕迹。
-
基于传感器的足迹:传感器图案噪声主要是由于图像传感器的不完善,导致感测到的场景与摄像机获取的图像之间存在细微差异。传感器图案噪声的主要成分是光响应不均匀性(PRNU)噪声。这是一种高频乘性噪声,在正常工作条件下,通常在相机的整个寿命期间保持稳定。这些特性不仅使其成为设备识别的理想选择,也使其成为单个设备链接的理想选择,如果在某些区域发现图像中 PRNU 图案的不一致,还可用于伪造检测。
除了 PRNU,摄像机在采集过程中留下的另一个重要伪像是由于 CFA(滤色器阵列)的存在。除了专业的三 CCD/CMOS 相机,入射光在到达传感器(CCD 或 CMOS)之前由 CFA 过滤,因此对于每个像素,仅收集一种特定的颜色。
以下是图像篡改分析的类型:
-
检测重采样痕迹:两幅或多幅图像可以拼接在一起,生成高质量、一致的图像伪造品。
在这种情况下,需要进行几何变换,如缩放、旋转或倾斜。
几何变换通常需要重采样和插值步骤。因此,我们需要复杂的重采样/插值检测器。
-
噪声不一致性分析:隐藏篡改痕迹的常用工具是将局部随机噪声添加到被更改的图像区域,这导致图像噪声的不一致性。因此,图像中各种噪声水平的存在通常意味着篡改。
-
循环平稳分析:循环平稳信号呈现周期性。循环平稳检测器通过检测其频谱分量之间的特定相关性来寻找几何变换的痕迹。事实证明,这种方法能产生有希望的结果。
-
增强检测:这包括平滑、对比度增强、直方图均衡化和中值滤波等增强操作。相邻行和列中的像素共享相同的值。这些“拖尾伪影”可以通过考虑两个像素的组的一阶差,然后研究它们相应的直方图来分析。这种简单的方法产生极高的检测率,只要图像没有被压缩。
-
接缝雕刻检测:自动检测图像中是否有没有相关内容的像素路径(接缝)。如果被检测到,这些路径将被消除,并且图像大小将会减小。我们可以认为这种技术是一种依赖内容的裁剪。
-
基于光照/阴影的拼接检测:在试图创建可信的赝品时,最常见的障碍之一是考虑场景中的物体如何与光源交互。从一张照片上剪下一个物体并粘贴到另一张照片上,需要调整物体的光照,并在场景中引入一致的阴影。如果没有做到这一点,照明方向和阴影的不一致可能表明图像已被修改。
-
基于几何/透视不一致的拼接检测:这涉及识别图像中场景的几何和透视设置、运动模糊和透视不一致的存在。
复制移动伪造
本节详细讨论复制-移动伪造,因为这是我们在这个项目中的主要焦点。复制-移动伪造是一种特殊类型的图像篡改,将图像的一部分复制并粘贴到另一部分,通常是为了隐藏图像中不想要的部分。在图 8-2 中,树的倒影被圈了起来,因为它没有出现在原始图像中。这是数字添加的。
图 8-2
复制-移动伪造的例子
因此,检测复制-移动伪造的目标是找到相同或极其相似的图像区域。如果复制的部分来自同一图像,则一些成分(例如,噪声和颜色)将与图像的其余部分兼容。这意味着这种攻击是无法使用寻找统计数据不兼容的取证方法检测到的。因此,已经提出了适当设计的方法来处理这种操纵。
首先,这种技术将不得不处理计算复杂性的问题,因为直接应用克隆区域的穷举搜索将会过于昂贵。此外,必须考虑克隆的区域可能不相等,而只是相似,因为创建伪造的篡改者可能利用图像处理工具来隐藏篡改。因此,伪造物检测方法应该被设计为对于这组可能的修改是鲁棒的。
不是寻找整个重复区域,而是将图像分割成重叠的正方形块,然后我们寻找相似的连接图像块。通过假设克隆区域大于块大小,并且因此该区域由许多重叠的克隆块组成,每个克隆块将以相同的位移移动,并且因此每个复制块对之间的距离也将相同。因此,伪造检测将在相同距离内寻找最小数量的相似图像块,这些图像块彼此连接以形成呈现相同形状的两个图像区域。
所有分析的方法都遵循相同的基于块匹配的过程:首先将图像分割成大小重叠的正方形块,每个块从左上角到右下角移动一个像素。从每个块中提取一组特征,并对其进行适当的量化,以消除克隆块之间可能存在的细微差异。
假设相似的块由相似的特征表示,则基于字典排序的匹配过程被应用于块特征向量以找到重复的块。最后,通过检查在同一移位内是否有超过一定数量的彼此相连的块对来做出伪造决定。这考虑到了大多数自然图像将具有许多相似块的事实。
以下是为代表每个图像块而选择的特征类型:
-
离散余弦变换(DCT)系数
-
与颜色相关的特征
-
像素的主成分分析(PCA)
-
傅里叶-梅林变换(FMT)作为分组签名
-
对数极坐标傅立叶变换作为签名
-
尺度不变特征变换(SIFT)局部特征,用于在同一图像中寻找匹配区域
关于数据集
名称:图像处理数据集
内容:
-
未修改/原始图像
-
JPEG 压缩的未修改/原始图像
-
一对一拼接
-
添加高斯噪声的拼接
-
添加了 JPEG 伪像的拼接
-
旋转副本
-
缩放副本
-
综合效应
-
多次粘贴的副本
来源: www5.cs.fau.de/research/data/image-manipulation/
创建者: V. Christlein,Ch 放大图片作者:John j .
论文:“对流行的复制-移动伪造检测方法的评估”,《IEEE 信息取证与安全汇刊》,第 7 卷第 6 期,第 1841-1854 页,2012 年。
必需的库
对于这个项目,我们将使用您在本书第一章中安装的基本库。以下是该项目所需的所有库的列表:
-
NumPy(安装说明见第一章)
-
操作系统(内置 Python 2 及更高版本)
-
熊猫(安装说明见第一章)
-
Matplotlib(安装说明见第一章)
-
Keras(安装说明见第一章)
-
TensorFlow(安装说明见第一章)
-
PIL(安装说明见第六章)
-
随机(内置 Python 2 及更高版本)
-
数学(内置 Python 2 及更高版本)
-
Imageio(安装说明在本章中)
-
cv2(安装说明见第六章)
-
撇油(安装说明见第六章)
除了 Imageio 之外,您应该拥有所有必需的库。让我们现在安装它。Use this command to
使用 PIP 安装 Imageio:
Pip3 install imageio
要检查安装,请使用以下命令:
Pip3 show imageio
有了这些,您应该有了这个项目所需的所有库。
解决纷争
确保 Python 和 Pillow 已经正确安装,因为它们是 Imageio 的先决条件。
CNN 架构
要检测 CNN 中的图像伪造,您需要一个具有正确顺序的正确层数的模型。图 8-3 显示了我们将在本项目中使用的模型。
图 8-3
该项目的 CNN 架构
让我们来看看 CNN 的“蓝图”。该模型将由以下几层组成:
-
卷积 2D 层:6
-
脱落层:4
-
最大池层:2
卷积 2D 层创建卷积核,该卷积核与层输入卷积以产生输出张量。dropout 层通过“丢弃”在训练期间获得的一些值来帮助避免过拟合。max-pooling 图层对空间数据进行操作,因此非常适合本项目。
我们使用泄漏 ReLU 激活函数,因为它避免了饱和(信号损失为零梯度或由数字舍入引起的混沌噪声的优势)。也解决了死 ReLU 问题。这确保了我们获得可能的最佳结果。
我们将使用二元交叉熵损失函数,因为它可以最小化两个概率分布(预测和实际)之间的距离。
程序
我们终于准备好实现 GRU 了。让我们打开一个新的 Jupyter 笔记本并开始工作。
第一步。导入库
首先加载必要的库。
from tensorflow.keras.applications.vgg16 import VGG16
import numpy as np
import os
import pandas as pd
import matplotlib.pyplot as plt
import keras
import tensorflow as tf
from tensorflow.keras.layers import Dense, Dropout, Input, InputLayer, Conv2D,UpSampling2D , Flatten,MaxPooling2D,Conv2DTranspose
from tensorflow.keras.models import Model,Sequential
from keras.datasets import mnist
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import layers
from PIL import Image
import random
from math import ceil
import imageio
import cv2
from skimage.data import astronaut
from skimage.filters import gaussian,sobel
from skimage import transform,io
使用os.chdir('')
设置文件路径:
os.chdir('D:\\Proj\\vinita\\Project 3- Image Manipulation\\manipulation_data\\Real_data\\')
第二步。准备数据集
该步骤包括几个子步骤,如下所述。
步骤 2a。整理和收集真实数据
首先,创建filelist=[]
列表来存储所有的图像文件。然后创建tampered_data=[]
列表来存储已经处理过的图像。
最后,创建real_data=[]
列表,它存储没有被处理过的原始图像。
现在初始化计数器i=0
。在for
循环中,os.walk()
将为它访问的每个目录生成两个列表——将它们分成文件和目录。os.sep()
分隔文件路径。
然后搜索图像文件并显示它们的文件路径,这样一旦执行了这个循环,就知道它正在工作。
Note
gt
文件格式是一种简单的二进制格式,旨在以紧凑和快速的方式存储 graph-tool 图形实例,包括该库支持的所有类型的属性映射。
现在调整图像大小为 300x300 以保持一致性。最后,将图像添加到real_data
列表中。
rootdir = os.getcwd()
filelist=[]
tampered_data=[]
real_data=[]
i=0
for subdir, dirs, files in os.walk(rootdir):
for file in files
:
filepath = subdir + os.sep + file
if (filepath.find('gt') >0):
i=i+1
print(filepath)
width = 300
height = 300
image1 = cv2.imread(filepath,0)
transformed_image = transform.resize(image1, (300,300), mode="symmetric", preserve_range=True)
transformed_image=(transformed_image-transformed_image.mean())/255
real_data.append(transformed_image)
步骤 2b。分类并收集处理过的数据
这个收集操作数据的过程类似于前面的代码块。
for subdir, dirs, files in os.walk(rootdir):
for file in files:
filepath = subdir + os.sep + file
if (filepath.find('gt') > 0):
i=i+1
print(filepath)
im = Image.open(filepath)
width = 300
height = 300
image1 = cv2.imread(filepath,0)
transformed_image = transform.resize(image1, (300,300), mode="symmetric", preserve_range=True)
transformed_image=(transformed_image-transformed_image.mean())/255
tampered_data.append(transformed_image)
步骤 2c。将数据转换为数组
您可以使用.mean()
功能查看平均值。
现在使用.max()
函数查看最大值,该函数返回具有最大值的项目。
然后使用.min()
函数查看最小值,该函数返回具有最低值的项目。
之后,您可以使用np.asarray()
将数据转换成数组。
transformed_image=(transformed_image-transformed_image.mean())/255
transformed_image.max()
transformed_image.min()
real_data=np.asarray(real_data)
tampered_data=np.asarray(tampered_data)
步骤 2d。创建组合数据集
我们已经对数据进行了预处理,所以现在您可以通过使用np.concatenate()
来组合两个数组以创建单个数据集。
现在使用combined_input.reshape()
重塑数据。使用ceil()
功能设置trainsize
,返回大于 0.8 的最小整数值。
我们使用ceil()
函数设置testsize
,该函数返回大于 0.2 的最小整数值。
现在使用np.random.randint()
选择一段随机的训练数据和测试数据:
combined_input=np.concatenate([real_data, tampered_data])
y_combined=np.zeros(real_data.shape[0]+tampered_data.shape[0])
y_combined[:real_data.shape[0]]=1
combined_input=combined_input.reshape(624,300,300,1)
trainsize= ceil(0.8 * combined_input.shape[0])
testsize= ceil(0.2 * combined_input.shape[0])+1
trainsel=np.random.randint(low=0,high=combined_input.shape[0],size=trainsize)
testsel=np.random.randint(low=0,high=combined_input.shape[0],size=testsize)
train_inp=combined_input[trainsel,]
test_inp=combined_input[testsel,]
train_out=y_combined[trainsel,]
test_out=y_combined[testsel,]
步骤 2e。定义优化器
定义优化器,我们在其中将 Adam 设置为其默认值。
def adam_optimizer():
return Adam(lr=0.001,beta_1=0.9)
第三步。构建模型
让我们使用以下代码为这个项目创建 CNN:
model = Sequential()
model.add(InputLayer(input_shape=(300, 300, 1)))
model.add(Conv2D(32, (3, 3), activation="tanh", padding="same", strides=2))
model.add(Conv2D(32, (3, 3), activation="tanh", padding="same"))
model.add(layers.LeakyReLU(0.6))
model.add(layers.Dropout(0.4))
model.add(Conv2D(32, (3, 3), activation="tanh", padding="same", strides=2))
model.add(layers.LeakyReLU(0.3))
model.add(layers.Dropout(0.2))
model.add(Conv2D(32, (3, 3), activation="tanh", padding="same", strides=2))
model.add(layers.LeakyReLU(0.3))
model.add(layers.Dropout(0.2))
model.add(Conv2D(32, (3, 3), activation="tanh", padding="same", strides=2))
model.add(layers.LeakyReLU(0.3))
model.add(layers.Dropout(0.2))
model.add(Conv2D(1, (3, 3), activation="tanh", padding="same",strides=2))
model.add(MaxPooling2D(pool_size = (3, 3)))
model.add(MaxPooling2D(pool_size = (2, 2)))
model.add(Flatten())
print(model.summary())
第四步。训练模型
通过使用 Adam 作为优化器和使用binary_crossentropy
作为损失函数来编译模型。
然后训练模型,将批量大小设置为 100,将时期数设置为 3。
model.compile(loss='binary_crossentropy', optimizer=adam_optimizer())
model.fit(x=train_inp, y=train_out, batch_size=100,epochs=3)
第五步。测试模型
使用model.predict()
获得训练和测试数据的预测:
train_pred = model.predict(train_inp)
test_pred = model.predict(test_inp)
第六步。检查结果
使用np.interp()
检查训练和测试数据的插值。
为了更好地理解结果,您可以使用plt.hist()
生成插值直方图。
最后,结合训练和测试数据的结果,使用np.concatenate()
来看看这个模型的整体性能。
check=np.interp(train_pred, (train_pred.min(), train_pred.max()), (0,1))
check1=np.interp(test_pred, (test_pred.min(), test_pred.max()), (0,1))
plt.hist(check)
plt.hist(check1)
train_check = np.concatenate((train_out.reshape(-1,1),check.reshape(-1,1)),axis=1)
test_check = np.concatenate((test_out.reshape(-1,1),check1.reshape(-1,1)),axis=1)
您已经成功训练此模型来检测图像中的操纵。
进一步测试
这里有一些想法可以尝试,从这个项目中学到更多:
-
尝试增加和减少 CNN 中的最大池层数。
-
添加一个上采样层,查看结果有何不同。
-
尝试最小池而不是最大池。
-
尝试增加下降图层。
-
尝试增加和减少历元的数量,以查看其对模型结果的影响。
摘要
下面是你在本章中学到的所有内容的快速回顾。
-
相机会在拍摄图像的瞬间创建数字水印/数字签名。
-
现有的软件允许伪造者创建逼真的计算机图形或混合生成的视觉内容,观众可以发现这些内容与原始照片图像没有区别。
-
多媒体取证来自经典的法庭科学,因为它使用科学方法从物理或数字证据中获取可证明的事实。
-
多媒体取证工具的任务是通过利用有关数字成像和多媒体安全研究的现有知识,暴露多媒体内容在其生命的每一步中留下的痕迹。
-
检测篡改图像的方法可以分为主动和被动两种。
-
主动方法:对于可信度的评估,这利用了从源(即,在摄像机中)获得的数据。主动方法可以进一步分为两个部分:数据隐藏方法(例如水印)和数字签名方法。
-
被动/盲目方法:使用这种方法,我们尝试仅使用数字内容进行评估。它在没有任何保护技术和没有使用任何关于图像的先验信息的情况下工作。盲法使用图像函数和伪造品可以给图像添加特定的可检测变化的事实。
-
-
数码相机配备有水印芯片或数字签名芯片,可以使用相机本身中硬连线的私钥容易地利用这些芯片。该芯片在将相机拍摄的每张照片存储到存储卡之前都会对其进行认证。
-
数字图像的历史可以由几个步骤组成,分为四个主要阶段:采集、编码、编辑和保存。
-
图像采集流水线对于大多数商用设备来说是常见的;然而,由于每个步骤都是根据特定制造商的选择来执行的,所以轨迹可能取决于特定的相机品牌和/或型号。
-
传感器图案噪声主要来自图像传感器的缺陷,导致感测到的场景和相机获取的图像之间的微小差异。
-
拼接可能比复制-移动伪造更常见,因为它更灵活,允许伪造者创建内容与原件非常不同的图像。
-
图像篡改检测的类型包括:检测重采样的痕迹、噪声不一致性分析、循环平稳分析、增强检测、接缝雕刻检测、基于光照/阴影的拼接检测、基于几何/透视不一致性的拼接检测。
-
复制-移动伪造是一种特定类型的图像篡改,其中图像的一部分被复制并粘贴到另一部分,通常是为了隐藏图像中不想要的部分。
参考
以下是本章使用的资料来源。
进一步阅读
有兴趣了解本章中涉及的一些主题吗?这里有一些很棒的链接可以查看:
-
图像取证:
https://link.springer.com/article/10.1007/s11042-010-0620-1
-
复制-移动伪造:
www.imedpub.com/articles/fusion-approaches-system-of-copymove-forgery-detection.php?aid=22116
九、神经网络集合
第三章提到,目前有大量的神经网络正在使用,更多的正在定期开发。这一章将指导你哪些可以作为未来深度学习项目的参考。本章分为三个主要部分:神经网络、优化函数和损失函数。
第一部分提供了您可以在自己的项目中轻松使用的神经网络示例。每个神经网络都附有一个解释,帮助你理解它们,以便你可以为你的深度学习项目做出正确的选择。本章的第二部分介绍了一组优化器,最后一部分介绍了损失函数,以及如何为模型选择正确参数的技巧。
神经网络动物园初级读本
第一个人工神经网络是由心理学家弗兰克·罗森布拉特在 1958 年发明的。他设计了感知机(在第二章中讨论),旨在复制人类大脑处理视觉数据和学习识别物体的方式。自从神经网络体系结构出现以来,就有新模型的冲击。由于新的模型被快速引入,有必要以某种方式组织和记录这些网络,使它们更容易跟踪和改进。
2016 年 9 月 14 日,来自阿西莫夫研究所的 Fjodor 范维恩和 Stefan Leijnen 决定为深度学习中使用的每个模型创建一个图表。他们把模型描绘成节点图。这是目前使用的最全面的模型图表。这张图表的标题是“神经网络动物园”本章引用了图表并对其进行了详细说明。
神经网络
这一主要部分回顾了为未来项目设计的一系列神经网络。每个神经网络部分以下列方式建立:
-
目前存在的主要神经网络的描述
-
每个神经网络的子类别列表和关联层列表
-
每个神经网络的应用列表
-
模型的示例实现
循环神经网络
rnn 用于深度学习和开发模拟人脑神经元活动的模型(见图 9-1 )。当预测结果至关重要时,它们尤其强大,并且与其他类型的人工神经网络不同,因为它们使用反馈循环来处理告知最终输出的数据序列,最终输出也可以是数据序列。这些反馈回路允许信息持续存在;这种效果通常被描述为记忆。RNN 背后的逻辑是考虑输入的顺序。
图 9-1
循环神经网络。RC 表示循环细胞
像前馈神经网络一样,RNNs 可以处理从初始输入到最终输出的数据。与前馈神经网络不同,RNNs 在整个计算过程中使用反馈回路,如时间反向传播(BPTT ),将信息反馈回网络。这将输入连接在一起,使 RNNs 能够处理顺序和时间数据。它们用于油藏计算。
油藏计算
储层计算(见图 9-2 )是神经网络的扩展,其中输入信号连接到一个固定的(不可训练的)和随机的动态系统(储层),从而创建一个更高维的表示(嵌入)。这种嵌入然后通过可训练单元连接到期望的输出。
图 9-2
油藏计算
rnn 的类型包括以下几种:
-
-什么
-
窥视孔连接
-
门控循环单位
-
LSM 乘法
-
LSTM 全神贯注
-
Hopfield 网络
-
马尔可夫链
-
双向联想存储器
RNN 中使用的图层如下:
-
把...嵌入
-
简单神经网络
-
简单电池
-
稠密的
-
拒绝传统社会的人
RNN 的应用如下:
-
语音识别
-
文本分类
-
图像识别
下面是 RNN 在 TensorFlow 2.0 中的实现:
rrn = tf.keras.Sequential()
rnn.add(layers.Embedding(input_dim=1000, output_dim=64))
rnn.add(layers.SimpleRNN(128))
rnn.add(layers.Dense(10, activation="softmax"))
rnn.add(layers.SimpleRNN(90))
rnn.compile(loss='binary_crossentropy', optimizer="rmsprop"
LSM 乘法
乘法 lstm(mlstm)是由 Benjamin Krause 在 2016 年提出的(见图 9-3 )。它们是一种用于序列建模的循环神经网络架构,结合了长短期记忆(LSTM)和乘法循环神经网络架构。mLSTM 的特征在于其对于每个可能的输入具有不同的递归转移函数的能力,这使得其对于自回归密度估计更具表达性。这是一种混合架构,将 mRNNs 的分解隐藏到隐藏转换与 LSTMs 的门控框架相结合。
图 9-3
LSTM 乘法(mlstm)
mLSTM 中的层如下:
-
把...嵌入
-
LSM 乘法
-
稠密的
-
拒绝传统社会的人
-
convtomm 2d
-
convlstm 2 dcell
-
LSTMCell
-
cudnnlstm(英国作家)
mLSTM 与离散互斥元素的序列一起使用,
下面是它在 TensorFlow 2.0 中的实现:
mlstm = tf.keras.models.Sequential()
mlstm.add(Embedding(1000, 64, input_length=10))
mlstm.add(MultiplicativeLSTM(512, dropout_W=0.2, dropout_U=0.2)
mlstm.add(Dense(1024, activation="linear")
mlstm.add(MultiplicativeLSTM(50, dropout_W=0.1, dropout_U=0.1)
mlstm.add(Dropout(0.2)
mlstm.compile(loss='categorical_crossentropy', optimizer="rmsprop"
全神贯注的安妮
机器学习中的注意力是指模型对数据中特定元素的专注能力(见图 9-4 )。
我们构建了一个使用所有隐藏状态的架构,而不是使用最后一个隐藏状态作为代理。这就是“注意力”机制的作用。每个生成的输出不仅仅是最终隐藏状态的函数,而是所有隐藏状态的函数。这不是一个简单地组合所有隐藏状态的操作。
图 9-4
LSTM 全神贯注
关注的类型包括以下几种:
-
全局注意力:使用所有编码器隐藏状态来为每个解码器步骤定义基于注意力的上下文向量。
-
局部注意:仅使用落入较小窗口内的几个隐藏状态。该窗口以编码器的隐藏状态为中心。
-
硬注意:它不是对所有隐藏状态进行加权平均,而是使用注意分数来选择单个隐藏状态。
-
软注意:将上下文向量计算为编码器隐藏状态的加权和,如上图所示。
-
潜在注意力:在每个特征上学习一个权重,以确定其对于预测触发或动作的重要性。与标准注意方法不同,潜在注意分两步计算特征权重。这提高了触发动作的准确性。潜在权重决定了最终的注意力权重,我们称之为主动权重。
-
主动关注:根据每个标记在最终预测中的重要性计算其权重。
-
区域注意力:注意力被应用于整个“区域”,而不局限于单个项目。它被定义为内存中一组结构上相邻的项目。通过组合存储器中的相邻项目来形成“区域”。
注意人工神经网络的层次如下:
-
标度点积注意力
-
多头注意力层
注意力人工神经网络的一个应用是自然语言处理。
Note
其他层取决于所使用的网络类型,如 RNN、CNN 等。
下面是它在 TensorFlow 2.0 中的实现:
#RNN with Attention
rrn = tf.keras.Sequential()
rnn.add(layers.Embedding(input_dim=1000, output_dim=64))
rnn.add(layers.SimpleRNN(128))
rnn.add(layers.Attention( [query_seq_encoding, value_seq_encoding]))
rnn.add(layers.Dense(10, activation="softmax"))
rnn.add(layers.SimpleRNN(90))
rnn.compile(loss='binary_crossentropy', optimizer="rmsprop"
变形金刚(电影名)
变形金刚有一个内存饥渴的架构风格(见图 9-5 )。它们是注意的前馈编码器/解码器。它们不需要单元状态存储器。他们一次从整个序列片段中挑选,将注意力集中在最重要的部分。
图 9-5
变形金刚(电影名)
变压器中的层如下:
-
输入层
-
隐藏层:通常是致密层
-
输出层
-
高斯噪声
-
高斯-安德鲁普特
变压器的一个应用是序列转换。
下面是 TensorFlow 2.0 中的实现:
# Encoder
def create_encoder():
encoder = tf.keras.models.Sequential()
encoder.add(Dense(50, activation="relu", input_shape(1,1,None))
encoder.add(Dropout(rate=dropout))
encoder.add(Dense(units=10, activation="relu"))
encoder.add(Dense(units=20))
encoder.add(Dropout(rate=dropout))
encoder.add(Dropout(rate=dropout))
return encoder
e = create_encoder( )
#Decoder
def create_decoder( ):
decoder = tf.keras.models.Sequential()
decoder.add(Dense(50, activation="relu", input_shape(1,1,None))
decoder.add(Dropout(rate=dropout)
decoder.add(Dense(units=10, activation="relu")
decoder.add(Dense(units=20)
decoder.add(Dropout(rate=dropout)
decoder.add(Dropout(rate=dropout)
return decoder
d = create_decoder()
# Definition of transformer
def create_transformer(encoder, decoder):
d.trainable=False
transformer_input = Input(shape=(None,100,100))
x = g(transformer_input)
transformer_output= d(x)
transformer= Model(inputs=transformer_input, outputs=transformer_output)
transfomer.compile(loss='binary-crossentropy', optimizer="adam")
return transformer
transformer = create_transfomer(e,d)
自编码器
一个自编码器是一个无监督的人工神经网络,它学习如何有效地压缩和编码数据(见图 9-6 )。然后,它学习如何从潜在空间表示中重建数据,以创建尽可能接近原始输入的表示。它忽略数据中的噪声以降低数据维数。
图 9-6
自编码器。I/O 表示匹配的输入/输出单元
自编码器由以下组件组成:
-
编码器:模型学习如何降低输入维度,将输入数据压缩成编码表示。
-
瓶颈:这一层包含输入数据的压缩表示。
-
解码器:该模型学习如何从编码表示中重建数据,以尽可能接近原始输入。
-
重建损失:使用反向传播测量解码器的性能,以最小化网络的重建损失。
网络的编码器部分用于编码,有时甚至用于数据压缩。它在每一层都有数量递减的隐藏单元。因此,这一部分被迫只选择最重要和最有代表性的数据特征。
网络的另一半执行解码功能。该部分在每层中具有越来越多的隐藏单元,以使其能够从编码数据中重建原始输入。
应用约束以从自编码器获得有用的特征。根据应用的约束或缺少约束,有两种类型的自编码器:
-
欠完成:在这种情况下,我们强制自编码器学习训练数据的最显著特征。如果潜在表示的维数与输入相同,则编码器可以学习执行复制任务,而无需提取任何关于数据分布的有用信息。
-
过完备:在这种情况下,潜在表征的维度大于输入。在这种情况下,即使线性编码器和线性解码器也可以学习将输入复制到输出,而无需学习任何关于数据分布的有用信息。
理想情况下,我们需要避免提到的这两种类型的自编码器。根据要建模的分布的复杂性,仔细选择编码维数以及编码器和解码器的容量。关键是实现正确的平衡。
自编码器中的层如下:
-
输入层
-
隐藏层:通常是致密层
-
输出层
-
高斯噪声
-
高斯-安德鲁普特
自编码器的应用包括:
-
异常检测
-
图像去噪
-
降维
下面是它在 TensorFlow 2.0 中的实现:
lstm_autoencoder = Sequential()
# Encoder
lstm_autoencoder.add(LSTM(40, activation="relu", input_shape=(timesteps, n_features), return_sequences=True))
lstm_autoencoder.add(LSTM(18, activation="relu", return_sequences=False))
lstm_autoencoder.add(RepeatVector(timesteps))
# Decoder
lstm_autoencoder.add(LSTM(12, activation="relu", return_sequences=True))
lstm_autoencoder.add(LSTM(30, activation="relu", return_sequences=True))
lstm_autoencoder.add(TimeDistributed(Dense(n_features)))
lstm_autoencoder.compile(loss='rmse', optimizer=adam)
可变自编码器
可变自编码器(vae)是强大的生成模型(见图 9-7 )。它们处理顺序或非顺序数据、连续或离散数据以及有标签或完全无标签的数据。
可变自编码器可以基于 CNN。这意味着他们将有类似 CNN 的层次。
图 9-7
可变自编码器。H 表示隐藏的单元格
可变自编码器是生成任务的理想选择,因为它们具有连续的潜在空间,允许简单的随机采样和插值。他们通过生成两个矢量输出来实现这一点:
-
均值向量
-
标准差的向量
这种随机生成允许每一遍编码中的微小变化,即使对于相同的平均值和标准偏差。
它们要求编码尽可能相互接近,但仍有区别。这使得平滑插值和构造新的样本成为可能。
为了迫使这一点,我们将 kull back-lei bler 散度(KL 散度)引入损失函数。两个概率分布之间的 KL 偏差简单地衡量了它们彼此之间偏差的程度。
对于 VAEs,KL 损失等于所选点和标准偏差之间所有 KL 偏差的和。
现在,使用纯 KL 损失导致随机放置的编码中的潜在空间,靠近潜在空间的中心,很少考虑附近编码之间的相似性。
然而,一起优化这两者导致潜在空间的生成,该潜在空间通过聚类在局部尺度上保持附近编码的相似性,然而在全局范围内,这在潜在空间原点附近非常密集(将轴与原始轴进行比较)。
卷积变分自编码器中的层如下:
-
卷积的
-
联营
-
上采样
-
拒绝传统社会的人
-
稠密的
变分自编码器的应用如下:
-
图画
-
图象生成
-
面部表情编辑
-
从静态图像预测
-
合成音乐一代
下面是它在 TensorFlow 2.0 中的实现:
#Encoder
def create_encoder():
input_img = Input(shape=(30, 30, 3))
vae_encoder = tf.keras.models.Sequential()
vae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same", input_shape=input_img)))
vae_encoder.addConv2D(32, (3, 3), activation="relu", padding="same")
vae_encoder.addMaxPooling2D((2, 2), padding="same")
vae_encoder.addConv2D(64, (3, 3), activation="relu", padding="same")
vae_encoder.addConv2D(64, (3, 3), activation="relu", padding="same")
vae_encoder.addMaxPooling2D((2, 2), padding="same")
vae_encoder.addConv2D(128, (3, 3), activation="relu", padding="same")
vae_encoder.addConv2D(128, (3, 3), activation="relu", padding="same")
vae_encoder.addMaxPooling2D((2, 2), padding="same")
vae_encoder.compile(loss='categorical_crossentropy', optimizer="rmsprop")
return encoder
e =create_encoder()
#Decoder
def create_decoder():
vae_decoder = tf.keras.models.Sequential()
vae_decoder = Conv2D(32, (3, 3), activation="relu", padding="same")
vae_decoder = Conv2D(32, (3, 3), activation="relu", padding="same")
vae_decoder = UpSampling2D((2, 2), interpolation="bilinear")
vae_decoder = Conv2D(64, (3, 3), activation="relu", padding="same")
vae_decoder = Conv2D(64, (3, 3), activation="relu", padding="same")
vae_decoder = UpSampling2D((2, 2), interpolation="bilinear")
vae_decoder = Conv2D(128, (3, 3), activation="relu", padding="same")
vae_decoder = Conv2D(128, (3, 3), activation="relu", padding="same")
vae_decoder = UpSampling2D((2, 2), interpolation="bilinear")
vae_decoder = Conv2D(3, (3, 3), activation="sigmoid", padding="same")
vae_encoder.compile(loss='KL divergence', optimizer="rmsprop")
return decoder
d= create_decoder()
def create_vae(encoder, decoder):
e.trainable=False
vae_input = input_img
vae= Model(inputs=vae_input, outputs=vae_output)
vae.compile(loss='KL divergence', optimizer="rmsprop")
return vae
vae = create_vae(e,d)
降噪自编码器
去噪自编码器(DAE)是自编码器的随机版本,被训练为从相同输入的损坏版本重建输入(见图 9-8 )。
图 9-8
降噪自编码器。I/O 表示匹配的输入/输出单元,NI 表示有噪声的输入单元
当隐藏层中的节点多于输入时,自编码器会学习“空函数”,这意味着输出等于输入。去噪自编码器通过随机将一些输入值归零来解决这个问题,这实质上破坏了数据。
计算损失函数时,重要的是将输出值与原始输入进行比较,而不是与被破坏的输入进行比较。这样,就消除了学习身份函数而不是提取特征的风险。
卷积去噪自编码器中的层如下:
-
卷积的
-
联营
-
上采样
-
拒绝传统社会的人
-
稠密的
去噪自编码器的应用包括:
-
水印去除
-
图像降维
-
图像灰度到颜色的转换
下面是它在 TensorFlow 2.0 中的实现:
# Encoder
def create_encoder():
dae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same", input_shape=(28, 28, 1)))
dae_encoder.add(MaxPooling2D((2, 2), padding="same")
dae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
return encoder
e =create_encoder()
# Decoder
def decoder():
dae_decoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
dae_decoder.add(UpSampling2D((2, 2))
dae_decoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
dae_decoder.add(UpSampling2D((2, 2))
return decoder
d= create_decoder()
dae = Model(e, d)
dae.compile(optimizer='adadelta',loss='binary_crossentropy')
循环自编码器
递归自编码器模型通过编码器结构将序列数据汇总为固定长度的向量,然后通过解码器结构重建原始序列(参见图 9-9 )。概括向量可用于表示时间序列特征。
图 9-9
递归自编码器
递归自编码器中的层如下:
-
把...嵌入
-
简单神经网络
-
简单电池
-
稠密的
-
拒绝传统社会的人
递归自编码器的一个应用是用于传感器信号分析。
下面是它在 TensorFlow 2.0 中的实现:
#encoder
def create_encoder():
rae.add(Dense(128, activation="tanh", input_shape=(timesteps, input_dim,))
rae.add(LSTM(64,return_sequences=True)
rae.add(LSTM(32,return_sequences=True)
return encoder
e = create_encoder()
#decoder
def create_decoder():
rae.add(Dense(input_dim, activation="tanh", inputs_shape =(timesteps, 32))
rae.add(LSTM(64, return_sequences=True,activation='tanh')
rae.add(LSTM(128, return_sequences=True,activation='tanh')
# model for rae
def create_rae(encoder, decoder):
e.trainable=False
rae_input = input_val
rae= Model(inputs=rae_input, outputs=rae_output)
rae.compile(loss='binary_crossentropy', optimizer="rmsprop")
return rae
rae = create_rae(e,d)
稀疏自编码器
稀疏自编码器(SAE)提供了一种替代方法来引入瓶颈,而不会减少隐藏层中的节点数量(见图 9-10 )。相反,损失函数不利于层内的激活。
图 9-10
稀疏自编码器。I/O 表示匹配的输入/输出单元
稀疏自编码器中的层如下:
-
输入层
-
隐藏层:通常是致密层
-
输出层
-
高斯噪声
-
高斯-安德鲁普特
稀疏自编码器的一个应用是分类问题。
下面是它在 TensorFlow 2.0 中的实现:
#encoder
def create_encoder():
sae.add(Dense(28, input_shape=(timesteps, input_dim,))
sae.add(LSTM(64,return_sequences=True)
sae.add(LSTM(32,return_sequences=True)
return encoder
e =create_encoder()
#decoder
def create_decoder():
sae.add(Dense(input_dim,inputs_shape =(timesteps, 32))
sae.add(LSTM(60, return_sequences=True,activation='tanh')
sae.add(LSTM(18, return_sequences=True,activation='tanh')
return decoder
d =create_decoder()
# model for sae
def create_sae(encoder, decoder):
e.trainable=False
sae_input = input_val
sae= Model(inputs=sae_input, outputs=sae_output)
sae.compile(loss='KL divergence', optimizer="rmsprop")
return sae
sae = create_sae(e,d)
Note
潜在空间的维度应该大于输入空间的维度。
堆叠自编码器
堆叠式自编码器是具有多层稀疏自编码器的神经网络(见图 9-11 )。当我们向自编码器添加更多隐藏层时,它有助于将高维数据减少为代表重要特征的较小代码。每个隐藏层都比上一个隐藏层更紧凑。对于后面的层,我们使用来自前面层的未被破坏的输入。在训练了一堆编码器之后,我们可以将输出作为独立的监督机器学习模型的输入,就像支持向量机或多类逻辑回归一样。
图 9-11
堆叠式自编码器。I/O 表示匹配的输入/输出单元
堆叠式自编码器中的层如下:
-
输入层
-
隐藏层:通常是致密层
-
输出层
-
高斯噪声
-
高斯-安德鲁普特
堆叠式自编码器的一个应用是用于手写分析。
下面是它在 TensorFlow 2.0 中的实现:
#encoder 1
def create_encoder1():
sae.add(Dense(28, input_shape=(timesteps, input_dim,))
sae.add(LSTM(64,return_sequences=True)
sae.add(LSTM(32,return_sequences=True)
return encoder
e1 = create_encoder1()
#decoder 1
def decoder1():
sae.add(Dense(input_dim,inputs_shape =(timesteps, 32))
sae.add(LSTM(60, return_sequences=True,activation='tanh')
sae.add(LSTM(18, return_sequences=True,activation='tanh')
return decoder
d1 =create_decoder1()
#encoder 2
def encoder2():
sae.add(Dense(28, input_shape=(timesteps, input_dim,))
sae.add(LSTM(64,return_sequences=True)
sae.add(LSTM(32,return_sequences=True)
return encoder
e2 =create_encoder1()
#decoder 2
def decoder2():
sae.add(Dense(input_dim,inputs_shape =(timesteps, 32))
sae.add(LSTM(60, return_sequences=True,activation='tanh')
sae.add(LSTM(18, return_sequences=True,activation='tanh')
return decoder
d2 =create_decoder2()
# model for sae
def create_sae(encoder, decoder):
e.trainable=False
sae_input = input_val
sae= Model(inputs=sae_input, outputs=sae_output)
sae.compile(loss='KL divergence', optimizer="rmsprop")
return sae
sae = create_sae(e,d)
卷积自编码器
通常在切片和堆叠数据时会丢失大量信息。卷积自编码器不堆叠数据,而是保持输入图像数据的空间信息不变(见图 9-12 )。它在所谓的卷积层中提取信息。例如,一个平面的 2D 图像被提取为一个厚的正方形,然后继续变成一个长的立方体,再变成另一个更长的立方体。此过程旨在保留数据中的空间关系。这是自编码器中的编码过程。在中间,有一个完全连接的自编码器。接下来是解码过程,将立方体展平为 2D 平面图像。
图 9-12
卷积自编码器
卷积自编码器中的层如下:
-
卷积的
-
联营
-
上采样
-
拒绝传统社会的人
-
稠密的
卷积自编码器的应用包括:
-
笔迹分析
-
图像降噪
下面是它在 TensorFlow 2.0 中的实现:
#Encoder
def create_encoder():
cae_encoder = tf.keras.Sequential()
cae_encoder.add(Conv2D(16, (3, 3), activation="relu", padding="same", input_shape=(28, 28, 1)))
cae_encoder.add(MaxPooling2D((2, 2), padding="same")
cae_encoder.add(Conv2D(8, (3, 3), activation="relu", padding="same")
cae_encoder.add(MaxPooling2D((2, 2), padding="same")
cae_encoder.add(Conv2D(8, (3, 3), activation="relu", padding="same")
cae_encoder.add(MaxPooling2D((2, 2), padding="same")
return encoder
e =create_encoder()
#Decoder
def create_decoder():
cae_decoder.add(Conv2D(8, (3, 3), activation="relu", padding="same")
cae_decoder.add(UpSampling2D((2, 2))(x)
cae_decoder.add(Conv2D(8, (3, 3), activation="relu", padding="same")
cae_decoder.add(UpSampling2D((2, 2))
cae_decoder.add(Conv2D(16, (3, 3), activation="relu")
cae_decoder.add(UpSampling2D((2, 2))
cae_decoder.add(Conv2D(1, (3, 3), activation="sigmoid", padding="same")
d =create_decoder()
autoencoder = Model(e, d)
autoencoder.compile(optimizer='adadelta', loss="binary_crossentropy")
堆叠去噪自编码器
在堆叠去噪自编码器中,输入损坏仅用于初始去噪(见图 9-13 )。这有助于了解数据中的重要特征。堆叠去噪自编码器就是许多去噪自编码器串在一起。
图 9-13
堆叠去噪自编码器。I/O 表示匹配的输入/输出单元
SDAs 的一个关键功能是无监督的预训练,当输入通过时,一层一层地进行。一旦对每一层进行预处理,以便对来自前一层的输入进行特征选择和提取,就可以进行第二阶段的监督微调。
SDAs 中的随机腐败
去噪自编码器将数据混洗,并通过尝试重建数据来了解数据。洗牌的行为就是噪音,而网络的工作就是识别噪音中的特征,以便对输入进行分类。当网络被训练时,它生成一个模型,并通过损失函数测量该模型和基准之间的距离。其最小化损失函数的尝试包括对混洗的输入进行重采样并重构数据,直到它找到使其模型最接近地面事实的那些输入。
堆叠卷积去噪自编码器中的层如下:
-
卷积的
-
联营
-
上采样
-
拒绝传统社会的人
-
稠密的
堆叠去噪自编码器的应用包括:
-
水印去除
-
图像降维
-
图像灰度到颜色的转换
下面是它在 TensorFlow 2.0 中的实现:
# Encoder1
def create_encoder1():
dae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same", input_shape=(28, 28, 1)))
dae_encoder.add(MaxPooling2D((2, 2), padding="same")
dae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
return encoder1
E1 =create_encoder1()
# Decoder1
def create_decoder1():
dae_decoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
dae_decoder.add(UpSampling2D((2, 2))
dae_decoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
dae_decoder.add(UpSampling2D((2, 2))
return decoder1
d1= create_decoder1()
# Encoder2
def create_encoder2():
dae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same", input_shape=(28, 28, 1)))
dae_encoder.add(MaxPooling2D((2, 2), padding="same")
dae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
return encoder2
e2 =create_encoder2()
# Decoder2
def create_decoder2():
dae_decoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
dae_decoder.add(UpSampling2D((2, 2))
dae_decoder.add(Conv2D(32, (3, 3), activation="relu", padding="same")
dae_decoder.add(UpSampling2D((2, 2))
return decoder2
d2= create_decoder2()
# model for scae
def create_scae(e2, d2):
e.trainable=False
scae_input = input_val
scae= Model(inputs=scae_input, outputs=scae_output)
scae.compile(loss='KL divergence', optimizer="rmsprop")
return scae
scae = create_scae(e2,d2)
收缩自编码器
收缩型自编码器是一种无监督的深度学习技术,可以帮助神经网络对未标记的训练数据进行编码(见图 9-14 )。这是通过构造损失项来实现的,该损失项惩罚隐藏层激活相对于输入训练示例的大导数,本质上惩罚输入中的小变化导致编码空间中的大变化的情况。
图 9-14
收缩自编码器。I/O 表示匹配的输入/输出单元
堆叠收缩自编码器中的层如下:
-
卷积的
-
联营
-
上采样
-
拒绝传统社会的人
-
稠密的
收缩自编码器的应用包括:
-
水印去除
-
图像降维
-
图像灰度到颜色的转换
-
信息检索
-
异常检测
下面是它在 TensorFlow 2.0 中的实现:
#Encoder
def create_encoder():
input_img = Input(shape=(30, 30, 3))
vae_encoder = tf.keras.models.Sequential()
vae_encoder.add(Conv2D(32, (3, 3), activation="relu", padding="same", input_shape=input_img)))
vae_encoder.addConv2D(32, (3, 3), activation="relu", padding="same")
vae_encoder.addMaxPooling2D((2, 2), padding="same")
vae_encoder.addConv2D(64, (3, 3), activation="relu", padding="same")
vae_encoder.addConv2D(64, (3, 3), activation="relu", padding="same")
vae_encoder.addMaxPooling2D((2, 2), padding="same")
vae_encoder.addConv2D(128, (3, 3), activation="relu", padding="same")
vae_encoder.addConv2D(128, (3, 3), activation="relu", padding="same")
vae_encoder.addMaxPooling2D((2, 2), padding="same")
vae_encoder.compile(loss='categorical_crossentropy', optimizer="rmsprop")
return encoder
e =create_encoder()
#Decoder
def create_decoder():
vae_decoder = tf.keras.models.Sequential()
vae_decoder = Conv2D(32, (3, 3), activation="relu", padding="same")
vae_decoder = Conv2D(32, (3, 3), activation="relu", padding="same")
vae_decoder = UpSampling2D((2, 2), interpolation="bilinear")
vae_decoder = Conv2D(64, (3, 3), activation="relu", padding="same")
vae_decoder = Conv2D(64, (3, 3), activation="relu", padding="same")
vae_decoder = UpSampling2D((2, 2), interpolation="bilinear")
vae_decoder = Conv2D(128, (3, 3), activation="relu", padding="same")
vae_decoder = Conv2D(128, (3, 3), activation="relu", padding="same")
vae_decoder = UpSampling2D((2, 2), interpolation="bilinear")
vae_decoder = Conv2D(3, (3, 3), activation="sigmoid", padding="same")
vae_encoder.compile(loss='binary_crossentropy', optimizer="rmsprop")
return decoder
d= create_decoder()
def create_vae(encoder, decoder):
e.trainable=False
vae_input = input_img
vae= Model(inputs=vae_input, outputs=vae_output)
vae.compile(loss='binary_crossentropy', optimizer="rmsprop")
return vae
vae = create_vae(e,d)
马尔可夫链
马尔可夫链是一种统计建模随机过程的方法(见图 9-15 )。马尔可夫链是基于“无记忆”原则的概率自动机换句话说,流程的下一个状态只取决于前一个状态,而不是状态序列。状态转移的概率分布通常表示为马尔可夫链的转移矩阵。如果马尔可夫链有j
个可能的状态,那么矩阵将是一个jxj
矩阵,使得条目(n, m)
是从状态n
转移到状态m
的概率。此外,转移矩阵必须是一个随机矩阵**,,它是一个每行中的元素相加必须正好为 1 的矩阵。马尔可夫链本质上由一组满足马尔可夫性质的转移组成,这些转移由某种概率分布决定。它是一套从一种“状态”(一种情况或一组值)跳到另一种“状态”的数学系统。
*在概率论和统计学中,术语“马尔可夫性质”是指随机过程的无记忆性质。
图 9-15
马尔可夫链。PHC 表示概率隐藏单元
马尔可夫链的层次如下:
-
哈密尔顿·蒙特卡洛
-
稠密的
马尔可夫链的应用包括:
-
文本生成
-
金融建模
-
视频建模
-
DNA 建模
下面是它在 TensorFlow 2.0 中的实现:
mc = tf.keras.models.Sequential()
mc.add(HamiltonianMonteCarlo(target_log_prob_fn=get_unnormalized_log_probability(data),num_leapfrog_steps=3,step_size=step_size,step_size_update_fn=tfp.mcmc.make_simple_step_size_update_policy(),seed=1398)
mc.add(Dense(102, activation="linear")
mc.add(Dense(104, activation="linear")
mc.add(Dense(124, activation="linear")
mc.add(Dense(14, activation="linear")
mc.add(Dense(12, activation="linear")
mcr.compile(loss='binary_crossentropy', optimizer="rmsprop")
Hopfield 网络
Hopfield 网络是一种联想神经网络模型(见图 9-16 )。Hopfield 网络与通过模式识别和存储来模拟人类记忆的概念相关联。它是一种循环神经网络。然而,Hopfield 网络返回相同大小的模式。
图 9-16
霍普菲尔德网络。BI 表示反馈输入单元
人类的记忆是如何工作的
人类的记忆并不位于大脑中的一个单一位置,而是一个全脑范围的过程,其中大脑的几个不同区域相互协同工作(有时称为分布式处理)。一个简单的任务被大脑从许多不同的区域积极而无缝地重建:记忆的每个元素(视觉、声音、词汇和情感)都被编码在最初创建该片段的大脑的同一个部分(视觉皮层、运动皮层、语言区等)。),并且记忆的回忆有效地重新激活了在原始编码期间产生的神经模式。把它想象成一个复杂的网络,其中的线象征着记忆的各种元素,这些元素在被称为节点的交叉点上连接起来,形成一个完整的圆形记忆(见图 9-17 )。
图 9-17
人类的记忆是如何工作的
Hopfield 网络中的层如下:
-
把...嵌入
-
稠密的
-
拒绝传统社会的人
Hopfield 网络的应用包括:
-
模式识别
-
图像检测和识别
-
X 射线图像的增强
-
医学图像恢复
下面是它在 TensorFlow 2.0 中的实现:
hn = tf.keras.models.Sequential()
hn.add(HamiltonianMonteCarlo(target_log_prob_fn=get_unnormalized_log_probability(data),num_leapfrog_steps=3)
hn.add(Dense(12, activation="linear")
hn.add(Dense(14, activation="linear")
hn.add(Dense(24, activation="linear")
hn.add(Dense(14, activation="linear")
hn.add(Dense(12, activation="linear")
hn.compile(loss='binary_crossentropy', optimizer="rmsprop")
双向联想存储器
双向联想记忆(BAM)是由 Bart Kosko 于 1988 年提出的。它是一种循环神经网络(见图 9-18 )。
关联存储器的类型包括:
-
自动联想
-
异联想的
图 9-18
双向联想存储器
BAM 是异关联的,所以给定一个模式,它可以返回另一个可能大小不同的模式。它类似于 Hopfield 网络,因为它们都是联想记忆的形式。
双向联想记忆中的层如下:
-
把...嵌入
-
稠密的
-
拒绝传统社会的人
双向联想记忆的应用包括:
-
模式识别
-
图像检测和识别
-
X 射线图像的增强
-
医学图像恢复
下面是它在 TensorFlow 2.0 中的实现:
bam = tf.keras.models.Sequential()
bam.add(visible_dim, hidden_dim, number_of_epochs=5, batch_size= 10)
bam.add(Dense(21, activation="linear")
bam.add(Dense(34, activation="linear")
bam.add(Dense(20, activation="linear")
bam.add(Dense(14, activation="linear")
bam.add(Dense(12, activation="linear")
bam.compile(loss='binary_crossentropy', optimizer="rmsprop")
玻尔兹曼机
受限玻尔兹曼机(RBM)是一个无向图形模型(见图 9-19 )。它最初是由保罗·斯摩棱斯基在 1986 年推出的“口琴”。受限玻尔兹曼机在协同过滤中实现了最先进的性能。玻尔兹曼机是一种随机(非确定性)或生成式深度学习模型,只有可见(输入)和隐藏节点。基于能量的模型(EBM)是一种 RBM,通过将标量能量与变量的每个配置相关联来捕捉变量之间的依赖性。推理包括钳制观察变量的值,并找到剩余变量的配置,使能量最小化。学习包括寻找一个能量函数,其中观察到的变量配置被赋予比未观察到的更低的能量。EBMs 可以被视为预测、分类或决策任务的概率估计的替代方案,因为不需要适当的标准化。
图 9-19
玻尔兹曼机。BI 表示反馈输入单元,PHC 表示概率隐藏单元
波尔兹曼机的类型包括以下几种:
-
受限玻尔兹曼机
-
深度信念网络
-
深度玻尔兹曼机(DBMs)
玻尔兹曼机中的层如下:
-
把...嵌入
-
联营
-
稠密的
-
拒绝传统社会的人
玻尔兹曼机的应用包括:
-
降维
-
分类
-
回归
-
协同过滤
-
特征学习
-
主题建模
Note
完整的玻尔兹曼机实现极其困难并且会耗尽资源。建议您不要在您的个人系统上实现此结构。这就是为什么我们不看玻尔兹曼机的例子。相反,我们将看一个更有效模型的例子,受限玻尔兹曼机。
受限玻尔兹曼机
受限玻尔兹曼机(RBMs)由 Geoffrey Hinton 发明,是一种两层人工神经网络(一层是可见层,另一层是隐藏层)。这两层由一个具有生成能力的完全二分图连接(见图 9-20 )。他们有能力学习一组输入的概率分布。RBM 是一类特殊的玻尔兹曼机,它们受限于可见单元和隐藏单元之间的联系。与其他玻尔兹曼机相比,这使得实现它们变得容易。这意味着可见层中的每个节点都连接到隐藏层中的每个节点,但同一组中没有两个节点相互连接。这种限制允许比一般玻尔兹曼机可用的训练算法更有效的训练算法,特别是基于梯度的对比发散算法。
成果管理制的类型包括:
-
Relational restricted Boltzmann machines
图 9-20
受限玻尔兹曼机。BI 表示反馈输入单元,PHC 表示概率隐藏单元
受限玻尔兹曼机中的层如下:
-
把...嵌入
-
联营
-
稠密的
-
拒绝传统社会的人
受限玻尔兹曼机的应用包括以下内容:
-
降维
-
分类
-
回归
-
协同过滤
-
特征学习
-
主题建模
下面是它在 TensorFlow 2.0 中的实现:
rbm_2 = BernoulliRBM(learning_rate=0.05, n_iter=50, n_components=150, random_state=0, verbose=True)
深度信念网络
深度信念网络(DBN)是一种复杂类型的生成神经网络,它使用无监督的机器学习模型来产生结果(见图 9-21 )。这种类型的网络说明了最近在使用相对无标签的数据来建立无监督模型方面所做的一些工作。
图 9-21
深度信念网络。BI 表示反馈输入单元;PHC 表示概率隐藏单元;I/O 表示匹配的输入/输出单元
深度信念网络可以定义为一堆受限的玻尔兹曼机,其中每个 RBM 层都与前一层和后一层进行通信。任何单个层的节点都不会彼此横向通信。
这一堆 RBM 可能以 Softmax 层结束,以创建一个分类器,或者它可能只是在无监督的学习场景中帮助聚类未标记的数据。
除了第一层和最后一层之外,深度信念网络中的每一层都有双重角色——它既是之前节点的隐藏层,又是之后节点的输入(或“可见”)层。
深度信念网络的类型:
-
贪婪分层训练
-
唤醒-睡眠算法
深度信念网络是由受限玻尔兹曼机(RBM)的几个中间层和作为分类器的最后一层组成的网络。
深度信念网络的应用包括以下内容:
-
自然语言处理
-
图象生成
-
图像识别
-
视频识别
下面是它在 TensorFlow 2.0 中的实现:
rbm_2 = BernoulliRBM(learning_rate=0.05, n_iter=50, n_components=150, random_state=0, verbose=True)
rbm_2 = BernoulliRBM(learning_rate=0.05, n_iter=50, n_components=150, random_state=0, verbose=True)
rbm_2 = BernoulliRBM(learning_rate=0.05, n_iter=50, n_components=150, random_state=0, verbose=True)
rbm_2 = BernoulliRBM(learning_rate=0.05, n_iter=50, n_components=150, random_state=0, verbose=True)
rbm.compile(loss='binary_crossentropy', optimizer="rmsprop")
去进化网络
去进化网络,也称为去进化神经网络,本质上与 CNN 非常相似,但执行方向相反(见图 9-22 )。一个信号可能由于与其他信号卷积而丢失。卷积神经网络模拟生物大脑额叶功能在图像处理中的工作。额叶有助于对物体进行归类和分类,此外还有助于区分不同的物体。
图 9-22
反进化网络。C/P 表示卷积或池单元
反向函数可以被视为卷积神经网络的逆向工程。它构建了从机器视觉视野中作为整个图像的一部分而捕获的层,并分离出已经错综复杂的内容。
Note
反卷积层更准确地描述为转置卷积层。它执行互相关。
去卷积网络中的层如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
解卷积网络的应用包括以下内容:
-
图像合成
-
映象分析
下面是它在 TensorFlow 2.0 中的实现:
model = Sequential()
model.add(Dense(25*15*20, input_shape=(56,)))
model.add(Activation('relu'))
model.add(Dense(25*15*20))
model.add(Activation('relu'))
model.add(Reshape((25,15,20)))
model.add(Conv2DTranspose(20, kernel_size=(3,3), strides=(2,2), padding="same"))
model.add(Activation('relu'))
model.add(Conv2DTranspose(20, kernel_size=(3,3), strides=(2,2), padding="same"))
model.add(Activation('relu'))
model.add(Conv2DTranspose(20, kernel_size=(3,3), strides=(2,2), padding="same"))
model.add(Activation('relu'))
model.add(Conv2D(1, kernel_size=(3,3), strides=(1,1), padding="same"))
model.add(Activation('relu'))
model.add(Lambda(lambda t: t[:,2:-2,2:-3,0]))
model.summary()
深度卷积逆图形网络
深度卷积逆图形网络(DCIGN)是 CNN 编码器和深度神经网络(DNN)解码器的组合(见图 9-23 )。它有一个很长的名字,适合它的长结构。然而,这个名字令人误解,因为它不是一个网络,而是一个变型自编码器 (VAE)。
图 9-23
深度卷积逆图形网络。C/P 表示卷积或池单元,I/O 表示匹配的输入/输出单元
解卷积网络中的层包括:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
解卷积网络的应用包括以下内容:
-
自动语义图像分割
-
放大图像
-
图像处理
下面是它在 TensorFlow 2.0 中的实现:
#Encoder
def create_encoder():
model = Sequential()
model.add (Embedding(len(vocabulary_inv), embedding_dim, input_length=sequence_length, name="embedding")
model.add (Dropout(dropout_prob[0])
model.add (Convolution1D(filters=num_filters, kernel_size=sz, padding="valid", activation="relu",strides=1)
model.add (MaxPooling1D(pool_size=2)
model.add (Flatten()
model.add (Dropout(dropout_prob[1])
model.add (Dense(hidden_dims, activation="relu")
model.add (Dense(1, activation="sigmoid")
model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])
return encoder
e =create_encoder()
#Decoder
def decoder():
model = Sequential()
model.add ( Dense(n_hidden_1, activation="relu")
model.add ( Dense(n_hidden_2, activation="relu")
model.add ( Dense(n_hidden_3, activation="relu")
model.add ( Dense(n_output, activation="sigmoid")
model = Model(input_bits, out_put)
model.compile(optimizer='adam', loss="mse", metrics=[bit_err])
model.summary()
return decoder
d =create_decoder()
def create_dcign(encoder, decoder):
e.trainable=False
vaedcign_input = input_img
dcign= Model(inputs=dcign_input, outputs=dcign_output)
dcign.compile(loss='binary_crossentropy', optimizer="rmsprop")
return dcign
Dcign = create_dcign(e,d)
液态机器
液态状态机(LSM)是一种脉冲神经网络(见图 9-24 )。节点随机地相互连接,并从外部源以及其他节点接收时变输入。这些连接是循环的,它将输入转换成网络节点中激活的时空模式,该模式由线性判别单元读出。它的灵感来源于人类大脑中的尖峰信号。液态机器由泄漏积分和点火(LIF)神经元组成。他们没有受过明确的训练。
图 9-24
液态机器。SHC 指出一个隐藏的尖峰细胞
人脑尖峰
棘波是非常快速的脑电波,因其在脑电图(EEG)上的形状而得名。每个波之后都可能有缓慢的三角波。棘波与脑电图上的其他大脑活动截然不同(见图 9-25 )。多棘波是一系列快速发生的尖峰。
图 9-25
人类大脑中的尖峰
LSM 中的图层如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
LSM 的应用包括视频活动识别。
下面是它在 TensorFlow 2.0 中的实现:
model = Sequential()
model.add (Embedding(len(vocabulary_inv), embedding_dim, input_length=sequence_length, name="embedding")
model.add (Dropout(dropout_prob[0])
model.add (Convolution1D(filters=num_filters, kernel_size=sz, padding="valid", activation="relu",strides=1)
model.add (MaxPooling1D(pool_size=2)
model.add (Flatten()
model.add (Dropout(dropout_prob[1])
model.add (Dense(hidden_dims, activation="relu")
model.add (Dense(1, activation="sigmoid")
model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])
回声状态网络
回声状态网络是一种循环神经网络(见图 9-26 )。输入、隐藏层(储层) Win、和储层 Wr 之间的权重是随机分配的,不可训练。输出神经元(读出层)的权重是可以训练和学习的,因此网络可以再现特定的时间模式。隐藏层(或储层)连接非常稀疏(通常< 10%连接)。蓄水池结构创建输入的循环非线性嵌入,然后可以连接到期望的输出。这些最终重量将是可训练的。可以将嵌入连接到不同的预测模型(例如用于分类问题的可训练 NN 或岭回归/SVM)。
图 9-26
回声状态网络。RC 表示复发细胞
ESN 中的层包括以下内容:
-
把...嵌入
-
简单神经网络
-
简单电池
-
稠密的
-
拒绝传统社会的人
ESN 的应用包括股票价格预测。
下面是它在 TensorFlow 2.0 中的实现:
#Use ESN library with tensor flow and Keras
from ESN import EchoStateRNNCell
Import tensorflow
Import Keras
rrn = tf.keras.Sequential()
rnn.add(layers.Embedding(input_dim=1000, output_dim=64))
rnn.add(layers. EchoStateRNNCell(128))
rnn.add(layers.Dense(10, activation="softmax"))
rnn.add(layers. EchoStateRNNCell(90))
rnn.compile(loss='binary_crossentropy', optimizer="rmsprop"
深层剩余网络
ResNet 的核心思想是引入一个跳过一层或多层的“身份快捷连接”(见图 9-27 )。何、、、任和孙健认为堆叠层不应该降低网络性能,因为我们可以简单地在当前网络上堆叠身份映射(不做任何事情的层),最终的架构也会执行相同的功能。
图 9-27
深残网。HC 表示隐藏单元格
这表明较深的模型不应该比其较浅的模型产生更高的训练误差。他们假设让堆叠层拟合残差映射比让它们直接拟合所需的底层映射更容易。残差块明确地允许它这样做。
由于其相似的体系结构,ResNet 可以被认为是高速公路网的一个特例。然而,高速公路网本身的表现不如 ResNets。这告诉我们,保持这些“梯度公路”的畅通比去寻求更大的解决方案空间更重要。
根据这种直觉,作者改进了剩余块,并提出了剩余块的预激活变体,其中梯度可以通过捷径连接流动到任何其他更早的层,不受阻碍。
ResNet 是批处理规范化的早期采用者之一(由 Ioffe 和 Szegedy 撰写的批处理规范论文已于 2015 年提交给 ICML)。资源网的基本构造块是 conv 和标识块。
DRN 中唯一的图层是嵌入图层。DRN 的一个应用是图像识别。
下面是它在 TensorFlow 2.0 中的实现:
#Use DRN library with tensorflow and Keras
import keras
Import tensorflow
import keras_resnet.models
Model = model.Sequential()
model.add(layers.Embedding(input_dim=1000, output_dim=64))
model.compile("adam", "categorical_crossentropy", ["accuracy"])
ResNeXt
ResNeXt 遵循与 ResNet 相同的拆分-转换-合并范例,除了在这个变体中,不同路径的输出通过相加在一起而被合并(见图 9-28 )。作者,加州大学圣地亚哥分校和脸书人工智能研究所(FAIR)引入了一个名为基数的超参数——独立路径的数量——以提供一种调整模型容量的新方法。实验表明,通过增加基数比通过更深或更宽可以更有效地获得准确性。
图 9-28
ResNeXt
作者表示,与 Inception 相比,这种新颖的架构更容易适应新的数据集/任务,因为它具有简单的范式,只有一个超参数需要调整,而 Inception 有许多超参数(如每个路径的卷积层的内核大小)需要调整。
在实践中,“分裂-变换-合并”通常由逐点分组卷积层来完成,该卷积层将其输入分成特征映射组,并分别执行正常卷积。它们的输出被深度连接,然后馈入 1×1 卷积层。
ResNeXt 中的层如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
ResNeXt 的一个应用是图像分类。
下面是它在 TensorFlow 2.0 中的实现:
from keras import layers
from keras import models
def residual_network(x):
#ResNeXt by default. For ResNet set `cardinality` = 1 above.
def add_common_layers(y):
y = layers.BatchNormalization()(y)
y = layers.LeakyReLU()(y)
return y
def grouped_convolution(y, nb_channels, _strides):
# when `cardinality` == 1 this is just a standard convolution
if cardinality == 1:
return layers.Conv2D(nb_channels, kernel_size=(3, 3), strides=_strides, padding="same")(y)
assert not nb_channels % cardinality
_d = nb_channels // cardinality
# in a grouped convolution layer, input and output channels are divided into `cardinality` groups,
# and convolutions are separately performed within each group
groups = [ ]
for j in range(cardinality):
group = layers.Lambda(lambda z: z[:, :, :, j * _d:j * _d + _d])(y)
groups.append(layers.Conv2D(_d, kernel_size=(3, 3), strides=_strides, padding="same")(group))
# the grouped convolutional layer concatenates them as the outputs of the layer
y = layers.concatenate(groups)
return y
def residual_block(y, nb_channels_in, nb_channels_out, _strides=(1, 1), _project_shortcut=False):
#Our network consists of a stack of residual blocks. These blocks have the same topology,
and are subject to two simple rules:
#If producing spatial maps of the same size, the blocks share the same hyper-parameters (width and filter sizes).
#Each time the spatial map is down-sampled by a factor of 2, the width of the blocks is multiplied by a factor of 2.
shortcut = y
# we modify the residual building block as a bottleneck design to make the network more economical
y = layers.Conv2D(nb_channels_in, kernel_size=(1, 1), strides=(1, 1), padding="same")(y)
y = add_common_layers(y)
# ResNeXt (identical to ResNet when `cardinality` == 1)
y = grouped_convolution(y, nb_channels_in, _strides=_strides)
y = add_common_layers(y)
y = layers.Conv2D(nb_channels_out, kernel_size=(1, 1), strides=(1, 1), padding="same")(y)
# batch normalization is employed after aggregating the transformations and before adding to the shortcut
y = layers.BatchNormalization()(y)
# identity shortcuts used directly when the input and output are of the same dimensions
if _project_shortcut or _strides != (1, 1):
# when the dimensions increase projection shortcut is used to match dimensions (done by 1×1 convolutions)
# when the shortcuts go across feature maps of two sizes, they are performed with a stride of 2
shortcut = layers.Conv2D(nb_channels_out, kernel_size=(1, 1), strides=_strides, padding="same")(shortcut)
shortcut = layers.BatchNormalization()(shortcut)
y = layers.add([shortcut, y])
# relu is performed right after each batch normalization,
# expect for the output of the block where relu is performed after the adding to the shortcut
y = layers.LeakyReLU()(y)
return y
# conv1
x = layers.Conv2D(64, kernel_size=(7, 7), strides=(2, 2), padding="same")(x)
x = add_common_layers(x)
# conv2
x = layers.MaxPool2D(pool_size=(3, 3), strides=(2, 2), padding="same")(x)
for i in range(3):
project_shortcut = True if i == 0 else False
x = residual_block(x, 128, 256, _project_shortcut=project_shortcut)
# conv3
for i in range(4):
# down-sampling is performed by conv3_1, conv4_1, and conv5_1 with a stride of 2
strides = (2, 2) if i == 0 else (1, 1)
x = residual_block(x, 256, 512, _strides=strides)
# conv4
for i in range(6):
strides = (2, 2) if i == 0 else (1, 1)
x = residual_block(x, 512, 1024, _strides=strides)
# conv5
for i in range(3):
strides = (2, 2) if i == 0 else (1, 1)
x = residual_block(x, 1024, 2048, _strides=strides)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(1)(x)
return x
image_tensor = layers.Input(shape=(img_height, img_width, img_channels))
network_output = residual_network(image_tensor)
model = models.Model(inputs=[image_tensor], outputs=[network_output])
print(model.summary())
来源: https://gist.github.com/mjdietzx/0cb95922aac14d446a6530f87b3a04ce
神经图灵机
作者 Alex Graves、Greg Wayne 和 Ivo Danihelka 设计了神经图灵机,它使用注意力过程与外部记忆资源进行交互(见图 9-29 )。我们以数组的形式创建一个内存结构,然后从中读取和写入。重要的是要注意,我们没有无限的记忆能力来容纳所有的信息,我们通过相似性或相关性来访问信息。
图 9-29
神经图灵机。HC 表示隐藏单元,MC 表示存储单元
例如、想象一个内存结构mt,它包含 x 行,每一行都有 y 元素。每行代表一条信息(内存)。我们使用权重 we 从记忆中检索信息,权重包括我们当前的输入、之前的焦点以及可能的移动和模糊等因素。
阅读
在常规编程中,通过索引mt【I】来访问内存。然后,我们推导出一种阅读机制,其中我们的结果是我们记忆的加权和,所有权重的和等于 1。在单词嵌入中,我们使用线性代数来处理关系。有时我们会根据积累的知识来合并信息。控制器从输入中提取特征( c t ),我们用它来计算权重。
为了计算权重,我们,测量 c t 和我们每个记忆条目之间的相似性。使用余弦相似度计算得分 s 。这里, n 是我们提取的特征 c t , m 是我们内存中的每一个单独的行。对分数应用 Softmax 函数以计算权重。添加 β t 来放大或缩小分数的差异。例如,如果它大于 1,它会放大差异。我们基于相似性检索信息。这称为内容寻址(见图 9-30 )。
图 9-30
记忆写入的过程
存储器写入过程由先前的状态和新的输入组成。我们擦除先前状态的一部分,其中 pt 是擦除向量。然后,我们写下我们的新信息,其中 adt 是我们想要添加的。在这里,通过一个产生 we 的控制器,我们从内存中读取和写入。
神经图灵机的应用如下:
-
自然语言处理
-
元学习
下面是它在 TensorFlow 2.0 中的实现:
import tensorflow as tf
from ntm_cell import NTMCell
from ntm import NTM
Model = model.Sequential()
model.add(NTMCell(input_dim=1000, output_dim=64))
model.compile("adam", "categorical_crossentropy", ["accuracy"])
胶囊网络
CAPSNet 由 Geoffrey Hinton 于 2017 年提出(见图 9-31 )。它旨在解决卷积神经网络(CNN)的问题。CNN 擅长对图像进行分类,但当图像旋转或倾斜时,或者当图像具有所需对象的特征,但顺序或位置不正确时,CNN 就会失败。CNN 难以对这些类型的图像进行分类的原因是,它执行多个卷积和汇集阶段。汇集步骤汇总并减少图像中发现的每个特征的信息。在此过程中,它会丢失重要信息,如要素的位置及其与其他要素的关系。
图 9-31
胶囊网络。HC 表示隐藏单元格;k 表示内核;CC 表示胶囊细胞
CAPSNet 架构
CAPSNet 基于神经“胶囊”的概念它从卷积步骤开始,就像普通的 CNN 一样。但是代替汇集步骤,当网络发现图像中的特征时,它将它们重塑成向量,使用特殊的激活功能“挤压”它们,并将每个特征输入到胶囊中。这是一个专门的神经结构,只处理这个功能。第一层中的每个胶囊开始处理,然后将其结果馈送到嵌套在第一胶囊内的一层或多层次级胶囊。这被称为协议路由。主胶囊检测学习到的特征,同时保留上下文信息,如位置和与其他元素的关系。
CAPSNet 的应用包括图像分类。
顶网的层数如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
下面是 TensorFlow 2.0 中神经图灵机的实现:
import keras
Import tensorflow
Import capsNet
from capsNet import CapsNet
Model = model.Sequential()
model.add(layers.Embedding(input_dim=1000, output_dim=64))
model.compile("adam", "categorical_crossentropy", ["accuracy"])
莱内-5
LeNet 是由 Yann LeCun 提出的最简单的架构之一(见图 9-32 )。它有两个卷积层和三个全连接层。我们现在知道的平均池层被称为子采样层,它具有可训练的权重(这不是当今设计 CNN 的当前实践)。
这种架构已经成为标准模板——堆叠回旋和汇集层,并以一个或多个全连接层结束网络。
图 9-32
莱内-5
LeNet-5 的一个应用是手写识别。LeNet-5 的层如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
下面是它在 TensorFlow 2.0 中的实现:
model = keras.Sequential()
model.add(layers.Conv2D(filters=6, kernel_size=(3, 3), activation="relu", input_shape=(32,32,1)))
model.add(layers.AveragePooling2D())
model.add(layers.Conv2D(filters=16, kernel_size=(3, 3), activation="relu"))
model.add(layers.AveragePooling2D())
model.add(layers.Flatten())
model.add(layers.Dense(units=120, activation="relu"))
model.add(layers.Dense(units=84, activation="relu"))
model.add(layers.Dense(units=10, activation = 'softmax'))
阿勒克斯网
2012 年,Alex Krizhevsky 提出了深度卷积神经网络。AlexNet 包含八个神经网络层,五个卷积层和三个全连接层(见图 9-33 )。这为传统的 CNN 奠定了基础,卷积层之后是激活函数,然后是最大池操作(有时池操作被省略以保留图像的空间分辨率)。AlexNet 刚刚在 LeNet-5 上又堆了几层。他是第一个实现校正线性单位(ReLUs)作为激活函数的人。
图 9-33
阿勒克斯网
AlexNet 的层次如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
AlexNet 的一个应用是机器视觉。
下面是它在 TensorFlow 2.0 中的实现:
model = Sequential()
model.add(Conv2D(filters=96, input_shape=(224,224,3), kernel_size=(11,11), strides=(4,4), padding="valid"))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding="valid"))
model.add(Conv2D(filters=256, kernel_size=(11,11), strides=(1,1), padding="valid"))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding="valid"))
model.add(Conv2D(filters=384, kernel_size=(3,3), strides=(1,1), padding="valid"))
model.add(Activation('relu'))
model.add(Conv2D(filters=384, kernel_size=(3,3), strides=(1,1), padding="valid"))
model.add(Activation('relu'))
model.add(Conv2D(filters=256, kernel_size=(3,3), strides=(1,1), padding="valid"))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding="valid"))
model.add(Flatten())
model.add(Dense(4096, input_shape=(224*224*3,)))
model.add(Activation('relu'))
model.add(Dropout(0.4))
model.add(Dense(4096))
model.add(Activation('relu'))
model.add(Dropout(0.4))
model.add(Dense(1000))
model.add(Activation('relu'))
model.add(Dropout(0.4))
model.add(Dense(17))
model.add(Activation('softmax'))
model.compile(loss=keras.losses.categorical_crossentropy, optimizer="adam", metrics=["accuracy"])
Google net(Google net)
GoogLeNet(见图 9-34 )是 Google 团队在 2014 年设计的 22 层深度网络,用于 2014 年“ImageNet 大规模视觉识别挑战赛”(ILSVRC2014)中的分类和检测。为了优化质量,架构的决定是基于 Hebbian 原则和多尺度处理的直觉。
图 9-34
Google net(Google net)
图片来源:arxiv . org/pdf/1409 . 4842 v1 . pdf
希伯恩理论是一种神经科学理论,试图解释突触可塑性,即学习过程中大脑神经元的适应。它指出突触效能的增加源于突触前细胞对突触后细胞的重复和持续刺激。
GoogLeNet 的层次如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
谷歌网络的应用包括以下方面:
-
目标检测
-
对象分类
下面是它在 TensorFlow 2.0 中的实现:
model = Sequential()
model.add(Conv2D(10, (1,1), padding="same", activation="relu")(input_img)
model.add(Conv2D(10, (3,3), padding="same", activation="relu")(layer_1)
model.add(Conv2D(10, (1,1), padding="same", activation="relu")(input_img)
model.add(Conv2D(10, (5,5), padding="same", activation="relu")(layer_2)
model.add(MaxPooling2D((3,3), strides=(1,1), padding="same")(input_img)
model.add(Conv2D(10, (1,1), padding="same", activation="relu")(layer_3)
model.add(dense_1 = Dense(1200, activation="relu")(flat_1)
model.add(dense_2 = Dense(600, activation="relu")(dense_1)
model.add(dense_3 = Dense(150, activation="relu")(dense_2)
model.add(Dense(nClasses, activation="softmax")(dense_3)
model.compile(optimizer='adam', loss="categorical_crossentropy", metrics=['accuracy'])
Xception
Xception 网络由 Franç ois Chollet 设计,是对 Inception 的改编,其中 Inception 模块已被深度方向可分离卷积所取代(见图 9-35 )。Xception 将盗梦空间假设发挥到了的极致(因此得名)。
图 9-35
Xception
资料来源: miro。介质(medium)。页:1。png〔??〕
例外网络中的层如下:
-
卷积的
-
conv 2 dttrans
-
联营
-
向上采样 2D
-
拒绝传统社会的人
-
稠密的
异常的应用包括以下内容:
-
目标检测
-
对象分类
下面是它在 TensorFlow 2.0 中的实现:
model = Sequential()
model.add(Conv2D(32, (3, 3), strides=(2, 2), use_bias=False, name="block1_conv1")
model.add(Conv2D(64, (3, 3), use_bias=False, name="block1_conv2")
model.add(Conv2D(128, (1, 1), strides=(2, 2), padding="same", use_bias=False)
model.add(SeparableConv2D(128, (3, 3), padding="same", use_bias=False, name="block2_sepconv1")
model.add(SeparableConv2D(128, (3, 3), padding="same", use_bias=False, name="block2_sepconv2")
model.add(MaxPooling2D((3, 3), strides=(2, 2), padding="same", name="block2_pool")
model.add(Conv2D(256, (1, 1), strides=(2, 2), padding="same", use_bias=False)
model.add(SeparableConv2D(256, (3, 3), padding="same", use_bias=False, name="block3_sepconv1")
model.add(SeparableConv2D(256, (3, 3), padding="same", use_bias=False, name="block3_sepconv2")
model.add(MaxPooling2D((3, 3), strides=(2, 2), padding="same", name="block3_pool")
model.add(Conv2D(728, (1, 1), strides=(2, 2), padding="same", use_bias=False)
model.add(SeparableConv2D(728, (3, 3), padding="same", use_bias=False, name="block4_sepconv1")
model.add(SeparableConv2D(728, (3, 3), padding="same", use_bias=False, name="block4_sepconv2")
model.add(MaxPooling2D((3, 3), strides=(2, 2), padding="same", name="block4_pool")
model.add(SeparableConv2D(728, (3, 3), padding="same", use_bias=False, name=prefix + '_sepconv1')
model.add(SeparableConv2D(728, (3, 3), padding="same", use_bias=False, name=prefix + '_sepconv2')
model.add(SeparableConv2D(728, (3, 3), padding="same", use_bias=False, name=prefix + '_sepconv3')
model.add(Conv2D(1024, (1, 1), strides=(2, 2),padding='same', use_bias=False)
model.add(SeparableConv2D(728, (3, 3), padding="same", use_bias=False, name="block13_sepconv1")
model.add(SeparableConv2D(1024, (3, 3), padding="same", use_bias=False, name="block13_sepconv2")
model.add(MaxPooling2D((3, 3), strides=(2, 2), padding="same", name="block13_pool")
model.add(SeparableConv2D(1536, (3, 3), padding="same", use_bias=False, name="block14_sepconv1")
model.add(SeparableConv2D(2048, (3, 3),padding='same', use_bias=False, name="block14_sepconv2")
model.add(layers.GlobalMaxPooling2D()
model.compile(optimizer='adam', loss="categorical_crossentropy", metrics=['accuracy'])
优化者
这一部分涵盖了你在深度学习项目中应该了解的优化器。
随机梯度下降
梯度是函数的斜率。给定一个参数的变化量,另一个参数的变化程度。在数学上,它可以被描述为一组参数相对于其输入的偏导数。坡度越大,坡度越陡(见图 9-36 )。
图 9-36
随机梯度下降
梯度下降是一个凸函数。这是一种迭代方法,用来寻找一个函数的参数值。它尽可能地最小化代价函数。参数最初被定义为特定值,并且从该值开始,在迭代过程中执行梯度下降以找到参数的最优值,使用微积分来找到给定成本函数的最小可能值。
在梯度下降中,“批次”表示用于计算每次迭代梯度的数据集中的样本总数。在典型的梯度下降优化中,例如批次梯度下降,批次被视为整个数据集。虽然使用整个数据集有助于以较少噪音或较少随机的方式达到最小值,但当我们的数据集变大时,问题就出现了。
假设你的数据集中有一百万个样本。如果使用典型的梯度下降优化技术,则在执行梯度下降时,必须使用所有一百万个样本来完成一次迭代,并且每次迭代都必须这样做,直到达到最小值。因此,执行起来在计算上变得非常昂贵。
随机一词指的是与随机概率相关联的系统或过程。
每次迭代随机选择几个样本,而不是整个数据集。在 SGD 中,它仅使用一个样本,即一个批次的样本,来执行每次迭代。样本被随机打乱并被选择用于执行迭代。
公式如下:
其中:
-
∂L/∂w 是梯度分量
-
α是学习率
-
w new 是新的重量
-
w 是重量
SGD 的优势包括以下几点:
-
具有动量的随机梯度下降为优化提供了一些速度,并且也有助于更好地避开局部最小值。
-
将 SGD 和内斯特罗夫用于浅层网络。
SGD 的缺点包括以下几点:
-
当参数处于不同的尺度时,它不能很好地工作,因为低的学习速率将使学习变慢,而大的学习速率可能导致振荡。
-
它不能有效地处理鞍点。
-
所有参数都有一个共同的学习率。
下面是它在 Keras 中的实现:
keras.optimizers.SGD(learning_rate=0.01, momentum=0.0, nesterov=False)
RMSProp
RMSProp 优化器类似于带有动量的梯度下降算法。动量将过去的梯度考虑在内,以平滑梯度下降的步骤。它可以应用于批量梯度下降、小批量梯度下降或随机梯度下降。
RMSProp 是一个自适应学习率,它试图在 AdaGrad 的基础上进行改进。不是取梯度平方的累积和,而是取指数移动平均(再次!)的这些梯度。
RMSProp 优化器限制垂直方向的振荡。因此,我们可以提高我们的学习率,我们的算法可以在水平方向上采取更大的步骤,从而更快地收敛。
像神经网络这样非常复杂的函数的梯度在能量通过函数传播时有消失或爆炸的趋势。函数越复杂,这个问题就越严重。
RMSProp 使用梯度平方的移动平均值来标准化梯度本身。这具有平衡步长的效果。减小大梯度的步长可避免爆炸,增大小梯度的步长可避免消失。
公式如下:
什么时候
其中:
-
a = 0.001
-
β = 0.9(论文作者推荐)
-
ε = 10 -6
RMSProp 的优势如下:
-
很好地处理鞍点
-
适用于深水网
-
伪曲率信息
-
非常适合小批量学习
RMSProp 的缺点如下:
-
如果学习率很大,重物会在峡谷中来回晃动。
-
如果学习率太大,这个振荡就会发散。
下面是它在 Keras 中的实现:
keras.optimizers.RMSprop(learning_rate=0.001, rho=0.9)
阿达格拉德
对于频繁更新的参数,AdaGrad 对学习速率的惩罚过于严厉,而对于更新不频繁的稀疏参数,AdaGrad 给予更高的学习速率。它非常适用于数据稀疏的数据集,如 tf-idf 等。这是因为它对不频繁的参数进行大的更新,对频繁的参数进行小的更新。AdaGrad 算法只是预处理随机梯度下降的一种变体。
AdaGrad 的主要好处是我们不需要手动调整学习速率。大多数实现使用默认值 0.01。
公式如下:
什么时候
其中:
-
a = 0.01
-
ε = 10 -7
AdaGrad 的一个优点是它能很好地处理鞍点。AdaGrad 的一个缺点是它的学习率总是在下降和衰减。
下面是它在 Keras 中的实现:
keras.optimizers.Adagrad(learning_rate=0.01)
阿达德尔塔
AdaDelta 类似于 RMSProp。唯一的区别是 AdaDelta 不需要一个初始学习速率常数。AdaDelta 是 AdaGrad 的扩展,它单调地降低学习速率。它通过将过去累积梯度的窗口限制为 w 的固定大小来做到这一点。在时间 t 运行平均值,然后取决于先前平均值和当前梯度。在 AdaDelta 中,我们不需要设置默认的学习速率,因为我们采用先前时间步长的运行平均值与当前梯度的比值。
公式如下:
当:
其中:
-
b = 0.95
-
ε = 10 -6
AdaDelta 的优势如下:
-
它能很好地处理鞍点。
-
它是 AdaGrad 的扩展,倾向于消除学习率衰减的问题。
-
我们不需要设定一个默认的学习率。
下面是它在 Keras 中的实现:
keras.optimizers.Adadelta(learning_rate=1.0, rho=0.95)
圣经》和《古兰经》传统中)亚当(人类第一人的名字
Adam 结合了 AdaDelta 和 RMSProp 的优点,因此在大多数问题上表现得更好。
公式如下:
时
其中:
-
a = 0.001
-
b1 = 0.9
-
b2 = 0.999
-
ε = 10 -8
Adam 的优势如下:
-
很好地处理鞍点。
-
适合深网。
-
相对较低的内存需求(虽然高于梯度下降和带动量的梯度下降)。
-
即使很少调整超参数,通常也能很好地工作。
下面是它在 Keras 中的实现:
keras.optimizers.Adam(learning_rate=0.001, beta_1=0.9, beta_2=0.999, amsgrad=False)
Note
当有疑问时,亚当是最好的选择。
阿达玛斯
当有稀疏参数更新时,Adamax 是有用的。这是 Adam 优化器的一个改编版本。
公式如下:
当:
其中:
-
a = 0.002
-
b1 = 0.9
-
b2 = 0.999
Adamax 的优势如下:
-
无穷阶范数使得算法稳定
-
适合稀疏更新的参数
下面是它在 Keras 中的实现:
keras.optimizers.Adamax(learning_rate=0.002, beta_1=0.9, beta_2=0.999)
涅斯捷罗夫加速梯度(nag)
内斯特罗夫加速梯度是正常梯度下降的一个微小变化,它可以加速训练并显著改善收敛性。内斯特罗夫动量是正常动量的简单变化。这里,梯度项不是从当前位置计算的。这很有帮助,因为梯度项总是指向正确的方向,而动量项可能不是。如果动量项指向错误的方向或过冲,梯度仍然可以“返回”并在同一个更新步骤中纠正它。
公式如下:
什么时候
其中:
-
m 被初始化为 0
-
b = 0.9
那达慕
那达慕是具有内斯特罗夫势头的亚当·拉姆斯罗普。那达慕结合了纳格和亚当。Nadam 用于噪声梯度或具有高曲率的梯度。通过对先前和当前梯度的移动平均值的指数衰减求和来加速学习过程。
公式如下:
时
其中:
-
a = 0.002
-
b1 = 0.9
-
b2 = 0.999
-
ε = 10 -7
下面是它在 Keras 中的实现:
keras.optimizers.Nadam(learning_rate=0.002, beta_1=0.9, beta_2=0.999)
损失函数
回想一下,在训练网络时,它会生成一个模型,并通过损失函数来测量该模型与基准之间的距离。其最小化损失函数的尝试包括对混洗的输入进行重采样并重构数据,直到它找到那些使其模型最接近地面事实的输入。以下部分讨论了你将会遇到的各种损失函数。
均方误差
均方差(MSE)是最常见的损失函数之一。MSE 损失函数在线性回归中广泛用于衡量模型的性能。要计算 MSE,计算预测值和实际值之间的差值的平方。然后计算整个数据集的平均值。
下面是它在 Keras 中的实现:
keras.losses.mean_squared_error(y_true, y_pred)
平均绝对误差
这里我们找到了绝对误差的平均值,它是一个量的推断值和它的实际值之间的差。多个量的和或差的绝对误差小于或等于它们的绝对误差之和。公式如下:
其中:
-
n =错误的数量
-
σ=求和符号(这意味着“将它们全部相加”)
-
| Xi–x | =绝对误差
下面是它在 Keras 中的实现:
keras.losses.mean_absolute_error(y_true, y_pred)
平均绝对百分比误差(MAPE)
当要预测的数量已知保持在零以上时,通常使用 MAPE。它是预测应用程序的理想选择,尤其是在有足够数据可用的情况下。
下面是它在 Keras 中的实现:
keras.losses.mean_absolute_percentage_error(y_true, y_pred)
均方对数误差(MSLE)
均方对数误差(MSLE)是真实值与预测值之比的度量。顾名思义,均方对数误差是均方误差的一种变体。
下面是它在 Keras 中的实现:
keras.losses.mean_squared_logarithmic_error(y_true, y_pred)
方形铰链
平方铰链损失用于“最大限度”二元分类。铰链损失保证了在训练期间,分类器将找到离每个不同类别的数据点尽可能远的分类边界。换句话说,它找到了保证不同类别的数据点之间的最大余量的分类边界。
下面是它在 Keras 中的实现:
keras.losses.squared_hinge(y_true, y_pred)
关键
它用于支持向量机,因此也被称为 SVM 损失 。铰链损耗简化了 SVM 所需的数学,从而在最大化误差的同时产生有效的结果。当您需要不太精确的实时决策时,它是理想的选择。铰链损失不仅不利于错误的预测,也不利于不自信的正确预测。
下面是它在 Keras 中的实现:
keras.losses.hinge(y_true, y_pred)
分类铰链
分类铰链损失也可以被优化,并因此用于在多类机器学习问题中生成决策边界。
下面是它在 Keras 中的实现:
keras.losses.categorical_hinge(y_true, y_pred)
对数余弦
Log cosh 是预测误差的双曲余弦的对数。这意味着它的工作方式很像均方差,但不会受到偶然的非常不正确的预测的强烈影响。
下面是它在 Keras 中的实现:
keras.losses.logcosh(y_true, y_pred)
胡伯损失
Huber 损耗结合了 MSE 和 MAE 的最佳特性。对于较小的误差,它是二次的,否则是线性的。这同样适用于它的梯度。
下面是它在 Keras 中的实现:
keras.losses.huber_loss(y_true, y_pred, delta=1.0)
范畴交叉熵
分类交叉熵是用于单标签分类的损失函数。这是指每个数据点仅适用一个类别。
下面是它在 Keras 中的实现:
keras.losses.categorical_crossentropy(y_true, y_pred, from_logits=False, label_smoothing=0)
稀疏分类交叉熵
稀疏分类交叉熵是一种损失函数,用于测量观察到的类别标签的分布和类别成员的预测概率之间的不相似性。分类指的是拥有两个以上类的可能性(而不是二进制,指的是两个类)。稀疏是指使用从零到类数减一的单个整数(如{ 0;1;或者 2 }对于三类问题的类标签),而不是类标签的密集的单热编码(例如{ 1,0,0;0,1,0;或者 0,0,1 }表示同一三类问题的类标签)。当类互斥时,使用稀疏分类交叉熵。
下面是它在 Keras 中的实现:
keras.losses.sparse_categorical_crossentropy(y_true, y_pred, from_logits=False, axis=-1)
二元交叉熵
二元交叉熵是一种损失函数,用于涉及是/否(二元)决策的问题。例如,在多标签问题中,一个示例可以同时属于多个类,模型试图为每个类确定该示例是否属于该类。
下面是它在 Keras 中的实现:
keras.losses.binary_crossentropy(y_true, y_pred, from_logits=False, label_smoothing=0)
库尔贝克-莱布勒散度
Kullback-Leibler 散度是两个概率向量之间差异的不对称度量。
下面是它在 Keras 中的实现:
keras.losses.kullback_leibler_divergence(y_true, y_pred)
泊松
泊松损失是在对计数数据建模时用于回归的损失函数。最小化泊松损失相当于在假设目标来自泊松分布的情况下最大化数据的似然性,以输入为条件。损失的形式有:
其中,ŷ是预测的期望值。
下面是它在 Keras 中的实现:
keras.losses.poisson(y_true, y_pred)
参考
本章中使用的参考资料如下:
-
www.computerworld.com/article/2591759/artificial-neural-networks.html
-
www.countbayesie.com/blog/2017/5/9/kullback-leibler-divergence-explained
-
https://searchenterpriseai.techtarget.com/definition/recurrent-neural-networks
-
https://towardsdatascience.com/attention-in-neural-networks-e66920838742
-
proceedings.mlr.press/v37/xuc15.pdf
-
https://papers.nips.cc/paper/6284-latent-attention-for-if-then-program-synthesis.pdf
-
www.sciencedirect.com/topics/engineering/deep-belief-network
-
https://towardsdatascience.com/10-gradient-descent-optimisation-algorithms-86989510b5e9
进一步阅读
有兴趣了解本章中涉及的一些主题吗?这里有一些很棒的链接可以查看:
-
自编码器:
http://ufldl.stanford.edu/tutorial/unsupervised/Autoencoders/
-
榆树:
-
RNNs:我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,
www.geeksforgeeks.org/introduction-to-recurrent-neural-network/
-
lstms 倍数:
-
注意力集中的 ann:
https://pathmind.com/wiki/attention-mechanism-memory-network
-
变压器:
-
降噪自编码器:
-
稀疏自编码器:
-
堆叠式自编码器:
-
卷积自编码器:
https://towardsdatascience.com/convolutional-autoencoders-for-image-noise-reduction-32fce9fc1763
-
堆叠去噪自编码器:
-
收缩自编码器:
https://deepai.org/machine-learning-glossary-and-terms/contractive-autoencoder
-
Hopfield 网络:
www.sciencedirect.com/topics/computer-science/hopfield-network
-
双向联想记忆:
https://uomustansiriyah.edu.iq/media/lectures/5/5_2017_02_28!06_30_52_PM.pdf
-
玻尔兹曼机:
http://artificialintelligence-notes.blogspot.com/2012/09/what-is-boltzmann-machine.html
-
受限玻尔兹曼机;
https://medium.com/datadriveninvestor/deep-learning-restricted-boltzmann-machine-b76241af7a92
-
深度信念网络:
-
反进化网络:
-
DCGIN:
https://medium.com/@Medmain/two-sides-of-the-same-network-efd3a0f3b425?source=rss-------1
-
液态机器:
-
回声状态网络:
https://towardsdatascience.com/gentle-introduction-to-echo-state-networks-af99e5373c68
-
深层剩余网络:
https://towardsdatascience.com/introduction-to-resnets-c0a830a288a4
-
神经图灵机;
-
胶囊神经网络:
https://towardsdatascience.com/capsule-networks-the-new-deep-learning-network-bd917e6818e8
-
LeNet-5:
http://yann.lecun.com/exdb/lenet/
-
GoogLeNet:
https://leonardoaraujosantos.gitbooks.io/artificial-inteligence/content/googlenet.html
-
Xception:
www.iitk.ac.in/esc101/05Aug/tutorial/essential/exceptions/definition.html
*
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器
2020-10-02 《线性代数》(同济版)——教科书中的耻辱柱