기존 DNN(Deep Neural Network)의 문제점.

-> 위와 같은 문제점 때문에 CNN(Convolutional Neural Networks)가 등장하게 됨.
이미지 2개를 합친다할때, 하나를 y축 기준으로 대칭 시킨 후 차례차례 곱해 나가는 것.



이런 원리를 토대로 Neural Network의 이미지 학습에도 적용한 것이 "CNN"이다!
앞서 언급한 convolution 과정을 이미지로 표현하면 아래와 같다.
맨왼쪽 그림이 이미지, 중간 이미지는 [1 1, 1 1] 필터
필터와 각 위치에 해당하는 값들을 모두 곱하고 더해서 맨오른쪽 이미지처럼 만들어 주게 된다.
일반화시키면 아래와 같이 표현할 수 있다.
그런데, 이렇게 되면 기존의 3X3 이미지가 2X2로 줄어들게 된다.
정보의 손실이 일어남과 동시에 특징들을 뭉뚱그려서 더 작은값으로 저장하게 되는 것.
-> 이 "정보의 손실" 이라는것을 최소화 하고자 "Zero Padding"을 사용함.
이렇게, 학습하는 이미지의 끝자락 부분을 0으로 채워(padding)서, 필터를 통과시킨 후에도 3X3의 사이즈를 유지하도록 하는 것.
padding을 하면 데이터의 손실을 줄여줄 수 있지만, 원래 데이터에 없던 데이터를 붙였기 때문에 noise가 발생. -> noise의 영향을 최소화하기 위해 padding 값을 0으로 하는 것.
여기서, filter가 무엇인가?
kernel이라고도 부르며, 층의 전체 뉴런에 적용된 하나의 필터(filter)는 하나의 특성맵(feature map)을 만듦.
합성곱 층은 여러 가지 필터를 가지고 필터마다 하나의 특성 맵을 출력.
하나의 합성곱 층이 입력에 여러 필터를 동시에 적용하여 입력에 있는 여러 특성을 감지할 수 있음.
아래 그림은 여러 가지 특성 맵으로 이루어진 합성곱 층과 3개의 컬러 채널임.
3D에서 특성맵들이 만들어지는 과정은 아래와 같음.
3개 채널의 입력 데이터와 3개 채널의 필터를 각각 Convolution하고 나온 3개의 출력값을 더함.
이렇게 나온 출력 데이터 = 하나의 feature map이 되는 것!!!
filter가 이동하는 간격을 stride라고 함. 이 값은 임의로 지정할 수 있음.
출력 데이터의 크기는 입력 데이터의 크기, 필터의 크기, stride 값, padding 값에 의해 결정됨.
-> Max-pooling과 Average-pooling이 있음.
Max-pooling: 해당 구역의 최대값을 찾는 방식.
Average-pooling: 해당 구역의 평균값을 계산하는 방식.
예를 들어, max-pooling은 아래와 같이 [1,2,0,1]중 가장 큰 2가 선택되는 방식이다.
위와 같이 풀링은 정보 손실이 엄청나지만, 하는 이유가 있다.
overfitting 방지하기 위해
ex) 이미지: 64x64, 필터: 8x8이 300개, stride: 1, padding:0 이라면?
한 개의 특성맵에 (64-8+1)x(64-8+1) = 3349개의 feature가 존재, 이러한 특성맵이 합성곱 층에 300개 존재하므로 총 974,700개의 특성이 존재하게 됨.
이렇게 feature가 많다면 overfitting이 생길 가능성이 높음.
이를 pooling을 통해 데이터의 크기를 줄여 조절하는 것.
평행이동이나 변형에 대해서 일정 수준의 불변성을 갖기 위해
데이터가 오른쪽으로 1칸 이동하고 Max-pooling을 해도, 원래 데이터에서 Max-pooling을 한 결과와 동일함. -> 어떤 물체의 구체적인 위치가 아닌 존재 여부가 더 중요할 땐 이런 일정 수준의 이동에 대한 불변성이 유용하게 작용!
Convolution을 통한 filtering과 max pooling을 반복하여, 중요한 특징을 정제한 후, classification 하는 것.

