BIG_PY
home
Introduce
home

5장 합성곱 신경망1

질문

이름과 질문을 적어주세요
유성현 5.4 - 설명가능한 CNN explainable CNN 이 왜 필요한지 주로 어디에 쓰이는지 궁금합니다. https://realblack0.github.io/2020/04/27/explainable-ai.html
황현수
5.5.2 - 그래프 신경망 인접 행렬에 단위 행렬을 적용한 것이 특성 행렬이라고 하는데 왜 단위 행렬을 적용하면 특성을 더 파악하기 쉬운지 궁금합니다.
박내은 5.5.1 (p.241) 그래프는 ”방향성이 있거나 없는” 에지로 연결된 노드의 집합이라는 게 이해가 안 됩니당.
박소정 5.3.2 미세조정기법 미세조정기법에서 파라미터 업데이트 과정에서 파라미터에 큰 변화를 주게 되면 과적합 문제가 발생한다는데 그 이유가 무엇인지 궁급합니다. 특성추출기법에서는 이런 일이 발생하지 않나요?

합성곱 신경망

전이 학습

-학습 데이터의 부족
CNN 모델의 품질을 높이기 위해서는 기본적으로 많은 양의 데이터를 이용하여 학습해야 함
많은 학습 데이터를 확보하려면 많은 비용과 시간이 소요되기 때문에 쉽지 않은데, 데이터 부족의 어려움을 해결하기 위해 등장한 것이 전이학습임
전이 학습이란?
이미지넷처럼 아주 큰 데이터셋을 써서 훈련된 모델의 가중치를 가져와 우리가 해결하려는 과제에 맞게 보정해서 사용하는 것
특성 추출 기법, 미세 조정 기법
특성 추출 기법
ImageNet 데이터셋으로 사전 훈련된 모델을 가져온 후 마지막에 완전연결층 부분만 새로 만듦
학습할 때는 마지막 완전연결층만 학습하고 나머지 계층들은 학습되지 않도록 함
합성곱층: 합성곱층과 풀링층으로 구성
데이터 분류기(완전연결층): 추출된 특성을 입력 받아 최종적으로 이미지에 대한 클래스를 분류하는 부분
사전 훈련된 네트워크의 합성곱층에 새로운 데이터를 통과시키고 , 그 출력을 데이터 분류기에서 훈련시킴
특성 추출 예시
# 라이브러리 호출 import os import time import copy import glob import cv2 import shutil import torch import torchvision import torchvision.transforms as transforms import torchvision.models as models import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt
Python
복사
# 이미지 데이터 전처리 방법 정의 from google.colab import files # 데이터 불러오기 file_uploaded=files.upload() # 데이터 불러오기: chap05/data/catndog.zip 파일 선택 !unzip catanddog.zip -d catanddog/ #catanddog 폴더 만들어 압축 풀기 data_path = 'catanddog/train/' transform = transforms.Compose( [ transforms.Resize([256, 256]), transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), ]) train_dataset = torchvision.datasets.ImageFolder( data_path, transform=transform ) train_loader = torch.utils.data.DataLoader( train_dataset, batch_size=32, num_workers=8, shuffle=True ) print(len(train_dataset))
Python
복사
# 학습에 사용될 이미지 출력 import numpy as np samples, labels = iter(train_loader).next() classes = {0:'cat', 1:'dog'} fig = plt.figure(figsize=(16,24)) for i in range(24): a = fig.add_subplot(4,6,i+1) a.set_title(classes[labels[i].item()]) a.axis('off') a.imshow(np.transpose(samples[i].numpy(), (1,2,0))) plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)
Python
복사
# 사전 훈련된 모델 내려받기 resnet18 = models.resnet18(pretrained=True)
Python
복사
# 사전 훈련된 모델의 파라미터 학습 유무 지정 def set_parameter_requires_grad(model, feature_extracting=True): if feature_extracting: for param in model.parameters(): param.requires_grad = False set_parameter_requires_grad(resnet18)
Python
복사
# ResNet18에 완전연결층 추가 resnet18.fc = nn.Linear(512, 2)
Python
복사
# 모델의 파라미터 값 확인 for name, param in resnet18.named_parameters(): if param.requires_grad: print(name, param.data)
Python
복사
# 모델 객체 생성 및 손실 함수의 정의 model = models.resnet18(pretrained = True) for param in model.parameters(): param.requires_grad = False model.fc = torch.nn.Linear(512, 2) for param in model.fc.parameters(): param.requires_grad = True optimizer = torch.optim.Adam(model.fc.parameters()) cost = torch.nn.CrossEntropyLoss() print(model)
Python
복사
# 모델 학습을 위한 함수 생성 def train_model(model, dataloaders, criterion, optimizer, device, num_epochs=13, is_train=True): since = time.time() acc_history = [] loss_history = [] best_acc = 0.0 for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch, num_epochs - 1)) print('-' * 10) running_loss = 0.0 running_corrects = 0 for inputs, labels in dataloaders: inputs = inputs.to(device) labels = labels.to(device) model.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) _, preds = torch.max(outputs, 1) loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) running_corrects += torch.sum(preds == labels.data) epoch_loss = running_loss / len(dataloaders.dataset) epoch_acc = running_corrects.double() / len(dataloaders.dataset) print('Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc)) if epoch_acc > best_acc: best_acc = epoch_acc acc_history.append(epoch_acc.item()) loss_history.append(epoch_loss) torch.save(model.state_dict(), os.path.join('catanddog/', '{0:0=2d}.pth'.format(epoch))) print() time_elapsed = time.time() - since print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60)) print('Best Acc: {:4f}'.format(best_acc)) return acc_history, loss_history
Python
복사
# 파라미터 학습 결과를 옵티마이저에 전달 params_to_update = [] for name,param in resnet18.named_parameters(): if param.requires_grad == True: params_to_update.append(param) print("\t",name) optimizer = optim.Adam(params_to_update)
Python
복사
# 모델 학습 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") criterion = nn.CrossEntropyLoss() train_acc_hist, train_loss_hist = train_model(resnet18, train_loader, criterion, optimizer, device)
Python
복사
→ 약 94%로 상당히 높은 정확도를 보여줌
훈련 데이터로는 학습이 잘 되었다고 할 수 있음
# 테스트 데이터 호출 및 전처리 test_path = 'catanddog/validation/' transform = transforms.Compose( [ transforms.Resize(224), transforms.CenterCrop(224), transforms.ToTensor(), ]) test_dataset = torchvision.datasets.ImageFolder( root=test_path, transform=transform ) test_loader = torch.utils.data.DataLoader( test_dataset, batch_size=32, num_workers=1, shuffle=True ) print(len(test_dataset))
Python
복사
# 테스트 데이터 평가 함수 생성 def eval_model(model, dataloaders, device): since = time.time() acc_history = [] best_acc = 0.0 saved_models = glob.glob('catanddog/' + '*.pth') saved_models.sort() print('saved_model', saved_models) for model_path in saved_models: print('Loading model', model_path) model.load_state_dict(torch.load(model_path)) model.eval() model.to(device) running_corrects = 0 for inputs, labels in dataloaders: inputs = inputs.to(device) labels = labels.to(device) with torch.no_grad(): outputs = model(inputs) _, preds = torch.max(outputs.data, 1) preds[preds >= 0.5] = 1 preds[preds < 0.5] = 0 running_corrects += preds.eq(labels).int().sum() epoch_acc = running_corrects.double() / len(dataloaders.dataset) print('Acc: {:.4f}'.format(epoch_acc)) if epoch_acc > best_acc: best_acc = epoch_acc acc_history.append(epoch_acc.item()) print() time_elapsed = time.time() - since print('Validation complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60)) print('Best Acc: {:4f}'.format(best_acc)) return acc_history
Python
복사
# 테스트 데이터를 평가 함수에 적용 val_acc_hist = eval_model(resnet18, test_loader, device)
Python
복사
→ 테스트 데이터 역시 94% 정도로 높은 정확도를 보임
# 훈련과 테스트 데이터의 정확도를 그래프로 확인 plt.plot(train_acc_hist) plt.plot(val_acc_hist) plt.show()
Python
복사
→ 진행될수록 정확도가 높아지면서 100%에 가까워지고 있음
# 훈련 데이터의 오차에 대한 그래프 확인 plt.plot(train_loss_hist) plt.show()
Python
복사
→ 진행될수록 오차가 낮아지고 있기 때문에 학습이 잘 되었다고 할 수 있음
# 예측 이미지 출력을 위한 전처리 함수 def im_convert(tensor): image=tensor.clone().detach().numpy() image=image.transpose(1,2,0) image=image*(np.array((0.5,0.5,0.5))+np.array((0.5,0.5,0.5))) image=image.clip(0,1) return image
Python
복사
# 개와 고양이 예측 결과 출력 plt.plot(train_loss_hist) plt.show()
Python
복사
# 훈련 데이터의 오차에 대한 그래프 확인 classes = {0:'cat', 1:'dog'} dataiter=iter(test_loader) images,labels=dataiter.next() output=model(images) _,preds=torch.max(output,1) fig=plt.figure(figsize=(25,4)) for idx in np.arange(20): ax=fig.add_subplot(2,10,idx+1,xticks=[],yticks=[]) plt.imshow(im_convert(images[idx])) a.set_title(classes[labels[i].item()]) ax.set_title("{}({})".format(str(classes[preds[idx].item()]),str(classes[labels[idx].item()])),color=("green" if preds[idx]==labels[idx] else "red")) plt.show() plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)
Python
복사
→ 초록색은 정확하게 예측한 것이고 빨간색은 예측이 잘못되었음을 의미함
결과를 보니 예측이 정확하지 않아 훈련 데이터를 늘리고 에포크 횟수도 늘리면 더 좋을 결과를 얻을 수 있음
미세 조정 기법
사전 훈련된 모델과 합성곱층, 데이터 분류기의 가중치를 업데이트하여 훈련시키는 방식
사전 학습된 모델을 목적에 맞게 재학습시키거나 학습된 가중치의 일부를 재학습시킴
사전 훈련된 네트워크를 미세 조정하여 분석하려는 데이터셋에 잘 맞도록 모델의 파라미터를 조정하는 기법
-데이터셋이 크고 사전 훈련된 모델과 유사성이 작을 경우: 모델 전체 재학습
-데이터셋이 크고 사전 훈련된 모델과 유사성이 클 경우: 합성곱층의 뒷부분과 데이터 분류기 학습
-데이터셋이 작고 사전 훈련된 모델과 유사성이 작을 경우: 합성곱층의 일부분과 데이터 분류기 학습
-데이터셋이 작고 사전 훈련된 모델과 유사성이 클 경우: 데이터 분류기만 학습

