深入理解自编码器(用变分自编码器生成图像)

news2024/11/20 3:29:27

文章目录

  • 自编码器
    • 欠完备自编码器
    • 正则自编码器
      • 稀疏自编码器
      • 去噪自编码器
      • 收缩自编码器
    • 变分自编码器
  • References

内容总结自花书《Deep Learning》以及《Python 深度学习》。

自编码器

自编码器(autoencoder)是神经网络的一种,经过训练后能尝试将输入复制到输出。自编码器内部有一个隐藏层 h h h,可以产生编码来表示输入。

我们可以将自编码器看作由两部分组成:一个由函数 h = f ( x ) h=f(x) h=f(x) 表示的编码器和一个生成重构的解码器 r = g ( h ) r=g(h) r=g(h)

但如果只是简单的复制输入,自编码器就没有什么特别的用处。我们通常会向自编码器强加一些约束,使它只能近似的复制,并只能复制与训练数据相似的输入。这些约束强制模型考虑输入数据的哪些部分需要被优先复制,因此往往能学到数据的有用特性

传统自编码器被用于降维特征学习,近年来,自编码器与潜变量模型理论的联系将自编码器带到了生成式建模的前沿。图像生成的关键思想就是找到一个低维的表示潜在空间(latent space),其中任意点都可以被映射为一张逼真的图像。一旦找到了这样的潜在空间,就可以从中随机采样,并将其映射到图像空间,从而生成前所未见的图像:

在这里插入图片描述
想要学习图像表示的这种潜在空间,GAN 和 VAE(变分自编码器)是两种不同的策略。VAE 非常适合用于学习具有良好结构的潜在空间,其中特定方向表示数据中有意义的变化轴,GAN 生成的图像可能非常逼真,但它的潜在空间可能没有良好结构,也没有足够的连续性。我们稍后就会介绍 VAE。


欠完备自编码器

传统的图像自编码器接收一张图像,通过一个编码器模块将其映射到潜在向量空间,然后再通过一个解码器模块将其解码为与原始图像具有相同尺寸的输出。然后,使用与输入图像相同的图像作为目标数据来训练这个自编码器。

我们刚刚也提到过,将输入复制到输出听起来没什么用,但我们通常不关心解码器的输出,相反,我们希望通过训练自编码器对输入进行复制而使 h h h 获得有用的特性。

从自编码器获得有用特征的一种方法是限制 h h h 的维度比 x x x 小,这种编码维度小于输入维度的自编码器称为欠完备(undercomplete)自编码器。学习欠完备的表示将强制自编码器捕捉训练数据中最显著的特征。

但如果编码器和解码器被赋予过大的容量,自编码器会执行复制任务而不会捕捉到数据的有用特征。

正则自编码器

编码维数小于输入维数的欠完备自编码器可以学习数据分布最显著的特征,但如果赋予这类自编码器过大的容量,它就不能学到任何有用的信息。我们可以通过对编码(即编码器的输出)施加各种限制,让自编码器学到比较有趣的数据潜在表示。

稀疏自编码器

稀疏自编码器简单的在训练时结合编码层的系数惩罚 Ω ( h ) \Omega(h) Ω(h) 和重构误差:
L ( x , g ( f ( x ) ) ) + Ω ( h ) L(x,g(f(x)))+\Omega(h) L(x,g(f(x)))+Ω(h)

参数的概念和我们上面提到的一致,即 h = f ( x ) h=f(x) h=f(x) 表示编码器的输出。稀疏自编码器一般用来学习特征,以便用于像分类这样的任务。

去噪自编码器

传统的自编码器最小化重构误差
L ( x , g ( f ( x ) ) ) L(x,g(f(x))) L(x,g(f(x)))

去噪自编码器(denoising autoencoder, DAE)则最小化
L ( x , g ( f ( x ~ ) ) ) L(x,g(f(\tilde{x}))) L(x,g(f(x~)))

