08wk-1: (합성곱신경망) – MNIST, CIFAR10, XAI란?

Author

최규빈

Published

April 23, 2025

1. 강의영상

2. Imports

import torch
import torchvision
import matplotlib.pyplot as plt
plt.rcParams['figure.figsize'] = (4.5, 3.0)
# CNN 
# net = 2d --> 1d
# 1d: (linr -> relu)
# 2d: (conv -> relu -> mp) 

3. MNIST

train_dataset = torchvision.datasets.MNIST(root='./data', train=True, download=True,transform=torchvision.transforms.ToTensor())
test_dataset = torchvision.datasets.MNIST(root='./data', train=False, download=True,transform=torchvision.transforms.ToTensor())
X,y = next(iter(torch.utils.data.DataLoader(train_dataset,batch_size=6000,shuffle=True)))
XX,yy = next(iter(torch.utils.data.DataLoader(train_dataset,batch_size=1000,shuffle=True)))
100.0%
100.0%
100.0%
100.0%
net = torch.nn.Sequential(
    torch.nn.Conv2d(1,32,kernel_size=5),
    torch.nn.ReLU(),
    torch.nn.MaxPool2d(kernel_size=2),
    torch.nn.Conv2d(32,32,kernel_size=3),
    torch.nn.ReLU(),
    torch.nn.Flatten(),
    #---#
    torch.nn.Linear(3200,10)
)
loss_fn = torch.nn.CrossEntropyLoss()
optimizr = torch.optim.Adam(net.parameters())
#---#
net.to("cuda:0")
X = X.to("cuda:0")
y = y.to("cuda:0")
XX = XX.to("cuda:0")
yy = yy.to("cuda:0")
#---#
for epoc in range(100):
    #1
    netout = net(X)
    #2
    loss = loss_fn(netout,y)
    #3
    loss.backward()
    #4 
    optimizr.step()
    optimizr.zero_grad()
(net(X).argmax(axis=1) == y).float().mean()
tensor(0.9733, device='cuda:0')
(net(XX).argmax(axis=1) == yy).float().mean()
tensor(0.9540, device='cuda:0')
torch.cuda.empty_cache()

4. CIFAR10

train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True,transform=torchvision.transforms.ToTensor())
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True,transform=torchvision.transforms.ToTensor())
X,y = next(iter(torch.utils.data.DataLoader(train_dataset,batch_size=10000,shuffle=True)))
XX,yy = next(iter(torch.utils.data.DataLoader(train_dataset,batch_size=2000,shuffle=True)))
100.0%

A. 직접설계

net = torch.nn.Sequential(
    torch.nn.Conv2d(3,32,kernel_size=5),
    torch.nn.ReLU(),
    torch.nn.MaxPool2d(kernel_size=2),
    torch.nn.Conv2d(32,32,kernel_size=3),
    torch.nn.ReLU(),
    torch.nn.Flatten(),
    #---#
    torch.nn.Linear(4608,10)
)
loss_fn = torch.nn.CrossEntropyLoss()
optimizr = torch.optim.Adam(net.parameters())
#---#
net.to("cuda:0")
X = X.to("cuda:0")
y = y.to("cuda:0")
XX = XX.to("cuda:0")
yy = yy.to("cuda:0")
#---#
for epoc in range(500):
    #1
    netout = net(X)
    #2
    loss = loss_fn(netout,y)
    #3
    loss.backward()
    #4 
    optimizr.step()
    optimizr.zero_grad()
(net(X).argmax(axis=1) == y).float().mean()
tensor(0.7344, device='cuda:0')
(net(XX).argmax(axis=1) == yy).float().mean()
tensor(0.6080, device='cuda:0')
  • 표현력자체에 문제가 있어보임
torch.cuda.empty_cache()

B. 알렉스넷?

img = torch.zeros(1,3*224*224).reshape(1,3,224,224)
img.shape
torch.Size([1, 3, 224, 224])
net = torch.nn.Sequential(
    torch.nn.Conv2d(3,96,kernel_size=(11,11),stride=4),
    torch.nn.ReLU(),    
    torch.nn.MaxPool2d((3,3),stride=2), # default stride는 3
    torch.nn.Conv2d(96,256,kernel_size=(5,5),padding=2),
    torch.nn.ReLU(),
    torch.nn.MaxPool2d((3,3),stride=2), # default stride는 3
    torch.nn.Conv2d(256,384,kernel_size=(3,3),padding=1),
    torch.nn.ReLU(),
    torch.nn.Conv2d(384,384,kernel_size=(3,3),padding=1),
    torch.nn.ReLU(),    
    torch.nn.Conv2d(384,256,kernel_size=(3,3),padding=1),
    torch.nn.ReLU(),    
    torch.nn.MaxPool2d((3,3),stride=2),
    torch.nn.Flatten(),
    torch.nn.Linear(6400,4096),
    torch.nn.ReLU(),
    torch.nn.Dropout(0.5),
    torch.nn.Linear(4096,4096),        
    torch.nn.ReLU(),
    torch.nn.Dropout(0.5),    
    torch.nn.Linear(4096,1000),
)

C. 알렉스넷으로 ImageNet 적합

net[-1] = torch.nn.Linear(4096,10)
img = torch.randn(1,3,32,32)
net(img)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[18], line 1
----> 1 net(img)

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/nn/modules/module.py:1739, in Module._wrapped_call_impl(self, *args, **kwargs)
   1737     return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1738 else:
-> 1739     return self._call_impl(*args, **kwargs)

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/nn/modules/module.py:1750, in Module._call_impl(self, *args, **kwargs)
   1745 # If we don't have any hooks, we want to skip the rest of the logic in
   1746 # this function, and just call forward.
   1747 if not (self._backward_hooks or self._backward_pre_hooks or self._forward_hooks or self._forward_pre_hooks
   1748         or _global_backward_pre_hooks or _global_backward_hooks
   1749         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1750     return forward_call(*args, **kwargs)
   1752 result = None
   1753 called_always_called_hooks = set()

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/nn/modules/container.py:250, in Sequential.forward(self, input)
    248 def forward(self, input):
    249     for module in self:
--> 250         input = module(input)
    251     return input

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/nn/modules/module.py:1739, in Module._wrapped_call_impl(self, *args, **kwargs)
   1737     return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1738 else:
-> 1739     return self._call_impl(*args, **kwargs)

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/nn/modules/module.py:1750, in Module._call_impl(self, *args, **kwargs)
   1745 # If we don't have any hooks, we want to skip the rest of the logic in
   1746 # this function, and just call forward.
   1747 if not (self._backward_hooks or self._backward_pre_hooks or self._forward_hooks or self._forward_pre_hooks
   1748         or _global_backward_pre_hooks or _global_backward_hooks
   1749         or _global_forward_hooks or _global_forward_pre_hooks):
-> 1750     return forward_call(*args, **kwargs)
   1752 result = None
   1753 called_always_called_hooks = set()

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/nn/modules/pooling.py:213, in MaxPool2d.forward(self, input)
    212 def forward(self, input: Tensor):
--> 213     return F.max_pool2d(
    214         input,
    215         self.kernel_size,
    216         self.stride,
    217         self.padding,
    218         self.dilation,
    219         ceil_mode=self.ceil_mode,
    220         return_indices=self.return_indices,
    221     )

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/_jit_internal.py:624, in boolean_dispatch.<locals>.fn(*args, **kwargs)
    622     return if_true(*args, **kwargs)
    623 else:
--> 624     return if_false(*args, **kwargs)

File ~/anaconda3/envs/dl2025/lib/python3.9/site-packages/torch/nn/functional.py:830, in _max_pool2d(input, kernel_size, stride, padding, dilation, ceil_mode, return_indices)
    828 if stride is None:
    829     stride = torch.jit.annotate(List[int], [])
--> 830 return torch.max_pool2d(input, kernel_size, stride, padding, dilation, ceil_mode)

RuntimeError: Given input size: (256x2x2). Calculated output size: (256x0x0). Output size is too small
net[:5](img).shape
torch.Size([1, 256, 2, 2])
net[5]
MaxPool2d(kernel_size=(3, 3), stride=2, padding=0, dilation=1, ceil_mode=False)

실패 ㅠㅠ

D. renset18

- res: https://arxiv.org/pdf/1512.03385

net = torchvision.models.resnet18()
# net 
net.fc = torch.nn.Linear(512,10)
loss_fn = torch.nn.CrossEntropyLoss()
optimizr = torch.optim.Adam(net.parameters())
#---#
net.to("cuda:0")
X = X.to("cuda:0")
y = y.to("cuda:0")
XX = XX.to("cuda:0")
yy = yy.to("cuda:0")
#---#
for epoc in range(500):
    #1
    netout = net(X)
    #2
    loss = loss_fn(netout,y)
    #3
    loss.backward()
    #4 
    optimizr.step()
    optimizr.zero_grad()
net.eval()
print((net(X).argmax(axis=1) == y).float().mean())
print((net(XX).argmax(axis=1) == yy).float().mean())
tensor(1., device='cuda:0')
tensor(0.5930, device='cuda:0')
  • 오버피팅이 있어보긴하지만 표현력자체는 올라감
torch.cuda.empty_cache()

E. resnet18, pretrained=True

- 아이디어: 하나를 잘하는 모델은 다른것도 잘하지 않을까? <– transfer learning

net = torchvision.models.resnet18(pretrained=True) # 아키텍처 + 학습된 가중치까지 
net.fc = torch.nn.Linear(512,10)
/home/cgb3/anaconda3/envs/dl2025/lib/python3.9/site-packages/torchvision/models/_utils.py:208: UserWarning: The parameter 'pretrained' is deprecated since 0.13 and may be removed in the future, please use 'weights' instead.
  warnings.warn(
/home/cgb3/anaconda3/envs/dl2025/lib/python3.9/site-packages/torchvision/models/_utils.py:223: UserWarning: Arguments other than a weight enum or `None` for 'weights' are deprecated since 0.13 and may be removed in the future. The current behavior is equivalent to passing `weights=ResNet18_Weights.IMAGENET1K_V1`. You can also use `weights=ResNet18_Weights.DEFAULT` to get the most up-to-date weights.
  warnings.warn(msg)
loss_fn = torch.nn.CrossEntropyLoss()
optimizr = torch.optim.Adam(net.parameters())
#---#
net.to("cuda:0")
X = X.to("cuda:0")
y = y.to("cuda:0")
XX = XX.to("cuda:0")
yy = yy.to("cuda:0")
#---#
for epoc in range(500):
    #1
    netout = net(X)
    #2
    loss = loss_fn(netout,y)
    #3
    loss.backward()
    #4 
    optimizr.step()
    optimizr.zero_grad()
net.eval()
print((net(X).argmax(axis=1) == y).float().mean())
print((net(XX).argmax(axis=1) == yy).float().mean())
tensor(1., device='cuda:0')
tensor(0.8050, device='cuda:0')
  • 잘함 (오버피팅은 여전히 있음)
torch.cuda.empty_cache()

5. XAI 란?

https://brunch.co.kr/@hvnpoet/140