[Python] 딕셔너리 key와 value 뒤집기

딕셔너리 key와 value 바꾸기

morse = {
'A' : '.-',
'B' : '-...',
'C' : '-.-.',
'D' : '-..',
'E' : '.',
'F' : '..-.',
'G' : '--.',
'H' : '....',
'I' : '..',
'J' : '.---',
'K' : '-.-',
'L' : '.-..',
'M' : '--',
'N' : '-.',
'O' : '---',
'P' : '.--.',
'Q' : '--.-',
'R' : '.-.',
'S' : '...',
'T' : '-',
'U' : '..-',
'V' : '...-',
'W' : '.--',
'X' : '-..-',
'Y' : '-.--',
'Z' : '--..',
}

위와 같은 딕셔너리에서 키와 밸류를 바꿔야 한다고 하겠습니다.
아래처럼 새 딕셔너리를 만든 뒤 for 문으로 키와 밸류의 자리를 바꿔 지정해주면 됩니다.

new_morse = {}
for k, v in morse.items():
new_morse[v] = k

print(new_morse)
{'.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y', '--..': 'Z'}

Deep Learning with Python - Ch.05


케라스 창시자에게 배우는 딥러닝을 실습하면서 정리한 포스트입니다. 코드 예제와 코드 설명은 역자 깃허브에서 받아볼 수 있습니다. 출판물이고 개인적으로만 참고하기 위한 요약노트이다 보니 설명이 불친절한 점은 양해 바랍니다. 보다 자세한 내용을 원하시는 분은 위 링크의 책을 참고하시기 바랍니다.


5장. 컴퓨터 비전을 위한 딥러닝

6장(텍스트)부터 학습 뒤 볼 예정입니다.

Deep Learning with Python - Ch.04


케라스 창시자에게 배우는 딥러닝을 실습하면서 정리한 포스트입니다. 코드 예제와 코드 설명은 역자 깃허브에서 받아볼 수 있습니다. 출판물이고 개인적으로만 참고하기 위한 요약노트이다 보니 설명이 불친절한 점은 양해 바랍니다. 보다 자세한 내용을 원하시는 분은 위 링크의 책을 참고하시기 바랍니다.


4장. 머신러닝의 기본 요소

머신러닝의 4가지 분류

지도학습

  • 가장 흔한 경우. 앞의 예제들과 광학 문자 판독, 음성 인식, 이미지 분류, 번역 등
  • 대부분 회귀지만 이런 변종도 있음
    • sequence generation: 사진이 주어지면 이를 설명하는 캡션 생성
    • syntax tree expectation: 문장이 주어지면 분해된 구문 트리를 예측
    • object detection: 사진 안의 특정 물체에 bounding box를 그림
    • image segmentation: 사진을 픽셀 단위로 특정 물체에 masking

비지도학습

  • 타깃 사용하지 않고 입력에 대한 흥미로운 변환을 찾는다
  • 데이터 시각화, 데이터 압축, 데이터 노이즈 제거, 상관관계 이해에 사용
  • 차원 축소와 클러스터링

자기지도학습

  • 지도학습의 특별한 경우. 지도학습이지만 사람이 만든 라벨을 사용하지 않음
  • 라벨이 필요하지만 휴리스틱 알고리즘(경험적인 알고리즘)을 사용해 입력 데이터에서 생성
  • ex) 오토인코더, 지난 프레임이 주어졌을 때 다음 프레임을 예측, 단어가 주어졌을때 다음 단어를 예측

강화학습

  • 자율주행 자동차, 자원 관리, 교육 등에서 애플리케이션 등장 예상됨

머신러닝 모델 평가

  • 과대적합을 완화하고 일반화를 최대화하기 위한 전략(처음 본 데이터에서 잘 작동하는 모델 찾기)

훈련, 검증, 테스트셋

  • 데이터가 적을 때 데이터셋을 나누려면 다음과 같은 고급 기법이 도움이 됨
    • 단순 홀드아웃 검증: 데이터 일부를 테스트셋으로 떼어 둠
    • K-겹 교차검증: 데이터를 동일한 크기를 가진 K개 분할로 나눠 각 분할 i에 대해 남은 K-1개의 분할로 모델을 훈련하고 분할 i 에서 모델을 평가
    • 셔플링을 사용한 반복 K-겹 교차 검증: K개의 분할로 나누기 전에 매번 데이터를 무작위로 섞기

기억해야 할 것

  • 대표성 있는 데이터를 골라야 한다. 타깃이 0~9까지 9가지 숫자인데 테스트셋에 타깃이 0~7까지 있는 데이터만 넣는다면?
  • 시간의 방향: 과거로부터 미래를 예측하려고 한다면 테스트셋에 있는 데이터가 트레이닝셋 데이터보다 미래에 있어야 한다
  • 데이터 중복: 한 데이터셋에 같은 데이터가 두 번 등장하면 트레이닝셋의 일부로 테스트를 하는 일이 발생할 수 있다.

데이터 전처리, 피쳐 엔지니어링, 피쳐 학습

신경망을 위한 데이터 전처리

  • 원본 데이터를 신경망에 적용하기 쉽게 만들기 위해 데이터를 전처리
  • 벡터화
    • 신경망에서 모든 입력과 타깃은 부동 소수 데이터로 이뤄진 텐서여야 함(특정 경우에는 정수로 이뤄진 텐서)
    • 데이터가 사운드 이미지 텍스트 뭐든 일단 텐서로 변환
  • 값 정규화
    • (MNIST) 숫자 이미지를 그레이스케일 인코딩인 0~255 사이의 정수로 인코딩. 이를 네트워크에 주입하기 전 float32 타입으로 변경하고 255로 나눠 최종적으로 0~1 사이의 부동 소수 값으로 만듦.
    • (보스턴 집값) 데이터를 네트워크에 주입하기 전 각 특성을 정규화해 평균 0, 표준편차 1이 되도록 만듦
    • 비교적 큰 값이나 균일하지 않은 데이터를 신경망에 주입하는건 위험(업데이트할 그래디언트가 커져 네트워크가 수렴하는걸 방해함)
    • 대부분 값이 0~1 사이, 모든 특성이 대체로 비슷한 범위를 가질수록 네트워크를 쉽게 학습시킬 수있음
    • 도움이 되는 정규화 방법
x -= x.mean(axix=0)
x /= x.std(axis=0)
  • 누락값 처리
    • 일반적으로 0이 사전에 정의된 의미 있는 값이 아니라면 누락값을 0으로 처리해도 괜찮음
    • 트레이닝셋에는 누락값이 없는데 테스트셋에 누락값이 있을 가능성이 있다면 트레이닝셋에 고의적으로 누락값이 있는 샘플을 만들어야 함

특성 공학

  • 모델이 수월하게 작업할 수 있는 어떤 방식으로 데이터가 표현될 필요

과대적합과 과소적합

  • 언더피팅
    • 훈련 데이터의 손실이 낮아질수록 테스트 데이터의 손실도 낮아짐
    • 모델의 성능이 계속 발전될 여지가 있음
  • 오버피팅
    • 어느 시점부터 일반화 성능이 더 높아지지 않음
    • 검증 세트의 성능이 멈추고 감소하기 시작
    • 훈련 데이터에 특화된 패턴을 학습하기 시작했다는 의미
  • regularization
    • 과대적합을 피하는 저리 과정