其中 x ~ \tilde{x} x~ 是被某种噪声损坏的 x x x 的副本。因此去噪自编码器必须撤销这些损坏,而不是简单的复制输入。我们会引入一个损坏过程 C ( x ~ ∣ x ) C(\tilde{x}|x) C(x~x),这个条件分布代表给定数据样本 x x x 产生损坏样本 x ~ \tilde{x} x~ 的概率。自编码器根据以下过程,从训练数据对 ( x , x ~ ) (x,\tilde{x}) (x,x~) 中学习重构分布 p r e c o n s t r u c t ( x ∣ x ~ ) p_{reconstruct}(x|\tilde{x}) preconstruct(xx~)

  1. 从训练数据中采一个训练样本 x x x
  2. C ( x ~ ∣ x ) C(\tilde{x}|x) C(x~x) 中采一个损坏样本;
  3. ( x , x ~ ) (x,\tilde{x}) (x,x~) 作为训练样本来估计自编码器的重构分布 p r e c o n s t r u c t ( x ∣ x ~ ) = p d e c o d e r ( x ∣ h ) p_{reconstruct}(x|\tilde{x})=p_{decoder}(x|h) preconstruct(xx~)=pdecoder(xh)

我们可以将去噪自编码器理解为将损坏的数据点 x ~ \tilde{x} x~ 映射回原始数据点 x x x

在这里插入图片描述
上图中,虚线圆圈就代表损坏过程 C ( x ~ ∣ x ) C(\tilde{x}|x) C(x~x),自编码器学习的就是紫色虚线箭头所表示的向量场 g ( f ( x ) ) − x g(f(x))-x g(f(x))x.

收缩自编码器

另一正则化自编码器的策略是使用一个类似稀疏自编码器中的惩罚项 Ω \Omega Ω
L ( x , g ( f ( x ) ) ) + Ω ( h , x ) L(x,g(f(x)))+\Omega(h,x) L(x,g(f(x)))+Ω(h,x)

其中
Ω ( h , x ) = λ ∑ i ∥ ∇ x h i ∥ 2 \Omega(h,x)=\lambda\sum_i \Vert\nabla_xh_i\Vert^2 Ω(h,x)=λixhi2

这迫使模型学习一个在 x x x 变化小时目标也没有太大变化的函数。这样正则化的自编码器称为收缩自编码器(contractive autoencoder, CAE)。


变分自编码器

VAE 不是将输入图像压缩成潜在空间中的固定编码,而是将图像转换为统计分布的参数,即平均值和方差。本质上来说,这意味着我们假设输入图像是由统计过程生成的,在编码和解码过程中应该考虑这一过程的随机性。然后,VAE 使用平均值和方差这两个参数来从分布中随机采样一个元素,并将这个元素解码到原始输入。

在这里插入图片描述

从技术角度来说,VAE 的工作原理如下:

  1. 一个编码器模块将输入样本 input_img 转换为表示潜在空间中的两个参数 z_meanz_log_variance
  2. 我们假定潜在正态分布能够生成输入图像,并从这个分布中随机采样一个点 z z z z = z= z= z_mean + exp(z_log_variance) * epsilon,其中 epsilon 是取值很小的随机张量
  3. 一个解码器模块将潜在空间的这个点映射回原始输入图像。

因为 epsilon 是随机的,所以这个过程可以确保,与 input_img 编码的潜在位置靠近的每个点都能被解码为与 input_img 类似的图像,从而迫使潜在空间能够连续的有意义。潜在空间中任意两个相邻的点都会被解码为高度相似的图像。连续性以及潜在空间的低维度,将迫使潜在空间中的每个方向都表示数据中一个有意义的变化轴,这使得潜在空间具有非常良好的结构。

VAE 的大致代码如下:

z_mean, z_log_variance = encoder(input_img) # 将输入编码为均值和方差两个参数

z = z_mean + exp(z_log_var) * epsilon

