Home [Keras/딥러닝 공부] 신경망 기본 개념, 텐서, MNIST 데이터 분류하기
포스트
취소

[Keras/딥러닝 공부] 신경망 기본 개념, 텐서, MNIST 데이터 분류하기

아래 내용은 ‘케라스 창시자에게 배우는 딥러닝 (프랑소와 슐레 저, 박해선 옮김, 길벗 출판사)’ 을 공부한 뒤, 배운 내용을 제 언어로 정리.기록한 것 입니다.


신경망 기본 개념

신경망은 여러 겹의 ‘층(Layer)’ 으로 이루어져 있다.

각 층은 데이터 특징 추출 ‘필터’ 이다.

  • 각 층은 함수로 생각할 수도 있다.

신경망은 여러 겹 필터로 구성된 ‘여과기’ 이다.


텐서(tensor)

머신러닝 기본 입력 데이터는 ‘텐서’다.

텐서

정의: 넘파이 다차원 배열

텐서 종류

스칼라(0차원 텐서)

숫자 하나 하나가 스칼라다. 스칼라는 0차원 텐서다.

1
2
3
4
# 스칼라 예 

s = np.array(3)
s.ndim

0

벡터(1차원 텐서)

스칼라의 모임, 벡터가 1차원 텐서다.

1
2
3
4
5
6
7
# 벡터 (1차원 텐서) 예

v = np.array([1,2,3])

# or 

v = np.array([[1],[2],[3]])

위 두 방식 모두 3차원 열벡터 나타내고, 동시에 1차원 텐서다.

행렬(2차원 텐서)

열벡터를 행벡터로 쌓은, 벡터의 모임. 행렬이 2차원 텐서다.

1
2
3
4
5
6
# 행렬(2차원 텐서) 예 

np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]])

3차원 텐서 및 고차원 텐서

행렬을 3차원으로 여러 개 쌓으면 3차원 텐서가 된다.

그리고 3차원 텐서 여러 개를 다시 여러 개 묶으면 4차원 텐서가 된다.

그 이상 차원을 가진 텐서도 가능하다.

보통 이미지 1개가 RGB 3개 채널(행렬)로 이루어진 3차원 텐서다.

$\Rightarrow$ 여러 이미지 묶음은 4차원 텐서가 될 것이다.

$\Rightarrow$ 1개 비디오는 여러 이미지 묶음(여러 프레임의 연속) 이므로 4차원 텐서가 된다. 여러 비디오를 묶어놓은 데이터셋은 5차원 텐서가 된다.


배치(batch) 데이터

1개 배치(batch) 는 여러 개 개별 데이터로 구성된 데이터 묶음이다.

예를 들어 배치 크기(batch size) 가 128 이라면, 1개 배치(묶음)에 128개 개별 데이터가 들어가 있다는 소리다.


벡터 브로드캐스팅

벡터 - 스칼라 연산을 할 때 스칼라를 1벡터 이용해 ‘브로드캐스팅’ 할 수 있었다. 그리고 그 결과, 벡터 - 스칼라 연산을 수행할 수 있었다.

한편 행렬 - 벡터 사이 벡터 브로드캐스팅도 가능하다.

예컨대 행렬 A

1
2
3
4
5
A = np.array([
    [1,2,3],
    [4,5,6],
    [7,8,9]
])

와 벡터 B

1
2
3
4
5
B = np.array([
    [1],
    [2],
    [3]
])

이 있다고 하자.

$A+B$ 연산을 수행하고 싶다.

이때, 벡터 B를 아래와 같이 브로드캐스팅 할 수 있다.

1
2
3
4
5
6
# 벡터 B를 브로드캐스팅해 만든 행렬 
B = np.array([
    [1,2,3],
    [1,2,3],
    [1,2,3]
])

이후 행렬 $A$ 와 $B$ 를 요소별 연산 $A+B$ 하면 된다.

1
2
3
4
5
6
# 요소별 연산 결과 
result = np.array([
    [2,4,6],
    [5,7,9],
    [8,10,12]
])

케라스 Sequential() 모델 이용해 MNIST 데이터 분류하기

데이터셋 로드

1
2
# mnist 데이터셋 로드 
from keras.datasets import mnist 

데이터셋을 훈련용 데이터셋과 검정용 데이터셋으로 분리

1
2
3
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

print(train_images.shape)

(60000, 28, 28)

train_images 는 3차원 텐서 데이터다.

6만개 28*28 행렬(2차원 텐서)로 구성되어 있다.

