딥러닝

Ch2

나몬 2024. 3. 13. 10:39

1. 신경망과의 첫 만남

- class(클래스)

머신 러닝에서 분류 문제의 범주(category)

 

- label(레이블)

특정 샘플(데이터 포인트)의 클래스

 

- layer(층)

신경망의 핵심 구성 요소

주어진 문제에 더 의미있는 표현을 입력된 데이터로부터 추출

 

학습 작업 순서

1. 훈련 데이터인 train_images와 train_labels을 네트워크에 주입

2. 네트워크가 이미지와 레이블을 연관시킬 수 있도록 학습

3. test_images에 대한 예측을 네트워크에 요청

4. 예측이 test_labels와 맞는지 확인

 

MNIST 데이터셋

케라스 파이썬 라이브러리 사용

넘파일 배열 형태이며 흑백 손글씨 숫자 이미지(28*28 픽셀)를 10개의 범주(0~9)로 분류

이미지와 레이블은 1 대 1관계

image - 넘파이 배열로 인코딩

label - 0~9의 숫자 배열

 

1. keras에서 MNIST 데이터셋 적재

from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

training set 구성 - train_images와 train_labels

test set 구성 - test_images와 test_labels

 

2. 신경망 구조

신경망 층에서 마지막 층

확률 점수가 들어 있는 배열(모두 더하면 1)을 반환

 

확률 점수 : 클래스 중 하나에 속할 확률

from keras import models
from keras import layers

#완전 연결된 신경망 층인 Dense 층 2개가 연속되어 있음.
network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))

#마지막 층은 10개의 확률 점수가 들어 있는 배열을 반환하는 softmax층
network.add(layers.Dense(10, activation='softmax'))

 

 

각 점수는 현재 숫자 이미지가 10개의 숫자 클래스 중 하나에 속할 확률임.

 

3. 컴파일 단계

손실함수, 옵티마이저, 훈련과 테스트 과정을 모니터링할 지표가 필요

network.compile(optimizer='rmsprop', #옵티마이저
                loss='categorical_crossentropy', #손실 함수
                metrics=['accuracy']) # 훈련과 테스트 과정을 모니터링 할 지표, 여기선 정확도 고려

 

손실함수(loss function)

훈련 데이터에서 신경망의 성능을 측정하는 방법

네트워크가 옳은 방향으로 학습할 수 있도록 도와줌.

 

옵티마이저(optimizer)

입력된 데이터와 손실 함수를 기반으로 네트워크를 업데이트하는 메커니즘

 

훈련과 테스트 과정을 모니터링할 지표

여기선 정확도(정확히 분류된 이미지의 비율)만 고려

 

 

4. 이미지 데이터 준비

훈련을 시작하기 전에 데이터를 네트워크에 맞는 크기로 바꿈.

모든 값을 0과 1 사이로 스케일을 조정

train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype('float32') / 255

test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype('float32') / 255

우리의 훈련 이미지는 [0, 255] 사이의 값인 uint8 타입의 (60000, 28, 28) 크기를 가진 배열로 저장되어 있음.

이 데이터를 0과 1 사이의 값을 가지는 float32 타입의 (60000, 28 * 28) 크기의 배열로 바꿔줌.

 

5. 레이블 준비

레이블을 범주형으로 인코딩

from keras.utils import to_categorical

train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

 

6. 모델 학습

fit 메서드를 호출하여 훈련 데이터에 모델을 학습

network.fit(train_images, train_labels, epochs=5, batch_size=128)

훈련 데이터에 대한 네트워크의 손실과 정확도인 두 개의 정보가 출력

훈련 데이터에 대해 0.989(98.9%)의 정확도가 달성됨.

 

7. 테스트

test_loss, test_acc = network.evaluate(test_images, test_labels)
print('test_acc:', test_acc)

테스트 세트의 정확도는 97.8%로 나옴.

훈련 정확도와 테스트 정확도 사이의 차이는 과대적합 때문

 

과대적합 : 머신 러닝 모델이 훈련 데이터보다 새로운 데이터에서 성능이 낮아지는 경향