설명 가능한 CNN

설명 가능한 CNN이란?
딥러닝 처리 결과를 사람이 이해할 수 있는 방식으로 제시하는 기술
신뢰하기 위해 CNN 처리 과정 시각화
특성 맵 시각화
특성 맵에서 입력 특성을 감지하는 방법을 이해할 수 있도록 돕는 것
# 필요한 라이브러리 호출 import matplotlib.pyplot as plt from PIL import Image import cv2 import torch import torch.nn.functional as F import torch.nn as nn from torchvision.transforms import ToTensor import torchvision import torchvision.transforms as transforms import torchvision.models as models device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
Python
복사
# 설명 가능한 네트워크 생성 class XAI(torch.nn.Module): def __init__(self, num_classes=2): super(XAI, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, 64, kernel_size=3, bias=False), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.Dropout(0.3), nn.Conv2d(64, 64, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), nn.Conv2d(64, 128, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(128), nn.ReLU(inplace=True), nn.Dropout(0.4), nn.Conv2d(128, 128, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(128), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), nn.Conv2d(128, 256, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(256), nn.ReLU(inplace=True), nn.Dropout(0.4), nn.Conv2d(256, 256, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(256), nn.ReLU(inplace=True), nn.Dropout(0.4), nn.Conv2d(256, 256, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(256), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), nn.Conv2d(256, 512, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(inplace=True), nn.Dropout(0.4), nn.Conv2d(512, 512, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(inplace=True), nn.Dropout(0.4), nn.Conv2d(512, 512, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), nn.Conv2d(512, 512, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(inplace=True), nn.Dropout(0.4), nn.Conv2d(512, 512, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(inplace=True), nn.Dropout(0.4), nn.Conv2d(512, 512, kernel_size=3, padding = 1, bias=False), nn.BatchNorm2d(512), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=2, stride=2), ) self.classifier = nn.Sequential( nn.Linear(512, 512, bias=False), nn.Dropout(0.5), nn.BatchNorm1d(512), nn.ReLU(inplace=True), nn.Dropout(0.5), nn.Linear(512, num_classes) ) def forward(self, x): x = self.features(x) x = x.view(-1, 512) x = self.classifier(x) return F.log_softmax(x)
Python
복사
# 모델 객체화 model=XAI() model.cpu() #모델에 입력되는 이미지를 넘파이로 받아오는 부분때문에 CPU를 사용하도록 지정하였습니다 model.eval()
Python
복사
# 특성 맵을 확인하기 위한 클래스 정의 class LayerActivations: features=[] def __init__(self, model, layer_num): self.hook = model[layer_num].register_forward_hook(self.hook_fn) def hook_fn(self, module, input, output): output = output #self.features = output.to(device).detach().numpy() self.features = output.detach().numpy() def remove(self): self.hook.remove()
Python
복사
# 이미지 호출 from google.colab import files # 데이터 불러오기 file_uploaded=files.upload() # chap05/data/cat.jpg 데이터 불러오기 img=cv2.imread("cat.jpg") plt.imshow(img) img = cv2.resize(img, (100, 100), interpolation=cv2.INTER_LINEAR) img = ToTensor()(img).unsqueeze(0) print(img.shape)
Python
복사
# Conv2d 특성 맵 확인 result = LayerActivations(model.features, 0) model(img) activations = result.features
Python
복사
# 특성 맵 확인 fig, axes = plt.subplots(4,4) fig = plt.figure(figsize=(12, 8)) fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05) for row in range(4): for column in range(4): axis = axes[row][column] axis.get_xaxis().set_ticks([]) axis.get_yaxis().set_ticks([]) axis.imshow(activations[0][row*10+column]) plt.show()
Python
복사
→ 첫 번째 계층에서 특성 맵에 대한 출력 결과
# 20번째 계층에 대한 특성 맵 result = LayerActivations(model.features, 20) model(img) activations = result.features
Python
복사
# 특성 맵 확인 fig, axes = plt.subplots(4,4) fig = plt.figure(figsize=(12, 8)) fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05) for row in range(4): for column in range(4): axis = axes[row][column] axis.get_xaxis().set_ticks([]) axis.get_yaxis().set_ticks([]) axis.imshow(activations[0][row*10+column]) plt.show()
Python
복사
→ 20번째 계층에서 특성 맵에 대한 출력 결과
→ 40번째 계층에서 특성 맵에 대한 출력 결과
출력층에 가까울수록 원래 형태를 찾아볼 수 없고 이미지의 특징만이 전달됨

