05) 컨볼루션 신경망과 컴퓨터 비전 - 파이썬으로 만드는 인공지능

05) 컨볼루션 신경망과 컴퓨터 비전 - 파이썬으로 만드는 인공지능

컨볼루션 신경망(CNN)은 딥러닝에서 가장 성공한 모델이다.

인공지능의 중요한 분야 중 하나인 컴퓨터 비전은 컴퓨터에 시각을 부여하는 주제를 연구한다.

컴퓨터 비전이 풀어야 하는 문제라면 딥러닝은 문제를 푸는 도구이다.

1 컨볼루션 신경망의 동기와 전개

생물 신경망에서의 과학적 발견은 인공 신경망 발전에 큰 영향을 미쳤다.

1998년 컨볼루션 신경망에 관련한 매우 의미 있는 논문 ▶ LeNet-5라는 컨볼루션 신경망 제시

이는 미국에서 발생하는 수표의 손 글씨를 자동으로 인식하는 시스템에 실제 활용되어 실용적 성능을 입 증한 최초의 컨볼루션 신경망 모델로 평가된다.

캡차(Capcha)는 웹에 접속한 상대가 실제 사람인지 로봇 프로그램인지 구별할 목적으로 사용하는 프로그램이다.

개와 고양이를 인식하는 컴퓨터 비전 시스템의 성능을 높이면 아시라 캡차는 무력화된다.

컨볼루션 신경망은 아시라 캡차를 완전히 무력화했다.

2012년엔, 자연 영상을 1000부류 중 하나로 분류하는 ILSVRC대회에서 AlexNet이라는 컨볼루션 신경망 모델이 5순위 오류율 15.3%을 달성해 우승을 차지하는 사건이 벌어졌다.

5순위 오류율 이란?

예측한 상위 5개 부류 안에 정답 부류가 있으면 맞혔다고 간주하는 성능 척도

컨볼루션 신경망은 순환 신경망 또는 강화학습과 결합해 이전에 낮은 성능에 머물던 응용을 실용적 수준으로 끌어 올리고 이전에는 상상도 못했던 인공지능 응용에 도전한다.

영상 주석달기

위 그림은 자연 영상이 입력되면 내용을 파악해 10개 정도의 단어로 이루어진 문장을 생성하는 시스템.

영상 내용을 분석하는 일은 컨볼루션 신경망이 맡고 분석 결과에 따라 자연어 문장을 생성하는 일은 순환 신경망이 맞는 컨볼루션 신경망 - 순환 신경망 협업 모델이다.

2 컨볼루션 신경망의 구조와 동작

컨볼루션 신경망을 구성하는 빌딩 블록과 빌딩 블록을 쌓아 신경망을 만드는 원리 설명

2.1 컨볼루션 연산으로 특징 맵 추출

컨볼루션은 신호에서 특징을 추출하거나 신호를 변환하는 데 사용하는 연산

(a) (2,2,2,2,2,9,9,9)라는 1*8크기의 1차원 신호에 (-1,0,1)이라는 1*3크기의 커널을 적용한 예

위치 5에 파랗게 칠한 영역이 수용장에 해당한다.

컨볼루션은 수용장과 커널의 선형결합이다.

선형결합이란, 해당하는 요소끼리 곱한 결과를 더하는 연산

따라서 위치 5의 결과는 2*(-1)+9*0+9*1=7 이다.

커널을 왼쪽부터 오른쪽으로 이동하면서 이런 계산을 수행하면 특징맵(feature map)을 얻는다.

왼쪽 끝과 오른쪽 끝은 커널이 바깥으로 나가므로 적용X

위의 1차원 컨볼루션을 식으로 쓰면 다음과 같다.

$z$는 입력 신호, $u$는 커널, $h$는 커널의 크기(보통 홀수 사용)

식 1

h=3이라면 시그마연산에서 $x=-1,0,1$이 되고 위치 1(i=1)에서의 결과는 $z(1-1)u(-1)+z(1-0)u(0)+z(1+1)u(1)$이 되는 것

2차원은 다음과 같다.

식 2

위 그림에서 (b)는 위치(5,2)에서 계산하는 예를 보여준다. 9개 화소를 해당하는 쌍기리 곱하고 결과를 더해 얻은 21을 해당 화소에 기록한다.

이렇게 모든 화소에 대해 수행하면 특징맵을 얻는다.

특징 맵은 컨볼루션 연산을 적용한 결과

위 그림(b)에서 2차원 커널은 오른쪽 열은 모두 1, 왼쪽 열은 모두 -1이다.

수용장의 오른쪽에서 왼쪽을 빼는 계산을 하는 셈 이다.

따라서, 오른쪽이 밝고 왼쪽이 어두운 수용장은 양수가 되고, 반대라면 음수가 된다.

명암 변화가 없는 수용장은 0이 된다.

