本文主要是介绍机器学习周记(第十七周:GAN.pt2)2023.11.13~2023.11.19,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
摘要
ABSTRACT
1 文献阅读
2 Tips for GAN
2.1 JS divergence is not suitble
2.2 Wasserstein distance
3 GAN is still challenge
3.1 GAN for Sequence Generation
3.2 Evaluation of Generation
3.3 Mode Collapse
3.4 Mode Dropping
4 相关代码
摘要
本周继续学习了GAN的相关知识,以WGAN为例了解了训练GAN的一些小技巧,比如使用Wasserstein distance来替代并不适合的JS divergence计算分布之间的散度。还学习了如何将GAN应用于序列生成任务中,如何解决这个过程中会遇到的问题,例如Mode Collapse和Mode Dropping。
本周看了一篇关于如何利用深度学习来预测人工湿地(CWs)的出水水质质量,这篇论文主要介绍了一种数据预处理方法(移动平均和归一化)和遗传算法(GA),通过这两种技术能够大大提高深度学习模型的准确度。
ABSTRACT
This week, We continued our study of GAN-related knowledge, using WGAN as an example to explore some tricks for training GANs. For instance, We learned about using Wasserstein distance as a replacement for the less suitable JS divergence in computing the divergence between distributions. Additionally, We delved into how to apply GANs to sequence generation tasks and addressed challenges encountered in the process, such as Mode Collapse and Mode Dropping.
We also read a paper this week on using deep learning to predict the effluent water quality of Constructed Wetlands (CWs). The paper primarily introduced a data preprocessing method involving moving averages and normalization, along with the utilization of genetic algorithms (GA). These techniques were shown to significantly enhance the accuracy of deep learning models for predicting water quality in artificial wetlands.
1 文献阅读
论文标题:Deep learning-based prediction of effluent quality of a constructed wetland
论文背景:与污水处理厂相比,人工湿地(CWs)因其建设和运行成本低、处理能力好、生态效益高等优点,在发展中国家被广泛应用于城市水污染深度净化。此外,在全球变暖的背景下,对废水处理提出了新的要求,即减少温室气体(GHG)排放。在这种情况下,CWs被广泛用作低碳和绿色的污水处理方法,以解决各种点源和非点源污染。为了最大限度地提高CWs的处理效率,需要及时预测出水的潜在变化,调整CWs的运行参数,以保证城市水系统的安全。因此,需要在对CW以往出水水质数据进行优化的基础上,建立令人满意的模型来预测未来突发性变化,这将为CWs的调控提供有效的策略,从而间接地为控制城市水污染提供途径。
过去方案:数学模型不仅经常被用于模拟CW净化机制,还用于预测出水质量。然而,要基于数学模型预测CWs出水水质,不仅需要持续监测一系列关键水质指标(5天生化需氧量(BOD5)、化学需氧量(COD)、氨氮()和总磷(TP))还要测量湿地植物的吸收和细菌的活性,这需要消耗大量的时间和精力。因此,耗时的采样和测量是水质感知和及时调整CW的主要障碍。同时,各种数据驱动的模型已被用于预测CWs的纯化能力。尽管模型需要许多数据点作为机理或数学模型,但数据驱动方法不需要详细的基础和机理知识。因此,与数学模型相比,数据驱动的模型在预测实际CW的水质方面具有更广泛的应用潜力,并取得了更好的预测性能。在众多的数据驱动方法中,深度学习因其具有较强的非线性映射和预测能力、更高的容错性和较好的泛化性,已成为水文时间序列预测中广泛应用的技术。迄今为止,深度学习方法在实际垂直流CW中预测出水质量的大规模应用尚未得到系统研究。以前的应用要么是在实验室的小规模CW中,要么主要集中在根据温度、流速和溶解氧等几个可访问的参数来预测特定污染物的浓度。然而,考虑到CWs在实际条件下的进水浓度具有很强的波动性,并且存在温度、降雨量、大气压力、湿度等大量参数影响CWs的处理能力,如何借助多源数据建立一种合适的同时预测多种污染物的方法仍然是一个挑战。
论文方案:本研究旨在通过深度学习算法和多源数据驱动方法的结合,及时模拟和预测大规模CWs的出水水质。首先,针对影响CWs处理能力的多源数据,研究了前一天的数据与次日CW出水中污染物浓度的映射关系。然后,我们开发了各种典型的方法来预测三种常规污染物的浓度,并将它们相互比较,以便我们可以在大空间尺度上确定这种复杂环境的最佳模型。最后,针对CWs出水浓度的高波动性,提出了一种数据预处理模块,可以对原始数据进行平滑处理,去除高频噪声,有效提高模型预测精度。本研究为提高水质模型在实际场景中大规模应用的预测精度提供了新的方法和思路。
数据预处理:数据预处理方法分为两部分:移动平均和归一化。移动平均是一种能够平滑高频噪声的数据平滑方法,需要使模式比原始模式更明显,以确保模型性能的稳定性。平滑公式如公式(1)所示。由于指标之间的维度差异,在建模过程中忽略了一些指标,通过原始数据的线性变换对原始变量进行归一化。例如,有i个指标分别表示j对象的属性,则原始数据集如公式(2)所示。“Min”和“Max”分别是索引的最小值和最大值。这些值通过min-max归一化将索引的原始值
映射到区间[0,1]内的值
,如公式(3)所示。
其中是第t天的出水浓度,
为平均后t天的出水浓度,n为平均天数;
式中,i为指标个数,j为每个指标的属性个数;
其中表示归一化值,max(
) 和 min(
) 分别是样本的最大值和最小值。
遗传算法(GA):GA是模仿生物进化的过程,以在所有可能的解决方案中选择最合适的结果。优化过程主要包括通过选择、交叉和突变获得大量个体,最终选择拟合度最佳的个体,如下图所示。
选择:选择过程基于群体中个体的适应性评估:个体越健康,他们产生的后代就越多,如公式(8)所示。
交叉:交叉是将两条独立的染色体重新组合以创建一个新个体的过程。计算过程如公式(9)所示。
突变:突变操作随机改变染色体上的一些值以创建新的个体。其计算如公式(10)、(11)所示。
其中是个体i的选择概率,
是个体i的适应度,n是种群中的个体数。
是第i个个体的第j个基因,
是第k个个体的第j个基因,
和
分别是基因的上限和下限。G是当前迭代次数,
是最大生成数,r是区间[0,1]中的随机数。优化过程包括对输入进行编码和解码、创建初始种群、计算适应度、迭代操作和调整参数。在获得第一代后,根据拟合结果从每一代中选出最合适的个体,然后通过迭代操作获得新一代,直到达到设定的世代数。