입력 이미지를 Convolution과 Max-Pooling을 통해 깊이를 깊게하고 size를 줄여준 후, 4x4xn2의 데이터를 1차원의 데이터로(Flatten) 만든다. -> 이렇게 1차원으로 만든 데이터를 DNN 네트워크를 통해 최종 output을 출력한다!
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.init as init
import matplotlib.pyplot as plt
import torchvision.datasets as dset
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
MNIST라는 숫자 데이터셋 사용을 위해, torchvision 라이브러리를 미리 다운로드 하기.
batch_size = 256
learning_rate = 0.0002
num_epoch = 10
mnist_train = dset.MNIST("./",train=True, transform = transforms.ToTensor(),target_transform=None, download = True)
mnist_test = dset.MNIST("./", train=False, transform = transforms.ToTensor(), target_transform=None, download = True)
train_loader = torch.utils.data.DataLoader(mnist_train,batch_size=batch_size,shuffle=True,num_workers=2,drop_last=True)
test_loader = torch.utils.data.DataLoader(mnist_test,batch_size=batch_size,shuffle=False,num_workers=2,drop_last=True)
DataLoader 부분에서
class CNN(nn.Module):
def __init__(self) :
super(CNN,self).__init__()
self.layer = nn.Sequential(
nn.Conv2d(1,16,5), # 이미지 크기: 28x28 -> 24x24
nn.ReLU(),
nn.Conv2d(16,32,5), # 이미지 크기: 24x24 -> 20x20
nn.ReLU(),
nn.MaxPool2d(2,2), # 이미지 크기: 20x20 -> 10x10
nn.Conv2d(32,64,5), # 이미지 크기: 10x10 -> 6x6
nn.ReLU(),
nn.MaxPool2d(2,2) # 이미지 크기: 6x6 -> 3x3
)
self.fc_layer = nn.Sequential(
nn.Linear(64*3*3,100),
nn.ReLU(),
nn.Linear(100,10)
)
def forward(self,x):
out = self.layer(x)
out = out.view(batch_size, -1) # 전결합층을 위해서 Flatten하는 과정.
out = self.fc_layer(out)
return out
super(CNN,self).__init__() Super class로 지금 작성하고있는 클래스 자체를 초기화.nn.Conv2d Convolution Filtering이라는 Signal Processing적인 방법으로 이미지를 처리 하는것 nn.Conv2d(1,16,5)self.layer CNN이 끝난 이후, 최종적으로 나오는 결과물은 [batch_size,64,3,3]nn.Linear(100,10)def forward(self,x)device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = CNN().to(device)
loss_func = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),lr=learning_rate)
loss_arr = []
for i in range(num_epoch):
for j,[image,label] in enumerate(train_loader):
x = image.to(device) # mnist 학습용 data(28x28)
y_ = label.to(device) # 각각의 data들이 0~9중 어떤숫자인지
optimizer.zero_grad() #optimizer 초기화
output = model.forward(x) # CNN 학습 시작.
loss = loss_func(output,y_) #학습해서 추정해낸 값과, 실제 라벨된 값 비교
loss.backward() #오차만큼 다시 Back Propagation 시행
optimizer.step() #Back Propagation시 ADAM optimizer 매 Step마다 시행
if j % 1000 == 0 : # 1000 미니 배치마다 출력.
print(loss)
loss_arr.append(loss.cpu().detach().numpy())
loss = loss_func(output,y_) 모델의 출력과 정답 레이블을 비교하여 손실을 계산한다. loss_func은 nn.CrossEntropyLoss()로, 크로스 엔트로피 손실 함수로 설정돼 있으므로, 모델이 예측한 확률 분포와 실제 레이블 간의 차이를 측정. loss.backward() 역전파를 수행하여 각 파라미터에 대한 손실의 기울기(gradient)를 계산. 이는 모델의 파라미터를 업데이트하는데 사용되는 것. optimizer.step() 역전파로 계산된 기울기를 사용하여 모델 파라미터를 조정. -> 만약 이 부분을 생략한다면 모델의 파라미터가 업데이트되지 않으며, 따라서 학습이 진행되지 않을 것correct = 0
total = 0
with torch.no_grad(): # 학습을 진행하지 않을 것이므로 torch.no_grad()
for image,label in test_loader :
x = image.to(device)
y_ = label.to(device)
output = model.forward(x)
_,output_index = torch.max(output,1)
total += label.size(0)
correct += (output_index == y_).sum().float()
print("Accuracy of Test Data : {}".format(100*correct/total))
참고한 자료 목록