Pytorch暹罗网络无法融合

时间:2020-05-14 08:58:42

标签: python pytorch convergence siamese-network

大家早上好

下面是我对pytorch暹罗网络的实现。我将32个批次大小,MSE损失和SGD与0.9的动量用作优化程序。

class SiameseCNN(nn.Module):
    def __init__(self):
        super(SiameseCNN, self).__init__()                                      # 1, 40, 50
        self.convnet = nn.Sequential(nn.Conv2d(1, 8, 7), nn.ReLU(),             # 8, 34, 44
                                    nn.Conv2d(8, 16, 5), nn.ReLU(),             # 16, 30, 40
                                    nn.MaxPool2d(2, 2),                         # 16, 15, 20
                                    nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), # 32, 15, 20
                                    nn.Conv2d(32, 64, 3, padding=1), nn.ReLU()) # 64, 15, 20
        self.linear1 = nn.Sequential(nn.Linear(64 * 15 * 20, 100), nn.ReLU())
        self.linear2 = nn.Sequential(nn.Linear(100, 2), nn.ReLU())
        
    def forward(self, data):
        res = []
        for j in range(2):
            x = self.convnet(data[:, j, :, :])
            x = x.view(-1, 64 * 15 * 20)
            res.append(self.linear1(x))
        fres = abs(res[1] - res[0])
        return self.linear2(fres)

每个批次包含交替的对,即[pos, pos], [pos, neg], [pos, pos]等...但是,网络不收敛,问题似乎是网络中的fres对于每对都是相同的(无论(无论是正对还是负对),self.linear2(fres)的输出总是近似等于[0.0531, 0.0770]。这与我的预期相反,即随着网络学习,[0.0531, 0.0770]的第一个值对于正数对将接近1,而第二个值对于负数对将接近1。这两个值也需要相加为1。

我已经为2通道网络体系结构测试了完全相同的设置和相同的输入图像,其中,不是以[pos, pos]的形式馈入,而是以深度方式堆叠这两个图像,例如{{1 }}。在此设置中,numpy.stack([pos, pos], -1)的尺寸也更改为nn.Conv2d(1, 8, 7)。效果很好。

我还测试了与传统CNN方法完全相同的设置和输入图像,在该方法中,我只是将单个正负灰度图像传递到网络中,而不是将它们堆叠(如2-CH方法)或将它们作为图像对传递(与暹罗方法一样)。这也可以完美地工作,但是结果不如2通道方法那么好。

编辑(我尝试过的解决方案):

nn.Conv2d(2, 8, 7)
convnet

也许要注意的另一件事是,在我的研究范围内,针对每个对象训练了一个暹罗网络。因此,第一类与包含所讨论对象的图像相关联,第二类与包含其他对象的图像相关联。不知道这是否可能是问题的原因。但是,在传统CNN和2通道CNN方法的背景下这不是问题。

根据要求,这是我的培训代码:

def forward(self, data):
    res = []
    for j in range(2):
        x = self.convnet(data[:, j, :, :])
        res.append(x)
    pdist = nn.PairwiseDistance(p=2)
    diff = pdist(res[1], res[0])
    diff = diff.view(-1, 64 * 15 * 10)
    fres = self.linear2(self.linear1(diff))
    return fres

model = SiameseCNN().cuda() ls_fn = torch.nn.BCELoss() optim = torch.optim.SGD(model.parameters(), lr=1e-6, momentum=0.9) epochs = np.arange(100) eloss = [] for epoch in epochs: model.train() train_loss = [] for x_batch, y_batch in dp.train_set: x_var, y_var = Variable(x_batch.cuda()), Variable(y_batch.cuda()) y_pred = model(x_var) loss = ls_fn(y_pred, y_var) train_loss.append(abs(loss.item())) optim.zero_grad() loss.backward() optim.step() eloss.append(np.mean(train_loss)) print(epoch, np.mean(train_loss)) 中的dp注意是具有属性dp.train_set的类,其中每个集合的创建方式如下:

train_set, valid_set, test_set

根据请求,以下是预测概率与真实标签的示例,您可以在其中看到模型似乎不是在学习:

DataLoader(TensorDataset(torch.Tensor(x), torch.Tensor(y)), batch_size=bs)

2 个答案:

答案 0 :(得分:1)

我认为您的方法是正确的,并且您做的很好。对我来说有点奇怪的是最后一个具有RELU激活的层。通常,对于暹罗网络,当两个输入图像属于同一类别时,您希望输出高概率,否则要输出低概率。因此,您可以使用单个神经元输出和S形激活功能来实现此功能。

因此,我将重新实现您的网络,如下所示:

class SiameseCNN(nn.Module):
    def __init__(self):
        super(SiameseCNN, self).__init__()                                      # 1, 40, 50
        self.convnet = nn.Sequential(nn.Conv2d(1, 8, 7), nn.ReLU(),             # 8, 34, 44
                                    nn.Conv2d(8, 16, 5), nn.ReLU(),             # 16, 30, 40
                                    nn.MaxPool2d(2, 2),                         # 16, 15, 20
                                    nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(), # 32, 15, 20
                                    nn.Conv2d(32, 64, 3, padding=1), nn.ReLU()) # 64, 15, 20
        self.linear1 = nn.Sequential(nn.Linear(64 * 15 * 20, 100), nn.ReLU())
        self.linear2 = nn.Sequential(nn.Linear(100, 1), nn.Sigmoid())
        
    def forward(self, data):
        for j in range(2):
            x = self.convnet(data[:, j, :, :])
            x = x.view(-1, 64 * 15 * 20)
            res.append(self.linear1(x))
        fres = res[0].sub(res[1]).pow(2)
        return self.linear2(fres)

然后要进行一致的训练,您应该使用二进制交叉熵:

criterion_fn = torch.nn.BCELoss()

请记住,当两个输入图像都属于同一类时,请将标签设置为1。

此外,我建议您在linear1层之后使用一些丢弃方法,大约有30%的概率丢弃神经元。

答案 1 :(得分:0)

问题解决了。事实证明,如果您每次都给它相同的图像,则网络每次都会预测相同的输出?数据分区期间我的索引错误很小。感谢大家的帮助和协助。这是目前的融合示例:

0 0.20198837077617646
1 0.17636818194389342
2 0.15786472541093827
3 0.1412761415243149
4 0.126698794901371
5 0.11397973036766053
6 0.10332610329985618
7 0.09474560652673245
8 0.08779258838295936
9 0.08199785630404949
10 0.07704121413826942
11 0.07276330365240574
12 0.06907484836131335
13 0.06584368328005076
14 0.06295975042134523
15 0.06039590438082814
16 0.058096024941653016