자꾸 까먹어서 공식홈 튜토리얼 보고 그냥 따라쳤다.

신경망 정의하기(LeNet)

import torch 
import torch.nn as nn 
import torch.nn.functional as F 
class Net(nn.Module):
    
    def __init__(self):
        super(Net, self).__init__()
        #Channel in 1, Channel out 6, filter 3x3
        self.conv1 = nn.Conv2d(1, 6, 3)
        self.conv2 = nn.Conv2d(6, 16, 3)

        self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 = Image Dimension
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # (2, 2) max pooling 
        x = self.conv1(x)
        x = F.relu(x)
        x = F.max_pool2d(x, (2, 2))

        x = self.conv2(x)
        x = F.relu(x) 
        x = F.max_pool2d(x, 2) # Same as (2,2)

        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        size = x.size()[1:] # except batch dimension 
        num_features = 1
        for s in size: 
            num_features *= s
        return num_features 

확인하기

net = Net()
print(net)

결과

Net(
  (conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
  (fc1): Linear(in_features=576, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)

forward함수만 정의하면 backward 함수는 autograd를 사용하여 자동으로 정의됨

학습 가능한 매개변수 반환: net.parameters()

params = list(net.parameters())
print(len(params))
print(params[0].size()) #conv1 .weight
10
torch.Size([6, 1, 3, 3])

임의의 32x32 입력값 넣기

지금 신경망(LeNet)의 입력 크기는 32x32이므로 다를 경우 전처리 해줘야 함

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[ 0.1355,  0.0196,  0.0171,  0.0212, -0.0444,  0.1296, -0.0707, -0.1405,
          0.0687,  0.1195]], grad_fn=<AddmmBackward>)

torch.nn은 미니배치만 지원함

nnConv2dnSamples x nChannels x Height x Width의 4차원 Tensor를 입력으로 함

하나의 샘플만 있을 경우 input.unsqueeze(0)을 사용해서 가상의 차원을 추가

손실 함수

output = net(input)
target = torch.randn(10)
target = target.view(1, -1)
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)
tensor(0.9602, grad_fn=<MseLossBackward>)

역전파(BackPropagation)

net.zero_grad()

print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([ 0.0241,  0.0114, -0.0016,  0.0101,  0.0079,  0.0082])

가중치 갱신

개념

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

하지만 이렇게 하지는 않고 torch.optim을 사용

import torch.optim as optim

optimizer = optim.SGD(net.parameters(), lr=0.01) # Stochastic Gradient Descent

# Training Loop
optimizer.zero_grad()
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()

optimizer.zero_grad() 를 사용하여 수동으로 변화도 버퍼를 0으로 설정(누적방지)