네트워크 크기 축소

  • 오버피팅을 막는 가장 단순한 방법은 모델에 있는 학습 파라미터의 수를 줄이는 것
  • 파라미터의 수(모델의 용량)는 층의 수와 각 층의 유닛 수에 의해 결정
  • 언더피팅되지 않도록 충분한 파라미터를 가진 모델을 사용해야 함.
  • 데이터에 알맞은 모델 크기를 찾으려면 각기 다른 구조를 평가해봐야 함
    • 비교적 적은 수의 층과 파라미터로 시작해 검증 손실이 감소되기 시작할 때까지 층이나 유닛 수를 늘리는게 일반적인 작업 흐름
# 원본 모델
from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

# 작은 용량의 모델
model = models.Sequential()
model.add(layers.Dense(6, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(6, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

# 큰 용량의 모델
model = models.Sequential()
model.add(layers.Dense(1024, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(1024, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
  • 작은 네트워크가 기본 네트워크보다 더 나중에 과대적합되기 시작. 과대적합이 시작됐을 때 성능이 더 천천히 감소
  • 용량이 큰 네트워크는 빨리 과대적합이 시작돼 갈수록 더 심해짐. 검증손실도 불안정
  • 용량이 큰 네트워크일수록 빠르게 훈련 데이터를 모델링하지만 과대적합에 민감해짐(트레이닝과 테스트 손실 사이 차이 발생)

가중치 규제 추가

  • 오캄의 면도날 이론
    • 두 가지 설명이 있다면 더 적은 가정이 필요한 간단한 설명이 옳다는 이론
    • 신경망 학습모델에도 적용됨. 복잡한 모델이 간단한 모델보다 과대적합될 가능성이 높음
  • 간단한 모델이란 파라미터 값 분포의 엔트로피가 작은 모델(혹은 적은 수의 파라미터를 가진 모델)
  • 과대적합 완화법: 네트워크의 복잡도에 제한을 둬서 가중치가 작은 값을 가지도록 강제하는 것
  • 가중치 값의 분포가 더 균일해짐(가중치 규제) -> 네트워크의 손실 함수에 큰 가중치에 연관된 두 가지 형태의 비용을 추가함
    • L1 규제: 가중치의 절댓값에 비례하는 비용이 추가됨(가중치의 L1 norm)
    • L2 규제(=가중치 감쇠, weight decay): 가중치의 제곱에 비례하는 비용이 추가됨(가중치의 L2 norm).
# 모델에 L2 가중치 추가하기
from keras import regularizers

model = models.Sequential()
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, kernel_regularizer=regularizers.l2(0.001),
activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
  • l2(0.001)는 가중치 행렬의 모든 원소를 제곱하고 0.001을 곱해 네트워크 전체 손실에 더해진다는 의미. 이 페널티 항은 트레이닝에서만 추가됨
  • L2 규제를 사용한 모델이 사용하지 않은 모델보다 과대적합을 잘 견딤(에포크 반복에 따라 loss가 덜 오름)
  • L2 규제 대신 사용 가능한 옵션
    • L1 규제 regularizers.l1(0.001)
    • L1, L2 규제 병행 regularizers.l1_l2(l1=0.001, l2=0.001)

dropout 추가

  • 네트워크 층에 드랍아웃을 적용하면 트레이닝 동안 랜덤으로 층의 일부 출력 특성을 제외시킴(0으로..)
    • ex) 한 층이 트레이닝되는 동안 어떤 입력샘플에 대해 [0.2, 0.5, 1.3, 0.8, 1.1] 벡터를 출력한다고 가정하면, 일부가 무작위로 0이 됨([0, 0.5, 1.3, 0, 1.1]
  • 드랍아웃 비율은 0이 될 특성의 비율(대개 0.2~0.5 로 지정)
  • 테스트 단계에서는 드랍아웃이 일어나지 않는다

  • layer_output *= np.random.randint(0, high=2, size=layer_output.shape) : 트레이닝시 유닛의 출력 중 50%를 버림

  • 테스트 시 드랍아웃 비율로 출력을 낮춰야: layer_output *= 0.5
  • 드랍아웃이 과대적합을 줄이는 원리
    • 층의 출력값에 노이즈를 추가해 중요하지 않은 우연한 패턴을 깨뜨림
  • 케라스에선 층의 출력 바로 뒤에 Dropout 층을 추가해 네트워크에 드랍아웃 적용 가능
# IMDB 네트워크에 드랍아웃 추가
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))

보편적인 머신러닝 작업 흐름

문제 정의와 데이터셋 수집

  • 무엇을 예측할 것인가
  • 입력 데이터는?
  • 어떤 종류의 문제인가? (이진분류 / 다중분류 / 스칼라 회귀 / 벡터회귀 / 다중 레이블 분류 / 군집 / 생성 / 강화학습)
  • 입력과 출력이 무엇인지

성공 지표 선택

  • 클래스 분포가 균일한 분류 문제
    • 정확도와 ROC AUC
  • 클래스 분폴가 균일하지 않은 문제
    • 정밀도와 재현율
  • 랭킹 문제나 다중 레이블 문제
    • 평균 정밀도

평가 방법 선택

  • 현재의 진척 상황 평가법
    • 홀드아웃 검증 세트 분리(데이터가 풍부할 때)
    • K-겹 교차 검증(샘플 수가 너무 적을 때)
    • 반복 K-겹 교차 검증(데이터가 적고 정확한 모델 평가 필요시)

데이터 준비

  • 머신러닝 모델을 심층 신경망이라 가정
    • 데이터는 텐서로 구성
    • 텐서에 있는 값은 일반적으로 작은 값으로 스케일 조정돼 있음 [-1, 1] or [0, 1]
    • 특성마다 범위가 다르면 정규화
    • 피처 엔지니어링

기본보다 나은 모델 훈련하기

  • 통계적 검정력을 달성하는게 목표
  • MNIST에서 통계적 검정력을 달성하려면 0.1보다 높은 정확도를 내는 모델이어야 함
  • 모델을 위해 고려할 세 가지
    • 마지막 층의 활성화 함수: 네트워크 출력에 필요한 제한을 가함. IMDB 분류에선 마지막 층에 시그모이드 함수 사용. 회귀에서는 마지막 층에 활성화 함수 사용 안함
    • 손실 함수: 풀려고 하는 문제의 종류에 적합해야. IMDB에선 binary_crossentropy, 회귀에선 mse.
    • 최적화 설정: 대부분의 경우 rmsprop과 기본 학습률 사용하는게 무난함
  • 손실함수는 미니 배치 데이터에서 계산 가능해야 하고 미분 가능해야 함
  • 문제 유형에 따른 마지막층 활성화 함수와 손실 함수 선택
    • 이진 분류
      • 시그모이드 / binary_crossentropy
    • 단일 레이블 다중 분류
      • 소프트맥스 / categorical_crossentropy
    • 다중 레이블 다중 분류
      • 시그모이드 / binary_crossentropy
    • 임의 값에 대한 회귀
      • 없음 / mse
    • 0과 1 가시 값에 대한 회귀
      • 시그모이드 / mse or binary_crossentropy

몸집 키우기: 과대적합 모델 구축

  • 머신러닝은 최적화와 일반화 사이의 줄다리기
    • 과소적합과 과대적합 사이
    • 과소용량과 과대용량 사이
  • 얼마나 큰 모델을 만들어야 할까? 일단 과대적합된 모델을 만들어본다
    1. 층을 추가
    2. 층의 크기를 키움
    3. 더 많은 에포크 동안 트레이닝
  • 훈련 손실과 검증 손실을 모니터링. 검증 데이터에서 모델 성능이 감소하기 시작했을 때 과대적합에 도달한 것

모델 규제와 하이퍼파라미터 튜닝

  • 드랍아웃 추가
  • 층을 추가하거나 제거
  • L1, L2 또는 둘 다를 추가해보기
  • 하이퍼파라미터를 바꿔보기(층의 유닛 수나 옵티마이저의 학습률 등)
  • 피처 엔지니어링

Deep Learning with Python - Ch.03


케라스 창시자에게 배우는 딥러닝을 실습하면서 정리한 포스트입니다. 코드 예제와 코드 설명은 역자 깃허브에서 받아볼 수 있습니다. 출판물이고 개인적으로만 참고하기 위한 요약노트이다 보니 설명이 불친절한 점은 양해 바랍니다. 보다 자세한 내용을 원하시는 분은 위 링크의 책을 참고하시기 바랍니다.


3장. 신경망 시작하기

신경망 구조

  • 네트워크(모델)를 구성하는 층
  • 입력데이터, 타겟
  • 손실 함수: 피드백 신호를 정의
  • 옵티마이저: 학습 진행 방식을 결정

층: 딥러닝의 구성 단위

  • 층: 하나 이상의 텐서를 입력받아 하나 이상의 텐서를 출력하는 데이터 처리 모듈
  • 대부분 가중치라는 층의 상태를 가짐(상태가 없는 층도 존재)
  • 가중치는 확률적 경사 하강법에 의해 학습되는 하나 이상의 텐서
  • 층마다 적절한 텐서 포맷과 데이터 처리 방식이 다름
    • 벡터 데이터(2D 텐서) : 밀집 연결 층
    • 시퀀스 데이터(3D 텐서) : LSTM 같은 순환 층
    • 이미지 데이터(4D 텐서) : Conv2D 클래스. 2D 합성곱 층
  • 케라스는 호환 가능한 층(호환성: 각 층이 특정 크기의 입력 텐서만 받고 특정 크기의 출력 텐서를 반환)을 엮어 데이터 변환 파이프라인을 구성해 딥러딩 모델을 만듦
# 첫 번째 차원이 784인 2D 텐서만 입력으로 받는 층
# 첫 번째 차원 크기가 32로 변환된 텐서를 출력
from keras import layers
layer = layers.Dense(32, input_shape=(784,))
  • 위 층에는 32차원의 벡터를 입력으로 받는 하위 층이 연결돼야 함
  • 케라스가 모델에 추가된 층을 자동으로 상위 층에 맞춰줌
from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(32, input_shape=(784,)))
model.add(layers.Dense(10))
  • 두 번째 층에는 input_shape 매개변수를 지정하지 않음(앞 층의 출력 크기를 입력 크기로 자동으로 채택)

모델: 층의 네트워크

  • 딥러닝 모델은 층으로 만든 Directed Acyclic Graph(DAG)
  • 자주 등장하는 네트워크 구조
    • branch가 2개인 네트워크
    • 출력이 여러 개인 네트워크
    • inception 블록
  • 네트워크 구조는 가설 공간(가능성 있는 공간)을 정의
  • 네트워크 구조 선택 : 가설 공간을 입력 데이터에서 출력 데이터로 매핑하는 일련의 특정 텐서 연산으로 제한
  • 딱 맞는 네트워크 구조 찾기는 과학보다 예술
  • 네트워크 구조 정의 후에는 손실함수와 옵티마이저를 선택해야 함

손실 함수와 옵티마이저: 학습 과정을 조절하는 열쇠

  • 손실 함수: 훈련하는 동안 최소화될 값. 문제에 대한 성공 지표
  • 옵티마이저: 손실 함수를 기반으로 네트워크가 어떻게 업데이트될지 결정.
  • 출력 여러 개를 내는 신경망은 여러 개의 손실 함수를 가질 수 있음
  • But, 경사 하강법 과정은 하나의 스칼라 손실 값을 기준으로 함
  • 따라서 손실이 여러 개인 네트워크에서는 모든 손실이 (평균을 내서) 하나의 스칼라 양으로 합쳐짐
  • 문제에 따라 올바른 목적 함수 선택해야
    • 2개의 클래스 분류 문제 : binary crossentropy
    • 여러개 클래스 분류 문제 : categorical crossentropy
    • 회귀 문제 : 평균 제곱 오차
    • 시퀀스 학습 문제 : Connection Temporal Classification
    • 완전히 새로운 연구: 독자적인 목적 함수

케라스 소개

  • 생략

딥러닝 컴퓨터 셋팅

  • 생략

영화 리뷰 분류: 이진 분류 예제

리뷰 텍스트를 기반으로 영화 리뷰를 긍정과 부정으로 분류

IMDB 데이터셋

from keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)
  • num_words=10000 : 트레이닝셋에서 가장 많이 등장하는 단어 1만 개만 사용
  • labels는 0(부정)과 1(긍정)을 나타내는 리스트

데이터 준비

  • 숫자 리스트를 신경망에 넣기 위해 텐서로 바꾸는 두 가지 방법
    • 리스트에 padding을 추가하고 (samples, sequence-length) 크기의 정수 텐서로 변환. 신경망 첫 번째 층으로 사용
    • 리스트를 원핫인코딩해 0, 1 벡터로 변환. (아래 예시)
# 정수 시퀀스를 이진 행렬로 인코딩
import numpy as np

def vectorize_sequences(sequences, dimension=10000):
# (시퀀스 길이, 차원) 크기의 0행렬 만들기
results = np.zeros((len(sequences), dimension))

# results[i]에서 특정 인덱스 위치를 1로 바꾸기
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.

return results

# 데이터를 벡터로 변환
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)

x_train.shape, x_test.shape는 각각 (25000, 10000) 모양이 됨

# 라벨을 벡터로 바꾸기
y_train = np.asarray(train_labels).astype('float32')
y_test = np.asarray(test_labels).astype('float32')

신경망 모델 만들기

  • 입력 데이터는 벡터, 라벨은 1 or 0의 스칼라
  • 이런 문제에 잘 작동하는 네트워크는 relu 활성화 함수를 사용한 완전 연결 층(Dense(16, activation=’relu’))을 그냥 쌓은 것
  • 16은 은닉 유닛의 수. 하나의 은닉 유닛은 층이 나타내는 표현 공간에서 하나의 차원이 됨.
  • output = relu(dot(W, input) + b)
  • 16개 은닉 유닛이 있다는 건 가중치 행렬 W의 크기가 (input_dimension, 16)이라는 의미. 입력 데이터와 W를 점곱하면 입력 데이터가 16차원으로 표현된 공간으로 투영됨(+ 편향 벡터 b를 더하고 relu 연산 적용)
  • 표현공간의 차원: ‘신경망이 내재된 표현을 학습할 때 가질 수 있는 자유도’
  • 중간의 은닉 층은 활성화 함수로 relu를, 마지막 층은 확률을 출력하기 위해 시그모이드 활성화 함수를 사용.
  • relu는 음수를 0으로 만드는 함수. 시그모이드는 임의의 값을 [0, 1] 사이로 압축-> 출력 값을 확률처럼 해석 가능
# 위 신경망의 케라스 구현
from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
  • 이진 분류 문제고 신경망 출력이 확률 -> binary_crossentropy 나 mean_squared_error
  • binary_crossentropy
    • 확률을 출력하는 모델 사용 시 최선의 선택
    • 크로스엔트로피: 확률 분포 간의 차이를 측정(여기선 원본 분포와 예측 분포 사이를 측정)
# 모델 컴파일하기
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
  • 옵티마이저의 매개변수를 바꾸거나 손실함수, 측정함수를 직접 만들어야 할 경우는 아래와 같이 설정
# 옵티마이저 설정하기
from keras import optimizers
model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss='binary_crossentropy',
metrics=['accuracy'])
# 손실, 측정함수 객체로 지정하기
from keras import losses
from keras import metrics