1
2
3
# GPU 확인
import tensorflow as tf
print("Num GPUs Available: ", len(tf.config.experimental.list_physical_devices('GPU')))

Num GPUs Available: 1

1
print(test_images.shape);print(len(test_labels));print(test_labels)

(10000, 28, 28)

10000

[7 2 1 … 4 5 6]

  • 검정용 test_images 데이터는 1만 개 28*28 행렬로 구성된 3차원 텐서다.
  • test_labels 레이블 데이터(정답값)는 1만개 값으로 되어 있다. 각 값은 0~9 이다.

모델 로드

1
2
3
4
5
6
7
# 신경망 구조 
from keras import models 
from keras import layers

network = models.Sequential() # sequantial 모델 객체 
network.add(layers.Dense(512, activation='relu', input_shape=(28*28,))) # 이미지 특징 추출 층 
network.add(layers.Dense(10, activation='softmax')) # 1번 레이어에서 추출된 특징 소프트맥스 함수 이용해 확률 꼴로 바꾸는 층 

모델 로드하고 2개 층을 구성했다. 1층은 렐루함수 이용해 이미지 특징 추출하는 층. 2층은 소프트맥스 함수 이용해 1층에서 나온 결과값들을 0과 1 사이, 총합 1 되는 ‘확률 꼴’ 로 바꾸는 층이다. 벡터로 출력된다.

모델 컴파일(훈련준비)

모델 훈련준비 시키는 과정을 ‘모델 컴파일’ 이라고 한다.

1
2
3
4
# 모델 컴파일(훈련준비)
network.compile(optimizer='rmsprop',# 최적화 방법
loss='categorical_crossentropy', # 손실함수(성능함수)
metrics=['accuracy']) # 모델 정확도 측정 방법 
  • 최적화 방법으로 rmsprop 방법을 적용했다.
  • 손실함수(손실) 계산 방법으로 범주형 크로스엔트로피를 적용했다. 정답값(레이블)을 0과 1만으로 구성된 원핫 인코딩 벡터 꼴로 변환해 줄 것이다. 특정 이미지를 신경망에 통과시키고 나서 나오는 소프트맥스 함수 출력값들은 각 카테고리값에 대한 확률 꼴일 것이다. 소프트맥스 함수 출력값과 레이블의 원핫인코딩 벡터를 각각 확률분포라 생각할 수 있다. 이때 정답값 분포와 소프트맥스 함수 출력값 분포 사이 크로스엔트로피 값은 두 분포가 다른 정도를 나타내는 쿨백-라이블러 발산값과 같아진다. 여기서 쿨백-라이블러 발산값 또는 ‘두 분포가 다른 정도’가 예측 오차 또는 손실이 될 것이다.
  • 모델 정확도 측정 방법으로 accuracy rate 를 적용했다. 곧, 정확하게 분류한 비율을 뜻한다.

입력 데이터 전처리

이미지 데이터는 일반적으로 신경망에 넣을 때 0과 1사이 값으로 크기 변환해서 넣는다.

1
2
3
4
5
6
7
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32')/255 # train 데이터 자료형을 실수형으로 바꾸고, 255로 나눠서 크기 조정 (0과 1사이 값들로 크기 조정)

test_images = test_images.reshape((10000, 28*28))
test_images = test_images.astype('float32')/255 # test 데이터 자료형을 실수형으로 변경 하고 255로 나눠서 크기 조정 (0과 1사이 값)

# 보통 신경망에 이미지 넣을 때는 0과 1사이 값으로 바꿔서 넣는다. 
  • 3차원 텐서였던 train_images 데이터셋(훈련용 데이터셋)을 $(60000,784)$ 크기 행렬로 변환한다. 여기서 주목할 점은, 개별 이미지는 $(1,784)$ 크기 행벡터가 되어 행렬을 구성하게 되었다는 점이다.
  • 이후 행렬 각 원소를 부동소수점 실수 자료형인 float32 타입으로 바꾸고, 모든 값을 255로 나눠서 크기가 0과 1사이가 되게 조정했다.
  • 한편 역시 3차원 텐서였던 test_images 데이터셋(검정용 데이터셋)을 $(10000, 784)$ 크기 행렬로 변환한다. 역시 개별 이미지는 $(1,784)$ 크기 행벡터가 되었다.
  • 행렬 각 원소를 float32 타입으로 변환하고, 255로 나눠 크기를 0과 1사이로 조정한다.
1
2
3
4
5
6
# 레이블값 (정답 값) 모두 원핫인코딩 벡터 꼴로 변환 

import keras.utils 