그래프 합성곱 네트워크

그래프 합성곱 네트워크란?
그래프 데이터를 위한 신경망
그래프란
방향성이 있거나 없는 에지로 연결된 노드의 집합
-노드: 파란색 원
-에지(간선): 두 노드를 연결한 선
노드는 원소를 의미하고 에지는 결합 방법(single, double, triple 등)을 의미함
그래프 신경망
그래프 구조에서 사용하는 신경망
1.
인접 행렬
네트워크가 있을 때 노드 n개를 nxn 행렬로 표현하고 i와 j의 관련성 여부를 만족하는 값으로 채워줌
→ 컴퓨터가 이해하기 쉽게 그래프로 표현하는 과정
2.
특성 행렬
인접 행렬에 단위 행렬을 적용함
→ 노드에 대한 특성을 한눈에 파악하기 쉽게 표현됨
그래프 합성곱 네트워크(GCN)
이미지에 대한 합성곱을 그래프 데이터로 확장한 알고리즘
리드아웃: 특성 행렬을 하나의 벡터로 변환하는 함수
→ 전체 노드의 특성 벡터에 대해 평균을 구하고 그래프 전체를 표현하는 하나의 벡터를 생성함
GCN에서 가장 중요한 부분은 그래프 합성곱층임
→ 그래프 합성곱층을 이용한 그래프 형태의 데이터는 행렬 형태의 데이터로 변환되어 딥러닝 알고리즘을 적용할 수 있기 때문
GCN은 SNS 관계 네트워크, 학술 연구에서 인용 네트워크, 3D Mesh 에서 활용됨