model.compile(optimizer=optimizers.RMSprop(lr=0.001),
loss=losses.binary_crossentropy,
metrics=[metrics.binary_accuracy])

훈련 검증

# 원본 훈련 데이터에서 1만개 샘플 떼어내 검증 셋 만들기
x_val = x_train[:10000]
partial_x_train = x_train[10000:]
v_val = y_train[:10000]
partial_y_train = y_train[10000:]
# 512개 샘플씩 미니 배치를 만들어 20번의 에포크 동안 훈련
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
Train on 15000 samples, validate on 10000 samples
Epoch 1/20
15000/15000 [==============================] - 3s 200us/step - loss: 0.5084 - acc: 0.7810 - val_loss: 0.3798 - val_acc: 0.8683
Epoch 2/20
15000/15000 [==============================] - 1s 95us/step - loss: 0.3005 - acc: 0.9043 - val_loss: 0.3002 - val_acc: 0.8901
Epoch 3/20
15000/15000 [==============================] - 1s 95us/step - loss: 0.2179 - acc: 0.9289 - val_loss: 0.3083 - val_acc: 0.8711
Epoch 4/20
15000/15000 [==============================] - 2s 124us/step - loss: 0.1750 - acc: 0.9437 - val_loss: 0.2843 - val_acc: 0.8835
Epoch 5/20
15000/15000 [==============================] - 1s 95us/step - loss: 0.1426 - acc: 0.9542 - val_loss: 0.2842 - val_acc: 0.8870
Epoch 6/20
15000/15000 [==============================] - 1s 94us/step - loss: 0.1150 - acc: 0.9653 - val_loss: 0.3154 - val_acc: 0.8772
Epoch 7/20
15000/15000 [==============================] - 1s 95us/step - loss: 0.0978 - acc: 0.9709 - val_loss: 0.3129 - val_acc: 0.8846
Epoch 8/20
15000/15000 [==============================] - 1s 94us/step - loss: 0.0807 - acc: 0.9765 - val_loss: 0.3857 - val_acc: 0.8650
Epoch 9/20
15000/15000 [==============================] - 2s 107us/step - loss: 0.0660 - acc: 0.9820 - val_loss: 0.3636 - val_acc: 0.8782
Epoch 10/20
15000/15000 [==============================] - 2s 134us/step - loss: 0.0561 - acc: 0.9849 - val_loss: 0.3844 - val_acc: 0.8793
Epoch 11/20
15000/15000 [==============================] - 2s 137us/step - loss: 0.0436 - acc: 0.9899 - val_loss: 0.4151 - val_acc: 0.8783
Epoch 12/20
15000/15000 [==============================] - 2s 117us/step - loss: 0.0379 - acc: 0.9920 - val_loss: 0.4542 - val_acc: 0.8684
Epoch 13/20
15000/15000 [==============================] - 2s 109us/step - loss: 0.0300 - acc: 0.9929 - val_loss: 0.4703 - val_acc: 0.8728
Epoch 14/20
15000/15000 [==============================] - 2s 125us/step - loss: 0.0247 - acc: 0.9945 - val_loss: 0.5042 - val_acc: 0.8718
Epoch 15/20
15000/15000 [==============================] - 2s 132us/step - loss: 0.0192 - acc: 0.9964 - val_loss: 0.5316 - val_acc: 0.8704
Epoch 16/20
15000/15000 [==============================] - 2s 109us/step - loss: 0.0164 - acc: 0.9969 - val_loss: 0.5650 - val_acc: 0.8690
Epoch 17/20
15000/15000 [==============================] - 1s 99us/step - loss: 0.0125 - acc: 0.9981 - val_loss: 0.5973 - val_acc: 0.8668
Epoch 18/20
15000/15000 [==============================] - 2s 108us/step - loss: 0.0108 - acc: 0.9983 - val_loss: 0.6285 - val_acc: 0.8670
Epoch 19/20
15000/15000 [==============================] - 2s 109us/step - loss: 0.0079 - acc: 0.9991 - val_loss: 0.7197 - val_acc: 0.8553
Epoch 20/20
15000/15000 [==============================] - 1s 100us/step - loss: 0.0048 - acc: 0.9998 - val_loss: 0.6812 - val_acc: 0.8674
  • model.fit() 메서드는 History 객체를 반환
