diff --git "a/Week_4_\354\230\210\354\212\265\352\263\274\354\240\234 \354\235\264\354\204\234\354\227\260.ipynb" "b/Week_4_\354\230\210\354\212\265\352\263\274\354\240\234 \354\235\264\354\204\234\354\227\260.ipynb" new file mode 100644 index 0000000..6c406b8 --- /dev/null +++ "b/Week_4_\354\230\210\354\212\265\352\263\274\354\240\234 \354\235\264\354\204\234\354\227\260.ipynb" @@ -0,0 +1,676 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 딥러닝 파이토치 교과서 5.3, 5.4, 5.5장 정리\n", + "### 5.3 전이 학습\n", + "\n", + "#### 5.3.1 특성 추출 기법\n", + "#- 전이 학습(Transfer Learning)은 기존에 학습된 모델(주로 대규모 데이터셋에서 학습된 모델)을 새로운 작업에 재사용하는 방법임. 사전 학습된 모델의 초기 레이어들은 저수준의 일반적인 특성(엣지, 텍스처 등)을 학습하고, 후반 레이어는 데이터셋에 특화된 고수준의 특성을 학습하는 구조를 가짐.\n", + "#- 특성 추출(Feature Extraction)은 사전 학습된 모델의 가중치를 고정시킨 상태에서 새로운 데이터셋의 특성을 추출하는 방법임. 고정된 모델에서 마지막 분류 레이어만 새롭게 학습시켜 새로운 작업에 맞춤화함. 이를 통해 기존 학습된 가중치를 활용하면서도 새로운 데이터셋에 맞는 예측이 가능해짐.\n", + "#- 이 기법은 특히 데이터셋이 작거나 학습 시간이 제한적일 때 유용함. 예를 들어, 사전 학습된 모델의 여러 계층에서 얻어진 특성 맵을 새로운 분류기에 적용함으로써 모델을 재학습하는 대신 새로운 데이터셋에 맞는 분류 작업을 수행함.\n", + " \n", + "#### 5.3.2 이미지 전처리 기법\n", + "#- 이미지 전처리(Image Preprocessing)는 전이 학습에 중요한 단계로, 학습에 사용할 데이터를 변환하여 모델이 효율적으로 학습할 수 있도록 도움. 전처리는 이미지 크기 조정(Resize), 자르기(CenterCrop), 정규화(Normalization), 텐서 변환(ToTensor) 등의 단계를 포함함.\n", + "#- 크기 조정은 입력 이미지의 크기를 모델에서 기대하는 고정 크기로 맞추는 것이며, 정규화는 데이터의 분포를 일정하게 맞추기 위해 평균과 표준편차를 조정함. 이는 학습 속도를 높이고 모델의 성능을 향상시킬 수 있음.\n", + "#- 또한, 데이터 증강(Data Augmentation)을 통해 이미지 데이터를 다양하게 변형하여 학습 데이터의 다양성을 높이고 과적합을 방지할 수 있음. 예를 들어, 무작위 회전, 좌우 반전, 밝기 조정 등이 있음.\n", + "\n", + "### 5.4 설명 가능한 CNN\n", + "\n", + "#### 5.4.1 특성 맵 시각화\n", + "#- 특성 맵(Feature Map)은 CNN의 각 레이어에서 입력 데이터를 처리한 후 생성되는 중간 결과임. 특성 맵을 시각화하면 모델이 어떤 정보에 주목하고 있는지 확인할 수 있음.\n", + "#- 특성 맵 시각화는 CNN 모델이 데이터를 처리하는 방식을 이해하고, 모델이 실제로 무엇을 학습하고 있는지 직관적으로 확인할 수 있는 도구임. 예를 들어, 초기 계층은 엣지나 텍스처와 같은 저수준의 특성을 추출하고, 깊은 계층은 객체나 패턴과 같은 고수준의 특성을 학습함.\n", + "#- 이러한 시각화는 설명 가능한 인공지능(XAI, Explainable AI)의 중요한 요소 중 하나로, 특히 블랙박스 모델로 불리는 심층 신경망의 내부 동작을 이해하는 데 도움을 줌. 이를 통해 모델의 예측 과정에서 발생하는 오류를 파악하거나, 특정 패턴에 대한 과도한 의존성을 확인할 수 있음.\n", + "\n", + "#### 5.4.2 레이어 출력 확인\n", + "#- Hook 함수를 사용하여 CNN 모델의 중간 레이어 출력을 직접 확인할 수 있음. Hook은 특정 레이어에서 forward 과정 중에 발생하는 데이터를 출력하여 해당 레이어가 어떻게 학습되고 있는지 추적함.\n", + "#- 이 방식은 모델의 학습 중간 상태를 확인하거나, 특정 계층이 이미지에서 어떤 특징을 추출하고 있는지 분석할 때 유용함. 이를 통해 모델을 설명하고, 성능을 개선하기 위한 수정이 가능함.\n", + "#- 또한, Hook을 사용하면 특정 레이어의 출력을 저장하고 나중에 이를 분석할 수 있어, 학습이 끝난 후에도 레이어별 학습 내용을 자세히 파악할 수 있음.\n", + "\n", + "\n", + "### 5.5 그래프 합성곱 네트워크(GCN)\n", + "\n", + "#### 5.5.1 그래프 표현\n", + "#- 그래프 합성곱 네트워크(Graph Convolutional Network, GCN)는 CNN과 달리, 격자 형태가 아닌 비정형 데이터 구조인 그래프 데이터에 특화된 신경망 구조임. 그래프는 노드(Node)와 엣지(Edge)로 구성되어 있으며, 각 노드는 데이터를 나타내고 엣지는 노드 간의 관계를 나타냄.\n", + "#- 예를 들어, 소셜 네트워크에서는 각 사람을 노드로 보고, 사람 간의 관계를 엣지로 표현할 수 있음. 그래프 신경망은 이러한 노드와 엣지의 관계를 기반으로 데이터를 학습하고, 그래프에서 중요한 노드나 관계를 찾아낼 수 있음.\n", + "#- 인접 행렬(Adjacency Matrix)와 웨이트 행렬(Weight Matrix)를 사용하여 그래프 데이터를 수학적으로 표현하고, 이를 통해 그래프 구조의 데이터를 신경망에서 처리할 수 있음.\n", + "\n", + "#### 5.5.2 그래프 신경망 구조\n", + "#- GCN은 노드 간의 관계를 학습함으로써 그래프 데이터를 처리함. 각 노드는 이웃 노드의 정보로부터 자신을 업데이트함. 이를 통해 그래프 데이터에 내재된 구조적 관계를 학습할 수 있음.\n", + "#- 그래프 합성곱 연산은 CNN에서 사용하는 필터와 유사하지만, 그래프의 구조적 특성에 맞게 데이터 전파 방식이 다름. CNN이 이미지 픽셀에서 패턴을 학습하는 것처럼, GCN은 노드 간의 패턴을 학습함.\n", + "#- GCN은 소셜 네트워크 분석, 분자 구조 분석, 3D Mesh 처리 등 다양한 그래프 데이터를 처리하는 데 사용될 수 있음.\n", + "\n", + "#### 5.5.3 그래프 합성곱 연산\n", + "#- 그래프 합성곱 연산은 각 노드가 자신의 이웃 노드로부터 정보를 취합하여 자신의 특성을 업데이트하는 방식임. CNN에서는 이미지의 필터가 픽셀 간의 관계를 학습하듯이, GCN에서는 노드 간의 관계를 학습함.\n", + "#- 노드가 자신과 이웃 노드의 정보를 결합함으로써 그래프 내에서 데이터의 전파가 일어남. 이 과정을 반복함으로써 각 노드는 더 넓은 범위의 이웃 정보를 통합하게 되고, 최종적으로 그래프 구조 전체에서 패턴을 학습할 수 있음.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 라이브러리 호출\n", + "import os\n", + "import time\n", + "import copy\n", + "import glob\n", + "import cv2\n", + "import shutil\n", + "\n", + "import torch\n", + "import torchvision\n", + "import torchvision.transforms as transforms # 데이터 전처리를 위해 사용되는 패키지\n", + "import torchvision.models as models\n", + "import torch.nn as nn\n", + "import torch.optim as optim\n", + "from torch.utils.data import DataLoader\n", + "\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 이미지 데이터 전처리 방법 정의\n", + "data_path = '../chap05/data/catanddog/train'\n", + "\n", + "transform = transforms.Compose(\n", + " [\n", + " transforms.Resize([256, 256]),\n", + " transforms.RandomResizedCrop(224),\n", + " transforms.RandomHorizontalFlip(),\n", + " transforms.ToTensor() \n", + " ])\n", + "train_dataset = torchvision.datasets.ImageFolder(\n", + " data_path,\n", + " transform=transform\n", + ")\n", + "train_loader = torch.utils.data.DataLoader(\n", + " train_dataset,\n", + " batch_size=32,\n", + " num_workers=8,\n", + " shuffle=True\n", + ")\n", + "\n", + "print(len(train_dataset))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 학습에 사용될 이미지 출력\n", + "samples, labels = iter(train_loader).next()\n", + "classes = {0:'cat', 1:'dog'}\n", + "fig = plt.figure(figsize=(16,24))\n", + "for i in range(24):\n", + " a = fig.add_subplot(4, 6, i+1)\n", + " a.set_title(classes[labels[i].item()])\n", + " a.axis('off')\n", + " a.imshow(np.transpose(samples[i].numpy(), (1,2,0)))\n", + "plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 사전 훈련된 모델 내려받기기\n", + "resnet18 = models.resnet18(pretrained=True) # pretrained=True는 사전 학습된 가중치를 사용하겠다는 의미" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 사전 훈련된 모델의 파라미터 학습 유무 지정\n", + "def set_parameter_requires_grad(model, feature_extracting=True):\n", + " if feature_extracting:\n", + " for param in model.parameters():\n", + " param.requires_grad = False\n", + "set_parameter_requires_grad(renet18)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ResNet18에 완전연결층 추가\n", + "resnet18.fc = nn.Linear(512, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 모델의 파라미터 값 할인\n", + "for name, param in resnet18.named_parameters(): # model.named_parameters()는 모델에 접근하여 파라미터 값들을 가져올 때 사용\n", + " if param.requries_grad:\n", + " print(name, param.data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 모델 객체 생성 및 손실 함수 정의\n", + "model = models.resnet18(pretrained=True) # 모델의 객체 생성\n", + "\n", + "for param in model.parameters(): # 모델의 합성곱층 가중치 고정\n", + " param.requires_grad = False\n", + "\n", + "model.fc = torch.nn.Linear(512, 2)\n", + "for param in model.fc.parameters():\n", + " param.requires_grad = True\n", + "\n", + "optimizer = torch.optim.Adam(model.fc.parameters())\n", + "cost = torch.nn.CrossEntropyLoss() # 손실 함수 정의\n", + "print(model)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 모델 학습을 위한 함수 생성\n", + "def train_model(model, dataloaders, criterion, optimizer, device, num_epochs=13, is_train=True):\n", + " since = time.time() # 컴퓨터의 현재 시간을 구하는 함수\n", + " acc_history = []\n", + " loss_history = []\n", + " best_acc = 0.0\n", + "\n", + " for epoch in range(num_epochs): # 에포크(전체 학습 데이터셋을 모두 학습하는 과정) 반복\n", + " print('Epoch {}/{}'.format(epoch, num_epochs - 1))\n", + " print('-' * 10)\n", + "\n", + " running_loss = 0.0\n", + " running_corrects = 0\n", + "\n", + " for inputs, labels in dataloaders: # 데이터로더에 전달된 데이터만큼 반복\n", + " inputs = inputs.to(device)\n", + " labels = labels.to(device)\n", + "\n", + " model.to(device)\n", + " optimizer.zero_grad() # 기울기를 0으로 설정\n", + "\n", + " # 순전파 학습\n", + " outputs = model(inputs)\n", + " loss = criterion(outputs, labels)\n", + " _, preds = torch.max(outputs, 1)\n", + "\n", + " # 역전파 학습\n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " # 출력 과정의 손실값과 예측 결과를 저장\n", + " running_loss += loss.item() * inputs.size(0)\n", + " running_corrects += torch.sum(preds == labels.data)\n", + "\n", + " epoch_loss = running_loss / len(dataloaders.dataset) # 평균 손실 계산\n", + " epoch_acc = running_corrects.double() / len(dataloaders.dataset) # 평균 정확도 계산\n", + "\n", + " print('Loss: {:.4f} Acc: {:.4f}'.format(epoch_loss, epoch_acc))\n", + "\n", + " # 최고의 성능을 기록한 모델 저장\n", + " if epoch_acc > best_acc:\n", + " best_acc = epoch_acc\n", + "\n", + " acc_history.append(epoch_acc.item())\n", + " loss_history.append(epoch_loss)\n", + " torch.save(model.state_dict(), os.path.join('../chap05/data/catanddog/',\n", + " '{0:0=2d}.pth'.format(epoch))) # 모델 재사용을 위해 저장해 둠.\n", + " print()\n", + "\n", + " time_elapsed = time.time() - since\n", + " print('Training complete in {:0f}s'.format(time_elapsed // 60,\n", + " time_elapsed % 60))\n", + " print('Best Acc: {:4f}'.format(best_acc))\n", + " return acc_history, loss_history # 모델의 정확도와 오차를 반환\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 파라미터 학습 결과를 옵티마이저에 전달\n", + "params_to_update = []\n", + "for name, param in resnet18.named_parameters():\n", + " if param.requires_grad == True:\n", + " params_to_update.append(param) # 파라미터 학습 결과를 저장\n", + " print(\"/t\", name)\n", + "\n", + "optimizer = optim.Adam(params_to_update) # 학습 결과를 옵티마이저에 전달" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 모델 학습\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n", + "criterion = nn.CrossEntropyLoss() # 손실 함수 지점\n", + "train_acc_hist, train_loss_hist = train_model(resnet18, train_loader, criterion,\n", + " optimizer, device)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 테스트 데이터 로딩 및 전처리\n", + "test_path = '../chap05/data/catanddog/test'\n", + "\n", + "transform = transforms.Compose([\n", + " transforms.Resize(224),\n", + " transforms.CenterCrop(224),\n", + " transforms.ToTensor(),\n", + "])\n", + "\n", + "test_dataset = torchvision.datasets.ImageFolder(\n", + " root=test_path,\n", + " transform=transform\n", + ")\n", + "\n", + "test_loader = torch.utils.data.DataLoader(\n", + " test_dataset,\n", + " batch_size=32,\n", + " num_workers=1,\n", + " shuffle=True\n", + ")\n", + "\n", + "print(len(test_dataset))\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 테스트 데이터 평가 함수 생성\n", + "def eval_model(model, dataloaders, device):\n", + " since = time.time()\n", + " acc_history = []\n", + " best_acc = 0.0\n", + "\n", + " saved_models = glob.glob('../chap05/data/catanddog/' + '*.pth') # 저장된 .pth 파일 목록을 정렬\n", + " saved_models.sort()\n", + " print('saved_models:', saved_models)\n", + "\n", + " for model_path in saved_models:\n", + " print('Loading model', model_path)\n", + " model.load_state_dict(torch.load(model_path))\n", + " model.eval()\n", + " model.to(device)\n", + " running_corrects = 0\n", + "\n", + " for inputs, labels in dataloaders: # 테스트 반복\n", + " inputs = inputs.to(device)\n", + " labels = labels.to(device)\n", + "\n", + " with torch.no_grad(): # autograd를 사용하지 않겠다는 의미\n", + " outputs = model(inputs) # 데이터로 모델에 전달한 결과를 outputs에 저장\n", + "\n", + " _, preds = torch.max(outputs.data, 1) # torch.max를 통해 출력 값이 가장 큰 인덱스를 예측\n", + " preds[preds >= 0.5] = 1 # torch.max로 출력 값이 0.5보다 크면 클라스로 예측\n", + " preds[preds < 0.5] = 0 # torch.max로 출력 값이 0.5보다 작으면 클라스로 예측\n", + " running_corrects += preds.eq(labels.cpu().int()).sum() # 예측 결과와 실제 값을 비교하여 맞춘 개수를 계산\n", + "\n", + " epoch_acc = running_corrects.double() / len(dataloaders.dataset) # 테스트 데이터의 정확도 계산\n", + " print('Acc: {:.4f}'.format(epoch_acc))\n", + "\n", + " if epoch_acc > best_acc:\n", + " best_acc = epoch_acc\n", + "\n", + " acc_history.append(epoch_acc.item())\n", + " \n", + " print()\n", + " time_elapsed = time.time() - since\n", + " print('Validation complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))\n", + " print('Best Acc: {:.4f}'.format(best_acc))\n", + "\n", + " return acc_history # 계산된 정확도 반환\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 테스트 데이터를 평가 함수에 적용\n", + "val_acc_hist = eval_model(resnet18, test_loader, device)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 훈련과 테스트 데이터의 정확도를 그래프로 확인\n", + "plt.plot(train_acc_hist)\n", + "plt.plot(val_acc_hist)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 훈련 데이터의 오차에 대한 그래프 확인\n", + "plt.plot(train_loss_hist)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 예측 이미지 출력을 위한 전처리 함수\n", + "def im_convert(tensor):\n", + " image = tensor.clone().detach().numpy()\n", + " image = image.transpose(1, 2, 0)\n", + " image = image * (np.array((0.5, 0.5, 0.5)) + np.array((0.5, 0.5, 0.5)))\n", + " image = image.clip(0, 1)\n", + " return image" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 개와 고양이 예측 결과 출력\n", + "classes = {0: 'cat', 1: 'dog'} # 개와 고양이 두 개에 대한 레이블\n", + "\n", + "dataiter = iter(test_loader) # 테스트 데이터셋을 가져옴\n", + "images, labels = dataiter.next() # 테스트 데이터에서 이미지와 레이블을 한 번에 가져옴\n", + "output = model(images)\n", + "_, preds = torch.max(output, 1)\n", + "\n", + "fig = plt.figure(figsize=(25, 4))\n", + "for idx in np.arange(20):\n", + " ax = fig.add_subplot(2, 10, idx+1, xticks=[], yticks=[]) # 2행 10열 형태로 서브플롯 생성\n", + " plt.imshow(im_convert(images[idx])) # 이미지를 출력 (im_convert 함수로 전처리)\n", + " ax.set_title(f\"{classes[preds[idx].item()]} ({classes[labels[idx].item()]})\", \n", + " color=\"green\" if preds[idx] == labels[idx] else \"red\")\n", + "\n", + "plt.show()\n", + "plt.subplots_adjust(bottom=0.2, top=0.6, hspace=0)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 필요한 라이브러리 호출\n", + "import matplotlib.pyplot as plt\n", + "from PIL import Image\n", + "import cv2\n", + "import torch\n", + "import torch.nn.functional as F\n", + "import torch.nn as nn\n", + "from torchvision.transforms import ToTensor\n", + "import torchvision\n", + "import torchvision.transforms as transforms\n", + "import torchvision.models as models\n", + "\n", + "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 설명 가능한 네트워크 생성\n", + "class XAI(nn.Module):\n", + " def __init__(self, num_classes=2):\n", + " super(AttrNet, self).__init__()\n", + " self.features = nn.Sequential(\n", + " nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False),\n", + " nn.BatchNorm2d(64),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(kernel_size=3, stride=2, padding=1),\n", + "\n", + " nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1, bias=False),\n", + " nn.BatchNorm2d(128),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(kernel_size=3, stride=2),\n", + "\n", + " nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1, bias=False),\n", + " nn.BatchNorm2d(256),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(kernel_size=3, stride=2),\n", + "\n", + " nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1, bias=False),\n", + " nn.BatchNorm2d(512),\n", + " nn.ReLU(inplace=True),\n", + " nn.MaxPool2d(kernel_size=3, stride=2),\n", + " )\n", + " self.classifier = nn.Sequential(\n", + " nn.Dropout(),\n", + " nn.Linear(512 * 7 * 7, 4096),\n", + " nn.ReLU(inplace=True),\n", + " nn.Dropout(),\n", + " nn.Linear(4096, 4096),\n", + " nn.ReLU(inplace=True),\n", + " nn.Linear(4096, num_classes)\n", + " )\n", + "\n", + " def forward(self, x):\n", + " x = self.features(x)\n", + " x = x.view(x.size(0), -1)\n", + " x = self.classifier(x)\n", + " return F.log_softmax(x, dim=1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 모델 객체화화\n", + "model = XAI() # model이라는 이름의 객체를 생성\n", + "model.to(device) # model을 장치(CPU 혹은 GPU)에 할당\n", + "model.eval() # 테스트 데이터에 대한 모델 평가 용도로 사용" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 특징 맵을 확인하기 위한 클래스 정의\n", + "class LayerActivations:\n", + " features = []\n", + "\n", + " def __init__(self, model, layer_num):\n", + " self.hook = model[layer_num].register_forward_hook(self.hook_fn) # Hook 함수 등록\n", + "\n", + " def hook_fn(self, module, input, output):\n", + " self.features = output.detach().numpy() # 출력값을 Numpy 배열로 변환하여 저장\n", + "\n", + " def remove(self): # Hook 제거\n", + " self.hook.remove()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 이미지 호출\n", + "img = cv2.imread('../chap05/data/cat.jpg') # 이미지 파일을 읽어옴\n", + "plt.imshow(img) # 이미지를 출력\n", + "\n", + "# 이미지 크기 조정 및 Tensor 변환\n", + "img = cv2.resize(img, (100, 100), interpolation=cv2.INTER_LINEAR) # 이미지를 100x100 크기로 리사이즈\n", + "img = ToTensor()(img).unsqueeze(0) # 이미지를 Tensor로 변환하고 차원을 추가\n", + "print(img.shape) # 변환된 이미지의 shape 출력" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# (0): Conv2d 특성 맵 확인\n", + "result = LayerActivations(model.features, 0) # 0번째 Conv2d 특성 맵 확인\n", + "\n", + "model(img)\n", + "activations = result.features" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 특성 맵 확인\n", + "fig, axes = plt.subplots(4, 4)\n", + "fig = plt.figure(figsize=(12, 8))\n", + "fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)\n", + "for row in range(4):\n", + " for column in range(4):\n", + " axis = axes[row][column]\n", + " axis.get_xaxis().set_ticks([])\n", + " axis.get_xaxis().set_ticks([])\n", + " axis.imshow(activations[0][row*10+column])\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 20번째 계층에 대한 특성 맵\n", + "result = LayerActivations(model.features, 20) # 20번째 Conv2d 특성 맵 확인\n", + "\n", + "model(img)\n", + "activations = result.features" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 특성 맵 확인\n", + "fig, axes = plt.subplots(4, 4)\n", + "fig = plt.figure(figsize=(12, 8))\n", + "fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)\n", + "\n", + "for row in range(4):\n", + " for column in range(4):\n", + " axis = axes[row][column]\n", + " axis.get_xaxis().set_ticks([]) # x축 눈금을 없앰\n", + " axis.get_yaxis().set_ticks([]) # y축 눈금을 없앰\n", + " axis.imshow(activations[0][row * 10 + column]) # 활성화 맵을 시각화\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 40번째 계층에 대한 특성 맵\n", + "result = LayerActivations(model.features, 40) # 40번째 Conv2d 특성 맵 확인\n", + "\n", + "model(img)\n", + "activations = result.features" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 특성 맵 확인\n", + "fig, axes = plt.subplots(4, 4)\n", + "fig = plt.figure(figsize=(12,8))\n", + "fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)\n", + "for row in range(4):\n", + " for column in range(4):\n", + " axis = axes[row][column]\n", + " axis.get_xaxis().set_ticks([])\n", + " axis.get_yaxis().set_ticks([]) # y축 눈금을 없앰\n", + " axis.imshow(activations[0][row * 10 + column]) # 활성화 맵을 시각화\n", + "\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}