따라서 $\begin{pmatrix} -1 & 0 & 1 \\ -1 & 0 & 1 \\ -1 & 0 & 1 \end{pmatrix}$의 커널은 수직 에지라는 특징을 추출한다. 커널이 $\begin{pmatrix} 1 & 1 & 1 \\ 0 & 0 & 0 \\ -1 & -1 & -1 \end{pmatrix}$라면 수평 에지를 추출할 것이다.

커널을 적용한 결과는 입력 영상과 크기가 같고 특징을 담고 있기 때문에 특징 맵이라 부른다.

이런 장점이 있는 컨볼루션을 신경망에 도입하면 입력 데이터의 형태를 고스란히 유지한 채 특징을 추출할 수 있게 된다.

깊은 다층 퍼셉트론에서는 2차원 모양의 영상을 1차원으로 펼쳐야 햇다.

이렇게 되면 이웃 정보가 사라져 정보 손실이 크지만, 컨볼루션 신경망에선 이런 정보 손실이 X

2.2 컨볼루션 층과 풀링 층

컨볼루션 신경망은 컨볼루션으로 특징 맵을 추출 하는 컨볼루션층과

요약 통계량을 구하는 풀링층으로 구성된다.

컨볼루션층

컨볼루션층을 구현하기 위해선 몇가지를 추가로 고려한다.

1) 가장 자리가 없어지는 문제 해결

딥러닝에선 컨볼루션층을 많이 쌓기 때문에 데이터의 크기가 점점 작아져 문제가 더욱 심각하다.

커널의 크기가 클수록 빨리 작아진다.따라서 0을 덧대기(padding)하여 크기를 유지한다.

0 대신 이웃 화솟값을 복사해 덧대기를 한다.

2) 보폭이라는 아이디어

입력 영상이 아주 큰 경우 영상 크기를 일부러 작게 줄일 필요가 있다.

(b)처럼 보폭을 2로 설정하면 커널을 두 화소에 한 번씩 적용 +특징 맵은 입력 영상에 비해 반으로 줄어든다.

보폭 $k$ ▶ 특징 맵 크기 $1/k$

3) 바이어스

(c)처럼 커널에는 바이어스가 있다.

위처럼 또 다른 확장이 필요하다.

커널은 한 종류의 특징만 추출하므로 매우 빈약하다. 영상에서는 변화무쌍한 변화가 나타나므로 풍부한 특징추출이 컨볼루션 신경망의 성공 여부를 가른다.

컨볼루션층은 커널을 64개 또는 128 개와 같이 아주 많이 사용하여 풍부한 특징 맵 생성.

(a)에선 특징맵이 $k'$개 생성된다. (커널의 개수 $k'$개)

대부분의 자연영상은 3차원 텐서이다.

3차원 텐서에 컨볼루션을 적용할 땐 주의할 점이 있다.

커널의 크기가 $h*h*k$인데 커널의 깊이 $k$가 입력 영상의 깊이와 같다

▶ 따라서 커널은 깊이 방향으로는 이동하지 않고 가로,세로 방향으로만 이동한다.

(b)는 (a)를 추상화해 블록 다이어그램으로 그린 것이다.

컨볼루션 층이 수행하는 연산을 정의하면 다음과 같다.

식 3

컨볼루션층은 식 2로 구한 특징 맵 $f$에 활성 함수 $\tau$를 적용하는데, 활성 함수는 특징 맵의 화소 각각에 적용한다.

컨볼루션층에서는 활성함수로 주로 ReLU를 사용한다.

풀링층

풀링은 세로·가로 방향의 공간을 줄이는 연산이다.

대상 영역을 하나로 집약하여 공간 크기를 줄인다.

위 그림은 2차원 텐서에 2*2 커널로 최대 풀링을 적용한 사례를 보여준다.

최대 풀링이란 커널 안에 있는 화소 중에서 최댓값을 취하는 연산

평균 풀링은 대상 영역의 평균을 계산한다.

풀링은 특징 맵에 나타난 상세함을 줄여 요약 통계량을 추출 한다.

따라서 보통 보폭을 1보다 크게 하는데, 보폭을 $s$로 설정하면 특징 맵의 크기는 $s$배 만큼 줄어든다.

풀링층은 지나치게 상세한 특징 맵에서 핵심을 추출해 성능을 높이는 뿐만 아니라 특징 맵의 크기를 줄여 메모리 효율과 계산 속도를 끌어올리는 효과까지 제공한다.

부분 연결성과 가중치 공유

컨볼루션층에는 부분 연결성과 가중치 공유라는 좋은 특성이 있다

(b)는 (a)의 컨볼루션 연산을 신경망 형태로 다시 그린 것이다.

오른쪽 층에 있는 노드를 보면 왼쪽 층 노드 3개하고만 에지로 연결되어 있다.

에지의 개수 = 커널의 크기 $h$

깊은 다층 퍼셉트론에서의 완전연결 구조와 확연히 다르다.

완전 연결 구조의 경우엔 $n_1*n_2$개의 에지

