[N431] TIL 및 회고

Sea Panda·2023년 1월 26일
0

부트캠프 TIL

목록 보기
41/46

0. 학습목표

Level 1.

  • CNN(Convolutional Neural Network)의 기본 구조에 대해 설명할 수 있다.
  • Convolution & Pooling Layer 의 동작 방식과 조정할 수 있는 값(Stride, Padding 등)에 대해 설명할 수 있다.
  • 전이 학습(Transfer Learning)을 설명할 수 있으며 이미지 처리를 위한 대표적인 사전 학습 모델을 2개 이상 설명할 수 있다.
  • 직접 CNN 모델을 구축하거나 사전 학습 모델을 사용하여 이미지 분류를 하는 코드를 작성할 수 있다.

Level 2.

  • CNN 층이 깊어졌을 때의 장점에 대해 설명할 수 있다.
  • 이미지 데이터 증강(Image Data Augmentation)의 개념에 대해 이해하고 실제 학습에 적용하는 코드를 작성할 수 있다.

Level 3.

  • 최근 발표되고 있는 이미지 분류를 위한 사전 학습 모델에는 어떤 것이 있는지 나열하고 각 모델의 특징에 대해 설명할 수 있다.

1. CNN(Convolution Neural Network)

합성곱 신경망Convolution Neural Network(CNN)은 대뇌의 시각피질 연구에서 시작되었고 1980년대부터 이미지 인식 분야에 사용되었다. 본격적으로 주목받은 시기는 2012년으로, 2012년 이미지넷(ImageNet) 데이터셋 분류 경진대회인 ILSVRC에서 AlexNet이라는 알고리즘이 우승하였기 때문이다.

이미지는 위치에 맞는 공간적인 특성이 존재한다. 하지만 앞 서 N41~에서 배운 다층 퍼셉트론 신경망(MLP)은 모든 입력 값을 Flatten으로 펴준 뒤에 연산하기 때문에 이런 공간적인 특성을 살려내지 못한다. MNIST 데이터 처럼 간단한 이미지 데이터는 MLP로도 분류가 가능하지만
패턴이 복잡한 컬러 이미지를 이런 방식으로 분류하는 것은 쉽지 않다.

반면 CNN은 학습 과정에서 이런 공간적 특성을 보존하면서 학습할 수 있기 때문에 층이 깊어지더라도 공간적인 특성을 보존할 수 있다는 장점 때문에 이미지 분류에서 주목받기 시작하였다.

본격적인 CNN의 구조를 알아보기 전에 CNN이 시작된 근본인 시각 피질 구조에 대해 먼저 알아보겠다.

시각 피질 구조

시각 피질 안의 많은 뉴런이 작은 국부 수용장local receptive field을 가진다는 것을 데이비드 허블과 토르스텐 비셀이 1958~1959년 사이에 진행한 고양이 실험과 원숭이 실험을 통해서 밝혀냈다. 다시 말하자면 뉴런들이 시야의 일부 범위 안에 있는 시각 자극에만 반응한다는 것이다.

뉴런의 수용장들은 서로 겹칠 수 있어서, 합치면 전체 시야를 감싸게 된다. 또한 두 뉴런이 동일한 수용장을 가진다 하더라도 어떤 뉴런은 수평선의 이미지에만 반응하고 반면 다른 뉴런은 다른 각도의 선분에 반응한다는 점을 보였다. 또한 어떤 뉴런은 큰 수용장을 가져서 저수준 패턴이 조합된 더 복잡한 패턴에 반응한다는 것을 밝혔다. 이를 통해서 고수준 뉴런이웃한 저수준 뉴런의 출력에 기반한다는 아이디어를 이끌어냈다. 즉, 각 뉴런은 이전 층에 있는 몇 개의(이웃한) 뉴런에만 연결된다는 것이다. 이러한 강력한 구조가 전체 시야 영역에 포함된 모든 종류의 복잡한 패턴을 감지할 수 있게 한다.

