Autoencoder自动编码器的发展

Autoencoder自动编码器的发展Autoencoder自动编码器的发展0、玻尔兹曼机中的测试实验——编码问题(1985)0.1、玻尔兹曼机0.2、受限的玻尔兹曼机0.3、编码问题——自动编码器雏形1、反向传播中的仿真——单层自动编码器(1986)2、利用神经网络进行数据降维——深度自动编码器(2006)3、去噪自编码器(2008)4、稀疏自编码器(2011)5、卷积自编码器(2011)6、变分自编码器(2013)6.1、模型6….

大家好,又见面了,我是你们的朋友全栈君。

算是一个综述性质的东西。

动机是实验室有缺陷检测的项目,找了几篇最新的文章,其中用到了自动编码器,有项目的原因也有自己好奇的原因,一路追溯到1985年的文章,再加上我能找到的很多博客都没挖到这里,就有了写这篇博客的想法。

文章都在参考文献里,有小飞机可以直达,没有的话不能保证。另外,这些文章只是我个人觉得比较有代表性的而已,中间还有很多研究没有放进来,这十几篇文章仅供参考。

0、玻尔兹曼机中的测试实验——编码问题(1985)

0.1、玻尔兹曼机

玻尔兹曼机[13]最早由Hinton与Sejnowski在1985年发明,大概长这样:


Autoencoder自动编码器的发展

它是最早能够学习内部表达,并能表达和(给定充足的时间)解决复杂的组合优化问题的神经网络。但是,没有特定限制连接方式的玻尔兹曼机目前为止并未被证明对机器学习的实际问题有什么用。所以它目前只在理论上显得有趣。

0.2、受限的玻尔兹曼机

受限的玻尔兹曼机(restricted Boltzmann machine, RBM)[14]在1986年由Paul Smolensky发明,最早被命名为簧风琴(Harmonium),直到Hinton及其合作者在2000年代中叶发明快速学习算法后,受限玻尔兹曼机才变得知名,它大概长这样:


Autoencoder自动编码器的发展

与原始的玻尔兹曼机相比,区别在于这是一个对称的二分图,可见层与隐藏层可以相互转换,它们共享W权重,但是拥有属于自己的偏置b与c,相比一般玻尔兹曼机,这样的限定使得更高效的训练算法成为可能。

0.3、编码问题——自动编码器雏形

为了解决玻尔兹曼机学习困难的问题,还是Hinton与Sejnowski,两人又发明了一种针对玻尔兹曼机的学习算法[1],从文章的名字来看就很明显:《A Learning Algorithm for Boltzmann Machines*》。
就是在这篇文章里,为了测试这个针对玻尔兹曼机的学习算法,自动编码器的雏形被提出来了,在这篇文章的第4章,它被称为The encoder problem:


Autoencoder自动编码器的发展
Autoencoder自动编码器的发展

左边是4-2-4编码器,右边是4-3-4编码器。问题被设计为,从上到下分别被称为V1层,H层,V2层,V1、V2层都与H层连接,但是它们自己相互之间没有连接,这时,希望两个可见层V1与V2能够沟通彼此的状态,每个阴影盒子被称为一个单元,单元中的黑色方块为负权重,白色方块为正权重,而这个问题的学习是从所有权重均为0的情况下开始学习的,没有任何先验知识。这两幅图都是经过一定数量的循环学习后得到的。

我看了很久也觉得没有完全参透这几个图,下面是我的一些观察。
直观来看,如左图下方的V2可见层,从左到右4个单元中,中间权重的颜色分别是(白,白)、(黑,黑)、(白,黑)、(黑,白),可以把它们视为不同的状态,接着它们被压缩成中间的H层,再解压到V1层,最后可以看到,V1与V2对应的单元中,对应位置的权重基本是相同的。
同时,以左图H层中左边的单元为例,顶部与底部的4个权重可以看成(1,0,1,0),中间的两个权重可以看成(0.3,0),这就很有我们所知道的自编码器的味道了。
这个算法学习的是黑白方块的权重,每个阴影盒子本身即可视为一个网络,但是这里又把每个阴影盒子作为一个状态,组成了一个更大的网络,这小网络叠成大网络是怎么操作的,实在是没看出来。

如果要把玻尔兹曼机系列参透,完全可以开另一个专题,由于这里的主题是自编码器,而且限于限于能力与精力,这个问题我就看到这里了。
所以,自动编码器的雏形,是在一个玻尔兹曼机的测试实验中产生的。那为什么测试玻尔兹曼机会用到这样的结构呢?让我们再看一眼这个4-2-4编码器,如果我们把它在隐藏层上下折叠起来,是不是就变成了上面的受限的玻尔兹曼机(RBM),又因为RBM是一个特殊的玻尔兹曼机,所以就有了玻尔兹曼机(对称、二分)→受限的玻尔兹曼机(较小的层展开)→自动编码器雏形
作者应该是想表达:如果这个算法能够将编码问题学习出比较好的结果,那么它也能学习玻尔兹曼机。

1、反向传播中的仿真——单层自动编码器(1986)

还是1986年,还是Hinton,在这一年,发表有两篇文章。
一篇是《Learning representations by back-propagating errors(1986a)》[2],发表在《Nature》上,至今被引用了17908次。
另一篇是《Learning internal representations by back propagation(1986b)》[3],发表在一本叫《Neurocomputing: foundations of research》的书上,至今被引用了24602次。
第一篇描述了反向传播算法在神经网络中的应用,短短4页开启了一个时代。
第二篇文章相当于第一篇文章的一个补充,它旨在证明一件事:反向传播算法可以让神经网络发现自身数据的内部表示

在这里要讲的是第二篇文章[3]——利用反向传播学习内部表征。
在这篇文章里,为了证明反向传播算法可以让神经网络发现自身数据的内部表示,Hinton他们做了好几个问题的仿真,比如异或门问题,对称问题,否定问题等,我们的编码问题也在其中:


Autoencoder自动编码器的发展
Autoencoder自动编码器的发展

这个模型是从第0节提到的那篇文章中提炼出来的——一组正交输入通过一个更小的隐藏层映射到另外一组正交输出。而输入等于输出,仅仅是这里的一个设定,为了看看利用反向传播训练稳定后,隐藏层会表现出一个怎样的情况,是否能表达其内部表征
经过学习得到的是右图的Table5,这表示这个输出等于输入的系统是利用了中间值(intermediate values)来处理这个问题的。原文中还有更多的论述,有兴趣的小伙伴可以读一读原文。在我看来,它出现在这里的目的仅仅是为了给论点——反向传播算法能够学习内部表征——提供论据而已:你们看,对这个编码问题,我们学习出了这样有趣的内部结构,巴拉巴拉(一通分析),所以,我们的论点是成立的。(像这样的仿真在文章里还有7个。)