reconstructed_img = decoder(z) # 将 z 解码为一张图像

model = Model(input_img, reconstructed_img) # 实例化自编码器模型

下面我们先定义编码器网络:

import keras
from keras import layers
from keras import backend as K
from keras.models import Model
import numpy as np

img_shape = (28, 28, 1)
batch_size = 16
latent_dim = 2 # 潜在空间维度:一个二维平面

input_img = keras.Input(shape=img_shape)

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

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

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

接下来的代码将使用 z_meanz_log_var 来生成一个潜在空间点 z z z。在 keras 中,任何对象都应该是一个层,所以如果代码不是内置层的一部分,我们应该将其包装到一个 Lambda 层(或自定义层)中。

def sampling(args):
    z_mean, z_log_var = args
    epsilon = K.random_normal(shape=(K.shape(z_mean)[0], latent_dim),
                              mean=0., stddev=1.)
    return z_mean + K.exp(z_log_var) * epsilon

z = layers.Lambda(sampling)([z_mean, z_log_var])

下列代码给出了解码器的实现:

decoder_input = layers.Input(K.int_shape(z)[1:])

x = layers.Dense(np.prod(shape_before_flattening[1:]),
                 activation='relu')(decoder_input)

x = layers.Reshape(shape_before_flattening[1:])(x)

x = layers.Conv2DTranspose(32, 3, padding='same', activation='relu',
                           strides=(2, 2))(x)

x = layers.Conv2D(1, 3, padding='same', activation='sigmoid')(x)

decoder = Model(decoder_input, x)

z_decoded = decoder(z)

VAE 有重构损失以及正则化损失的双重损失,我们需要编写一个自定义层,并在其内部使用内置的 add_loss 层方法来创建我们想要的损失:

class CustomVariationalLayer(keras.layers.Layer):
    
    def vae_loss(self, x, z_decoded):
        x = K.flatten(x)
        z_decoded = K.flatten(z_decoded)
        xent_loss = keras.metrics.binary_crossentropy(x, z_decoded)
        kl_loss = -5e-4 * K.mean(1 + z_log_var - K.square(z_mean) - K.exp(z_log_var), axis=-1)
        
        return K.mean(xent_loss + kl_loss)
    
    def call(self, inputs):
        x = inputs[0]
        z_decoded = inputs[1]
        loss = self.vae_loss(x, z_decoded)
        self.add_loss(loss, inputs=inputs)
        return x
    
y = CustomVariationalLayer()([input_img, z_decoded])

最后,将模型实例化并开始训练。因为损失包含在自定义层中,所以在编译时无须指定外部损失(即 loss=None),这意味着在训练过程中不需要传入目标数据。

from keras.datasets import mnist

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

(x_train, _), (x_test, y_test) = mnist.load_data()

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

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

如果到这一步发现报错,可以在开头加入下面的语句,并重新运行上述所有代码:

from tensorflow.python.framework.ops import disable_eager_execution
disable_eager_execution()

模型训练好后,我们就可以使用 decoder 网络将任意潜在空间向量转换为图像:

import matplotlib.pyplot as plt
from scipy.stats import norm

n = 15
digit_size = 28
figure = np.zeros((digit_size * n, digit_size * n))
grid_x = norm.ppf(np.linspace(0.05, 0.95, n))
grid_y = norm.ppf(np.linspace(0.05, 0.95, n))

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

在这里插入图片描述
当我们沿着潜在空间的一条路径观察时,会观察到一个数字逐渐变形为另一个数字。


References

[1] 《Python 深度学习》,François Chollet.

[2] I. J. Goodfellow, Y. Bengio, and A. Courville, Deep Learning. Cambridge, MA, USA: MIT Press, 2016, http://www.deeplearningbook.org.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/112273.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习经典算法:决策树(2)