컨볼루션 신경망에는 $h*n_2$개의 에지

이런 특성을 부분 연결성이라 한다.

컨볼루션 신경망에는 커널을 구성하는 화소 하나하나가 가중치에 해당하는데,

깊은 다층 퍼셉트론에서는 인접한 두 층의 노드 쌍을 연결하는 에지에 가중치가 잇다.

가중치 공유라는 컨볼루션 신경망의 또다른 장점.

▶ 입력 영상의 모든 곳에 같은 커널을 적용하므로 모든 화소가 하나의 커널을 공유하는 셈

가중치 공유는 학습이 최적화해야 하는 매개변수의 개수를 획기적으로 줄여준다.

2.3 빌딩 블록을 쌓아 만드는 컨볼루션 신경망

컨볼루션 신경망은 빌딩 블록을 쌓아가는 방식으로 만든다.

빌딩 블록

깊은 다층 퍼셉트론은 은닉층을 쌓아서 만든다. 따라서 은닉층 하나가 빌딩 블록.

위 그림은 컨볼루션 신경망이 사용하는 빌딩블록.

입력 텐서가 들어오면 다중 커널을 적용해 다중 특징 맵을 추출.

이때 보폭에 따라 $m*n$ 크기가 $m'*n'$크기로 바뀌며, 커널 개수에 따라 깊이는 $k'$가 된다.

활성함수는 다중 특징맵의 화소별로 적용되므로, 활성 함수를 적용한 다중 특징 맵은 $k'*m'*n'$ 유지

풀링층의 출력은 그보다 작은 $k'*m''*n''$ 크기의 텐서 ▶ 다음 빌딩 블록의 입력 텐서

(b)는 빌딩 블록을 쌓아 컨볼루션 신경망을 만드는 방식을 설명한다.

LeNet-5 사례

현재 기준으로 보면 아주 간단한 구조의 컨볼루션 신경망이다.

필기 숫자 영상은 컬러가 아닌 명암 영상이므로 입력 데이터의 깊이는 1이다. (컬러가 1개이므로)

C는 컨볼루션층, P는 풀링층, FC는 완전연결층이다.

첫 번째 층은 컨볼루션층, 5*5커널을 6개 사용하고 보폭은 1, 덧대기는 하지 않았다.

따라서 1*32*32 텐서에서 28*28크기의 특징맵을 6개 추출해, 6*28*28의 텐서 출력.

다음은 풀링층, 2*2커널을 보폭 2로 적용해 6*14*14의 텐서가 출력된다.

여기까지가 빌딩블록 1

다음은 컨볼루션층, 입력은 풀링층에서 받은 6*14*14의 텐서이고, 출력은 16*10*10텐서이다.

이어지는 풀링층은 2*2커널을 보폭 2로 적용해 16*5*5 텐서를 출력한다.

여기까지가 빌딩블록 2

이후에 컨볼루션 연산을 한 번 더 적용하고, 완전연결(FC)을 두 번 적용.

전체적으로는 C-P-C-P-C-FC-FC층으로 구성.

그렇다면 학습 알고리즘이 최적화해야 하는 매개변수의 개수는?

3개의 컨볼루션층 ▶ 오른쪽으로 가면서 5*5커널 6개, 5*5커널 16개, 5*5커널 120개를 쓴다.

따라서 (5*5+1)*6+(5*5+1)*16+(5*5+1)*120=3962개

완전연결층의 매개변수는 (120+1)*84+(84+1)*10=11014개

3 컨볼루션 신경망의 학습

3.1 손실 함수와 옵티마이저

컨볼루션 신경망의 내부 구조는 깊은 다층 퍼셉트론과 다르지만 출력층은 같다.

따라서 컨볼루션 신경망은 깊은 다층 퍼셉트론과 같은 손실함수 를 사용한다.

옵티마이저는 손실 함수의 최저점을 찾아간다. 이때 매개변수가 어느 방향으로 변해야 손실함수가 최저점에 가까워지는지 알아야 하는데, 손실 함수를 매개변수로 미분하여 방향을 알아낸다.

컨볼루션 신경망은 커널이 매개변수에 해당하고 깊은 다층 퍼셉트론은 에지의 가중치가 매개변수라는 점만 다르고 미분으로 방향을 알아내는 원리는 같다.

따라서 두 신경망은 같은 옵티마이저를 사용 한다.

3.2 통째 학습

고전적인 컴퓨터 비전에서는 수평에지와 수직에지 특징을 추출하는 커널을 사람이 직접 설계했다.

딥러닝은 특징을 학습으로 알아내기 때문에 특징 학습을 한다고 말한다.

또한, 입력패턴에서 최종 출력에 이르는 전체 과정을 한꺼번에 학습한다는 의미에서 통째학습(end-to-end learning)을 한다고 말한다.

컨볼루션 신경망은 딥러닝의 일종이므로 딥러닝 패러다임을 따른다.

LeNet-5에 통째학습 패러다임