시각 피질에 대한 이런 연구를 통해 얻은 아이디어가 지금의 CNN으로 점진적으로 진화되었다. 그로다 1998년에 발표된 얀 르쿤 등의 논문 "Gradien-Based Learning Applied to Document Recognition"이 이미지 분류의 중요한 전환점이 되었다. 이 논문에서는 수표에 쓰인 손글씨 숫자를 인식하는데 널리 사용된 유명한 LeNet-5 구조를 소개했다. 이 구조에서 안 르쿤 교수는 위에서 말한 고수준 뉴런이웃한 저수준 뉴런의 출력에 기반한다는 아이디어를 통해서 획기적인 인공신경망을 고안해냈고, 이것이 바로 합성곱 신경망Convolution Neural Network(CNN)이다.

CNN 구조

기본적인 CNN의 구조는 위의 그림과 같이 특징 추출 부분(Conv-Pooling)과 분류를 위한 신경망, 2단계로 나눌 수 있다. 먼저 특징이 추출되는 합성곱 층(Covolution Layer)과 풀링 층(Pooling Layer)에 대해서 알아보자.

합성곱 층convolution Layer

합성곱 층에서는 합성곱 필터(Convolution Filter)가 **슬라이딩(Sliding)하며 이미지 부분부분의 특징을 읽어나간다. 한 번에 여러개가 보이니, 보기 어려울 수 있으니 정지돈 상황에서의 예시를 살펴보겠다.

Gif를 통해서 같은 방식으로 9번의 연산이 이뤄지며, 9칸이 모두 채워지는 것을 확인할 수 있다. 그리고 바로 위의 그림을 통해서 각 Patch단위로 어떠한 방식으로 합성곱이 연산되는지를 확인할 수 있다.

위 두 그림을 잘 살펴보면 입력 이미지의 모든 픽셀에 한 번에 연결하는 것이 아니라 합성곱 층 뉴런의 수용장(=Filter)안에 있는 픽셀에만 연결되고, 이를 합성곱을 통해서 다시 전체 이미지의 Convoluted Feature를 구성하는 것을 볼 수 있다. 이런 구조는 네트워크가 이미지의 작은 저수준 특성에 집중하고, 그 후 더 큰 고수준 특성으로 조합해나가도록 도와준다. 이런 계층적 구조는 실제 이미지에서 흔히 볼 수 있으며, 이는 CNN이 이미지 인식에서 잘 작동하는 이유 중 하나이다.

뉴런의 수용장. 즉, Filter는 하나의 특성 맵을 만들고, 이 맵은 필터를 가장 크게 활성화시키는 이미지의 영역을 강조한다. 물론 수동으로 필터를 정의할 필요는 없다. 필터의 크기와 필터의 수만 정의하면 훈련하는 동안 합성곱 층이 자동으로 해당 문제에 가장 유용한 필터를 찾고 상위층은 이들을 연결하여 더 복잡한 패턴을 학습하게 된다.

다음으로는 합성곱 층에 적용할 수 있는 패딩(Padding)과 스트라이드(Stride)에 대해서 알아보도록 하겠다.


패딩Padding

패딩은 이미지 외부를 특정한 값으로 둘러싸서 처리해주는 방식이다. 보통 0으로 둘러싸주는 Zero-Padding이 가장 많이 사용된다. Padding을 사용하는 이유는 연산되어 나오는 Output, 즉 Feature map의 크기를 조절하고 실제 이미지 값을 충분히 활용하기 위해서이다.

만약 Padding이 되어있지 않다면 가장 첫번째 칸에 들어있는 값은 1번만 사용된다. 마지막 칸도 마찬가지이다. 이는 담고있는 정보가 충분히 활용되지 못한 것이다. 하지만 Padding을 한 후에 합성곱을 진행하면 정보를 충분히 활용할 수 있게 된다.

Padding의 형식은 3가지가 있다. 하지만 사실상 Vaild의 경우에는 패딩을 진행하지 않는 것이다. Same의 경우에는 상하좌우에 행과 열을 1줄씩 추가하는 것이다. 이렇게 하면 입력값과 출력값의 크기가 동일하게 출력된다. Full의 경우에는 필터의 크기만큼 상하좌우에 행과 열을 추가하는 것이다. 이 경우 정보가 더 많이 활용되며, 입력보다 출력의 크기가 더 커지게 된다.

Keras에서는 Valid또는 same 두가지 방식만 지원한다.

스트라이드Stride

