import%20marimo%0A%0A__generated_with%20%3D%20%220.16.0%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20seaborn%20as%20sns%0A%20%20%20%20import%20torch%0A%20%20%20%20from%20torchvision%20import%20models%2C%20transforms%0A%20%20%20%20from%20PIL%20import%20Image%0A%20%20%20%20return%20Image%2C%20mo%2C%20np%2C%20torch%2C%20transforms%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Homework%0A%0A%20%20%20%20%3E%20**Note**%3A%20it's%20very%20likely%20that%20in%20this%20homework%20your%20answers%20won't%20match%20%0A%20%20%20%20%3E%20the%20options%20exactly.%20That's%20okay%20and%20expected.%20Select%20the%20option%20that's%0A%20%20%20%20%3E%20closest%20to%20your%20solution.%0A%20%20%20%20%3E%20If%20it's%20exactly%20in%20between%20two%20options%2C%20select%20the%20higher%20value.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Dataset%0A%0A%20%20%20%20In%20this%20homework%2C%20we'll%20build%20a%20model%20for%20classifying%20various%20hair%20types.%20%0A%20%20%20%20For%20this%2C%20we%20will%20use%20the%20Hair%20Type%20dataset%20that%20was%20obtained%20from%20%0A%20%20%20%20%5BKaggle%5D(https%3A%2F%2Fwww.kaggle.com%2Fdatasets%2Fkavyasreeb%2Fhair-type-dataset)%20%0A%20%20%20%20and%20slightly%20rebuilt.%0A%0A%20%20%20%20You%20can%20download%20the%20target%20dataset%20for%20this%20homework%20from%20%0A%20%20%20%20%5Bhere%5D(https%3A%2F%2Fgithub.com%2FSVizor42%2FML_Zoomcamp%2Freleases%2Fdownload%2Fstraight-curly-data%2Fdata.zip)%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20wget%20https%3A%2F%2Fgithub.com%2FSVizor42%2FML_Zoomcamp%2Freleases%2Fdownload%2Fstraight-curly-data%2Fdata.zip%0A%20%20%20%20unzip%20data.zip%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20In%20the%20lectures%20we%20saw%20how%20to%20use%20a%20pre-trained%20neural%20network.%20In%20the%20homework%2C%20we'll%20train%20a%20much%20smaller%20model%20from%20scratch.%20%0A%0A%20%20%20%20We%20will%20use%20PyTorch%20for%20that.%0A%0A%20%20%20%20You%20can%20use%20Google%20Colab%20or%20your%20own%20computer%20for%20that.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Data%20Preparation%0A%0A%20%20%20%20The%20dataset%20contains%20around%201000%20images%20of%20hairs%20in%20the%20separate%20folders%20%0A%20%20%20%20for%20training%20and%20test%20sets.%20%0A%0A%20%20%20%20%23%23%23%20Reproducibility%0A%0A%20%20%20%20Reproducibility%20in%20deep%20learning%20is%20a%20multifaceted%20challenge%20that%20requires%20attention%20%0A%20%20%20%20to%20both%20software%20and%20hardware%20details.%20In%20some%20cases%2C%20we%20can't%20guarantee%20exactly%20the%20same%20results%20during%20the%20same%20experiment%20runs.%0A%0A%20%20%20%20Therefore%2C%20in%20this%20homework%20we%20suggest%20to%20set%20the%20random%20number%20seed%20generators%20by%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20torch%0A%0A%20%20%20%20SEED%20%3D%2042%0A%20%20%20%20np.random.seed(SEED)%0A%20%20%20%20torch.manual_seed(SEED)%0A%0A%20%20%20%20if%20torch.cuda.is_available()%3A%0A%20%20%20%20%20%20%20%20torch.cuda.manual_seed(SEED)%0A%20%20%20%20%20%20%20%20torch.cuda.manual_seed_all(SEED)%0A%0A%20%20%20%20torch.backends.cudnn.deterministic%20%3D%20True%0A%20%20%20%20torch.backends.cudnn.benchmark%20%3D%20False%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Also%2C%20use%20PyTorch%20of%20version%202.8.0%20(that's%20the%20one%20in%20Colab).%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20torch)%3A%0A%20%20%20%20def%20set_seed(seed)%3A%0A%20%20%20%20%20%20%20%20np.random.seed(seed)%0A%20%20%20%20%20%20%20%20torch.manual_seed(seed)%0A%0A%20%20%20%20%20%20%20%20if%20torch.cuda.is_available()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20torch.cuda.manual_seed(seed)%0A%20%20%20%20%20%20%20%20%20%20%20%20torch.cuda.manual_seed_all(seed)%0A%0A%20%20%20%20%20%20%20%20torch.backends.cudnn.deterministic%20%3D%20True%0A%20%20%20%20%20%20%20%20torch.backends.cudnn.benchmark%20%3D%20False%0A%0A%20%20%20%20set_seed(42)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Model%0A%0A%20%20%20%20For%20this%20homework%20we%20will%20use%20Convolutional%20Neural%20Network%20(CNN).%20We'll%20use%20PyTorch.%0A%0A%20%20%20%20You%20need%20to%20develop%20the%20model%20with%20following%20structure%3A%0A%0A%20%20%20%20*%20The%20shape%20for%20input%20should%20be%20%60(3%2C%20200%2C%20200)%60%20(channels%20first%20format%20in%20PyTorch)%0A%20%20%20%20*%20Next%2C%20create%20a%20convolutional%20layer%20(%60nn.Conv2d%60)%3A%0A%20%20%20%20%20%20%20%20*%20Use%2032%20filters%20(output%20channels)%0A%20%20%20%20%20%20%20%20*%20Kernel%20size%20should%20be%20%60(3%2C%203)%60%20(that's%20the%20size%20of%20the%20filter)%0A%20%20%20%20%20%20%20%20*%20Use%20%60'relu'%60%20as%20activation%20%0A%20%20%20%20*%20Reduce%20the%20size%20of%20the%20feature%20map%20with%20max%20pooling%20(%60nn.MaxPool2d%60)%0A%20%20%20%20%20%20%20%20*%20Set%20the%20pooling%20size%20to%20%60(2%2C%202)%60%0A%20%20%20%20*%20Turn%20the%20multi-dimensional%20result%20into%20vectors%20using%20%60flatten%60%20or%20%60view%60%0A%20%20%20%20*%20Next%2C%20add%20a%20%60nn.Linear%60%20layer%20with%2064%20neurons%20and%20%60'relu'%60%20activation%0A%20%20%20%20*%20Finally%2C%20create%20the%20%60nn.Linear%60%20layer%20with%201%20neuron%20-%20this%20will%20be%20the%20output%0A%20%20%20%20%20%20%20%20*%20The%20output%20layer%20should%20have%20an%20activation%20-%20use%20the%20appropriate%20activation%20for%20the%20binary%20classification%20case%0A%0A%20%20%20%20As%20optimizer%20use%20%60torch.optim.SGD%60%20with%20the%20following%20parameters%3A%0A%0A%20%20%20%20*%20%60torch.optim.SGD(model.parameters()%2C%20lr%3D0.002%2C%20momentum%3D0.8)%60%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(torch)%3A%0A%20%20%20%20import%20torch.nn%20as%20nn%0A%0A%20%20%20%20class%20BinaryClassification(nn.Module)%3A%0A%20%20%20%20%20%20%20%20def%20__init__(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20super().__init__()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.conv%20%3D%20nn.Conv2d(in_channels%3D3%2C%20out_channels%3D32%2C%20kernel_size%3D3)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.relu%20%3D%20nn.ReLU()%0A%20%20%20%20%20%20%20%20%20%20%20%20self.pool%20%3D%20nn.MaxPool2d(kernel_size%3D2%2C%20stride%3D2)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.linear%20%3D%20nn.Linear(32%20*%2099%20*%2099%2C%2064)%0A%20%20%20%20%20%20%20%20%20%20%20%20self.output%20%3D%20nn.Linear(64%2C%201)%0A%0A%20%20%20%20%20%20%20%20def%20forward(self%2C%20x)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20self.conv(x)%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20self.relu(x)%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20self.pool(x)%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20torch.flatten(x%2C%201)%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20self.linear(x)%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20self.relu(x)%0A%20%20%20%20%20%20%20%20%20%20%20%20x%20%3D%20self.output(x)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20x%0A%20%20%20%20return%20BinaryClassification%2C%20nn%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Question%201%0A%0A%20%20%20%20Which%20loss%20function%20you%20will%20use%3F%0A%0A%20%20%20%20*%20%60nn.MSELoss()%60%0A%20%20%20%20*%20%60nn.BCEWithLogitsLoss()%60%0A%20%20%20%20*%20%60nn.CrossEntropyLoss()%60%0A%20%20%20%20*%20%60nn.CosineEmbeddingLoss()%60%0A%0A%20%20%20%20(Multiple%20answered%20can%20be%20correct%2C%20so%20pick%20any)%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22I%20used%20**nn.BCEWithLogitsLoss()**.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Question%202%0A%0A%20%20%20%20What's%20the%20total%20number%20of%20parameters%20of%20the%20model%3F%20You%20can%20use%20%60torchsummary%60%20or%20count%20manually.%20%0A%0A%20%20%20%20In%20PyTorch%2C%20you%20can%20find%20the%20total%20number%20of%20parameters%20using%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20%23%20Option%201%3A%20Using%20torchsummary%20(install%20with%3A%20pip%20install%20torchsummary)%0A%20%20%20%20from%20torchsummary%20import%20summary%0A%20%20%20%20summary(model%2C%20input_size%3D(3%2C%20200%2C%20200))%0A%0A%20%20%20%20%23%20Option%202%3A%20Manual%20counting%0A%20%20%20%20total_params%20%3D%20sum(p.numel()%20for%20p%20in%20model.parameters())%0A%20%20%20%20print(f%22Total%20parameters%3A%20%7Btotal_params%7D%22)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20*%20896%20%0A%20%20%20%20*%2011214912%0A%20%20%20%20*%2015896912%0A%20%20%20%20*%2020073473%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22The%20number%20of%20parameters%20of%20the%20model%20is%20**20073473**%2C%20as%20shown%20below.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(get_classifier)%3A%0A%20%20%20%20def%20count_model_parameters()%3A%0A%20%20%20%20%20%20%20%20model%2C%20_%2C%20_%20%3D%20get_classifier()%0A%20%20%20%20%20%20%20%20total_params%20%3D%20sum(p.numel()%20for%20p%20in%20model.parameters())%0A%20%20%20%20%20%20%20%20print(f%22Total%20parameters%3A%20%7Btotal_params%7D%22)%0A%0A%20%20%20%20count_model_parameters()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Generators%20and%20Training%0A%0A%20%20%20%20For%20the%20next%20two%20questions%2C%20use%20the%20following%20transformation%20for%20both%20train%20and%20test%20sets%3A%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(transforms)%3A%0A%20%20%20%20def%20get_transforms()%3A%0A%20%20%20%20%20%20%20%20return%20transforms.Compose(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.Resize((200%2C%20200))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.ToTensor()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.Normalize(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mean%3D%5B0.485%2C%200.456%2C%200.406%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20std%3D%5B0.229%2C%200.224%2C%200.225%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%5D)%0A%20%20%20%20return%20(get_transforms%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22We%20don't%20need%20to%20do%20any%20additional%20pre-processing%20for%20the%20images.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(DataLoader%2C%20nn%2C%20torch)%3A%0A%20%20%20%20import%20torch.optim%20as%20optim%0A%0A%20%20%20%20def%20get_device()%20-%3E%20str%3A%0A%20%20%20%20%20%20%20%20return%20torch.device(%22cuda%22%20if%20torch.cuda.is_available()%20else%20%22cpu%22)%0A%0A%20%20%20%20def%20train(%0A%20%20%20%20%20%20%20%20model%3A%20nn.Module%2C%0A%20%20%20%20%20%20%20%20optimizer%3A%20optim.Adam%2C%0A%20%20%20%20%20%20%20%20criterion%3A%20nn.CrossEntropyLoss%2C%0A%20%20%20%20%20%20%20%20train_loader%3A%20DataLoader%2C%0A%20%20%20%20%20%20%20%20val_loader%3A%20DataLoader%2C%0A%20%20%20%20%20%20%20%20checkpoint_filename%3A%20str%2C%0A%20%20%20%20%20%20%20%20num_epochs%3A%20int%20%3D%2010%2C%0A%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20best_val_accuracy%20%3D%200.0%0A%0A%20%20%20%20%20%20%20%20train_history%20%3D%20%7B%22accuracy%22%3A%20%5B%5D%2C%20%22loss%22%3A%20%5B%5D%7D%0A%20%20%20%20%20%20%20%20validation_history%20%3D%20%7B%22accuracy%22%3A%20%5B%5D%2C%20%22loss%22%3A%20%5B%5D%7D%0A%0A%20%20%20%20%20%20%20%20for%20epoch%20in%20range(num_epochs)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22Epoch%20%7Bepoch%2B1%7D%20%2F%20%7Bnum_epochs%7D%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20model.train()%0A%20%20%20%20%20%20%20%20%20%20%20%20running_loss%20%3D%200.0%0A%20%20%20%20%20%20%20%20%20%20%20%20correct%20%3D%200%0A%20%20%20%20%20%20%20%20%20%20%20%20total%20%3D%200%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20inputs%2C%20labels%20in%20train_loader%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inputs%2C%20labels%20%3D%20inputs.to(get_device())%2C%20labels.to(get_device())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20labels%20%3D%20labels.float().unsqueeze(1)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20optimizer.zero_grad()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20outputs%20%3D%20model(inputs)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20loss%20%3D%20criterion(outputs%2C%20labels)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20loss.backward()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20optimizer.step()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20running_loss%20%2B%3D%20loss.item()%20*%20inputs.size(0)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20predicted%20%3D%20(torch.sigmoid(outputs)%20%3E%200.5).float()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20total%20%2B%3D%20labels.size(0)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20correct%20%2B%3D%20(predicted%20%3D%3D%20labels).sum().item()%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20train_loss%20%3D%20running_loss%20%2F%20len(train_loader)%0A%20%20%20%20%20%20%20%20%20%20%20%20train_acc%20%3D%20correct%20%2F%20total%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20train_history%5B%22loss%22%5D.append(train_loss)%0A%20%20%20%20%20%20%20%20%20%20%20%20train_history%5B%22accuracy%22%5D.append(train_acc)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20Train%20Loss%3A%20%7Btrain_loss%3A.4f%7D%2C%20Train%20Acc%3A%20%7Btrain_acc%3A.4f%7D%22)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20val_loss%2C%20val_acc%20%3D%20evaluate(model%2C%20criterion%2C%20val_loader)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20validation_history%5B%22loss%22%5D.append(train_loss)%0A%20%20%20%20%20%20%20%20%20%20%20%20validation_history%5B%22accuracy%22%5D.append(train_acc)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20val_acc%20%3E%20best_val_accuracy%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20best_val_accuracy%20%3D%20val_acc%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20torch.save(model.state_dict()%2C%20checkpoint_filename)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%22%20%20Checkpoint%20saved%3A%20%7Bcheckpoint_filename%7D%22)%0A%0A%20%20%20%20%20%20%20%20return%20train_history%2C%20validation_history%0A%0A%20%20%20%20def%20evaluate(%0A%20%20%20%20%20%20%20%20model%3A%20nn.Module%2C%0A%20%20%20%20%20%20%20%20criterion%3A%20nn.CrossEntropyLoss%2C%0A%20%20%20%20%20%20%20%20val_loader%3A%20DataLoader%0A%20%20%20%20)%20-%3E%20tuple%5Bfloat%5D%3A%0A%20%20%20%20%20%20%20%20model.eval()%0A%20%20%20%20%20%20%20%20val_loss%20%3D%200.0%0A%20%20%20%20%20%20%20%20val_correct%20%3D%200%0A%20%20%20%20%20%20%20%20val_total%20%3D%200%0A%0A%20%20%20%20%20%20%20%20with%20torch.no_grad()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20inputs%2C%20labels%20in%20val_loader%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inputs%2C%20labels%20%3D%20inputs.to(get_device())%2C%20labels.to(get_device())%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20labels%20%3D%20labels.float().unsqueeze(1)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20outputs%20%3D%20model(inputs)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20loss%20%3D%20criterion(outputs%2C%20labels)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20val_loss%20%2B%3D%20loss.item()%20*%20inputs.size(0)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20predicted%20%3D%20(torch.sigmoid(outputs)%20%3E%200.5).float()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20val_total%20%2B%3D%20labels.size(0)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20val_correct%20%2B%3D%20(predicted%20%3D%3D%20labels).sum().item()%0A%0A%20%20%20%20%20%20%20%20val_loss%20%2F%3D%20len(val_loader)%0A%20%20%20%20%20%20%20%20val_acc%20%3D%20val_correct%20%2F%20val_total%0A%20%20%20%20%20%20%20%20print(f%22%20%20Val%20Loss%3A%20%7Bval_loss%3A.4f%7D%2C%20Val%20Acc%3A%20%7Bval_acc%3A.4f%7D%22)%0A%0A%20%20%20%20%20%20%20%20return%20val_loss%2C%20val_acc%0A%20%20%20%20return%20evaluate%2C%20get_device%2C%20optim%2C%20train%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20*%20Use%20%60batch_size%3D20%60%0A%20%20%20%20*%20Use%20%60shuffle%3DTrue%60%20for%20both%20training%2C%20but%20%60False%60%20for%20test.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Image%2C%20get_transforms)%3A%0A%20%20%20%20import%20os%0A%20%20%20%20from%20torch.utils.data%20import%20Dataset%2C%20DataLoader%0A%0A%20%20%20%20class%20HomeworkDataset(Dataset)%3A%0A%20%20%20%20%20%20%20%20def%20__init__(self%2C%20data_dir%2C%20transform%3DNone)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.data_dir%20%3D%20data_dir%0A%20%20%20%20%20%20%20%20%20%20%20%20self.transform%20%3D%20transform%0A%20%20%20%20%20%20%20%20%20%20%20%20self.image_paths%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20self.labels%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20self.classes%20%3D%20sorted(os.listdir(data_dir))%0A%20%20%20%20%20%20%20%20%20%20%20%20self.class_to_idx%20%3D%20%7Bcls%3A%20i%20for%20i%2C%20cls%20in%20enumerate(self.classes)%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20label_name%20in%20self.classes%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label_dir%20%3D%20os.path.join(data_dir%2C%20label_name)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20img_name%20in%20os.listdir(label_dir)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.image_paths.append(os.path.join(label_dir%2C%20img_name))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.labels.append(self.class_to_idx%5Blabel_name%5D)%0A%0A%20%20%20%20%20%20%20%20def%20__len__(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20len(self.image_paths)%0A%0A%20%20%20%20%20%20%20%20def%20__getitem__(self%2C%20idx)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20img_path%20%3D%20self.image_paths%5Bidx%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20image%20%3D%20Image.open(img_path).convert('RGB')%0A%20%20%20%20%20%20%20%20%20%20%20%20label%20%3D%20self.labels%5Bidx%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20self.transform%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20image%20%3D%20self.transform(image)%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20image%2C%20label%0A%0A%20%20%20%20def%20get_dataloader(split%3A%20str%2C%20batch_size%3A%20int%20%3D%2020%2C%20shuffle%3A%20bool%20%3D%20True)%20-%3E%20DataLoader%3A%0A%20%20%20%20%20%20%20%20dataset%20%3D%20HomeworkDataset(%0A%20%20%20%20%20%20%20%20%20%20%20%20data_dir%3Df%22.%2Fmodule-8%2Fdata%2Fhomework%2F%7Bsplit%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transform%3Dget_transforms()%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20return%20DataLoader(dataset%2C%20batch_size%20%3D%20batch_size%2C%20shuffle%20%3D%20shuffle)%0A%0A%20%20%20%20train_loader%20%3D%20get_dataloader(%22train%22%2C%20shuffle%3DTrue)%0A%20%20%20%20val_loader%20%3D%20get_dataloader(%22test%22%2C%20shuffle%3DFalse)%0A%20%20%20%20return%20DataLoader%2C%20HomeworkDataset%2C%20train_loader%2C%20val_loader%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22Now%20fit%20the%20model.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20BinaryClassification%2C%0A%20%20%20%20evaluate%2C%0A%20%20%20%20get_device%2C%0A%20%20%20%20nn%2C%0A%20%20%20%20optim%2C%0A%20%20%20%20torch%2C%0A%20%20%20%20train%2C%0A%20%20%20%20train_loader%2C%0A%20%20%20%20val_loader%2C%0A)%3A%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20weights_filename%20%3D%20%22.%2Fmodule-8%2Fdata%2Fhomework-model.torch%22%0A%0A%20%20%20%20def%20get_classifier()%20-%3E%20tuple%5Bnn.Module%2C%20optim.Adam%2C%20nn.CrossEntropyLoss%5D%3A%0A%20%20%20%20%20%20%20%20device%20%3D%20get_device()%0A%0A%20%20%20%20%20%20%20%20model%20%3D%20BinaryClassification()%0A%20%20%20%20%20%20%20%20model.to(device)%0A%0A%20%20%20%20%20%20%20%20optimizer%20%3D%20optim.SGD(model.parameters()%2C%20lr%3D0.002%2C%20momentum%3D0.8)%0A%20%20%20%20%20%20%20%20criterion%20%3D%20nn.BCEWithLogitsLoss()%0A%0A%20%20%20%20%20%20%20%20return%20model%2C%20optimizer%2C%20criterion%0A%0A%20%20%20%20%23%20This%20values%20were%20manually%20written%20after%20a%20train%20execution%0A%20%20%20%20%23%20Running%20the%20training%20process%20will%20reset%20them%0A%20%20%20%20train_history%20%3D%20%7B%22accuracy%22%3A%20%5B0.7965%2C%200.7965%5D%2C%20%22loss%22%3A%20%5B-2.948%2C%202.948%5D%7D%0A%20%20%20%20validation_history%20%3D%20%7B%22accuracy%22%3A%20%5B%5D%2C%20%22loss%22%3A%20%5B%5D%7D%0A%0A%20%20%20%20if%20not%20Path(weights_filename).exists()%3A%0A%20%20%20%20%20%20%20%20print(%22The%20model%20weights%20were%20not%20found%2C%20so%20the%20model%20will%20be%20trained%22)%0A%20%20%20%20%20%20%20%20model%2C%20optimizer%2C%20criterion%20%3D%20get_classifier()%0A%20%20%20%20%20%20%20%20train_history%2C%20validation_history%20%3D%20train(%0A%20%20%20%20%20%20%20%20%20%20%20%20model%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20optimizer%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20criterion%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20train_loader%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20val_loader%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20checkpoint_filename%20%3D%20weights_filename%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20num_epochs%20%3D%2010%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(%22The%20model%20weights%20were%20found%2C%20so%20the%20model%20weights%20will%20be%20loaded%20and%20the%20model%20will%20be%20evaluated%22)%0A%20%20%20%20%20%20%20%20model%2C%20optimizer%2C%20criterion%20%3D%20get_classifier()%0A%20%20%20%20%20%20%20%20model.load_state_dict(torch.load(weights_filename%2C%20weights_only%3DTrue))%0A%20%20%20%20%20%20%20%20evaluate(%0A%20%20%20%20%20%20%20%20%20%20%20%20model%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20criterion%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20val_loader%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20train_history%2C%20validation_history%0A%20%20%20%20return%20Path%2C%20criterion%2C%20get_classifier%2C%20model%2C%20optimizer%2C%20train_history%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Question%203%0A%0A%20%20%20%20What%20is%20the%20median%20of%20training%20accuracy%20for%20all%20the%20epochs%20for%20this%20model%3F%0A%0A%20%20%20%20*%200.05%0A%20%20%20%20*%200.12%0A%20%20%20%20*%200.40%0A%20%20%20%20*%200.84%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22After%20a%20training%2C%20the%20following%20expression%20returned%200.7965%2C%20so%20the%20closest%20suggested%20option%20is%20**0.84**.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20train_history)%3A%0A%20%20%20%20np.median(train_history%5B%22accuracy%22%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Question%204%0A%0A%20%20%20%20What%20is%20the%20standard%20deviation%20of%20training%20loss%20for%20all%20the%20epochs%20for%20this%20model%3F%0A%0A%20%20%20%20*%200.007%0A%20%20%20%20*%200.078%0A%20%20%20%20*%200.171%0A%20%20%20%20*%201.710%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22After%20a%20training%2C%20the%20following%20expression%20returned%202.948%2C%20so%20the%20closest%20suggested%20option%20is%20**1.710**.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20train_history)%3A%0A%20%20%20%20np.std(train_history%5B%22loss%22%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Data%20Augmentation%0A%0A%20%20%20%20For%20the%20next%20two%20questions%2C%20we'll%20generate%20more%20data%20using%20data%20augmentations.%20%0A%0A%20%20%20%20Add%20the%20following%20augmentations%20to%20your%20training%20data%20generator%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20transforms.RandomRotation(50)%2C%0A%20%20%20%20transforms.RandomResizedCrop(200%2C%20scale%3D(0.9%2C%201.0)%2C%20ratio%3D(0.9%2C%201.1))%2C%0A%20%20%20%20transforms.RandomHorizontalFlip()%2C%0A%20%20%20%20%60%60%60%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(transforms)%3A%0A%20%20%20%20def%20get_augmented_transforms()%3A%0A%20%20%20%20%20%20%20%20return%20transforms.Compose(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.RandomRotation(50)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.RandomResizedCrop(200%2C%20scale%3D(0.9%2C%201.0)%2C%20ratio%3D(0.9%2C%201.1))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.RandomHorizontalFlip()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.Resize((200%2C%20200))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.ToTensor()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transforms.Normalize(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20mean%3D%5B0.485%2C%200.456%2C%200.406%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20std%3D%5B0.229%2C%200.224%2C%200.225%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%5D)%0A%20%20%20%20return%20(get_augmented_transforms%2C)%0A%0A%0A%40app.cell%0Adef%20_(DataLoader%2C%20HomeworkDataset%2C%20get_augmented_transforms)%3A%0A%20%20%20%20def%20get_augmented_dataloader(split%3A%20str%2C%20batch_size%3A%20int%20%3D%2020%2C%20shuffle%3A%20bool%20%3D%20True)%20-%3E%20DataLoader%3A%0A%20%20%20%20%20%20%20%20dataset%20%3D%20HomeworkDataset(%0A%20%20%20%20%20%20%20%20%20%20%20%20data_dir%3Df%22.%2Fmodule-8%2Fdata%2Fhomework%2F%7Bsplit%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20transform%3Dget_augmented_transforms()%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20return%20DataLoader(dataset%2C%20batch_size%20%3D%20batch_size%2C%20shuffle%20%3D%20shuffle)%0A%0A%20%20%20%20train_augmented_loader%20%3D%20get_augmented_dataloader(%22train%22%2C%20shuffle%3DTrue)%0A%20%20%20%20val_augmented_loader%20%3D%20get_augmented_dataloader(%22test%22%2C%20shuffle%3DFalse)%0A%20%20%20%20return%20train_augmented_loader%2C%20val_augmented_loader%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Question%205%20%0A%0A%20%20%20%20Let's%20train%20our%20model%20for%2010%20more%20epochs%20using%20the%20same%20code%20as%20previously.%0A%0A%20%20%20%20%3E%20**Note%3A**%20make%20sure%20you%20don't%20re-create%20the%20model.%0A%20%20%20%20%3E%20we%20want%20to%20continue%20training%20the%20model%20we%20already%20started%20training.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20Path%2C%0A%20%20%20%20criterion%2C%0A%20%20%20%20evaluate%2C%0A%20%20%20%20get_classifier%2C%0A%20%20%20%20model%2C%0A%20%20%20%20optimizer%2C%0A%20%20%20%20torch%2C%0A%20%20%20%20train%2C%0A%20%20%20%20train_augmented_loader%2C%0A%20%20%20%20val_augmented_loader%2C%0A)%3A%0A%20%20%20%20augmented_weights_filename%20%3D%20%22.%2Fmodule-8%2Fdata%2Fhomework-augmented_model.torch%22%0A%0A%20%20%20%20%23%20This%20values%20were%20manually%20written%20after%20a%20train%20execution%0A%20%20%20%20%23%20Running%20the%20training%20process%20will%20reset%20them%0A%20%20%20%20augmented_train_history%20%3D%20%7B%22accuracy%22%3A%20%5B%5D%2C%20%22loss%22%3A%20%5B10.44%5D%7D%0A%20%20%20%20augmented_validation_history%20%3D%20%7B%22accuracy%22%3A%20%5B0.7523%2C%200.7523%2C%200.7523%2C%200.7523%2C%200.7523%2C%200.7523%5D%2C%20%22loss%22%3A%20%5B%5D%7D%0A%0A%20%20%20%20if%20not%20Path(augmented_weights_filename).exists()%3A%0A%20%20%20%20%20%20%20%20print(%22The%20model%20weights%20were%20not%20found%2C%20so%20the%20model%20will%20be%20trained%22)%0A%20%20%20%20%20%20%20%20augmented_train_history%2C%20augmented_validation_history%20%3D%20train(%0A%20%20%20%20%20%20%20%20%20%20%20%20model%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20optimizer%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20criterion%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20train_augmented_loader%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20val_augmented_loader%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20checkpoint_filename%20%3D%20augmented_weights_filename%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20num_epochs%20%3D%2010%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(%22The%20model%20weights%20were%20found%2C%20so%20the%20model%20weights%20will%20be%20loaded%20and%20the%20model%20will%20be%20evaluated%22)%0A%20%20%20%20%20%20%20%20augmented_model%2C%20augmented_optimizer%2C%20augmented_criterion%20%3D%20get_classifier()%0A%20%20%20%20%20%20%20%20augmented_model.load_state_dict(torch.load(augmented_weights_filename%2C%20weights_only%3DTrue))%0A%20%20%20%20%20%20%20%20evaluate(%0A%20%20%20%20%20%20%20%20%20%20%20%20augmented_model%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20augmented_criterion%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20val_augmented_loader%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20augmented_train_history%2C%20augmented_validation_history%0A%20%20%20%20return%20augmented_train_history%2C%20augmented_validation_history%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20What%20is%20the%20mean%20of%20test%20loss%20for%20all%20the%20epochs%20for%20the%20model%20trained%20with%20augmentations%3F%0A%0A%20%20%20%20*%200.008%0A%20%20%20%20*%200.08%0A%20%20%20%20*%200.88%0A%20%20%20%20*%208.88%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22The%20mean%20of%20the%20loss%20for%20the%20augmented%20epochs%20is%2010.44.%20The%20closest%20suggested%20option%20is%20**8.88**.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(augmented_train_history%2C%20np)%3A%0A%20%20%20%20np.mean(augmented_train_history%5B%22loss%22%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%23%20Question%206%0A%0A%20%20%20%20What's%20the%20average%20of%20test%20accuracy%20for%20the%20last%205%20epochs%20(from%206%20to%2010)%0A%20%20%20%20for%20the%20model%20trained%20with%20augmentations%3F%0A%0A%20%20%20%20*%200.08%0A%20%20%20%20*%200.28%0A%20%20%20%20*%200.68%0A%20%20%20%20*%200.98%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22The%20average%20test%20accuracy%20for%20the%20last%205%20epochs%20trained%20with%20augmentations%20is%200.7523%2C%20and%20the%20closest%20suggested%20option%20is%20**0.68**.%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(augmented_validation_history%2C%20np)%3A%0A%20%20%20%20np.average(augmented_validation_history%5B%22accuracy%22%5D%5B5%3A%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20r%22%22%22%0A%20%20%20%20%23%23%20Submit%20the%20results%0A%0A%20%20%20%20*%20Submit%20your%20results%20here%3A%20https%3A%2F%2Fcourses.datatalks.club%2Fml-zoomcamp-2025%2Fhomework%2Fhw08%0A%20%20%20%20*%20If%20your%20answer%20doesn't%20match%20options%20exactly%2C%20select%20the%20closest%20one.%20If%20the%20answer%20is%20exactly%20in%20between%20two%20options%2C%20select%20the%20higher%20value.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
48b0dda0f160f4c7caf66250d2c595f181ca8309cc47645b9dfe44033c56ff08