2 Tips for GAN
实际上GAN是非常难训练的,想要更好的训练一个GAN模型需要使用一些小技巧,以较为知名的WGAN为例来说明如何使用这些技巧。
2.1 JS divergence is not suitble
在解释WGAN之前,先来处理JS divergence存在的一些问题。实际上,和
只有很少一部分是重叠的,JS divergence并不适用于GAN。这一点可以从两个方面来解释:① 从数据本身来看,
和
都只是高维空间下的低维流形而已,它们重叠的那小部分完全可以忽略不计。② 因为我们完全不知道
和
是什么样的,只能从中sampling数据,对
和
的理解也仅来自这些sampling出来的数据。所以即使
和
有很大部分的重叠,如果我们sampling出来的数据不够多,不够密,也完全有可能被机器认为
和
是没有重叠的。
为什么和
没有重叠会让JS divergence产生问题?实际上只要两个分布没有重叠,不管它们是不是接近,算出来的JS divergence就是log2。例如下图:虽然
比
更接近
,但是从JS divergence看起来,两者是没有差别的。这样的话,训练起来是有问题的,因为在实际训练的时候,我们的生成器的目标是最小化
和
之间的散度,然后使用判别器量出二者的JS divergence,但是对生成器来说
和
是一样的,因此生成器不会将
更新为
。我们是怎样求两个分布的散度的呢?实际上的操作是:我们有两群data,把它视为两个class,然后判别器就是一个二元分类器,然后最小化交叉熵损失。只要两堆数据没有重合,就能够很好地分成两类,那么他们的loss就是一样的,这意味着量出来的JS散度是一样的。在原始GAN里面,当训练的是一个二元分类器时,就会发现是比较难以训练,因为没有重叠就是一样差的。但一旦从不重叠训练到重叠就是对精确度的100%提高。
2.2 Wasserstein distance
不用JS disvergence改用其他的方法可以解决问题吗?这是可行的。例如换一种方式来衡量和
,用的是Wasserstein distance / earth mover’s distance,意思是假设你有两堆土(data) P 和 Q,而你开着一个推土机,将P土推到Q土 ,移动的平均距离就叫Wasserstein distance。
计算这个平均距离的过程也可以换一种角度来说明。假设用推土机重新将P的形状塑造成Q的形状,存在很多的移动方案,有的“舍近求远”,有的“舍远求近”,迁移的距离也有的小有的大。由此定义Wasserstein distance:无论移动方案的有多少种,都只选取平均距离最小的那个移动方案作为Wasserstein distance。
先不解释如何计算Wasserstein distance,先来看看假设我们能够计算Wasserstein distance能够带来什么样的好处。用Wasserstein distance替换掉JS divergence,如下图所示,和
的距离是
,那么Wasserstein distance就是
,如果
和
的距离是
,那么Wasserstein distance就是
。这样哪怕
和
两个并未重叠,我们也能够知道
比
更好,更接近
。
计算Wasserstein distance的公式如下:如果是从
中sample出来的,让它的判别器输出越大越好;如果
是从
中生成的,让它的判别器输出越小越好。除此之外,还要有一个约束,就是判别器必须是一个1-Lipschitz Function;一个重要特点就是它很平滑,如果没有这个约束,判别器一味着让real的分数就越来越高,generated的分数越来越低,那么就崩溃了,永远不会收敛(高度可以一直变化到无穷大无穷小)。因此必须有额外的限制,让判别器必须是平滑的。
现在最主要的问题则是如何实现这种让判别器必须是平滑的限制呢?最初的WGAN强迫参数限制在
和
之间。在参数
更新之后,如果
,则令
;如果
,则令
。这种方法并不能真的让Discriminator变成1-Lipschitz Function,虽然它在一定程度上确实让Discriminator变得平滑。然后Improved WGAN中又提出了一种新的方法,叫做Gradient Penalty。就是从
和
选取两个点,连成直线,从直线间sample当作
中的x;为什么可以只对中间部分penalty呢?因为我们是想让
往
移动,
是看着中间部分的斜率移动的,只有中间的区域才会影响结果,才需要考虑Discriminator的形状。
3 GAN is still challenge
Discriminator无法分辨差异,Generator就无法生成更准确的图片,二者其中一个无法进步时另一个也无法进步了,而且训练中无法调整超参数。
3.1 GAN for Sequence Generation
当将GAN应用于Sequence Generation任务时,难度会更加巨大。Decoder的参数发生小变化的时候,可能并不会对token的最大输出产生影响,所以Discriminator的结果可能是没有变化的,这种情况下没有办法做Gradient Descent。在做max polling的时候,我们知道哪个元素导致了这个最大值,在做Back Propagation的时候,只需要考虑这一个元素即可,其他元素置零。而对于GAN来说,我们不知道是哪个元素导致了这些字的输出。而且也不能简单地将Gradient Descent的问题直接就当做Reinforcement Learning来训练,因为RL本身也很难训练,RL+GAN就更难训练了。
正因如此,在很长一段时间内,GAN都被认为是不适合来产生一段序列,文字的,直到一篇名为Training language GANs from Scratch的发表。这篇论文能让GAN作为序列生成器进行序列的自动生成,主要用到的方法就是暴调超参数。
3.2 Evaluation of Generation
如何评估一个Generator的好坏?也许可以直接让人来评判,例如图像对抗生成网络生成的图片,如果符合人的预期则认为这个网络是优秀的,否则则是差劲的。但这种人类评判的方法不仅代价过高,而且有时候是不公平的或者说不稳定的。因此在相当长的一段时间内,关于GAN的论文都是从人类的视角来评判的,缺乏公正客观且准确的评判方式。如果针对图像对抗生成网络,我们或许可以将生成的图像作为对应的图像检测网络的输入,图像检测网络输出的图像识别的结果越准确,代表图像对抗生成网络生成的图像越真实。这只针对某种特殊的网络,放到更一般的情况呢?也就是说我们需要无论什么图像都能被准确识别。可以考虑用一个图像分类器来解决这个问题,将图像输入分类器,观察分类器输出的概率分布结果,概率越集中在哪个分类,代表分类器越肯定这个图像是一个什么图像,反之则表明它很困惑,无法识别图像的分类。
3.3 Mode Collapse
但是这样也有一些问题,就是生成的数据可能集中在某部分区域,比如下面两种情况。看下图你可能会发现,有一个相似的头像图片反复出现,这就是generated data集中在某一个real data周围的情况,这个区域可以认为是discriminator的“盲区”,区域内的图片判定为真的可能性大,因此generator“投机取巧”,反复生成这种图片。至今还未有很好的办法解决这个问题。
3.4 Mode Dropping
除了Mode Collapse,还有Mode Dropping的问题存在。如下图所示,如果看左边的数据分布,会觉得generated data还不错,但其实real data的多样性不止于此,还有右边部分的分布。初看生成图像集感觉还不错,但仔细观察发现生成的人脸都太白了,实际上它还可以生成其他肤色的人脸。实际上就算是现在最好的GAN模型都有可能存在这个问题。
4 相关代码
遗传算法(GA)求解函数的最大值和最小值。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3DDNA_SIZE = 24
POP_SIZE = 200
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.005
N_GENERATIONS = 50
X_BOUND = [-3, 3]
Y_BOUND = [-3, 3]def F(x, y):return 3 * (1 - x) ** 2 * np.exp(-(x ** 2) - (y + 1) ** 2) - 10 * (x / 5 - x ** 3 - y ** 5) * np.exp(-x ** 2 - y ** 2) - 1 / 3 ** np.exp(-(x + 1) ** 2 - y ** 2)def plot_3d(ax):X = np.linspace(*X_BOUND, 100)Y = np.linspace(*Y_BOUND, 100)X, Y = np.meshgrid(X, Y)Z = F(X, Y)ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm)ax.set_zlim(-10, 10)ax.set_xlabel('x')ax.set_ylabel('y')ax.set_zlabel('z')plt.pause(3)plt.show()def get_fitness(pop):x, y = translateDNA(pop)pred = F(x, y)return (pred - np.min(pred)) + 1e-3 # 减去最小的适应度是为了防止适应度出现负数,通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],最后在加上一个很小的数防止出现为0的适应度def translateDNA(pop): # pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目x_pop = pop[:, 1::2] # 奇数列表示Xy_pop = pop[:, ::2] # 偶数列表示y# pop:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1)x = x_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (X_BOUND[1] - X_BOUND[0]) + X_BOUND[0]y = y_pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2 ** DNA_SIZE - 1) * (Y_BOUND[1] - Y_BOUND[0]) + Y_BOUND[0]return x, ydef crossover_and_mutation(pop, CROSSOVER_RATE=0.8):new_pop = []for father in pop: # 遍历种群中的每一个个体,将该个体作为父亲child = father # 孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)if np.random.rand() < CROSSOVER_RATE: # 产生子代时不是必然发生交叉,而是以一定的概率发生交叉mother = pop[np.random.randint(POP_SIZE)] # 再种群中选择另一个个体,并将该个体作为母亲cross_points = np.random.randint(low=0, high=DNA_SIZE * 2) # 随机产生交叉的点child[cross_points:] = mother[cross_points:] # 孩子得到位于交叉点后的母亲的基因mutation(child) # 每个后代有一定的机率发生变异new_pop.append(child)return new_popdef mutation(child, MUTATION_RATE=0.003):if np.random.rand() < MUTATION_RATE: # 以MUTATION_RATE的概率进行变异mutate_point = np.random.randint(0, DNA_SIZE * 2) # 随机产生一个实数,代表要变异基因的位置child[mutate_point] = child[mutate_point] ^ 1 # 将变异点的二进制为反转def select(pop, fitness): # nature selection wrt pop's fitnessidx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,p=(fitness) / (fitness.sum()))return pop[idx]def print_info(pop):fitness = get_fitness(pop)max_fitness_index = np.argmax(fitness)print("max_fitness:", fitness[max_fitness_index])x, y = translateDNA(pop)print("最优的基因型:", pop[max_fitness_index])print("(x, y):", (x[max_fitness_index], y[max_fitness_index]))if __name__ == "__main__":fig = plt.figure()ax = Axes3D(fig)fig.add_axes(ax)plt.ion() # 将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行plot_3d(ax)pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE * 2)) # matrix (POP_SIZE, DNA_SIZE)for _ in range(N_GENERATIONS): # 迭代N代x, y = translateDNA(pop)if 'sca' in locals():sca.remove()sca = ax.scatter(x, y, F(x, y), c='black', marker='o');plt.show();plt.pause(0.1)pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))# F_values = F(translateDNA(pop)[0], translateDNA(pop)[1])#x, y --> Z matrixfitness = get_fitness(pop)pop = select(pop, fitness) # 选择生成新的种群print_info(pop)plt.ioff()plot_3d(ax)
运行结果:
这篇关于机器学习周记(第十七周:GAN.pt2)2023.11.13~2023.11.19的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!