尽管在这个时候,编码问题仍然只是一个测试,还没有实际的应用,但是最早提出这样简单明确的“大-小-大”的编码-解码网络结构,加上“输出等于输入”这个设定,“自动编码器”算是真正出生了。

2、利用神经网络进行数据降维——深度自动编码器(2006)

时间来到2006年,还是Hinton,这次是和他的一位学生Salakhutdinov,发表了一篇《reduce the dimensionality of data with neural networks》[4]——神经网络的数据降维,在这里用到的网络结构正是深度自编码器。

这篇文章主要做了两件事情:

1、从单层自编码器变成深度自编码器,做到了有效的数据降维,功能类似于PCA,但是比PCA灵活,不仅能表征线性变换,也能表征非线性变换。
2、从1986年到2006年,虽然有反向传播算法,但是这段时间里对深度网络的训练仍然存在两个问题,一个是梯度消失,另一个是当时的权重W都是随机初始化的——这是一个坏主意,给大了容易陷入局部极值,给小了训练困难(慢)。这篇文章提出了“无监督预训练+有监督微调”的方法,先把权重W预训练到一个比较好的数值上,使梯度下降能良好地工作,再整体微调,解决了W初始化的问题,同时也初步解决了梯度消失的问题。


Autoencoder自动编码器的发展

上图就是这篇文章的关键步骤展示:

Pretrainning(预训练):
无监督预训练用到的正是上面提到的受限制的玻尔兹曼机RBM(见0.2,图就不再贴了)。上图左起第一列的pretraining,最下面一组图表示,把一张人脸图(假设是100*100,展开成10000维的向量)压缩到2000维的向量,权重是w1,这时候可以把人脸的10000维向量看成0.2图中的V层(可见层),把2000维视为H层(隐藏层),再注意到w1旁边的箭头是双向的,即可见层与隐藏层是可以相互转换的,训练在这两层间不断进行。
RBM这里的训练目标是最小化它的能量:


Autoencoder自动编码器的发展

bi、bj是偏置,vi、hj是可见层、隐藏层的向量,wij是它们共同的权重。

RBM是一个能量模型也是一个概率模型,最小化这个函数其中也还有很多细节,其中的门道在这里不细讲,直接讲一下结论:系统越有序或者概率分布越集中,系统的能量越小。反之,系统越无序或者概率分布越趋于均匀分布,则系统的能量越大。
能量函数的最小值,对应于系统的最稳定状态,则此时的2000维特征向量能最稳定地表达这10000维像素向量,故此时的w1是一个比较良好的值(而为什么只是“良好”,因为这里连带偏置bi、bj也训练出来了,我们却只需要权重wij,而且这只是深度网络中的其中一层)。
当2000维特征训练好后,从2000维到1000维,从1000维到500维,从500维到30维,同理依次训练出w2、w3、w4,这样,深度自编码器编码部分的每层都有了较好的初始权重(解码部分的初始权重为其转置)。

Unrolling(展开):
在上图的第二列,很明显,这里只是把预训练得到的初始权重放在各层之间,搭起了一个编码-解码的网络,注意到这里encoder与decoder的虚线框是不对齐的,因为这里只取了预训练的权重,没有取偏置,所以最后一步就是微调偏置。

Fine-tuning(微调):
最后,全局微调阶段通过确定性实值(即原图展开的向量)替换随机活动,并通过整个自动编码器使用反向传播来微调权重以进行最佳重建。

到这里,我忍不住吐槽一下,从1985年为了找到内部表征的玻尔兹曼机,到1986年反向传播算法解决了非线性分类与学习的问题(但是也出现了梯度消失的问题),到2006年“无监督预训练+有监督微调”初步解决梯度消失的问题,自动编码器发展到这里,看起来就像是深度学习发展中的副产物。(ps:另外,2011年relu函数基本解决了梯度消失问题,12年后基本就放弃了“预训练+微调”的方法,再到15年的残差网络,网络已经变得很深,然而后续也发现,深度网络训练不好不完全是梯度消失的问题。To be continue…)

但是从06年的这个版本之后,自动编码器有了种自立门户的感觉。下文是几种编码器的发展以及它与其他一些东西的结合,有了自己的应用,而且,与其说它是一种结构,它更倾向于是一种思想。

3、去噪自编码器(2008)

去噪自编码器最早是在2008年由Pascal Vincent(终于不是Hinton了)的团队提出:《Extracting and Composing Robust Features with Denoising Autoencoders》[5],这时候还是一个单层的去噪编码器,还是这个团队的同一拨人,2010年发表了深度去噪编码器:《Stacked Denoising Autoencoders: Learning Useful Representations in a Deep Network with a Local Denoising Criterion》[6]。两篇文章对去噪编码器本身的论述基本是一样的,只是10年的文章做成了深度的,而且增加了很多介绍、证明、实验。

去噪自编码器:顾名思义,在训练时加入噪声,在噪声中重建。
去噪自编码器的观点:我们的目的不是去噪本身,而是通过去噪来建立一个训练准则学习如何找出有用的特征,噪声中可以重建出更鲁棒更稳定的结果。
下面介绍深层去噪自编码器是怎么堆叠起来的。
单层:


Autoencoder自动编码器的发展

x是输入向量(待编码向量),z是输出向量(解码向量),y是压缩向量, x ^ \hat{x} x^ 是受污染的向量(维数与x相同)。
qD是污染数据的过程,fθ是编码过程,gθ’是解码码过程,LH(x,z)是损失函数。
常用污染方式:二项式噪声(翻转黑白)、高斯噪声等。
将x污染(大概污染30%)成 x ^ \hat{x} x^,把 x ^ \hat{x} x^编码成y,再把y解码成z,损失函数为未受污染的初始输入x与解码输出z之间的误差。

多层:


Autoencoder自动编码器的发展

左边第一层使用上图单层的方式训练完毕后,其输出z作为下一层的输入,在训练的时候再污染输入,得到输出,层层训练(每一层都污染输入)。

整体:


Autoencoder自动编码器的发展
层层堆叠起来后,在最后一层的f
θ
sup加上一个有监督的全连接层,并与Target一起做一个损失函数,用于分类训练、预测。

直观的例子:


Autoencoder自动编码器的发展

4、稀疏自编码器(2011)