앞부분의 컨볼루션층과 풀링층은 특징 추출을, 뒷부분의 완전연결층은 분류를 담당한다.

컨볼루션의 학습 알고리즘은 오류 역전파 알고리즘 을 사용한다.

단지 최적화해야 할 매개변수가 완전연결층의 가중치 뿐만 아니라 컨볼루션층의 커널도 포함된다.

커널과 가중치를 난수로 초기화한다음, 오류를 줄이는 방향으로 커널과 가중치를 개선.

3.3 컨볼루션 신경망의 성능이 월등한 이유

통째학습을 한다.

특징추출과 분류를 동시에 최적화하는 학습 방법은 따로 최적화한 후에 결합하는 학습 방법보다 뛰어나다.

특징 학습을 한다.

데이터셋에 최적인 특징 추출 알고리즘을 학습으로 알아내기 때문에 상당한 성능 향상.

특징 맵은 컨볼루션층을 거치면서 고급 특징으로 발전.

신경망의 깊이를 더욱 깊게 하여 풍부한 특징을 추출한다.

컨볼루션층은 부분 연결성과 가중치 공유로 인해 매개변수 개수가 적다.

따라서 신경망의 깊이를 충분히 깊게 해도 학습이 잘 된다.

컨볼루션 연산은 데이터의 원래 구조를 그대로 유지한 채 특징을 추출한다.

영상의 한 화소는 상하좌우의 이웃 화소와 상관관계가 큰데, 컨볼루션 연산은 이 관계 정보를 그대로 유지하는 강점이 있다.

4 컨볼루션 신경망 프로그래밍

숫자영상뿐 아니라 숫자보다 훨씬 어려운 자연 영상의 인식 시도

4.1 필기 숫자 인식

초창기 모델인 LeNet-5를 텐서플로로 재현하여 데이터를 인식한다.

컨볼루션 신경망의 유연성을 확인하기 위해 표전적인 빌딩 블록을 벗어난 신경망도 실험한다.

LeNet-5 재현

MNIST를 인식하는 컨볼루션 신경망이다.

import numpy as np import tensorflow as tf from tensorflow.keras.datasets import mnist from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense from tensorflow.keras.optimizers import Adam #데이터를 신경망에 입력할 형태로 변환 (x_train, y_train), (x_test,y_test) = mnist.load_data() x_train = x_train.reshape(60000,28,28,1) #달라짐 x_test = x_test.reshape(10000,28,28,1) #달라짐 x_train = x_train.astype(np.float32)/255.0 x_test = x_test.astype(np.float32)/255.0 y_train = tf.keras.utils.to_categorical(y_train,10) y_test = tf.keras.utils.to_categorical(y_test,10) #LeNet-5 신경망 모델 설계 cnn = Sequential() cnn.add(Conv2D(6,(5,5),padding='same', activation='relu', input_shape=(28,28,1))) cnn.add(MaxPooling2D(pool_size=(2,2))) cnn.add(Conv2D(16,(5,5),padding='same', activation='relu')) cnn.add(MaxPooling2D(pool_size=(2,2))) cnn.add(Conv2D(120,(5,5), padding='same', activation='relu')) cnn.add(Flatten()) cnn.add(Dense(84, activation='relu')) cnn.add(Dense(10,activation='softmax')) #신경망 모델 학습 cnn.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy']) hist = cnn.fit(x_train,y_train, batch_size = 128, epochs = 30, validation_data=(x_test, y_test), verbose=1) #신경망 모델 정확률 평가 res=cnn.evaluate(x_test,y_test, verbose=0) print("정확률은",res[1]*100) import matplotlib.pyplot as plt #정확률 그래프 plt.plot(hist.history['accuracy']) plt.plot(hist.history['val_accuracy']) plt.title('Model accuracy') plt.ylabel('accuracy') plt.xlabel('Epoch') plt.legend(['Train', 'Validation'], loc='best') plt.grid() plt.show() #손실 함수 그래프 plt.plot(hist.history['loss']) plt.plot(hist.history['val_loss']) plt.title('Model loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Train', 'Validation'], loc='best') plt.grid() plt.show()

신경망을 C-P-C-P-C-FC-FC 순서로 층을 쌓아 LeNet-5와 가급적 비슷한 구조로 설계한다.

데이터셋을 준비할 때 2차원맵을 1차원으로 변환해야 하므로 reshape(60000,784)를 사용했는데

여기서는 2차원 맵을 그대로 입력해야 하므로 reshape(60000,28,28,1)을 사용한다.

정확률은 99.17%이며, 학습 곡선을 보면 수렴도 훨씬 빠르다.

padding인자는 'SAME'이나 'VALID'로 설정할 수 있으며, 이 둘의 차이는 다음과 같다.

'VALID': 합성곱층에 zero-padding을 사용하지 않는다. 그렇기 때문에 합성곱 연산을 통해 이미지의 데이터 손실이 일어난다.