# 훈련과 검증 손실
import matplotlib.pyplot as plt

history_dict = history.history
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(loss) + 1)

plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('훈련과 검증 손실')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()

# 훈련과 검증 정확도
plt.clf()
acc = history_dict['acc']
val_acc = history_dict['val_acc']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('훈련과 검증 정확도')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()

  • 훈련 손실은 에포크마다 감소, 훈련 정확도는 에포크마다 증가
  • 트레이닝셋에서 잘 작동하지만 테스트셋에서는 아님(overfitting 됐기 때문)
  • 오버피팅을 막기 위해 세번째 에포크 이후 훈련을 중지
# 처음부터 다시 훈련하기
model = models.Sequential()
model.add(layers.Dense(16, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(16, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])

model.fit(x_train, y_train, epochs=4, batch_size=512)
model.evaluate(x_test, y_test)
Epoch 1/4
25000/25000 [==============================] - 3s 102us/step - loss: 0.4749 - acc: 0.8216
Epoch 2/4
25000/25000 [==============================] - 2s 76us/step - loss: 0.2659 - acc: 0.9096
Epoch 3/4
25000/25000 [==============================] - 2s 70us/step - loss: 0.1983 - acc: 0.9298
Epoch 4/4
25000/25000 [==============================] - 2s 67us/step - loss: 0.1678 - acc: 0.9403
25000/25000 [==============================] - 3s 140us/step
[0.3244189430713654, 0.87316]
  • 87%의 정확도 달성

훈련된 모델로 새로운 데이터에 대해 예측하기

model.predict(x_test)
array([[0.23489225],
[0.99956626],
[0.95799285],
...,
[0.16514498],
[0.11655141],
[0.74928373]], dtype=float32)

뉴스 기사 분류: 다중 분류 문제

  • 로이터 뉴스를 46개 토픽으로 분류하는 신경망 만들기
  • 각 데이터가 하나의 카테고리로 분류되는 단일 레이블 다중 분류 문제
  • 각 데이터가 여러 개의 카테고리에 속할 수 있다면 다중 레이블 다중 분류 문제

로이터 데이터셋

# 케라스에서 데이터셋 불러오기
from keras.datasets import reuters

(train_data, train_labels), (test_data, test_labels) = reuters.load_data(num_words=10000)
  • num_words=10000 : 데이터에서 가장 자주 등장하는 단어 10000개로 제한
# 샘플 수 확인하기
len(train_data), len(test_data)
(8982, 2246)
  • 트레이닝셋 8982개, 테스트셋 2246개
train_data[-1]
[1,
227,
2406,
91,
2,
125,
2855,
21,
4,
3976,
76,
7,
4,
757,
481,
3976,
790,
5259,
5654,
9,
111,
149,
8,
7,
10,
76,
223,
51,
4,
417,
8,
1047,
91,
6917,
1688,
340,
7,
194,
9411,
6,
1894,
21,
127,
2151,
2394,
1456,
6,
3034,
4,
329,
433,
7,
65,
87,
1127,
10,
8219,
1475,
290,
9,
21,
567,
16,
1926,
24,
4,
76,
209,
30,
4033,
6655,
5654,
8,
4,
60,
8,
4,
966,
308,
40,
2575,
129,
2,
295,
277,
1071,
9,
24,
286,
2114,
234,
222,
9,
4,
906,
3994,
8519,
114,
5758,
1752,
7,
4,
113,
17,
12]
  • 각 샘플은 정수 리스트
# 텍스트로 디코딩
word_index = reuters.get_word_index()
reverse_word_index = dict((value, key) for (key, value) in word_index.items())
decoded_newswire = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[-1]])
# 0, 1, 2는 각각 '패딩, 문서 시작, 사전에없음' 인덱스이므로 3을 뺌
decoded_newswire
"? currency fluctuations may ? their influence on the bullion market in the near future bullion bankers samuel montagu and co ltd said in a market report but the firm said silver may lag behind gold in any reactions to movements on foreign exchanges opec's failure to address the recent decline in oil prices remains a worrying factor however and on balance it appears that the market should be approached cautiously montagu said the bank said the us economy has shown no ? long term improvement and that both latin american debt and the iranian arms affair could undermine confidence in the dollar reuter 3"
train_labels.min(), train_labels.max()
(0, 45)
  • 라벨은 0과 45 사이의 정수