질문

5.4 - 설명 가능한 CNN explainable CNN 이 왜 필요한지 주로 어디에 쓰이는지 궁금합니다.
→ CNN은 이미지를 인식하기 위해 패턴을 찾는데 특히 유용합니다. 데이터에서 직접 학습하고 패턴을 사용해 이미지를 분류합니다. 즉, 특징을 수동으로 추출할 필요가 없습니다. 이러한 장점 때문에 자율 주행 자동차, 얼굴 인식과 같은 객체 인식이나 computer vision이 필요한 분야에 많이 사용되고 있습니다.
5.5.2 - 그래프 신경망 인접 행렬에 단위 행렬을 적용한 것이 특성 행렬이라고 하는데 왜 단위 행렬을 적용하면 특성을 더 파악하기 쉬운지 궁금합니다.
→ 인접 행렬만으로는 특성을 파악하기 어렵기 때문에 단위 행렬을 적용한다고 합니다.
5.5.1 그래프는 ”방향성이 있거나 없는” 에지로 연결된 노드의 집합이라는 게 이해가 안 됩니다.
→ 위의 그림처럼 왼쪽 노드에서 오른쪽 노드로 밖에 접근을 못 하면 방향성이 있다고 하고 이런 식으로 그려진 그래프를 방향 그래프라고 합니다.
아래 그림처럼 방향이 없는 간선(에지)이라면 왼쪽에서 오른쪽, 오른쪽에서 왼쪽 어떤 방향으로든 옮겨 다닐 수 있습니다.
방향 그래프
무방향 그래프
5.3.2 미세 조정 기법 미세 조정 기법에서 파라미터 업데이트 과정에서 파라미터에 큰 변화를 주게 되면 과적합 문제가 발생한다는데 그 이유가 무엇인지 궁금합니다. 특성추출기법에서는 이런 일이 발생하지 않나요?
→ 데이터셋이 작고 사전 훈련된 모델과 유사성이 큰 경우 과적합이 발생할 수 있습니다.
데이터가 적어서 많은 계층에 기법을 적용하면 과적합이 발생하는데 그래서 이 경우에는 재학습을 최종 데이터 분류기인 완전 연결층에 대해서만 적용합니다. 특성 추출 기법은 목표 특성을 잘 추출했다는 전제하에 좋은 성능이 나오는데 특성추출 기법에서 특성이 잘못 추출되었다면 미세 조정 기법으로 이미지 데이터를 사용해 네트워크의 가중치를 업데이트 해서 특성을 다시 추출할 수 있습니다.