《Sparse autoencoder》[7],Ng给CS294A写的讲义(是熟悉的味道),比较令我惊讶的是,这份讲义居然有500多次的引用(Ng:妹想到吧)。
稀疏编码器的观点:稀疏编码器可以学习出比人工选择更好的特征,稀疏“过完备”也是一种压缩方式。
稀疏“过完备”:常规来说,编码器隐藏层中神经元节点的数量小于可见层,但是在这个稀疏编码器里,隐藏层的神经元节点是“过完备”的,它表面上的数量可以超过可见层,但是,在稀疏操作下,实际被激活的极少,这可以视为一种压缩。

借用这份讲义中的图,这是一个自动编码器(在稀疏编码器中,你可以认为LayerL2层有100个神经元节点,而不止图中的3个):


Autoencoder自动编码器的发展

好,现在这里有一个稀疏自编码器的损失函数,读懂了这个损失函数,我们就知道稀疏自编码器在干什么了:


Autoencoder自动编码器的发展

加号前后的两个式子我在下面分别称之为J(W,b)与KL散度,这个KL散度在这里被称为稀疏惩罚项,β用来控制稀疏惩罚的力度。

首先,J(W,b)展开的形式为:


Autoencoder自动编码器的发展

这里,前半项为一个常见的平均平方和误差项,后半项是一个减小权重大小的正则项(防止过拟合),其中λ是一个权重衰减参数,用来控制权重惩罚的力度,而后者的三个连加,表示将这个网络中出现的每一个权重项,自平方再累加。

什么意思呢:我希望这个编码器的输出和输入尽量相似(最好相等),同时我还希望网络中的权重尽可能小。

这里看起来还是一个正常的自编码器,那稀疏是怎么做到的呢,我们来看Jsparse(W,b)的后一项, ρ ^ \hat{\rho} ρ^ j j j ρ \rho ρ的KL散度,它展开来是这样的:


Autoencoder自动编码器的发展

其中:


Autoencoder自动编码器的发展

s2表示隐藏层中神经元的个数,ρ是一个设定的稀疏参数(假设ρ=0.05),m是样本个数,aj(2)(x(i))表示样本x(i)的隐藏层LayerL2中的节点j是否被激活(激活函数为sigmiod函数)。

ρ ^ \hat{\rho} ρ^ j j j表示:在样本集合中,隐藏层的节点j被激活的样本数,占总样本的多少,即节点j在训练数据集上的平均激活率。
KL散度在这里则是在衡量 ρ ^ \hat{\rho} ρ^ j j j ρ \rho ρ的相似程度 ρ \rho ρ已经假设为0.05, ρ ^ \hat{\rho} ρ^ j j j与0.05越接近,其惩罚越小:


Autoencoder自动编码器的发展

这是 ρ \rho ρ为0.2时的情况,可以看到,只有当 ρ ^ \hat{\rho} ρ^ j j j也为0.2, ρ ^ \hat{\rho} ρ^ j j j ρ \rho ρ的KL散度值才为0(最小),在其他情况下都会出现正值,加在损失函数里即变成了惩罚。
那么,这KL散度在这里是什么意思呢:我希望隐藏层的每个节点的激活率都与我设定的稀疏参数 ρ \rho ρ相同(如这里的0.05),这样,隐藏层里实际工作的节点就很少(100个里面只有5个被激活了),从而实现了稀疏。

下面是稀疏自编码器的技术总结。
可以先假设这个自编码器的隐藏层中,神经元节点数比可见层要多得多,然后进行训练的时候保证三点:
1、输出尽量等于输入
2、所有神经元的权重都尽可能地小
3、隐藏层中被激活的神经元的比率尽量跟设定的稀疏参数(非常小)相等

这里其实有个启发,上面的第一点是常规的均方差误差,第二和第三点都是特殊的约束,只不过在这里,加完相应的约束之后,它是一个稀疏自编码器。也有人把这里的约束改成了其他的东西,于是有了不同的功能,然后发了文章,比如收缩自编码器等,在这里我觉得它们是同一个类型的东西,就不再看这类相似的文章了。

5、卷积自编码器(2011)

《Stacked Convolutional Auto-Encoders for Hierarchical Feature Extraction》[8],我能找到的最早的卷积自编码器,作者是Jonathan Masci,至今有800多次的引用。在今天看来,这篇文章有些索然无味,但是结合当时的情况来看,还是比较合理了。

文章的贡献:我们用卷积自编码器来做特征提取,并且,我们不需要使用任何规则项(约束)就能做到稀疏,因为我们使用了maxpooling(当时,我的脑子里缓缓打出了一个问号)。

卷积(编码):


Autoencoder自动编码器的发展

反卷积(解码):


Autoencoder自动编码器的发展

损失函数(均方差函数,2n的n是为了求导消去):


Autoencoder自动编码器的发展

损失函数对权重的偏导(反向传播更新权重):


Autoencoder自动编码器的发展

这个偏导里面的δh跟δy,文章里面只有一句:δh and δy are the deltas of the hidden states and the reconstruction, respectively.
我就不参悟了。

上面这些式子都是文章里的内容,咋一看,就是一个普普通通的卷积自编码器,但是结合当时的历史情况,这篇文章还是有几个有意思的地方。
首先,2012年Hinton(Hello?)课题组才首次参加ImageNet图像识别比赛,其通过构建的CNN网络AlexNet一举夺得冠军,且碾压第二名(SVM方法)的分类性能,自此,CNN才吸引到了众多研究者的注意。因此,卷积自编码器发表的2011年,卷积网络本身就还不太受关注,而且自编码器也才刚开始发展,作者在这种情形下能想到把两者结合起来,也是比较厉害了。
第二,在上面的稀疏自编码器有提到,当时流行的是在损失函数里加正则项来进行各种约束,而作者可能就是看到了卷积一般都自带maxpooling,正好有稀疏的功能,而且不用大费周章地去设计、调试正则项,所以才有了这篇文章(个人猜测)。
第三,2011年还没有relu激活函数,所以还是“预训练-微调”的训练方法,而这里预训练用的不是06年的那个RBM预训练方法,而是10年的去噪编码器——加入噪声后预训练每一层的权重,上一层的输出是下一层的输入。
作者在2011年把上述要素结合起来,是一篇不错的文章了。
回到现在,不管是卷积自编码器还是去噪自编码器,在各种强大的框架之下,都是小几十行就能实现的东西,而且不用像以前那样预训练,只需要框架搭好数据一填一把梭跑起来等结果。它们现在都是很基础很简单的东西了,在很多博文里,它们经常作为baseline一样的东西来介绍自编码器。
总之吧,在今天看来,这篇文章确实有些乏善可陈,卷积(编码)-反卷积(解码),over。

