다양한 신경망 - 이미지 처리
1. 서론
* 이미지 처리 기술 예시
: 얼굴 인식 카메라, 화질 개선(Super Resolution), 이미지 자동 태깅 (사진 안의 물체를 분류해서 tagging)
* 이미지 처리를 위해 우리는 '데이터 전처리' -> '딥러닝 모델'의 과정을 거친다.
2. 이미지 데이터 전처리
* 컴퓨터에게 이미지는 각 pixel값을 가진 숫자 배열로 인식
(작은 단위의 정사각형으로 이루어짐 - pixel 안의 색상으로 숫자 표현
* 배열로 이루어짐 - 색상 숫자
* 모두 같은 크기를 갖는 이미지로 통일
- 가로, 세로 픽셀 사이즈를 표현하는 해상도 통일
(예를 들어 HD는 1280x720으로 1280 가로 pixel & 720 세로 pixel 수만큼 표현됨: 이런 해상도 방식을 모두 통일)
- 색을 표현하는 방식 통일 (RGB, HSV, Gray-scale, Binary, ....)
ex)
- MNIST 데이터: 사람의 손글씨를 이미지로 표현한 데이터
- 전처리 점검 1) 위 MNIST 데이터에서 보면 총 9개 데이터가 모두 28x28의 해상도로 표현됨
- 전처리 점검 2) 색상 스케일은 Gray-scale로 표현됨 (Gray-scale은 검정색, 흰색, 회색으로 표현하는 방식)
- 즉, 위 9개의 데이터는 모두 동일한 해상도 & 색상 스케일로 표현됨
- 위 원본 이미지(맨 오른쪽)의 경우 250 x 300으로 해상도가 맞지 않음 (가로와 세로가 동일한 해상도여야 함)
- 해상도를 28x28로 동일하게 표현
- Gray-scale 방식을 이용해서 색 표현을 변환한다
- 결과, MNIST 데이터와 비슷하게 표현 가능: 전처리 완료
3. [1] 실습 - MNIST 분류 CNN 모델: 데이터 전처리
** 손으로 쓴 0 ~9 까지의 글자들이 있고, 이 데이터를 이용해서 신경망을 학습시킨다. 학습된 결과가 손글씨를 인식할 수 있는 지 검증하자.
* MNIST 구성 - 색상은 Gray-scale & 크기는 28x28 & 각 사진별 label들이 정해져 있음
[1] CNN을 위한 데이터 전처리
* CNN 모델은 채널(RGB 혹은 흑백 - 색상 고려)까지 고려한 3차원 데이터를 입력으로 받기에 채널 차원을 추가해 데이터의 모양(shape)을 바꿔야 함
* 즉, [데이터 수, 가로 길이, 세로 길이] -> (변환) [데이터 수, 가로 길이, 세로 길이, 채널 수]
(shape() 결과)
{1} import & MNIST dataset 불러오기 및 가공
* 내장함수 load_data()를 통해 test set & train set으로 나누어 불러온다.
* 불러온 train set & test set은 모두 tensor 형태로 저장되어 있다.
* 불러온 각각의 set을 train set의 경우 5000개, test set의 경우 10000개만 slicing한다.
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
mnist = tf.keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images, train_labels = train_images[:5000], train_labels[:5000]
test_images, test_labels = test_images[:1000], test_labels[:1000]
* train_images와 test_images의 shape() 결과 각각 (5000, 28, 28) & (1000, 28, 28)이 나온다.
* train_labels & test_labels는 각각 레이블용 설명으로 달린 MNIST 데이터
{2} sample data 출력하기
1. 한 개의 MNIST 데이터 출력
plt.figure(figsize=(10, 10))
plt.imshow(train_images[0], cmap=plt.cm.binary)
plt.colorbar()
plt.title("Training Data Sample")
plt.savefig("sample1.png")
- 상단 코드 설명 -
* imshow) train_images의 첫번째 예시 출력 (train_images[0]) & cmap 값으로 Gray-scale 방식 사용
(Gray-scale 방식은 우측의 색상 바의 색상 구성이 흰색 ~ 검은색 사이의 하단 색으로 구성 - 즉, 해당 구성된 색상으로만 픽셀에 표현된다는 뜻)
* colorbar() 우측의 색상 바 출력
* title 넣고 savefig 출력하여 사용자 지정 이름으로 저장
2. 9개의 샘플 데이터 출력
* class_names 따로 리스트 형태로 뽑아서 출력 결과에 label 값까지 표현
class_names = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
for i in range(9):
plt.subplot(3,3,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.xlabel(class_names[train_labels[i]])
{2} CNN 모델의 input 형태로 변환하기
* tf.expand_dims(data, axis) 함수 활용
= tensor 배열 데이터에서 해당 축 index에 차원 하나를 추가하는 함수
- axis가 0이라면 data[0], 즉 첫 번째 데이터에 차원 하나가 추가되고 그 뒤의 데이터가 밀림
- axis가 -1일 경우 맨 마지막에 차원 하나가 추가되는 형태
* (하단 실습 한정) CNN 모델의 입력으로 사용할 수 있도록 (샘플개수, 가로픽셀, 세로픽셀, 1) 형태로 변환하기 (채널값 = 1)
- 해당 실습의 경우 맨 마지막 인자로 channel 1값이 추가되므로 axis값으로는 -1을 입력받음
tf.expand_dims(data, axis)
train_images = # axis -1
test_images = # axis -1
- train_images_shape의 결과는 (5000, 28, 28, 1)
- test_images_shape의 결과는 (1000, 28, 28, 1)
4. 이미지 처리 - 딥러닝 모델
[1] 기존 다층 퍼셉트론 기반 신경망(MLP)의 이미지 처리 방식
* 예를 들어 6x6 형태의 이미지를 MLP 신경망 모델의 input으로 집어넣을 경우 2차원이기에 이를 1차원 형태로 총 36가지의 데이터 배열 형태여야 하기에 극도로 많은 수의 파라미터가 필요하다
* 또한 2차원을 1차원으로 변환하였기에 2차원 형태의 기존 이미지 의미를 잃게 된다
* 추가로 이미지에 조금의 변화가 생기면 데이터의 관점에서는 해당 이미지의 의미를 쉽게 파악하기 어렵게 된다
-> 해결 방법은 '합성곱 신경망'
[2] 합성곱 신경망(Convolution Neural Network; CNN)
* 작은 필터를 순환시키는 방식
* 이미지의 패턴이 아닌 특징을 중점으로 인식
{1} 합성곱 신경망의 구조
* 입력 이미지의 특징을 추출 & 분류하는 과정으로 동작
- CNN(Convolution Layer + Pooling Layer) + FC(Fully-Connected Layer)
- Fully-Connected: 각 layer마다 node들이 서로 서로 full로 연결되어 있다는 뜻 (=Dense Layer)
(받은 layer들은 CNN을 통해 받은 각 특징별 layer 집합)
- Convolution Layer & Pooling Layer가 특징을 추출
(즉 CNN이 위의 예에서 고양이의 귀, 코, 수염, 꼬리라는 특징을 추출해 냄)
- 그 이후 FC가 분류 과정을 수행함
(분류 딥러닝 모델 포스팅은 아래 링크 참조 - FC의 역할)
{2} Convolutional Layer
* Convolution Layer - 이미지에서 어떠한 특징이 있는 지를 구하는 과정
- 필터가 이미지를 이동하며 새로운 이미지(피쳐맵)를 생성
- 구별해 낼 특징을 가진 필터를 입력 이미지 내에서 한 칸 씩 이동하며 해당 이미지가 있거나 비슷하면 큰 값으로 계산됨
- 그 결과 원하는 특징이 이미지에서 존재하는 지, 존재한다면 어디에 위치해 있는 지 확인 가능
* 특징별 수만가지의 필터 생성 가능 - 이에 따른 각각의 피쳐맵(feature map) 생성 가능
* Padding - 원본 이미지의 상하좌우에 한 줄씩 추가
- feature map의 경우 필터가 이동한 결과 원본 입력 이미지에 비해 한 칸씩 크기가 작게 나오기에, 그 크기를 맞추기 위해 패딩 사용
- 즉, 원본 이미지의 상하좌우에 0을 각각 추가
* Striding - 필터를 이동시키는 거리(stride) 설정
- 이렇게 padding & striding을 통해 feature map을 다양한 방식으로 만들 수 있음
{3} Pooling Layer
* 이미지 왜곡의 영향(노이즈)를 축소하는 과정 - feature map 축소
* 예를 들어 귀 특징 필터를 적용해서 feature map을 생성하였을 때, 원하는 귀가 있는 쪽의 데이터만 중요할 뿐, 그 외에 의미 없는 0의 숫자들은 필요 없음 - 이 때 pooling layer를 통해 필요부분만 남겨 강조하는 방식 (정보 압축)
* pooling layer를 통해 알고자 하는 필요 부분은 해당 특징의 유무 & 해당 특징이 존재한다면 그의 위치 정보
* Average pooling보다는 사실상 Max pooling을 주로 많이 사용
{4} Fully-Connected Layer
* 추출된 특징을 사용하여 이미지를 분류
* pooling layer의 결과로 생성된 각 특징 필터의 결괏값이 1차원 형태의 배열로 각 필터들이 쭉 나열되어 생성됨 (flatten layer 활용하면)
- 생성된 해당 1차원 형태의 배열을 통해 분류가 가능
* Softmax 활성화 함수
- 단순이 한 개의 label이 아닌 지, 맞는 지의 경우 앞서 언급된 계단함수를 사용하면 되지만,
- 여러 개의 label을 분류하고 싶다면 softmax 함수를 사용
- 즉, 마지막 FC layer의 unit 개수는 예측해야 하는 범주 label의 개수에 해당
- softmax함수의 출력값은 (위 a ~f) 각각 각 label이 나올 확률이 됨
- 즉 a~f는 모두 확률값이며 (상단 예), a+b+c+d+e+f = 1, a,b,c,d,e,f >=0이 됨
- 즉, 결과물인 여러 확률을 봤을 때 가장 큰 값을 가진 확률에 해당하는 label이 우리가 원하는 이미지의 label이라고 결론을 내릴 수 있다.
{5} 정리
* 합성곱을 통해 특징 추출 - 풀링을 통해 사이즈 조절 & 노이즈 처리 - FC layer의 활성함수를 통해 분류가 가능
* Convolution Layer는 특징을 찾아내고
* Pooling Layer는 처리할 맵(이미지) 크기를 줄여준다. 이를 N번 반복
- 합성곱 & 풀링 과정은 N번 반복
- 즉, 예를 들어 고양이의 '귀' 특징 필터를 만들어 내었지만, 귀의 더 자세한 여러 특징 필터를 생성하여 더 자세하게 특징을 분리해 낸다
- 필터의 개수가 많아져도 풀링의 과정을 통해 도중도중 지속적으로 사이즈를 축소시키기에 용량 문제는 해당 없음
* 반복할 때마다 줄어든 영역에서의 특징을 찾게 되고, 영역의 크기를 작아졌기 때문에 빠른 학습이 가능해진다.
* Object detection & segmentation
- 그림 안의 물체를 판단하고 어떤 종류인지 판단하는 기술
* Super Resolution (SR)
- 해상도가 낮은 이미지를 높이는 기술 역시 CNN 기반의 딥러닝으로 실현 가능
5. [2] 실습 - MNIST 분류 CNN 모델: 모델 구현
* 위 [1] 실습에 이어서 진행 (이미지 데이터 전처리 완료 가정)
* 모델 설정
- keras를 활용한다
- 분류 모델에 맞게 마지막 layer의 node 수는 10개 (MNIST데이터 숫자 총 0 ~9 까지 구성) & activation 함수는 'softmax'로 설정
[1] CNN - Convolution layer 만들기
: 입력 이미지의 특징, 즉 처리할 feature map을 추출하는 layer
- filters: 필터(커널) 개수
- kernel_size: 필터(커널)의 크기
- activation: 활성화 함수 종류
- padding: 이미지가 필터를 거칠 때 그 크기가 줄어드는 것을 방지하기 위해서 가장자리에 0의 값을 가지는 픽셀을 넣을 지 말 지를 결정하는 변수 ('SAME'은 넣는다는 뜻 / 'VALID'는 넣지 않는다는 뜻)
tf.keras.layers.Conv2D(filters, kernel_size, activation, padding)
[2] CNN - Pooling layer 만들기 - Maxpool layer
: 처리할 feature map의 크기를 줄여주는 layer
- padding: 'SAME' (적용) / 'VALID' (미적용)
tf.keras.layers.MaxPool2D(padding)
[3] Flatten layer 만들기
: convolution layer 또는 pooling layer의 결과는 N차원의 tensor 형태이다. 따라서 이 결과를 1차원의 형태로 평평하게 만들어 준다.
- 앞선 모든 convolution & pooling layer를 거친 다음 마지막에 삽입
tf.keras.layers.Flatten()
[4] Dense layer 만들기
: 분류 과정 수행
- node: 노드(뉴런) 개수
- activation: 활성화 함수 종류
tf.keras.layers.Dense(node, activation)
[5] 실습 코드
{1} 모델 설정
- 첫 번째 layer는 32개의 filter, 크기는 (3,3) 적용 & 활성화 함수는 ReLU 적용
(첫 번째 layer에는 앞선 실습에서 이미지 데이터 전처리한 결과 형태 (28,28,1)을 input_shape 인자로 꼭 집어넣는다.) (가로픽셀, 세로픽셀, 채널값)
- 총 3개의 layer (각 layer 당 padding 적용)
- flatten layer 적용) 만들어진 특징들을 1차원 형태의 vector로 변환
- Dense layer 적용) 총 두 종류 relu & softmax 각각 설정
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(filters = 32, kernel_size = (3,3), activation = 'relu', padding = 'SAME', input_shape = (28,28,1)),
tf.keras.layers.MaxPool2D(padding = 'SAME'),
tf.keras.layers.Conv2D(filters = 32, kernel_size = (3,3), activation = 'relu', padding = 'SAME'),
tf.keras.layers.MaxPool2D(padding = 'SAME'),
tf.keras.layers.Conv2D(filters = 32, kernel_size = (3,3), activation = 'relu', padding = 'SAME'),
tf.keras.layers.MaxPool2D(padding = 'SAME'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(64, activation = 'relu'),
# tf.keras.layers.Dense(, activation = '') - 10 nodes with 'softmax' activation function
])
{2} 모델의 학습방법 설정 & 학습
* 해당 실습 모델은 분류를 통한 학습을 수행하므로 분류 관련 CNN 모델 학습방법을 설정한다
* 분류 학습에서는 loss 함수로 sparse_categorical_crossentropy 주로 사용
* adam optimizer를 사용하며 매 epoch마다 accuracy 정확도를 평가하는 모델을 설정한다.
model.compile(loss = 'sparse_categorical_crossentropy',
optimizer = 'adam',
metrics = ['accuracy'])
* keras fit 함수에서 batch_size 인자까지 epochs와 함께 동시에 설정 가능
history = model.fit(train_images, train_labels, epochs = 20, batch_size = 512)
{3} 모델 학습 결과 시각화
* Visualize 함수 생성
def Visulaize(histories, key='loss'):
for name, history in histories:
plt.plot(history.epoch, history.history[key],
label=name.title()+' Train')
plt.xlabel('Epochs')
plt.ylabel(key.replace('_',' ').title())
plt.legend()
plt.xlim([0,max(history.epoch)])
plt.savefig("plot.png")
* 학습 결과 시각화 - Visualize 함수 사용
Visulaize([('CNN', history)], 'loss')
* 시각화 결과 (plot.png) 및 정확도 결과
- loss & accuracy의 수치 결과 CNN 모델이 잘 작동되었음이 확인 가능하다
- epoch가 진행되면서 loss 값이 감소 - 모델이 잘 작동되었음을 확인 가능
{4} 모델 구조 review
print(model.summary())
* conv2d: shape (None, 28, 28, 32)
- input shape으로 넣은 28x28 (가로픽셀x세로픽셀) + 32개의 filter 개수
* max_pooling2d: shape (None, 14, 14, 32)
- pooling의 결과 size가 각각 반으로 줄어듦
* conv + max_pooling을 총 두 번 더 반복 (two layers 추가)
- max_pooling2d_2까지의 결과 (None, 4, 4, 32)
* flatten: shape (None, 512)
- pooling까지 모두 마친 결과 가로 pixel 4 x 세로 pixel 4 x 필터 개수 32 = 512
* Dense
- 총 두 개의 Dense layer를 통해 각각 64개의 node & 10개의 node 생성
* 모델 내에는 총 52,298개의 파라미터
6. [3] 실습 - MNIST 분류 CNN 모델: 모델 평가 및 예측
[1] 모델 평가
* evaluate 함수 사용
* 학습된 모델을 바탕으로 입력한 feature data X & label Y의 lossr값과 metrics값을 출력한다 - 평가이기에 당연히 test data 입력
model.evaluate(X, Y)
[2] 모델 예측
* predict_classes 함수 사용
- 해당되는 class의 softmax 값들 중 제일 큰 확률을 갖는 값을 label로 선정하여 뽑아 list 형태로 정렬되어 출력됨
(X데이터의 예측 label 출력)
- 즉, softmax의 값들에서 제일 큰 값을 골라 label로 선정하는 모든 과정을 predict_classes 함수가 맡는다
model.predict_classes(X)
[3] 실습 코드
{1} 모델 평가 및 예측
* 복잡성을 줄이기 위해 위 모델의 epochs 값을 10으로 줄이고, batch_size를 128로 줄임
loss, test_acc = model.evaluate(test_images, test_labels, verbose = 0)
predictions = model.predict_classes(test_images)
- 예측 결과를 predictions에 저장
{2} 결과 출력 (+ layer별 인식 class 형태 출력)
* 예측 결과를 출력한다 (test loss & test accuracy 및 예측한 test data가 어디 class에 속하는 지 출력)
print('\nTest Loss : {:.4f} | Test Accuracy : {}'.format(loss, test_acc))
print('예측한 Test Data 클래스 : ',predictions[:10])
* 평가용 데이터에 대한 각 layer 결과를 시각화한다.
def Plotter(test_images, model):
img_tensor = test_images[0]
img_tensor = np.expand_dims(img_tensor, axis=0)
layer_outputs = [layer.output for layer in model.layers[:6]]
activation_model = models.Model(inputs=model.input, outputs=layer_outputs)
activations = activation_model.predict(img_tensor)
layer_names = []
for layer in model.layers[:6]:
layer_names.append(layer.name)
images_per_row = 16
for layer_name, layer_activation in zip(layer_names, activations):
n_features = layer_activation.shape[-1]
size = layer_activation.shape[1]
n_cols = n_features // images_per_row
display_grid = np.zeros((size * n_cols, images_per_row * size))
for col in range(n_cols):
for row in range(images_per_row):
channel_image = layer_activation[0, :, :, col * images_per_row + row]
channel_image -= channel_image.mean()
channel_image /= channel_image.std()
channel_image *= 64
channel_image += 128
channel_image = np.clip(channel_image, 0, 255.).astype('uint8')
display_grid[col * size : (col+1) * size, row * size : (row+1) * size] = channel_image
scale = 1. / size
print('레이어 이름: ', layer_name)
plt.figure(figsize=(scale * display_grid.shape[1], scale * display_grid.shape[0]))
plt.grid(False)
plt.imshow(display_grid, aspect='auto', cmap='viridis')
plt.savefig("plot.png")
elice_utils.send_image("plot.png")
plt.show()
Plotter(test_images, model)
* layer별 출력 그림 결과)
- 첫 layer conv2d 결과
- 마지막 layer max_pooling2d_2 결과
- 해석: 첫 층에서 마지막 층으로 가면서 단순한 데이터로 정리가 됨. 마지막 층에서는 찾고자 하는 feature가 있는 지의 여부 + 있다면 어느 위치에 있는 지 위치 정보들이 담겨 있음. 여러 layer를 거치기에 pixel size가 많이 줄어든 것을 확인할 수 있다.
- 출처 - 2021 NIPA/AI 기본/응용 교육과정
'Artificial Intelligence > Deep Learning' 카테고리의 다른 글
다양한 신경망 - 자연어 처리 (0) | 2021.12.11 |
---|---|
텐서플로우(TensorFlow) (+Keras) (0) | 2021.12.07 |
퍼셉트론 (perceptron) (0) | 2021.12.06 |
댓글