混合物密度网络


大家好!

正如您可能已经猜到的,让我们谈谈神经网络和机器学习。 从名称中可以很清楚地看到关于混合物密度网络的信息,然后是MDN,我不想翻译该名称并保持原样。 是的,是的,是的...数学和概率论会有些枯燥,但是不幸的是,没有它,不幸的是,还是幸运的是,您需要决定想象机器学习世界的难度。 但是我要向您保证,它会相对较小,并且不会很困难。 无论如何,都可以跳过它,但是只看Python和PyTorch中的少量代码,是的,我们将使用PyTorch以及带有结果的各种图形来编写网络。 但是最重​​要的是,将有机会稍微了解一下并了解什么是MD网络。

好吧,让我们开始吧!



回归


首先,让我们稍微刷新一下知识,然后简要回顾一下线性回归是什么。

我们有一个向量 X = \ {x_1,x_2,...,x_n \}X = \ {x_1,x_2,...,x_n \} 我们需要预测价值 Y ,这取决于 X 使用一些线性模型:

 hatY=XT hat beta

作为误差函数,我们将使用平方误差:

SE beta= sumni=1yi hatyi2= sumNi=1yixTi hat beta2

通过采用SE的导数并将其值设置为零,可以直接解决此问题:

 frac deltaSE beta delta beta=2XT mathbfyX beta=0

因此,我们简单地找到它的最小值,而SE是一个二次函数,这意味着最小值将始终存在。 之后,您已经可以轻松找到  beta

 hat beta=XTX1XT mathbfy

就这样,问题就解决了。 在这里,我们结束了什么是线性回归。

当然,数据生成本质中固有的依赖性可能会有所不同,因此必须已经在我们的模型中添加了一些非线性。 直接解决大型和真实数据的回归问题也是一个坏主意,因为存在矩阵 XTX 尺寸 n\倍n ,仍然需要找到其逆矩阵,并且经常发生这样的矩阵根本不存在的情况。 在这种情况下,可以使用各种基于梯度下降的方法。 模型的非线性可以多种方式实现,包括使用神经网络。

但是现在,让我们谈论的不是错误功能,而是错误功能。 当数据可以具有非线性关系时,SE和对数似然有什么区别?

我们与动物园打交道,即:OLS,LS,SE,MSE,RSS
所有这些本质上是相同的,RSS-残差平方和,OLS-普通最小二乘,LS-最小二乘,MSE-均方误差,SE-平方误差。 在不同的来源中,您可以找到不同的名称。 其实质仅仅是一个: 二次偏差 。 您当然会感到困惑,但是很快就会习惯。

值得注意的是,MSE是标准偏差,是整个训练数据集的一定误差平均值。 在实践中,通常使用MSE。 公式没有特别的不同:

MSE beta= frac1N sumni=1yi hatyi2

N -数据集的大小, \帽yi -模型预测 yi

别说了 可能性? 这是概率论中的东西。 是的-这是纯粹的概率论。 但是二次偏差如何与似然函数相关? 以及结果。 它与找到最大似然(最大似然)和正态分布有关,更确切地说,与正态分布有关。 \亩

为了实现这一点,让我们再次看一下平方偏差函数:

RSS beta= sumni=1yi hatyi2 qquad qquad1

现在假设似然函数具有正态形式,即高斯或正态分布:

LX=pX| theta= prodX mathcalNxi; mu sigma2

总的来说,似然函数是什么,它的含义是什么?我不会告诉您,您可以在其他地方阅读它,您还应该熟悉条件概率的概念,贝叶斯定理等等,以进行更深入的了解。 所有这些都进入了纯概率论,在学校和大学中都进行了研究。

现在,记住正态分布公式,我们得到:

L(X; \ mu,\ sigma ^ 2)= \ prod ^ X \ frac {1} {\ sqrt {2 \ pi \ sigma ^ 2}} e ^ {-\ frac {{x_i- \ mu)^ 2} {2 \ sigma ^ 2}} \ qquad \ qquad(2)

如果我们输入标准差怎么办  sigma2=1 并删除公式(2)中的所有常量,只删除而不减少,因为找到函数的最小值并不依赖于它们。 然后我们将看到:

LX; mu sigma2 sim prodXexi mu2

