안녕하신지요?
CNN을 찾아보다 들어오셨군요^_^
쌈뽕하게 말아드리겠습니다.
CNN 기초부터 들어갑니다.
저는 공부를 할때 항상 뭐의 약자. 인지부터 확인을 하는데요, CNN -> Convolutional Neural Network 직역하면 합성곱 신경망으로 딥러닝의 종류 중 하나입니다.
원래는 MLP -> Multi Layer Perceptron : 다중 퍼셉트론을 사용했습니다?
그런데! 요놈은 문제가 좀 많은 친구입니다;;
1. 공간 정보를 무시합니다.
-> MLP의 경우 2차원 이미지를 1차원 벡터로 변환하여 사용하기 때문에 픽셀들의 연결성을 유추할 수가 없습니다. 쉽게 말해 그림을 봐도 생각이 짧아서 이해를 못하는 친구입니다.
2. 계산의 복잡도가 증가합니다.
-> 모든 픽셀을 입력 데이터로 사용합니다. 때문에 계산이 어마어마하게 늘어나서 복잡하겠죠? 별로입니다.
3. 입력 이미지에 대한 변화가 출력에 큰 영향을 미칩니다.
-> 이미지 상의 특정 물체가 움직이거나 회전 등의 변화가 일어나면 인식을 못해요. 역시 생각이 짧은 멍청한 모델입니다.
4. 과적합되기 쉽습니다.
-> 모든 픽셀들이 하나의 특징으로 간주되기 때문에 학습 데이터에만 잘 맞는 복잡한 모델이 됩니다.
아아아아~~ 그러면 어떡하지??
-> 이 문제를 해결하기 위해 사용하는 모델이 바로 CNN- > Convolutional Neural Network입니다.
모든 설명글이 그렇듯, 짧게 개념 설명 들어갑니다.
CNN은 DNN-> Deep Neural Network가 인간의 신경망을 본떠 만든 것처럼, CNN도 시각 피질의 neuron의 작동 원리를 본떠서 만들었습니다.
대충 사람의 눈으로 물체를 보고 판단하는 과정을 그대로 구현했다고 생각하시면 됩니다. 자세한 내용은 생명과학에서 배우시죠,,
CNN은 다음과 같은 구조로 이루어졌습니다.
특징을 뽑아내고, 뽑아낸 특징을 압축하여 MLP와 같은 방식으로 목표값을 찾는 과정.
- Convolution Layer : 특징을 추출하는 층
- Pooling Layer : 정보를 압축하는 층
- Fully Connected Layer : 기족의 MLP와 같은 방식으로 작동
Convolution Layer에 대해 조금 더 자세히 다뤄볼게요.
Convolution Layer는 입력 데이터로부터 특징을 추출합니다.
특징을 추출할때 filter라는, 특징을 추출하는 기능을 담당하는 부분과 이 filter의 값을 비선형으로 변형하는 활성화 함수로 구성됩니다.
Filter?
filter를 이용하여 탐색하면서 이미지의 특징을 추출한다고 했죠?
추출한 특징들을 가지고 Feature Map으로 생성합니다.
여기서 필요한 용어 2개만 정리할게요.
- Stride : filter가 순회하는 간격을 말합니다. 그림에서는 stride가 1입니다.
- Padding : 특징들만 추출하다보니 Feature Map의 크기는 입력데이터보다 작아집니다. 이 문제를 방지하기 위해 외각, 테두리에 지정한 픽셀만큼 특정 값으로 채웁니다. 농구할때 인원수 채우기 느낌처럼 별 의미는 없음.
예제 코드를 살펴보며 이해해보겠습니다.
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# 여기서 이미지를 가져올 디렉토리를 확인하고 싶다면
# import os
# print(os.getcwd())
# 위의 코드 사용
# 혹은 해당 이미지 파일을 오른클릭 후 속성을 들어가면 위치가 표시되며
# 해당 위치를 복사하여 붙여넣고 \를 /로 변경
image = Image.open('house.jpg').convert('L')
image = np.array(image)
plt.imshow(image, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.show()
vertical_filter = np.array([[1, 0, -1],
[1, 0, -1],
[1, 0, -1]])
horizontal_filter = np.array([[1, 1, 1],
[0, 0, 0],
[-1, -1, -1]])
# a1 문제
def apply_filter(image, filter):
# 이미지의 크기
image_height, image_width = image.shape
# 필터의 크기
filter_height, filter_width = filter.shape
# feature map을 위한 배열 초기화
# 이미지 크기 - 필터 크기 + 1을 그대로 구현, 이미지의 크기의 경우 가로, 세로의 값이 다를 수 있어 각각 계산
output_image = np.zeros((image_height - filter_height + 1, image_width - filter_width + 1))
# 이미지의 일부(필터의 크기와 동일한)를 가져와 필터와 컨볼루션 연산
# range(image_height - filter_height + 1)를 사용하는 이유는 필터가 이미지의 각 꼭짓점부분까지 가면
# 이미지의 크기를 벗어나 없는 값에 접근하기 때문에 범위를 조절하여 필터가 이미지에서 벗어나지 않게 조절
for i in range(image_height - filter_height + 1):
# 위와 동일한 이유로 조절
for j in range(image_width - filter_width + 1):
# image[i:i+filter_height, j:j+filter_width]의 경우 i, j번째 이미지의 부분에 접근하여 필터의 크기만큼
# 이미지를 가져와서 filter와 곱한 후 각 값을 모두 더하는 컨볼루션 연산 진행
# 마지막으로 feature map의 해당하는 위치에 값을 넣어줌
output_image[i, j] = np.sum(image[i:i+filter_height, j:j+filter_width] * filter)
return output_image
# a2 문제
def apply_filter_stride(image, filter, stride=2):
# 동일하게 이미지와 필터의 크기를 가져옴
image_height, image_width = image.shape
filter_height, filter_width = filter.shape
# 이번에는 stride가 추가되었으며 기존의 경우 1씩 전진을 하였지만 이번에는 stride만큼 가기 때문에
# 나누기를 통해서 feature map의 크기를 계산
# 식의 경우 (이미지의 크기 - 필터의 크기) / stride + 1
output_height = (image_height - filter_height) // stride + 1
output_width = (image_width - filter_width) // stride + 1
output_image = np.zeros((output_height, output_width))
# range(a, b, c) : a 부터 b - 1까지 c만큼 건너뜀, 이 말은 stride 만큼 가져올 이미지의 위치를 조절
for i in range(0, image_height - filter_height + 1, stride):
# 위와 동일한 이유로 조절
for j in range(0, image_width - filter_width + 1, stride):
# feature map의 경우 i와 j의 값이 stride배 만큼 오르기 때문에 그만큼 값을 나누어주어야 함
# 그 부분을 제외하고 뒷 부분은 a1과 동일
output_image[i // stride, j // stride] = np.sum(image[i:i+filter_height, j:j+filter_width] * filter)
return output_image
vertical_output = apply_filter(image, vertical_filter)
horizontal_output = apply_filter(image, horizontal_filter)
combined_output = vertical_output + horizontal_output
fig, ax = plt.subplots(1, 3, figsize=(18, 6))
ax[0].imshow(vertical_output, cmap='gray')
ax[0].set_title('Vertical Filter Output')
ax[0].axis('off')
ax[1].imshow(horizontal_output, cmap='gray')
ax[1].set_title('Horizontal Filter Output')
ax[1].axis('off')
ax[2].imshow(combined_output, cmap='gray')
ax[2].set_title('Combined Output')
ax[2].axis('off')
plt.show()
a1) 주어진 filter와 입력 데이터에 Convolution 연산을 하는 함수를 만들어보자
이미지의 크기와 필터의 크기는 각각의 높이와 너비로 변수.shape으로 설정합니다.
feature map(출력 이미지)의 크기의 경우 이미지 크기 - 필터 크기 + 1 이라고 했으므로.
output_image = np.zeros((image_height - filter_height +1, image_width - filter_width + 1)) 을 하여
0으로 배열을 만들고 안에 값을 각각의 높이와 너비의 차이 값에 +1 한 값으로 채워줍니다.
이후 Convolution 연산을 수행해줍니다.
for i in range(image_height - filter_height + 1):
for j in range(image_width - filter_width + 1):
이미지의 경우 i, j번째 이미지의 부분에 먼저 접근을 합니다.
이후 필터의 크기만큼 이미지를 가져와서 필터와 곱한 후 각 값을 모두 더하는 컨볼루션 연산을 아래에 수행합니다.
output_image[i, j] = np.sum(image[i:i+filter_height, j:j+filter_width] * filter)
이후 출력된 이미지를 반환합니다.
return output_image
'전공 공부 > AI(인공지능)' 카테고리의 다른 글
ADAM : A METHOD FOR STOCHASTIC OPTIMIZATION 논문 정리 (0) | 2024.05.07 |
---|