데이터 준비

# 데이터를 벡터로 변환
def vectorize_sequences(sequences, dimension=10000):
results = np.zeros((len(sequences), dimension))
for i, sequence in enumerate(sequences):
results[i, sequence] = 1.
return results

# 데이터를 벡터로 변환
x_train = vectorize_sequences(train_data)
x_test = vectorize_sequences(test_data)
  • 라벨을 벡터로 바꾸는 두 가지 방법
    • 라벨의 리스트를 정수 텐서로 변환한따
    • 원 핫 인코딩(범주형 인코딩)(아래 예시)
# 케라스 내장 함수를 사용한 원핫인코딩
from keras.utils.np_utils import to_categorical

one_hot_train_labels = to_categorical(train_labels)
one_hot_test_labels = to_categorical(test_labels)

모델 구성

  • 출력 클래스는 46개. 규모가 작은 층을 사용하면 병목현상으로 유용한 정보를 잃게될 수 있다. 따라서 아래에선 64개 유닛을 사용한다.
from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
  • 마지막 Dense 층의 크기는 46. (각 입력 샘플에 대해 46차원의 벡터를 출력)
  • 마지막 층의 softmax 활성화 함수 : 각 입력 샘플마다 46개 출력 클래스에 대한 확률 분포를 출력. 46개의 값을 더하면 1이 됨
# 모델 컴파일하기
model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
  • 카테고리컬 크로스엔트로피 손실함수는 두 확률 분포 사이의 거리를 측정
  • 여기선 네트워크가 출력한 확률 분포와 진짜 라벨의 분포 사이의 거리를 측정
  • 두 분포 사이의 거리를 좁힐수록 진짜 라벨에 가까운 출력을 내도록 훈련시킨다

훈련 검증

# 테스트셋에서 1000개 샘플을 따로 떼어내 검증셋 준비하기
x_val = x_train[:1000]
partial_x_train = x_train[1000:]
y_val = one_hot_train_labels[:1000]
partial_y_train = one_hot_train_labels[1000:]
# 모델 트레이닝(에포크 20번)
history = model.fit(partial_x_train,
partial_y_train,
epochs=20,
batch_size=512,
validation_data=(x_val, y_val))
# 훈련과 검증 손실
import matplotlib.pyplot as plt

loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(1, len(loss) + 1)

plt.plot(epochs, loss, 'bo', label='훈련 손실')
plt.plot(epochs, val_loss, 'b', label='검증 손실')
plt.title('훈련과 검증 손실')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()

# 훈련과 검증 정확도
plt.clf()

acc = history.history['acc']
val_acc = history.history['val_acc']

plt.plot(epochs, acc, 'bo', label='훈련 정확도')
plt.plot(epochs, val_acc, 'b', label='검증 정확도')
plt.title('훈련과 검증 정확도')
plt.xlabel('에포크')
plt.ylabel('정확도')
plt.legend()

plt.show()

  • 아홉 번째 에포크 이후 오버피팅이 시작됨. 에포크 9로 새로운 모델 훈련하고 테스트셋에서 평가
model = models.Sequential()
model.add(layers.Dense(64, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))

model.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
model.fit(partial_x_train,
partial_y_train,
epochs=9,
batch_size=512,
validation_data=(x_val, y_val))
results = model.evaluate(x_test, one_hot_test_labels)
results
# 78%의 정확도
[0.9839374910797907, 0.7858414960459524]
import copy

test_labels_copy = copy.copy(test_labels)
np.random.shuffle(test_labels_copy)
float(np.sum(np.array(test_labels) == np.array(test_labels_copy))) / len(test_labels)
0.19100623330365094
  • 무작위 분류시의 19%에 비하면 좋은 결과!

새로운 데이터에 대해 예측하기

# 테스트셋 예측하기
predictions = model.predict(x_test)
  • predictions
    • 각 항목은 길이가 46인 벡터
    • 이 벡터의 원소의 합은 1
    • 가장 큰 값이 가장 확률이 높은 클래스(np.argmax 사용)

레이블과 손실을 다루는 다른 방법

  • 라벨을 정수 텐서로 변환해서 인코딩 할때는 손실 함수 하나만 바꾸면 된다
    • loss=sparse_categorical_crossentropy

주택 가격 예측: 회귀 문제

보스턴 주택 가격 데이터셋

  • 506개 데이터 포인트: 트레이닝셋 404개, 테스트셋 102개
  • 각 피쳐는 스케일이 다름
from keras.datasets import boston_housing
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()

print(train_data.shape)
print(test_data.shape)
(404, 13)
(102, 13)