还是没有? 不行吗 好吧,如果我们采用函数的对数怎么办? 从对数来看,有一些优点:乘法将变成和,度将变成乘法,并且  loge=1 -对于此属性,有必要澄清一下我们在谈论自然对数,严格来讲  lne=1 。 通常,函数的对数不会更改其最大值,这对我们来说是最重要的功能。 与Log-Likelihood和Likelihood的关系,以及为什么有用的原因,将在下面进行一些小小的论述。 因此,我们做了什么:删除所有常量,并采用似然函数的对数。 他们还删除了减号,从而将对数似然转化为负对数似然(NLL),它们之间的联系也将被描述为奖励。 结果,我们得到了NLL函数:

 logLX; muI2 sim sumX mu2

再看一下RSS功能(1)。 是的,他们是一样的! 没错! 还可以看到  mu=\帽y

如果您使用MSE标准偏差函数,则可得出以下信息:

 operatornameargminMSE beta sim operatornameargmax mathbbEX simPdata logPmodelx; beta

在哪里  mathbbE -数学期望  beta -模型参数,将来我们将其表示为:  theta

结论:如果在回归问题中将LS族用作误差函数,则从本质上解决了在分布为高斯分布的情况下找到最大似然函数的问题。 和预测值 \帽y 等于正态分布中的平均值。 现在我们知道所有这些如何关联,概率理论(及其似然函数和正态分布)与标准偏差或OLS方法如何关联。 关于此的更多细节可以在[2]中找到。

这是承诺的奖金。 由于我们正在讨论各种错误函数之间的关系,因此我们将考虑(不一定要阅读):

交叉熵,似然性,对数似然和负对数似然之间的关系
假设我们有数据 X = \ {x_1,x_2,x_3,x_4,... \} ,每个点都属于一个特定的类,例如 \ {x_1 \ rightarrow1,x_2 \ rightarrow2,x_3 \ rightarrow n,... \} 。 总在那里 n 类,而发生第1类 c1 次,第2类- c2 时代与阶级 n -- cn 次。 根据这些数据,我们训练了一些模型  theta 。 它的似然函数(似然)将如下所示:

P| theta=P0,1...n| theta=P0| thetaP1| theta...Pn| theta

P1| thetaP2| theta...Pn| theta= prodc1 haty1 prodc2 haty2... prodcn\帽yn=\帽yc11\帽yc22...\帽ycnn


在哪里 Pn| theta=\帽yn -班级的预测概率 n

我们采用似然函数的对数,得到对数似然:

\ log {P(data | \ theta)} = \ log {(\ hat {y} _1 ^ {c_1} ... \ hat {y} _n ^ {c_n})} = c_1 \ log {\ hat { y_1}} + ... + c_n \ log {\ hat {y_n}} = \ sum_i ^ n {c_i \ log {\ hat {y_i}}}}

机率  haty in[01] 根据概率的定义,它位于0到1的范围内。 因此,对数将具有负值。 如果将对数似然乘以-1,我们将得到函数负对数似然(NLL):

NLL= logPdata| theta= sumnici log hatyi

如果我们将NLL除以 XN=c1+c2+...+cn 然后我们得到:

 frac1N logPdata| theta= sumni fracciN log hatyi

可以注意到,该类别的实际概率 n 等于: yn= fraccnN 。 从这里我们得到:

NLL= sumniyi log hatyi

现在,如果您看一下交叉熵的定义 Hpq= sump logq 然后我们得到:

NLL=Hyi\帽yi

在只有两个班级的情况下 n=2 (二进制分类),我们得到了二进制交叉熵的公式(您也可以使用众所周知的对数Log-Loss):

Hy haty=y log haty+1y log1 haty

从所有这一切可以理解,在某些情况下,最小化交叉熵等效于最小化NLL或找到似然函数(似然性)或对数似然性的最大值。

一个例子。 考虑二进制分类。 我们具有类值:

y = np.array([0, 1, 1, 1, 1, 0, 1, 1]).astype(np.float32) 

实际概率 y 对于0级等于 2/8=0.25 ,对于1类等于 6/8=0.75 。 假设我们有一个二元分类器来预测0类的概率 \帽y 对于每个示例,分别对于类别1,概率为 1\帽y 。 让我们为不同的预测绘制对数损失函数的值 \帽y


在图形上,您可以看到对数损失函数的最小值对应于点0.75,即 如果我们的模型完全“学习”了源数据的分布, \帽y=y

神经网络回归


因此,我们进行了更有趣的练习。 让我们看看如何使用神经网络(神经网络)解决回归问题。 我们将使用Python编程语言实现所有内容,以使用PyTorch深度学习库创建一个网络。

