def fit(epochs, model, loss_func, opt, train_dl, valid_dl, tqdm_=False):
"""Modified fit function for reconstruction tasks"""
= tqdm if tqdm_ else lambda x: x
progress for epoch in range(epochs):
model.train()= 0.0, 0
trn_loss, trn_count for xb, _ in progress(train_dl):
= to_device(xb)
xb = loss_func(model(xb), xb) # 👈
loss *_ = xb.shape
bs, += loss.item() * bs
trn_loss += bs
trn_count
loss.backward()
opt.step()
opt.zero_grad()
eval()
model.with torch.no_grad():
= 0.0, 0.0, 0
tst_loss, tot_acc, tst_count for xb, _ in progress(valid_dl):
= to_device(xb)
xb = model(xb)
pred *_ = xb.shape
bs, += bs
tst_count += loss_func(pred, xb).item() * bs
tst_loss
print(
f"{epoch=}: trn_loss={trn_loss / trn_count:.3f}, tst_loss={tst_loss / tst_count:.3f}"
)
Autoencoders
Adapted from:
Autoencoders learn a bottleneck representation that can be “reversed” to reconstruct the original image.
Typically, they are not used on their own but are used to produce compressed representations.
We’ve seen how a convolutional neural network can produce a simple representation of an image: that is, the categorical probability distribution over all the fashion classes. How do reverse this process to reconstruct the original image.
Transpose or “Stride \(\frac{1}{2}\)” convolutions work, but this notebook focuses on the nearest neighbor upsampling. This upsamples the activations from the previous layer and applies a convolutional layer to restore detail.
deconv
deconv (c_in, c_out, ks=3, act=True)
We need to modify the fit
function because the loss function is no longer of the label.
get_model
get_model ()
= get_model()
autoencoder autoencoder
Sequential(
(0): ZeroPad2d((2, 2, 2, 2))
(1): Sequential(
(0): Conv2d(1, 2, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(1): ReLU()
)
(2): Sequential(
(0): Conv2d(2, 4, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(1): ReLU()
)
(3): Sequential(
(0): Conv2d(4, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
(1): ReLU()
)
(4): Sequential(
(0): UpsamplingNearest2d(scale_factor=2.0, mode='nearest')
(1): Conv2d(8, 4, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(2): ReLU()
)
(5): Sequential(
(0): UpsamplingNearest2d(scale_factor=2.0, mode='nearest')
(1): Conv2d(4, 2, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
(2): ReLU()
)
(6): Sequential(
(0): UpsamplingNearest2d(scale_factor=2.0, mode='nearest')
(1): Conv2d(2, 1, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
)
(7): ZeroPad2d((-2, -2, -2, -2))
(8): Sigmoid()
)
with fashion_mnist() as (_, tst_dl):
= next(iter(tst_dl)) xb, _
assert xb.shape == autoencoder(xb.to(def_device)).shape
= get_model()
model with fashion_mnist() as dls:
= optim.AdamW(model.parameters(), lr=0.01)
opt 10, model, F.mse_loss, opt, *dls) fit(
epoch=0: trn_loss=0.052, tst_loss=0.028
epoch=1: trn_loss=0.024, tst_loss=0.021
epoch=2: trn_loss=0.020, tst_loss=0.019
epoch=3: trn_loss=0.019, tst_loss=0.018
epoch=4: trn_loss=0.018, tst_loss=0.018
epoch=5: trn_loss=0.018, tst_loss=0.018
epoch=6: trn_loss=0.018, tst_loss=0.018
epoch=7: trn_loss=0.017, tst_loss=0.017
epoch=8: trn_loss=0.017, tst_loss=0.018
epoch=9: trn_loss=0.017, tst_loss=0.017
= model(xb.to(def_device))
pred 0, ...].squeeze(), pred[0, ...].squeeze()]) show_images([xb[
That looks…not great.
At this point, Jeremy pauses to go over building a framework to iterate on this problem more quickly. Continued in the next notebook.