스트라이드(Stride)는 "보폭"이라는 뜻을 가진 단어이다. Stride를 조절하면 슬라이딩(Sliding)시에 몇 칸 씩 건너뛸지를 나타낸다. 위에서 본 gif파일은 스트라이드가 1인 경우이다.

Keras에서는 튜플 형태로 이를 전달하며 기본 Default값은 (1,1)이다. 즉 가로로 1칸씩 이동하면서, 끝에 도달하면 그 다음 1줄 내려간 후에 다시 가로로 이동하게된다.

위의 그림의 경우로 말하자면 첫 번째는 Stride=(1,1)인 경우이고, 두 번째는 Stride=(2,2)라고 할 수 있다.


위에서 다룬 Padding과 Stride 그리고 Filter Size에 따라서 Feature map의 크기가 달라진다. 여기서 Feature Map은 Convoluted Feature와 같은 의미로 합성곱에 의한 출력을 의미한다. 그리고 이 Feature map의 크기 공식은 다음과 같다.

Nout=[Nin+2pks]+1N_{\text{out}} = \bigg[\frac{N_{\text{in}} + 2p - k}{s}\bigg] + 1
  • NinN_{\text{in}}: 입력되는 이미지의 크기(=피처 수)
  • NoutN_{\text{out}}: 출력되는 이미지의 크기(=피처 수)
  • kk: 합성곱에 사용되는 커널(=필터)의 크기
  • pp: 합성곱에 적용한 패딩 값
  • ss: 합성곱에 적용한 스트라이드 값

풀링Pooling

어떻게 합성곱 층이 작동하는지 이해했다면 풀링 층Pooling layer은 매우 쉽게 이해할 수 있다. 이 층의 목적은 계산량과 메모리 사용량, (결과적으로 과대적합의 위험을 줄여주는) 파라미터 수를 줄이기 위해 입력 이미지의 부표본(Subsample), 즉, 축소본을 만드는 것이다.

풀링 방법에는 최대 풀링(Max Pooling)평균 풀링(Average Pooling)이 있다. 최대 풀링은 정해진 범위 내에서 가장 큰 값을 꺼내오는 방식이며, 평균 풀링은 정해진 범위 내에 있는 모든 요소의 평균을 가져오는 방식이다. 풀링의 방식을 살펴보면 가중치가 존재하지 않는다는 것을 알 수 있다. 단순히 값을 꺼내올 뿐인 것이다. 또한 채널 수 역시 변하지 않는다. 보통 최대 풀링이 평균 풀링보다 성능이 더 좋기 때문에 최대 풀링을 주로 사용한다.

합성곱 층에서와 마찬가지로 풀링 층의 각 뉴련은 이전 층의 작은 사각 영역의 수용장 안에 있는 뉴런의 출력과 연결되어 있다. 이전과 동일하게 크기, 스트라이드, 패딩 유형을 지정해야 한다. 이때 보통 크기와 스트라이드를 동일하게 설정한다.

아래 그림은 2X2크기의 최대 풀링과 평균 풀링을 처리하는 과정을 비교하여 나태낸 것이다.

위 그림에서 왼쪽은 최대 풀링으로 각각의 2×2의 범위 내에서 가장 큰 요소인 100,184,12,45 출력 데이터로 가져온다. 오른쪽은 평균 풀링으로 각각의 2×2의 범위 내 요소의 평균값인 36,80,12,15 를 출력 데이터로 가져온다.

계산량, 메모리 사용량, 파라미터 수를 감소하는 것 외에도 최대 풀링의 경우 작은 변화에도 일정 수준의 불변성을 만들어 준다.

위 그림에서 볼 수 있듯이 8이라는 형태가 전체 픽셀에서 어느 정도 이동하더라도 풀링 층을 거친 결과는 동일하다는 것을 볼 수 있다. 이를 이동 불변성이라고 한다. CNN에서 몇 개 층마다 최대 풀링 층을 추가하면 전체적으로 일정 수준의 이동 불변성을 얻을 수 있다. 또한 최대 풀링은 회전과 확대, 축소에 대해서 약간의 불변성을 제공한다. 이와 같은 불변성은 제한적이긴 하지만 분류 작업처럼 예측이 이런 작은 부분에서 영향을 받지 않는 경우 유용할 수 있다.

