728x90
반응형
SMALL
1. CNN(Convolutional Neural Network)
- 합성곱 인공 신경망
- 전통적인 뉴럴 네트워크에 컨볼루셔널 레이어를 붙인 형태
- 컨볼루셔널 레이어를 통해 입력 받은 이미지에 대한 특징(Featuer)을 추출하게 되고 추출한 특징을 기반으로 기존의 뉴럴 네트워크에 이용하여 분류
[CNN체험] https://adamharley.com/nn_vis/
1-1. CNN을 사용하는 이유
- 이미지를 분류할 때 DNN(Deep Neural Network)의 문제점
- 일반적인 DNN은 1차원 형태의 데이터를 사용 ➡️ 2차원 이상의 데이터가 입력되는 경우는 flatten 시켜서 한줄로 데이터를 변환 후 넣음
- 이미의 공간적/지역적 정보가 손실됨
DNN의 문제점을 해결하기 위해 이미지를 그대로(Raw Input) 받으므로 공간적/지역적 정보를 유지
1-2. 이미지 데이터(Image Data)
- 컬러 이미지는 3개의 채널(width, height, channel)로 이루어진 텐서(Tensor)
- 컴퓨터는 이미지를 숫자로 인식하여 연산을 함
- 이미지의 정보는 0 ~ 255 까지의 255개의 숫자로 표현
- 빨강(R) 255, 파랑(B) 255, 초록(G) 255은 흰색
- 빨강 255, 파랑 0, 초록 0은 빨강
- 빨강 0, 파랑 255, 초록 0은 파랑
- 빨강 0, 파랑 0, 초록 255은 초록
2. Convolutional 연산
2-1. 연산의 원리
- 컨볼루션 연산을 진행하면 출력 텐서의 크기가 작아짐
- 컬러 이미지에는 2D 컨볼루션 연산을 사용
2-2. 패딩(Padding)
- 입력값 주위로 0(통상적으로)을 넣어서 입력값의 크기를 인위적으로 키워 결과값이 작아지는 것을 방지
- 출력 크기를 동일하게하기 위해 패딩(padding)을 사용 (시작할 때 없는 테두리 값을 주면 동일한 크기의 output이 나옴)
2-3. 풀링(Pooling)
- 중요한 특징을 추출하고 차원을 축소하기 위해 Pooling 연산을 사용
- MaxPool(MaxPool2D)
- AvgPool(AvgPool2D)
2-4. 스트라이드(stride)
- 필터를 적용하는 간격의 설정(필터를 적용하는 단계 간의 이동 거리)
- 스트라이드의 값은 일반적으로 1보다 크거나 같은 자연수로 설정
- 필터를 적용해서 얻어낸 결과를 Feature map 또는 Activation map이라고 부름
2-5. 드롭아웃 레이어(Dropout Layer)
- 오버피팅(과적합)을 막기 위해 사용하는 레이어
- 학습중일 때 랜덤하게 값을 발생하여 학습을 방해함으로 학습용 데이터의결과가 치우치는 것을 방지함
- 학습 데이터에 지나치게 적합되는 것을 억제하고, 다양한 특징을 학습할 수 있도록 도와줌
2-6. FC Layer(Fully Connected) 레이어
- 이미지를 분류 또는 설명하기 위해 예측하는 레이어
- 이전 레이어의 모든 뉴런과 연결된 가중치를 갖고 있음
- 각 입력 뉴런은 출력 뉴런과 fully connected되어 있으며, 입력과 가중치의 곱을 통해 출력 값을 계산
- 계산된 출력 값은 활성화 함수를 통과한 뒤 다음 레이어로 전달
- 전체 신경망 구조에서 FC Layer는 주로 마지막 레이어로 사용
3. CNN을 구성하는 레이어
- Conv2D: 특징 추출
- ReLU: 활성화 함수
- MaxPool2D: 차원 축소
- Conv2D: 특징 추출
- ReLU: 활성화 함수
- MaxPool2D: 차원 축소
... - Flatten: 다차원에서 1차원으로 변경
- Linear: 선형 회귀
- ReLU: 활성화 함수
... - Sigmoid(or Softmax)
3-1. 간단한 CNN 모델 실습
import torch
import torch.nn as nn
import torch.optim as optim
# 배치 크기 * 채널(1: grayscale, 3: color) * 높이 * 너비
inputs = torch.Tensor(1, 1, 28, 28)
print(inputs.shape)
>>> torch.Size([1, 1, 28, 28])
✅ 첫번째 Conv2D
# Conv2d(입력 데이터가 1개, 출력되는 피쳐의 수 32개, 마스크가 3*3짜리(기울기), padding='same': 테두리를 채워서 크기를 유지)
conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, padding='same')
out= conv1(inputs)
print(out.shape) # 출력이 32개
>>> torch.Size([1, 32, 28, 28])
✅ 2번째 MaxPool2D
# MaxPool2d(kernel_size: 스트라이드가 2(2칸씩 오른쪽 아래로 이동))
pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape) # torch.Size([1, 32, 14, 14]): 크기가 줄음
>>> torch.Size([1, 32, 14, 14])
✅ 두번째 Conv2D
# Conv2d(입력 데이터가 32개, 출력되는 피쳐의 수 64개, 마스크가 3*3짜리(기울기), padding='same': 테두리를 채워서 크기를 유지)
conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding='same')
out= conv2(out)
print(out.shape) # 출력이 64개
>>> torch.Size([1, 64, 7, 7])
✅ 두번째 MaxPool2D
# MaxPool2d(kernel_size: 스트라이드가 2(2칸씩 오른쪽 아래로 이동))
pool = nn.MaxPool2d(kernel_size=2)
out = pool(out)
print(out.shape) # torch.Size([1, 64, 7, 7]): 크기가 줄음
>>> torch.Size([1, 64, 7, 7])
✅ Flatten(배치를 제외한 3차원 데이터를 1차원으로 펼침 ➡️ 1차원으로 만들어야 nn.Linear() 레이어에 넣을 수 있음)
flatten = nn.Flatten()
out = flatten(out)
print(out.shape) # 64 * 7 * 7 = 3136(한줄로 펼쳐짐)
>>> torch.Size([1, 3136])
✅ Dense(Fully Connected) 레이어
# 선형 회귀
# Linear(3136개의 입력값, 10개의 결과값(0~9))
fc = nn.Linear(3136, 10)
out = fc(out)
print(out.shape) # torch.Size([1, 10]): 10개의 확률로 나옴
>>> torch.Size([1, 10])
4. CNN으로 MNIST 분류하기
- MNIST는 손으로 쓴 숫자(0부터 9까지)로 이루어진 대표적인 데이터셋
import torch
import torchvision.datasets as datasets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader
✅ GPU 환경 설정
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print(device)
>>> cuda
✅ 학습 데이터셋과 테스트 데이터셋 불러오기
train_data = datasets.MNIST(
root = 'data', # 데이터셋이 저장될 디렉토리 경로
train=True, # 훈련 데이터를 가져옴
transform=transforms.ToTensor(), # 이미지 데이터를 텐서로 변환하는 변환
download=True # 데이터셋이 로컬에 없을 경우 인터넷에서 데이터를 자동으로 다운로드
)
test_data = datasets.MNIST(
root = 'data',
train=False, # test용으로 설정하여 로드
transform=transforms.ToTensor(), # 이미지 데이터를 텐서로 변환하는 변환
download=True
)
print(train_data)
print(test_data)
>>> Dataset MNIST
Number of datapoints: 60000
Root location: data
Split: Train
StandardTransform
Transform: ToTensor()
Dataset MNIST
Number of datapoints: 10000
Root location: data
Split: Test
StandardTransform
Transform: ToTensor()
✅ 데이터로더를 사용하여 학습 데이터셋을 배치 단위로 로드하기
loader = DataLoader(
dataset = train_data, # 훈련 데이터셋을 배치단위로 로드
batch_size=64, # 배치 크기 64
shuffle=True # 데이터를 섞어줌
)
✅ 학습 데이터셋 시각화
imgs, labels = next(iter(loader)) # 데이터로더에서 배치를 가져옴
fig, axes = plt.subplots(8, 8, figsize=(16, 16)) # 16*16픽셀의 이미지데이터가 8행 8열로 만드는 subplot
for ax, img, label in zip(axes.flatten(), imgs, labels):
ax.imshow(img.reshape((28,28)), cmap='gray') # 이미지를 28x28 크기로 변형, 흑백으로 표시
ax.set_title(label.item()) # lable값을 제목으로 설정
ax.axis('off') # 축 숨김
✅ CNN 모델 만들기
model = nn.Sequential(
nn.Conv2d(1, 32, kernel_size=3, padding='same'),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(32, 64, kernel_size=3, padding='same'),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2),
# Flatten
nn.Flatten(),
nn.Linear(7 * 7 * 64, 10)
).to(device) # gpu로 보냄
print(model)
>>>
Sequential(
(0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=same)
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=same)
(4): ReLU()
(5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Flatten(start_dim=1, end_dim=-1)
(7): Linear(in_features=3136, out_features=10, bias=True)
)
✅ 학습 데이터로 모델 학습시키기
optimizer = optim.Adam(model.parameters(), lr=0.001)
epochs = 10
for epoch in range(epochs+1):
sum_losses = 0
sum_accs = 0
for x_batch, y_batch in loader:
x_batch = x_batch.to(device) # gpu 연산을 위해 보냄
y_batch = y_batch.to(device)
y_pred = model(x_batch)
loss = nn.CrossEntropyLoss()(y_pred, y_batch) # 3개 이상 클래스로 분류 -> CrossEntropyLoss
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 배치 단위 loss 저장
sum_losse = sum_losses + loss.item()
# 배치 단위 정확도 저장
y_prob = nn.Softmax(1)(y_pred)
y_pred_index = torch.argmax(y_prob, axis=1)
acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
sum_accs = sum_accs + acc
avg_loss = sum_losses / len(loader)
avg_acc = sum_accs / len(loader)
print(f'Epoch: {epoch+1:4d}/{epochs} Loss: {avg_loss:6f} Accuracy: {avg_acc:2f}%')
✅ 데이터로더를 사용하여 test 데이터셋을 배치 단위로 로드하기
test_loader = DataLoader(
dataset = test_data,
batch_size=64,
shuffle=True
)
✅ test 데이터셋 시각화
imgs, labels = next(iter(test_loader))
fig, axes = plt.subplots(8, 8, figsize=(16, 16)) # 16*16픽셀의 이미지데이터가 8행 8열로 만드는 subplot
for ax, img, label in zip(axes.flatten(), imgs, labels):
ax.imshow(img.reshape((28,28)), cmap='gray')
ax.set_title(label.item())
ax.axis('off')
✅ 모델을 테스트 모드로 전환하여 모델 성능 평가하기
📍 평가모드로 전환하는 이유
- 테스트나 추론 단계에서 일관된 결과를 얻고, 드롭아웃이나 배치 정규화와 같은 학습 중에만 필요한 기법들을 비활성화하여 모델의 성능을 평가하기 위해
model.eval() # 모델을 테스트 모드로 전환(학습시키려고 모델을 사용하는게 아니라 평가모드로 전환)
sum_accs = 0
for x_batch, y_batch in test_loader:
x_batch = x_batch.to(device) # gpu 연산을 위해 보냄
y_batch = y_batch.to(device)
y_pred = model(x_batch)
y_prob = nn.Softmax(1)(y_pred)
y_pred_index = torch.argmax(y_prob, axis=1)
acc = (y_batch == y_pred_index).float().sum() / len(y_batch) * 100
sum_accs = sum_accs + acc
avg_loss = sum_losses / len(test_loader)
avg_acc = sum_accs / len(test_loader)
print(f'테스트 정확도는 {avg_acc:.2f}% 입니다')
>>> 테스트 정확도는 99.014732% 입니다
728x90
반응형
LIST
'Python > ML&DL' 카테고리의 다른 글
[파이썬, Python] 딥러닝 - 3️⃣ 다중 분류 신경망 모델 구현하기! (0) | 2023.07.02 |
---|---|
[파이썬, Python] 딥러닝 - 2️⃣ 전이학습(Transfer Learning), 이진분류 신경망 모델 구현하기! (0) | 2023.07.01 |
[파이썬, Python] 활성화 함수(Activation Function) 종류에 대해 알아보자! (0) | 2023.06.29 |
[파이썬, Python] 딥러닝(DeepLearning) - 퍼셉트론과 역전파 (0) | 2023.06.29 |
[파이썬, Python] 데이터로더(DataLoader) - 배치 단위로 학습시키기! (0) | 2023.06.29 |