I have a simple autoencoder architecture in PyTorch, which I train to do feature compression and reconstruction. My goal is to use the latent space of the autoencoder to reduce the initial dimensionality of my data and compress it in the test phase.
To perform this, I would need to pass my test data only to my encoder, not the whole autoencoder. Would you have any idea of how to do this ? Something like model = Autoencoder.encoder() or else?
My complete architecture is below:
class Autoencoder(nn.Module):
def __init__(self, n_features):
super(Autoencoder, self).__init__()
self.n_sensors = n_sensors
self.encoder = nn.Sequential(
nn.Linear(self.n_features, 1),
nn.ReLU(True))
self.decoder = nn.Sequential(
nn.Linear(1, self.n_features),
nn.ReLU(True))
def forward(self, x):
x = self.encoder(x)
x = self.decoder(x)
return x
Base on your model definition, you can call forward on the encoder submodule directly:
class Autoencoder(nn.Module):
def __init__(self, n_features):
super(Autoencoder, self).__init__()
self.n_features = n_features
self.encoder = nn.Sequential(
nn.Linear(self.n_features, 1),
nn.ReLU(True))
self.decoder = nn.Sequential(
nn.Linear(1, self.n_features),
nn.ReLU(True))
def forward(self, x):
x = self.encoder(x)
x = self.decoder(x)
return x
Keep in mind you first need to initialize the model:
>>> ae = Autoencoder(n_features=10)
>>> x = torch.empty(16, 10)
>>> ae.encoder(x).shape
(16, 1)
Related
I am unable to plot graph-neural-networking. I have seen few related questions(1, 2, 3) to this topic but their answers do not apply to graph-neural-networks.
What makes it different is that the input vector include objects of different dimensions e.g. properties matrix dimension is [n_nodes, n_node_features], adjacency matrix dimension is [n_nodes, n_nodes] etc. Here is the example of my Model:
class GIN0(Model):
def __init__(self, channels, n_layers):
super().__init__()
self.conv1 = GINConv(channels, epsilon=0, mlp_hidden=[channels, channels])
self.convs = []
for _ in range(1, n_layers):
self.convs.append(
GINConv(channels, epsilon=0, mlp_hidden=[channels, channels])
)
self.pool = GlobalAvgPool()
self.dense1 = Dense(channels, activation="relu")
self.dropout = Dropout(0.5)
self.dense2 = Dense(channels, activation="relu")
def call(self, inputs):
x, a, i = inputs
x = self.conv1([x, a])
for conv in self.convs:
x = conv([x, a])
x = self.pool([x, i])
x = self.dense1(x)
x = self.dropout(x)
return self.dense2(x)
One of the answers in 2 suggested to add build_graph function as follows:
class my_model(Model):
def __init__(self, dim):
super(my_model, self).__init__()
self.Base = VGG16(input_shape=(dim), include_top = False, weights = 'imagenet')
self.GAP = L.GlobalAveragePooling2D()
self.BAT = L.BatchNormalization()
self.DROP = L.Dropout(rate=0.1)
self.DENS = L.Dense(256, activation='relu', name = 'dense_A')
self.OUT = L.Dense(1, activation='sigmoid')
def call(self, inputs):
x = self.Base(inputs)
g = self.GAP(x)
b = self.BAT(g)
d = self.DROP(b)
d = self.DENS(d)
return self.OUT(d)
# AFAIK: The most convenient method to print model.summary()
# similar to the sequential or functional API like.
def build_graph(self):
x = Input(shape=(dim))
return Model(inputs=[x], outputs=self.call(x))
dim = (124,124,3)
model = my_model((dim))
model.build((None, *dim))
model.build_graph().summary()
However, I am not sure how to define dim or Input Layer using tf.keras.layers.Input for such a hybrid data-structure as described above.
Any suggestions?
Here is the minimal code to plot such subclass multi-input model. Note, as stated in the comment above, there are some issue of your GINConv which is from spektral and it's not related to the main query. So, I will give general soluton of such multi-input modeling scenarios. To make it work with your speckral, please reach to the package author for further discussion.
From specktral repo, here, I got the idea the shape of the input tensors.
x, y = next(iter(loader_tr))
bs_x = list(x[0].shape)
bs_y = list(x[1].shape)
bs_z = list(x[2].shape)
bs_x, bs_y, bs_z
([1067, 4], [1067, 1067], [1067])
Similar model, it also takes same amount of inputs and with same shape. But without GINConv.
class GIN0(Model):
def __init__(self, channels, n_layers):
super().__init__()
self.conv1 = tf.keras.layers.Conv1D(channels, 3, activation='relu')
self.conv2 = tf.keras.layers.Conv1D(channels, 3, activation='relu')
self.dense1 = Dense(channels, activation="relu")
self.dropout = Dropout(0.5)
self.dense2 = Dense(n_out, activation="softmax")
def call(self, inputs):
x, a, i = inputs
x = self.conv1(x)
x = tf.keras.layers.GlobalAveragePooling1D()(x)
a = self.conv2(a)
a = tf.keras.layers.GlobalAveragePooling1D()(a)
x = tf.keras.layers.Concatenate(axis=1)([a, x, i])
x = self.dense1(x)
x = self.dropout(x)
return self.dense2(x)
def build_graph(self):
x = tf.keras.Input(shape=bs_x)
y = tf.keras.Input(shape=bs_y)
z = tf.keras.Input(shape=bs_z)
return tf.keras.Model(
inputs=[x, y, z],
outputs=self.call([x, y, z])
)
model = GIN0(channels, layers)
model.build(
[
(None, *bs_x),
(None, *bs_y),
(None, *bs_z)
]
)
# OK
model.build_graph().summary()
# OK
tf.keras.utils.plot_model(
model.build_graph(), show_shapes=True
)
I have a one hop GCN layer
class GCN_AISUMMER(nn.Module):
"""
"""
def __init__(self, in_features, out_features, bias=True):
super().__init__()
self.linear = nn.Linear(in_features, out_features, bias=bias)
def forward(self, X, A):
"""
A: adjecency matrix
X: graph signal
"""
L = create_graph_lapl_norm(A)
num_neighbours = L.sum(dim=-1, keepdims=True)
x = self.linear(X)
node_feats = torch.bmm(L, x)
node_feats = node_feats / num_neighbours
return node_feats
which is used in the following neural net
class GNN(nn.Module):
def __init__(self,
in_features = 12,
hidden_dim = 128,
classes = 2,
dropout = 0.5):
super(GNN, self).__init__()
self.conv1 = GCN_AISUMMER(in_features, hidden_dim)
self.conv2 = GCN_AISUMMER(hidden_dim, hidden_dim)
self.conv3 = GCN_AISUMMER(hidden_dim, hidden_dim)
self.fc = nn.Linear(hidden_dim, classes)
self.dropout = dropout
def forward(self, x,A):
x = self.conv1(x, A)
x = F.relu(x)
x = self.conv2(x, A)
x = F.relu(x)
x = self.conv3(x, A)
x = F.dropout(x, p=self.dropout, training=self.training)
# aggregate node embeddings
x = x.mean(dim=1)
# final classification layer
return self.fc(x)
I tried to print out the weight of input data after training. I tried print(model.conv1.weight) and gotAttributeError: 'GCN_AISUMMER' object has no attribute 'weight'
print(model.trainable_weights) and gotAttributeError: 'GNN' object has no attribute 'trainable_weights'
I got the weight of fc1, when I use print(model.fc1.weight), but I want to got the weight of input data after training.
Image from tensorboard
I got a problem when I check my model.
I built encoder/decoder structured model and draw the graph to check tensors.
Defined encoder, decoder separately then merged them by new module.
I choose this architecture for flexibility.
As you can see from the above image, 2 tensors flow from output to encoder (especially to the output layer).
These tensors disappear when one of encoder or decoder is removed from the model.
I want to know the followings
Why did they appear and flow backwards.
How to remove them.
If these cannot be removed, is there any effect on the model?
Here is source code for reproduction.
I got a same issue even from a simplified model.
import os, torch
from torch.utils.tensorboard import SummaryWriter
class Encoder(torch.nn.Module):
def __init__(self):
super(Encoder, self).__init__()
self.embed = torch.nn.Sequential(
torch.nn.Linear(5, 10), torch.nn.ELU())
self.network = torch.nn.Sequential(
torch.nn.Linear(10, 20), torch.nn.ELU(), torch.nn.Dropout(0.2),
torch.nn.Linear(20, 10), torch.nn.ELU(), torch.nn.Dropout(0.2),
)
self.output = torch.nn.Linear(10, 2)
def forward(self, x):
h = self.embed(x)
h = self.network(h)
h = self.output(h)
return h
class Decoder(torch.nn.Module):
def __init__(self):
super(Decoder, self).__init__()
self.network = torch.nn.Sequential(
torch.nn.Linear(2, 5), torch.nn.ELU(), torch.nn.Dropout(0.2),
torch.nn.Linear(5, 10), torch.nn.ELU(), torch.nn.Dropout(0.2),
)
self.output = torch.nn.Linear(10, 5)
def forward(self, x):
h = self.network(x)
h = self.output(h)
return h
class EncoderDecoder(torch.nn.Module):
def __init__(self, encoder, decoder):
super(EncoderDecoder, self).__init__()
self.encoder = encoder
self.decoder = decoder
def forward(self, x):
h = self.encoder(x)
h = self.decoder(h)
return h
root = './test'
os.makedirs(root, exist_ok=True)
writer = SummaryWriter(root)
model = EncoderDecoder(Encoder(), Decoder())
model.eval()
with torch.no_grad():
writer.add_graph(model, torch.rand(20).view(-1,5))
I want to add a linear layer after an encoder in VAE to get a smaller latent space of a group of data, but the loss returns nan.
This is the simple linear layer I want to add between the encoder and decoder of VAE.
class FC_en(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(2429*32, 64)
self.BN1 = nn.BatchNorm1d(64)
def forward(self, x):
z_loc = self.BN1(self.fc1(x))
return z_loc
class FC_de(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(64,2429*32)
self.BN1 = nn.BatchNorm1d(2429*32)
def forward(self, z):
x = self.BN1(self.fc1(z))
return x
The code followed is the VAE model
class VAE(nn.Module):
def __init__(self, z_dim=16, hidden_dim=1000, use_cuda=True):
super().__init__()
# create the encoder and decoder networks
self.encoder = Encoder(z_dim, hidden_dim)
self.decoder = Decoder(z_dim, hidden_dim)
self.fc3 = FC_en()
self.fc4 = FC_de()
if use_cuda:
# calling cuda() here will put all the parameters of
# the encoder and decoder networks into gpu memory
self.cuda()
self.use_cuda = use_cuda
self.z_dim = z_dim
# define the model p(x|z)p(z)
def model(self, x):
# register PyTorch module `decoder` with Pyro
pyro.module("decoder", self.decoder)
with pyro.plate("data", x.shape[0]):
# setup hyperparameters for prior p(z)
z_loc = x.new_zeros(torch.Size((x.shape[0], self.z_dim)))
z_scale = x.new_ones(torch.Size((x.shape[0], self.z_dim)))
# sample from prior (value will be sampled by guide when computing the ELBO)
z = pyro.sample("latent", dist.Normal(z_loc, z_scale).to_event(1))
# decode the latent code z
loc_img = self.decoder(z)
loc_img = loc_img.reshape(-1,200*200)
pyro.sample("obs", dist.Bernoulli(loc_img).to_event(1), obs=x.reshape(-1, 200*200))
# define the guide (i.e. variational distribution) q(z|x)
def guide(self, x):
# register PyTorch module `encoder` with Pyro
pyro.module("encoder", self.encoder)
with pyro.plate("data", x.shape[0]):
# use the encoder to get the parameters used to define q(z|x)
z_loc, z_scale = self.encoder(x)
z_sum = torch.cat((z_loc,z_scale),1)
z_sum = z_sum.view(2, 2429*32)
z_sum_z = self.fc3(z_sum)
loc_img = self.fc4(z_sum_z)
loc_img = loc_img.reshape(2429*2, 32)
z_loc = loc_img[:, 0:16]
z_scale = loc_img[:, 16:32]
# sample the latent code z
pyro.sample("latent", dist.Normal(z_loc, z_scale).to_event(1))
Does my idea have some problems? how to avoid the nan loss in the VAE model?
I need your kind help.
How can I determine a CNNs model to train a multiple labels for image classification?
My data is a bunch of spectra. Each spectrum has 4 labels. How can I build a CNN to classify those images.
How to compose the forward function and initialization function
What kinds of layers do you recommend?
First, the metadata structure goes like this:
enter image description here
Here is my Dataset
class OurDataset(Dataset):
spectra_dir = f"./data/spectrograms_fm"
metaData_path = f"./data/FMAudio/metaData.csv"
def __init__(self):
self.audio_labels = panda.read_csv(self.metaData_path)
self.transform = torchvision.transforms.Compose([
torchvision.transforms.Resize((201, 81)),
torchvision.transforms.ToTensor()
])
def __len__(self):
return len(self.audio_labels)
def __getitem__(self, idx):
img = PILImage.open(f"./data/spectrograms_fm/mutiplelabels/{self.audio_labels.iloc[idx, 8]}.png").convert("RGB")
img = self.transform(img)
label_A = torch.tensor(self.audio_labels.iloc[idx, 4])
label_Fw = torch.tensor(self.audio_labels.iloc[idx, 5])
label_P = torch.tensor(self.audio_labels.iloc[idx, 6])
label_Fi = torch.tensor(self.audio_labels.iloc[idx, 7])
return img, label_A, label_Fw, label_P, label_Fi
I defined the training function
def train(dataloader, model, loss, optimizer):
model.train()
size = len(dataloader.dataset)
for batch, (img_tensors, Y_A, Y_Fw, Y_P, Y_Fi) in enumerate(dataloader):
optimizer.zero_grad()
pred = model(img_tensors.float())
loss_A = cost(pred, Y_A)
loss_Fw = cost(pred, Y_Fw)
loss_P = cost(pred, Y_P)
loss_Fi = cost(pred, Y_Fi)
loss = loss_A + loss_Fw + loss_P + loss_Fi
loss.backward()
optimizer.step()
I used the official CNN model from Microsoft pytorch tutorial image classification which is not apt to my case.
class CNNet(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=5)
self.conv2 = nn.Conv2d(32, 64, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.flatten = nn.Flatten()
self.fc1 = nn.Linear(2112, 50)
self.fc2 = nn.Linear(50, 4)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 4))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 4))
x = self.flatten(x)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = F.relu(self.fc2(x))
return F.log_softmax(x,dim=1)
Can you write my a demo?
Cheers