2. 신경망을 위한 데이터 표현

최근 모든 머신 러닝 시스템은 일반적으로 텐서를 기본 데이터 구조로 사용

 

텐서(tensor)

머신 러닝의 기본 구성 요소

데이터를 위한 컨테이너(container)

거의 항상 수치형 데이터를 다루므로 숫자를 위한 컨테이너

임의의 차원 개수를 가지는 행렬의 일반화된 모습 (텐서에서는 차원(dimension)을 종종 (axis)이라고 부름.)

 

스칼라(0차원 텐서, 0D 텐서)

하나의 숫자만 담고 있는 텐서

넘파이에서는 float32나 float64 타입의 숫자가 스칼라 텐서

ndim 속성을 사용하면 넘파이 배열의 축 개수를 확인할 수 있음.

 

import numpy as np
x=np.array(12)
x.ndim

축 개수(랭크, rank) = 0

 

벡터(1D 텐서)

숫자의 배열

딱 하나의 축을 가짐.

 

import numpy as np

#5개의 원소를 가진 5차원 벡터(5D 벡터)
x=np.array([1,2,3,4,5])
x.ndim

축 개수(랭크, rank) = 1

 

5D 벡터와 5D 텐서의 차이

5D 벡터 : 하나의 축을 따라 5개의 차원을 가진 것

5D 텐서 : 5개의 축을 가진 것

 

행렬(2D 텐서)

벡터의 배열

행(row)과 열(column)인 2개의 축 존재

 

행(row)

첫 번째 축에 놓여 있는 원소

열(column)

두 번째 축에 놓여 있는 원소

import numpy as np

# 첫 번째 행 : [5, 78, 2, 34, 0]
# 첫 번째 열 : [5, 6, 7]

x = np.array([[5, 78, 2, 34, 0], 
             [6, 79, 3, 35, 1], 
             [7, 80, 4, 36, 2]])
          
x.ndim

축 개수(랭크, rank) = 2

 

3D 텐서와 고차원 텐서

행렬들을 하나의 새로운 배열로 합치면 숫자가 채워진 직육면체 형태

import numpy as np

x = np.array([[[5, 78, 2, 34, 0], 
              [6, 79, 3, 35, 1], 
              [7, 80, 4, 36, 2]], 
             [[5, 78, 2, 34, 0], 
              [6, 79, 3, 35, 1], 
              [7, 80, 4, 36, 2]], 
             [[5, 78, 2, 34, 0], 
              [6, 79, 3, 35, 1], 
              [7, 80, 4, 36, 2]]])

x.ndim

축 개수(랭크, rank) = 3

 

 

4D 텐서

3D 텐서들을 하나의 배열로 합친 텐서

축 개수(랭크, rank) = 4

 

딥러닝에서는 보통 0D에서 4D까지의 텐서를 다루며, 동영상 데이터를 다룰 경우에는 5D 텐서까지 사용하기도 함.

 

텐서의 3개 핵심 속성

축의 개수(랭크, rank)

예를 들어 3D 텐서에는 3개의 축이 있고, 행렬에는 2개의 축이 있음.

넘파이 라이브러리에서는 ndim 속성에 저장

 

크기(shape)

텐서의 각 축을 따라 얼마나 많은 차원이 있는지를 나타낸 파이썬의 튜플(tuple)

  • 앞에 나온 행렬의 크기는 (3, 5)
  • 3D 텐서의 크기는 (3, 3, 5)
  • 벡터의 크기는 (5,)처럼 1개의 원소로 이루어진 튜플
  • 배열 스칼라는 ()처럼 크기가 없음.

데이터 타입(dtype)

텐서에 포함된 데이터의 타입

float32, uint8, float64 등 (드물게 char 타입 사용)

텐서는 사전에 할당되어 연속된 메모리에 저장되어야 하므로 넘파이 배열은 (그리고 대부분 다른 라이브러리는) 가변 길이의 문자열을 지원하지 않음.

 

배치 데이터