데이터 준비

  • 상이한 스케일을 가진 값을 신경망에 주입하면 문제(학습을 어렵게 만든다)
  • 특성별로 정규화를 해보자
  • 각 특성에 대해 특성의 평균을 빼고 표준편차로 나눔
# 데이터 정규화하기
mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std

test_data -= mean
test_data /= std

모델 구성

# 모델 정의하기
from keras import models
from keras import layers

def build_model():
model = models.Sequential()
model.add(layers.Dense(64, activation='relu',
input_shape=(train_data.shape[1],)))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(1))
model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
return model
  • 마지막 층은 선형층(하나의 유닛, 활성화 함수 없음)
    • 전형적인 스칼라 회귀(하나의 연속적인 값을 예측하는 회귀)를 위한 구성
    • sigmoid 활성화 함수를 적용하면 네트워크가 0과 1 사이의 값을 예측하도록 학습될 것. 여기선 선형이므로 범위 제한이 없음
  • mse(평균 제곱 오차) 손실 함수로 컴파일(예측과 타깃 사이 거리의 제곱)
  • 훈련하는 동안 mae(평균 절대 오차)를 측정(예측과 타깃 사이의 절대값)

K-겹 검증을 사용한 훈련 검증

  • 데이터셋이 작으면 트레이닝셋과 테스트셋으로 어떤 데이터가 선택됐는지에 따라 검증 점수가 크게 달라지는데 이럴 때 사용
  • 데이터를 K개로 나누고 K개의 모델을 각각 만들어 K-1개의 분할에서 훈련하고 나머지 분할에서 평가
import numpy as np

k = 4
num_val_samples = len(train_data) // k
num_epochs = 100
all_scores = []

for i in range(k):
print("처리중인 폴드 #", i)
# 검증 데이터 준비: k번째 분할
val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]

# 훈련 데이터 준비: 다른 분할 전체
partial_train_data = np.concatenate(
[train_data[:i * num_val_samples],
train_data[(i + 1) * num_val_samples:]], axis=0)
partial_train_targets = np.concatenate(
[train_targets[:i * num_val_samples],
train_targets[(i + 1) * num_val_samples:]], axis=0)

# 케라스 모델 구성(컴파일 포함)
model = build_model()

# 모델 훈련(verbose=0 이므로 훈련 과정 출력은 없다)
model.fit(partial_train_data, partial_train_targets,
epochs=num_epochs, batch_size=1, verbose=0)
val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
all_scores.append(val_mae)

print(all_scores)
print(np.mean(all_scores))
[2.0463592958922434, 2.3122981940165603, 3.0172314785494665, 2.323145497553419]
2.4247586165029222
  • 4가지 검증 점수는 2.04부터 3.01까지 편차가 크지만 평균값인 2.42는 이보다 신뢰할 만하다
  • 다음은 신경망을 500 에포크 동안 훈련
# 각 폴드에서 검증점수 로그에 저장하기
num_epochs = 500
all_mae_histories = []
for i in range(k):
print('처리중인 폴드 #', i)
val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
partial_train_data = np.concatenate(
[train_data[:i * num_val_samples],
train_data[(i + 1) * num_val_samples:]], axis=0)
partial_train_targets = np.concatenate(
[train_targets[:i * num_val_samples],
train_targets[(i + 1) * num_val_samples:]], axis=0)

# 케라스 모델 구성(컴파일 포함)
model = build_model()
hisgory = model.fit(partial_train_data, partial_train_targets,
validation_data=(val_data, val_targets),
epochs=num_epochs, batch_size=1, verbose=0)
mae_history = history.history['val_mean_absolute_error']
all_mae_histories.append(mae_history)

**

정리

  • 회귀는 손실함수로 평균제곱오차(MSE)를 자주 사용한다
  • 회귀는 평가 지표로 평균절대오차(MAE)를 자주 사용한다
  • 입력 피쳐들이 서로 다른 범위면 스케일을 하자
  • 트레이닝셋이 적다면 은닉층을 한 두개 정도만 사용하자

Deep Learning with Python - Ch.02

케라스 창시자에게 배우는 딥러닝을 실습하면서 정리한 포스트입니다. 코드 예제와 코드 설명은 역자 깃허브에서 받아볼 수 있습니다. 출판물이고 개인적으로만 참고하기 위한 요약노트이다 보니 설명이 불친절한 점은 양해 바랍니다. 보다 자세한 내용을 원하시는 분은 위 링크의 책을 참고하시기 바랍니다.


2장. 시작 전에: 신경망의 수학적 구성 요소

신경망과의 첫 만남

  • MNIST 예제
# 케라스에서 MNIST 데이터셋 불러오기
from keras.datasets import mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
# 신경망 구조
from keras import models
from keras import layers
network = models.Sequential()
network.add(layers.Dense(512, activation='relu', input_shape=(28 * 28,)))
network.add(layers.Dense(10, activation='softmax'))
  • 신경망의 핵심 구성 요소인 층(layer)은 일종의 데이터 처리 필터
  • 어떤 데이터가 들어가면 더 유용한 형태로 출력됨(층은 주어진 문제에 더 의미 있는 표현을 입력된 데이터로부터 추출함)
  • 딥러닝 모델은 데이터 정제 필터(층)가 연속되어 있는 데이터 프로세싱을 위한 여과기와 같음
  • 위 예시는 조밀하게 연결된 신경망 층인 Dense 층 2개가 연속된 구조의 신경망 구조
  • 두 번째 층은 10개의 확률 점수가 들어 있는 배열(모두 더하면 1)을 반환하는 소프트맥스 층
  • 각 점수는 현재 숫자 이미지가 10개의 숫자 클래스 중 하나에 속할 확률임
  • 신경망이 훈련 준비를 마치기 위해서 컴파일 단계에 포함될 세 가지가 더 필요
    • 손실 함수 : 훈련 데이터에서 신경망의 성능을 측정하는 방법. 네트워크가 옳은 방향으로 학습될 수 있도록 도움
    • 옵티마이저: 입력된 데이터와 손실 함수를 기반으로 네트워크를 업데이트하는 메커니즘입니다.
    • 훈련과 테스트 과정을 모니터링할 지표 : 여기에서는 정확도(정확히 분류된 이미지의 비율)만 고려하겠습니다.
# 컴파일 단계
network.compile(optimizer='rmsprop',
loss='categorical_crossentropy',
metrics=['accuracy'])
  • 트레이닝 시작 전에 데이터를 네트워크에 맞는 크기로 바꾸고 모든 값을 0과 1 사이로 스케일을 조정
  • MNIST 이미지는 [0, 255] 사이의 값인 unit8 타입의 (60000, 28, 28) 크기의 배열로 저장돼 있음. 이를 0과 1 사이 값을 가지는 float32 타입의 (60000, 28*28) 크기인 배열로 변경
  • categorical_crossentropy: 손실 함수. 가중치 텐서를 학습하기 위한 피드백 신호로 사용. 훈련하는 동안 최소화됨
  • rmsprop : 경사 하강법 적용 방식은 이 옵티마이저에 의해 결정