源数据生成


输入数据  mathbfX in mathbbRN 使用均匀分布生成,取区间-15至15  mathbfX inU[1515] 。 点数  mathbfY 我们使用以下公式获得:

 mathbfY=0.5 mathbfX+8 sin0.3 mathbfX+ qquad qquad3

在哪里 是尺寸的噪声矢量 N 使用具有参数的正态分布获得:  mu=0 sigma2=1

资料产生
 N = 3000 #   IN_DIM = 1 OUT_DIM = IN_DIM x = np.random.uniform(-15., 15., (IN_DIM, N)).T.astype(np.float32) noise = np.random.normal(size=(N, 1)).astype(np.float32) y = 0.5*x+ 8.*np.sin(0.3*x) + noise #  3 x_train, x_test, y_train, y_test = train_test_split(x, y) #      



接收到的数据的图形。

网络建设


创建常规前馈神经网络或FFNN。

建筑物FFNN
 class Net(nn.Module): def __init__(self, input_dim=IN_DIM, out_dim=OUT_DIM, layer_size=40): super(Net, self).__init__() self.fc = nn.Linear(input_dim, layer_size) self.logit = nn.Linear(layer_size, out_dim) def forward(self, x): x = F.tanh(self.fc(x)) #  4 x = self.logit(x) return x 


我们的网络由一个包含40个神经元的维和激活函数-双曲正切的隐藏层组成:

 tanhx= fracexexex+ex qquad qquad4

输出层是没有激活函数的正常线性变换。

学习并取得成果


作为优化器,我们将使用AdamOptimizer。 学习的时期数= 2000,学习率(学习率或lr)= 0.1。

FFNN培训
 def train(net, x_train, y_train, x_test, y_test, epoches=2000, lr=0.1): criterion = nn.MSELoss() optimizer = optim.Adam(net.parameters(), lr=lr) N_EPOCHES = epoches BS = 1500 n_batches = int(np.ceil(x_train.shape[0] / BS)) train_losses = [] test_losses = [] for i in range(N_EPOCHES): for bi in range(n_batches): x_batch, y_batch = fetch_batch(x_train, y_train, bi, BS) x_train_var = Variable(torch.from_numpy(x_batch)) y_train_var = Variable(torch.from_numpy(y_batch)) optimizer.zero_grad() outputs = net(x_train_var) loss = criterion(outputs, y_train_var) loss.backward() optimizer.step() with torch.no_grad(): x_test_var = Variable(torch.from_numpy(x_test)) y_test_var = Variable(torch.from_numpy(y_test)) outputs = net(x_test_var) test_loss = criterion(outputs, y_test_var) test_losses.append(test_loss.item()) train_losses.append(loss.item()) if i%100 == 0: sys.stdout.write('\r Iter: %d, test loss: %.5f, train loss: %.5f' %(i, test_loss.item(), loss.item())) sys.stdout.flush() return train_losses, test_losses net = Net() train_losses, test_losses = train(net, x_train, y_train, x_test, y_test) 


现在让我们看一下学习成果。


MSE函数值的图形取决于训练的迭代;训练数据和测试数据的值的图形。


测试数据的真实和预测结果。

倒置数据


我们使任务复杂化并反转数据。

数据倒置
 x_train_inv = y_train y_train_inv = x_train x_test_inv = y_train y_test_inv = x_train 



倒置数据图。

进行预测  mathbf\帽Y 让我们使用上一节中的直接分销网络,看看它是如何处理的。

 inv_train_losses, inv_test_losses = train(net, x_train_inv, y_train_inv, x_test_inv, y_test_inv) 


MSE函数值的图形取决于训练的迭代;训练数据和测试数据的值的图形。


测试数据的真实和预测结果。

从上面的图表中可以看到,我们的网络根本无法应对此类数据,它根本无法预测它们。 所有这一切都发生了,因为在这样一个倒置的问题中,只有一点 x 可能对应几个点 y 。 你问,那噪音呢? 他还创造了一种情况 x 可以获得一些价值 y 。 是的,没错。 但总的来说,尽管有噪音,但那都是一个确定的分布。 而且由于我们的模型基本上可以预测 py|x ,对于MSE,它是正态分布的平均值(为什么在本文的第一部分中进行了说明),因此它很好地应对了“直接”任务。 否则,我们会得到一种不同的分布 x 因此,仅靠一个正态分布就无法获得良好的结果。

混合物密度网络