train_labels = tf.keras.utils.to_categorical(train_labels) # 훈련용 데이터 정답값
test_labels = tf.keras.utils.to_categorical(test_labels) # 테스트 데이터 정답값 

한편, 훈련용 데이터셋과 검정용 데이터셋의 레이블 데이터를 모두 원핫인코딩 벡터 꼴로 바꾼다.

변환 방법은 위와 같다.

변환 후 0~9 사이 각 레이블값들은 모두 0과 1로만 구성된 원핫 인코딩 벡터로 바뀌었다.

이렇게 입력데이터 전처리를 마친다.

1
2
# 전처리된 입력 데이터 크기 확인 
train_images.shape

(60000, 784)

1
train_labels.shape

(60000, 10)

모델 훈련시키기

1
2
3
# 컴파일 된 모델 훈련데이터로 지도학습 하기 

network.fit(train_images, train_labels, epochs=5, batch_size=128) # batch_size: 한번에 몇 개씩 예측.평가해서 가중치 업데이트 할 건가. 

Screen Shot 2022-01-18 at 22 49 27

M1 맥의 GPU를 이용했다.

에포크는 5번, 배치 크기는 128을 설정했다.

  • 모델을 훈련(학습) 시키는 작업 = 최적의 가중치(파라미터)를 찾는 작업 으로 정의할 수 있다.
  • 모델 가중치는 학습 결과이자 모델에 저장된 정보다.

모델 성능 교차검증하기

1
2
# 모델에 테스트셋 넣어서 성증 교차검증 하기 
test_loss, test_acc = network.evaluate(test_images, test_labels)

Screen Shot 2022-01-18 at 22 50 50

0.9801 정도 정확도가 나왔다.

1
2
# 모형 성능평가 결과값 
print(f'test_loss:{test_loss}');print(f'test_acc:{test_acc}')

test_loss:0.06420930474996567

test_acc:0.9801000356674194

학습된 모형에 이미지 넣어보기

데이터셋 로드 - 훈련데이터 vs 검증데이터 나누기 - 모델 로드 - 모델 컴파일 - 데이터셋 전처리 - 모델 학습 - 성능검증

위 과정을 통해 학습된 모델을 얻었다.

이 모델에 임의의 MNIST 데이터 하나를 넣어서, 어떻게 분류해내는지 한번 보자.

1
2
# 임의의 MNIST 데이터 
first_image = train_images[3].reshape((1,28*28))

훈련용 셋에서 3번째 이미지 데이터를 빼냈다. 그리고 이걸 행벡터 형태로 변환했다.

그 후 모형의 predict() 메소드에 넣어서 3번째 이미지를 분류하도록 했다.

1
2
result = network.predict(first_image)[0]
result # 소프트맥스 함수 출력값

결과:

1
2
3
array([2.3608993e-09, 9.9996030e-01, 1.2336987e-05, 8.0996138e-08,
       1.6119817e-06, 1.2742596e-08, 2.0098611e-08, 1.7875993e-05,
       7.7193372e-06, 4.1783554e-09], dtype=float32)

결과는 소프트맥스 함수 출력값. 즉 확률 꼴 값이다. 각 값들은 0과 1사이 값이고, 총합은 1이다.

이 경우 result 값은 10차원 벡터로 나오고, 각 값들은 모형에 예측하도록 시킨 3번째 이미지가 각 카테고리값일 확률을 나타낸다.

위 출력값을 좀 더 이해하기 쉽게 시각화했다.

1
2
3
4
5
6
7
import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(5,2))
sns.barplot(list(range(0,10)), result)
plt.title('prediction result')
plt.show()

Screen Shot 2022-01-18 at 23 15 02

seaborn을 이용해 막대그래프로 각 카테고리값 별 확률을 나타냈다.

모델은 3번째 이미지를 ‘1’ 로 예측했음을 볼 수 있다.

그러면 1로 분류된 3번째 이미지가 실제로 어떤 형상이었는지, 직접 육안으로 확인하자.

1
2
3
4
plt.figure(figsize=(1,1))
plt.imshow(train_images[3].reshape(28,28))
plt.axis('off')
plt.show() # 모델이 1로 분류해냈다. 

Screen Shot 2022-01-18 at 23 16 24

사람인 내가 봐도 이 숫자는 1인것 같다.

모델이 제대로 잘 분류해냈음을 관찰할 수 있었다.

[2021 인공지능전문가 교육과정 복습] 스택 활용, 덱 활용 (괄호검사, 후위표기변경, 후위표기 계산, 그리고 회문분석)

[2021 인공지능전문가 교육과정 복습] 트리, 이진트리, 이진트리 순회