'SAME' : 합성곱층에 zero-padding을 사용하며, 이 경우에는 출력 특성맵의 크기는 입력을 스트라이드로 나눈 다음 올림 한 것과 같다(ceil(13/5)=3).

출처: https://excelsior-cjh.tistory.com/180 [EXCELSIOR]

컨볼루션 신경망의 유연한 구조

keras.io사이트에서 제공하는 예제 프로그램의 신경망 구조와 하이퍼 매개변수 설정을 따랐다.

이 프로그램은 컨볼루션층을 연달아 2개 쌓는다는 측면에서 표준적인 빌딩 블록을 벗어난다.

전체 신경망 구조는 C-C-P-dropout-FC-dropout-FC인데, 드롭아웃은 별개의 층으로 볼 순 없다.

더보기 dropout은 활성함수처럼 층의 출력엔 연산을 적용하는 역할에 불과하므로

FC층을 제외하면 LeNet-5가 C-P-C-P-C의 5개층인데,

이 프로그램 신경망에선 C-C-P로 3개의 층만 있다.

import numpy as np import tensorflow as tf from tensorflow.keras.datasets import mnist from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout from tensorflow.keras.optimizers import Adam #데이터를 신경망에 입력할 형태로 변환 (x_train, y_train), (x_test,y_test) = mnist.load_data() x_train = x_train.reshape(60000,28,28,1) #달라짐 x_test = x_test.reshape(10000,28,28,1) #달라짐 x_train = x_train.astype(np.float32)/255.0 x_test = x_test.astype(np.float32)/255.0 y_train = tf.keras.utils.to_categorical(y_train,10) y_test = tf.keras.utils.to_categorical(y_test,10) #신경망 모델 설계 cnn = Sequential() cnn.add(Conv2D(32,(3,3), activation='relu', input_shape=(28,28,1))) cnn.add(Conv2D(64,(3,3), activation='relu')) cnn.add(MaxPooling2D(pool_size=(2,2))) cnn.add(Dropout(0.25)) cnn.add(Flatten()) cnn.add(Dense(128, activation='relu')) cnn.add(Dropout(0.25)) cnn.add(Dense(10,activation='softmax')) #신경망 모델 학습 cnn.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy']) hist = cnn.fit(x_train,y_train, batch_size = 128, epochs = 12, validation_data=(x_test, y_test), verbose=1) #신경망 모델 정확률 평가 res=cnn.evaluate(x_test,y_test, verbose=0) print("정확률은",res[1]*100) import matplotlib.pyplot as plt #정확률 그래프 plt.plot(hist.history['accuracy']) plt.plot(hist.history['val_accuracy']) plt.title('Model accuracy') plt.ylabel('accuracy') plt.xlabel('Epoch') plt.legend(['Train', 'Validation'], loc='best') plt.grid() plt.show() #손실 함수 그래프 plt.plot(hist.history['loss']) plt.plot(hist.history['val_loss']) plt.title('Model loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Train', 'Validation'], loc='best') plt.grid() plt.show()

12세대만 반복했는데 불과 99.23의 정확률을 얻었다.

4.2 텐서플로 프로그래밍

손실함수를 지원하는 loss클래스를, 옵티마이저를 지원하는 optimizer클래스를 공부했었는데

loss로는 평균제곱오차, 교차엔트로피를, optimizer로는 SGD, Adam, Adagrad, RMSprop를 소개했다.

필요할 때마다 keras.io사이트에 접속해 도움을 받자!!

models클래스 : Sequential과 functional API

models클래스는 신경망 모델을 생성하는데 쓰는 Sequential과 functional API라는 두 가지 함수 제공.

이 함수를 호출해 모델 객체를 생성한 후 add함수를 사용해 층을 쌓는다.

순차적인 구조에서는 Sequential사용.

텐서가 흐르다가 중간에서 여러 개로 갈라져 출력층이 여러개인 경우도 있다. (시계열 데이터에서 종종 발생)

이런 경우엔 functional API를 사용한다.

layers클래스 : Dense, Conv2D, MaxPooling2D, Flatten 함수

Conv2D는 2차원 커널을 사용하는 컨볼루션층을 추가

Conv2D(32, (3,3), activation='relu', input_shape=(28,28,1))

첫번째 층, 즉 입력층을 지정하므로 이벽에 대한 정보를 알려준다 ▷ input_shape=(28,28,1)

(28,28)이 아닌 (28,28,1)인 이유는 영상이 몇개의 채널로 구성되므로 일반성을 유지하기 위함

컬러영상이라면 RGB세 채널로 구성되므로 (28,28,3)일 것

맨 앞의 두 매개변수 32, (3,3)은 3*3크기의 커널을 32개 쓰라는 뜻

activation='relu'는 컨볼루션 결과에 ReLU활성함수를 적용하라는 뜻

Conv2D함수의 API

두 번째와 세 번째 층은 각각 컨볼루션층, 풀링층이었다. 두번째층에서 input-shape을 지정하지 않았다.

