当前位置: 首页 > news >正文

第十六周:机器学习

目录

摘要

Abstract

一、HW6——生成二次元图片

1、forward process

2、backward process 

神经网络中的辅助函数 

神经网络的组件

3、diffusion model 

二、非监督学习——生成式模型

1、pixel RNN

2、VAE

3、GAN

总结


摘要

本周学习非监督学习的其中一种类型——生成式模型,其中包含pixel RNN、VAE和GAN三种模型。简单的了解了三种模型的提出和生成数据的方式以及其优缺点。还进行了代码实践——李宏毅机器学习HW6:生成二次元图片。该代码实例是扩散模型,学习了该模型的一些代表性的辅助块和组件,以及添加噪声的数据处理。

Abstract

This week, we learn about one of the types of unsupervised learning, generative models, which contains three models, pixel RNN, VAE and GAN. Briefly learned how the three models are proposed and generate data as well as their advantages and disadvantages. Code practice was also conducted - Hongyi Li Machine Learning HW6: Generating Quadratic Images. The code example is the diffusion model, and some representative auxiliary blocks and components of the model were learned, as well as data processing for adding noise.

一、HW6——生成二次元图片

1、forward process

def linear_beta_schedule(timesteps):"""linear schedule, proposed in original ddpm paper"""scale = 1000 / timestepsbeta_start = scale * 0.0001beta_end = scale * 0.02return torch.linspace(beta_start, beta_end, timesteps, dtype = torch.float64)def extract(a, t, x_shape):b, *_ = t.shapeout = a.gather(-1, t)return out.reshape(b, *((1,) * (len(x_shape) - 1)))

linear_beta_schedule()函数:生成扩散模型中的线性β(beta)时间表

extract()函数:从张量中提取特定索引值

数据集的处理 

class Dataset(Dataset):def __init__(self,folder,image_size):self.folder = folderself.image_size = image_sizeself.paths = [p for p in Path(f'{folder}').glob(f'**/*.jpg')]################################### TODO: Data Augmentation ###################################self.transform = T.Compose([T.Resize(image_size),T.ToTensor()])def __len__(self):return len(self.paths)def __getitem__(self, index):path = self.paths[index]img = Image.open(path)return self.transform(img)

三个内置方法与HW3相同CSDN 

2、backward process 

神经网络中的辅助函数 

残差块 

class Residual(nn.Module):def __init__(self, fn):super().__init__()self.fn = fndef forward(self, x, *args, **kwargs):return self.fn(x, *args, **kwargs) + x

 在forward方法中,它将输入 x 通过 fn 处理后,再与原始输入 x 相加,实现了残差连接。

上/下采样

def Upsample(dim, dim_out = None):return nn.Sequential(nn.Upsample(scale_factor = 2, mode = 'nearest'),nn.Conv2d(dim, default(dim_out, dim), 3, padding = 1))def Downsample(dim, dim_out = None):return nn.Sequential(Rearrange('b c (h p1) (w p2) -> b (c p1 p2) h w', p1 = 2, p2 = 2),nn.Conv2d(dim * 4, default(dim_out, dim), 1))

上采样:首先使用upsample进行最近邻插值,将特征图的尺寸放大两倍;然后,使用conv2d进行卷积操作,以调整通道数。

下采样:首先使用rearrange操作来重新排列数据,以准备进行下采样;然后,使用conv2d进行卷积操作,以调整通道数。

权重标准化的二维卷积

class WeightStandardizedConv2d(nn.Conv2d):  #权重标准化的二维卷积def forward(self, x):eps = 1e-5 if x.dtype == torch.float32 else 1e-3weight = self.weightmean = reduce(weight, 'o ... -> o 1 1 1', 'mean')var = reduce(weight, 'o ... -> o 1 1 1', partial(torch.var, unbiased = False))     #计算均值和方差normalized_weight = (weight - mean) * (var + eps).rsqrt()return F.conv2d(x, normalized_weight, self.bias, self.stride, self.padding, self.dilation, self.groups)   #最后对标准化的结果进行二维卷积

在forward方法中,首先计算了卷积核的均值和方差,并使用这些统计数据来标准化权重。其中的reduce()函数是为了计算均值和方差。

层归一化

