访问自动编码器的低维编码数据

Access lower dimensional encoded data of autoencoder

这是我在教程中使用的自动编码器:https://debuggercafe.com/implementing-deep-autoencoder-in-pytorch/

我只是在学习自动编码器,我已经修改了源编码一个自定义的小数据集,其中包括:

[0,1,0,1,0,1,0,1,0],[0,1,1,0,0,1,0,1,0],[0,1,1,0,0,1,0,1,0],[0,1,1,0,0,1,0,1,0]

似乎工作正常,但我不确定如何访问维度 2 的低维嵌入值(由参数 out_features 设置)。

我已将自动编码器 class 添加到 return 嵌入的方法,这是访问嵌入的推荐方法吗?

代码:

import torch
import torchvision
from torch import nn
from torch.autograd import Variable
from torch.utils.data import DataLoader
from torchvision import transforms
from torchvision.datasets import MNIST
from torchvision.utils import save_image
import warnings
import os

# import packages
import os
import torch 
import torchvision
import torch.nn as nn
import torchvision.transforms as transforms
import torch.optim as optim
import matplotlib.pyplot as plt
import torch.nn.functional as F

from torchvision import datasets
from torch.utils.data import DataLoader
from torchvision.utils import save_image
import numpy as np

# utility functions
def get_device():
    if torch.cuda.is_available():
        device = 'cuda:0'
    else:
        device = 'cpu'
    return device

device = get_device()

features = torch.tensor(np.array([ [0,1,0,1,0,1,0,1,0],[0,1,1,0,0,1,0,1,0],[0,1,1,0,0,1,0,1,0],[0,1,1,0,0,1,0,1,0] ])).float()
tic_tac_toe_data_loader = torch.utils.data.DataLoader(features, batch_size=1, shuffle=True)

class Encoder(nn.Module):
    def __init__(self):
        super(Encoder, self).__init__()
        self.fc1 = nn.Linear(in_features=9, out_features=2)

    def forward(self, x):
        return F.sigmoid(self.fc1(x))

class Decoder(nn.Module):
    def __init__(self):
        super(Decoder, self).__init__()
        self.fc1 = nn.Linear(in_features=2, out_features=9)

    def forward(self, x):
        return F.sigmoid(self.fc1(x))


class Autoencoder(nn.Module):

    def __init__(self):
        super(Autoencoder, self).__init__()
        self.fc1 = Encoder()
        self.fc2 = Decoder()

    def forward(self, x):
        return self.fc2(self.fc1(x))

net = Autoencoder()
net.to(device)

NUM_EPOCHS = 50
LEARNING_RATE = 1e-3

criterion = nn.MSELoss()
optimizer = optim.Adam(net.parameters(), lr=LEARNING_RATE)

# image transformations
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))
])

outputs = None

def train(net, trainloader, NUM_EPOCHS):
    train_loss = []
    for epoch in range(NUM_EPOCHS):
        running_loss = 0.0
        for data in trainloader:
            img = data
            img = img.to(device)
            img = img.view(img.size(0), -1)
#             print('img.shape' , img.shape)
            optimizer.zero_grad()

            outputs = net(img)
            loss = criterion(outputs, img)
            loss.backward()
            optimizer.step()
            running_loss += loss.item()

        loss = running_loss / len(trainloader)
        train_loss.append(loss)

    return train_loss

# train the network
train_loss = train(net, tic_tac_toe_data_loader, NUM_EPOCHS)

我可以使用

访问低维嵌入

print(编码器().forward(torch.tensor(np.array([0,1,0,1,0,1,0,1,0])).float() ))

但是这是使用经过训练的权重值进行嵌入吗?如果我使用相同的值多次调用编码器:

print(Encoder().forward( torch.tensor(np.array([0,1,0,1,0,1,0,1,0])).float()))
print(Encoder().forward( torch.tensor(np.array([0,1,0,1,0,1,0,1,0])).float()))

return编辑了不同的结果:

tensor([0.5083, 0.5020], grad_fn=<SigmoidBackward>)
tensor([0.4929, 0.6940], grad_fn=<SigmoidBackward>)

为什么会这样?调用 Encoder 是否会调用额外的训练步骤?

通过调用 Encoder(),您基本上每次都在创建一个新的编码器实例,并且每次都会随机初始化权重。

通常,您创建一个实例并对其进行训练,保存权重并对其进行推断。

另外,对于PyTorch,不需要调用.forward(),直接调用实例即可。 Forward 被它隐式调用,包括其他钩子方法。

enc = Encoder()
input = torch.from_numpy(np.asarray([0,1,0,1,0,1,0,1,0]).float()
print(enc(input))
print(enc(input))

当您将 Encode() 实例传递给 train 函数时,会发生训练传递。调用 Encoder() 只会创建一个新对象。
由于每个对象都有自己的权重,并且权重是随机初始化的(请参阅 xavier 和 kaiming 初始化),所以输出不同。移动到单个对象,您仍然必须使用 train 函数对其进行显式训练。

就像其他响应者在您调用 Encoder() 时指出的那样,您会生成具有随机初始化权重的新实例。因为您对编码器生成的 低维嵌入 感兴趣,所以您需要在训练的 net:

中访问编码器的权重
trained_encoder = net.fc1

现在您的编码器具有经过训练的权重,下面几行应该会产生相同的结果:

print(trained_encoder.forward( torch.tensor(np.array([0,1,0,1,0,1,0,1,0])).float()))
print(trained_encoder.forward( torch.tensor(np.array([0,1,0,1,0,1,0,1,0])).float()))

正如其他人所指出的,您可以通过直接传递输入来进一步简化:

test_input = torch.tensor(np.array([0,1,0,1,0,1,0,1,0])).float()
print(trained_encoder(test_input))