1. 概述 决策树(Decision Tree)是有监督学习中的一种算法,并且是一种基本的分类与回归的方法。决策树有两种:分类树和回归树。 决策树是用于分类和回归的工具,它将数据特征值拆分为决策节点处的分支(例如&a…

六、Kubernetes核心技术Pod详解、实例

1、概述 Pod 是 k8s 系统中可以创建和管理的最小单元,是资源对象模型中由用户创建或部署的最 小资源对象模型,也是在 k8s 上运行容器化应用的资源对象,其他的资源对象都是用来支 撑或者扩展 Pod 对象功能的,比如控制器对象是用来管…

某大型政务网站的优化咨询案例(视频点播VOD+GZIP压缩+静态文件CDN+Redis缓存+全文索引)

2022年圣诞节到来啦,很高兴这次我们又能一起度过~ 这次分享关于一个对某大型政务网站的优化咨询的案例,发生在今年的下半年,已过去一段时间,并取得了良好的成果!* 项目背景 某大型政务网站准备上线,需要…

08-Golang中的运算符

[TOC](Golang中的运算符运算符介绍算数运算符基本介绍细节说明关系运算符(比较运算符)基本介绍细节说明逻辑运算符基本介绍细节说明赋值运算符基本介绍细节说明运算符优先级运算符介绍 运算符是一种特殊的符号,用来表示数据的运算、赋值和比较 1.算数运…

Vue事件处理的基本使用

前言 事件处理在vue中也是非常重要的一项技术,它类似于js的事件处理,但是也有不同,下面就简单介绍一下在vue中如何进行事件使用以及一些要点 1 事件基本使用 在这里我们使用单击事件为例,简单讲讲在vue中单击事件的编写以及细节…

最近面试遇到一个算法题,简单写一点。

第⼀题(必答) 请针对有重复数字的数组设计⼀个快排算法,⽐如:[34, 34, 89, 1, 1, 20, 12],排序后结果为 [89,34,34,20,12,1,1] 第⼆题(必答) 请利⽤Redis 实现⼀个通⽤分布式锁,并…

技术进步、研发计划启动及政策支持 共促我国合成生物学市场容量加速扩张

合成生物学是对生物体进行有目标的设计、改造乃至重新合成,这一名词最早出现于DNA重组技术发展的上世纪70年代。合成生物学汇聚并融合了生命科学、工程学和信息科学等诸多学科,在天然产物合成、化学工业、生物能源、生物医药等诸多领域有广泛的应用前景。…

【Animejs】——Anime.js照片墙案例实现

目录 一、实现的效果&#xff1a; 二、实现js的具体步骤 1、需要实现的逻辑梳理 2、切换风格的逻辑 三、完整代码&#xff1a; 用js编写使用animejs实现图片复杂的切换效果 一、实现的效果&#xff1a; 点击——> <——箭头&#xff0c;实现不同动画效果的炫酷切换 …

【小5聊】C++ 输入矩阵数字,然后回环方式输出

C 输入矩阵数字&#xff0c;然后回环方式输出 1、题目内容 输入 第一行是两个m和n&#xff0c;分别代表矩阵的行数和列数。 第二行开始输入对应矩阵 输出 第二行回转输出。 相邻的两个整数之间用一个空格分开&#xff0c;行尾无空格 样例输入 5 6 4 8 9 4 5 6 1 2 5 6…

控制算法-PID算法总结-从公式原理到参数整定解析

目录 一、控制系统 1.1控制系统的分类 1.2 性能指标 二、PID算法的起源及特点 三、PID应用 四、PID公式原理 五、PID源码 六、PID整定方法 6.1 经验法 6.2 衰减曲线法 6.3 响应曲线法 参考文献&#xff1a; 一、控制系统 1.1控制系统的分类 分为开环控制、闭环控制和…

Java 8 Stream 从入门到进阶——像SQL一样玩转集合

0.阅读完本文你将会 了解Stream的定义和它的特征了解Stream的基础和高阶用法 1. 前言 在我们日常使用Java的过程中&#xff0c;免不了要和集合打交道。对于集合的各种操作有点类似于SQL——增删改查以及聚合操作&#xff0c;但是其方便性却不如SQL。 所以有没有这样一种方式…

【每日一题Day66】LC1754构造字典序最大的合并字符串 | 贪心 双指针模拟

构造字典序最大的合并字符串【LC1754】 You are given two strings word1 and word2. You want to construct a string merge in the following way: while either word1 or word2 are non-empty, choose one of the following options: If word1 is non-empty, append the fir…

10.2、Django入门--前台管理

文章目录1、URLconf 路由管理展示首页2、视图函数处理业务逻辑展示书籍的详细页3、模板管理实现好看的HTML页面3.1 模板引擎配置3.2 模板语法&#xff1a;变量3.3 模板语法: 常用标签3.4 主页与详情页前端HTML设计常用的HTML编写基础标题标签列表标签图片标签链接标签表格标签表…

耗时二周,万字总结Maven简明教程,与君共勉!

什么是Mavne Maven 是一个项目管理工具&#xff0c;它包含了一个项目对象模型 (POM&#xff1a;Project Object Model)&#xff0c;一组标准集合。由于 Maven 使用标准目录布局和默认构建生命周期&#xff0c;开发团队几乎可以立即自动化项目的构建基础设施。在多个开发团队环…

代码随想录训练营第60天|LeetCode 84.柱状图中最大的矩形

LeetCode 84.柱状图中最大的矩形 双指针 注意&#xff0c;双指针解法可行&#xff0c;但是在力扣上提交会超时。 以heights[i]为中心&#xff0c;用两个指针向两边扩散&#xff0c;直到heights[left]和heights[right]小于heights[i]为止&#xff0c;这样就构成了以left和rig…

第11章_数据库的设计规范(理论了解)

第11章_数据库的设计规范 范式 2.3键和相关属性的概念 范式的定义会使用到主键和候选键&#xff0c;数据库中的键(Key)由一个或者多个属性组成。数据表中常用的几种键和属性的定义: 超键︰能唯─标识元组的属性集叫做超键。候选键︰如果超键不包括多余的属性&#xff0c;那…

基于HOG+LBP完成特征工程,基于机器学习模型同时完成人脸识别+表情识别

这周前两天有时间我写了一篇博文&#xff0c;基于LBP和HOG实现人脸好表情特征的提取计算&#xff0c;之后分别训练了人脸识别模型和表情识别模型&#xff0c;在推理阶段实现了单张图像一次性人脸识别和表情识别的计算分析&#xff0c;但这个我前面就说了这个还是间接的实现方式…

关于GC原理和性能调优实践,看这一篇就够了

前言 本文介绍 GC 基础原理和理论&#xff0c;GC 调优方法思路和方法&#xff0c;基于 Hotspot jdk1.8&#xff0c;学习之后你将了解如何对生产系统出现的 GC 问题进行排查解决。 正文 本文的内容主要如下&#xff1a; GC 基础原理&#xff0c;涉及调优目标&#xff0c;GC 事…

Redis原理篇—数据结构

Redis原理篇—数据结构 笔记整理自 b站_黑马程序员Redis入门到实战教程 底层数据结构 动态字符串SDS 我们都知道 Redis 中保存的 Key 是字符串&#xff0c;value 往往是字符串或者字符串的集合。可见字符串是 Redis 中最常用的一种数据结构。 不过 Redis 没有直接使用C语言中…

Python圣诞树

目录 一、前言 二、创意名 三、效果展示 四、实现步骤 五、编码实现 一、前言 一年一度的圣诞节又要来喽~在这么浪漫的节日里怎么能少的了一个浪漫的程序员呢~让我们一起画个圣诞树&#xff0c;送给你喜欢的那个人吧~ 二、创意名 Python浪漫圣诞树&#xff0c;具体源码见&…