class LayerNorm(nn.Module):   #层归一化:是在单个样本的特征图上的操作(通过规范化层的输入来减少内部协变量偏移)def __init__(self, dim):super().__init__()self.g = nn.Parameter(torch.ones(1, dim, 1, 1))def forward(self, x):eps = 1e-5 if x.dtype == torch.float32 else 1e-3var = torch.var(x, dim = 1, unbiased = False, keepdim = True)mean = torch.mean(x, dim = 1, keepdim = True)return (x - mean) * (var + eps).rsqrt() * self.g

初始化方法中g用于在归一化后缩放特征;在forward方法中,计算了输入x的均值和方差,并使用这些统计数据来归一化输入 。

前规范化:位于激活函数和权重之前

class PreNorm(nn.Module):def __init__(self, dim, fn):super().__init__()self.fn = fnself.norm = LayerNorm(dim)  #每个样本的特征都被独立地规范化,使得每个特征的均值为0,标准差为1def forward(self, x):x = self.norm(x)return self.fn(x)

在初始化方法中dim定了归一化的维度,fn是一个神经网络层或层的序列;在forward方法中,首先对输入x进行归一化,然后将归一化后的特征传递给fn进行处理。

正弦位置嵌入

class SinusoidalPosEmb(nn.Module):  #生成正弦位置嵌入def __init__(self, dim):super().__init__()self.dim = dim  #表示嵌入位置的维度def forward(self, x):device = x.devicehalf_dim = self.dim // 2emb = math.log(10000) / (half_dim - 1)emb = torch.exp(torch.arange(half_dim, device=device) * -emb)emb = x[:, None] * emb[None, :]emb = torch.cat((emb.sin(), emb.cos()), dim=-1)return embclass RandomOrLearnedSinusoidalPosEmb(nn.Module):def __init__(self, dim, is_random = False):super().__init__()assert (dim % 2) == 0half_dim = dim // 2self.weights = nn.Parameter(torch.randn(half_dim), requires_grad = not is_random)def forward(self, x):x = rearrange(x, 'b -> b 1')freqs = x * rearrange(self.weights, 'd -> 1 d') * 2 * math.pifouriered = torch.cat((freqs.sin(), freqs.cos()), dim = -1)fouriered = torch.cat((x, fouriered), dim = -1)return fouriered

为模型提供序列中每个元素的位置信息。以上两个类都是用于生成正弦位置嵌入。

神经网络的组件

神经网络块

class Block(nn.Module):  #块:权重标准化卷积+层归一化+silu激活函数def __init__(self, dim, dim_out, groups = 8):super().__init__()self.proj = WeightStandardizedConv2d(dim, dim_out, 3, padding = 1) #卷积层进行卷积操作self.norm = nn.GroupNorm(groups, dim_out)   #层归一化对卷积后的输出结果进行层归一化self.act = nn.SiLU()  #激活函数为了引入非线性def forward(self, x, scale_shift = None):x = self.proj(x)x = self.norm(x)if exists(scale_shift):scale, shift = scale_shiftx = x * (scale + 1) + shiftx = self.act(x)return x

 包含了权重标准化卷积+层归一化+silu激活函数

ResNet块 

class ResnetBlock(nn.Module):  #包含时间嵌入和残差连接的ResNet块def __init__(self, dim, dim_out, *, time_emb_dim = None, groups = 8):super().__init__()self.mlp = nn.Sequential(   #MLP是通过非线性激活函数来学习和模拟输入数据与输出数据之间的复杂关系和映射nn.SiLU(),nn.Linear(time_emb_dim, dim_out * 2)) if exists(time_emb_dim) else None#如果 time_emb_dim存在,创建序列模型(激活函数+非线性函数)用于将时间嵌入转换为缩放和平移参数#两个 Block 实例,用于构建ResNet块中的两个卷积块self.block1 = Block(dim, dim_out, groups = groups)self.block2 = Block(dim_out, dim_out, groups = groups)self.res_conv = nn.Conv2d(dim, dim_out, 1) if dim != dim_out else nn.Identity()#一个卷积层或恒等映射,用于实现残差连接def forward(self, x, time_emb = None):scale_shift = Noneif exists(self.mlp) and exists(time_emb):   time_emb = self.mlp(time_emb) #用MLP来处理时间嵌入time_emb = rearrange(time_emb, 'b c -> b c 1 1') #调整形状来满足卷积层的输入要求scale_shift = time_emb.chunk(2, dim = 1)   #将输出分割为缩放和平移参数h = self.block1(x, scale_shift = scale_shift)  #使用缩放和平移参数h = self.block2(h)return h + self.res_conv(x)  #将第二个块的输出与通过 self.res_conv 处理的原始输入相加,实现残差连接