하지만 이 풀링은 단점 역시 가지고 있다. 풀링을 진행하게 되면 입력값의 정보가 일부 사라지게 된다. 대체로 최대 풀링이 평균 풀링보다 더 많은 정보 손실을 가져온다. 그리고 최대 풀링의 불변성의 경우에는 어떤 어플리케이션에서는 불필요하기 때문에 목표하는 어플리케이션의 기능이나 Task에 맞게 풀링을 사용할지 안할지 결정해야 한다.

💡 추가 내용
위에서 말한 2가지의 풀링 층 외에도 현대적인 신경망 구조에서 볼 수 있는 전역 평균 풀링 층(GAP)이라는 풀링 층이라는 것도 존재한다. 이 층은 동작 방식이 매우 독특하다. 각 Feature map의 평균을 계산한다.

위 그림에서 볼 수 있듯이, 각 샘플의 Feature map마다 하나의 숫자를 출력한다는 것이다. 이 방식은 엄청난 정보 손실을 가져오지만 출력층에서는 유용할 수 있다.

이런 출력 층을 만들려면 keras.layers.GlobalAvgPool2D클래스를 사용하면 된다.

완전 연결 신경망(Fully Connected Layer)

합성곱 층(Convolutional Layer)와 풀링 층(Pooling Layer)에서 충분히 특징을 추출했다면,
다음은 분류를 위한 완전 연결 신경망을 구축할 차례이다.

완전 연결 신경망은 여러분이 이전에 구축했던 다층 퍼셉트론 신경망으로 구성되어 있으며
풀어야 하는 문제에 따라서 출력층을 잘 설계해주는 것이 중요하다.

2. CNN의 학습

CNN에서 학습되는 부분은 바로 Convolution 층에 있는 Filter의 가중치와 편향, 완전 연결 신경망을 구성하는 다층 퍼셉트론 신경망의 가중치와 평향이다.

학습된 필터를 시각화하면 아래의 그림과 같은 형태로 나타난다. 참고로 아래의 그림은 ImageNet 데이터를 학습한 CNN의 Convolution층의 Filter의 가중치를 시각화한 것이다.

층이 깊어지면 Convolution 층과 Pooling 층을 거치면서 이미지가 작아지고 Convolution 층의 Filter는 더 큰 특징을 포착하게 된다.

위 그림을 보면 낮은 층에서는 단순하게 가로, 세로, 대각선 등의 특징을 학습하는 것을 볼 수 있고, 층을 지날수록 물체의 일부분을 포착하면서 결국 물체 전체의 윤곽에 해당하는 특징을 학습하는 것을 알 수 있다.


CNN 학습 example

일반적인 이미지를 10개의 클래스로 분류하는 데이터셋인 Cifal 10 데이터셋을 직접 구축한 CNN을 통하여 분류하는 예제를 풀어보겠다.

먼저 필요한 패키지와 라이브러리를 불러오고, 시드를 고정한다.

from tensorflow.keras.datasets import cifar10
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Dense, Conv2D, MaxPooling2D, Flatten
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential
from tensorflow.keras.datasets import cifar10

from sklearn.model_selection import train_test_split

import numpy as np
import tensorflow as tf

np.random.seed(42)
tf.random.set_seed(42)

그 후 데이터셋을 불러온 후 학습 데이터셋(Train Dataset)과 시험 데이터셋(Test Dataset)으로 나누어(Split)주고 픽셀값을 정규화 하여준다.

# 데이터 불러오기
(X_train, y_train), (X_test, y_test) = cifar10.load_data()

# 픽셀값 정규화
X_train = X_train.astype('float32') / 255.
X_test = X_test.astype('float32') / 255.

# 데이터셋 Split
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=.2)

이제 본격적으로 신경망 모델을 구축해 보겠다. 3개의 Convolution 층 사이에 Pooling 층을 끼워넣어 특징 추출 부분을 구성하고, 1개의 은닉층과 출력층으로 구성된 완전 연결 신경망으로 분류기를 구축하여 보겠다.

# 모델 구축
model = Sequential()

# Conv-Pool layer 특징을 추출하는 부분
model.add(Conv2D(32, (3,3), padding='same', activation='relu'))
model.add(MaxPooling2D(2,2))
model.add(Conv2D(64, (3,3), padding='same', activation='relu'))
model.add(MaxPooling2D(2,2))
model.add(Conv2D(64, (3,3), padding='same', activation='relu'))