6、变分自编码器(2013)

变分自编码器(Variational auto-encoder,VAE)是一类重要的生成模型(generative model),文章《Auto-Encoding Variational Bayes》[9]于2013年底(2013年12月20)由Diederik P.Kingma和Max Welling发表。
变分自编码器我看了挺久,因为本身比较难懂,网上的很多中文资料也显得比较混乱,最后我是在《Tutorial on Variational Autoencoders》[15](2016年Carl Doersch写了一篇VAEs的tutorial,对VAEs做了更详细的介绍,更好理解)这篇文章里大概地把推理过程理顺的。
下面我也要为混乱的变分自编码器解读教程添砖加瓦了。

6.1、模型

变分自编码器的提出动机是:我们希望学习出一个模型,能产生训练样本中没有,但与训练集相似的数据
变分自编码器的最终实现的模型比较简单,借用Tutorial里的一张图就能理解:


Autoencoder自动编码器的发展

先看左图, X X X是数据集,首先通过编码器学习出数据的均值 μ ( X ) \mu(X) μ(X)与方差 Σ ( X ) \Sigma(X) Σ(X),同时希望这个 μ ( X ) \mu(X) μ(X) Σ ( X ) \Sigma(X) Σ(X)构成的正态分布与标准正态分布尽可能接近(用KL散度衡量),学习出这个正态分布之后,再从这个分布里面取样 z z z(注意,这个 z z z X X X中没有,但它又是符合 X X X的分布的),再把 z z z放进解码器中解码,同时希望解码出来的样本与原始样本 X X X尽可能相同。
这样, z z z相当于从 X X X数据分布中采样而来,再经解码器还原,就做到了“产生训练样本中没有,但与训练集相似的数据”。
右图是“重参数技巧”,因为“采样”这个动作本身不连续不可导(这个数据你取还是不取?那一个呢?),但是采样的结果是连续可导的,因此这里从标准正态分布 N ( 0 , 1 ) N(0,1) N(0,1)中采样出 ϵ \epsilon ϵ,这个 ϵ \epsilon ϵ就是标准正态分布中的一个值,如0.9856,再令 z = μ ( X ) + Σ 1 / 2 ( X ) ∗ ϵ z=\mu(X)+\Sigma^{1/2}(X)*\epsilon z=μ(X)+Σ1/2(X)ϵ z z z N ( μ ( X ) , Σ ( X ) ) N(\mu(X),\Sigma(X)) N(μ(X),Σ(X))中的位置相当于 ϵ \epsilon ϵ N ( 0 , 1 ) N(0,1) N(0,1)中的位置,这就巧妙地把采样不连续不可导的问题解决了,其他与左图相同。

好了,下面是推理部分,我试着讲一讲,有兴趣的同学试着看一看。

6.2、推理过程

目标

首先我们有一批数据 { X 1 , X 2 , X 3 , . . . , X n } \{X1,X2,X3,…,Xn\} {
X1,X2,X3,...,Xn}
,整体用 X X X来表示,如果我们能从 X X X中得到它的分布 P ( X ) P(X) P(X),那么我们就能得到包括 { X 1 , X 2 , X 3 , . . . , X n } \{X1,X2,X3,…,Xn\} {
X1,X2,X3,...,Xn}
以外的所有 X X X,比如我有标准正态分布的100个数,我算出了它的分布,那么我可以得到这100个数以外的所有数值。

但是实际上这很难做到,因为这需要极大的样本量,同时我们不知道这批数据服从什么分布,而且这批数据中可能有离群点,如手写数字图片里有个样本一半是0另一半是8。因此预先定义好要生成什么东西是很有帮助的,如在手写数字识别里预先定义只生成0到9的数字。

这样的预定义/假设/先验知识,可以称之为隐变量

变分自编码器是想生成训练样本中没有,但与训练集相似的数据,现在我们假设所有生成的 X X X,都能由隐变量 z z z映射得到: X = f ( z ; θ ) X=f(z;\theta) X=f(z;θ)。然后用一个分布 P ( X ∣ z ; θ ) P(X|z;\theta) P(Xz;θ)代替 X = f ( z ; θ ) X=f(z;\theta) X=f(z;θ),前者表示:在某分布下(在这里, P P P分布与 f f f函数是同一个东西),参数 θ \theta θ固定的情况下, z z z生成 X X X的概率。然后写出我们的生成过程:
P ( X ) = ∫ P ( X ∣ z ; θ ) P ( z ) d z P(X) = \int P(X|z;\theta) P(z)dz P(X)=P(Xz;θ)P(z)dz
需要提示一下的是,看一眼上面变分自编码器的模型图,Decoder下写着一个P,Decoder的作用正好是把 z z z还原成 X X X,所以,在这里 P ( X ∣ z ) P(X|z) P(Xz)可以理解成由 z z z生成 X X X的解码器。
其中,解码器的分布选择为正态分布: P ( X ∣ z ; θ ) = N ( X ∣ f ( z ; θ ) , σ 2 ∗ I ) P(X|z;\theta)=N(X|f(z;\theta),\sigma^2*I) P(Xz;θ)=N(Xf(z;θ),σ2I) f ( z ; θ ) f(z;\theta) f(z;θ)是均值, σ 2 ∗ I \sigma^2*I σ2I是方差。
P ( z ) P(z) P(z)则假设为标准正态分布: P ( z ) = N ( z ∣ 0 , I ) P(z)=N(z|0,I) P(z)=N(z0,I)
不幸的是,由于 P ( X ) P(X) P(X)很难计算,我们才把这个积分展开,但是,我们却不能真的直接将全部的数据都用于模型的训练,因为其中会有不符合模型分布的点,这些点不应该对模型有贡献。

变分自编码器的关键点在于,我们应该从那些更能生成 X X X的数据中采样 z z z,并仅用这些数据计算 P ( X ) P(X) P(X)。这意味着我们需要引入一个新的分布 Q ( z ∣ X ) Q(z|X) Q(zX),可以用一个 X X X取得一个更倾向于生成 X X X z z z
注意了,在上面的变分自编码器模型图中,Q是写在Encoder下面的,再结合Encoder的作用是把 X X X压缩成 z z z,所以在这里, Q ( z ∣ X ) Q(z|X) Q(zX)可以理解成将 X X X压缩成 z z z的编码器。

