如何在 PyTorch 中对这个函数建模

How to model this function in PyTorch

我想训练一个 feed-forward 具有单个隐藏层的神经网络,对以下方程进行建模。

h = g(W1.input1 + V1.input2 + b)
output1 = f(W2.h + b_w)
output2 = f(V2.h + b_v)

f and g是激活函数,h是隐藏表示,W1, W2, V1, V2是权重矩阵,b, b_w, b_v是各自的偏差。

我无法连接 2 个输入,因为这将导致单个权重矩阵。我无法训练两个单独的 NN,因为潜在表示会错过 2 个输入之间的交互。任何帮助深表感谢。我还附上了下面的NN图

PyTorch 允许您以函数形式定义前向实现,因此您可以:

class NN(nn.Module):
    def __init__(self, in_ch, h_ch, out_ch):
        super().__init__()
        self.in1 = nn.Linear(in_ch, h_ch-in_ch)
        self.fc1 = nn.Linear(h_ch, out_ch)
        self.fc2 = nn.Linear(h_ch, out_ch)
        self.act = nn.ReLU()

    def forward(self, i1, i2):
        i = torch.cat((self.in1(i1), i2), 1)
        h = self.act(i)
        o1 = self.act(self.fc1(h)) 
        o2 = self.act(self.fc2(h))
        return o1, o2

然后将其用作:

>>> model = NN(5, 10, 3)
>>> model(torch.rand(2, 5), torch.rand(2, 5))

我要指出的是,您可以组合 W_1 和 V_1,前提是您只需将它们沿对角线平铺在一个更大的矩阵中并将所有其他值设置为 0。然后您可以裁剪权重在每个优化步骤之后将这些参数限制为保持为 0。或者您可以使用稀疏张量来仅表示您想要更改的权重。无论如何:

C = [[W_1   0]
     [0   V_1]]

设 W1 为 mxn 矩阵,V1 为 qxp 矩阵:

C = [[w11 w12 ... w1n 0   0   ... 0  ]
     [w21 w22 ... w2n 0   0   ... 0  ]
     [... ... ... ... ... ... ... ...]
     [wm1 wm2 ... wmn 0   0   ... 0  ]
     [0   0   ... 0   v11 v12 ... v1p]
     [0   0   ... 0   v21 v22 ... v2p]
     [... ... ... ... ... ... ... ...]
     [0   0   ... 0   vq1 vq2 ... vqp] 

和:

a  = [[input1]
      [input2]]

(请原谅尺寸 flip-flops 如果它们存在,我没有 double-check)。由此产生的乘法为您提供了您关心的中间值。 h = g(Ca + b)

同样,我相信第二个操作与普通的全连接层相同。您也可以连接最终偏差项,因为偏差项已定义为每个输出一个参数。 b_cat = [b_w, b_v]。也就是说,以下会产生相同的结果:

f(W2.h + b_w) , f(V2.h + b_v)
f(C2.h + b_cat)

where C2 = [W2,V2]  # concatenated

所以唯一的新颖之处在于,您需要将初始级联权重矩阵C中的某些参数约束为0。

我决定编写自己的线性层来计算 h = g(W1.input1 + V1.input2 + b) 我通过创建 2 个参数 W1 和 V1 将 input1 和 input2 与 2 个参数相乘然后添加所有内容来实现。代码如下:

import torch
import torch.nn as nn
import math


class MyLinearLayer(nn.Module):
    def __init__(self, size_in1, size_out1):
        super().__init__()
        self.size_in1, self.size_out1 = size_in1, size_out1
        W_1 = torch.Tensor(size_out1, size_in1)
        V_1 = torch.Tensor(size_out1, size_in1)
        self.W1 = nn.Parameter(W_1)
        self.V1 = nn.Parameter(V_1)
        bias = torch.Tensor(size_out1)
        self.bias = nn.Parameter(bias)

    def forward(self, x):
        w_times_x= torch.mm(x[0], self.W1.t())
        v_times_x= torch.mm(x[1], self.V1.t())
        weight_times_x = torch.add(w_times_x, v_times_x)
        return torch.add(weight_times_x, self.bias)  # w times x + w times v + b

    
class NN(nn.Module):
    def __init__(self, in_ch, h_ch, out_ch):
        super().__init__()
        self.input = MyLinearLayer(in_ch, h_ch)
        self.W2 = nn.Linear(h_ch, out_ch)
        self.V2= nn.Linear(h_ch, out_ch)
        self.act = nn.ReLU()

    def forward(self, i1, i2):
        # I pass in stacked input 
        inp = torch.stack([i1,i2])
        h = self.act(self.input(inp))
        o1 = self.act(self.W2(h)) 
        o2 = self.act(self.V2(h))
        return o1, o2

model = NN(5, 10, 5)
o1,o2 = model(torch.rand(2, 5), torch.rand(2, 5))
for name, param in model.named_parameters():
    if param.requires_grad:
        print(name, '->',param.data.shape)

输出7个待训练参数:

input.W1 -> torch.Size([10, 5])
input.V1 -> torch.Size([10, 5])
input.bias -> torch.Size([10])
W2.weight -> torch.Size([5, 10])
W2.bias -> torch.Size([5])
V2.weight -> torch.Size([5, 10])
V2.bias -> torch.Size([5])

感谢@aretor、@Ivan 和@DerekG 的所有投入