샘플 축(sample axis) == 샘플 차원(sample dimension)

일반적으로 딥러닝에서 사용하는 모든 데이터 텐서의 첫 번째 축(인덱스가 0부터 시작하므로 0번째 축)

MNIST 예제에서는 숫자 이미지가 샘플

딥러닝 모델은 한 번에 전체 데이터셋을 처리하지 않고 데이터를 작은 배치(batch)로 나눔.

 

배치 축(batch axis) == 배치 차원(batch dimension)

배치 데이터를 다룰  첫 번째 축(0번 축)

 

텐서의 실제 사례

벡터 데이터

(samples, features) 크기의 2D 텐서

 

시계열 데이터 또는 시퀀스(sequence) 데이터

(samples, timesteps, features) 크기의 3D 텐서

 

이미지

(samples, height, width, channels) 또는 (samples, channels, height, width) 크기의 4D 텐서

 

동영상

(samples, frames, height, width, channels) 또는 (samples, frames, channels, height, width) 크기의 5D 텐서

 

벡터 데이터

대부분의 경우에 해당

첫 번째 축은 샘플 축(sample axis)이고, 두 번째 축은 특성 축(feature axis)

  • 사람의 나이, 우편 번호, 소득으로 구성된 인구 통계 데이터. 각 사람은 3개의 값을 가진 벡터로 구성되고 10만 명이 포함된 전체 데이터셋은 (100000, 3) 크기의 텐서에 저장될 수 있음.
  • (공통 단어 2만 개로 만든 사전에서) 각 단어가 등장한 횟수로 표현된 텍스트 문서 데이터셋. 각 문서는 2만 개의 원소(사전에 있는 단어마다 하나의 원소에 대응합니다)를 가진 벡터로 인코딩될 수 있음.
  • 500개의 문서로 이루어진 전체 데이터셋은 (500, 20000) 크기의 텐서로 저장됨.

 

시계열 데이터 또는 시퀀스 데이터

데이터에서 시간이 (또는 연속된 순서가) 중요할 때는 시간 축을 포함하여 3D 텐서로 저장

관례적으로 시간 축은 항상 두 번째 축(인덱스가 1인 축)

  • 주식 가격 데이터셋
    1분마다 현재 주식 가격, 지난 1분 동안에 최고 가격과 최소 가격을 저장
    1분마다 데이터는 3D 벡터로 인코딩되고 하루 동안의 거래는 (390, 3) 크기의 2D 텐서로 인코딩됨. (하루의 거래 시간은 390분).
    250일치의 데이터는 (250, 390, 3) 크기의 3D 텐서로 저장될 수 있음.
    여기에서 1일치 데이터가 하나의 샘플이 됨.
  • 트윗 데이터셋
    각 트윗은 128개의 알파벳으로 구성된 280개의 문자 시퀀스
    여기에서는 각 문자가 128개의 크기인 이진 벡터로 인코딩될 수 있음.(해당 문자의 인덱스만 1이고 나머지는 모두 0인 벡터)
    그러면 각 트윗은 (280, 128) 크기의 2D 텐서로 인코딩될 수 있음.
    100만 개의 트윗으로 구성된 데이터셋은 (1000000, 280, 128) 크기의 텐서에 저장

 

이미지 데이터

이미지는 전형적으로 높이, 너비, 컬러 채널의 3차원으로 이루어짐

 

[이미지 텐서의 크기를 지정하는 방식]

1. 채널 마지막(channel-last) 방식

텐서플로에서 사용

(samples, height, width, color_depth)처럼 컬러 채널의 깊이를 끝에 놓음.

 

2. 채널 우선(channel-first)방식

씨아노에서 사용

(samples, color_depth, height, width)처럼 컬러 채널의 깊이를 배치 축 바로 뒤에 놓음.

 

케라스 프레임워크는 두 형식을 모두 지원

 

비디오 데이터

비디오 데이터는 현실에서 5D 텐서가 필요한 몇 안 되는 데이터 중 하나

하나의 비디오는 프레임의 연속이고 각 프레임은 하나의 컬러 이미지