有趣的开始! 什么是混合物密度网络(以下称为MDN或MD网络)? 通常,这是一个可以同时模拟多个分布的特定模型:

p mathbfy| mathbfx; theta= sumKk pik mathbfx mathcalN mathbfy; muk mathbfx sigma2 mathbfx qquad qquad5

你说这真是一个奇怪的公式。 让我们弄清楚。 我们的MD网络正在学习建模均值 \亩 和方差  sigma2 对于多个发行版。 在公式(5)中  pik mathbfx -每个点分别分布的所谓显着性因子 xi in mathbfx ,某个混合因子或每个分布对某个点的贡献量。 总在那里 K 分布。

关于几句话  pik mathbfx -实际上,这也是一个分布,表示一个点的概率 xi in mathbfx 将是一个条件 k

再次,这个数学,让我们已经写点东西了。 因此,我们将开始实现网络。 对于我们的网络,我们采取 K=30

 self.fc = nn.Linear(input_dim, layer_size) self.fc2 = nn.Linear(layer_size, 50) self.pi = nn.Linear(layer_size, coefs) self.mu = nn.Linear(layer_size, out_dim*coefs) # mean self.sigma_sq = nn.Linear(layer_size, coefs) # variance 

定义网络的输出层:

 x = F.relu(self.fc(x)) x = F.relu(self.fc2(x)) pi = F.softmax(self.pi(x), dim=1) sigma_sq = torch.exp(self.sigma_sq(x)) mu = self.mu(x) 

我们编写误差函数或损失函数,公式(5):

 def gaussian_pdf(x, mu, sigma_sq): return (1/torch.sqrt(2*np.pi*sigma_sq)) * torch.exp((-1/(2*sigma_sq)) * torch.norm((x-mu), 2, 1)**2) losses = Variable(torch.zeros(y.shape[0])) # p(y|x) for i in range(COEFS): likelihood = gaussian_pdf(y, mu[:, i*OUT_DIM:(i+1)*OUT_DIM], sigma_sq[:, i]) prior = pi[:, i] losses += prior * likelihood loss = torch.mean(-torch.log(losses)) 

完整的MDN构建代码
 COEFS = 30 class MDN(nn.Module): def __init__(self, input_dim=IN_DIM, out_dim=OUT_DIM, layer_size=50, coefs=COEFS): super(MDN, self).__init__() self.fc = nn.Linear(input_dim, layer_size) self.fc2 = nn.Linear(layer_size, 50) self.pi = nn.Linear(layer_size, coefs) self.mu = nn.Linear(layer_size, out_dim*coefs) # mean self.sigma_sq = nn.Linear(layer_size, coefs) # variance self.out_dim = out_dim self.coefs = coefs def forward(self, x): x = F.relu(self.fc(x)) x = F.relu(self.fc2(x)) pi = F.softmax(self.pi(x), dim=1) sigma_sq = torch.exp(self.sigma_sq(x)) mu = self.mu(x) return pi, mu, sigma_sq #       def gaussian_pdf(x, mu, sigma_sq): return (1/torch.sqrt(2*np.pi*sigma_sq)) * torch.exp((-1/(2*sigma_sq)) * torch.norm((x-mu), 2, 1)**2) #   def loss_fn(y, pi, mu, sigma_sq): losses = Variable(torch.zeros(y.shape[0])) # p(y|x) for i in range(COEFS): likelihood = gaussian_pdf(y, mu[:, i*OUT_DIM:(i+1)*OUT_DIM], sigma_sq[:, i]) prior = pi[:, i] losses += prior * likelihood loss = torch.mean(-torch.log(losses)) return loss 


我们的MD网络已准备就绪。 快准备好了。 仍然需要训练她并查看结果。

MDN培训
 def train_mdn(net, x_train, y_train, x_test, y_test, epoches=1000): optimizer = optim.Adam(net.parameters(), lr=0.01) N_EPOCHES = epoches BS = 1500 n_batches = int(np.ceil(x_train.shape[0] / BS)) train_losses = [] test_losses = [] for i in range(N_EPOCHES): for bi in range(n_batches): x_batch, y_batch = fetch_batch(x_train, y_train, bi, BS) x_train_var = Variable(torch.from_numpy(x_batch)) y_train_var = Variable(torch.from_numpy(y_batch)) optimizer.zero_grad() pi, mu, sigma_sq = net(x_train_var) loss = loss_fn(y_train_var, pi, mu, sigma_sq) loss.backward() optimizer.step() with torch.no_grad(): if i%10 == 0: x_test_var = Variable(torch.from_numpy(x_test)) y_test_var = Variable(torch.from_numpy(y_test)) pi, mu, sigma_sq = net(x_test_var) test_loss = loss_fn(y_test_var, pi, mu, sigma_sq) train_losses.append(loss.item()) test_losses.append(test_loss.item()) sys.stdout.write('\r Iter: %d, test loss: %.5f, train loss: %.5f' %(i, test_loss.item(), loss.item())) sys.stdout.flush() return train_losses, test_losses mdn_net = MDN() mdn_train_losses, mdn_test_losses = train_mdn(mdn_net, x_train_inv, y_train_inv, x_test_inv, y_test_inv) 



