5 분 소요

본 글은 [케라스 창시자에게 배우는 딥러닝] - (박해선 옮김) 책을 개인공부하기 위해 요약, 정리한 내용입니다.
전체 코드는 https://github.com/ingu627/deep-learning-with-python-notebooks에 기재했습니다.(원본 코드 fork)
뿐만 아니라 책에서 설명이 부족하거나 이해가 안 되는 것들은 외부 자료들을 토대로 메꿔 놓았습니다. 즉, 딥러닝은 이걸로 끝을 내보는 겁니다.
오타나 오류는 알려주시길 바라며, 도움이 되길 바랍니다.





6_1. 텍스트 데이터 다루기.

  • 시퀀스 데이터를 처리하는 기본적인 딥러닝 모델은 순환 신경망 (recurrent neural network)1D (1D convnet) 두 가지이다.
  • 텍스트는 가장 흔한 시퀀스 형태의 데이터이다. 단어의 시퀀스나 문자의 시퀀스로 이해할 수 있다.
  • 자연어 처리를 위한 딥러닝은 단어, 문장, 문단에 적용한 패턴 인식이다.
  • 딥러닝 모델은 수치형 텐서만 다룰 수 있다.
  • 텍스트 벡터화 (vectorrizing text) : 텍스트를 수치형 텐서로 변환하는 과정
    • 텍스트를 단어로 나누고 각 단어를 하나의 벡터로 변환한다.
    • 텍스트를 문자로 나누고 각 문자를 하나의 벡터로 변환한다.
    • 텍스트에서 단어나 문자의 n-그램을 추출하여 각 n-그램을 하나의 벡터로 변환한다.
      • n-gram : 연속된 단어나 문자의 그룹으로 텍스트에서 단어나 문자를 하나씩 이동하면서 추출
  • 토큰 (token) : 텍스트를 나누는 단위(단어, 문자, n-그램)
  • 토큰화 (tokenization) : 텍스트를 토큰으로 나누는 작업
  • 모든 텍스트 벡터화 과정은 어떤 종류의 토관화를 적용하고 생성된 토큰에 수치형 벡터를 연결하는 것으로 이루어진다.
  • 이런 벡터는 시퀀스 텐서로 묶여져서 심층 신경망에 주입된다.
  • 토큰과 벡터를 연결하는 방법
    • 원-핫 인코딩 (one-hot encoding)
    • 토큰 임베딩 (token embedding)



n-그램과 BoW

  • 단어 n-그램 : 문장에서 추출한 N개의 연속된 단어 그룹.
  • 가방 (bag) : 다루고자 하는 것이 리스트나 시퀀스가 아니라 토큰의 집합을 의미. 특정한 순서가 없는데 이런 종류의 토큰화 방법을 BoW



6_1_1. 단어와 문자의 원-핫 인코딩

  • 원-핫 인코딩 : 모든 단어에 고유한 정수 인덱스를 부여하고 이 정수 인덱스 i를 크기가 N(어휘 사전의 크기)인 이진 벡터로 변환한다.
    • 이 벡터는 i번째 원소만 1이고 나머지는 모두 0이다.



단어 수준의 원-핫 인코딩하기



문자 수준 원-핫 인코딩하기



케라스를 사용한 단어 수준의 원-핫 인코딩하기

image

image

  • Tokenizer : 문장으로부터 단어를 토큰화하고 숫자에 대응시키는 딕셔너리를 사용할 수 있도록 한다. -fit_on_texts : 문자 데이터를 입력받아서 리스트의 형태로 변환한다.
  • word_index : tokenizer의 word_index 속성은 단어와 숫자의 키-값 쌍을 포함하는 딕셔너리를 반환한다.

image

  • texts_to_sequences() : 텍스트 안의 단어들을 숫자의 시퀀스의 형태로 변환
  • texts_to_matrix() : 텍스트를 시퀀스 리스트로 바꾸어 주는 texts_to_sequences() 메서드와 시퀀스 리스트를 넘파이 배열로 바꾸어 주는 sequences_to_matrix() 메서드를 차례대로 호출한다.
    • mode 매개변수에서 지원하는 값은 기본값 binary외에 count(단어의 출현 횟수), freq(출현 횟수를 전체 시퀀스의 길이로 나누어 정규화), tfidf(TF-IDF)가 있다.



해싱 기법을 사용한 단어 수준의 원-핫 인코딩하기

  • 이 방식은 어휘 사전에 있는 고유한 토큰의 수가 너무 커서 모두 다루기 어려울 때 사용한다.
  • 각 단어에 명시적으로 인덱스를 할당하고 이 인덱스를 딕셔너리에 저장하는 대신에 단어를 해싱하여 고정된 크기의 벡터로 변환한다.



6_1_2. 단어 임베딩 사용하기

  • 단어 임베딩 == 밀집 단어 벡터
  • 원-핫 인코딩으로 만든 벡터는 희소(sparse)하고(대부분 0으로 채워진다) 고차원이다.(어휘 사전에 있는 단어의 수와 차원이 같다.)
  • 반면에 단어 임베딩은 저차원의 실수형 벡터이다.(희소 벡터의 반대인 밀집 벡터이다.)