到这里,我们希望 E z ∼ Q P ( X ∣ z ) E_{z\sim Q}P(X|z) EzQP(Xz) P ( X ) P(X) P(X)尽可能相似。
P ( X ) P(X) P(X)表示原始数据集的分布。
E z ∼ Q P ( X ∣ z ) E_{z\sim Q}P(X|z) EzQP(Xz)表示:在 P P P分布下,我的 X X X是由 z z z产生的,同时 z z z服从 Q Q Q分布,而在 Q Q Q分布中, z z z是由 X X X产生的——这其实就是一个 X X X编码成 z z z z z z解码成 X X X的过程。
希望 E z ∼ Q P ( X ∣ z ) E_{z\sim Q}P(X|z) EzQP(Xz) P ( X ) P(X) P(X)尽可能相似,意味着希望自编码器编码-解码生成的数据与原始数据相同,这是自动编码器的初衷,而 z z z的采样则给整个系统带了一些不确定因素,但是 z z z又是服从原始数据的分布的,这就达成了变分自编码器“能产生训练样本中没有,但与训练集相似的数据”的意图。

公式推导

这里有点突兀,Tutorial里说,要理解 E z ∼ Q P ( X ∣ z ) E_{z\sim Q}P(X|z) EzQP(Xz) P ( X ) P(X) P(X)的关系,要先从 Q ( z ) Q(z) Q(z) P ( z ∣ X ) P(z|X) P(zX)说起。但是,这是两个什么东西呢?
上面说到 Q ( z ∣ X ) Q(z|X) Q(zX)可以理解成将 X X X压缩成 z z z的编码器,但是这里的 Q ( z ) Q(z) Q(z)可以是任何分布;
P ( X ∣ z ) P(X|z) P(Xz)可以理解成由 z z z生成 X X X的解码器,但是这里的 P ( z ∣ X ) P(z|X) P(zX),文中只提到说“它描述了z的值,这些值很可能在前面的模型 P ( X ) = ∫ P ( X ∣ z ; θ ) P ( z ) d z P(X) = \int P(X|z;\theta) P(z)dz P(X)=P(Xz;θ)P(z)dz 下产生了像X这样的样本”,并说 P ( z ∣ X ) P(z|X) P(zX)不是一个可以计算的东西。
虽是这样说,但是经过文中的推导, Q ( z ) Q(z) Q(z) P ( z ∣ X ) P(z|X) P(zX)都被别的可计算的东西替换掉了。
下面来看推导过程, D D D表示KL散度,衡量两个分布之间的相似度,有公式:
D [ P ( x ) ∣ ∣ Q ( x ) ] = 1 m ∑ m P ( x ) ∗ log ⁡ P ( x ) Q ( x ) = E x ∼ P log ⁡ P ( x ) Q ( x ) = E x ∼ P [ log ⁡ P ( x ) − log ⁡ Q ( x ) ] \begin{aligned} D[P(x)||Q(x)] &= \frac{1}{m} \sum \limits_{m} P(x)* \log \frac{P(x)}{Q(x)} \\ &=E_{x\sim P}\log \frac{P(x)}{Q(x)} \\ &=E_{x\sim P}[\log P(x)-\log Q(x)] \end{aligned} D[P(x)Q(x)]=m1mP(x)logQ(x)P(x)=ExPlogQ(x)P(x)=ExP[logP(x)logQ(x)]
同理有:
D [ Q ( z ) ∣ ∣ P ( z ∣ X ) ] = E z ∼ Q [ log ⁡ Q ( z ) − log ⁡ P ( z ∣ X ) ] = E z ∼ Q [ log ⁡ Q ( z ) − log ⁡ ( P ( X ∣ z ) P ( z ) P ( X ) ) ] = E z ∼ Q [ log ⁡ Q ( z ) − log ⁡ P ( X ∣ z ) − log ⁡ P ( z ) ] + log ⁡ P ( X ) \begin{aligned} D[Q(z)||P(z|X)] &= E_{z\sim Q}[\log Q(z)-\log P(z|X)] \\ &=E_{z\sim Q}[\log Q(z)-\log (\frac{P(X|z)P(z)}{P(X)})] \\ &=E_{z\sim Q}[\log Q(z)-\log P(X|z) – \log P(z)] + \log P(X) \end{aligned} D[Q(z)P(zX)]=EzQ[logQ(z)logP(zX)]=EzQ[logQ(z)log(P(X)P(Xz)P(z))]=EzQ[logQ(z)logP(Xz)logP(z)]+logP(X)
因为 P ( X ) P(X) P(X) z z z无关,对 z z z来说 log ⁡ P ( X ) \log P(X) logP(X)是个常数,所以提出来了,接着将上式移项,得到:
log ⁡ P ( X ) − D [ Q ( z ) ∣ ∣ P ( z ∣ X ) ] = E z ∼ Q [ log ⁡ P ( X ∣ z ) ] − D [ Q ( z ) ∣ ∣ P ( z ) ] \begin{aligned} \log P(X) – D[Q(z)||P(z|X)] &= E_{z\sim Q}[\log P(X|z)]-D[Q(z)||P(z)] \end{aligned} logP(X)D[Q(z)P(zX)]=EzQ[logP(Xz)]D[Q(z)P(z)]
再用 Q ( z ∣ X ) Q(z|X) Q(zX)代替 Q ( z ) Q(z) Q(z),有:
log ⁡ P ( X ) − D [ Q ( z ∣ X ) ∣ ∣ P ( z ∣ X ) ] = E z ∼ Q [ log ⁡ P ( X ∣ z ) ] − D [ Q ( z ∣ X ) ∣ ∣ P ( z ) ] = E z ∼ Q [ log ⁡ P ( X ∣ z = μ ( X ) + Σ 1 / 2 ( X ) ∗ ϵ ) ] − D [ Q ( z ∣ X ) ∣ ∣ P ( z ) ] \begin{aligned} \log P(X) – D[Q(z|X)||P(z|X)] &= E_{z\sim Q}[\log P(X|z)]-D[Q(z|X)||P(z)]\\ &=E_{z\sim Q}[\log P(X|z=\mu(X)+\Sigma^{1/2}(X)*\epsilon)]-D[Q(z|X)||P(z)] \end{aligned} logP(X)D[Q(zX)P(zX)]=EzQ[logP(Xz)]D[Q(zX)P(z)]=EzQ[logP(Xz=μ(X)+Σ1/2(X)ϵ)]D[Q(zX)P(z)]
等号左边表示,我们要最大化 log ⁡ P ( x ) \log P(x) logP(x)并同时最小化 D [ Q ( z ∣ X ) ∣ ∣ P ( z ∣ X ) ] D[Q(z|X)||P(z|X)] D[Q(zX)P(zX)],但是 log ⁡ P ( x ) \log P(x) logP(x) P ( z ∣ X ) P(z|X) P(zX)都是无法计算的,但是等号右边的东西都是可以获取并计算的:
其中 Q ( z ∣ X ) = N ( z ∣ μ ( X ; θ ) , Σ ( X ; θ ) ) Q(z|X)=N(z|\mu(X;\theta),\Sigma(X;\theta)) Q(zX)=N(zμ(X;θ),Σ(X;θ)) μ \mu μ Σ \Sigma Σ是编码器从数据中学习到的,并且 Q ( z ∣ X ) Q(z|X) Q(zX)的分布要与 P ( z ) P(z) P(z)相似, P ( z ) P(z) P(z)则在前面就被假设为标准正态分布: P ( z ) = N ( z ∣ 0 , I ) P(z)=N(z|0,I) P(z)=N(z0,I),即 Q ( z ∣ X ) Q(z|X) Q(zX)其实是与标准正态分布相似的。
P ( X ∣ z ) P(X|z) P(Xz) z = μ ( X ) + Σ 1 / 2 ( X ) ∗ ϵ z=\mu(X)+\Sigma^{1/2}(X)*\epsilon z=μ(X)+Σ1/2(X)ϵ,由从标准正态分布中采样 ϵ \epsilon ϵ并结合 μ \mu μ Σ \Sigma Σ计算得到,再用这样的 z z z生成 X X X,就可以生成与数据集类似的数据了。

