生成合成患者数据
快速浏览使用 Synthea
马库斯·斯皮斯克在 Unsplash 上的照片
使用医疗保健数据进行研究可能会很棘手,为了使用某些数据,可能会遇到许多法律和财务障碍。在处理具体患者的信息时更是如此。但是,这些障碍可以通过使用开源患者生成器 Synthea 创建的合成数据来避免。Synthea 创建了可以无限制使用的真实数据。最重要的是,它非常人性化。
在你开始创建你自己的病人之前,确保你有最新版本的 JDK (JDK 14)。如果你还没有它,你可以遵循这个安装指南。确保你安装的是 JDK 14 ,而不是 JRE 14 。
下一步是安装 Synthea。如果你只是想运行 Synthea,而没有太多的灵活性和可定制性,你可以使用这里的链接安装基本版本。或者,如果你想更自由地处理代码,你可以创建一个 Github 库的本地副本。我将使用基本版本,并将探索更扩展版本的任务留给您(按照这些说明)。
一旦安装了 Synthea,打开您的终端并切换到下载文件的目录。对我来说,那是“下载”文件夹。
cd /directory/with/downloaded/file
一旦进入目录,只需一个命令就可以运行 Synthea!你需要做的就是打开 JAR 文件。
java -jar synthea-with-dependencies.jar
您应该会看到终端中弹出一串文本。
终端输出
就这样,你成功地创造了一个人造病人;但是,这只是 Synthea 所能提供的表面现象。
添加参数
有几个不同的参数可用于定制您创建的患者。如需完整的参数列表,请查看合成维基。
下面我们来看看一些有用的。
-p size
—生成指定大小的群体(该大小指定群体中活着的成员的数量,因此总群体大小可能会更大)
-g gender
—生成一个性别的群体(M 代表男性,F 代表女性)
-a ageRange
—生成一个年龄范围内的人口(即 30-40 岁)
-m module
—生成患有特定疾病(如哮喘、皮炎、狼疮等)的人群。)完整的模块列表可在这里找到。
State City
—生成特定区域内的人口(如明尼苏达州、得克萨斯州、达拉斯等)。)城市可选。一次只能运行一个状态;如果没有指定,默认的州是马萨诸塞州。
--exporter.csv.export true
—将数据导出为 CSV 文件。默认格式是 FHIR。基于不同的数据,如“药物”、“就诊”、“提供者”等,患者信息被分割成多个 CSV 文件。您也可以从许多其他输出格式中进行选择。完整列表可在这里找到。
有了这些参数,我们可以生成一些非常有意义的数据。这些参数都可以组合,使用的参数越多,生成的数据就越具体。让我们看几个例子。
java -jar synthea-with-dependencies.jar -p 500 -a 20–50 Minnesota
这会生成一个居住在明尼苏达州的年龄在 20 到 50 岁之间的患者列表。该列表将有 500 个活着的患者和一些死亡的患者。
生成的患者输出示例
java -jar synthea-with-dependencies.jar -p 100 -g F -m Asthma --exporter.csv.export true
仅使用哮喘模块生成至少 100 名女性患者的列表,并将数据导出为 CSV 文件。
CSV 文件输出示例
有很多方法可以组合不同的参数来创建非常不同的数据集。我鼓励您进行试验,尝试不同的参数,看看您会得到什么。或者,如果您对基本模型感到满意,您可以安装完整存储库并探索 Synthea 提供的一切。
我希望您喜欢这个关于 Synthea 的简短介绍,也希望我向您展示了这个工具的易用性和强大功能。无论你是在做一个大规模的项目,还是在你的笔记本电脑上做实验,都有无限的可能性。
资源:
- https://docs . Oracle . com/javase/8/docs/technotes/guides/install/install _ overview . html # a 1097257
- https://synthetic health . github . io/synth ea/build/libs/synth ea-with-dependencies . jar
- 【https://github.com/synthetichealth/synthea
- https://github . com/synthetic health/synthetia/wiki/Developer-Setup-and-Running
- https://github.com/synthetichealth/synthea/wiki
- https://github . com/synthetic health/syntheea/wiki/Module-Gallery
- https://github . com/synthetic health/syntheea/wiki/CSV-File-Data-Dictionary
- https://github . com/synthetic health/synthetia/wiki/Common-Configuration
用 Python 生成合成地震图
从测井曲线生成地震记录的七个步骤
地球物理学中最基本的概念之一是卷积。我们在地球物理操作中记录的地震数据是来自地球内部表面的反射能量,这些表面与相邻层具有不同的岩石物理特性。实际上,地震信号是地层反射率与能量源子波褶积的结果。
从测井曲线生成合成地震记录是一个建模过程。我们将使用零相位小波(如 Ricker 小波)对反射率序列(从测井数据中获得)进行卷积。我们需要地震图来将油井数据与地震数据联系起来,以完成解释任务。
在本文中,我将尝试在合成地震图的制作过程中加入一些编程的乐趣。我非常喜欢 python,因为它有各种各样的库,可以使任务比其他编程平台容易得多。
我已经上传了一个完整的 jupyter 笔记本文件和原始数据到我的 GitHub 账号里,如果你想自己尝试的话。我们将用 las 格式处理日志数据。如果你不熟悉如何将 las 文件读入 python 的过程,请查看我之前的文章。
生成地震记录所需步骤:
1-数据准备(单位转换和声波/密度测井处理)
2-时深关系构建
3-声阻抗(AI 或 Z)计算
4-反射系数(Rc)计算
5-重采样到时域和 Rc 重新计算
6-子波&卷积
7-可视化
1-数据准备
调节装置
将测井文件(las 格式)读入 python 并查看日志标题后,我们需要将密度和声波测井的单位转换为 SI 系统,因为我们需要这两种测井来计算阻抗。为了计算声阻抗,我们需要 s/m 单位的声波测井(DT)和 kg/m3 单位的密度测井(RHOB)。看标题:DT 有美制/英尺单位和 RHOB g/cm3。
w.data['DT'] = w.data['DT'] / 0.3048 #unit convert to µs/m
w.data['RHOB'] = w.data['RHOB'] * 1000 #unit convert to kg/m3
去毛刺和平滑
尖峰信号在测井记录中很常见,尤其是在声波测量中。这些尖峰并不能真正代表整个井段的岩石特性变化,需要消除。
#Sonic Despiking
dt = w.data['DT']
w.data['DT_DS'] = dt.despike(window_length=50, z=2)#Density Despiking
den = w.data['RHOB']
w.data['RHOB_DS'] = den.despike(window_length=50, z=2)
我们将输入要平滑的去尖峰数据:
#Sonic Smoothing
dt_ds = w.data['DT_DS']
w.data['DT_DS_SM'] = dt_ds.smooth(window_length=10, samples=False)#Density Smoothing
den_ds = w.data['RHOB_DS']
w.data['RHOB_DS_SM'] = den_ds.smooth(window_length=10, samples=False)
2-时间-深度关系
地震数据是在时间域测量的,而井数据主要在深度域测量。要将时间转化为深度,反之亦然,我们需要建立一种关系。有几种方法可以做到这一点,但在这里,我们使用声波测井,这是声波速度的倒数。从数学上讲,如果我们在深度区间上对 DT 积分,我们将计算出时间与深度的关系。在此之前,我们应该对上部测井数据进行补救,它与地面有数据缺口。由于这是岸上现场数据,我们没有水柱。测井曲线以上部分的替换速度几乎为 2600 米/秒。简单地说,用替换速度除以间隙层厚度(测井曲线开始深度减去 kb)就可以得到时间。
log_start = 1517 # Depth of logging starts(m) from header
kb = 15 # Kelly Bushing elevation(m) from headergap_int = log_start - kb
repl_vel = 2632 # this is from VSP data knowledge (m/s)
log_start_time = 2.0 * gap_int / repl_vel # 2 for twt
T-D 机构
让我们实施一体化。由于深度采样间隔为 0.1524 米,我们需要用测得的声波数据计算该厚度传播所需的时间。然后计算向下的累计行程时间。从测井起始深度计算显示第一个值为零,而我们知道零时间属于地表。所以,这个间隙的传播时间应该加到综合声波时间中。
#first replace NaN values with zero
dt_iterval = np.nan_to_num(dt) * 0.1524 / 1e6
t_cum = np.cumsum(dt_iterval) * 2
w.data['TWT'] = t_cum + log_start_time
3-声阻抗
声阻抗(AI,有时用 Z 表示)定义为声波速度和介质密度的乘积。在反射地震学中,地质层之间的 AI 变化负责向我们收集地震数据的表面的能量反射。
df['Vsonic'] = 1e6/df.DT_DS_SM #(unit: m/s)
df['AI'] = df['Vsonic'] * df['RHOB_DS_SM'] #(unit: kg/m2.s)
4-反射系数
反射系数(Rc)定义为两个相邻层的阻抗差除以它们的总和。数学上,这是深度的导数。
Imp = df['AI'].values
Rc=[]
for i in range(len(Imp)-1):
Rc.append((Imp[i+1]-Imp[i])/(Imp[i]+Imp[i+1]))
5-重采样到时域和 Rc 计算
虽然我们建立了时深关系(TDR ),但合成地震记录所需的分量在深度域。反射率系列应该转换到时域进行卷积。定义时间向量后,我们可以使用 numpy 库中的插值函数。
dt = 0.001 #sampleing interval
t_max = 3.0 # max time to create time vector
t = np.arange(0, t_max, dt)
AI_tdom = np.interp(x=t, xp = df.TWT, fp = df.AI) #resampling# again Rc calulation but in reampled time domain
Rc_tdom = []
for i in range(len(AI_tdom)-1):
Rc_tdom.append((AI_tdom[i+1]-AI_tdom[i])/(AI_tdom[i]+AI_tdom[i+1]))
6-小波和卷积
在反射地震学中,假设我们在地表记录的地震数据是用子波卷积(掩蔽)的能量反射。在各种子波中,里克子波是最常见的一种,因为它是零相位的,有利于地震解释任务。
# define function of ricker wavelet
def ricker(f, length, dt):
t0 = np.arange(-length/2, (length-dt)/2, dt)
y = (1.0 - 2.0*(np.pi**2)*(f**2)*(t0**2)) * np.exp(-(np.pi**2)*(f**2)*(t0**2))
return t0, y
盘旋
在这一步中,我们将利用子波对反射率序列进行卷积,以创建一个合成地震图。这通常被称为翻转和滑动一个功能到另一个功能上。如果你有兴趣知道这个操作背后的数学原理,你可以看看这个伟大的视频。
f=20 #wavelet frequency
length=0.512 #Wavelet vector length
dt=dt # Sampling prefer to use smiliar to resampled AI
t0, w = ricker (f, length, dt) # ricker wavelet
synthetic = np.convolve(w, Rc_tdom, mode='same')
7-可视化
图 1
在该图中,我们将绘制测井曲线(覆盖平滑和去尖峰结果),并计算深度域中的 AI 和反射率。关于 python 中的绘图程序,请参考笔记本文件这里的。
图 2
在该图中,我们将在时域中绘制 AI 和卷积结果。我们还可以获得油井周围真实地面地震记录,这些记录可以与合成地震记录并置,以查看与数据的匹配程度。
查看最后一条轨迹表明,合成地震记录和地面地震数据之间存在适当的匹配,尤其是在关键层位。关于为什么匹配可以是相同的,有很多争论。一个重要的原因是地面地震和声波测井的频带。如果你有兴趣学习更多,请参考我的教授的书、劳伦斯·r·莱恩斯。
结论
在反射地震学中,合成地震记录是以褶积理论为基础的。地震图是地震解释的一个非常重要的工具,它是井和地面地震数据之间的桥梁。在这篇文章中,我试图展示我们如何用 python 中的真实数据用几行代码实现这个任务。请随意下载源代码,四处看看,并为自己的数据实现。对于评论和问题,请在这里留言。
使用 SQL 生成测试数据
数据工程
使用 SQL 支持应用程序测试和开发
开发人员在开发和测试应用程序时需要测试数据。测试数据对于应用程序开发至关重要,因为这是开发人员能够领先模仿 API 响应一步的唯一方法。生成测试数据涉及两种不同的方法。两者都应该用来使应用程序变得更好。
- 使用应用程序期望的数据进行测试(高质量的数据)
- 使用应用程序不期望的数据进行测试(质量差的数据)
质量差的数据通常由不支持的字符、较长的字符串、高精度浮点数等组成。了解坏数据通过系统时会发生什么总是很有帮助的。
你需要知道什么
用 SQL 生成数据非常容易。要编写测试数据生成查询,只需要了解一些事情
- cte 和递归 cte-公用表表达式是命名的结果集。假设查询的输出有一个表名。它的工作原理和熊猫的数据框架非常相似。
- rand(),md5()函数—
rand()
用于生成随机数,md5()
不用于加密数据。 - 基本字符串操作—基本字符串操作主要用于限制字段长度。
- 子查询—使用简单的子查询为给定行生成列值。请记住,本质上,这些既不是内联子查询,也不是相关子查询。
使用 cte
递归 cte 很棒。它们可用于生成系列数据。类似的事情可以在 PostgreSQL 中使用generate_series
,在 Oracle 中使用connect by level
,在 MySQL 中使用会话变量@id := @id + 1
来完成。几乎所有的数据库都有生成数字序列的变通方法。其中一些变通办法比其他的更好。
使用 MariaDB 的递归 cte。在 MySQL 中,您可以使用会话变量来模拟这种情况。在 Oracle 中,您可以使用“按级别连接”来实现这一点。
SQL 函数— rand()和 md5()
rand()
函数给你一个 0 到 1 之间的数字。如果我们想要一个介于 0 和 100 之间的数字,我们只需将rand()
函数的输出乘以 100。在下面分享的例子中,我正在生成一个介于 12 和 100 之间的数字。
生成一个介于 12 和 100 之间的数字,用于生成一个人的随机年龄数字
使用另一个 CTE 将城市列表用作表格。CTE 上的order by rand()
将从城市列表中随机选择一个城市。请注意两个后续查询的结果是不同的。
从城市列表中随机选择一个城市
类似地,还有两个 cte,一个用于常见的名,另一个用于常见的姓。随机选取的这两个字段的任意组合将生成一个新名称。显然,由于order by rand()
条款和提供给 CTE 的名称数量有限,名称可能会重复。
如果你不在乎名字看起来有多真实,你可以使用substring(md5(rand()),1,6)
生成随机的字符串。这会给你一个包含六个字符的随机字符串。
从名和姓的列表中生成随机名称
生成随机日期
一旦你想好了在给定范围内生成随机数,那么生成一个随机日期就很容易了。以下示例显示了如何生成 1919 年 1 月 1 日到 2019 年 12 月 28 日之间的随机日期。唯一的警告是,这涵盖了任何给定月份的 1 日至 28 日之间的天数。我们完全可以编写一个查询,通过添加一些条件来覆盖所有日期。
最后,我们来看一个仅由 SQL 查询生成的样本数据集。用自动生成的真实数据获取数十万条记录只需要几秒钟的执行时间。
下面是生成以下数据的脚本
样本人口统计数据集
不使用任何外部依赖项生成测试数据的 SQL 脚本
显然,下一步是将查询参数化,并使其成为存储函数或存储过程。您可以传递要生成的记录的数量、出生日期的范围等等。
快速修复
您可能会看到一个错误,如下图所示。请注意,我试图获取 100000 条记录—在开始时在递归 CTE 中定义— id < 100000
。
配置变量cte_max_recursion_depth
需要设置为 100000 或更大,以获取这些记录。
将会话变量 cte_max_recursion_depth 设置为 100001
获取 100K 条记录
在这之后,你应该可以走了。
用 LSTM 制作电视剧本
使用 LSTMs 的初学者项目
朋友们,我刚刚完成了这个项目,作为 Udacity 深度学习纳米学位的一部分。
该项目是关于预测脚本中的下一个单词,给定之前使用 LSTM 的上下文。
我会解释做这个项目时需要的主要和非常基本的概念。我介绍的部分包括:
如何将我们的数据转换成模型可以处理的形式?
- 预处理数据
- 批处理数据
模型架构如何处理批量数据,将数据从一层传递到另一层,以及它如何最终导致预测序列中的下一个单词。
目标受众和先决条件
这是 LSTM 完成的一个基础项目,任何对神经网络和 RNNs 有基础知识的人都可以很容易理解这一点。我试图尽可能简单地将项目分解到最基本的细节。
我在模型架构、输入形状、输出形状和每一层考虑的参数上投入了大量的精力。我已经编译并解释了这些输入/输出形状的含义,以及它们为什么以特定的方式成形,以便您更好地形象化。
注意:这个项目已经用 Pytorch 编码了。然而,LSTM 模型层考虑的参数没有太大的差别。所以,任何试图在 Keras 做这件事的人也可以参考我的解释。
我没有提到的是什么?
LSTM 是什么的理论细节,或者为什么它工作,虽然我已经触及了为什么在这种情况下它比神经网络更好的一部分。我附上了一个主项目的链接,但我没有逐行解释它的全部。
然而,我已经涵盖了我认为足以理解主要工作的部分,以便您可以将这个概念推广到其他类似的项目。
让我们直接跳进来吧🐬
文本预处理
我们有了文本,我们首先以一种可以输入 LSTM 的形式批量处理数据。
其中一个重要的部分是文本预处理,我们对文本应用了一些非常基本的必要但不充分的预处理:
- 用小写字母书写文本
- 将文本标记化:将较长的文本字符串分割成较小的片段或标记的步骤。
- 标点符号化,即替换“太好了!”由一个“太棒了”,“||感叹||”从而得出“太棒了!”和“伟大”不被认为是不同的词。
- 创建两个将单词映射到整数的查找表(因为神经网络只理解数字语言),反之亦然。
其他一些文本处理技术可能是词干化、词汇化。 这个 是文字处理上的好读物。
在对文本进行预处理并将其转换为整数后,文本数据以 LSTM 可以将其视为输入的方式进行批处理。
在进一步讨论之前,有一个非常基本但重要的问题:
为什么 RNN/LSTM 不是一个正常的神经网络?
RNN/ LSTMs 中最重要的两个术语是循环单元和隐藏单元。这两者都在考虑文本的上下文中起作用。例如,如果你加入了一个你很难交谈的讨论,你需要在你说话之前知道讨论的背景。这在隐藏单元的帮助下成为可能,隐藏单元从以前的上下文中携带信息,所以 LSTM/RNN 单元考虑 2 个输入:输入单词和隐藏单元。
正常的神经网络也可以工作,但它的工作方式不考虑前面的上下文,只给出一个单词作为下一个单词出现的概率,给定前面的单词。
输入批次:[什么,是,你,在想]目标:‘问’
在上面的图像中,“h”是隐藏单元,“o”表示每个 LSTM 单元的输出,最终输出与目标的相似性匹配。
批处理示例:
让我们考虑一段文字,这是 quora 的摘录:
”“你在想什么?”老板问道我们没有问任何人任何问题。通过问几个问题,我们无法评估任何人的技能。所以我们的测试是评估这个人的态度。我们保留了一些基于候选人行为的测试,并通过闭路电视观察每个人。"
在选择了合适的序列长度和批量大小(这里分别为 4 和 3)后, LSTM 单元格的输入如下所示(这里我忽略了标点符号):
第一批:
[什么,你在想什么]->[问道]
[是,你,在想,在问]->[美]
[你,思考,问,]–>[老板]
第二批:
[思考,被问,那个,老板]->[我们]
[问,那个,老板,我们]->[没]
【那个,老板,我们,没有】–>【问】
当然,转换成整数。
相反,如果我们使用一个普通的神经网络,批次看起来会类似这样(批次大小:3):
第一批:
[什么]->[是]
【是】–>【你】
【思考】–>【关于】
所以很明显,神经网络没有考虑之前的环境。
模型架构和工作:
下面介绍 LSTMs 如何处理这些批次。
注意:
下面是一张带有参数的模型结构图
Batch_size:2,sequence_len:3,embedding_dim:15,output_size=vocab_size:20,hidden_dim:10
请参考它以更好地理解架构,为了便于表示,我使用了较小的数字。
1.嵌入层
因此,如果这些整数(在 vocab_to_int 字典中映射到整数的单词)被传递到 LSTM 单元,它可能会发现将这些整数视为一些权重的有偏见的含义,为了避免这个问题,我们有时一次性编码这些整数。但是,当词汇表很大时,编码会占用很多不必要的空间。
假设 vocab 字典大小=3
向量占据 3*3 单元大小的空间,即 9
[1,0,0]
[0,1,0]
[0,0,1]
现在想象一下词汇量为 10000 的情况。这将意味着 10 ⁰细胞大小的空间。
因此就有了 嵌入层 的概念。
这一次,我们没有用一次性编码替换这些单词,而是用随机选择的数字填充一些固定长度的向量。随着时间的推移,这个值矩阵与模型一起接受训练,并学习单词的近似矢量表示,这有助于为单词添加意义,并有助于提高整体准确性。
torch.nn.Embedding 图层接受以下参数:
- num_embeddings (字典的大小),等于 vocab_size
- embedding_dim (用于单词表示的嵌入向量的大小)
该层的输入形状为 (batch_size,seq_length) ,该层的输出形状为 (batch_size,seq_length,embedding_dim)。
对于 25000 的 vocab 大小,300 的嵌入向量工作良好,这比在一个热码编码向量的情况下 25000 的每个向量大小小得多。
一些预先训练的单词表示模型如 fastText、GloVe 是可用的,并且可以直接使用,而不是用嵌入层生成这些表示。
2.LSTM 层
torch.nn.LSTM 需要参数 input_size,hidden_dim,num_layers 。Input _ size&hidden _ dim分别是输入和隐藏状态下的特征个数。
注:熟悉 CNN 的人可以把它比作 CNN 层的***【in _ channels】***和【hidden _ dim】out _ channels。
由于我们使用了嵌入层,嵌入向量长度就是输入特征的数量。因此,input _ size=embedding _ dim。参数 hidden_dim 和num _ layers(LSTM 的层数,一般取值为 1 或 2)可以任意选择。
nn。LSTM 层返回一个输出和一个隐藏状态,输出的形状为 (batch_size,sequence_length,hidden_dim)。
3.线性层
该输出被整形为到*(batch _ size * sequence _ length,hidden_dim)* 并传递到线性层,其中 in_features = hidden_dim 和out _ features = output _ size。
在我们的例子中, output_size =词汇表大小,即输出节点的数量等于词汇表中存在的单词总数,因此网络可以从整个单词词汇表中选择最可能的下一个单词。
来自线性层的输出被整形为*(batch _ size * sequence_length,output_size)* ,其被进一步整形为以具有形状 (batch_size,sequence _ length,output_size) ,并且尽管我们在整个语料库上训练模型来预测序列的每个 单词的下一个单词
因此,最终输出层的形状为 (batch_size,output_size) 。
我们的模型的输出为我们提供了给定批次的每个序列的最可能的下一个单词。💁
Batch_size:2,sequence_len:3,embedding_dim:15,output_size=vocab_size:20,hidden_dim:10(图片由作者提供)C
代码:
这个项目的完整代码可以在这里找到:
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/Ankita-Das/Deep-Learning-Nanodegree/blob/master/TV_script_generation/dlnd_tv_script_generation_copy.ipynb)
试试这个!💻
我已经试着涵盖了做这个项目的所有重要和必要的概念,但是如果你在任何地方遇到困难,如果你觉得我错过了一点,请在下面的评论中告诉我。
我将热切地等着看你完成这个项目。🙌
最美好的祝愿。
告别😁
乔纳森·肯珀在 Unsplash 上拍摄的照片
生成对抗网络
更简单直观的解释。
超分辨率。GAN 的一个应用(来源)。
让我们假设塞尔吉奥正在计划他的下一次大抢劫,他需要他的团队前往外国。由于他的工作性质,他决定制作假护照。他不想冒险,因为他相信护照官员可以辨别出真假护照。他决定利用他的设计师和专家网络来有效地实施他的计划。
Generative Adversarial Networks have two components a generator and a discriminator. Just take my words as of now :)Let's get back to Sergio's plan and problems.
塞尔吉奥的护照生成器
下图显示了 Sergio 的行动计划,其中利用专业人员的综合技能制作假护照。
塞尔吉奥的发电机网络(图片由作者使用 ML-Visuals )。
如果我们要概括塞尔吉奥的计划,我们可以说,图表是假护照的生成器。一群基本上了解护照安全特征的专业人士试图利用他们的知识复制一个类似的护照。换句话说,给定一个空间,目标是画出一个与原始空间相似的例子。
这正是 GAN 的发生器组件的作用。它基于真实世界的例子来观察它们的特征,然后创建一个与原始例子相似的新例子。
护照官员作为鉴别者
下图显示了护照官员的工作流程。他每天检查数百本护照,塞尔吉奥希望他生成的护照能够被识别为真护照。
护照官员的工作流程(图片由作者使用 ML-Visuals 制作)。
护照官员类似于 GAN 的第二个组件,称为鉴别器。鉴别器的工作是分类。它从伪造或生成的数据实例中识别出真实的数据实例。
在伪造的情况下,塞尔吉奥和护照官员是相互竞争的。塞尔吉奥希望他生成的护照被归类为真护照,而护照官员希望做好检测假护照的工作。这使他们成为对手,因此得名敌对网络。
现在我们已经了解了 GAN 的两个组成部分,让我们看看如何训练它们以获得 GAN。
把它放在一起
甄别训练
在 GAN 的情况下,鉴别器只是一个分类器,它将给定的实例标记为真或假。你可以使用任何适合你的问题陈述的神经网络。
当训练一个鉴别器时,它被馈送来自真实世界的例子作为正例,以及从生成器网络生成的例子作为负例。当训练鉴别器时,发生器没有被训练,也就是说它的权重没有改变。简单地说,发生器的输出作为负实例被馈送给鉴别器。如果鉴别器随后将负实例分类为正实例,或者反之亦然,则它被损失函数惩罚,并且它的权重通过反向传播来更新。
鉴别器培训(作者使用ML-visual制作的图片)。
发电机培训
我们已经看到,生成器的工作是输出新的数据实例。发电机只不过是一个神经网络。然而,神经网络有输入。在发生器的情况下,噪声作为输入被馈送给它们,并且最终它学会生成可感知的数据。对于任何生成的图像,如果鉴别器将其归类为假的,则生成器将受到惩罚。在训练生成器时,仅更新生成器的权重。
发电机培训(图片由作者使用ML-visual)。
在实践中,使用迭代方法来训练 GAN,在这种方法中,您可以在为一次迭代训练生成器和为下一次迭代训练鉴别器之间切换。
生成性对抗网络:构建一个用 Streamlit 给 B&W 照片着色的 web 应用程序
使用 Streamlit 快速将生成式对抗网络模型转换为 web 应用程序,并部署到 Heroku
黑白照片彩色化的 Web 应用程序。
介绍
之前我做了一个 GAN 模型,给黑白照片上色。它运行良好,所以我希望与人们分享。在 Jupyter notebook 中编码非常方便开发,但不方便共享,因此我决定制作一个 web 应用程序并部署到 Heroku。
利用 Fastai 的生成性对抗网络(GAN)使黑白照片变得丰富多彩
towardsdatascience.com](/colorize-black-and-white-photos-by-ai-cc607e164160)
我没有网页开发的经验和技能,所以我花了一些时间来弄清楚如何去做。我发现了一个有用的工具 Stremlit,它可以让我用 Python 编码,并快速将其转化为 web 应用程序。
我做这个 app 有几个步骤:
- 导出模型
- 下载模型
- 为用户创建照片上传工具
- 调整上传照片的大小
- 加载模型
- 分析上传的照片
- 显示结果
- 把它部署到 Heroku
导出模型
我使用 fastai 训练 GAN 模型,所以我只需使用
learner.export(export.pkl)
这会将模型导出为 pkl 文件。
下载模型
这个应用程序的第一件事是下载模型。因为模型尺寸太大(> 200MB),不方便推送到 Github 或者直接推送到 Heroku。因此,我需要应用程序从外部 url 下载文件,以确保模型准备就绪。
我将模型存储在我的 dropbox 账户中,并创建了一个可下载的链接。
EXTERNAL_DEPENDENCIES = {
"export_5.pkl": {
"url": "https://dl.dropboxusercontent.com/s/xxxxxxx/export_5.pkl?dl=0",
"size": 246698366}
}
然后我用 Streamlit 的演示代码(https://github.com/streamlit/streamlit)创建了一个带有进度条的下载功能
我可以调用函数并下载模型
for filename in EXTERNAL_DEPENDENCIES.keys():
download_file(filename)
为用户创建上传工具
我想有一个工具,允许用户上传他们的 B&W 照片进行分析。
Streamlit 提供了一个工具来实现它:st.file_uploader
uploaded_file = st.file_uploader("upload a black&white photo", type=['jpg','png','jpeg'])
然后我将它保存到一个临时位置
if uploaded_file is not None:
g = io.BytesIO(uploaded_file.read()) # BytesIO Object
temporary_location = "image/temp.jpg"
with open(temporary_location, 'wb') as out: # Open temporary file as bytes
out.write(g.read()) # Read bytes into file
# close file
out.close()
照片上传工具
调整上传照片的大小
由于计算时间和限制,如果上传的照片大于 800 像素,我想把它调整到 800 像素。我用 Opencv 写了一个简单的函数。
def resize_one(fn,img_size=800):
dest = 'image/image_bw/temp_bw.jpg'
# Load the image
img=cv2.imread(str(fn))
height,width = img.shape[0],img.shape[1]if max(width, height)>img_size:
if height > width:
width=width*(img_size/height)
height=img_size
img=cv2.resize(img,(int(width), int(height)))
elif height <= width:
height=height*(img_size/width)
width=img_size
img=cv2.resize(img,(int(width), int(height)))cv2.imwrite(str(dest),img)
加载模型
如前所述,我使用 fastai 训练模型,所以我使用 fastai 加载模型,我创建了一个函数来加载它。
def create_learner(path,file):
learn_gen=load_learner(path,file)
return learn_gen
分析上传的照片并显示结果
然后我需要一个函数来预测图像的颜色,并使用 st.image 显示图像,请注意 st.image 无法读取 fastai 生成的张量,所以我需要 image2np()将其转换为 Numpy 数组。
def predict_img(fn,learn_gen,img_width=640):
_,img,b=learn_gen.predict(open_image(fn))
img_np=image2np(img)
st.image(img_np,clamp=True,width=img_width)
把它们放在一起
现在我已经有了我需要的一切,让我们把它们放在主函数中。
首先,我从外部链接下载模型,并制作页面标题
def main():
for filename in EXTERNAL_DEPENDENCIES.keys():
download_file(filename) st.title("Black&White Photos Colorisation")
然后我制作照片上传器,调整上传照片的大小并显示它
uploaded_file = st.file_uploader("upload a black&white photo", type=['jpg','png','jpeg'])
if uploaded_file is not None:
g = io.BytesIO(uploaded_file.read()) # BytesIO Object
temporary_location = "image/temp.jpg"
with open(temporary_location, 'wb') as out: # Open temporary file as bytes
out.write(g.read()) # Read bytes into file
# close file
out.close() resize_one("image/temp.jpg",img_size=800)
st.image("image/temp.jpg",width=800)
然后,我制作一个按钮来调用创建模型和预测函数
start_analyse_file = st.button('Analyse uploaded file')
if start_analyse_file== True:
learn_gen=create_learner(path='',file='export_5.pkl')
predict_img("image/image_bw/temp_bw.jpg",learn_gen,img_width=800)
然后最后调用这个主函数
if __name__ == "__main__":
main()
web 应用程序已准备就绪。我可以通过这个命令在我的本地机器上测试它。
黑白照片彩色化的 Web 应用程序。
部署到 Heroku
部署到 Heroku 非常简单——只需按下它。
我还需要三个文件:requirements.txt、setup.sh 和 Procfile。
requirements.txt
它包含 Heroku 需要安装的软件包。就我而言:
scikit_image==0.17.2
numpy==1.17.2
opencv_python==4.1.1.26
streamlit==0.64.0
pandas==0.25.1
https://download.pytorch.org/whl/cpu/torch-1.6.0%2Bcpu-cp36-cp36m-linux_x86_64.whl
fastai
setup.sh
mkdir -p ~/.streamlit
echo "[server]
headless = true
port = $PORT
enableCORS = false
" > ~/.streamlit/config.toml
Procfile
web: sh setup.sh && streamlit run run.py
然后注册一个免费的 Heroku 账号,下载并安装 Heroku CLI。
登录 Heroku,会弹出一个窗口输入账号和密码
heroku login
然后创建一个 web 应用程序
heroku create yourapp
将显示类似这样的内容
Creating app… done, ⬢ yourapp
https://yourapp.herokuapp.com/ |
https://git.heroku.com/yourapp.git
然后把回购推给 Heroku
git add .
git commit -m "Enter your message here"
git push heroku master
它将开始下载和安装包,过一会儿,它就准备好了。
现在模型已经上线运行。但是…当我点击分析时,它崩溃了。这可能是由于 Heroku 自由层帐户的计算限制,它提供了 512MB 的内存,这是不够的。所以我不得不将照片的大小调整为 256 以使其运行,结果不是很令人满意,但至少有一些东西在运行。我觉得简单升级账号就能解决。
有兴趣可以试试。https://colorisation.herokuapp.com/
感谢阅读,随时给我任何建议和意见!
虚拟生成对抗网络(GAN)——循序渐进教程
理解、构建和训练具有防弹 Python 代码的 GANs 的终极初学者指南。
这篇文章介绍了你需要从生成性敌对网络中获得的一切。不需要预先了解 GANs。我们提供了如何在大型图像数据集上训练 GANs 并使用它们通过 Keras 生成新的名人脸的分步指南。
人工智能教父、脸书副总裁兼首席人工智能科学家 Yann LeCun 的“生成对抗网络——过去十年机器学习中最有趣的想法”。
Alex Iby 在 Unsplash 上拍摄的照片
虽然生成对抗网络(GAN)是一个源于博弈论的旧思想,但它们是由 Ian J. Goodfellow 和合著者在文章生成对抗网络中于 2014 年引入机器学习社区的。GAN 是如何工作的,它有什么用途?
甘斯可以创造出看起来像人脸照片的图像,尽管这些人脸并不属于任何真实的人。
阿甘创造的仿真人脸(来源)
我们在上一篇文章的中看到了如何使用可变自动编码器生成新的照片级逼真图像。我们的 VAE 是在著名的名人面孔数据集上接受训练的。
使用我们的 VAE 码(自创)的图像及其重建的例子
VAEs 通常会产生模糊和非真实感的人脸。这是建立生成性对抗网络的动机。
在本文中,我们将研究 GANs 如何提供一种完全不同的方法来生成与训练数据相似的数据。
概率游戏
生成新数据是一个概率游戏。当我们观察周围的世界并收集数据时,我们正在进行一项实验。一个简单的例子就是拍一张名人的脸。
这可以认为是一个概率实验,有一个未知的结果 X ,也叫随机变量。
如果实验重复多次,我们通常将随机变量 X 得到值小 x 的概率定义为小 x 发生的次数的分数。
例如,我们可以定义这张脸是著名歌手泰瑞斯的概率。
泰瑞斯·吉布森
这种实验的所有可能结果构建了所谓的样本空间,表示为ω(所有可能的名人面孔)。
因此,我们可以将概率视为一个函数,它获取一个结果,即来自样本空间(一张照片)的一个元素,并将该结果映射到一个非负实数,使得所有这些数字的总和等于 1。
我们也称此为概率分布函数 P(X) 。当我们知道样本空间(所有可能的名人面孔)和概率分布(每个面孔出现的概率)时,我们就有了实验的完整描述,我们可以对不确定性进行推理。
你可以通过下面的文章来更新你的概率知识。
数据科学家概率概念实用指南
towardsdatascience.com](/understanding-probability-finally-576d54dccdb5)
名人脸概率分布
生成新面孔可以用一个随机变量生成问题来表示。脸部由随机变量描述,通过其 RGB 值表示,展平成一个由 N 个数字组成的向量。
名人头像的高度为 218 像素,宽度为 178 像素,有 3 个颜色通道。因此每个向量是 116412 维的。
如果我们建立一个有 116412 ( N )个轴的空间,那么每个面都将是那个空间中的一个点。名人脸概率分布函数 P(X) 会将每个脸映射到一个非负实数,使得所有脸的所有这些数字的总和等于 1。
该空间的一些点很可能代表名人的脸,而对于其他一些人来说,这是极不可能的。
名人脸概率分布函数(自创)
GAN 通过在 N 维向量空间上生成遵循名人面部概率分布的新向量来生成新的名人面部。
简单地说, GAN 会产生一个关于特定概率分布的随机变量。
如何从复杂分布中生成随机变量?
N 维向量空间上的名人脸概率分布非常复杂,我们不知道如何直接生成复杂的随机变量。
图片来自像素点上的数码相机
幸运的是,我们可以用一个适用于均匀随机变量的函数来表示复杂的随机变量。这就是变换方法的思想。它首先产生 N 个不相关的均匀随机变量,这很容易。然后它对这个简单的随机变量应用一个非常复杂的函数!非常复杂的函数自然地被神经网络近似。训练后,网络将能够接受一个简单的 N 维均匀随机变量作为输入,并返回另一个 N 维随机变量,该变量将遵循我们的名人脸概率分布。这是生成性敌对网络背后的核心动机。
为什么是生成性对抗网络?
在变换神经网络的每次训练迭代中,我们可以将名人训练集中的人脸样本与生成的人脸样本进行比较。
理论上,我们将使用最大平均差异(MMD)方法比较真实分布和基于样本生成的分布。
这将给出分布匹配误差,该误差可用于通过反向传播来更新网络。这种直接方法实际上实现起来非常复杂。
GANs 不是直接比较真实分布和生成分布,而是解决真实样本和生成样本之间的无差别任务。
一个 GAN 有三个主要组件:一个用于生成新数据的生成器模型,一个用于对生成的数据是真实人脸还是虚假人脸进行分类的鉴别器模型,以及使它们相互对抗的对抗网络。
生成部分负责将 N 维均匀随机变量(噪声)作为输入,生成假面。生成器捕获概率 P(X) ,其中 X 为输入*。*
鉴别部分是一个简单的分类器,用于评估和区分生成的人脸和真正的名人人脸。鉴别器捕获条件概率 P(Y|X) ,其中 X 为输入, Y 为标签*。*
名人脸生成对抗网络(自创)
训练生成性对抗网络
生成网络被训练成最大化最终分类误差(真实数据和生成数据之间),而判别网络被训练成最小化最终分类误差。这就是对抗性网络概念的来源。
从博弈论的角度来看,当生成器产生遵循名人脸概率分布的样本,并且鉴别器以相等的概率预测假货或非假货时,就达到了均衡,就好像它只是抛硬币一样。
重要的是,两个网络在训练期间平等地学习,并且将 汇聚到一起。一种典型的情况是,当鉴别网络在识别假货方面变得更好时,导致生成网络被卡住。
在鉴别器训练过程中,我们忽略发生器损耗,只使用鉴别器损耗,这是对鉴别器将真实人脸误分类为假或将生成的人脸误分类为真实人脸的惩罚。生成器的权重通过反向传播来更新。发电机的重量没有更新。
在生成器训练时,我们使用生成器损耗,惩罚生成器未能骗过鉴别器,生成一张被鉴别器归类为假的脸。鉴别器在生成器训练期间被冻结,并且只有生成器的权重通过反向传播被更新。
这就是用 GANs 合成名人脸的魔术。收敛经常被认为是短暂的,而不是稳定的。当你把一切都做对了,GANs 会提供令人难以置信的结果,如下所示。
构建和训练 DCGAN 模型
在本节中,我们将介绍为名人面孔数据集创建、编译和训练 DCGAN 模型所需的所有步骤。深度卷积生成对抗网络(DCGANs)是使用卷积层的 gan。
鉴别器
鉴别器可以是任何图像分类器,甚至是决策树。我们使用一个卷积神经网络来代替,它有 4 层。每个块都包括一个卷积、批量归一化和另一个卷积,该卷积将图像缩小一倍,并进行另一个批量归一化。结果通过平均池,然后是返回单一输出概率的密集 sigmoid 层。
发电机
生成器获取潜在维度的噪声向量并生成图像。图像的形状应该与鉴别器输入的形状相同(spatial _ dimxspatial _ dim)。
发生器首先用密集层对噪声向量进行上采样,以便有足够的值来整形到第一个发生器块中。投影的目标是与鉴别器架构中的最后一个模块具有相同的维数。这相当于鉴频器最后一个卷积层中的 4 x 4 x 数量的滤波器,我们将在本文后面演示。
每个生成器模块应用去卷积来对图像进行上采样和批量归一化。我们使用 4 个解码器块和一个最终卷积层来获得一个 3D 张量,它表示一个具有 3 个通道的伪图像。
甘
通过在生成器顶部添加鉴别器来构建联合 DCGAN。
在编译完整设置之前,我们必须将鉴别器模型设置为不可训练。这将冻结其权重,并告知整个网络中唯一需要训练的部分是发电机。
尽管我们编译了鉴别器,但我们不需要编译生成器模型,因为我们不单独使用生成器。
这个顺序确保鉴别器在正确的时间被更新,并在必要时被冻结。因此,如果我们训练整个模型,它将只更新生成器,而当我们训练鉴别器时,它将只更新鉴别器。
发电机架构
DCGAN 架构
甘培训
现在是艰难而缓慢的部分:训练一个生成性的对抗网络。因为 GAN 由两个独立训练的网络组成,收敛很难识别。
图像由 024–657–834 在 Pixabay 上显示
下面的步骤来回执行,让甘斯处理其他棘手的生殖问题。
步骤 1 —从训练集中选择若干真实图像。
第二步 —生成一些假图像。这是通过采样随机噪声向量并使用发生器从它们创建图像来完成的。
步骤 3 —使用假图像和真实图像训练鉴别器一个或多个时期。这将通过将所有真实图像标记为 1 并将虚假图像标记为 0 来仅更新鉴别器的权重。
鉴别器训练模式(自行创建)-不更新生成器的权重。仅调整鉴别器的权重。真假数据都用。鉴别器学会检测假图像。
步骤 4 —生成另一批伪图像。
步骤 5 —仅使用伪图像训练一个或多个时期的完整 GAN 模型。这将通过将所有假图像标记为 1 来仅更新生成器的权重。
生成器训练模式(自行创建)-鉴别器权重未更新。仅调整发生器的权重。发电机学会愚弄鉴别器。
甘训练时生成器生成的假面
上面我们可以看到我们的甘表现很好。即使照片质量不如 CelebA 训练集中的照片质量好,生成的人脸看起来也很合理。这是因为我们在重新调整的 64x64 图像上训练我们的 GAN,这些图像变得比原始的 218x178 更小更模糊。
与甘的区别
与我们上一篇文章中的中的 variable auto encoder 生成的人脸相比,DCGAN 生成的人脸看起来足够生动,足以代表足够接近现实的有效人脸。
训练时由一个可变自动编码器生成的假脸(来源
与 vae 相比,gan 是典型的高级深度生成模型。尽管 vae 注定要在一个潜在的空间工作,他们训练起来更容易也更快。VAEs 可以被认为是半监督学习器,因为它们被训练来最小化再现某个图像的损失。另一方面,GAN 正在解决一个无监督的学习问题。
结论
在这篇文章中,我解释了生成敌对网络如何能够近似一大组图像的概率分布,并使用它来生成照片级的图像。
我提供了可以工作的 Python 代码,允许您构建和训练一个 GAN 来解决您自己的任务。
你可以通过谷歌开发者或 Joseph Rocca 的文章了解更多关于 GANs 的信息。变型自动编码器将在我下面的文章中进一步探讨。
[## 用于假人的可变自动编码器(VAEs)——循序渐进教程
DIY 实践指南与实践证明代码建设和培训与 Keras 的名人脸上的 VAEs。
towardsdatascience.com](/variational-autoencoders-vaes-for-dummies-step-by-step-tutorial-69e6d1c9d8e9)
感谢《走向数据科学》的 Amber Teng 的编辑评论。
感谢阅读。注意安全。好好呆着。
生成对抗网络
深度学习
用解读甘博弈
阿曼达·达尔比约恩在 Unsplash 上拍摄的照片
什么是生成性对抗网络?
由 Ian Goodfellow 和他的同事在 2014 年设计的 GANs 由两个神经网络组成,它们在零和游戏中一起训练,其中一个玩家的损失是另一个玩家的收益。
为了理解 GANs,我们需要熟悉生成模型和判别模型。
创成式模型 尝试使用训练集中的分布输出新的数据点。这些模型生成新的数据实例。这些模型捕捉联合概率 p(X,Y)
生成模型的类型 1。显式密度模型
2。隐式密度模型
显式密度模型定义了显式密度函数,而隐式密度模型定义了可直接生成数据的随机程序。
如果您有兴趣阅读更多关于生成模型的内容,请查看下面这个流行的 GitHub 知识库。
生成模型的集合,例如 GAN、Pytorch 中的和 Tensorflow。
github.com](https://github.com/wiseodd/generative-models)
另一方面,判别模型 捕捉条件概率 p(X/Y),它们区分不同的数据实例。
图片来源:谷歌开发者。根据知识共享署名 4.0 许可证进行许可。
生成模型解决困难的任务。与辨别模型相比,注意力细节的水平更高。简单地说,生成模型做更多的工作。生成模型试图尽可能接近真实的数据分布。
在上图中,我们可以看到判别模型试图分离 0 和 1 的数据空间。而生成模型非常接近 0 和 1 的数据空间。
现在你知道了生成模型和判别模型的基本定义,让我们来学习一下 gan。
鉴别器和发生器网络 GAN 游戏
JESHOOTS.COM在 Unsplash 上的照片
生成对抗网络是一种生成模型。它们并行生成完整的图像。GANs 由两个网络组成:鉴别器和发电机网络
图片来源:谷歌开发者。根据知识共享署名 4.0 许可证获得许可。
甘斯使用了一个可微函数。这通常是一个神经网络。我们称之为发电机网络。 本发电机网络采用随机输入。这些输入是噪声。这种噪声被提供给一个可微分函数,该函数将其转换和整形为一个可识别的结构。这可能是一个镜像,它高度依赖于可微分函数输入端的噪声。
对于各种噪声输入,我们可以生成许多图像。然而,生成器网络不会立即给出真实的图像。我们需要训练它。
我们如何训练这个发电机网络?可能和其他网络一样?其实没有!
生成器网络看到许多图像,并试图输出类似于相同概率分布的东西。这是怎么做到的?👀
鉴别器来了,常规的神经网络分类器。鉴别器引导我们的发电机网络。
为简单起见,我们称发电机网络的**输出图像为假图像。**发生器的输出,即假图像,作为输入提供给鉴别器。**鉴别器还从训练数据中看到所谓的真实图像。**鉴别器然后输出输入是真实图像的概率。所以 1 代表真实图像,0 代表虚假图像。与此同时,生成器还试图输出可能被鉴别器赋予概率为 1 的图像。
大多数机器学习模型试图通过优化参数来最小化一些成本函数。如果我们给 GANs 分配成本函数,我们可以说鉴别器的成本是发电机成本的负数,反之亦然。
因此,让我们通过假设鉴别器和生成器是两个参与者,a 是一个函数 f,来理解 GANs 是如何工作的。
发生器试图减少函数 f 的输出值,而鉴别器试图增加它。让我们假设这样做,直到我们达到一个平衡,在这个平衡中,发生器不能减少函数 f 的输出值,鉴别器也不能增加它。由于我们同时使用两种优化算法,一种用于发生器,另一种用于鉴别器,我们可能永远达不到平衡。亚当优化器是一个很好的选择。
简单来说,发生器和鉴别器竞争,发生器给鉴别器假数据。鉴别器也看到训练数据,预测接收到的图像是真的还是假的。
看看下面这个来自谷歌开发者机器学习速成班的例子。
生成器从不切实际的图像开始,很快学会了愚弄鉴别者。
图片来源:谷歌开发者。根据知识共享署名 4.0 许可证获得许可。
因此,随着时间的推移,生成器被训练来欺骗鉴别器,使其看起来像鉴别器看到的真实图像一样。
那么训练过程是怎样的呢?
在鉴别器的训练过程中,显示真实图像并使用计算鉴别器损失。它对来自生成器的真实和虚假图像进行分类,如果任何图像被错误分类,鉴别器损失惩罚鉴别器。通过反向传播,鉴别器更新其权重。
类似地,发生器被给予噪声输入以产生假图像。这些图像被提供给鉴别器,并且发生器损耗惩罚发生器产生被鉴别器网络分类为假的样本。权重通过从鉴别器到生成器的反向传播来更新。
需要注意的是,在鉴频器训练阶段,发生器必须保持不变。同样,鉴别器在发电机训练阶段保持不变。因此,GAN 训练以交替的方式进行。
甘
在本节中,我们将学习设计一个 GAN,它可以生成手写数字的新图像。我们将使用著名的 MNIST 数据集。拿过来。
鉴别器架构
鉴别器将会是一个典型的线性分类器。
我们将使用的激活函数是泄漏 ReLu 。
图片来自 PyTorch 文档。(开源)
为什么会漏 ReLu?我们应该使用一个泄漏的 ReLU 来允许梯度不受阻碍地通过图层回流。泄漏的 ReLU 类似于正常的 ReLU,除了对于负输入值有一个小的非零输出。
作者代码。
发电机架构
生成器使用潜在样本制作假图像。这些潜在样本是映射到伪图像的向量。一个潜在向量只是一个图像的压缩的特征级表示!
为了理解什么是潜在样本,考虑一个自动编码器。连接网络的编码器和解码器部分的输出由压缩表示组成,该压缩表示也可以称为潜在向量。
所有层的激活函数保持不变,除了我们将在输出中使用 Tanh 。
图片来自 PyTorch 文档。(开源)
为什么在输出端出现 Tanh?
已经发现,对于发电机输出的𝑡𝑎𝑛ℎtanh,发电机表现最佳,其将输出缩放到-1 和 1 之间,而不是 0 和 1。
作者代码。
缩放图像
我们希望生成器的输出与真实图像的像素值相当,后者是 0 到 1 之间的归一化值。因此,当我们训练鉴别器时,我们还必须缩放我们的实际输入图像,使像素值在-1 和 1 之间。
这将在培训阶段完成。
一般化
为了帮助鉴别器更好地归纳,标签从 1.0 减少到 0.9 。为此,我们将使用参数 smooth 如果是真的,那么我们应该平滑我们的标签。在 PyTorch 中,这看起来像:
labels = torch.ones(size) * 0.9
我们还利用了脱落层来避免过度拟合。
损失计算
鉴别器的目标是为真实图像输出 1,为虚假图像输出 0。另一方面,生成器希望制作与真实图像非常相似的假图像。
由此我们可以说如果***【D】***代表了对于鉴别器的损失,那么下面可以陈述: ***鉴别器的目标:***D(real _ images)= 1&D(fake _ images)= 0 目标生成器: D(real_images)=)
作者代码
我们将使用 BCEWithLogitsLoss ,它结合了一个 sigmoid 激活函数(我们希望鉴别器输出一个 0–1 的值,指示一个图像是真实的还是伪造的)和二进制交叉熵损失。
Binar 交叉熵损失方程。图片由作者提供。
培养
如前所述,Adam 是一个合适的优化器。
生成器接收一个向量 z 并输出假图像。鉴别器在真实图像的训练和由生成器产生的伪图像的训练之间交替。
鉴别器培训中涉及的步骤:
- 我们首先计算真实图像的损失
- 生成假图像
- 计算假图像的损失
- 添加真实和虚假图像的损失
- 执行反向传播并更新鉴别器的权重
发电机培训涉及的步骤:
- 生成假图像
- 计算带有反向标签的伪图像的损失
- 执行反向传播并更新生成器的权重。
作者代码。
培训损失
我们将绘制发生器和鉴频器损耗与历元数的关系图。
作者代码。
培训损失。图片由作者提供。
生成器生成的样本
开始时
作者代码。
图片由作者提供。
加班
作者代码。
图片由作者提供。
这样,生成器从有噪声的图像开始,并随着时间的推移而学习。
您也可以在我的 GitHub 个人资料上查看代码和自述文件。
了解 MNIST 实施的 GANs。
github.com](https://github.com/NvsYashwanth/MNIST-GAN)
结论
自从蒙特利尔大学的 Ian Goodfellow 和他的同事设计出 GANs 以来,它们就大受欢迎。申请的数量惊人。GAN 通过许多变体得到进一步改进,其中一些变体是循环 GAN、条件 GAN、渐进 GAN 等。要了解更多信息,请点击此链接。现在打开一个 Jupyter 笔记本,尝试实现你所学到的任何东西。
谢谢你。下一场见。
生成对抗网络
combreakpix abay
生成对抗网络或简称 GANs 是一种神经网络,可用于生成数据,而不是试图对其进行分类。虽然有点令人不安,以下网站提供了一个令人印象深刻的例子。
此人不存在
这个人不是 Existthispersondoesnotexist.com](https://thispersondoesnotexist.com/)
生成性对抗网络由两部分组成。一个学习生成似是而非的数据的生成器和一个学习区分生成器的假数据和真实数据的鉴别器。只要检测到虚假数据,鉴别器就会处罚生成器。
鉴别器和发生器的训练阶段是分开的。换句话说,生成器的权重保持固定,同时它为鉴别器提供训练样本,反之亦然。通常,我们交替训练鉴别器和生成器一个或多个时期。
鉴别器训练过程与任何其他神经网络的训练过程相当。鉴别器对来自发生器的真实样本和虚假数据进行分类。鉴别器损失函数惩罚将真实实例误分类为假实例或将假实例误分类为真实实例的鉴别器,并通过反向传播更新鉴别器的权重。
类似地,发生器产生样本,然后由鉴别器分类为假的或真的。然后将结果输入损失函数,该函数因生成器未能欺骗鉴别器而对其进行惩罚,并使用反向传播来修改生成器的权重。
随着生成器随着训练而改进,鉴别器的性能变得更差,因为鉴别器无法区分真假。如果生成器完全成功,那么鉴别器有 50%的准确性(不比随机机会好)。后者对 GAN 整体的收敛提出了真正的问题。如果 GAN 继续训练超过鉴别器给出完全随机反馈的点,那么发生器开始训练垃圾反馈,其自身的性能可能会受到影响。
Python 代码
让我们来看看如何用 Python 来实现一个生成式对抗网络。首先,我们导入以下库。
from keras.datasets import mnist
from keras.layers import Input, Dense, Reshape, Flatten, Dropout
from keras.layers import BatchNormalization, Activation
from keras.layers.advanced_activations import LeakyReLU
from keras.models import Sequential, Model
from keras.optimizers import Adam
import matplotlib.pyplot as plt
import sys
import numpy as np
我们将使用 MNIST 数据集,其中包含 28×28 的手写数字图像。我们用以下参数创建了一个名为GAN
的类。
class GAN():
def __init__(self):
self.image_rows = 28
self.image_cols = 28
self.channels = 1
self.image_shape = (self.image_rows, self.image_cols, self.channels)
self.input_dim = 100
optimizer = Adam(0.0002, 0.5)
self.discriminator = self.build_discriminator()
self.discriminator.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy'])
self.generator = self.build_generator()_in = Input(shape=(self.input_dim,))
image = self.generator(_in)self.discriminator.trainable = Falsevalidity = self.discriminator(image)self.combined = Model(_in, validity)
self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)
我们定义了发电机网络。
def build_generator(self):
model = Sequential()
model.add(Dense(256, input_dim=self.input_dim))
model.add(LeakyReLU(alpha=0.2))
model.add(BatchNormalization(momentum=0.8))
model.add(Dense(512))
model.add(LeakyReLU(alpha=0.2))
model.add(BatchNormalization(momentum=0.8))
model.add(Dense(1024))
model.add(LeakyReLU(alpha=0.2))
model.add(BatchNormalization(momentum=0.8))
model.add(Dense(np.prod(self.image_shape), activation='tanh'))
model.add(Reshape(self.image_shape))
model.summary()
noise = Input(shape=(self.input_dim,))
image = model(noise)
return Model(noise, image)
我们定义鉴别器网络。
def build_discriminator(self):
model = Sequential()
model.add(Flatten(input_shape=self.image_shape))
model.add(Dense(512))
model.add(LeakyReLU(alpha=0.2))
model.add(Dense(256))
model.add(LeakyReLU(alpha=0.2))
model.add(Dense(1, activation='sigmoid'))
model.summary()
image = Input(shape=self.image_shape)
validity = model(image)
return Model(image, validity)
接下来,我们定义一个函数来训练模型。我们首先对每个图像的像素进行归一化,使它们的范围从负到正。我们使用 Numpy 来创建随机噪声,它反过来被生成器用来产生假数据。除了已知为真实的样本之外,还对生成的数据训练鉴别器。最后,通过将输出与实际样本进行比较来计算发电机损耗。
def train(self, epochs, batch_size=128, sample_interval=50):
(X_train, _), (_, _) = mnist.load_data()
X_train = X_train / 127.5 - 1.
X_train = np.expand_dims(X_train, axis=3)
valid = np.ones((batch_size, 1))
fake = np.zeros((batch_size, 1))for epoch in range(epochs):
index = np.random.randint(0, X_train.shape[0], batch_size)
images = X_train[index]
noise = np.random.normal(0, 1, (batch_size, self.input_dim))
gen_images = self.generator.predict(noise)
d_loss_real = self.discriminator.train_on_batch(images, valid)
d_loss_fake = self.discriminator.train_on_batch(gen_images, fake)
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)noise = np.random.normal(0, 1, (batch_size, self.input_dim))
g_loss = self.combined.train_on_batch(noise, valid)print ("%d [Discriminator loss: %f, acc.: %.2f%%] [Generator loss: %f]" % (epoch, d_loss[0], 100*d_loss[1], g_loss))
if epoch % sample_interval == 0:
self.sample_images(epoch)
我们定期保存输出,以便在整个训练过程中评估模型的性能。
def sample_images(self, epoch):
r, c = 5, 5
noise = np.random.normal(0, 1, (r * c, self.input_dim))
gen_images = self.generator.predict(noise)
gen_images = 0.5 * gen_images + 0.5
fig, axs = plt.subplots(r, c)
count = 0
for i in range(r):
for j in range(c):
axs[i,j].imshow(gen_images[count, :,:,0], cmap='gray')
axs[i,j].axis('off')
count += 1
fig.savefig("images/%d.png" % epoch)
plt.close()
最后,我们创建 GAN 类的一个实例并训练该模型。
gan = GAN()
gan.train(epochs=100000, batch_size=128, sample_interval=10000)
最初,GAN 的输出只是随机噪声。
然而,到最后,输出开始看起来像手写数字。
生成对抗网络
一个温和的甘的介绍,为什么训练他们是有挑战性的,有什么主要的补救措施?
这些超现实的人脸图像不是真实的;它们是由英伟达风格的 GAN 制作的,可以控制图像的不同方面
生成对抗网络(又名 GANs)代表了深度学习领域最近最令人兴奋的创新之一。gan 最初是由蒙特利尔大学的 Ian Goodfellow 和 Yoshua Bengio 在 2014 年提出的,Yann LeCun 认为它们是*“过去 10 年中最有趣的想法”*。
GAN 是一个生成模型,其中两个神经网络在典型的博弈论场景中竞争。第一个神经网络是生成器,负责生成类似于你的训练数据的新合成数据实例,而它的对手鉴别器试图区分生成器生成的真实(训练)和虚假(人工生成)样本。生成器的任务是试图愚弄鉴别器,鉴别器试图抵制被愚弄。这就是为什么整个系统被描述为对抗的原因。
如下图所示,生成器的输入只是一个随机噪声,而只有鉴别器可以访问用于分类目的的训练数据。该发生器仅基于鉴别器网络的反馈(在与训练数据匹配的情况下为正,在不匹配的情况下为负)来不断改进其输出。
形式上,GANs 是基于零和(极小最大)非合作博弈,一方赢对方输。当第一个玩家试图最大化其行动时,第二个玩家的行动旨在最小化它们。从博弈论的角度来看,当鉴别器和发生器达到众所周知的纳什均衡时,GAN 模型收敛,该均衡是上述极小极大博弈的最佳点。由于两个参与者试图误导对方,当其中一个参与者不管对手做什么都不改变其行动时,纳什均衡就发生了。
因此,众所周知,gan 在实践中很难训练,存在严重的不收敛问题,在生成器开始产生接近真实数据的假数据之前需要一段时间。
共同面临的挑战
GANs 有许多常见的故障模式,在训练时可能会出现。在这些失败中,三大挑战问题是世界各地几个研究小组的工作重点。虽然这些问题都没有完全解决,但我们会提到一些人们已经尝试过的事情。
1。模式崩溃
在训练 GANs 时,目标是产生各种各样的模拟真实数据的假数据*(即符合相同的分布)。从一个随机输入,我们想创造一个完全不同的新输出(例如一个新的现实的人脸)*。然而,当发生器发现一个或一个有限多样性的样本而不考虑输入时,这对于鉴别器来说似乎是最合理的,发生器可以 合法地 学习只产生那个输出。即使它起初看起来是训练进展的良好迹象,但当训练被称为 模式崩溃 或 helvetica 场景 的 gan 时,这是最具挑战性的失败之一。一旦鉴别器陷入局部最小值并且不能区分真实输入和发电机的输出,这种情况就可能发生。此时,生成器将很容易注意到这个黑洞,并不断生成相同的输出,或者最多是略有不同的输出。
尝试补救
- 使用不同的损失函数,例如 Wasserstein 损失,让你训练鉴别器达到最优,而不用担心消失梯度。如果鉴别器没有陷入局部最小值,它会学习拒绝发电机稳定的输出。所以发电机必须尝试新的东西。
- 展开的 GANs 使用发电机损耗函数,该函数不仅包含当前鉴别器的分类,还包含未来鉴别器版本的输出。所以生成器不能对单个鉴别器进行过度优化。
- 用不同的数据样本训练 GANs。
2.不收敛
GANs 经常无法收敛。通过假设两个神经网络相互竞争,目标是两个网络最终都将达到平衡,对抗性训练场景可能很容易看起来不稳定。如果不深入了解如何规避这种风险,这可能被认为是一个天真的假设,因为无法保证竞争梯度更新将导致收敛而不是随机振荡。
尝试补救
- 一个简单的技巧是将噪声添加到鉴别器输入(真实和合成数据)中,以阻止它对其分类过于自信,或者依靠一组有限的特征来区分训练数据和生成器的输出。
- 在与上一个技巧相同的方向上,我们可以尝试使用 NIPS’2017 中提出的 两个时标更新规则 ,其中作者提供了收敛到纳什均衡的数学证明。为鉴别器选择比生成器更高的学习速率。因此,我们的生成器将比鉴别器有更多的训练迭代和更多的训练时间。很容易理解,训练一个分类器比训练一个生成模型要容易得多。
- 惩罚歧视者权重:参见,例如,通过正则化稳定生成性对抗网络的训练。
3.消失渐变
研究表明,如果你的鉴别器太好,那么发电机训练可能会因梯度消失而失败。实际上,最佳鉴别器不能为生成器提供足够的信息来取得进展。当我们应用反向传播时,我们使用微分的链式法则,它具有乘法效应。因此,梯度反向流动,从最后一层到第一层。当它向后流动时,它变得越来越小。有时,梯度太小,以至于初始层学习非常慢或者完全停止学习。在这种情况下,梯度根本不改变初始层的权重值,因此网络中初始层的训练被有效地停止。这就是所谓的消失渐变问题。
尝试补救
- 我们可以使用激活功能,例如 ReLU 、 LeakyReLU ,而不是 sigmoid 或 tanh 分别在 0 和 1 或-1 和 1 之间挤压输入值,导致梯度呈指数下降。
- Wasserstein 损失旨在防止梯度消失,即使您将鉴别器训练到最佳状态。
- 原甘论文提出了一种改进的极大极小损失来处理消失梯度。
了解你的作者
拉贝赫·阿亚里是一名高级数据科学家,致力于信用风险建模和欺诈分析的应用人工智能问题,并在机器学习方面进行原创研究。我的专业领域包括使用深度神经网络的数据分析、机器学习、数据可视化、特征工程、线性/非线性回归、分类、离散优化、运筹学、进化算法和线性编程。欢迎随时给我留言这里!
草图到彩色图像生成| GANs
GANS 系列
2-学习使用条件 GANs 构建草图到颜色图像生成模型
150 个纪元后发电机模型的输出(Gif 由作者制作)
本文是我在 towards data sciences on media 上发表的Gans-Series的一部分。如果您不知道什么是赶工,或者您对赶工有一个想法,但希望很快再复习一遍,我强烈建议您阅读 之前的文章,这只是一篇 7 分钟的阅读,为刚接触这一令人惊叹的深度学习领域的人提供了对赶工的简单理解。
如您从上面显示的 gif 中所知,本文将学习如何创建条件 GAN,以便在不了解实际情况的情况下,根据给定的黑白草图输入预测彩色图像。
在进入编码模式之前,一些需要知道的事情…
草图到彩色图像生成是一种使用条件生成对抗网络的图像到图像翻译模型,如 Phillip Isola、朱俊彦、周廷辉、Alexei A. Efros 2016、等在原论文中所述。**
当我第一次看到这篇论文时,看到作者展示的如此伟大的成果是令人惊讶的,基本的想法本身也是令人惊讶的。
应用程序
作者在原论文中描述了许多条件遗传算法的应用场景。部分选择如下。
- 航空照片地图,航空照片地图
- 拍照城市风景
- 照片的建筑立面标签
- 白天到晚上的照片
- 照片修复
- 彩色图像草图 (我们将在本文中构建的那个)
图片来自 Phillip Isola,朱俊彦,周廷辉,Alexei A. Efros 2016,条件敌对网络下的图像间翻译
我们将要建立一个条件生成对抗网络,它接受一个 256x256 px 的黑白草图图像,并在不知道基本事实的情况下预测该图像的彩色版本。该模型将在 Kaggle 上可用的 动漫素描着色对数据集 上进行训练,该数据集包含 14.2k 对素描着色动漫图像。
当我在我的系统上训练模型时,我在单个 GeForce GTX 1060 6GB 显卡和 16 GB RAM 上运行了 150 个 epochs,耗时约 23 小时。经过所有的努力和耐心,结果是完全值得的!
经过训练的生成器模型的输出(图片由作者提供)
现在让我们进入有趣的部分…
为了构建这个模型,我使用了 TensorFlow 2.x,大部分代码都是基于他们关于 pix 2 pix forCMP Facade Dataset的精彩教程,该教程从 Facade 标签预测建筑照片。TensorFlow 教程是理解框架和从事一些知名项目的好方法。我强烈推荐你浏览网站上的所有教程—https://www.tensorflow.org/tutorials。
要求
要构建这个模型,您需要在您的系统上安装一些基本要求,以便它能够正常工作。
- Python 3.6及以上
- pip 19.0或更新
- Windows 7 或更高版本(64 位)
- 适用于 Visual Studio 2015、2017 和 2019 的 Microsoft Visual C++可再发行版
- tensor flow 2.2及以上
- GPU 支持 需要支持 CUDA 的卡
如果你计划使用任何像 Google Colab 这样的云环境,你需要记住训练将会花费很多时间,因为 GANs 运行起来计算量很大。Google Colab 有 12 个小时的绝对超时,这意味着笔记本内核被重置,所以你需要考虑一些要点,如安装 Google Drive 并定期保存检查点,以便你可以从超时前停止的地方继续训练。
下载数据集
下载 Kaggle 上的 动漫素描上色对数据集 并保存到文件夹目录。根文件夹将包含文件夹colorgram
、train
和val
。为了方便起见,我们将根文件夹的路径称为path/to/dataset/
。
一旦检查了基本要求,并且数据集被下载到您的机器上,就该开始编写您自己的条件 GAN 了。
在我们开始之前,请注意,如果你想理解它背后的基本工作原理,我将要提供的代码不应该只是从这里复制和粘贴。不要犹豫提出你的问题,因为这是学习的方式——通过提问。
最后是代码!
首先,让我们初始化参数来配置模型的训练。如前所述,我们将使用 TensorFlow 框架,因此我们需要通过使用import tensorflow as tf
来导入它。
os
模块用于与操作系统交互。我们将使用它来访问和修改路径变量,以便在训练期间保存检查点。time
模块让我们显示相对时间,因此,我们可以检查每个历元在训练中花费了多少时间。
matplotlib
是另一个很酷的 python 库,我们将用它来绘制和显示图像。
BUFFER_SIZE
用于我们在训练时混洗数据样本。这个值越高,洗牌的程度就越大,因此,模型的准确性就越高。但是对于大数据,打乱图像需要很大的处理能力。对于采用英特尔酷睿 i7–8750h CPU 和 16 GB 内存的系统,可以将其设置为等于训练数据集样本的大小,即 14,224。
N 注:
shuffle()
的最高效率是当你设置 buffer_size 等于数据样本的大小时。通过这种方式,它获取主存储器中的所有样本[在本例中为 14,224 个],并从中随机选择一个。如果你把它设置为 10,它会从内存中取出 10 个样本,从这 10 个样本中随机选择一个,然后对其余的样本重复这个过程。所以,检查你的机器性能,找出最佳点。
BATCH_SIZE
用于将数据集分成小批量进行训练。该值越高,训练过程越快。但是您可能已经猜到了,更大的批量意味着机器上更高的负载。
现在,如果您看一下数据集,您有一个大小为 1024x512 px 的单个图像,其中左侧有一个大小为 512x512 px 的彩色图像,右侧有一个大小为 512x512 px 的黑白草图图像。
可视化数据(来自 动画草图-着色对数据集 的图像)
我们将定义一个函数load()
,它将图像路径作为一个参数,并返回一个input_image
,它是我们将作为模型输入的黑白草图,以及一个 real_image
,它是我们想要的彩色图像。
预处理
现在我们已经加载了数据,我们需要做一些预处理,以便为模型准备数据。
下面给出了几个用于此目的的简单函数。
resize()
功能用于返回 286x286 像素的图像。这样做是为了在数据集中偶然出现不同大小的图像时具有统一的图像大小。将大小从 512x512 px 减小到一半也有助于加速模型训练,因为它的计算量较小。
random_crop()
函数返回所需尺寸为 256x256 px 的裁剪输入和真实图像。
normalize()
函数,顾名思义,将图像归一化为[-1,1]。
在上面显示的random_jitter()
函数中,所有之前的预处理函数被放在一起,随机图像被水平翻转。您可以从下面给出的图像中看到数据预处理的结果。
预处理图像(作者提供的图像)
加载训练和测试数据
load_image_train()
function 用于将之前看到的所有函数放在一起,输出最终的预处理图像。
tf.data.Dataset.list_files()
收集数据集的train/
文件夹中所有可用 png 文件的路径。然后映射这些路径的集合,每个路径作为参数单独发送给load_image_train()
函数,该函数返回最终的预处理图像并将其添加到train_dataset
。
最后,使用BUFFER_SIZE
对这个train_dataset
进行洗牌,然后如前所述分成小批量。
为了加载测试数据集,我们将使用一个类似的过程,除了一个小的变化。这里我们将省略random_crop()
和random_jitter()
函数,因为不需要这样做来测试结果。同样,出于同样的原因,我们可以省略对数据集的混洗。
构建发电机模型
现在让我们构建生成器模型,它采用 256x256 px 的输入黑白草图图像,并输出有望类似于训练数据集中的彩色地面真实图像的图像。
生成器模型是一个 UNet 体系结构模型,并且跳过了与中间层以外的其他层的连接。请注意,由于输出和输入形状需要与连接的层相匹配,因此设计这样的架构会变得很复杂,所以要小心设计。
下采样叠层具有卷积层,这导致输入图像的尺寸减小。一旦缩小的图像通过具有某种“反向”卷积层的上采样堆栈,大小就会恢复到 256x256 像素。因此,发生器模型的输出是具有 3 个输出通道的 256x256 px 图像。
你可以看看下面给出的模型概要。
发电机模型的模型摘要(图片由作者提供)
建立鉴别器模型
鉴别器模型的主要目的是找出哪个图像来自实际训练数据集,哪个图像是生成器模型的输出。
可以看看下面给出的鉴频器的型号总结。这不像生成器模型那样复杂,因为它的基本任务只是对真实和虚假的图像进行分类。
鉴别器模型摘要(图片由作者提供)
模型的损失函数
由于我们有两个模型,我们需要两个不同的损失函数来独立计算它们的损失。
发生器的损耗通过寻找发生器输出的 sigmoid 交叉熵损耗和 1 的阵列来计算。这意味着我们正在训练它欺骗鉴别器输出值为 1,这意味着它是一个真实的图像。此外,为了使输出在结构上与目标图像相似,我们还考虑了 L1 损耗。 原论文 的作者建议LAMBDA
的值保持为 100。
对于鉴别器损失,我们取真实图像和 1 阵列的相同 sigmoid 交叉熵损失,并将其与发生器模型和 0 阵列的输出图像的交叉熵损失相加。
优化者
优化器是用来改变神经网络属性的算法或方法,如权重和学习率,以减少损失。在大多数用例中,Adam Optimizer 是最好使用的优化器之一。
创建检查点
如前所述,云环境有一个特定的超时,它会中断训练过程。此外,如果您使用本地系统,可能会出现由于某些原因培训中断的情况。
gan 需要很长的训练时间,并且计算量很大。因此,最好定期保存检查点,这样您就可以恢复到最新的检查点,并从那里继续,而不会丢失您的机器之前所做的艰苦工作。
显示输出图像
上面给出的代码块是一个基本的 python 函数,它使用来自matplotlib
库的pyplot
模块来显示生成器模型预测的图像。
通过未训练的生成器模型显示预测图像(作者提供的图像)
记录损失
您可以将损失等重要指标记录在文件中,以便在 Tensorboard 等工具上进行训练时进行分析。
训练步骤
一个基本的训练步骤将包括以下过程:
- 生成器输出预测
- 鉴频器模型设计为一次有 2 个输入。第一次给出了输入的草图图像和生成的图像。下一次给它真实的目标图像和生成的图像。
- 现在,发电机损耗和鉴频器损耗计算完毕。
- 然后,根据损失计算梯度,并将其应用于优化器,以帮助生成器生成更好的图像,并帮助鉴别器以更好的洞察力检测真实的和生成的图像。
- 使用之前使用
tf.summary
定义的summary_writer
记录所有损失。
Model.fit()
TensorFlow 是一个非常棒的、易于使用的模型训练框架。像model.fit()
这样的小命令就能为我们带来奇迹。
不幸的是,它在这里不能直接工作,因为我们已经创建了两个协同工作的模型。但是这也很容易做到。
在这里,我们对每个时期进行迭代,并将相对时间分配给start
变量。然后我们展示了一个由生成器模型生成的图像的例子。这个例子帮助我们直观地看到生成器如何在每个时期更好地生成颜色更好的图像。然后我们调用模型的train_step
函数,从计算的损耗和梯度中学习。最后,我们检查纪元编号是否能被 5 整除,以节省检查点。这意味着我们每完成 5 个训练周期就保存一个检查点。在整个时期完成后,从最终相对时间中减去开始时间,以计算该特定时期所用的时间。
啊!终于,我们到了…
我们现在要做的就是运行这一行代码,然后等待模型自己施展魔法。好吧,我们不要完全相信这个模型,我们已经做了很多艰苦的工作,现在是时候看看结果了。
恢复最新的检查点
在继续之前,我们必须恢复可用的最新检查点,以便在对映像进行测试之前加载最新版本的已定型模型。
测试输出
这从test_dataset
中随机选择 5 幅图像,并将它们分别输入到发生器模型中。现在该模型被训练得足够好,并且预测输入草图图像的接近完美的彩色版本。
经过训练的生成器模型后的黑白草图输出(图片由作者提供)
保存模型
我们不要在做了这么多工作之后就把模型给毁了,好吗?
一个模特不应该在笔记本里结束自己的生命!
—丹尼尔·伯克说得对
只需一行代码就可以将整个模型保存为 Keras 模型支持的. H5 文件。
结论
原来如此,原来如此!
我们不仅看到了条件 GAN 是如何工作的,而且还成功地实现了它来从给定的黑白输入草图图像预测彩色图像。
你可以从我的 GitHub 库 中浏览全部代码并下载下来,看看它在你的系统上是如何工作的。
草图到彩色图像的生成是一个图像到图像的翻译模型,使用条件生成的对抗…
tejasmorkar.github.io](https://tejasmorkar.github.io/sketch-to-color/)
如果您面临任何问题,想要提出一些改进建议,或者只是想留下一个快速反馈,请不要犹豫,通过任何最适合您的媒体与我联系。
我的联系信息:
LinkedIn:https://www.linkedin.com/in/tejasmorkar/ GitHub:https://github.com/tejasmorkar
Twitter:https://twitter.com/TejasMorkar**
生成性对抗网络
GANs 系列
1 —以更简单的方式理解 GANs
在谈到甘斯时,脸书首席人工智能科学家、ACM 图灵奖获得者扬·勒村(Yann LeCun)曾公开引用说,对抗性训练是,
“过去 10 年中最有趣的想法”
gan 是 ML 领域中相对较新的发明。它是由伊恩·古德菲勒等人在 2014 年通过这篇令人惊叹的研究论文介绍的。
那么,甘斯到底有什么了不起的?
我们先来单独看一下术语— Generative Adversarial Network
生成性是什么意思?
深度学习可以分为两种类型的模型目标,它们是
- 判别模型
这些模型用于从给定的输入数据中映射出一个可能的输出。在这个领域中,你能想到的最常见的例子是分类器。他们的目标是简单地识别一类输入数据,如“垃圾邮件或非垃圾邮件”,或者像手写字符识别等。
这些模型捕捉条件概率 P(y|x),即‘给定 x 的 y 的概率’。 - 生成模型
这些模型用于寻找数据集的概率分布,并生成类似结构的数据。
生成模型主要是为了从给定的数据概率分布中找到密度函数。如下图所示,这些点表示数据在一维轴上的分布,该轴由右侧图像中的高斯密度拟合。
密度估计 —作者从 NIPS 2016 教程复制的图像:生成对抗网络
GANs 并不专注于精确地找到这个密度函数,而是观察给定的数据集,并在两个互为对手的模型的帮助下,生成符合给定数据样本中潜在结构的新样本。因此得名—
GANs 的工作
GANs 由两种模型组成,即:
- 生成器
其功能是获取输入噪声向量(z)并将其映射到一个图像,该图像有望类似于训练数据集中的图像。 - 鉴别器
鉴别器模型的主要目的是找出哪个图像来自实际训练数据集,哪个是生成器模型的输出。
由发生器和鉴别器模型组成的 GANs 的基本结构(图片由作者提供)
你可以把生成器模型想象成伪币制造者,他们想要生成假币并愚弄所有人相信它是真的,而鉴别器模型是警察,他们想要识别假币并抓住伪币制造者。
开始时,伪造者制造出完全不像真货币的随机货币。在被警察抓住后,他们从错误中吸取教训(在我们的案例中,模型丢失了),并创造出比以前更好的新货币。
假币与真币不相似的例子(图片由作者提供)
这样,警察就能更好地辨别假币和真币,同时,伪造者也能更好地制造看起来和真币相似的货币。
造假者在制造假币方面训练有素的时间点(图片由作者提供)
这是两个模型之间的最小-最大 2 人游戏,其中发电机模型试图最小化其损耗,最大化鉴别器损耗。
因此,生成器模型将输入向量(z)映射到与训练数据集中的数据相似的输出。
最后,当鉴频器不再能识别假输出时,它的准确度约为 50%。这意味着它现在是在进行随机猜测,以区分假数据和真实数据。这就是所谓的纳什均衡点。
注意:在实践中,很难达到这个平衡点,因此会产生一些问题。其中之一是模式崩溃问题,发生器在训练的早期阶段产生一个非常好的输出,然后更频繁地使用它,因为鉴别器还不能将其归类为假的。结果,生成器仅学习输出其中具有很少多样性的数据。有各种方法可以克服这一点,例如使用不同的损失函数,如 Wasserstein 损失 。
甘斯能做什么?
GANs 的主要目标是从给定的数据集生成新的样本。自从 GANs 发明以来,他们已经发展到可以用更好的结果和更多的特性来完成这个任务。
Ian J. Goodfellow 等人 2014 年,生成对抗网络
上图显示的是 Ian Goodfellow 等人在 2014 年发表的第一篇 GANs 论文的输出结果。集合 a)包含在手写数字的 MNIST 数据集上生成的输出,集合 b)示出了 Toronto Face 数据集的结果,集合 c)具有来自 CIFAR-10 数据集上的全连接模型的输出,集合 d)包含由 CIFAR-10 数据集上的卷积鉴别器和“去卷积”生成器模型生成的输出。
Progressive GAN 于 2017 年推出,作者表明,通过在开始时训练生成器输出低分辨率图像,并随着训练的进行提高分辨率,可以显著提高生成图像的质量。通过这种方式,他们能够从 CelebA HQ 数据集上训练的生成器中生成 1024x1024 分辨率的高质量人脸图像。
Tero Karras,Timo Aila,Samuli Laine,Jaakko Lehtinen 2017,为提高质量、稳定性和变化性而逐步种植 GANs】
关于DC GAN的论文是最重要的研究之一,它表明在 GAN 模型中使用深度卷积有助于产生更好的结果。他们没有使用任何有助于提高效率的完全连接和池化层。
本文中有趣的一点是向量空间算法,它显示输出结果可以根据简单的算术等式进行更改,如下图所示。
亚历克·拉德福德等 2015,深度卷积生成对抗网络的无监督表示学习
Cycle-gan能够将输入类映射到期望的输出集。训练是在两组数据上进行的,不需要其他标签。该模型学习将一组图像转换成另一组图像。
在下面的图像中,你可以看到马的图像是如何转换成斑马的图像的。这里值得注意的一点是,该模型了解到,相对于斑马,马主要与更绿的草原相关联。因此,该模型的结果是,斑马的输出背景较暗。当我们试图将斑马图像转换为马时,这也会产生更绿的马。
朱俊彦,朴泰星,菲利普·伊索拉,阿列克谢·埃夫罗斯 2017,使用循环一致对抗网络的不成对图像到图像翻译
*最后,最有趣的 GAN 变体之一是 **StarGAN。*它获取面部的输入图像,然后将其映射到相应的提升面部特征。
例如,你可以输入一张脸并将其转换成相反的性别,使其更年轻或更老,改变其肤色,等等。
下图的右侧显示了如何使用 StarGAN 来改变任何输入人脸的面部表情。
Yunjey Choi 等人 2017, StarGAN:用于多领域图像到图像翻译的统一生成对抗网络
结论
所以,你已经看到甘有多强大,他们能做什么。关于 GANs 最令人惊奇的事情是,它是一个相当复杂的目标的非常简单的实现。
如果你对卷积神经网络的工作原理和反向传播有一个基本的了解,那么你可以马上开始研究 GANs。这篇文章是为了介绍生成性对抗网络背后的基本工作,以及它的变化如何有助于产生不同的和更好的结果。
在以后的帖子中,我会分享如何从头开始构建 GAN 并从中产生美妙的结果。我已经构建了一个 DCGAN 来从 CelebA 数据集生成新的面孔,以及一个条件 GAN 来学习获取一个动画的黑白草图并将其转换为彩色输出,如下所示。
***
在数据集上训练 DCGAN 模型,显示左边的 MNIST 和右边的西里巴**(图片由作者提供)***
左栏为之前未见过的黑白草图测试图像,右栏为模型预测的彩色输出图像,但不知道真实情况(图片由作者提供)
生成对抗网络 GANs:初学者指南
关于 GAN 模型如何与 Python 中的示例一起工作的演练。
由 Unsplash 上的 drmakete 实验室拍摄的照片
T 机器学习的假设例子是围绕拥有一台能够思考并模仿通过某种程度智能测试的机器来设想的。虽然这是最终目标,但我们还没有达到,我们还有很长的路要走。在过去的几年中,已经开发了许多模型来在无人监督的模式下学习,试图与另一台计算机或人类进行竞争,以执行某项任务。这篇文章阐明了生成性对抗网络(GANs)的使用,以及如何在当今世界使用它们。
I. GANs 和机器学习
机器学习已经显示出一些识别数据分布、图像和事件序列等模式的能力,以解决分类和回归问题。Ian Goodfellow 等人在 2014 年[1]发表了一篇文章,使用两个独立的神经网络来生成与真实数据具有相似属性的合成数据。这项工作使研究界对生成逼真的图像、视频和通用合成结构数据更感兴趣。
图 1:渐进学习 GAN 模型生成人工人脸的例子。
gan 是无监督的深度学习技术。通常使用两个神经网络来实现:生成器和鉴别器。这两种模式以一种游戏设定的形式相互竞争。GAN 模型将在真实数据和由发生器产生的数据上被训练。鉴别器的工作是从真实数据中确定假数据。生成器是一个学习模型,因此最初,它可能会产生低噪声数据,甚至是完全噪声数据,这些数据不能反映真实数据的真实分布或属性。
生成器模型的主要目标是生成能够成功通过鉴别器的人工数据。该模型开始获取一些噪声,通常是高斯噪声,并产生一个格式化为像素矢量的图像。生成器必须学会如何欺骗鉴别器并赢得肯定的分类(生成的图像被分类为真实的)。每当这些生成的图像中的任何一个被成功检测为“假”时,就计算生成步骤的损失。鉴别者必须学会如何逐步识别那些假图像。每当模型不能识别假图像时,负损失被给予鉴别器。关键概念是同时训练发生器和鉴别器。
生成手写数字的示例:
研究社区有许多有趣的数据集来衡量 GAN 模型的准确性。在本文中,我们将详细使用其中的一些数据集,从 MNIST 开始。MNIST 是解释广泛用于图像处理的生成模型理论的最重要的例子之一。来自 MNIST 数据集的样本如图 2 所示。
图 2:来自 MNIST 数据集的手写数字图像样本。
为了生成人工手写图像,我们需要实现两个模型:一个用于生成假图像,另一个用于区分假图像和真图像。图 3 显示了训练 GAN 模型的总体流程。
图 GAN 学习框架,它同时训练了生成器和鉴别器。
在构建鉴别器和生成器时,有许多架构需要考虑。我们可以建立一个深度神经网络或卷积神经网络(CNN)和其他一些选项。我们将很快检查 GAN 模型的类型,但是首先,让我们现在选择 CNN。
这个例子的源代码在我的 Github 上有。
鉴别器模型架构从接收图像(28 x 28 x 1)开始,并将其通过两个卷积层,每个卷积层中有 64 个滤波器。或者,我们可以使用 128 个过滤器,这代表了每层中隐藏节点的数量。我们可以通过使用具有 64、128 和 256 个隐藏节点的三层来使神经网络架构更密集。为了简化 GAN 网络的工作方式,我们将在本教程中使用简单架构,它仍然具有很高的精度。图 4 显示了鉴频器的整体架构。
图 4:鉴别器模型的架构显示了层的数量和每个层中的参数。
生成器模型学习如何生成逼真的图像,但需要从潜在空间中的一些随机点开始。如果将图 5 中的生成器架构与图 4 中的鉴别器架构进行比较,您会发现它们看起来几乎相同。重要的是要知道,在构建发电机网络时,没有必要翻转 discrinmiator。生成器的架构可以有不同数量的层、过滤器和更高的整体复杂性。
图 5:显示每一层的生成器模型的架构。
鉴别器和发生器的另一个主要区别是激活函数的使用。discrminator 在输出层中使用 s 形。这是一个布尔分类问题,这将确保输出不是 0 就是 1。另一方面,生成器没有损失函数或任何要使用的优化算法。它使用转置卷积层从潜在空间对低分辨率密集层进行上采样,以构建更高分辨率的图像。构建生成器模型的技巧是我们不需要编译它。GAN 模型现在将结合完整的框架,该框架结合了生成器、鉴别器和编译模型。我们将在下一节详细讨论这些方面。
def building_gan(generator, discriminator):
GAN = Sequential()
discriminator.trainable = False
# Adding the generator and the discriminator
GAN.add(generator)
GAN.add(discriminator)
# Optimization function
opt = tf.keras.optimizers.Adam(lr=2e-4, beta_1=0.5)
# Compile the model
GAN.compile(loss='binary_crossentropy', optimizer=opt)
return GAN
下一个动画显示了在训练过程中,发生器在每组时期的改进情况:
图 5:显示使用 GAN 模型生成的数字的渐进质量的动画图像。
GAN 的能力和挑战
(假可训练)
(1)评估
其中一个关键问题是生成的数据(无论是图像、文本还是歌曲)的质量,以及这些生成的文章的多样性。鉴别器帮助我们检查生成的数据是真是假。然而,从鉴别器的角度来看,生成的样本可能看起来很真实,但是对于人眼来说可能太明显而不会注意到。因此,我们需要与主观评价相关的评价指标。研究这个问题的一种方法是分析真实数据和生成数据之间的分布特性。
两个评估指标可以从统计上帮助测量生成数据的质量:Inception Score [3]和 Frechet Inception [4]。这两种客观指标都被研究团体广泛采用,尤其是用于测量生成图像的质量。由于本教程是一个介绍,我们不会详细介绍这些指标是如何工作的。
(2)损失函数
正如我们之前所讨论的,GAN 模型具有同时训练发生器和鉴别器的独特属性。这需要损失函数来平衡一侧(鉴别器)的训练,同时也改善另一侧(发生器)的训练。当构建鉴别器模型时,我们像任何其他神经网络架构一样明确定义损失函数。
*# Defining the discriminator model*
**def** building_discriminator():
*# The image dimensions provided as inputs*
image_shape = (28, 28, 1)
disModel = Sequential()
disModel.add(Conv2D(64, 3, strides=2, input_shape=image_shape))
disModel.add(LeakyReLU())
disModel.add(Dropout(0.4))
*# Second layer*
disModel.add(Conv2D(64, 3, strides=2))
disModel.add(LeakyReLU())
disModel.add(Dropout(0.4))
*# Flatten the output*
disModel.add(Flatten())
disModel.add(Dense(1, activation='sigmoid'))
*# Optimization function*
opt = tf.keras.optimizers.Adam(lr=2e-4, beta_1=0.5)
*# Compile the model*
disModel.compile(loss='binary_crossentropy', optimizer=opt, metrics = ['accuracy'])
**return** disModel
另一方面,发电机模型没有明确定义损失函数。它基于鉴别器和根据其损失函数更新的发生器的训练。
*# Defining the generator model*
**def** building_generator(noise_dim):
genModel = Sequential()
genModel.add(Dense(128 * 6 * 6, input_dim=noise_dim))
genModel.add(LeakyReLU())
genModel.add(Reshape((6,6,128)))
*# Second layer*
genModel.add(Conv2DTranspose(128, (4,4), strides=(2,2)))
genModel.add(LeakyReLU())
*# Third layer*
genModel.add(Conv2DTranspose(128, (4,4), strides=(2,2)))
genModel.add(LeakyReLU())
genModel.add(Conv2D(1, (3,3), activation='sigmoid'))
**return** genModel
选择损失函数的选项很少,例如:
- 最小二乘法。
- 瓦瑟斯坦损失函数。
(3)收敛性的确定
与 GAN 模型相关的关键问题之一是确定模型何时收敛。鉴别者和产生者之间的竞争使得游戏很难达到最后的赢家。这两种模型都希望最大化它们的增益并最小化它们的损耗。在我们的情况下,我们希望两个模型都达到这样的程度,即它们几乎可以完全猜测图像是假还是真,以及生成的图像是否会成功通过鉴别器。50-50 的机会是从博弈论中继承的完美的理想案例,其中两种模型都很难被赢得。
众所周知,GAN 模型存在收敛速度慢的问题。与其他无监督模型类似,真实标签的缺乏增加了确定训练何时可以停止的挑战。我们需要确保培训时间和产品质量之间的平衡。几个因素有助于减慢或加快训练过程,例如输入的归一化、批量归一化、梯度惩罚以及在训练 GAN 模型之前很好地训练鉴别器。
(4)产生的图像尺寸
众所周知,GAN 模型在生成图像的尺寸方面能力有限。我们在 MNIST 的例子中看到的图像尺寸只有 28 x 28 像素。这些是在实际应用中使用的非常小的图像。如果我们想要生成更大的图像,比如说 1024 x 1024,我们将需要一个更具可伸缩性的模型。研究团体一直对提高 GAN 能力感兴趣。例如,在 2017 年,T Karras 等人提出了一种称为渐进生长 GANs 的新模型来解决这种问题[2]。
GAN 模型的类型
前几节中介绍的一些挑战使得研究界扩展了 GAN 模型的思想,以解决上述一个或多个问题。本节涵盖一些流行的扩展和优化的 GAN 架构,以扩展原有的 GAN 功能。
图 6:GAN 模型架构和扩展类型的概述。
深度卷积 GAN (DCGAN): 这是一种扩展,用拉德福德等人提出的 CNN 架构取代前馈神经网络【5】。使用 CNN 架构和通过滤波器学习的想法提高了 GAN 模型的准确性。
**wasser stein GAN(WGAN)😗*WGAN 是由 M. Arjovsky 等人设计的【6】。WGAN 侧重于定义生成分布和真实分布之间的距离,这决定了模型的收敛性。他们建议使用推土机(EM)距离来有效地近似这些分布之间的差异。
**Progressive GAN:**Progressive GAN 由 T. Karras 等人[7]设计,并在 ICLR 会议上提出。这项工作对生成器和鉴别器从较低分辨率到较高分辨率层的渐进增长做出了很大贡献。该技术要求在计算小批量标准偏差时减少小批量的大小。ProgressiveGan 还使用均衡学习率和逐像素特征归一化。
研究从以前的类型发展到引入半监督学习模型。该模型可以转化为多类分类器,而不是让鉴别器对给定的图像进行真伪分类。例如,在 MNIST,我们有十个代表十个手写数字的类。鉴别器将有十加一个类,其中附加的类表示要分类的假图像。在这种情况下,鉴别器不仅从真实图像中学习伪像,而且将每个真实图像分类到其对应的类别。这将利用两种损失(Softmax 和 Sigmoid)拉伸目标函数,以使鉴别器更有效地对图像进行正确分类(见图 7)。我们列表中的其余类型(cGAN、CycleGAN)引入了类似的新目标。
图 7:一个生成的合成图像可能被归类为真实的,但是另一个数字分类器会把它归类为数字 8 吗?
条件 GAN (cGAN): cGAN 于 2014 年由 M. Mehdi 和 S. Osindero 发表[8]。它支持每个图像都有标签的想法,生成器学习如何为每个标签生成逼真的图像。鉴别者学会辨别假图像,同时确保它们带有正确的标签。如果我们在图 3 的学习框架上应用 cGAN,将需要进行以下更改:
图 8:基于条件 GAN 的学习框架,显示了潜在空间和用于生成图像的随机选择的标签。
**pix 2 pix:**pix 2 pix 模型通过考虑图像到另一组图像的转换,应用了条件 GAN 模型概念的特殊情况。P. Isola 等人[9]在 2017 年提出了这个想法。标签和产生的输出是成对图像的集合。就像翻译一部分文本内容一样,我们可以将一个图像翻译成另一个具有特定属性的图像(参见图 9)。后来,研究界提出了一种更有效的方法,如我们将在接下来讨论的 Cycle GAN。
图 9:展示图像到图像转换能力的示例。这些图片摘自于[9]中发表的原始论文。
Cycle GAN:Cycle GAN 的独特思想是使用多个生成器和鉴别器,而不是一对一的架构。Pix2pix 让模型从一对图像中学习。这个条件后来被 J-Y. Zhu 等人[10]放宽了。他们想法的新颖之处在于创造了一个循环,在这个循环中,产生的翻译被再次循环以产生原始图像。如果在翻译过程中有任何损失,差异就是我们需要优化的。可以使用两个生成器和鉴别器将输入翻译到“域 A”,然后翻译到“域 B”。这种结构化在本文中被称为前后一致性(反向翻译和再硅化)。两个鉴别器检查两个域的生成输出。这个概念非常类似于自动编码器的基本思想。
自动编码器由编码器和解码器两部分组成。这些模型超出了本文的范围,因为它们代表了使用单一模型的独立类别的学习者。自动编码器以压缩编码格式构建输入。然后,解码器从生成的压缩形式中重建输入。
渐进式氮化镓:
我们已经讨论了如何使用来自 MNIST 数据集的非常小的图像来构建 GAN 模型。图像大小仅为 128 x 128 像素。如前所述,渐进式 GAN 模型可以从低分辨率层增长到高分辨率层,从而可以扩大模型输出。我们将在下面的例子中看到如何从 64 x 64 像素生成 1024 x 1024 像素。
图 10:显示渐进 GAN 如何逐渐添加新层以达到目标分辨率的示意图。
该模型基于两个主要概念设计:淡入和微调。该模型从简单的 4 x 4 分辨率发展到更高的分辨率。它提供了在训练每一层之后增长的能力,并且我们将能够在我们逐步训练时监控输出(如图 10 所示)。alpha 参数被缩放为 0 到 1 之间的值。它决定了我们是应该使用先前训练的层还是放大。鉴别器
结论和进一步阅读
这篇文章简要介绍了 GAN 模型有多神奇。我们刚刚对 GAN 模型的工作原理有了一些基本的了解。我稍后将在单独的线程中扩展一些相关的主题。然而,如果你想了解更多,我推荐两个地方:
- 由 Jakub Langr 和 Vladimir Bok 撰写的“GANs in Action”是一本很好的书,讲述了如何构建 GAN 模型,并详细介绍了本博客中介绍的各个方面。
- “The-gan-zoo”是一个 Github 知识库,它跟踪该领域最近发表的所有研究文章。你可以从这个回购访问你感兴趣的文件,并导航到其他有趣的工作列表。
参考
[1]古德费勒,伊恩,等。“生成性对抗性网络。”神经信息处理系统的进展。2014.
[2] Karras,Tero 等,“为提高质量、稳定性和变化性而逐步种植甘蔗” arXiv 预印本 arXiv:1710.10196。 2017 年。
[3]萨利曼斯,蒂姆,等,“训练甘斯的改进技术”神经信息处理系统的进展。2016.
[4] Heusel,Martin 等人,“通过双时标更新规则训练的 Gans 收敛到局部纳什均衡。”神经信息处理系统的进展。2017.
[5]拉德福德,亚历克,卢克·梅斯,和苏密特·钦塔拉。“深度卷积生成对抗网络的无监督表示学习.” arXiv 预印本 arXiv:1511.06434 (2015)。
[6] Arjovsky、Martin、Soumith Chintala 和 Léon Bottou。“瓦瑟斯坦·甘” arXiv 预印本 arXiv:1701.07875 (2017)。
[7] Karras,Tero 等,“为提高质量、稳定性和变化性而逐步种植甘蔗” arXiv 预印本 arXiv:1710.10196 (2017)。
[8]米尔扎、迈赫迪和西蒙·奥辛德罗。“条件生成对抗网络.” arXiv 预印本 arXiv:1411.1784 (2014)。
[9] Isola,Phillip,等,“用条件对抗网络进行图像到图像的翻译”IEEE 计算机视觉和模式识别会议论文集。2017.
[10]朱,严军,等.“利用循环一致对抗网络进行不成对的映象对映象翻译”IEEE 计算机视觉国际会议论文集。2017.
生成性对抗网络——难吗?不是。
GAN 工作流程简介。
米凯拉·帕兰特在 Unsplash 上的照片
自 2014 年问世以来,生成性对抗网络(俗称 GAN)已被广泛纳入众多研究工作。但是是什么让甘有如此神奇的魔力呢?
事实上,有很多教程都在教授如何实现 GAN 代码——但是没有足够简单地解释 GAN。在本教程中,我将介绍 GANs 背后的数学知识,并尝试涵盖最初的 GAN 论文中的大部分基础知识。您可能已经猜到这不是一个逐行代码的教程。
那么什么是甘呢?
一个生成对抗网络包含的不是一个单一的网络,而是一组至少两个总是相互“交战”的网络。一个网络被称为发电机,另一个是鉴别器。顾名思义,生成器生成由鉴别器评估的数据。两个网络之间的对抗部分或“战争”是,生成器反复尝试欺骗鉴别器,而鉴别器则被确定不在生成器的伪装下。
发电机所做的是我们的神经网络长期以来一直在做的事情。生成器试图估计数据分布,并基于该估计生成数据。一般而言,神经网络通常用于基于一些参数来估计函数。是什么使生成器不同于传统的神经网络?魔法在哪里?—发电机以噪音作为输入工作!一台机器可以从噪音中生成人的笔迹,这一事实让甘斯变得神奇。
由于我们的发电机网络以噪声作为输入,我们必须首先定义输入噪声变量的优先。让我们把先验表述为—
先验噪声分布
在贝叶斯 统计推断中,一个不确定量的先验概率分布,通常简称为先验是概率分布,它将表达一个人对这个量的信念在之前,一些证据被考虑到了——https://en.wikipedia.org/wiki/Prior_probability
如果我们用 G 表示发电机网络,我们可以说 G 将噪声变量映射到数据空间,如下所示
发电机网络
简单地说,生成器 G 获取随机噪声,并尝试重建真实数据。假设我们将发生器给出的数据定义为 p 的输出。需要注意的是,我们没有优化p——我们优化了θ,这样我们就可以得到p的正确估计。现在,我们并不期望生成器自己训练并给出真实数据,对吗?我们需要某种东西来检查生成器,如果它不能从噪声中生成真正的数据,就对它进行某种“惩罚”。
这个“惩罚者”就是鉴别者。鉴别器(D)是其最基本的形式,是一个分类器,用于对其输入进行真假分类。因此,鉴别器输出单个标量。 D(x) 表示 x 来自真实数据而非生成器 g 的概率。由于我们已经定义了生成器和鉴别器模型以及它们如何工作,我们现在将讨论成本函数或损失函数。
损失函数
可以说,GAN 最重要的部分之一是损耗函数及其设计。在介绍 GAN 的论文中定义的损失函数是—
损失函数
这是一个“最小-最大”损失函数,我们训练鉴别器以最大化该损失函数,同时训练发生器以最小化该损失函数的最后一项。不同类型的 gan 还有其他损失函数。其中一些损失函数是对这个的修改。一个 GAN 甚至可以有多个损耗函数,一个用于发生器,一个用于鉴频器
培养
现在我们已经完成了损失函数,我们应该如何处理训练呢?我们先训练哪个网络?对于每个历元,首先计算鉴别器的梯度,并且首先更新其权重。然后,我们训练发电机。
训练算法(步骤)—
- 首先,我们从我们之前定义的噪声中采样一个小批量的 m 个噪声样本
- 然后,我们从数据生成分布(训练集)中抽取少量的 m 个样本
- 我们使用 SGD 根据计算的梯度更新鉴别器的权重。SGD 代表随机梯度下降。更多信息—https://medium . com/@ hmrishavbandyopadhyay/neural-network-optimizer-hard-not-2-7ecc 677892 cc
- 然后,我们再次从噪声先验中采样小批量的 m 个噪声样本
- 我们使用计算的梯度和应用 SGD 来更新生成器的权重。
训练算法
因此,我们现在已经成功地训练了一个非常原始和简单的 GAN 网络。然而,这只是对 2014 年推出的 GAN 论文的解释。从那时起,已经产生了数百种类型的 GAN,并且每一种都有它们自己的损失函数和它们自己的训练算法。
实现 GANs 可能会变得非常有趣。如果你有任何疑问,请在评论中告诉我——乐意效劳;)
查看我的博客以获得更快的更新,并订阅优质内容:D
克罗伊斯,吕底亚(小亚细亚)的国王,曾经问特尔斐的神谕,他是否应该对波斯开战…
www.theconvolvedblog.vision](https://www.theconvolvedblog.vision)
Hmrishav Bandyopadhyay 是印度 Jadavpur 大学电子与电信系的二年级学生。他的兴趣在于深度学习、计算机视觉和图像处理。可以通过以下方式联系到他:hmrishavbandyopadhyay@gmail.com | |https://hmrishavbandy . github . io
Python 中的生成对抗网络
Python 中的 GANs 简介
生成对抗网络(GANs)是一组用于产生合成数据的深度神经网络模型。该方法由伊恩·古德费勒于 2014 年开发,并在论文生成对抗网络中进行了概述。GAN 的目标是训练鉴别器能够区分真实和虚假数据,同时训练生成器产生可以可靠地欺骗鉴别器的数据的合成实例。
GANs 的一个流行应用是在“GANgogh”项目中,在这个项目中,经过 wikiart.org 绘画训练的 GANs 生成合成画。独立研究人员 Kenny Jones 和 Derrick Bonafilia 能够生成合成的宗教、风景、花卉和肖像图像,表现令人印象深刻。文章甘戈:用甘斯来创造艺术详细介绍了这种方法。在本帖中,我们将介绍用 python 构建基本 GAN 的过程,我们将使用它来生成手写数字的合成图像。这篇文章中使用的大部分代码可以在 GANs Tensorflow 教程页面上找到,可以在这里找到。
我们开始吧!
现在,让我们导入必要的包。让我们从导入‘matplotlib’,‘tensor flow . keras’层和‘tensor flow’库开始。让我们也定义一个变量,我们可以使用它来存储和清除我们的会话:
import matplotlib.pyplot as plt
from tensorflow.keras import layers
import tensorflow as tf
from tensorflow.python.keras import backend as K
K.clear_session()
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
接下来,让我们加载“MNIST”数据集,它在“张量流”库中可用。数据包含手写数字的图像和对应于数字的标签:
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()
让我们来看看训练数据中的第一幅图像:
plt.imshow(train_images[0], cmap='gray')
我们可以看到这是手写的‘5’。接下来,让我们重塑数据,将图像像素转换为浮点值,并将像素值归一化为-1 到 1 之间的值:
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1).astype('float32')
train_images = (train_images - 127.5) / 127.5
现在让我们定义我们的发电机模型:
def generator_model():
model = tf.keras.Sequential()
model.add(layers.Dense(7*7*256, use_bias=False, input_shape=(100,)))
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU()) model.add(layers.Reshape((7, 7, 256)))
assert model.output_shape == (None, 7, 7, 256) # Note: None is the batch size model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False))
assert model.output_shape == (None, 7, 7, 128)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU()) model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False))
assert model.output_shape == (None, 14, 14, 64)
model.add(layers.BatchNormalization())
model.add(layers.LeakyReLU()) model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh'))
assert model.output_shape == (None, 28, 28, 1) return model
我们首先初始化一个顺序模型对象。然后我们添加第一层,这是一个普通的密集神经网络层。还有一系列转置卷积层,是带填充的卷积层。对于那些不熟悉的,卷积层学习矩阵(核心)的权利,然后结合起来,形成过滤器用于特征提取。通过学习滤波器权重,卷积层学习表示关于图像的高级信息的卷积特征。通过学习的过滤器,这些层可以执行像边缘检测、图像锐化和图像模糊这样的操作。这些是计算机视觉中核心矩阵的一些例子:
如果你有兴趣,可以在这里了解更多关于卷积神经网络的知识。还有一系列泄漏的“ReLu”层:
这些是经过修改的“ReLu”激活,通过增加“ReLu”功能的范围,有助于缓解神经元死亡问题。还有批量归一化图层,用于固定各图层输入的均值和方差。这有助于提高神经网络的速度、性能和稳定性。
发生器和鉴别器网络以类似于普通神经网络的方式被训练。即,随机初始化权重,评估损失函数及其相对于权重的梯度,并且通过反向传播迭代更新权重。
训练过程将帮助生成器模型从噪声中产生看起来真实的图像,并且鉴别器在检测看起来真实的假图像方面做得更好。让我们看一个生成器模型的输入示例。首先,让我们定义我们的生成器并初始化一些噪声“像素”数据:
generator = generator_model()
noise = tf.random.normal([1, 100])
接下来,让我们将噪声数据传入“generator_model”函数,并使用“matplotlib”绘制图像:
your_session = K.get_session()
generated_image = generator(noise, training=False)
array = generated_image[0, :, :, 0].eval(session=your_session)
plt.imshow(array, cmap='gray')
我们看到这只是一个嘈杂的黑白图像。我们的目标是让我们的生成器学习如何通过对这些嘈杂的数据进行迭代训练来生成看起来真实的数字图像,就像我们之前绘制的图像一样。经过充分的训练,我们的生成器应该能够从如上所示的噪声输入中生成真实的手写数字。
现在让我们定义我们的鉴别器函数。这将是一个用于分类的普通卷积神经网络:
def discriminator_model():
model = tf.keras.Sequential()
model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same',
input_shape=[28, 28, 1]))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3)) model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same'))
model.add(layers.LeakyReLU())
model.add(layers.Dropout(0.3)) model.add(layers.Flatten())
model.add(layers.Dense(1)) return model
接下来,让我们定义损失函数和鉴别器对象:
discriminator = discriminator_model()
cross_entropy = tf.keras.losses.BinaryCrossentropy(from_logits=True)
接下来,我们定义特定于鉴别器的损失函数。该函数测量鉴别器区分真实图像和虚假图像的能力。它将鉴别器的二进制预测与真实图像和伪图像上的标签进行比较,其中“1”对应于真实图像,“0”对应于伪图像:
def discriminator_loss(real_output, fake_output):
real_loss = cross_entropy(tf.ones_like(real_output), real_output)
fake_loss = cross_entropy(tf.zeros_like(fake_output), fake_output)
total_loss = real_loss + fake_loss
return total_loss
发生器损耗函数衡量发生器欺骗鉴别器的能力:
def generator_loss(fake_output):
return cross_entropy(tf.ones_like(fake_output), fake_output)
由于生成器和鉴别器是独立的神经网络,它们各自都有自己的优化器。我们将使用“Adam”优化器来训练我们的鉴别器和生成器:
generator_optimizer = tf.keras.optimizers.Adam(1e-4)
discriminator_optimizer = tf.keras.optimizers.Adam(1e-4)
接下来,让我们定义历元数(训练数据的完整遍数)、噪声数据的维度大小以及要生成的样本数:
EPOCHS = 50
noise_dim = 100
num_examples_to_generate = 16
然后,我们为训练循环定义我们的函数。“@tf.function”装饰器编译该函数。“train_step()”函数从随机噪声生成图像开始:
@tf.function
def train_step(images):
noise = tf.random.normal([BATCH_SIZE, noise_dim])
with tf.GradientTape() as gen_tape, tf.GradientTape() as disc_tape:
generated_images = generator(noise, training=True) #random seed images
然后使用鉴别器对真实和伪造的图像进行分类:
@tf.function
def train_step(images):
...
real_output = discriminator(images, training=True)
fake_output = discriminator(generated_images, training=True)
然后,我们计算发生器和鉴频器损耗:
@tf.function
def train_step(images):
...gen_loss = generator_loss(fake_output)
disc_loss = discriminator_loss(real_output, fake_output)
然后,我们计算损失函数的梯度:
@tf.function
def train_step(images):
...
gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables)
gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables)
然后,我们应用优化器来找到使损失最小化的权重,并更新生成器和鉴别器:
@tf.function
def train_step(images):
...
generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables))
discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables))
接下来,我们定义一种方法,允许我们在训练完成后生成假图像,并保存它们:
def generate_and_save_images(model, epoch, test_input): predictions = model(test_input, training=False) fig = plt.figure(figsize=(4,4)) for i in range(predictions.shape[0]):
plt.subplot(4, 4, i+1)
plt.imshow(predictions[i, :, :, 0] * 127.5 + 127.5, cmap='gray')
plt.axis('off') plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
plt.show()
接下来,我们定义允许我们同时训练生成器和鉴别器的训练方法。接下来,让我们导入“时间”和“操作系统”模块。让我们也定义一个检查点对象,它将允许我们保存和恢复模型:
import time
import os
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt")
checkpoint = tf.train.Checkpoint(generator_optimizer=generator_optimizer, discriminator_optimizer=discriminator_optimizer,
generator=generator,
discriminator=discriminator)
接下来,我们定义我们的函数,该函数从迭代历元数开始:
def train(dataset, epochs):
for epoch in range(epochs):
在历元循环中,我们从每个训练步骤中生成图像:
def train(dataset, epochs):
...
display.clear_output(wait=True)
generate_and_save_images(generator,
epoch + 1,
seed)
if (epoch + 1) % 5 == 0:
checkpoint.save(file_prefix = checkpoint_prefix)print ('Time for epoch {} is {} sec'.format(epoch + 1, time.time()-start))
然后,我们从最后一个时期生成图像。让我们每隔 5 个时期保存我们的模型:
def train(dataset, epochs):
... display.clear_output(wait=True)
generate_and_save_images(generator,
epochs,
seed)
最后,我们可以使用 epochs 参数对训练数据调用“train()”方法:
train(train_dataset, EPOCHS)
如果我们用两个时期运行我们的代码,我们应该得到假图像的如下输出:
我们看到输出噪声仍然很大。经过 50 个时期后,我们应该生成以下图(注意,这需要在 16 G 内存的 MacBook Pro 上运行几个小时):
正如我们所看到的,一些数字是可识别的,而另一些需要更多的训练来提高。可以推测,随着时代的增加,数字看起来会更可信。我就讲到这里,但是您可以随意使用数据并自己编码。还有许多其他数据集可以用来训练 GANs,包括英特尔图像分类数据集、 CIFAR 数据集和猫&狗数据集。其他有趣的应用包括深度假视频和深度假音频。
要开始在视频上训练 GAN,您可以查看论文 复杂数据集的对抗性视频生成 *。*在本文中,作者在 UCF-101 动作识别数据集上训练 GAN,该数据集包含来自 YouTube 的 101 个动作类别的视频。要开始在音频上训练 GAN,请查看论文对抗性音频合成。在本文中,作者对 GAN 进行了第一个到第九个语音命令的训练,这些命令包含鼓的声音、鸟的叫声等等。
结论
总之,在这篇文章中,我们讨论了生成对抗网络(GAN)以及如何用 python 实现它。我们发现 GANs 同时训练两个神经网络,一个用于数据生成,另一个用于数据识别。鉴别器和生成器的层最显著地分别包含转置卷积和普通卷积层,它们学习图像的高级特征表示。我鼓励你尝试在一些其他有趣的数据上训练 GAN,比如我上面提到的语音或视频数据集。同样,这篇文章中使用的代码可以在 GANs Tensorflow 教程页面上找到,可以在这里找到。我希望你觉得这篇文章有用/有趣。这篇文章的代码也可以在 GitHub 上找到。感谢您的阅读!
使用 seq2seq 模型的生成型聊天机器人!
深度学习|自然语言处理
一个聊天机器人,产生一个响应,而不是从现有的选择!
聊天机器人是一种为用户提供真实对话体验的软件。有封闭域聊天机器人和开放域(生成型)聊天机器人。封闭域聊天机器人是一个用预定义文本进行响应的聊天机器人。顾名思义,生成型聊天机器人会生成响应。
下面的文章展示了如何在机器学习分类器的帮助下创建封闭域聊天机器人。
有一种简单有效的方法来创建使用朴素贝叶斯分类器的封闭域聊天机器人。
towardsdatascience.com](/a-naive-bayes-approach-towards-creating-closed-domain-chatbots-f93e7ac33358)
在上面的文章中,答案是固定的,机器学习帮助选择用户问题中给出的正确答案。但是在这里,我们不会从预定义的响应中进行选择,而是基于训练语料库生成响应。对于这种方法,我们将使用编码器-解码器(seq2seq)模型。
seq2seq 创建创成式聊天机器人方法介绍
seq2seq 模型也称为编码器-解码器模型,使用长短期记忆 LSTM 从训练语料库中生成文本。seq2seq 模型在机器翻译应用中也很有用。seq2seq 或编码器-解码器模型用简单的话来说是做什么的?它预测用户输入中给定的单词,然后使用该单词出现的可能性来预测接下来的每个单词。在构建我们的生成式聊天机器人时,我们将使用这种方法来生成用户输入中给出的文本。
编码器-解码器模型
编码器输出最终状态向量(存储器),该向量成为解码器的初始状态。我们使用一种叫做 的方法,教师强制 来训练解码器,使其能够预测前面单词中给定的目标序列中的后续单词。如上所示,状态通过编码器传递到解码器的每一层。“嗨”、“怎么样”、“是”和“你”被称为输入标记,而“我”、“我”和“好”被称为目标标记。令牌“am”的可能性取决于前面的单词和编码器状态。我们正在添加’< END >'标记,让我们的解码器知道何时停止。你可以在这里了解更多关于 seq2seq 车型的信息。
让我们从头开始构建我们的生成性聊天机器人吧!我们要做的第一项任务是预处理我们的数据集。
预处理数据集
我们将要使用的数据集是从 Kaggle 中收集的。你可以在下面找到。它包含人类的反应和机器人的反应。每个条目有 2363 个。
157 次聊天和 6300 多条信息与一个(假的)虚拟伙伴
www.kaggle.com](https://www.kaggle.com/eibriel/rdany-conversations)
首先,我们必须在正则表达式的帮助下清理我们的语料库。然后,我们将需要像人类反应-机器人反应这样的配对,以便我们可以训练我们的 seq2seq 模型。我们将如下所示执行这些任务。
import re
import random
data_path = "human_text.txt"
data_path2 = "robot_text.txt"
# Defining lines as a list of each line
with open(data_path, 'r', encoding='utf-8') as f:
lines = f.read().split('\n')with open(data_path2, 'r', encoding='utf-8') as f:
lines2 = f.read().split('\n')lines = [re.sub(r"\[\w+\]",'hi',line) for line in lines]
lines = [" ".join(re.findall(r"\w+",line)) for line in lines]
lines2 = [re.sub(r"\[\w+\]",'',line) for line in lines2]
lines2 = [" ".join(re.findall(r"\w+",line)) for line in lines2]
# grouping lines by response pair
pairs = list(zip(lines,lines2))
#random.shuffle(pairs)
创建对子后,我们也可以在训练前洗牌。我们的对子现在看起来像这样:
[('hi', 'hi there how are you'), ('oh thanks i m fine this is an evening in my timezone', 'here is afternoon'),...]
这里,“hi”是输入序列,“hi there how are you”是目标序列。我们必须为输入序列和目标序列创建单独的列表,我们还需要为数据集中的唯一标记(输入标记和目标标记)创建列表。对于目标序列,我们将在序列的开头添加“”,在序列的结尾添加“”,以便我们的模型知道在哪里开始和结束文本生成。我们将这样做,如下所示。
import numpy as npinput_docs = []
target_docs = []
input_tokens = set()
target_tokens = set()for line in pairs[:400]:
input_doc, target_doc = line[0], line[1]
# Appending each input sentence to input_docs
input_docs.append(input_doc)
# Splitting words from punctuation
target_doc = " ".join(re.findall(r"[\w']+|[^\s\w]", target_doc))
# Redefine target_doc below and append it to target_docs
target_doc = '<START> ' + target_doc + ' <END>'
target_docs.append(target_doc)
# Now we split up each sentence into words and add each unique word to our vocabulary set
for token in re.findall(r"[\w']+|[^\s\w]", input_doc):
if token not in input_tokens:
input_tokens.add(token)
for token in target_doc.split():
if token not in target_tokens:
target_tokens.add(token)input_tokens = sorted(list(input_tokens))
target_tokens = sorted(list(target_tokens))
num_encoder_tokens = len(input_tokens)
num_decoder_tokens = len(target_tokens)
注意:为了简单起见,我们只取前 400 对,但结果是,我们会得到非常低的准确度。
我们的数据集有唯一的输入标记和目标标记。现在,我们将创建一个输入特征字典,将输入标记存储为键-值对,单词是键,值是索引。类似地,对于目标标记,我们将创建一个目标特性字典。特征字典将帮助我们把我们的句子编码成一个热点向量。毕竟计算机只懂数字。为了对句子进行解码,我们需要创建逆向特征字典,将索引存储为键,将单词存储为值。
input_features_dict = dict(
[(token, i) for i, token in enumerate(input_tokens)])
target_features_dict = dict(
[(token, i) for i, token in enumerate(target_tokens)])reverse_input_features_dict = dict(
(i, token) for token, i in input_features_dict.items())
reverse_target_features_dict = dict(
(i, token) for token, i in target_features_dict.items())
培训设置
为了训练我们的 seq2seq 模型,我们将使用三个独热向量矩阵,编码器输入数据、解码器输入数据和解码器输出数据。我们对解码器使用两个矩阵的原因是 seq2seq 模型在训练*时使用的一种称为 教师强制 的方法。*这背后的想法是什么?我们有一个来自前一个时间步的输入令牌来帮助模型训练当前的目标令牌。让我们创建这些矩阵。
#Maximum length of sentences in input and target documents
max_encoder_seq_length = max([len(re.findall(r"[\w']+|[^\s\w]", input_doc)) for input_doc in input_docs])
max_decoder_seq_length = max([len(re.findall(r"[\w']+|[^\s\w]", target_doc)) for target_doc in target_docs])encoder_input_data = np.zeros(
(len(input_docs), max_encoder_seq_length, num_encoder_tokens),
dtype='float32')
decoder_input_data = np.zeros(
(len(input_docs), max_decoder_seq_length, num_decoder_tokens),
dtype='float32')
decoder_target_data = np.zeros(
(len(input_docs), max_decoder_seq_length, num_decoder_tokens),
dtype='float32')for line, (input_doc, target_doc) in enumerate(zip(input_docs, target_docs)):
for timestep, token in enumerate(re.findall(r"[\w']+|[^\s\w]", input_doc)):
#Assign 1\. for the current line, timestep, & word in encoder_input_data
encoder_input_data[line, timestep, input_features_dict[token]] = 1.
for timestep, token in enumerate(target_doc.split()):
decoder_input_data[line, timestep, target_features_dict[token]] = 1.
if timestep > 0:
decoder_target_data[line, timestep - 1, target_features_dict[token]] = 1.
为了清楚地了解编码器 _ 输入 _ 数据的尺寸如何工作,请参见下图。解码器 _ 输入 _ 数据和解码器 _ 目标 _ 数据同样具有尺寸。
编码器-解码器训练设置
我们的编码器模型需要一个输入层和一个 LSTM 层,输入层定义了一个用于保存独热向量的矩阵,而层具有一些隐藏状态。解码器模型结构与编码器几乎相同,但这里我们将状态数据与解码器输入一起传入。
from tensorflow import keras
from keras.layers import Input, LSTM, Dense
from keras.models import Model#Dimensionality
dimensionality = 256#The batch size and number of epochs
batch_size = 10
epochs = 600#Encoder
encoder_inputs = Input(shape=(None, num_encoder_tokens))
encoder_lstm = LSTM(dimensionality, return_state=True)
encoder_outputs, state_hidden, state_cell = encoder_lstm(encoder_inputs)
encoder_states = [state_hidden, state_cell]#Decoder
decoder_inputs = Input(shape=(None, num_decoder_tokens))
decoder_lstm = LSTM(dimensionality, return_sequences=True, return_state=True)
decoder_outputs, decoder_state_hidden, decoder_state_cell = decoder_lstm(decoder_inputs, initial_state=encoder_states)
decoder_dense = Dense(num_decoder_tokens, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)
您可以在这里了解更多关于如何编码编码器-解码器模型的信息,因为对它的完整解释超出了本文的范围。
建立和训练 seq2seq 模型
现在,我们将创建 seq2seq 模型,并使用编码器和解码器数据对其进行训练,如下所示。
#Model
training_model = Model([encoder_inputs, decoder_inputs], decoder_outputs)#Compiling
training_model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'], sample_weight_mode='temporal')#Training
training_model.fit([encoder_input_data, decoder_input_data], decoder_target_data, batch_size = batch_size, epochs = epochs, validation_split = 0.2)
training_model.save('training_model.h5')
这里,我们使用 rmsprop 作为优化器,使用分类交叉熵作为损失函数。我们称之为*。*fit()【方法】通过给定编码器和解码器的输入数据(X/input)和解码器的目标数据(Y/label)。训练结束后,我们得到的训练准确率在 20%左右。这种较低准确性的原因是我们只使用了 400 对数据集。如果在更大的数据集上训练,可以实现更高的准确性。
测试设置
现在,为了处理模型没有看到的输入,我们需要一个逐步解码的模型,而不是使用教师强制,因为我们创建的模型只有在目标序列已知的情况下才能工作*。*在生成型聊天机器人应用程序中,我们不知道对用户传入的输入会产生什么样的响应。为此,我们将不得不构建一个 seq2seq 模型。让我们首先用编码器输入和编码器输出状态构建一个编码器模型。我们将在之前训练好的模型的帮助下完成这项工作。
from keras.models import load_model
training_model = load_model('training_model.h5')encoder_inputs = training_model.input[0]
encoder_outputs, state_h_enc, state_c_enc = training_model.layers[2].output
encoder_states = [state_h_enc, state_c_enc]
encoder_model = Model(encoder_inputs, encoder_states)
接下来,我们将需要为解码器输入状态创建占位符,因为我们不知道我们需要解码什么或者我们将得到什么隐藏状态。
latent_dim = 256
decoder_state_input_hidden = Input(shape=(latent_dim,))
decoder_state_input_cell = Input(shape=(latent_dim,))
decoder_states_inputs = [decoder_state_input_hidden, decoder_state_input_cell]
现在,我们将借助之前培训的解码器 LSTM 和密集层来创建新的解码器状态和输出。
decoder_outputs, state_hidden, state_cell = decoder_lstm(decoder_inputs, initial_state=decoder_states_inputs)
decoder_states = [state_hidden, state_cell]
decoder_outputs = decoder_dense(decoder_outputs)
最后,我们有解码器输入层,来自编码器的最终状态,来自解码器密集层的解码器输出,以及解码器输出状态,它是网络从一个字到下一个字期间的存储器。我们现在可以将所有这些放在一起,并设置如下所示的解码器模型。
decoder_model = Model([decoder_inputs] + decoder_states_inputs, [decoder_outputs] + decoder_states)
测试我们的模型
最后,我们将创建一个函数,它接受我们的文本输入,并使用我们创建的编码器和解码器生成响应。在下面的函数中,我们传入表示文本句子的 NumPy 矩阵,并从中获取生成的响应。我为几乎每一行代码都添加了注释,以便您快速理解。下面的函数是这样的:1。)我们从编码器 2 中检索输出状态。)我们将输出状态传递给解码器(这是解码器的初始隐藏状态),以逐字解码句子 3。)在解码每个字之后更新解码器的隐藏状态,以便我们可以使用先前解码的字来帮助解码新的字
一旦我们遇到我们在预处理任务中添加到目标序列的’'标记,或者我们达到序列的最大长度,我们就会停止。
def decode_response(test_input):
#Getting the output states to pass into the decoder
states_value = encoder_model.predict(test_input)
#Generating empty target sequence of length 1
target_seq = np.zeros((1, 1, num_decoder_tokens))
#Setting the first token of target sequence with the start token
target_seq[0, 0, target_features_dict['<START>']] = 1.
#A variable to store our response word by word
decoded_sentence = ''
stop_condition = Falsewhile not stop_condition:
#Predicting output tokens with probabilities and states
output_tokens, hidden_state, cell_state = decoder_model.predict([target_seq] + states_value)#Choosing the one with highest probability
sampled_token_index = np.argmax(output_tokens[0, -1, :])
sampled_token = reverse_target_features_dict[sampled_token_index]
decoded_sentence += " " + sampled_token#Stop if hit max length or found the stop token
if (sampled_token == '<END>' or len(decoded_sentence) > max_decoder_seq_length):
stop_condition = True#Update the target sequence
target_seq = np.zeros((1, 1, num_decoder_tokens))
target_seq[0, 0, sampled_token_index] = 1.
#Update states
states_value = [hidden_state, cell_state]return decoded_sentence
将所有这些放在一起——生成聊天机器人
让我们创建一个包含运行聊天机器人所需方法的类。
class ChatBot:
negative_responses = ("no", "nope", "nah", "naw", "not a chance", "sorry")
exit_commands = ("quit", "pause", "exit", "goodbye", "bye", "later", "stop")#Method to start the conversation
def start_chat(self):
user_response = input("Hi, I'm a chatbot trained on random dialogs. Would you like to chat with me?\n")
if user_response in self.negative_responses:
print("Ok, have a great day!")
return
self.chat(user_response)#Method to handle the conversation
def chat(self, reply):
while not self.make_exit(reply):
reply = input(self.generate_response(reply)+"\n")
#Method to convert user input into a matrix
def string_to_matrix(self, user_input):
tokens = re.findall(r"[\w']+|[^\s\w]", user_input)
user_input_matrix = np.zeros(
(1, max_encoder_seq_length, num_encoder_tokens),
dtype='float32')
for timestep, token in enumerate(tokens):
if token in input_features_dict:
user_input_matrix[0, timestep, input_features_dict[token]] = 1.
return user_input_matrix
#Method that will create a response using seq2seq model we built
def generate_response(self, user_input):
input_matrix = self.string_to_matrix(user_input)
chatbot_response = decode_response(input_matrix)
#Remove <START> and <END> tokens from chatbot_response
chatbot_response = chatbot_response.replace("<START>",'')
chatbot_response = chatbot_response.replace("<END>",'')
return chatbot_response#Method to check for exit commands
def make_exit(self, reply):
for exit_command in self.exit_commands:
if exit_command in reply:
print("Ok, have a great day!")
return True
return False
chatbot = ChatBot()
在上面的代码中,所有方法都是不言自明的。下面是我们的生成式聊天机器人的最终输出!
与聊天机器人对话!
你可以在 GitHub 的这里找到上面的所有代码,在 LinkedIn 的这里找到我。
未来范围与限制
这里我们使用了一个非常小的数据集,得到了大约 20%的准确率。将来对于更大的数据集,该模型可能会提供更好的准确性。使用这种方法创建聊天机器人的局限性在于,我们需要一个非常大的数据集来为用户提供最佳响应,正如我们在上面的输出中可以看到的那样,由于数据集较小,聊天机器人在某些情况下不会给出正确的响应。
我们可以用上面显示的方法做的一个类似的任务是机器翻译。下面的文章展示了我们如何使用 seq2seq 模型来执行机器翻译。
使用 seq2seq 模型讨论两种不同的机器翻译方法。
towardsdatascience.com](/machine-translation-with-the-seq2seq-model-different-approaches-f078081aaa37)
结论
当开放域架构使我们能够执行无限的文本生成时,封闭域架构侧重于从一组预定义的响应中选择响应。封闭域系统使用意图分类、实体识别和响应选择。但是对于一个开放领域的聊天机器人来说,意图分类更加困难,并且可能有大量的意图。开放域或生成模型不是选择完整的响应,而是逐字生成响应,允许新的语言组合。
在行业中,一些公司使用封闭域聊天机器人来确保用户总是从预定义的聊天机器人那里收到正确的响应。自然语言处理(NLP)领域正在开发和训练神经网络,以模拟人脑处理语言的方式。这种深度学习策略可以让计算机更有效地处理人类语言。