20240806 모각코 활동 6회차
오늘의목표
CNN 실습 - MNIST 이미지 분류 ( RESNET은 7회차에 진행할 예정 )
import torch #pytorch 가져오기
데이터 가져오기
#데이터셋불러오고 텐서로 바꿔주기
from torchvision import datasets #데이터셋 불러오고
from torchvision.transforms import ToTensor #텐서로 바꿔주기
#datasets에서 MNIST 가져와서 훈련데이터와 테스트데이터 가져와주기.
#datasets.MNIST(root - 데이터가 저장될 경로, train - train이 true 이면 train data이고 false면 test data, download - 데이터 없으면 인터넷에서 다운로드해줌 , transform - transform을 ToTensor로 지정해주지 않으면 텐서의 형식이 아닌, PIL이미지로 데이터가 가져와지게 된다)
train_data = datasets.MNIST(
root = "data",
train = True, #train data를 다운로드
transform = ToTensor(),
download = True
)
test_data = datasets.MNIST(
root = 'data',
train = False, #test data를 다운로드
transform = ToTensor()
)
데이터 확인하기
#학습데이터 확인
print(train_data)
print(train_data.data.size())
# 데이터셋의 이름은 MNIST
# 데이터의 수는 60000개
# 훈련데이터
# StandardTransform(데이터셋에 일관되게 적용되는 변환의 표준을 정의) -> Transform: ToTensor() #이미지 데이터들을 모두 일관되게 텐서 형태로 변환하겠다는 것을 의미.
#테스트데이터 확인
print(test_data)
print(test_data.data.size())
#데이터의 수가 10000 인 것과 테스트데이터라는 것을 제외하면 나머지 속성은 학습데이터와 동일함.
#데이터 시각적으로 확인
import matplotlib.pyplot as plt #시각적 확인을 위해 matplotlib을 사용.
fig, ax = plt.subplots() # fig -> 데이터가 담기는 프레임 / ax -> 실제 데이터가 그려지는 캔버스
ax.imshow(train_data.data[0], cmap='gray') #데이터의 모습
#이미지 위에 각 픽셀 값을 표시해서 나타내보기
for i in range(train_data.data[0].shape[0]): # i와j는 텍스트를 표시할 위치를 지정하기 위함.
for j in range(train_data.data[0].shape[1]):
c = 1 if train_data.data[0][i, j].item() < 125 else 0 # 이미지의 각 픽셀 값( train_data.data[0][i,j].item() )이 125보다 작으면 c = 1 흰색을 사용, 크면 c = 0 검정 사용.
ax.text(j, i, str(train_data.data[0][i, j].item()), color=(c, c, c), ha='center', va='center', fontsize=5) # text()를 사용하여 이미지 위에 텍스트 그리기
plt.title("%i" % train_data.targets[0])
plt.show
데이터 준비하기
from torch.utils.data import DataLoader
# DataLoader -> 데이터를 미니배치 형태로 만들어서 우리가 실제로 학습할 때 이용할 수 있도록 함.
#DataLoader(dataset 데이터 , batch_size=1 한 번의 배치 안에 있는 샘플 사이즈, shuffle=False 데이터셋을 섞어서 데이터가 학습되는 순서를 바꿈, num_workers=0 동시에 처리하는 프로세서의 수. 하나 더 추가하면 20%정도 속도가 빨라짐.)
#배치 학습 -> 전체 데이터를 n등분 하여 학습.
loaders = {
'train' : torch.utils.data.DataLoader(train_data,
batch_size=100,
shuffle=True,
num_workers=1),
'test' : torch.utils.data.DataLoader(test_data,
batch_size=100,
shuffle=True,
num_workers=1)
}
loaders
CNN 모델 설정하기
class CNN(torch.nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.layer1 = torch.nn.Sequential(
torch.nn.Conv2d(1, 16, kernel_size=5, stride=1, padding=2), #컨볼루션 레이어(합성곱층) #1차원(1개채널) 데이터를 받아 16개의 feature(16개의채널)로 나누겟다!!임.
torch.nn.ReLU(), #ReLU층
torch.nn.MaxPool2d(kernel_size=2, stride=2)) #풀링층
self.layer2 = torch.nn.Sequential(
torch.nn.Conv2d(16, 32, kernel_size=5, stride=1, padding=2),
torch.nn.ReLU(),
torch.nn.MaxPool2d(kernel_size=2, stride=2))
# layer 1, layer2 층까지는 이미지를 형상으로 분할하고 분석하는 부분
# 다음 fc 층에서는 이미지를 분류 예측하는 부분.
self.fc = torch.nn.Linear(32 * 7 * 7, 10, bias=True) #32*7*7만큼의 입력을 linear레이어에 의해 계산되게 해서... 10개의 출력( MNIST 이미지를 0부터 9까지 분류해야하기때문 )이 나오도록 함.
torch.nn.init.xavier_uniform_(self.fc.weight) # 신경망의 가중치를 초기화 ( 신경망의 가중치를 학습 전에 적절한 값으로 설정하는 과정 )
# __init__에서는 필요한 레이어들을 정의내렸다고 볼 수 있음.
# 아래 forward(얘가 실제적인 모델의 형태가 됨)에서 사용한다.
def forward(self, x): #순전파 #순전파만 지정해주어도 pytorch에서는 역전파 과정을 매우 쉽게 할 수 있도록 해준다.
out = self.layer1(x)
out = self.layer2(out)
out = out.view(out.size(0), -1) # view() 함수는 텐서의 크기를 변경하는 데 사용 # 데이터를 완전 연결(fc) 층에 전달하기 위해 2차원 또는 3차원 텐서를 1차원 벡터로 평탄화 하는 과정이 필요함.
out = self.fc(out)
return out
model = CNN()
model
CNN(
(layer1): Sequential(
(0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(layer2): Sequential(
(0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
)
(fc): Linear(in_features=1568, out_features=10, bias=True)
)
학습하기
learning_rate = 0.01 # 파라미터를 얼마나 업데이트할 것인지를 결정. 학습률, step size. 너무 크지도 작지도 않아야 함.
loss_func = torch.nn.CrossEntropyLoss() # 모델 예측과 실제값 간의 차이를 측정하는 손실함수.
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) # 손실함수를 통해 나온 을 최소화하기 위해 가중치를 업데이트하는 방법
training_epochs = 10 # 전체 데이터셋을 몇 번 반복할 것인지 결정.
# 반복의 횟수는 epoch과 batch의 크기에 따라 결정
total_batch = len(loaders['train'])
for epoch in range(training_epochs):
avg_cost = 0
for X, Y in loaders['train']:
optimizer.zero_grad() # 학습에서, 역전파를 거칠 때 마다 각 .grad 값에 변화도가 저장이 되는데, 이어지는 다음 학습에서 .grad의 값을 0으로 초기화시켜주지 않으면 이전에 저장된 변화도 값이 다음 학습에 영향을 주기 때문에 원하는 방향으로 학습하기 힘들다. 그래서zero_grad를 통해 .grad 의 값들을 0으로 초기화시켜준다.
pred = model(X) #순전파
cost = loss_func(pred, Y) #손실함수계산
cost.backward() #역전파
optimizer.step() # 역전파 단계에서 수집된 변화도로 매개변수를 조정
avg_cost += cost / total_batch
print('[Epoch: {:>4}] cost = {:>.9}'.format(epoch + 1, avg_cost))
# `epoch + 1` 값을 최소 4칸의 너비로 오른쪽 정렬하여 출력
# `avg_cost` 값을 최소 9자리까지 나타내어 오른쪽 정렬하여 출력
print('Learning Finished....>_<')
[Epoch: 1] cost = 0.0461711548
[Epoch: 2] cost = 0.0472225286
[Epoch: 3] cost = 0.0413064063
[Epoch: 4] cost = 0.0417594947
[Epoch: 5] cost = 0.0395734794
[Epoch: 6] cost = 0.0441303253
[Epoch: 7] cost = 0.0408433564
[Epoch: 8] cost = 0.043582622
[Epoch: 9] cost = 0.0441764817
[Epoch: 10] cost = 0.0412645154
Learning Finished....>_<