论文中生成的数据:


Autoencoder自动编码器的发展

7、循环自编码器(2014)

顾名思义,循环自编码器是循环神经网络(RNN)与自动编码器的结合,最早出自《Learning phrase representations using rnn encoder-decoder for statistical machine translation》[10],在2014年由Kyunghyun Cho发表,有意思的是,在这篇文章作者列表的最后,挂有约书亚·本吉奥(Yoshua Bengio,2018年图灵奖获得者之一)的名字。
这篇文章的两个贡献是:
1、首度将encoder-decoder+RNN的结构用在了机器翻译上。
2、提出了GRU的结构,可以理解为一个改进的LSTM。
在介绍文章之前,先简述RNN与LSTM。

RNN

相对于一般的前向网络或者CNN,RNN由两个特点:
1、RNN加入了时序:当前输出于前面的输出有关。
2、RNN的输入输出结构不固定:可能有一对一、一对多、多对一、多对多的情况。
时序:


Autoencoder自动编码器的发展


Autoencoder自动编码器的发展

结构不固定:


Autoencoder自动编码器的发展

参考 RNN wiki 的描述,根据隐层 ht 接受的是上时刻的隐层(hidden layer) ht−1 还是上时刻的输出(output layer)yt−1,分成了两种 RNN,定义如下:


Autoencoder自动编码器的发展

Elman network 接受上时刻的隐层 h t − 1 h_{t−1} ht1,Jordan network 接受上时刻的输出 y t − 1 y_{t−1} yt1 ,应用得比较多的是Elman network 的做法,如“时序”下的两张图。
RNN的公式可以简单表示为:
h t = f h ( h t − 1 , x t ) g t = f g ( h t ) \begin{aligned} h_t&=f_h(h_{t-1},x_t) \\ g_t&=f_g(h_t) \end{aligned} htgt=fh(ht1,xt)=fg(ht)
f h f_h fh决定隐藏层使用什么函数,标准的RNN一般用logistic函数或者tanh函数,进阶的可以使用LSTM或者GRU等。
f g f_g fg决定输出结构,如logistic函数输出一个结果,softmax输出多个结果。
上式即为RNN的意义:当前隐藏层的状态 h t h_t ht由当前输入 x t x_t xt与前一个隐藏层状态 h t − 1 h_{t-1} ht1决定,某个时刻 t t t的输出结果 g t g_t gt由该时刻的隐藏状态 h t h_t ht决定。

LSTM

LSTM相当于一个 f h f_h fh
一个Cell的记忆内容是有限的,一般的logistic函数或者tanh函数用在RNN的隐藏层中时,当序列过长,RNN会出现“失忆”问题,前面的输入会被渐渐遗忘,只记得后面的(最新的),然而并不是越后面的越重要,如做翻译时,只关注后面几个词并不能做到准确翻译。
LSTM(Long Short Term Memory,长短期记忆)用来解决这个长期依赖问题,它能使RNN遗忘不重要的东西、记住重要的东西。
RNN:


Autoencoder自动编码器的发展

LSTM:


Autoencoder自动编码器的发展

f t f_t ft表示要遗忘的幅度,为遗忘门:


Autoencoder自动编码器的发展

i t i_t it决定更新哪些地方, C ~ t \tilde{C}_t C~t决定更新候选值, i t i_t it为输入门:


Autoencoder自动编码器的发展

C t C_t Ct为细胞状态,决定怎么做——忘掉哪些,记住哪些:


Autoencoder自动编码器的发展

o t o_t ot为细胞状态的输出, h t h_t ht为最终输出, o t o_t ot为输出门:


Autoencoder自动编码器的发展

整体公式:


Autoencoder自动编码器的发展

看着复杂,LSTM其实就是一个隐藏层函数,其能决定忘掉谁、记住谁,从而解决长期依赖问题。更详细的介绍可参考此处。

GRU

r t r_t rt为更新门,相当于LSTM中输入门与遗忘门的结合,此为改进之处, z t z_t zt为重置门, h ~ t \tilde{h}_t h~t为新信息, h t h_t ht为输出:


Autoencoder自动编码器的发展

这是这篇文章[10] 提出的结构。

RNN+Encoder-Decoder

正题来了,同时也讲得差不多了。这篇论文最早提出了RNN+Encoder-Decoder的神经网络结构,这种结构的网络能够将一个可变长度的数据序列编码成固定长度的向量,并且能够将这个向量再次解码成可变长度的数据序列。


Autoencoder自动编码器的发展

将一个可变长度的数据序列编码成固定长度的向量:

h t = f h ( h t − 1 , x t ) g t = f g ( h t ) \begin{aligned} h_{t}&=f_h(h_{t-1},x_t) \\ g_{t}&=f_{g}(h_{t}) \end{aligned} htgt=fh(ht1,xt)=fg(ht)
E E E表示Encoder,输入序列中不管有多少个数据,都会被一一压进上式中, c c c向量随着输入数据的加入不断变化,但是 c c c向量本身的长度不变,从而实现将可变长度的数据序列编码成固定长度的向量。
……
将向量解码成可变长度的数据序列:
h t = f h ( h t − 1 , y t − 1 , c ) P ( y t ∣ y t − 1 , y t − 2 , . . . , y 1 , c ) = f g ( h t , y t − 1 , c ) \begin{aligned} h_{t}&=f_h(h_{t-1},y_{t-1},c) \\ P(y_t|y_{t-1},y_{t-2},…,y_1,c)&=f_{g}(h_{t},y_{t-1},c) \end{aligned} htP(ytyt1,yt2,...,y1,c)=fh(ht1,yt1,c)=fg(ht,yt1,c)
解压时,每个输出都用到了 c c c向量与前面的每个隐藏状态与输出。上面式子的两个 f h f_h fh在文章中使用的是它提出的GRU。

RNN自编码器其实就是一个多对一RNN加一个一对多RNN,由 n n n个输入多对一输出一个编码 c c c为编码过程,由 c c c一对多解码得到多个输出为解码过程。
……
对于最早的循环编码器到这里就讲完了,但是对用于序列处理的这个网络结构的发展才刚开始,从这个结构引出了注意力机制,一直到后面的Transformer,要把这些讲清楚又是一个很长的故事,足可另开一个专题,所以这一节就到这里。

8、自动编码器在缺陷检测中的应用

《Deep Autoencoding Models for Unsupervised Anomaly Segmentation in Brain MR Images》[11],脑核磁共振图像中无监督异常分割的深度编码器模型,提出了一个变分编码器+GAN的模型:


Autoencoder自动编码器的发展

很明显地,变分+GAN的结合,其损失函数为:


Autoencoder自动编码器的发展

λ 1 \lambda_1 λ1 λ 2 \lambda_2 λ2 λ 3 \lambda_3 λ3均为超参数,分别对应损失函数中的第1、2、3项。
第一项的 x x x是原图, x ^ \hat x x^是变分编码器生成的图像,这里用了L1范数:原图与生成的图片相减后,残差图像中非0元素的个数。
第二项为隐变量 z z z与标准正态分布的KL散度,即希望 z z z服从标准正态分布,这是变分编码器的内容,这能使编码器生成跟原图不同但是跟原图很相似的图片。
第三项中, E n c Enc Enc表示编码器, D e c Dec Dec表示解码器,在这里, D e c ( E n c ( x ) ) = x ^ Dec(Enc(x))=\hat x Dec(Enc(x))=x^,即模型生成的 x ^ \hat x x^是经过编码、解码生成的,同时, D i s Dis Dis是GAN的判断器,表示希望生成的图片能够骗过这个分类器,尽可能地真实。
D i s Dis Dis这个分类器就是通过GAN训练的:


Autoencoder自动编码器的发展

又是一个KL散度,希望原图 x x x与生成的 D e c ( E n c ( x ) ) = x ^ Dec(Enc(x))=\hat x Dec(Enc(x))=x^尽可能相似。
点子好像不错,但是效果嘛——相比纯变分编码器只提高了一个百分点,而且文章里面扯了很多,这个不好那个不好并且放了别人不好的图,但是它居然没有放出它做的好的图,感觉有点可疑,不知道是不是实现得不太好的原因。
……
《Improving Unsupervised Defect Segmentation by Applying Structural Similarity To Autoencoders》[16]
利用结构相似性对自动编码器改进无监督缺陷分割,将损失函数中的L2范数换成了文章中提出的结构相似性,突出了缺陷的边缘,令其变得更好检测:


Autoencoder自动编码器的发展

因为L2范数是以每个像素为单位重建图片的,它会无差别地重建缺陷部分与良好部分,但是结构相似性是以 K ∗ K K*K KK的块为单位重建的,经过设计的结构相似性能够突出文章中所用的纳米纤维材料的缺陷,下面是结构相似性的定义:


Autoencoder自动编码器的发展


Autoencoder自动编码器的发展

经过结合、计算:


Autoencoder自动编码器的发展

其中,结构相似性的图示:


Autoencoder自动编码器的发展

这是个有意思的点子,缺陷检测缺陷检测,如果我们能把缺陷突出,自然就能更好地检测了,同时, S S I M SSIM SSIM被设计成了这个样子,跟文章中是用到纳米纤维这种材料应该不无关系,所以这个结构应该不是万能的,但其中的启发是,对于其他场景中的缺陷检测,如果能设计出相应的 S S I M SSIM SSIM,应该也能获得不错的效果。
……
《Memorizing Normality to Detect Anomaly: Memory-augmented Deep Autoencoder for Unsupervised Anomaly Detection》[12],记忆常态以检测异常:用于无监督异常检测的内存增强深度自动编码器,它处理这种情况——有时候重建得太好了,导致输出的图片中仍然含有输入图片的缺陷,这就失去了重建的意义。
这篇文章通过加入内存记忆的方式解决这个问题:建立一块内存,里面由许多无缺陷的图片 x 1 ∼ x c x_1 \sim x_c x1xc,有缺陷的输入图片 x x x从这些无缺陷图片中找到与自己最相似的那一张无缺陷图片 x i x_i xi,用 x i x_i xi重建 x x x的无缺陷图片 x ^ i \hat x_i x^i


Autoencoder自动编码器的发展