(텐서의 크기가 지정되어있으므로 생략 가능)

그 다음 Dropout을 적용하였다.

여기까지가 C-C-P로 특징 추출을 담당하는 앞부분.

이제부터는 분류를 수행하는 층을 쌓는데,

cnn.add(Flatten())

위 코드는 특징추출부분의 출력을 1차원 구조로 변환해 완전연결층에 연결할 수 있게 해준다.

완전연결층을 쌓고, 드롭아웃을 적용한다. 그 다음으로는 출력층에 해당하는 완전연결층을 쌓는다.

요약하면 C-C-P-FC-FC의 5개 층 구조를 갖는 신경망이 만들어졌는데, 컨볼루션층과 풀링층이 추출한 특징을 다층 퍼셉트론으로 분류한다고 볼 수 있다.

4.3 패션 인식

컨볼루션 신경망으로 fashion MNIST를 인식하는 프로그램이다.

달라지는 것은 데이터를 읽어들이는 부분

#나머지는 위 프로그램과 똑같음 ... from tensorflow.keras.datasets import fashion_mnist ... (x_train, y_train),(x_test,y_test) = fashion_mnist.load_data() ...

12세대 후 92.74%의 정확률을 얻었으며, 세대수가 적어 충분히 수렴하지 못했지만 세대 수를 늘리면 성능 향상을 얻을 수 있을 것.

4.4 자연 영상 인식

이제 자연 영상에 대한 컨볼루션 신경망을 적용한다.

딥러닝이 주로 사용하는 자연 영상 데이터셋에는 가장 방대한 편인 ImageNet과 MSCoCo가 있는데 메모리 용량을 많이 차지하고 학습 시간이 오래 걸리기 때문에 여기서는 CIFAR-10 데이터셋을 활용한다.

영상 샘플

CIFAR-10은 {airplane,automobile,bird,cat,deer,dog,frog,horse,ship,truck}의 10분류로 구성

한 장의 영상은 32*32 컬러맵으로 표현되는 아주 작은 영상

일단 MNIST, fashion MNIST보다 변화가 훨씬 심함

MNIST, fashion MNIST를 인식하는 프로그램과 비슷하여 데이터부분과 모델 설계 부분이 조금 다르다.

import numpy as np import tensorflow as tf from tensorflow.keras.datasets import cifar10 from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout from tensorflow.keras.optimizers import Adam #데이터를 신경망에 입력할 형태로 변환 (x_train, y_train),(x_test,y_test) = cifar10.load_data() x_train = x_train.astype(np.float32)/255.0 x_test = x_test.astype(np.float32)/255.0 y_train = tf.keras.utils.to_categorical(y_train,10) y_test = tf.keras.utils.to_categorical(y_test,10) #신경망 모델 설계 cnn = Sequential() cnn.add(Conv2D(32,(3,3), activation='relu', input_shape=(32,32,3))) cnn.add(Conv2D(32,(3,3), activation='relu')) cnn.add(MaxPooling2D(pool_size=(2,2))) cnn.add(Dropout(0.25)) cnn.add(Conv2D(64,(3,3), activation='relu')) cnn.add(Conv2D(64,(3,3), activation='relu')) cnn.add(MaxPooling2D(pool_size=(2,2))) cnn.add(Dropout(0.25)) cnn.add(Flatten()) cnn.add(Dense(512, activation='relu')) cnn.add(Dropout(0.5)) cnn.add(Dense(10,activation='softmax')) #신경망 모델 학습 cnn.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy']) hist = cnn.fit(x_train,y_train, batch_size = 128, epochs = 30, validation_data=(x_test, y_test), verbose=1) #신경망 모델 정확률 평가 res=cnn.evaluate(x_test,y_test, verbose=0) print("정확률은",res[1]*100) import matplotlib.pyplot as plt #정확률 그래프 plt.plot(hist.history['accuracy']) plt.plot(hist.history['val_accuracy']) plt.title('Model accuracy') plt.ylabel('accuracy') plt.xlabel('Epoch') plt.legend(['Train', 'Validation'], loc='best') plt.grid() plt.show() #손실 함수 그래프 plt.plot(hist.history['loss']) plt.plot(hist.history['val_loss']) plt.title('Model loss') plt.ylabel('Loss') plt.xlabel('Epoch') plt.legend(['Train', 'Validation'], loc='best') plt.grid() plt.show()

MNIST에서는 샘플 하나가 (28,28)로 표현되므로 (28.28,1)로 확장하기 위해 reshape함수를 적용했지만

CIFAR-10에선 데이터가 (32,32,3)으로 표현되므로 reshape를 적용할 필요가 없다.

첫번째층을 쌓는 행에서 input_shape=(32,32,3)으로 설정해, 영상이 제대로 입력되게 한다.

C-C-P층을 한 번 더 쌓는다.