프레임이 (height, width, color_depth)3D 텐서로 저장될 수 있기 때문에 프레임의 연속은 (frames, height, width, color_depth)4D 텐서로 저장될 수 있음.

여러 비디오의 배치는 (samples, frames, height, width, color_depth)5D 텐서로 저장될 수 있음.


3. 신경망의 톱니바퀴: 텐서 연산

심층 신경망이 학습한 모든 변환을 수치 데이터 텐서에 적용하는 몇 종류의 텐서 연산으로 나타낼 수 있음

 

원소별 연산

relu함수와 덧셈

텐서에 있는 각 원소에 독립적으로 적용

고도의 병렬 구현이 가능한 연산

 

relu(x) = max(x,0)

입력이 0보다 크면 입력을 그대로 반환

입력이 0보다 작으면 0을 반환

 

브로드캐스팅

크기가 다른 두 텐서가 연산될 때 모호하지 않고 실행이 가능하면 작은 텐서가 큰 텐서에 크기를 맞춤.

 

[브로드캐스팅 과정]

1. 큰 텐서의 ndim에 맞도록 작은 텐서에 축이 추가

2. 작은 텐서가 새 축을 따라서 큰 텐서의 크기에 맞도록 반복

x -> (32,10) / y -> (10,)

 

1. y의 비어 있는 첫 번째 축을 추가하여 크기를 (1,10)으로 만듦.

2. y를 이 축에 32번 반복하여 텐서 y의 크기를 (32,10)으로 만듦.

 

텐서 점곱 (텐서 곱셈)

원소별 연산과 반대로 입력 텐서의 원소들을 결합시킴. (우리가 알고 있는 행렬의 곱과 같음.)

넘파이, 케라스, 씨아노, 텐서플로에서 원소별 곱셈은 * 연산자 사용

넘파이, 케라스에서 점곱 연산은 dot연산자 사용

 

텐서 크기 변환

특정 크기에 맞게 열과 행을 재배열

크기가 변환인 텐서는 원래 텐서와 원소 개수가 동일

 

신경망 전체적으로 텐서 연산의 연결로 구성
모든 텐서 연산 입력 데이터의 기하학적 변환

4. 신경망의 엔진: 그래디언트 기반 최적화

훈련 반복 루프

1. 훈련 샘플 x와 이에 상응하는 타깃 y의 배치를 추출

2. x를 사용하여 네트워크를 실행하고 예측 y_pred를 구함.

3. y_pred와 y의 차이를 측정하여 배치에 대한 네트워크 손실을 계산

4. 배치에 대한 손실이 조금 감소되도록 네트워크의 모든 가중치를 업데이트

 

신경망의 사용된 모든 연산은 미분 가능하다는 장점을 사용해 네트워크 가중치에 대한 손실의 그래디언트를 계산하는 것이 좋은 방법

 

미분 가능 == 변화율을 유도할 수 있음의 의미

 

그래디언트(gradient)

텐서 연산의 변화율

그래디언트의 반대 방향으로 가중치를 이동하면 손실이 감소

 

 

확률적 경사 하강법

확률적 : 각 배치 데이터가 무작위로 선택됨.

 

랜덤한 배치 데이터에서 현재 손실 값을 토대로 조금씩 파라미터 수정하여 가장 적은 손실 함수의 값을 만드는 가중치의 조합을 해석적으로 찾는 것

변화율이 0이 되는 지점을 모두 찾고 이 중에서 어떤 포인트의 함수 값이 가장 적은 지를 확인하는 것

(함수의 최솟값은 변화율이 0이 되는 지점)

 

옵티마이저 (optimizer, 최적화 방법)

업데이트를 하면서 다음 가중치를 계산할 때 현재 그래디언트 값만 보지 않고 이전에 업데이트된 가중치를 여러 가지 다른 방식으로 고려

 

 

 

역전파 알고리즘 (후진 모드 자동 미분)

변화율 연결

연쇄 법칙을 신경망의 그래디언트 계산에 적용하여 탄생한 알고리즘