# 이미지 데이터 준비하기
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
# 레이블 준비_범주형으로 인코딩
from keras.utils import to_categorical
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
# fit 메서드로 트레이닝 데이터에 모델을 학습시킴
network.fit(train_images, train_labels, epochs=5, batch_size=128)
  • 네트워크가 128개 샘플씩 미니 배치로 훈련 데이터를 5번 반복
  • 5번의 에포크 동안 네트워크는 2345번의 그래디언트 업데이트를 수행(에포크당 469번)
  • 훈련 데이터에 대한 네트워크의 손실과 정확도 정보가 출력됨
Epoch 1/5
60000/60000 [==============================] - 3s 54us/step - loss: 0.2556 - acc: 0.9261
Epoch 2/5
60000/60000 [==============================] - 3s 51us/step - loss: 0.1042 - acc: 0.9688
Epoch 3/5
60000/60000 [==============================] - 3s 51us/step - loss: 0.0685 - acc: 0.9796
Epoch 4/5
60000/60000 [==============================] - 3s 52us/step - loss: 0.0499 - acc: 0.9845
Epoch 5/5
60000/60000 [==============================] - 3s 52us/step - loss: 0.0369 - acc: 0.9888
<keras.callbacks.History at 0x137a2e860>
# 테스트셋에서 모델이 잘 작동하는지 확인하기
test_loss, test_acc = network.evaluate(test_images, test_labels)
print('test_acc:', test_acc)
10000/10000 [==============================] - 0s 38us/step
test_acc: 0.9798

잘 작동한다!

  • 테스트셋의 정확도는 97.8%. 트레이닝셋 정확도와 차이가 나는 이유는 오버피팅 때문임

신경망을 위한 데이터 표현

  • 텐서 : 데이터(숫자)를 위한 컨테이너
  • 최근의 모든 머신러닝 시스템은 텐서를 기본 데이터 구조로 사용

스칼라(0D 텐서)

  • 하나의 숫자만 담고 있는 텐서
  • 넘파이에서는 float32나 float64 타입의 숫자가 스칼라 텐서
  • ndim 속성을 사용하면 넘파이 배열의 축 개수를 확인 가능
  • 스칼라 텐서의 축 개수는 0(ndim == 0)
  • 텐서의 축 개수를 랭크(rank)라고도 부름
# 스칼라 텐서(0D 텐서))
import numpy as np
x = np.array(12)
x, x.ndim
(array(12), 0)

벡터(1D 텐서)

  • 숫자의 배열
  • 하나의 축을 가짐
# 벡터(1D 텐서)
x = np.array([12, 3, 6, 14, 7])
x, x.ndim
(array([12,  3,  6, 14,  7]), 1)
  • 위 예에서 x는 5개의 원소를 가지고 있으므로 5차원 벡터
    • 5D 벡터: 하나의 축을 따라 5개의 차원을 가진 것
    • 5D 텐서: 5개의 축을 가진 것
  • 차원수(dimensionality)는 특정 축을 따라 놓인 원소의 개수(5D 벡터와 같은 경우)이거나 텐서의 축 개수(5D 텐서와 같은 경우)이거나 텐ㅅ의 축 개수(5D 텐서와 같은 경우)를 의미하므로 가끔 혼동하기 쉽다
  • 후자의 경우 랭크 5인 텐서라고 말하는게 보다 정확(텐서의 랭크가 축의 개수)

행렬(2D 텐서)

  • 벡터의 배열이 행렬 또는 2D 텐서
  • 행렬에는 행과 열 2개의 축이 있음
x = np.array([[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 36, 2]])
x.ndim
2

3D 텐서와 고차원 텐서

  • 행렬들을 하나의 새로운 배열로 합치면 숫자로 채워진 직육면체 형태인 3D 텐서
# 3D 텐서
x = np.array([[[2, 45, 2, 34, 0],
[5, 34, 5, 36, 1],
[7, 80, 4, 36, 2]],
[[5, 78, 2, 34, 0],
[6, 79, 3, 35, 1],
[7, 80, 4, 37, 2]],
[[5, 89, 3, 35, 1],
[7, 80, 4, 36, 2],
[8, 72, 2, 40, 5]]])
x.ndim
3
  • 3D 텐서들을 하나의 배열로 합치면 4D 텐서
  • 딥러닝에선 보통 0D에서 4D까지의 텐서를 다룬다
  • 동영상 데이터를 다룰 때는 5D 텐서까지 가기도

핵심 속성

  • 텐서의 핵심 속성 3가지
    • 축의 개수(rank) : 3D 텐서에는 3개의 축, 행렬에는 2개의 축
    • 크기(shape) : 텐서의 각 축을 따라 얼마나 많은 차원이 있는지 나타낸 파이썬의 튜플.
    • 데이터 타입 : float32, float64. uint8 등

배치 데이터

  • 일반적으로 데이터 텐서의 첫 번째 축(0번 축)은 샘플 축(MNIST 예제에서는 숫자 이미지가 샘플)
  • 딥러닝 모델은 데이터를 작은 batch로 나눠서 처리
  • 이런 batch 데이터를 다룰 땐 0번 축을 배치 축 또는 배치 차원이라 부른다

텐서의 실제 사례

  • 벡터 데이터
    • (샘플들, 피처들) 크기의 2D 텐서
    • ex) 사람의 나이, 우편번호, 소득으로 구성된 인구 통계 데이터. 각 사람은 3개의 값을 가진 벡터로 구성. 10만명이 포함된 전체 데이터셋은 (100000, 3) 크기의 텐서에 저장
  • 시계열 데이터
    • (샘플들, timsteps, 피처들) 크기의 3D 텐서
    • 시간이 중요할 때는 시간 축을 포함해 3D 텐서로 저장
    • 관례적으로 시간 축은 항상 1번(두 번째) 축
    • ex) 주식 가격 데이터셋. 1분마다 현재 주식 가격, 지난 1분 동안 최고가와 최소가를 저장. 1분마다 데이터는 3D 벡터로 인코딩. 하루(390분) 거래는 (390, 3) 크기의 2D 텐서로 인코딩. 250일치 데이터는 (250, 390, 3) 크기의 3D 텐서로 저장될 수 있음.
  • 이미지
    • (샘플들, 높이, 너비, 컬러 채널) 크기의 4D 텐서
    • ex) 256 x 256 크기의 흑백 이미지에 대한 128개의 배치는 (128, 256, 256, 1) 크기의 텐서에 저장 가능. 컬러라면 (128, 256, 256, 3)
  • 동영상
    • (samples, frames, height, width, channels) 크기의 5D 텐서
    • ex) 60초짜리 144 x 256 유튜브 영상을 초당 4프레임으로 샘플링하면 240프레임. 이 영상을 4개 가진 배치는 (4, 240, 144, 256, 3) 크기의 텐서에 저장.

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

  • 케라스 층 생성하기
    • keras.layers.Dense(512, activation='relu')
    • 2D 텐서를 입력받고 입력 텐서의 새로운 표현인 또 다른 2D 텐서를 반환하는 함수로 볼 수 있음
    • output = relu(dot(W, input) + b) 함수와 같음
    • 이 함수에는 3개의 텐서 연산이 있음. 입력 텐서와 W의 dot, dot의 결과인 2D 텐서와 벡터 b 사이의 덧셈, relu 연산

원소별 연산

  • relu 함수와 덧셈은 원소별 연산.
  • 각 원소에 독립적으로 적용됨