epochs=30으로 설정했는데 실행 결과를 보면 충분히 수렴하지 않았다.

4.5 학습된 모델 저장과 재활용

학습엔 시간이 많이 걸리므로 모델을 파일에 저장해두고 필요할 때 읽어다가 쓰면 유용하다.

save함수로 파일에 저장

모델을 파일에 저장하는 코드

cnn.save("my_cnn.h5")

save함수는 신경망의 구조 정보와 가중치 정보를 저장한다.

대용량 데이터를 저장하는데 널리 쓰이는 HDF5파일 형식을 사용하기 때문에 확장자를 h5로 지정한다ㅏ.

모델을 다시 불러다 쓰는 코드

import numpy as np import tensorflow as tf from tensorflow.keras.datasets import cifar10 #신경망 구조와 가중치를 저장하고 있는 파일을 읽어옴 cnn=tf.keras.models.load_model('my_cnn.h5') cnn.summary() #데이터를 신경망에 입력할 형태로 변환 (x_train, y_train),(x_test,y_test) = cifar10.load_data() x_train = x_train.astype(np.float32)/255.0 x_test = x_test.astype(np.float32)/255.0 y_train = tf.keras.utils.to_categorical(y_train,10) y_test = tf.keras.utils.to_categorical(y_test,10) res = cnn.evaluate(x_test,y_test, verbose=0) print("정확률은", res[1]*100)

summary 함수는 모델의 구조를 확인한다.

실행 결과를 보면 모델이 제대로 복원되었음을 확인할 수 있다.

5 컨볼루션 신경망의 시각화

시각화는 딥러닝의 동작을 잘 이해하는데 효과적이다.

앞에서는 수렴 특성을 관찰할 수 있도록 학습 곡선을 시각화했음.

두 가지 시각화를 추가로 시도한다.

1. 커널 시각화

2. 특징맵 시각화

5.1 커널의 시각화 프로그래밍

#Sequential과 compile로 모델을 구축하고 fit로 학습하는 부분 cnn.summary() #cnn모델의 정보 출력 # 1) 컨볼루션층의 커널을 시각화 for layer in cnn.layers: if 'conv' in layer.name: kernel, biases = layer.get_weights() print(layer.name, kernel.shape) #커널의 텐서 모양 출력 kernel,biases = cnn.layers[0].get_weights() #층 0의 커널 정보 저장 minv,maxv = kernel.min(), kernel.max() kernel= (kernel-minv)/(maxv-minv) n_kernel=32 import matplotlib.pyplot as plt plt.figure(figsize=(20,3)) plt.suptitle('Kernels of conv2d_4') for i in range(n_kernel): #i번째 커널 f=kernel[:,:,:,i] for j in range(3): #j번째 채널 plt.subplot(3,n_kernel, j*n_kernel+i+1) plt.imshow(f[:,:,j],cmap='gray') plt.xticks([]); plt.yticks([]) plt.title(str(i)+'_'+str(j)) plt.show() # 2)컨볼루션층의 특징 맵을 시각화 for layer in cnn.layers: if 'conv' in layer.name: print(layer.name, layer.output.shape) #특징 맵의 텐서 모양을 출력 from tensorflow.keras.models import Model partial_model = Model(inputs=cnn.inputs, outputs= cnn.layers[0].output) #층 0만 떼어냄 partial_model.summary() feature_map = partial_model.predict(x_test) #부분 모델로 테스트 집합을 예측 fm = feature_map[1] #1번 영상의 특징 맵을 시각화 plt.imshow(x_test[1]) #1번 영상을 출력 plt.figure(figsize=(20,3)) plt.suptitle("Feature maps of conv2d_4") for i in range(32): #i번째 특징 맵 plt.subplot(2,16,i+1) plt.imshow(fm[:,:,i], cmap='gray') plt.xticks([]); plt.yticks([]) plt.title('map'+str(i)) plt.show()

위 프로그램에서 1로 표기한 코드는 CIFAR-10으로 학습한 컨볼루션 신경망의 커널을 시각화한다.

2로 표시한 코드는 특징 맵을 시각화한다.

summary함수는 모델의 내부구조를 알려준다.

맨 위에 있는 conv2D층은 Conv2D함수로 만든 층이고, 출력텐서는 (None,30,30,32)이며 학습해야 하는 매개변수는 896개라는 사실을 알려준다.

왜 None?

한꺼번에 입력되는 영상의 개수에 해당하는데, 실제 실행하기 전에 몇 장이 들어올지 알 수 없어 None으로 표시

for layer in cnn.layers: if 'conv' in layer.name: kernel, biases = layer.get_weights() print(layer.name, kernel.shape)

위 부분에선 이름에 conv가 포함된 층, 즉 컨볼루션층에 대해서 커널의 텐서 모양을 출력한다.