x x x是输入图片, f e ( ) f_e() fe()是编码器, z z z x x x经过 f e ( ) f_e() fe()编码压缩成的向量, z ^ \hat z z^是内存中与 z z z最相像的压缩向量, f d ( ) f_d() fd()是解码器, x ^ \hat x x^是输出图片,由 z ^ \hat z z^ f d ( ) f_d() fd()解码得到。
这篇文章的重点在于如何从内存 M M M中找到与 z z z最接近的 z ^ \hat z z^
首先,这里由一份内存 M M M,它是一个 N ∗ C N*C NC矩阵,表示有 N N N张图片,每张图片都被编码器压缩成了 C C C维向量, z z z z ^ \hat z z^均为 C C C维向量。先计算 z z z与内存中每一张图片的相似程度,这里用的是cos距离:
d ( z , m i ) = z m i T ∣ ∣ z ∣ ∣ ∗ ∣ ∣ m i ∣ ∣ d(z,m_i)=\frac{z{m_i}^T}{||z||*||m_i||} d(z,mi)=zmizmiT
得到权重 w i w_i wi
w i = exp ⁡ ( d ( z , m i ) ) ∑ j = 1 N exp ⁡ ( d ( z , m j ) ) w_i=\frac{\exp (d(z,m_i))}{\sum _{j=1} ^N \exp (d(z,m_j))} wi=j=1Nexp(d(z,mj))exp(d(z,mi))
把权重过低的筛除:
w ^ i = h ( w i ; λ ) = { w i , if  w i > λ   0 , otherwise \hat w_i = h(w_i;\lambda) = \begin{cases} w_i, & \text {if $w_i>\lambda$ } \\ 0, & \text{otherwise} \end{cases} w^i=h(wi;λ)={
wi,0,if wi>λ otherwise

因不好反向传播,再丢进RELU函数中, ϵ \epsilon ϵ是一个很小的正数:
w ^ i = max ⁡ ( w i − λ , 0 ) ∗ w i ∣ w i − λ ∣ + ϵ \hat w_i=\frac{\max (w_i-\lambda,0)*w_i}{|w_i-\lambda|+\epsilon} w^i=wiλ+ϵmax(wiλ,0)wi

经过上述步骤,得到的 w ^ = { w ^ 1 , w ^ 2 , . . . . . . , w ^ n } \hat w=\{\hat w_1,\hat w_2,……,\hat w_n\} w^={
w^1,w^2,......,w^n}
1 ∗ N 1*N 1N向量,再经过归一化 w ^ i = w ^ i / ∣ ∣ w ^ ∣ ∣ 1 \hat w_i=\hat w_i/||\hat w||_1 w^i=w^i/w^1,乘上 N ∗ C N*C NC维的 M M M,得到 1 ∗ C 1*C 1C维的向量 z ^ \hat z z^
z ^ = w ^ M = ∑ i = 1 n w ^ i m i \hat z=\hat wM=\sum_{i=1}^n\hat w_im_i z^=w^M=i=1nw^imi
如此,就实现了从内存 M M M中找到与 z z z最接近的 z ^ \hat z z^
这个点子嘛,推起来挺顺畅的,但我品不出更多的意思了,就当是一个小技巧吧。

9、尾声

自动编码器到底是个什么东西?
经过上面的探究,本来应该清晰了才是,但是这时候的我反而不好给它下一个定义了,我本来以为输出等于输入是它的一个特征,但是看到循环自编码器那里我就知道这不是了,而且我看它比较百搭,好像跟什么都能结合一腿,变成一个新的什么东西。
……
从上文看下来,自动编码器只有一个动作是固定的:将一个东西编码压缩到更小,然后将其解码解压出来。
也正是由于它本身的约束非常小,所以它是一个相当灵活的结构,在编码、解码的过程中,你可以极尽想象去对它动一切手脚、想办法让它跟别的东西结合起来,万一成了,说不定就是一篇文章。
……
现在你手上的工作,能不能用上自动编码器呢?

参考文献

[1] A Learning Algorithm for Boltzmann Machines*(1985)
[2] Learning representations by back-propagating errors(1986a)
[3] Learning internal representations by back propagation(1986b)
[4] reduce the dimensionality of data with neural networks(2006)
[5] Extracting and Composing Robust Features with Denoising Autoencoders(2008)
[6] Stacked Denoising Autoencoders: Learning Useful Representations in a Deep Network with a Local Denoising Criterion(2010)
[7] Sparse autoencoder(2011)
[8] Stacked Convolutional Auto-Encoders for Hierarchical Feature Extraction(2011)
[9] Auto-Encoding Variational Bayes(2013)
[10] Learning phrase representations using rnn encoder-decoder for statistical machine translation(2014)
[11] Deep Autoencoding Models for Unsupervised Anomaly Segmentation in Brain MR Images(2018)
[12] Memorizing Normality to Detect Anomaly: Memory-augmented Deep
Autoencoder for Unsupervised Anomaly Detection(2019)

[13] Learning and Relearning in Boltzmann Machines (1985)
[14] Information Processing in Dynamical Systems: Foundations of Harmony Theory(1986)
[15] Tutorial on Variational Autoencoders(2016)
[16]Improving Unsupervised Defect Segmentation by Applying Structural Similarity To Autoencoders(2019)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/133185.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 判断入射满射c语言编码,例4,判断下列函数是否是满射、单射、双射。.PDF

    判断入射满射c语言编码,例4,判断下列函数是否是满射、单射、双射。.PDF例4,判断下列函数是否是满射、单射、双射。4,判断下列函数是否是满射、单射、双射。(1)f:N→Z,F(n)=小于n的完全平方数的个数f(n)={<0、0>,<1,1>,<2,2>,<3,2>,<4,2>,<5、2>}:f(48)=7f(49)=7f(50)=8,不是单射,48,49的像均是7,不…

  • net framework 4.0安装失败0x80240037_0x80300001

    net framework 4.0安装失败0x80240037_0x80300001太~不容易了我一定要写个博客。网上的什么dism各种都试过了,都不行后来试了下面这个方法,还是不行,然后死马当活马医重启了一下,没有联网安了一遍,竟然奇迹般地装好了。Cortana中输入服务,回车后查看WindowsUpdate,AppReadiness,CryptographicServices,BackgroundIntelligentTransferService,Win…

  • C++的pair_pair的复数是什么

    C++的pair_pair的复数是什么1,pair的应用pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。pair的实现是一个结构体,主要的两个成员变量是firstsecond因为是使用struct不是class,所以可以直接使用pair的成员变量。其标准库类型–pair类……

    2022年10月16日
  • 【USB】全球USB厂家 USB ID大全。更新时间:2018-01-04

    【USB】全球USB厂家 USB ID大全。更新时间:2018-01-04## ListofUSBID’s## MaintainedbyStephenJ.Gowdy# Ifyouhaveanynewentries,pleasesubmitthemvia# http://www.linux-usb.org/usb-ids.html# orsendentriesaspatches(diff-uoldnew)i

  • xshell 与 putty

    xshell 与 putty

  • ubuntu20.04内核降级_ubuntu20更新内核

    ubuntu20.04内核降级_ubuntu20更新内核Ubuntu20.04如何降低内核版本?如题,在不小心安装新内核之后,发现没办法降级(网上的各种方法最后结合信息摸索出了解决方案:首先,查看自己的grub版本:grub-install–version记住(GRUB)之后的大版本是2.00以后还是2.00以前查看自己现有的内核版本(完全版)grep’menuentry’/boot/grub/grub.cfg找到自己想换回的内核例如,这里我想要更换为5.8.0-50,就找到对应的选项,有menuentry’U

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号