包含时间嵌入和残差连接的块。forward方法中首先判断是否包含时间嵌入并生成缩放和平移参数,然后返回值处实现残差连接。

线性的注意力机制

class LinearAttention(nn.Module):    #线性注意力机制,比transformer注意力更加高效def __init__(self, dim, heads = 4, dim_head = 32):super().__init__()self.scale = dim_head ** -0.5  #注意力因子self.heads = heads    #注意力头的数量hidden_dim = dim_head * heads    #每个注意力头的维度self.to_qkv = nn.Conv2d(dim, hidden_dim * 3, 1, bias = False)  #一个二维卷积层,将输入x转化为q、k、vself.to_out = nn.Sequential(     #一个序列模型:二维卷积层+归一化层nn.Conv2d(hidden_dim, dim, 1),LayerNorm(dim))def forward(self, x):b, c, h, w = x.shapeqkv = self.to_qkv(x).chunk(3, dim = 1)q, k, v = map(lambda t: rearrange(t, 'b (h c) x y -> b h c (x y)', h = self.heads), qkv)q = q.softmax(dim = -2)k = k.softmax(dim = -1)   #对q、k应用softmax函数,从而获得注意力分散q = q * self.scalev = v / (h * w)context = torch.einsum('b h d n, b h e n -> b h d e', k, v)  #计算注意力上下文,这是通过 K 和 V 的外积实现的out = torch.einsum('b h d e, b h d n -> b h e n', context, q)out = rearrange(out, 'b h c (x y) -> b (h c) x y', h = self.heads, x = h, y = w)return self.to_out(out)

forward方法实现了线性注意力的计算过程,包括对Q和K应用softmax函数,计算注意力上下文,以及通过注意力机制增强输入特征。比传统的transformer注意力机制更加高效。

3、diffusion model 

高斯分布模型包含了很多个方法。如下:

def predict_start_from_noise(self, x_t, t, noise):return (extract(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t -extract(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * noise)def predict_noise_from_start(self, x_t, t, x0):return ((extract(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - x0) / \extract(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape))def q_posterior(self, x_start, x_t, t):posterior_mean = (extract(self.posterior_mean_coef1, t, x_t.shape) * x_start +extract(self.posterior_mean_coef2, t, x_t.shape) * x_t)posterior_variance = extract(self.posterior_variance, t, x_t.shape)posterior_log_variance_clipped = extract(self.posterior_log_variance_clipped, t, x_t.shape)return posterior_mean, posterior_variance, posterior_log_variance_clippeddef model_predictions(self, x, t, clip_x_start = False, rederive_pred_noise = False):model_output = self.model(x, t)maybe_clip = partial(torch.clamp, min = -1., max = 1.) if clip_x_start else identitypred_noise = model_outputx_start = self.predict_start_from_noise(x, t, pred_noise)x_start = maybe_clip(x_start)if clip_x_start and rederive_pred_noise:pred_noise = self.predict_noise_from_start(x, t, x_start)return pred_noise, x_startdef p_mean_variance(self, x, t, clip_denoised = True):noise, x_start = self.model_predictions(x, t)if clip_denoised:x_start.clamp_(-1., 1.)model_mean, posterior_variance, posterior_log_variance = self.q_posterior(x_start = x_start, x_t = x, t = t)return model_mean, posterior_variance, posterior_log_variance, x_start

predict_start_from_noisepredict_noise_from_start 方法用于从噪声预测干净的数据点和从干净的数据点预测噪声;q_posterior 方法用于计算后验分布的均值和方差;model_predictions 方法使用模型进行预测,并根据需要裁剪预测的噪声;p_mean_variance 方法计算模型的均值和方差。

生成图像的逐步过程如下:

 

二、非监督学习——生成式模型

 非监督学习:就是训练数据没有标签

生成模型:给定训练集,产生与训练集同分布的新样本。首先学习训练样本中的数据分布,然后再在该分布中采样出一个点,通过这个点就可以生成想要的类似于真样本的假数据。 

1、pixel RNN

可以看出,每次生成一张图片都需要一个个像素依次生成。上图3*3的可以看成是RNN预留了9个维度的像素生成。 

多个维度的像素处理如下:

在生成图像的时候,第一种方法是像素的输入是3维向量,分别代表RGB,缺陷就是RGB的权重很接近导致生成图片近乎灰色;第二种方法是将不同颜色的像素块以“独热码”的形式表示出来,缺陷就是颜色太多导致维度过大;所以提出第三种方法——聚类  

问题:无法确定每个pixel出现的正确顺序 

2、VAE

auto-encoder提出是为了让机器通过输入的数据,经过神经网络的计算生成新数据。

自编码器(Auto-encoder) 是一种用于无监督学习的神经网络模型,其主要目的是学习数据的低维表示(或压缩表示),同时能够从这些低维表示中重构原始输入数据。由encoder和decoder组成。

问题:auto-encoder一般生成图片的效果不好

解决:VAE

实际上,VAE就是在anto-encoder的基础上进行了改进,主要是使得encoder和decoder中间的code计算更加复杂了。m_i\sigma _i是decoder给出的两组3维向量,c_i则代表code。最终要使得重构误差最小。简单来说,VAE就是加了噪声的code。

重构误差reconstruction error: x→y→z,y是被污染的x,z是从y中试图对x进行的重构。重构误差是用来衡量重构的好坏。

一般来说,我们可以把encoder输出的n维向量选取2个维度进行调整,固定其他(n-2)维。这样就能明显的看出图像生成的具体变换。如下所示:

问题:为什么生成模型VAE要优于auto-encoder

回答: VAE能生成一些介于几张不同真实图片之间的图片,如下:

可以看出,左图当中是auto-encoder模型,给定的database中只有满月和半月的图像,所以生成的图像也只有这两种;右图中VAE模型在code中添加了噪声,能够生成3/4月的图像,使得机器有一定的想象力。

混合高斯模型GaussianMixture Model: 假设我们有m个权重、方差、均值各不相同的高斯模型,那我们分布出x的概率就是每个高斯分布的权重P(m)和这个正态分布分布出x的概率的成绩的求和。

假设z是正态分布,并且x|z也是关于x的不同均值方差的正态分布。在z分布曲线上取一些点,就能对应到P(x)上的一个正态分布。VAE的高斯分布图如下图所示:

已知P(z)是正态分布,想要估计关于z的均值方差,就需要使观测对象x的可能性得到最大,如下面的最大似然函数的推导过程: 

 

 

问题:该模型从来没有真正的学习生成新的数据,而是模仿原有database中的旧数据,在其基础上做一些线性变换。 

解决:GAN

3、GAN

 GAN可以做到生成全新的数据,它的训练过程就是:固定discriminator,使得generator生成的数据更加接近真实数据;固定generator,使得discriminator很大程度上区分真实图片和虚假的生成图片。

总结

本周的代码实践仅仅是扩散模型的simple版本,后续还将添加数据增强以及其他参数的调整实现更好的生成效果。并且继续学习flow-based GAN的相关内容。


http://www.mrgr.cn/news/54781.html

相关文章:

  • C#学习笔记(一)
  • 未来智慧城市发展的四大引领方向
  • 2024年4个好用的录屏软件大盘点,轻松录制精彩瞬间。
  • django连接mysql数据库
  • 红日安全vulnstack (一)
  • 【每日一题】24.10.14 - 24.10.20
  • 差分题目总和
  • 【电子通识】热敏打印头的结构类型和特点
  • 第十五届蓝桥杯Java大学b组(解)
  • 股票与基金资料收集
  • 二叉树的模拟实现—Java数据结构
  • 使用 VSCode 通过 Remote-SSH 连接远程服务器详细教程
  • 字符串和集合的转换
  • Deformable DETR:结合多尺度特征、可变形卷积机制的DETR
  • Python画笔案例-089 绘制 三角圆图
  • 11.useComponentDidMount
  • STL-vector+题目
  • hadoop的MapReduce提交任务到yarn实操
  • 【Redis】数据结构(下)
  • fftw 的安装与编译
  • 算法题——二分查找类型题大全
  • java实现文件变动监听
  • vulnhub靶场之JOY
  • 提示词高级阶段学习day2.1-在提示词编写中对{}的使用教程
  • 卷积神经网络
  • R语言中的stat_compare_means():如何解决anova目标对象的方法问题