--25.PyTorch 모델 학습.ipynb--
import numpy as np
import matplotlib.pyplot as plt
import os
import torch
from torchvision import datasets, transforms
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module) : # nn.Module을 상속받아 클래스 정의
def init(self) :
super(Net, self).init()
self.conv1 = nn.Conv2d(1, 20, 5, 1) # (in-channel, out-channel, kernel-size, stride)
self.conv2 = nn.Conv2d(20, 50, 5, 1)
self.fc1 = nn.Linear(4 4 50, 500) # (in-feature, out-feature)
self.fc2 = nn.Linear(500, 10)
def forward(self, x) : # x : 입력
# 1. Feature Extraction
x = F.relu(self.conv1(x)) # Conv 결과는 relu 활성화 함수를 거치게 한다.
x = F.max_pool2d(x, 2, 2) # (input, kernel, stride)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2, 2)
# conv1 입력 (1, 28, 28) => conv1 => (20, 24, 24) => maxpool => (20, 12, 12)
# conv2 입력 (20, 12, 12) => conv2 => (50, 8, 8) => maxpool => (50, 4, 4)
# 2. Classification (Fully connected)
# view(batchsize, ) <= batch size 는 몇개일지 모르기 때문에 -1로 지정
x = x.view(-1, 4 * 4 * 50)
x = F.relu(self.fc1(x))
x = self.fc2(x)
# 3. output은 softmax
return F.log_softmax(x, dim=1)
model = Net() # 모델 생성!
base_path = r'./'
train_loader = torch.utils.data.DataLoader(
datasets.MNIST(base_path,
train = True, download=True,
transform=transforms.Compose([transforms.ToTensor()])
),
batch_size = 1,
)
image, label = next(iter(train_loader))
image.shape, label.shape
result = model.forward(image)
result
model.conv1
model.conv1(image)
import torch.optim as optim # Optimizer
no_cuda = False
use_cuda = not no_cuda and torch.cuda.is_available()
device = torch.device('cuda' if use_cuda else 'cpu')
device
seed = 1
batch_size = 64
test_batch_size = 64
torch.manual_seed(seed)
train_loader = torch.utils.data.DataLoader(
datasets.MNIST(base_path, train = True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))
])),
batch_size = batch_size, shuffle = True)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST(base_path, train = False,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307, ), (0.3081, ))
])),
batch_size = test_batch_size, shuffle = True)
import torch.optim as optim # Optimizer
model = Net().to(device) # model을 생성하여 device에 컴파일 해준다.
params = list(model.parameters())
params
len(params) # 학습 가능 레이어 4개 + 4(각 레이어 마다 bias)
for param in params :
print(param.size())
"""
torch.Size([20, 1, 5, 5]) <-- conv1 의 weight size
torch.Size([20]) <-- conv1 의 bias (out channel의 개수만큼)
torch.Size([50, 20, 5, 5]) <-- conv2 의 weight size
torch.Size([50]) <-- conv1 의 bias (out channel의 개수만큼)
torch.Size([500, 800]) <-- fc1 의 weight size (out_channel, in_channel)
torch.Size([500]) <-- fc1 의 bias (out_channel의 개수만큼)
torch.Size([10, 500]) <-- fc2 의 weight size
torch.Size([10]) <-- fc2 의 bias
"""
None
optimizer = optim.SGD(model.parameters(),
lr=0.001, # learning rate
momentum=0.5)
optimizer
model.train()
※ 일단 '하나' 씩 넣어서 확인, 나중에는 for로 돌릴 예정
data, target = next(iter(train_loader)) # 첫 batch 꺼내기. (현재 64로 세팅)
data.shape, target.shape
data, target = data.to(device), target.to(device)
data.shape, target.shape
optimizer.zero_grad()
output = model(data) # 예측값 아님! (지금은 train mode 다!) 잠시후 loss 값 계산해야한다.
output.shape
loss = F.nll_loss(output, target)
loss
loss.backward() # 기울기 (gradient) 계산
optimizer.step()
이상이 '학습' 의 "1 스텝"입니다
위의 최적화 과정을 반복하여 학습시작
epochs = 1
log_interval = 100 # 로그를 확인하기 위해 몇 스텝마다 로그 출력할지 결정
for epoch in range(1, epochs + 1) :
model.train()
for batch_idx, (data, target) in enumerate(train_loader) :
# 데이터를 device에 compile
data, target = data.to(device), target.to(device)
# 3. 기울기 clear
optimizer.zero_grad()
# 4. model에 데이터 넣기
output = model(data)
# 5. loss 계산
loss = F.nll_loss(output, target)
# 6. back propagation 하여 gradient 계산
loss.backward()
# 7, parameter 업데이트
optimizer.step
# 중간중간에 로그 확인
if batch_idx % log_interval == 0 :
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100 * batch_idx / len(train_loader),
loss.item() # loss 값 <-- 학습이 진행되면서 loss 값이 내려가는지 관찰
))
model.eval() # 평가 모드 전환
test_loss = 0
correct = 0
with torch.no_grad() :
data, target = next(iter(test_loader)) # 테스트 데이터에서 batch 하나 꺼내기
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
# reduction 을 안하면 batch size 별로, 데이터별로 따로 따로 계산하게 되는데
# reduction=sum 을 해주면 하나의 스칼라 값으로 리턴해줌
# 그것을 test_loss 에 누적 더해주는 것
pred = output.argmax(dim = 1, keepdim = True)
# keepdim=True : output 과 pred 의 dimention 유지
correct = pred.eq(target.view_as(pred)).sum().item() # pred 과 target 이 얼마나 같은지 판정
test_loss
correct # 한개의 batch (64개) 에서 총 맞춘 개수
output.shape
pred.shape
target
target.shape
target.view_as(pred).shape
pred.eq(target.view_as(pred))
pred.eq(target.view_as(pred)).sum()
pred.eq(target.view_as(pred)).sum().item()
pred.eq(target.view_as(pred)).sum().item() / test_batch_size # 한 bacth size 에서 맞춘 확률
test_loss # 누적된 loss 값
import numpy as np
import matplotlib.pyplot as plt
import os
import torch
from torchvision import datasets, transforms
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module): # nn.Module 을 상속받아 클래스 정의
def init(self):
super(Net, self).init()
self.conv1 = nn.Conv2d(1, 20, 5, 1) # (in-channel, out-channel, kernel-size, stride)
self.conv2 = nn.Conv2d(20, 50, 5, 1)
self.fc1 = nn.Linear(4 4 50, 500) # (in-feature, out-feature)
self.fc2 = nn.Linear(500, 10)
def forward(self, x): # x: 입력
# 1. Feature Extraction
x = F.relu(self.conv1(x)) # Conv 결과는 relu 활성화 함수를 거치게 한다.
x = F.max_pool2d(x, 2, 2) # (input, kernel, stride)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2, 2)
# conv1 입력 (1, 28, 28) => conv1 => (20, 24, 24) => maxpool => (20, 12, 12)
# conv2 입력 (20, 12, 12) => conv2 => (50, 8, 8) => maxpool => (50, 4, 4)
# 2. Classification (Fully connected)
# view(batchsize, ) <= batch size 는 몇개일지 모르기 때문에 -1로 지정
x = x.view(-1, 4 * 4 * 50)
x = F.relu(self.fc1(x))
x = self.fc2(x)
# 3. output 은 softmax
return F.log_softmax(x, dim=1)
model = Net() # 모델 생성!
base_path = '.'
train_loader = torch.utils.data.DataLoader(
datasets.MNIST(base_path,
train=True, download=True,
transform=transforms.Compose([transforms.ToTensor()])
),
batch_size=1
)
image, label = next(iter(train_loader))
image.shape, label.shape
result = model.forward(image)
result
model.conv1
model.conv1(image) # 각 레이어에 데이터 통과시켜볼수도 있다
no_cuda = False
use_cuda = not no_cuda and torch.cuda.is_available()
device = torch.device('cuda' if use_cuda else 'cpu')
device
seed = 1
batch_size = 64
test_batch_size = 64
torch.manual_seed(seed)
train_loader = torch.utils.data.DataLoader(
datasets.MNIST(base_path,train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=batch_size, shuffle=True)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST(base_path,train=False,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=test_batch_size, shuffle=True)
import torch.optim as optim # Optimizer
model = Net().to(device) # model 을 생성하여 device에 컴파일 해줍니다.
params = list(model.parameters())
params
len(params) # 학습 가능 레이어 4개 + 4(각 레이어마다 bias)
for param in params:
print(param.size())
"""
torch.Size([20, 1, 5, 5]) <-- conv1 의 weight size
torch.Size([20]) <-- conv1 의 bias (out channel 의 개수만큼)
torch.Size([50, 20, 5, 5]) <-- conv2 의 weight size
torch.Size([50]) <-- conv2 의 bias
torch.Size([500, 800]) <-- fc1 의 weight size(out_channel, in_channel)
torch.Size([500]) <-- fc1 의 bias (out_channel 의 개수만큼)
torch.Size([10, 500]) <-- fc2 의 weight size
torch.Size([10]) <-- fc2 의 bias
"""
None
optimizer = optim.SGD(model.parameters(),
lr=0.001, # learning rate
momentum=0.5)
optimizer
model.train()
※ 일단 '하나' 씩 넣어서 확인, 나중에는 for 로 돌릴 예정
data, target = next(iter(train_loader)) # 첫 batch 꺼내기. (현재 64개로 세팅)
data.shape, target.shape
data, target = data.to(device), target.to(device)
data.shape, target.shape
optimizer.zero_grad()
output = model(data) # 예측값 아님! (지금은 train mode 다!) 잠시후 loss 값 계산해야 한다
output.shape
loss = F.nll_loss(output, target)
loss
loss.backward() # 기울기 (gradient) 계산
optimizer.step()
이상이 '학습' 의 "1 스텝"입니다
위의 최적화 과정을 반복하여 학습시작
epochs = 1
log_interval = 100 # 로그를 확인하기 위해 몇 스텝마다 로그 출력할지 결정
for epoch in range(1, epochs + 1):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
# 데이터를 device 에 compile
data, target = data.to(device), target.to(device)
# 3. 기울기 clear
optimizer.zero_grad()
# 4. model 에 데이터 넣기
output = model(data)
# 5. loss 계산
loss = F.nll_loss(output, target)
# 6. back propagation 하여 gradient 계산
loss.backward()
# 7. parameter 업데이트
optimizer.step()
# 중간중간에 로그 확인
if batch_idx % log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100 * batch_idx / len(train_loader),
loss.item() # loss 값 <- 학습이 진행되면서 loss 값이 내려가는지 관찰
))
model.eval() # 평가 모드 전환
test_loss = 0
correct = 0
with torch.no_grad():
data, target = next(iter(test_loader)) # 테스트 데이터에서 batch 하나 꺼내기
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
# reduction 을 안하면 batch size 별로, 데이터별로 따로 따로 계산하게 되는데
# reduction=sum 을 해주면 하나의 스칼라 값으로 리턴해줌
# 그것을 test_loss 에 누적 더해주는 것
pred = output.argmax(dim=1, keepdim=True)
# keepdim=True : output 과 pred 의 dimension 유지
correct = pred.eq(target.view_as(pred)).sum().item() # pred 과 target 이 얼마나 같은지 판정
test_loss
correct # 한개의 batch (64개) 에서 총 맞춘 개수
output.shape
pred.shape
target
target.shape
target.view_as(pred).shape
pred.eq(target.view_as(pred))
pred.eq(target.view_as(pred)).sum()
pred.eq(target.view_as(pred)).sum().item()
pred.eq(target.view_as(pred)).sum().item() / test_batch_size # 한 batch size 에서 맞춘 확률
test_loss # 누적된 loss 값
test_loss / test_batch_size
model.eval()
test_loss = 0
correct = 0
with torch.no_grad() :
for data, target in test_loader : # 모든 batch 를 다 돌려 볼거다
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # 얼마나 틀렸는지 모으고
pred = output.argmax(dim = 1, keepdim = True)
correct += pred.eq(target.view_as(pred)).sum().item() # 얼마나 맞혔는지도 모으고
test_loss /= len(test_loader.dataset) # loss의 평균값
print('\nTest set : Average Loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset), 100.0 * correct / len(test_loader.dataset)
))
epochs = 5
for epoch in range(1, epochs + 1):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100 * batch_idx / len(train_loader),
loss.item()
))
model.eval()
test_loss = 0
correct = 0
with torch.no_grad() :
for data, target in test_loader : # 모든 batch 를 다 돌려 볼거다
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item() # 얼마나 틀렸는지 모으고
pred = output.argmax(dim = 1, keepdim = True)
correct += pred.eq(target.view_as(pred)).sum().item() # 얼마나 맞혔는지도 모으고
test_loss /= len(test_loader.dataset) # loss의 평균값
print('\nTest set : Average Loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset), 100.0 * correct / len(test_loader.dataset)
))
torch.save(model.state_dict(), os.path.join(base_path, 'MNIST.pth'))
model = None
model = Net().to(device)
model.load_state_dict(torch.load(os.path.join(base_path, 'MNIST.pth')))
import glob
img_paths = glob.glob('digit*.png')
img_paths
from PIL import Image
import PIL.ImageOps as ops
def predict(file_path) :
img = Image.open(file_path)
mono8img = img.convert('L') # gray scale 전환
invImg = ops.invert(mono8img) # 색상 반전
resizeImg = invImg.resize((28, 28)) # resize
data_arr = np.array(resizeImg).reshape(1, 1, 28, 28) # torch 에서 사용하는 입력 shape로 변환
tensor = torch.Tensor(data_arr) # Torch에서 사용하는 Tensor 로 변환
transformed = transforms.Normalize((0.1307,), (0.3081,))(tensor) # 학습에 사용된 것과 동일한 scaling
output = model(transformed.to(device))
pred = output.argmax(dim = 1, keepdim = True)
return pred.item()
for img_path in img_paths :
print(img_path, '->', predict(img_path))