一、理论问题
1、怎么理解重参数化技术?
重参数化是神经网络中的一种技术,也可以称之为参数重整化。当我们从一个分布 p θ ( z ) p_\theta(z) pθ(z)中采样时,由于直接进行采样的动作是离散的,不可微,所以没有梯度信息,在BP反向传播的时候无法对参数梯度进行更新。这个时候我们就可以通过重参数化技术解决这个问题。重参数化技巧主要用于连续分布的情况,但有时也会进行变通,用于处理离散分布。在连续分布的情况下,以高斯分布为例:原本需要从 N ( μ , σ 2 ) N(\mu,\sigma^2) N(μ,σ2)中采样得到 z z z,重参数化技巧将其转化为了从正态分布 N ( 0 , 1 ) N(0,1) N(0,1)中采样得到 ε \varepsilon ε,从而 z = μ + ε σ z=\mu+\varepsilon \sigma z=μ+εσ,从而把随机性转移出了计算图,解决了采样导致梯度不可传递的问题,整个过程可以通过梯度下降来进行优化。针对离散分布的采样可以使用Gumbel-Softmax Trick,这种技巧将离散的采样过程转化为连续的可微操作,从而使得模型可以通过梯度下降进行优化。
2、KL散度是什么?
KL散度是衡量两个概率分布之间差异的一种方法,又可以叫做相对熵。KL散度越小,说明两个概率分布之间的差异越小。当两分布一致时,KL散度为0。假设有两个概率分布p和q,KL散度 D K L ( p ∣ ∣ q ) D_{KL}(p||q) DKL(p∣∣q)表示当用分布p来近似真实分布q时的信息损失。公式表达为: D K L ( p ∣ ∣ q ) = − ∑ x p ( x ) l o g q ( x ) p ( x ) D_{KL}(p||q)=-\sum_{x}p(x)log\frac{q(x)}{p(x)} DKL(p∣∣q)=−∑xp(x)logp(x)q(x)。
补充提问:KL散度是距离吗?
KL散度不能被看作是不同分布之间距离的度量,因为从KL散度的计算公式就可以看出KL散度不符合对称性(距离度量应该满足对称性)。用 p近似q和用q近似p,二者所损失的信息并不是一样的。
3、DDPM
DDPM是一种生成模型,全称为Denoising Diffusion Probabilistic Models 去噪扩散概率模型。DDPM的本质作用,就是学习训练数据的分布,产出尽可能符合训练数据分布的真实图片。DDPM的核心思想是通过迭代地应用扩散过程,逐渐将简单的分布(通常是高斯分布)演变成目标数据的分布。DDPM通过前向过程和反向过程来模拟数据的生成。前向过程逐步将清晰数据转换为噪声,反向过程则尝试从噪声中恢复原始数据。实现DDPM需要满足三个条件:①满足马尔可夫假设;②微小;③高斯噪声变化。
【论文精读】DDPM:Denoising Diffusion Probabilistic Models 去噪扩散概率模型
4、什么是马尔可夫过程
马尔可夫过程是一种随机过程,其中未来状态只依赖于当前的状态,而与过去状态无关。马尔科夫链条件概率形式如下:
P
(
A
,
B
,
C
)
=
P
(
C
∣
B
,
A
)
P
(
B
,
A
)
=
P
(
C
∣
B
)
P
(
B
∣
A
)
P
(
A
)
P
(
B
,
C
∣
A
)
=
P
(
B
∣
A
)
P
(
C
∣
B
)
P(A,B,C)=P(C|B,A)P(B,A)=P(C|B)P(B|A)P(A)\\ P(B,C|A)=P(B|A)P(C|B)
P(A,B,C)=P(C∣B,A)P(B,A)=P(C∣B)P(B∣A)P(A)P(B,C∣A)=P(B∣A)P(C∣B)
5、GAN模型
GAN模型由生成器和判别器组成,生成器负责生成逼真数据以 “骗” 过 判别器,而判别器负责判断一个样本是真实的还是 “造” 出来的。在训练过程中,生成器和判别器相互对抗、相互提升,最终使得生成器能够生成逼真的数据,判别器则很难区分真假数据。
【论文精读】GAN:Generative Adversarial Nets 生成对抗网络
6、VAE模型
VAE模型全称为变分自动编码器模型,它结合了自动编码器和变分推断的思想。VAE 模型是一种包含隐变量的生成模型,主要包括编码器和解码器两个部分。编码器将输入数据映射到潜在空间的概率分布参数,通常是均值和方差。解码器则从潜在空间的采样中重构原始数据。
AutoEncoder自动编码器、VAE变分自编码器、VQVAE量子化(离散化)的自编码器
二、力扣回顾-单调栈
涉及题目:
每日温度:
- 给定输入:整数数组 temperatures表示每天的温度
- 要求的输出:
- answer[i] 表示对于第 i 天,下一个更高温度出现在几天后。
- 如果气温在这之后都不会升高,请在该位置用 0 来代替。
下一个更大的元素 I:
- 给定输入:两个没有重复元素 的数组 nums1 和 nums2,nums1 是 nums2 的子集
- 要求的输出:
- 输出一个长度为 nums1.length 的数组 ans
- ans[i]表示与nums1[i]数值相等的nums2[j]在 nums2的 下一个更大元素
- 如果不存在下一个更大元素,则返回 -1
下一个更大元素 II:
- 给定输入:一个循环数组 nums
- 要求的输出:
- 输出nums 中每个元素的 下一个更大元素
- 如果不存在,则输出 -1
对比分析:
这三道题都是单调栈有关的题目,当遇到需要寻找一个元素右边或者左边第一个比自己大或者小的元素的位置时,就可以考虑使用单调栈来完成。739. 每日温度就是寻找temperatures数组右边第一个比自己大的元素;496. 下一个更大元素 I 是寻找数组nums1对应的数组nums2中元素的右边第一个比自己大的元素,相比前一道题多了一个寻找对应关系的部分;503. 下一个更大元素 II 跟之前相比,它需要查询的数组是循环数组。
739. 每日温度
- 注意单调栈是保存索引的,计算一个元素右边或者左边第一个比自己大或者小的元素的位置时,也就是计算索引的差值
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
answer=[0]*len(temperatures) #准备输出数组
stack=[0] #准备单调栈并且初始化为0
for i in range(1,len(temperatures)):#标号为0的已经放进去了,从1开始遍历
if temperatures[i]<=temperatures[stack[-1]]: #如果小于则加进去
stack.append(i)
else: #大于则需要进行计算
while len(stack)!=0 and temperatures[i]>temperatures[stack[-1]]:
answer[stack[-1]]=i-stack[-1]
stack.pop()
stack.append(i)
return answer
496. 下一个更大元素 I
- 注意这里的输出是下一个更大的元素,而不是下一个更大的元素所在的位置
- 由于nums1是nums2的子数组,所以可以先对nums2中所有元素求出其下一个更大的元素,然后判断是否为nums1的元素,如果是就加入最后的结果
class Solution:
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
ans=[-1]*len(nums1)
stack=[0]
for i in range(1,len(nums2)):
if nums2[i]<=nums2[stack[-1]]:
stack.append(i)
else:
while len(stack)!=0 and nums2[i]>nums2[stack[-1]]:
if nums2[stack[-1]] in nums1:
index=nums1.index(nums2[stack[-1]])
ans[index]=nums2[i]
stack.pop()
stack.append(i)
return ans
503. 下一个更大元素 II
- 注意这里的输出是下一个更大的元素,而不是下一个更大的元素所在的位置
- 因为是循环数组,所以直接拼接两个原数组,然后求得下一个更大元素后取一个数组的长度即可
class Solution:
def nextGreaterElements(self, nums: List[int]) -> List[int]:
nums2=nums+nums
ans=[-1]*len(nums2)
stack=[0]
for i in range(1,len(nums2)):
if nums2[i]<=nums2[stack[-1]]:
stack.append(i)
else:
while len(stack)!=0 and nums2[i]>nums2[stack[-1]]:
ans[stack[-1]]=nums2[i]
stack.pop()
stack.append(i)
return ans[0:len(nums)]
参考:
代码随想录算法训练营第五十天|739. 每日温度,496.下一个更大元素 I
代码随想录算法训练营第五十一天|503.下一个更大元素II,42. 接雨水