# Dense layer에 입력 가능하도록 Data 펼치기
model.add(Flatten())

# 분류를 위한 신경망 구성
model.add(Dense(128, activation='relu'))
model.add(Dense(10, activation='softmax'))

Conv2D에 대해서 조금 더 자세히 살펴보면 다음과 같다.

tf.keras.layers.Conv2D(
    filters,
    kernel_size,
    strides=(1, 1),
    padding='valid',
    data_format=None,
    dilation_rate=(1, 1),
    groups=1,
    activation=None,
    use_bias=True,
    kernel_initializer='glorot_uniform',
    bias_initializer='zeros',
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    **kwargs
)
  • filters: 정수로 Output shape의 차원을 결정한다. 더 쉽게 말하면 filter의 수를 의미한다.
  • kernel_size: 필터의 크기를 지정

위 두가지 파라미터에 대해서 조금 더 자세히 다루어 보겠다. Conv2D Layer를 지나게 되면 기본적으로 Chanel의 수가 filters에 입력된 수로 변하게 된다. 그 과정은 다음과 같다고 할 수 있다.

1개의 feature map은 다음과 같이 계산됩니다.

  1. 각각의 channel에 대응되는 kernel을 통해 슬라이딩
  2. 각 channel의 결과를 모두 더한다 → 하나의 feature map 생성
  3. 헤딩 과정을 생성하려는 feature map의 수만큼 반복

그림에서 볼 수 있듯이 각 필터에는 입력된 채널에 대응하는 커널이 존재하고, 이 커널의 합으로 결국에 하나의 Ouput을 만들어 낸다. 그리고 이 Output을 결국 필터의 수 만큼 반복하기 때문에 Filters의 값이 출력값의 차원, 즉 Channel수가 된다.

  • Padding: padding 방식 지정, vaildsame을 사용할 수 있다.

이제 모델을 컴파일하고 훈련 시켜 준다.

# 모델 컴파일
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
# 모델 훈련
model.fit(X_train, y_train,
          batch_size=128,
          validation_data=(X_val, y_val),
          epochs=10)

이 학습한 신경망 모델을 사용하여 성능을 평가한다.

model.evaluate(X_test, y_test, verbose=2)

3. 전이학습(Transfer Learning)

신경망의 계층 구조는 심층 신경망이 좋은 솔루션으로 빠르게 수렴하게끔 도와줄 뿐만 아니라 새로운 데이터에 일반화되는 능력도 향상시켜준다.

이런 계층 구조를 가진 새로운 신경망을 구축 할 때 처음에 가중치와 편향을 난수로 초기화 하는 대신에 사전 학습 모델(Pre-Trained Model)의 가중치를 그대로 가져와 사용하면 대부분의 사진과 같은 데이터에 나타나는 저수준 구조를 학습할 필요가 없게 된다. 즉, 고수준 구조만 학습하면 된다. 이를 전이 학습Transfer Learning이라고 한다.

사전 학습 모델의 가중치는 대량의 데이터를 학습하여 얻어진다. 여러 데이터의 일반적인 특징을 많이 학습하였기 때문에 어떠한 데이터를 넣더라도 준수한 성능을 보인다. 일반적으로 사전 학습 가중치는 학습되지 않도록 고정(freeze)한 채로 진행되기 때문에 빠르게 좋은 결과를 얻을 수 있다는 장점이 있다.

이미지 분류를 위한 주요 사전 학습 모델로는 다음과 같은 것들이 있다. 블로그 정리 글을 링크로 남겨두었으니 나중에 복습할 때 참고하자.

사전 학습 모델
1. VGG - VGG 논문
2. GoogLeNet(Inception) - GoogLeNet 논문
Inception에 대해서 다시 또 고찰한 "Rethinking the Inception Architecture for Computer Vision"라는 논문도 있다. Inception은 가로 방향으로 층을 넓게 구성한 구조를 의미한다.
3. ResNet - Resnet 논문
ResNet에서는 Residual Connection(=Skipped Connection)라는 중요한 특징이 있으니 추후 복습 시에 이 부분을 찾아보자.