kernel,biases = cnn.layers[0].get_weights() #층 0의 커널 정보 저장 minv,maxv = kernel.min(), kernel.max() kernel= (kernel-minv)/(maxv-minv) n_kernel=32 import matplotlib.pyplot as plt plt.figure(figsize=(20,3)) plt.suptitle('Kernels of conv2d_4') for i in range(n_kernel): #i번째 커널 f=kernel[:,:,:,i] for j in range(3): #j번째 채널 plt.subplot(3,n_kernel, j*n_kernel+i+1) plt.imshow(f[:,:,j],cmap='gray') plt.xticks([]); plt.yticks([]) plt.title(str(i)+'_'+str(j)) plt.show()

이 부분은 맨 앞의 컨볼루션층, 즉 (3,3,3,32)텐서 구조를 가지는 conv2d 층의 커널을 시각화한다.

더 자세히 보면,

kernel,biases = cnn.layers[0].get_weights()

맨 앞에 있는 conv2d층에서 가중치를 읽어온다.

minv,maxv = kernel.min(), kernel.max() kernel= (kernel-minv)/(maxv-minv)

kernels 객체의 값을 [0,1]사이로 정규화해 imshow로 디스플레이 할 수 있게 해준다.

n_kernel=32

conv2d층의 커널 개수를 지정한다.

plt.figure(figsize=(20,3))

그림의 크기를 지정한다

plt.suptitle('Kernels of conv2d_4')

그림의 제목을 지정한다

f=kernel[:,:,:,i]

i번째 커널을 f에 저장한다.

plt.imshow(f[:,:,j],cmap='gray')

커널의 j번째 채널을 그린다.

5.2 특징 맵의 시각화 프로그래밍

for layer in cnn.layers: if 'conv' in layer.name: print(layer.name, layer.output.shape) #특징 맵의 텐서 모양을 출력 from tensorflow.keras.models import Model partial_model = Model(inputs=cnn.inputs, outputs= cnn.layers[0].output) #층 0만 떼어냄 partial_model.summary() feature_map = partial_model.predict(x_test) #부분 모델로 테스트 집합을 예측 fm = feature_map[1] #1번 영상의 특징 맵을 시각화 plt.imshow(x_test[1]) #1번 영상을 출력 plt.figure(figsize=(20,3)) plt.suptitle("Feature maps of conv2d_4") for i in range(32): #i번째 특징 맵 plt.subplot(2,16,i+1) plt.imshow(fm[:,:,i], cmap='gray') plt.xticks([]); plt.yticks([]) plt.title('map'+str(i)) plt.show()

위 부분은 특징 맵을 시각화하는 프로그램이다.

for layer in cnn.layers: if 'conv' in layer.name: print(layer.name, layer.output.shape)

이 부분은 conv가 포함된 층, 즉 컨볼루션층에 대해서 특징 맵의 텐서 모양을 출력한다.

partial_model = Model(inputs=cnn.inputs, outputs= cnn.layers[0].output)

학습된 cnn모델객체에서 층 0만 떼내어 partial_model 객체에 저장한다.

cnn객체를 그대로 사용하면 전방 계산이 이루어져 입력 영상이 신경망을 흘러 출력까지 단숨에 가므로, 최종 출력만 관찰할 수 있다.

따라서 partial_model을 만들어 층 0의 특징맵을 가로챈다.

feature_map = partial_model.predict(x_test) #부분 모델로 테스트 집합을 예측 fm = feature_map[1] #1번 영상의 특징 맵을 시각화 plt.imshow(x_test[1]) #1번 영상을 출력 plt.figure(figsize=(20,3)) plt.suptitle("Feature maps of conv2d_4") for i in range(32): #i번째 특징 맵 plt.subplot(2,16,i+1) plt.imshow(fm[:,:,i], cmap='gray') plt.xticks([]); plt.yticks([]) plt.title('map'+str(i)) plt.show()

테스트 집합의 1번 영상, 즉 x_test[1]에 대한 특징 맵을 시각화한다.

더 자세히보면,

feature_map = partial_model.predict(x_test)

predict함수로 테스트 집합에 있는 10,000장의 영상을 예측한다.

이때 partial_model을 사용하므로 층0까지만 계산하게 된다.

fm = feature_map[1]

10,000장에서 1번 영상에 대한 것만 fm에 저장한다.

plt.imshow(x_test[1])

원본 영상을 출력한다.

ship부류에 속하는 영상이다.

for i in range(32): #i번째 특징 맵 plt.subplot(2,16,i+1) plt.imshow(fm[:,:,i], cmap='gray') plt.xticks([]); plt.yticks([]) plt.title('map'+str(i))

0번부터 31번까지 특징 맵을 2행 16열로 배치한다.

이때,

plt.imshow(fm[:,:,i], cmap='gray')

이 부분은, i번째 특징맵을 imshow함수로 그리는 부분이다.

이 특징을 학습 알고리즘이 최적화한 커널로 추출했음을 기억하자!

6 딥러닝의 규제

from http://haystar.tistory.com/34 by ccl(A) rewrite - 2021-11-01 23:26:59