# 파이썬으로 구현한 원소별 연산_relu
def naive_relu(x):
assert len((x.shape) == 2)

x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] = max(x[i, j], 0)
return x

# 파이썬으로 구현한 원소별 연산_덧셈
def naive_add(x, y):
assert len(x.shape) == 2
assert x.shape == y.shape

x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] += y[i, j]
return x

브로드캐스팅

  • 크기가 다른 두 텐서가 더해진다면? 실행 가능하다면 작은 텐서가 큰 텐서 크기에 맞춰 브로드캐스팅됨
    1. 큰 텐서의 ndim에 맞게 작은 텐서에 브로드캐스팅 축이 추가됨
    2. 작은 텐서가 새 축을 따라서 큰 텐서의 크기에 맞도록 반복됨
  • ex) X.shape = (32, 10), y.shape = (10,)
    1. y에 비어 있는 축을 추가해 (1, 10)으로
    2. y를 이 축에 32번 반복하면 텐서 Y.shape는 (32, 10)
    3. 크기가 같아져서 더할 수 있음
# 단순 구현
def naive_add_matrix_and_vector(x, y):
assert len(x.shape) == 2
assert len(y.shape) == 1
assert x.shape[1] == y.shape[0]

x = x.copy()
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i, j] += y[j]
return x
# 크기가 다른 두 텐서에 브로드캐스팅으로 원소별 maximum 연산 적용
import numpy as np
x = np.random.random((64, 3, 32, 10))
y = np.random.random((32, 10))

z = np.maximum(x, y)

tensor product

  • 원소별 연산과 반대로 입력 텐서의 원소들을 결합시킴
# 점곱 연산
def naive_vector_dot(x, y):
assert len(x.shape) == 1
assert len(y.shape) == 1
assert x.shape[0] == y.shape[0]

z = 0.
for i in range(x.shape[0]):
z += x[i] * y[i]
return z
# 행렬 x와 벡터 y의 점곱
def naive_matrix_vector_dot(x, y):
assert len(x.shape) == 2
assert len(y.shape) == 1
assert x.shape[1] == y.shape[0]

z = np.zeros(x.shape[0])

for i in range(x.shape[0]):
for j in range(x.shape[1]):
z[i] += x[i, j] * y[j]
return z

텐서 크기 변환(tensor reshaping)

  • 특정 크기에 맞게 열과 행을 재배열
  • 행과 열을 바꾸는 전치도 자주 사용

텐서 연산의 기하학적 해석

  • 아핀 변환, 회전, 스케일링 등 기본적인 기하학적 연산은 텐선으로 표현 가능

딥러닝의 기하학적 해석

  • 빨간색 파란색 2장의 색종이를 겹친 다음 뭉쳐서 작은 공을 만든다고 가정
  • 종이공: 입력 데이터, 색종이: 분류 문제의 데이터 클래스
  • 신경망의 역할: 종이 공을 펼쳐서 두 클래스가 분리되는 변환을 찾는 것

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

output = relu(dot(W, input) + b)

  • 텐서 W와 b는 층의 속성. 가중치 또는 훈련되는 파라미터.
  • 이런 가중치에는 훈련 데이터를 신경망에 노출시켜 학습된 정보가 담겨 있음
  • 초기에는 가중치 행렬이 작은 난수로 채워짐(무작위 초기화 단계)
  • 피드백 신호에 따라 가중치가 점진적으로 조정됨(훈련 단계)
  • 훈련 반복 루프
    1. 트레이닝 샘플 x와 타깃 y의 배치를 추출
    2. x를 사용해 네트워크를 실행(forward pass 단계)하고 y_pred 구하기
    3. y와 y_pred 차이를 측정해 이 배치에 대한 네트워크 손실 계산
    4. 배치에 대한 손실이 감소되도록 네트워크 가중치 업데이트
  • 이 접근법은 모든 가중치 행렬의 원소바다 두 번의 forward pass를 계산해야 하므로 비효율적
  • 신경망에 사용된 연산이 미분 가능하다는 점을 이용해 네트워크 가중치에 대한 손실의 그래디언트를 계산하는 게 더 좋은 방법

변화율

  • derivative!

텐서 연산의 변화율: 그래디언트

  • 다차원 입력, 즉 텐서를 입력으로 받는 함수에 변화율 개념을 확장시킨 것
  • y_pred = dot(W, x)
  • loss_value = loss(y_pred, y)
  • 입력 데이터 x와 y가 고정돼 있다면 이 함수는 W를 손실 값에 매핑하는 함수로 볼 수 있음
  • loss_value = f(W)
  • W0(W의 현재값)에서 f의 변화율: W와 같은 크기의 텐서인 gradient(f)(W0)
  • 이 텐서의 각 원소 gradient(f)(W0)[i, j]: W0[i, j]를 변경했을 때 loss_value가 바뀌는 방향과 크기를 나타냄
  • 즉 gradient(f)(W0)가 W0에서 함수 f(W) = loss_value의 그래디언트
  • gradient(f)(W0)는 W0에서 f(W)의 기울기를 나타내는 텐서로 해석 가능
  • f(W)에서 그래디언트의 반대 방향으로 W를 움직이면 f(W) 값을 줄일 수 있음

확률적 경사 하강법

  • 절충안: 미니 배치 확률적 경사 하강법(미니 배치 SGD)
    1. 훈련샘플 배치 x와 타깃 y를 추출
    2. x로 네트워크를 실행하고 y_pred 구하기
    3. y와 y_pred 사이의 오차를 측정해 네트워크 손실 계산
    4. 네트워크의 파라미터에 대한 손실 함수의 그래디언트 계산(backward pass)
    5. 그래디언트 반대 방향으로 파라미터 이동
  • 트루 SGD
    • 반복마다 하나의 샘플과 하나의 타깃을 뽑음
  • 배치 SGD
    • 가용한 모든 데이터로 반복 실행
    • 업데이트가 정확하지만 많은 비용
  • SGD 변종들
    • 업데이트할 다음 가중치를 계산할 때 현재 그래디언트 값만 보지 않고 이전에 업데이트된 가중치를 여러 가지 다른 방식으로 고려
    • ex) 모멘텀을 사용한 SGD, Adagrad, RMSProp 등
    • 이런 변종들을 최적화 방법 or 옵티마이저라 부름
    • 모멘텀은 SGD에 있는 2개의 문제점인 수렴 속도와 지역 최솟값을 해결
# 모멘텀 단순 구현
past_velocity = 0.
momentum = 0.1
while loss > 0.1:
w, loss, gradient = get_current_parameters()
velocity = momentum * past_velocity - learning_rate * gradient
w = w + momentum * velocity - learning_rate * gradient
past_velocity = velocity
update_parameter(w)

변화율 연결: 역전파 알고리즘

  • 3개의 텐서 연산 a, b, c와 가중치 행렬 W1, W2, W3로 구성된 네트워크 f의 예
  • f(W1, W2, W3) = a(W1, b(W2, c(W3)))
  • 연쇄법칙을 신경망 그래디언트 계산에 적용한 역전파 알고리즘
© 2019 THE DATASCIENTIST All Rights Reserved.
Theme by hiero