损失函数值的图形取决于训练的迭代;训练数据和测试数据的值的图形。

由于我们的网络已经学会了几种分布的平均值,因此让我们看一下:

 pi, mu, sigma_sq = mdn_net(Variable(torch.from_numpy(x_test_inv))) 


绘制每个点的两个最有可能的平均值的图表(左)。 绘制每个点的4个最可能的平均值的图表(右)。


绘制每个点的所有平均值的图表。

为了预测数据,我们将随机选择几个值 \亩 sigma2 根据价值  pik mathbfx 。 然后根据它们生成目标数据 \帽y 使用正态分布。

结果预测
 def rand_n_sample_cumulative(pi, mu, sigmasq, samples=10): n = pi.shape[0] out = Variable(torch.zeros(n, samples, OUT_DIM)) for i in range(n): for j in range(samples): u = np.random.uniform() prob_sum = 0 for k in range(COEFS): prob_sum += pi.data[i, k] if u < prob_sum: for od in range(OUT_DIM): sample = np.random.normal(mu.data[i, k*OUT_DIM+od], np.sqrt(sigmasq.data[i, k])) out[i, j, od] = sample break return out pi, mu, sigma_sq = mdn_net(Variable(torch.from_numpy(x_test_inv))) preds = rand_n_sample_cumulative(pi, mu, sigma_sq, samples=10) 


10个随机选择值的预测数据 \亩  sigma2 (左)和两个(右)。

从图中可以看出,MDN在“逆向”任务方面做得很好。

使用更复杂的数据


让我们看看我们的MD网络如何处理更复杂的数据,例如螺旋数据。 笛卡尔坐标系中的双曲螺旋方程:

x= rho cos phi qquad qquad qquad qquad qquad qquad6y= rho sin phi

螺旋数据生成
 N = 2000 x_train_compl = [] y_train_compl = [] x_test_compl = [] y_test_compl = [] noise_train = np.random.uniform(-1, 1, (N, IN_DIM)).astype(np.float32) noise_test = np.random.uniform(-1, 1, (N, IN_DIM)).astype(np.float32) for i, theta in enumerate(np.linspace(0, 5*np.pi, N).astype(np.float32)): #  6 r = ((theta)) x_train_compl.append(r*np.cos(theta) + noise_train[i]) y_train_compl.append(r*np.sin(theta)) x_test_compl.append(r*np.cos(theta) + noise_test[i]) y_test_compl.append(r*np.sin(theta)) x_train_compl = np.array(x_train_compl).reshape((-1, 1)) y_train_compl = np.array(y_train_compl).reshape((-1, 1)) x_test_compl = np.array(x_test_compl).reshape((-1, 1)) y_test_compl = np.array(y_test_compl).reshape((-1, 1)) 



螺旋数据图。

有趣的是,让我们看看常规的前馈网络将如何处理此类任务。


不出所料,前馈网络无法解决此类数据的回归问题。

我们使用先前描述和创建的MD网络来训练螺旋数据。


在这种情况下,混合物密度网络做得很好。

结论


在本文开头,我们回顾了线性回归的基础。 我们发现在找到正态分布的平均值与MSE之间的共同点。 消除了NLL和交叉熵之间的联系。 最重要的是,我们找出了MDN模型,该模型能够从混合分布获得的数据中学习。 尽管有一些数学知识,但我希望本文是可以理解和有趣的。

完整的代码可以在GitHub上查看。


文学作品


  1. 混合物密度网络(Christopher M. Bishop,神经计算研究组,伯明翰阿斯顿大学计算机科学与应用数学系) -这篇文章全面描述了MD网络的理论。
  2. 最小二乘和最大似然(MROsborne)

Source: https://habr.com/ru/post/zh-CN433804/


All Articles