混合建模:结合生成模型与分类器的新方法
1. 引言
在机器学习中,仅学习条件分布 $p(y|x)$ 是不够的,我们应关注联合分布 $p(x, y)$,其可分解为 $p(x, y) = p(y|x)p(x)$。这是因为条件分布 $p(y|x)$ 无法让我们了解 $x$ 的信息,它只是尽力做出决策。例如,即使给出一个从未观测过的对象,$p(y|x)$ 仍可能对其分类有很高的确定性。而训练好 $p(x)$ 后,理论上我们能评估给定对象的概率,进而判断决策是否可靠。
之前我们主要探讨了如何单独学习 $p(x)$,涉及基于似然的模型,如自回归模型(ARMs)、基于流的模型(flows)和变分自编码器(VAEs)。现在的问题是如何将深度生成模型与分类器(或回归器)结合使用,下面我们以分类任务为例,探讨可能的方法。
1.1 方法一:简单独立训练
我们可以分别训练 $p(y|x)$ 和 $p(x)$,这样就得到了一个分类器和对象的边缘分布。这种方法用不同颜色的神经网络分别参数化两个分布,如图 1 所示。
对联合分布取对数可得:
$\ln p(x, y) = \ln p_{\alpha}(y|x) + \ln p_{\beta}(x)$
其中 $\alpha$ 和 $\beta$ 表示两个分布的参数化(即神经网络)。训练时计算关于 $\alpha$ 和 $\beta$ 的梯度:
$\nabla_{\alpha} \ln p(x, y) = \nabla_{\alpha} \ln p_{\alpha}(y|x) + \nabla_{\alpha} \ln p_{\beta}(x) = \nabla_{\alpha} \ln p_{\alpha}(y|x)$
$\nabla_{\beta} \ln p(x, y) = \nabla_{\beta} \ln p_{\alpha}(y|x) + \nabla_{\beta} \ln p_{\beta}(x) = \nabla_{\beta} \ln p_{\beta}(x)$
这意味着我们可以先用带标签的数据训练 $p_{\alpha}(y|x)$,再用所有可用数据训练 $p_{\beta}(x)$。
然而,这种方法存在潜在问题。首先,不能保证两个分布对 $x$ 的处理方式一致,可能引入误差。其次,由于训练的随机性,随机变量 $x$ 和 $y$ 之间没有信息流动,神经网络会各自寻找局部最小值,就像鸟的两个翅膀完全独立、异步运动。此外,分别训练两个模型效率低下,需要使用两个不同的神经网络,且没有权重共享。虽然这种方法可能有效,但可能得到远非最优的模型。
1.2 方法二:共享参数化
第二种方法采用部分共享参数化,即有一个神经网络处理 $x$,其输出分别输入到分类器和 $x$ 的边缘分布的神经网络中,如图 2 所示。
此时联合分布的对数为:
$\ln p(x, y) = \ln p_{\alpha, \gamma}(y|x) + \ln p_{\beta, \gamma}(x)$
两个分布部分共享参数化 $\gamma$。训练时,$x$ 和 $y$ 之间有明显的信息共享,两个分布以相同方式处理经过处理的 $x$,然后将该表示专门用于给出类别和对象的概率。
这种方法有两个优点。一是两个分布紧密相连,就像鸟的两个翅膀可以同步运动。二是从优化角度看,梯度通过 $\gamma$ 网络流动,该网络包含 $x$ 和 $y$ 的信息,有助于找到更好的解决方案。
2. 混合建模
乍一看,使用 $\ln p(x, y) = \ln p_{\alpha, \gamma}(y|x) + \ln p_{\beta, \gamma}(x)$ 作为训练目标似乎没有问题。但考虑 $y$ 和 $x$ 的维度,若 $y$ 是二进制的,只有一个比特表示类别标签,而二进制向量 $x$ 有 $D$ 个比特,两者尺度存在明显差异。
以二进制变量为例,假设神经网络返回的所有概率都为 0.5,对于独立的伯努利变量:
$\ln Bern(y|0.5) = y \ln 0.5 + (1 - y) \ln 0.5 = -\ln 2$
$\ln \prod_{d=1}^{D} Bern(x_d|0.5) = \sum_{d=1}^{D} \ln Bern(x_d|0.5) = -D \ln 2$
可以看出,$\ln p_{\beta, \gamma}(x)$ 部分比 $\ln p_{\alpha, \gamma}(y|x)$ 部分强 $D$ 倍。训练时,$\gamma$ 网络会从边缘分布获得更多信息,这可能会削弱分类部分,导致最终模型偏向边缘部分。
为解决这个问题,有两种方法:
-
方法一
:考虑 $\ln p(y|x)$ 和 $\ln p(x)$ 的凸组合作为目标函数:
$L(x, y; \lambda) = (1 - \lambda) \ln p(y|x) + \lambda \ln p(x)$
其中 $\lambda \in [0, 1]$。但这种加权方案并非基于定义良好的分布,破坏了基于似然方法的优雅性。
-
方法二
:只对 $\ln p(x)$ 进行加权:
$\ell(x, y; \lambda) = \ln p(y|x) + \lambda \ln p(x)$
其中 $\lambda \geq 0$。虽然 $\lambda$ 不是从概率角度推导出来的,但可以解释为鼓励对输入变化的鲁棒性,也可看作基于雅可比矩阵的正则化惩罚。
在这种方法中,用基于流的模型建模 $p(x)$,并将可逆神经网络(如耦合层)与分类器共享,最终层用于做出决策 $y$,目标函数为 $\ell(x, y; \lambda)$。这种方法有以下优点:
- 可逆神经网络可用于模型的生成和判别部分,使基于流的模型能获取标签信息。
- 加权因子 $\lambda$ 可控制模型更偏向判别还是生成。
- 可以使用任何基于流的模型。
- 可以使用任何分类器(或回归器)。
但这种方法的缺点是需要确定 $\lambda$,这是一个需要调整的额外超参数,且 $\lambda$ 的值会显著改变模型的性能。
3. 实现混合模型
3.1 分类器建模
我们使用全连接神经网络来建模条件分布 $p(y|x)$:
$z \to Linear(D, M) \to ReLU \to Linear(M, M) \to ReLU \to Linear(M, K) \to Softmax$
其中 $D$ 是 $x$ 的维度,$K$ 是类别的数量。对于分类任务,使用分类分布:
$p(y|x) = \prod_{k=1}^{K} \theta_k(x)^{[y=k]}$
其中 $\theta_k(x)$ 是第 $k$ 类的 softmax 值,$[y = k]$ 是艾弗森括号。
3.2 $p(x)$ 建模
我们可以使用任何边缘模型,如基于流的模型和变量变换公式:
$p(x) = \pi(z = f^{-1}(x)) |J_f(x)|^{-1}$
其中 $J_f(x)$ 是变换 $f$ 在 $x$ 处的雅可比矩阵,通常 $\pi(z) = N(z|0, 1)$,即标准高斯分布。
将这些分布代入混合建模的目标函数 $\ell(x, y; \lambda)$ 可得:
$\ell(x, y; \lambda) = \sum_{k=1}^{K} [y = k] \ln \theta_{k, g, f}(x) + \lambda N(z = f^{-1}(x)|0, 1) - \ln |J_f(x)|$
其中 $\theta_{k, g, f}$ 由流的神经网络 $f$ 和最终分类的神经网络 $g$ 参数化。
3.3 使用整数离散流(IDFs)
为了更具创新性,我们使用整数离散流(IDFs)来建模 $p(x)$。IDFs 操作于整数,且不需要计算雅可比矩阵。对于 IDFs,我们选择离散化的逻辑分布 $\pi(z) = DL(z|\mu, \nu)$,则混合建模的目标函数可重写为:
$\ell(x, y; \lambda) = \sum_{k=1}^{K} [y = k] \ln \theta_{k, g, f}(x) + \lambda DL(z = f^{-1}(x)|\mu, \nu)$
4. 代码实现
以下是实现混合整数离散流(HybridIDF)的代码:
import torch
import torch.nn as nn
# 假设 RoundStraightThrough 已定义
class RoundStraightThrough(torch.autograd.Function):
@staticmethod
def forward(ctx, x):
return x.round()
@staticmethod
def backward(ctx, grad_output):
return grad_output
class HybridIDF(nn.Module):
def __init__(self, netts, classnet, num_flows, alpha=1., D=2):
super(HybridIDF, self).__init__()
print('HybridIDF by JT.')
# 这里使用之前讨论的两种选项:耦合层或广义可逆变换
if len(netts) == 1:
self.t = torch.nn.ModuleList([netts[0]() for _ in range(num_flows)])
self.idf_git = 1
self.beta = nn.Parameter(torch.zeros(len(self.t)))
elif len(netts) == 4:
self.t_a = torch.nn.ModuleList([netts[0]() for _ in range(num_flows)])
self.t_b = torch.nn.ModuleList([netts[1]() for _ in range(num_flows)])
self.t_c = torch.nn.ModuleList([netts[2]() for _ in range(num_flows)])
self.t_d = torch.nn.ModuleList([netts[3]() for _ in range(num_flows)])
self.idf_git = 4
self.beta = nn.Parameter(torch.zeros(len(self.t_a)))
else:
raise ValueError('You can provide either 1 or 4 translation nets.')
# 用于在 z 上进行分类的额外层
self.classnet = classnet
# 流的数量(即 f 的数量)
self.num_flows = num_flows
# 舍入操作符
self.round = RoundStraightThrough.apply
# 基础分布 pi 的均值和对数尺度
self.mean = nn.Parameter(torch.zeros(1, D))
self.logscale = nn.Parameter(torch.ones(1, D))
# 输入的维度
self.D = D
# 由于在 Python 中使用“lambda”会引起混淆,我们在代码中用 alpha 表示之前方程中的 lambda
self.alpha = alpha
# 使用 PyTorch 内置的损失函数,用于教学目的
self.nll = nn.NLLLoss(reduction='none') # 需要输入对数 softmax
# 耦合层
def coupling(self, x, index, forward=True):
if self.idf_git == 1:
(xa, xb) = torch.chunk(x, 2, 1)
if forward:
yb = xb + self.beta[index] * self.round(self.t[index](xa))
else:
yb = xb - self.beta[index] * self.round(self.t[index](xa))
return torch.cat((xa, yb), 1)
elif self.idf_git == 4:
(xa, xb, xc, xd) = torch.chunk(x, 4, 1)
if forward:
ya = xa + self.beta[index] * self.round(self.t_a[index](torch.cat((xb, xc, xd), 1)))
yb = xb + self.beta[index] * self.round(self.t_b[index](torch.cat((ya, xc, xd), 1)))
yc = xc + self.beta[index] * self.round(self.t_c[index](torch.cat((ya, yb, xd), 1)))
yd = xd + self.beta[index] * self.round(self.t_d[index](torch.cat((ya, yb, yc), 1)))
else:
yd = xd - self.beta[index] * self.round(self.t_d[index](torch.cat((xa, xb, xc), 1)))
yc = xc - self.beta[index] * self.round(self.t_c[index](torch.cat((xa, xb, yd), 1)))
yb = xb - self.beta[index] * self.round(self.t_b[index](torch.cat((xa, yc, yd), 1)))
ya = xa - self.beta[index] * self.round(self.t_a[index](torch.cat((yb, yc, yd), 1)))
return torch.cat((ya, yb, yc, yd), 1)
# 置换层
def permute(self, x):
return x.flip(1)
# 流变换的前向传播
def f(self, x):
z = x
for i in range(self.num_flows):
z = self.coupling(z, i, forward=True)
z = self.permute(z)
return z
# 流变换的反向传播
def f_inv(self, z):
x = z
for i in reversed(range(self.num_flows)):
x = self.permute(x)
x = self.coupling(x, i, forward=False)
return x
# 分类函数
def classify(self, x):
z = self.f(x)
y_pred = self.classnet(z) # 输出:概率(即 softmax)
return torch.argmax(y_pred, dim=1)
# 计算分类损失的辅助函数
def class_loss(self, x, y):
z = self.f(x)
y_pred = self.classnet(z) # 输出:概率(即 softmax)
return self.nll(torch.log(y_pred), y)
# 采样函数
def sample(self, batchSize):
# 采样 z
z = self.prior_sample(batchSize=batchSize, D=self.D)
# x = f^−1(z)
x = self.f_inv(z)
return x.view(batchSize, 1, self.D)
# 基础分布的对数概率
def log_prior(self, x):
# 假设 log_integer_probability 已定义
log_p = log_integer_probability(x, self.mean, self.logscale)
return log_p.sum(1)
# 从基础分布采样
def prior_sample(self, batchSize, D=2):
# 从逻辑分布采样
y = torch.rand(batchSize, self.D)
x = torch.exp(self.logscale) * torch.log(y / (1. - y)) + self.mean
# 然后四舍五入为整数
return torch.round(x)
# 前向传播
def forward(self, x, y, reduction='avg'):
z = self.f(x)
y_pred = self.classnet(z) # 输出:概率(即 softmax)
idf_loss = -self.log_prior(z)
class_loss = self.nll(torch.log(y_pred), y) # 记得在 softmax 上应用对数
if reduction == 'sum':
return (class_loss + self.alpha * idf_loss).sum()
else:
return (class_loss + self.alpha * idf_loss).mean()
以下是使用示例:
# 可逆变换的数量
num_flows = 2
# 这里仅展示选项 1 的 IDF
nett = lambda: nn.Sequential(nn.Linear(D // 2, M), nn.LeakyReLU(),
nn.Linear(M, M), nn.LeakyReLU(),
nn.Linear(M, D // 2))
netts = [nett]
# 三层分类器
classnet = nn.Sequential(nn.Linear(D, M), nn.LeakyReLU(),
nn.Linear(M, M), nn.LeakyReLU(),
nn.Linear(M, K),
nn.Softmax(dim=1))
# 初始化 HybridIDF
model = HybridIDF(netts, classnet, num_flows, D=D, alpha=alpha)
运行代码并训练 HybridIDF 后,我们可以得到类似图 4 所示的结果。
总结
本文介绍了混合建模的方法,将深度生成模型与分类器结合使用。通过分别训练和共享参数化两种方法,我们可以学习联合分布 $p(x, y)$。但在实际应用中,需要考虑 $y$ 和 $x$ 的维度差异,通过加权的方式解决模型偏向问题。最后,我们使用整数离散流(IDFs)实现了一个混合模型,并给出了代码实现。未来的研究方向包括探索混合 VAE、半监督混合学习、消除加权因子 $\lambda$ 以及研究更好的联合分布分解方式等。
以下是一个简单的流程图,展示混合建模的主要步骤:
graph TD;
A[输入数据] --> B[分别训练 p(y|x) 和 p(x) 或共享参数化];
B --> C[考虑维度差异,调整目标函数];
C --> D[选择合适的模型(如 IDFs)];
D --> E[实现混合模型];
E --> F[训练模型];
F --> G[评估模型性能];
表格 1:两种训练方法的比较
| 方法 | 优点 | 缺点 |
| — | — | — |
| 分别训练 | 简单直接 | 可能引入误差,效率低下,可能得到非最优模型 |
| 共享参数化 | 信息共享,有助于优化 | 无明显缺点,但需要调整超参数 |
通过这些方法和技术,我们可以构建更强大的机器学习模型,提高模型的性能和可靠性。
5. 后续研究方向
5.1 混合 VAE
混合建模的思想不仅局限于使用基于流的模型来建模 $p(x)$,还可以采用变分自编码器(VAE)。在应用变分推断后,我们可以得到混合建模目标的一个下界:
$\tilde{\ell}(x, y; \lambda) = \ln p(y|x) + \lambda E_{z \sim q(z|x)} [\ln p(x|z) + \ln p(z) - \ln q(z|x)]$
其中 $p(y|x)$ 使用了编码器 $q(z|x)$。这种方法为混合建模提供了新的思路,将 VAE 的优势融入到混合模型中。
5.2 半监督混合学习
混合建模的视角非常适合半监督学习场景。对于有标签的数据,我们可以使用目标函数 $\ell(x, y; \lambda) = \ln p(y|x) + \lambda \ln p(x)$;而对于无标签的数据,我们可以只考虑 $\ln p(x)$ 部分。这种方法在一些研究中已经得到应用,例如在 VAE 的半监督学习中。有研究提出了一种半监督 VAE 的目标函数,它类似于混合建模的目标函数,但去掉了麻烦的加权因子 $\lambda$。
5.3 加权因子 $\lambda$ 的问题与解决思路
加权因子 $\lambda$ 存在一些问题。首先,它不是从一个合适的概率分布推导出来的;其次,它需要进行调整,这增加了额外的工作量。不过,有研究表明可以去掉 $\lambda$,这为解决这个问题提供了方向。未来的研究可以探索使用不同的学习算法和参数化方式来消除 $\lambda$ 的影响,例如使用一些特殊的神经网络结构。
5.4 联合分布的分解方式
有人可能会疑惑,联合分布的分解 $p(x, y) = p(y|x) p(x)$ 是否真的比 $p(x, y) = p(x|y) p(y)$ 更好。如果我们想要从特定的类别 $y$ 中采样 $x$,那么后者可能更好。但我们更关注的是为世界分配合适的概率,因此更倾向于 $p(x, y) = p(y|x) p(x)$。未来的研究可以进一步探讨不同分解方式的优劣,寻找更适合特定任务的分解方法。
6. 总结与展望
本文围绕混合建模展开,详细介绍了将深度生成模型与分类器结合的方法。从引言部分强调学习联合分布 $p(x, y)$ 的重要性,到介绍两种不同的训练方法(分别训练和共享参数化),再到分析混合建模中维度差异带来的问题以及相应的解决方法,最后通过使用整数离散流(IDFs)实现了一个混合模型并给出代码。
6.1 主要成果总结
- 提出了两种训练联合分布的方法,分别训练和共享参数化,各有优缺点。
- 针对混合建模中 $y$ 和 $x$ 的维度差异问题,提出了两种加权方法来调整目标函数。
- 使用 IDFs 实现了一个混合模型,避免了计算雅可比矩阵,简化了计算过程。
- 给出了完整的代码实现,方便读者实践和验证。
6.2 未来研究展望
未来的研究可以在以下几个方面深入探索:
-
模型拓展
:进一步研究混合 VAE 和半监督混合学习,挖掘这些方法在不同任务中的潜力。
-
参数优化
:寻找消除加权因子 $\lambda$ 的方法,通过改进学习算法和参数化方式,提高模型的稳定性和性能。
-
分解方式研究
:探索不同联合分布分解方式的优劣,为特定任务选择更合适的分解方法。
通过不断的研究和实践,我们有望构建更强大、更智能的机器学习模型,为解决各种实际问题提供更有效的工具。
表格 2:后续研究方向总结
| 研究方向 | 主要内容 | 潜在优势 |
| — | — | — |
| 混合 VAE | 将 VAE 应用于混合建模,得到目标函数下界 | 融合 VAE 优势,可能提高模型性能 |
| 半监督混合学习 | 针对有标签和无标签数据采用不同目标函数 | 利用无标签数据,提高数据利用率 |
| 消除 $\lambda$ | 探索不同学习算法和参数化方式消除 $\lambda$ | 简化模型,减少超参数调整 |
| 联合分布分解 | 研究不同分解方式的优劣 | 为特定任务选择更合适的分解方法 |
以下是一个流程图,展示未来研究的主要方向:
graph TD;
A[混合建模研究] --> B[混合 VAE];
A --> C[半监督混合学习];
A --> D[消除加权因子 $\lambda$];
A --> E[研究联合分布分解方式];
B --> F[提高模型性能];
C --> G[提高数据利用率];
D --> H[简化模型];
E --> I[选择更合适的分解方法];
通过对这些研究方向的深入探索,我们可以不断推动混合建模领域的发展,为机器学习的进步做出贡献。
超级会员免费看

被折叠的 条评论
为什么被折叠?