Embedding 층을 사용하여 단어 임베딩 학습하기

  • 단어 임베딩은 언어를 기하학적 공간에 매핑하는 것이다.

image

  • 단어 임베딩 공간은 전형적으로 이런 해석 가능하고 잠재적으로 유용한 수천 개의 벡터를 특성으로 가진다.
  • Embedding Layer(임베딩 층) : 가지고 있는 훈련 데이터 -> 처음부터 학습(Embedding)s



Embedding 층의 객체 생성하기

  • Embedding 층은 적어도 2개의 매개변수를 받는다. 가능한 토큰의 개수(여기서는 1000으로 단어 인덱스 최댓값 +1)와 임베딩 차원이다.
  • Embedding 층을 (특정 단어를 나타내는) 정수 인덱스를 밀집 벡터로 매핑하는 딕셔너리로 이해하면 된다.
    • 정수를 입력으로 받아 내부 딕셔너리에서 이 정수에 연관된 벡터를 찾아 반환한다.
  • 단어 인덱스 -> Embedding 층 -> 연관된 단어 벡터



원리

  • Embedding 층은 크기가 (samples, sequence_length)인 2D 정수 텐서를 입력으로 받는다. 각 샘플은 정수의 시퀀스이다. 가변 길이의 시퀀스를 임베딩할 수 있다.
    • 예를 들어 Embedding 층에 (32, 10) 크기의 배치(=길이가 10인 시퀀스 32개로 이루어진 배치)나 (64, 15) 크기의 배치(=길이가 15인 시퀀스 64개로 이루어진 배치)를 주입할 수 있다.
    • 배치에 있는 모든 시퀀스는 길이가 같아야 하므로(하나의 텐서에 담아야 하기 때문에) 작은 길이의 시퀀스는 0으로 패딩되고 길이가 더 긴 시퀀스는 잘린다.
  • Embedding 층은 크기가 (samples, sequence_length, embedding_dimensionality)인 3D 실수형 텐서를 반환한다.
    • 이런 텐서는 RNN 층이나 1D 합성곱 층에서 처리된다.
  • Embedding 층의 객체를 생성할 때 가중치(토큰 벡터를 위한 내부 딕셔너리)는 다른 층과 마찬가지로 랜덤하게 초기화된다.
    • 훈련하면서 이 단어 벡터는 역전파를 통해 점차 조정되어 이어지는 모델이 사용할 수 있도록 임베딩 공간을 구성한다.
    • 훈련이 끝나면 임베딩 공간은 특정 문제에 특화된 구조를 많이 가지게 된다.
  • Embedding(단어종류개수, 임베딩 벡터 차원, 문서(최대) 길이)



IMDB 영화 리뷰 감성 예측 문제 적용해보기

  • 영화 리뷰에서 가장 빈도가 높은 1만 개의 단어를 추출하고 리뷰에서 20개가 넘는 단어는 버린다.
  • 이 네트워크는 1만 개의 단어에 대해 8차원의 임베딩을 학습하여 정수 시퀀스 입력(2D 정수 텐서)을 임베딩 시퀀스(3D 실수형 텐서)로 바꾼다.
  • 그 다음 이 텐서를 2D로 펼쳐서 분류를 위한 Dense 층을 훈련한다.

image

  • pad_sequences : 서로 다른 개수의 단어로 이루어진 문장을 같은 길이로 만들어주기 위해 패딩을 사용한다. 이 함수에 이 시퀀스를 입력하면 숫자 0을 이용해서 같은 길이의 시퀀스로 변환한다.
    • padding : model에 데이터를 정형화하여 넣어줘야하기 때문에 최대 길이를 정하고, 이에 못미치는 굉장히 짧은 문장에 대해서는 0이라는 값으로 채워주며, 최대 길이를 넘어가는 문장에 대해서는 잘라내버리는 작업
    • 옵션 값들
    • truncating= post/pre: 문장의 길이가 maxlen보다 길 때, 뒷 / 앞 부분을 잘라준다.
    • padding= post/pre: 문장의 길이가 maxlen보다 길 때, 뒷 / 앗 부분을 잘라준다.
    • maxlen : 최대 문장 길이 정의
  • Embedding(10000, 8, input_length=maxlen) : 10000개의 개수(10000개의 단어 종류가 있다)를 입력 받아 (예를 들어 [0,1,0,0,…0]) -> 신경망(임베딩) 학습 -> 8차원 임베딩 벡터(출력의 차원)-> input_length는 한 번에 학습하고자 하는 문장의 길이를 의미. maxlen는 각 문서에 대해 패딩을 수행한 이후 최대 문서의 길이
    • 다시 해석: vocab_size=10000으로 정의되어 있기 때문에 단어들은 10000차원 공간안에 정의되어 있다고 볼 수 있다. 이를 8차원으로 내려 Data Sparsity를 해결하고 효율적으로 학습할 수 있도록 한다.

image



6_1_3. 모든 내용을 적용하기: 원본 텍스트에서 단어 임베딩까지

IMDB 원본 데이터 전처리하기