전이학습 예제

# 패키지 및 라이브러리 불러오기
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras.models import Sequential
from tensorflow.keras.datasets import cifar10

from sklearn.model_selection import train_test_split

import numpy as np
import tensorflow as tf

# Seed 고정
np.random.seed(42)
tf.random.set_seed(42)

# 데이터셋 불러오기
(X_train, y_train), (X_test, y_test) = cifar10.load_data()

# 픽셀값 정규화
X_train = X_train.astype('float32') / 255.
X_test = X_test.astype('float32') / 255.

# 데이터셋 Split
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=.2)

이제 다음 코드로 사전 학습된 모델(VGG16)을 불러온다.

pretrained_model = VGG16(weights='imagenet', include_top=False)

이제 사전 학습 모델 위에 완전 연결 신경망을 추가한다. 이때 GlobalAveragePooling2d()층은 Flatten과 비슷한 역할을 수행하는 층으로 데이터의 Shape을 (None, None, None, 512)에서 (None, 512)로 변화시켜주는 역할을 한다.

# 완전 연결 신경망 추가하여 모델 생성
model = Sequential()
model.add(pretrained_model)
model.add(GlobalAveragePooling2D())
model.add(Dense(128,activation='relu'))
model.add(Dense(10,activation='softmax'))

# 모델 컴파일
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 모델 훈련
model.fit(X_train, y_train,
          batch_size=128,
          validation_data=(X_val, y_val),
          epochs=10)

# 신경망 모델을 사용하여 평가
model.evaluate(X_test, y_test, verbose=2)

4. 이미지 증강

이미지 증강이란 회전, 반전, 자르기 ,밝기 혹은 채도 변화 등을 통하여 데이터를 늘리는 방법이다.

인간은 쉽지 않게 위 두 이미지를 고양이라고 판단할 수 있다. 하지만 컴퓨터의 경우 학습 시에 대부분 정면의 고양이 사진을 가지고 학습을 하고, 그렇기 때문에 모델이 기울어져 있거나 뒤집어진 이미지에서 잘 예측하지 못한다는 취약점을 가지고 있다.

따라서 사진을 일부로 회전, 반전, 자르기, 밝기 혹은 채도 변화, 늘리기 등의 가공을 하여 더 강건한 모델을 만들기 위해 진행한다.

다음과 같은 코드로 이미지 증강을 할 수 있으며 상하 반전과 회전을 준 경우의 코드이다.

data_augmentation = tf.keras.Sequential([
  layers.experimental.preprocessing.RandomFlip("vertical"),
  layers.experimental.preprocessing.RandomRotation(0.2),
])
augmented_image = data_augmentation(image)

이 방법 외에도 ImageDataGenerator를 사용하여 이미지 증강을 할 수 있다.

ImageDataGenerator(horizontal_filp=True, vertical_flip=True,
rescale=1/255.0, rotation=0.45, zoom_range=[0.5, 1.5]) 

더 자세한 내용은 공식문서를 확인하자

5. 1X1 Convolution

1X1 convolutionGoogeLeNet에서 많이 사용되는 방법으로 계산량 감소, 차원축소, 비선형성 증가, Overfitting 방지등의 장점을 가지고 있어서 많이 사용된다. 자세한 설명은 다음 블로그를 참고하자.

💡 1X1 Convolution

6. 회고

확실히 NLP보다 CV가 더 재미있는 것 같다. 그리고 오늘 ADsP시험을 신청했는데 열심히 준비해야겠다. 오늘 원래 대충 정리하고 21:30부터 ADsP공부 좀 하려 했는데...결국 11시까지 못 그만두고 다 해버렸다. 내일부터는 좀 계획된 시간대로 움직이고 더 부지런하게 움직여야 할 것 같다. 4일 연휴 연장 푹 쉬었으니까 다시 열심히 해겠다. 아 근데 아직 정리 안한 N33x들과 N42x들은 언제 정리하지....



참고자료
1. 합성곱 신경망(CNN)/고양이 눈에서 답을 얻다.
2. 오렐리앙 제옹, 핸즈온 머신러닝(2판), 서울:한빛미디어,O\cdotREILLY, 2020
3. CodeStates Lecture Note - N431

0개의 댓글