데이터 토큰화

  • label을 np.array로 변환 : list 타입은 허용하지 않기 때문에, labels를 np.array로 변환



GloVe 단어 임베딩 내려받기

image



GloVe 단어 임베딩 파일 파싱하기



GloVe 단어 임베딩 행렬 준비하기



모델 정의하기



사전 훈련된 단어 임베딩을 Embedding 층에 로드하기

  • Embedding 층은 하나의 가중치 행렬을 가진다. 이 행렬은 2D 부동 소수 행렬이고 각 i번째 원소는 i번째 인덱스에 상응하는 단어 벡터이다.



훈련과 평가하기



결과 그래프 그리기

image



6_2. 순환 신경망 이해하기

RNN

  • 순환 신경망(Recurrent Neural Network, RNN)은 입력과 출력을 시퀀스 단위로 처리하는 모델이다.
  • 시퀀스의 원소를 순회하면서 지금까지 처리한 정보를 상태(state)에 저장한다.
  • RNN의 상태는 2개의 다른 시퀀스를 처리하는 사이에 재설정된다.
  • 하나의 시퀀스가 여전히 하나의 데이터 포인트로 간주된다.
  • 또 다른 정의로는 히든 노드가 방향을 가진 엣지로 연결돼 순환구조를 이루는(directed cycle) 인공신경망의 한 종류이다.
  • 시퀀스 길이에 관계없이 인풋과 아웃풋을 받아들일 수 있는 네트워크 구조이기 때문에 필요에 따라 다양하고 유연하게 구조를 만들 수 있다는 점이 RNN의 가장 큰 장점이다.

용어는 비슷하지만 순환 신경망과 재귀 신경망(Recursive Neural Network)은 전혀 다른 개념이다.

image



예시

image



의사코드로 표현한 RNN

해석

  • 이 RNN은 크기가 (timesteps, input_features)인 2D 텐서로 인코딩된 벡터의 시퀀스를 입력받는다.
  • 이 시퀀스는 타임스텝을 따라서 반복한다.
  • 각 타임스텝 t에서 현재 상태와 (input_features,) 크기의 입력을 연결하여 출력을 계산한다.
  • 그다음 이 출력을 다음 스텝의 상태로 설정한다.
  • f 함수는 입력과 상태를 출력으로 변환한다.



넘파이로 구현한 간단한 RNN



6_2_1. 케라스의 순환 층

  • 넘파이로 간단하게 구현한 과정이 실제 케라스의 SimpleRNN 층에 해당한다.
  • SimpleRNN은 (batch_size, timesteps, input_features) 크기의 입력을 받는다.
  • 케라스에 있는 모든 순환 층과 마찬가지로 SimpleRNN은 두 가지 모드로 실행할 수 있다.
    • 1. 각 타임스텝의 출력을 모은 전체 시퀀스를 반환(크기가 (batch_size, timesteps, output_features)인 3D 텐서)
      • return_sequences=True
    • 2. 입력 시쿤스에 대한 마지막 출력만 반환 (크기가 (batch_size, output_features))
  • 이 모드는 객체를 생성할 때 return_sequences 매개변수로 선택할 수 있다.



IMDB 데이터 전처리하기



Embedding 층과 SimpleRNN 층을 사용한 모델 훈련하기



결과 그래프 그리기



6_2_2. LSTM과 GRU 층 이해하기

  • SimpleRNN은 이론적으로 시간 t에서 이전의 모든 타임스텝의 정보를 유지할 수 있었다.
  • 하지만 실제로는 긴 시간에 걸친 의존성은 학습할 수 없는 것이 문제이다.
    • 층이 많은 일반 네트워크에서 나타는 것과 비슷한 현상인 그래디언트 소실 문제 때문이다.
      • 그래디언트 소실 문제 : 관련 정보와 그 정보를 사용하는 지점 사이 거리가 멀 경우 역전파시 그래디언트가 점차 줄어 학습능력이 크게 저하되는 것
  • LSTM은 RNN의 히든 state에 cell-state를 추가한 구조이다. (정보를 여러 타임스텝에 걸쳐 나르는 방법이 추가된다.)
  • 나중을 위해 정보를 컨베이어 벨터 의에 정보를 저장함으로써 처리 과정에서 오래된 시그널이 점차 소실되는 것을 막아 준다.
  • LSTM 셀의 역할은 과거 정보를 나중에 다시 주입하여 그래디언트 소실 문제를 해결하는 것이다.



케라스에서 LSTM 층 사용하기


LSTM 구조

  • SimpleRNN 코드
from tensorflow.keras.layers import Embedding, SimpleRNN, Dense
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(max_features, 32))
model.add(SimpleRNN(32))
model.add(Dense(1, activation='sigmoid'))


  • LSTM 코드
from tensorflow.keras.layers import Embedding, LSTM, Dense
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(max_features, 32))
model.add(LSTM(32))
model.add(Dense(1, activation='sigmoid'))


image

  • GRU 코드
from tensorflow.keras.layers import Embedding, GRU, Dense
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(max_features, 32))
model.add(GRU(32))
model.add(Dense(1, activation='sigmoid'))



References

댓글남기기