02-01. Tokenization

  • Corpus에서 token이라 불리는 단위로 나누는 작업
  • 단어 토큰화에서 단순히 구두점이나 특수문자를 제거하는 것은 의미의 손실을 발생시킬 수 있기 때문에,
    사용자의 목적과 일치하는 토큰화 도구를 사용할 필요가 있음
  • 구두점이나 특수 문자가 필요한 경우: Ph.D, AT&T, $45.55, 01/02/06 등
  • 줄임말과 단어 내에 띄어쓰기가 있는 경우: what’re/what are, New York, rock ’n’ roll 등
  • 문장 토큰화에서 단순히 마침표를 기준으로 문장을 잘라내는 것은 192.168.56.31, gmail.com과 같은 경우를 고려했을 때 올바르지 않음

한국어 토큰화

  • 한국어의 경우 띄어쓰기가 가능한 단위가 어절인데,
    ‘그가’, ‘그에게’, ‘그를’과 같이 어절이 독립적인 단어로 구성되는 것이 아니라
    조사 등의 무언가가 붙어있는 경우가 많기 때문에 이를 전부 형태소 단위로 분리해줘야 함
  • 자립 형태소: 접사, 어미, 조사와 상관업싱 자립하여 사용할 수 있는 형태소, [체언, 수식언, 감탄사] 등
  • 의존 형태소: 다른 형태소와 결합하여 사용되는 형태소, [접사, 어미, 조사, 어간]
  • 한국어의 경우 띄어쓰기가 지켜지지 않아도 글을 쉽게 이해할 수 있어 띄어쓰기가 잘 지켜지지 않음
  • 품사 태깅: 단어의 의미를 제대로 파악하기 위해서는 해당 단어가 어떤 품사로 쓰였는지 구분할 필요

NLTK, KoNLPy

1
2
from nltk.tokenize import word_tokenize # 단어 토큰화
from nltk.tag import pos_tag # 품사 태깅
1
2
3
4
from konlpy.tag import Okt
okt = Okt()
okt.porphs(sentence) # 형태소 추출
okt.pos(sentence) # 품사 태깅

02-02. Cleaning and Normalization

  • Cleaning(정제): 갖고 있는 corpus로부터 노이즈 데이터를 제거
  • Normalization(정규화): 표현 방법이 다른 단어들을 통합시켜서 같은 단어로 만듦
  • 영어권 언어에서 단어의 개수를 줄이는 정규화 방법으로 대,소문자 통합을 활용
  • 노이즈 데이터: 아무 의미 없는 특수 문자 등, 분석하고자 하는 목적에 맞지 않는 불필요한 단어들
  • 불필요한 단어를 제거하기 위해 불용어, 등장 빈도가 적은 단어, 길이가 짧은 단어 등을 제거
  • 노이즈 데이터의 특징을 잡아낼 수 있다면, 정규표현식을 사용해서 제거

02-03. Stemming and Lemmatization

Lemmatization

  • Lemma(표제어): 기본 사전형 단어, [am, are, is]의 뿌리 단어 be 등
  • Stem(어간): 단어의 의미를 담고 있는 단어의 핵심 부분, ‘cats’에서 ‘cat’
  • Affix(접사): 단어에 추가적인 의미를 주는 부분, ‘cats’에서 ’s’
1
2
3
4
from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
lemmatizer.lemmatize(word)
# am -> be, having -> have

Stemming

1
2
3
4
from nltk.stem import PorterStemmer
stemmer = PorterStemmer()
stemmer.stem(word)
# am -> am, having -> hav

한국어에서의 어간 추출

  • 5언 9품사의 구조에서 용언에 해당되는 동사와 형용사는 어간과 어미의 결합으로 구성
  • 활용: 용언의 어간이 어미를 가지는 일
  • 규칙 활용: 어간이 어미를 취할 때 어간의 모습이 일정, 잡/어간 + 다/어미
  • 불규칙 활용: 어간이 어미를 취할 때 어간의 모습이 바뀌거나 특수한 어미일 경우, ‘오르+아/어->올라’ 등

02-04. Stopword

  • Stopword(불용어): 문장에서는 자주 등장하지만 실제 의미 분석을 하는데는 기여하지 않는 단어, [조사, 접미사] 등
1
2
from nltk.corpus import stopwords
stop_words_list = stopwords.words('english')
1
2
3
okt = Okt()
okt.morphs(sentence) # 조사, 접속사 등 제거
# 또는 불용어 사전을 만들어서 제거

02-05. Regular Expression

02-06. Integer Encoding

  • 컴퓨터는 텍스트보다 숫자를 더 잘 처리할 수 있기 때문에 텍스트를 숫자로 변경
  • 단어를 빈도수 순으로 정렬하고 순서대로 낮은 숫자부터 정수를 부여
  • dictionary, Counter, nltk.FreqDist, keras.Tokenizer 등 활용
1
2
from nltk import FreqDist
FreqDist(np.hstack(preprocessed_sentences)) # np.hastack으로 문장 구분을 제거
1
2
3
4
5
6
from tensorflow.keras.preprocessing.text import Tokenizer
tokenizer = Tokenizer() # num_words 파라미터로 사용할 단어 개수 지정
tokenizer.fit_on_texts(preprocessed_sentences) # 빈도수 기분으로 단어 집합 생성
print(tokenizer.word_intex) # 정수 인덱스 확인
print(tokenizer.word_counts) # 단어 빈도수 확인
print(tokenizer.texts_to_sequences(preprocessed_sentences)) # corpus를 인덱스로 변환

02-07. Padding

  • 병렬 연산을 위해 여러 문장의 길이를 임의로 동일하게 맞춰주는 작업이 필요
1
2
3
4
5
6
from tensorflow.keras.preprocessing.sequence import pad_sequences
encoded = tokenizer.texts_to_sequences(preprocessed_sentences)
padded = pad_seqences(encoded)
# padding='post'를 입력해야 뒤에서 부터 0을 채움
# maxlen으로 문장 길이 조절
# truncating='post'를 통해 문장 길이 초과 시 뒤의 단어가 삭제되도록 설정

02-08. One-Hot Encoding

  • Vocabulary(단어 집합): 서로 다른 단어들의 집합, book과 books과 같은 변형 형태도 다른 단어로 간주
  • One-Hot Encoding: 단어 집합의 크기를 벡터의 차원으로 하고,
    표현하고 싶은 단어에 1, 다른 인텍스에 0을 부여하는 단어의 벡터 표현 방식
  • 단어의 개수가 늘어날 수록 벡터를 저장하기 위해 필요한 공간이 계속 늘어나는 단점
  • 단어의 유사도를 표현하지 못하는 단점 (강아지, 개, 냉장고 등)
1
2
3
from tensorflow.keras.utils import to_categorical
encoded = tokenizer.texts_to_sequences(preprocessed_sentences)[0]
one_hot = to_categorical(encoded)

03-01. Language Model

  • 단어 시퀀스(문장)에 확률을 할당하는 모델, 이전 단어들이 주어졌을 때 다음 단어를 예측
  • 단어 시퀀스 W의 확률 $P(W)=P(w_1,w_2,w_3,w_4,w_5,…,w_n)$
  • 다음 단어 등장 확률 $P(w_n|w_1,…,w_{n-1})$

03-02. Statistical Language Model

조건부 확률

남학생(A)여학생(B)
중학생(C)10060160
고등학생(D)80120200
180180360
  • 학생을 뽑았을 때, 고등학생이면서 남학생일 확률 $P(A \bigcap B)=80/360$
  • 고등학생 중 한명을 뽑았을 때, 남학생일 확률 $P(A|D)=P(A \bigcap D)/P(D)=(80/360)/(200/360)$

문장에 대한 확률

  • ‘An adorable little boy is spreading smiles’의 확률
    $P(\text{An adorable little boy is spreading smiles})=\ P(\text{An}) \times P(\text{adorable}|\text{An}) \times … \times P(\text{smiles}|\text{An adorable little boy is spreading})$

카운트 기반 접근

  • An adorable little boy가 100번 등장했을 때 그 다음에 is가 등장한 경우가 30번이라면,
    $P(\text{is}|\text{An adorable little boy})$는 30%
  • 카운트 기반으로 훈련할 경우 단어 시퀀스가 없어 확률이 0이 되는 경우를 방지하기 위해 방대한 양의 훈련 데이터가 필요
  • 회소 문제: 충분한 데이터를 관측하지 못하여 언어를 정확히 모델링하지 못하는 문제

$$P(\text{is}|\text{An adorable little boy})= \frac{count(\text{An adorable little boy is})}{count(\text{An adorable little boy})}$$

03-03. N-gram Language Model

  • 통계적 언어 모델의 일종이지만, 모든 단어가 아닌 일부 단어만 고려하는 접근 방법 사용
  • An adorable little boy에서 is가 나올 확률을 boy가 나왔을 때 is가 나올 확률로 대체
    $P(\text{is}|\text{An adorable little boy}) \approx P(\text{is}|\text{boy})$
  • 뒤의 단어 몇 개만 보다 보니 의도하고 싶은 대로 문장을 끝맺음하지 못하는 경우가 발생
  • 전체 문장을 고려한 언어 모델보다는 정확도가 떨어짐
  • 몇 개의 단어를 볼지 n을 정하는 것은 trade-off 문제를 발생시킴, n은 최대 5를 넘게 잡아서는 안된다고 권장

N-gram

  • n개의 연속적인 단어 나열
  • An adorable little boy에 대해
    unigrams: an, adorable, little, boy
    bigrams: an adorable, adorable little, little boy

03-05. Perplexity

  • Perplexity(PPL): 헷갈리는 정도, 낮을수록 언어 모델의 성능이 좋음

$$PPL(W)=P(w_1,w_2,w_3,…,w_N)^{-\frac{1}{N}}=\sqrt[N]{\frac{1}{P(w_1,w_2,w_3,…,w_N)}}$$

Branching Factor

  • Branching factor(분기계수): PPL이 선택할 수 있는 가능한 경우의 수
  • 대해 PPL이 10이 나왔을 때, 언어 모델은 테스트 데이터에 대해 다음 단어를 예측할 때 평균 10개의 단어를 고려
  • PPL의 값이 낮다는 것은 테스트 데이터 상에서 높은 정확도를 보이는 것일뿐, 반드시 사람이 직접 느끼기에 좋은 모델인 것은 아님

$$PPL(W)=P(w_1,w_2,w_3,…,w_N)^{-\frac{1}{N}}=(\frac{1}{10}^N)^{-\frac{1}{N}}=\frac{1}{10}^{-1}=10$$


04-01. 단어의 표현 방법

  • 국소 표현(이산 표현): 해당 단어 그 자체만 보고, 특정값을 맵핑하여 단어를 표현하는 방법
  • 분산 표현(연속 표현): 그 단어를 표현하고자 주변을 참고하여 단어를 표현하는 방법

04-02. Bag of Words(BoW)

  • 단어들의 순서는 고려하지 않고, 단어들의 출현 빈도에만 집중하는 텍스트 데이터 수치화 표현 방법
1
2
3
doc1 = '정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다.'
vocabulary : {'정부': 0, '가': 1, '발표': 2, '하는': 3, '물가상승률': 4, '과': 5, '소비자': 6, '느끼는': 7, '은': 8, '다르다': 9}
bag of words vector : [1, 2, 1, 1, 2, 1, 1, 1, 1, 1]
1
2
3
4
from sklearn.feature_extraction.text import CounteVectorizer
vector = CounterVectorizer() # stop_words 파라미터로 불용어 제거('english' 또는 리스트 등)
print('bag of words vector:', vector.fit_transform(corpus).toarray())
print('vocabulary:', vector.vocabulary_)

04-03. Document-Term Matrix(DTM)

  • 다수의 문서에서 등장하는 각 단어들의 빈도를 행렬로 표현한 것
  • One-hot vector와 마찬가지로 대부분의 값이 0인 희소 표현의 문제 발생
  • 불용어와 중요한 단어에 대해서 가중치를 주기 위해 TF-IDF를 사용

04-04. TF-IDF

  • 단어의 빈도와 역 문서 빈도를 사용하여 DTM 내의 각 단어들마다 중요도를 가중치로 부여하는 방법
  • $tf(d,t)$: 특정 문서 $d$에서의 특정 단어 $t$의 등장 횟수, DTM에서의 각 단어들의 가진 값
  • $df(t)$: 특정 단어 $t$가 등장한 문서의 수, 특정 단어가 각 문서에서 등장한 횟수는 무시
  • $idf(d,t)$: $df(t)$에 반비례하는 수,
    총 문서의 수 n이 커질수록 기하급수적으로 증가하는 것을 방지하기 위해 $log$(일반적으로 자연 로그) 적용

$$idf(d,t)=log(\frac{n}{1+df(t)})$$

1
2
3
from sklearn.feature_extraction.text import TfidfVectorizer
print(vector.fit_transform(corpus).toarray())
print(ve)

05. Vector Similarity

Cosine Similarity

  • 두 벡터 간의 코사인 각도를 이용하여 구할 수 있는 두 벡터의 유사도
  • 두 벡터의 방향이 동일하면 1의 값을 가지며, 값이 1에 가까울수록 유사도가 높음
  • 문서의 길이가 다른 상황에서 비교적 공정한 비교를 할 수 있음

Euclidean Distance

  • 다차원 공간에서 두 개의 점 $p$와 $q$가 각각 $p=(p_1,p_2,p_3,…,p_n)$과 $q=(q_1,q_2,q_3,…,q_n)$의 좌표를 가질 때
    두 점 사이의 거리를 계산하는 유클리드 거리 공식

$$\sqrt{(q_1-p_1)^2+(q_2-p_2)^2+…+(q_n-p_n)^2}=\sqrt{\Sigma^n_{i=1}(q_i-p_i)^2}$$

Jaccard Similarity

  • 합집합에서 교집합의 비율을 구한다면 두 집합 A와 B의 유사도를 구할 수 있음
  • 자카드 유사도 J는 0과 1사이의 값을 가지며, 두 집합이 동일하면 1, 공통 원소가 없으면 0의 값을 가짐

$$J(A,B)=\frac{|A \bigcap B|}{|A \bigcup B|}=\frac{|A \bigcap B|}{|A|+|B|-|A \bigcap B|}$$


06. Machine Learning

Classification and Regression

  • Bianry Classification: 두 개의 선택지 중 하나의 답을 선택
  • Multi-class Classification: 세 개 이상의 선택지 중에서 답을 선택
  • Regression: 연속적인 값의 범위 내에서 예측값을 도출

Learning

  • Supervised Learning: 정답 레이블과 함께 함습
  • Unsupervised Learning: 데이터에 별도의 레이블이 없이 학습
  • Self-Supervised Learning: 레이블이 없는 데이터가 주어지면, 모델이 학습을 위해 스스로 레이블을 생성

Confusion Matrix

예측 참예측 거짓
실제 참TP(정답)FN(오답)
실제 거짓FP(오답)TN(정답)
  • Precision(정밀도): True라고 분류한 것 중 실제 True의 비율, $Precision=\frac{TP}{TP+FP}$
  • Recall(재현율): 실제 True인 것 중에서 모델이 True라고 예측한 것의 비율, $Recall=\frac{TP}{TP+FN}$
  • Accuracy(정확도): 전체 예측한 데이터 중 정답을 맞춘 것에 대한 비율, $Accuracy=\frac{TP+TN}{TP+FN+FP+TN}$

Overfitting and Underfitting

  • Overfitting: 훈련 데이터를 과하게 학습, 훈련 데이터에 비해 테스트 데이터의 오차가 커짐
  • Underfitting: 테스트 데이터의 성능이 올라갈 여지가 있음에도 훈련을 덜 한 상태, 훈련 데이터에서도 정확도가 낮음

07. Deep Learning

Perceptron

  • 입력값 $x$, 가중치 $w$, 출력값 $y$
  • 가중치의 값이 크면 클수록 해당 입력 값이 중요하다는 것을 의미
  • 단층 퍼셉트론: 값을 보내는 input layer와 값을 받아서 출력하는 output layer로 구성
  • 다층 퍼셉트론(MLP): 입력층과 출력층 사이에 hidden layer를 추가

FFNN

  • FFNN(피드 포워드 신경망): 오직 입력층에서 출력층 방향으로 연산이 전개되는 신경망
  • RNN(순환 신경망): 은닉층의 출력값이 다시 은닉층으로 입력되는 신경망

Activision Function

  • 은닉층과 출력층의 뉴런에서 출력값을 결정하는 함수
  • Step function, Sigmoid function, ReLU 등 비선형 함수의 특성

Loss Function

  • MSE: 연속형 변수 예측
  • Binary Cross-Entropy: 시그모이드 함수 출력
  • Categorical Cross-Entropy: 소프트맥스 함수 출력

Optimizer

  • Momentum: 경사 하강법에 모멘텀을 더해 Local Minimum에 빠지더라도 빠져나갈 수 있게 함
  • Adagrad: 각 매개변수에 서로 다른 학습률을 적용
  • RMSprop: Adagrad가 학습을 진행할수록 학습률이 지나치게 떨어지는 단점을 개선
  • Adam: RMSprop과 Momentum을 합친 듯한 방법, 방향과 학습률 두 가지를 모두 잡기 위한 방법

Overfitting 방지

  1. 데이터의 양을 늘리기
    데이터의 양이 적으면 데이터의 특정 패턴이나 노이즈까지 쉽게 암기해버림, Data Augmentation 활용
  2. 모델의 복잡도 줄이기
    인공 신경망의 복잡도는 은닉층의 수나 매개변수의 수 등으로 결정
  3. 가충치 규제 적용하기
    L1 규제(가중치의 절댓값 합계를 비용 함수에 추가), L2 규제(모든 가중치들의 제곱합을 비용 함수에 추가)
  4. Dropout
    학습 시에 인공 신경망이 특정 뉴런 또는 특정 조합에 너무 의존적이게 되는 것을 방지

기울기 소실

  • 기울기 소실: 역전파 과정에서 입력층으로 갈 수록 기울기가 점차적으로 작아지는 현상
  • 기울기 폭주: 기울기가 점차 커지다가 가중치들이 비정상적으로 큰 값이 되면서 발산되는 경우
  • Gradient Clipping: 기울기 폭주를 막기 위해 임계값을 넘지 않도록 값의 크기를 감소

Weight Initialization

  • Xavier Initialization: 균등 분포 또는 정규 분포로 초기화 할 때 두 가지 경우로 나뉨
  • He Initialization: Xavier 초기화와 다르게 다음 층의 뉴런의 수를 반영하지 않음

Batch Normalization

  • 내부 공변량 변화: 학습 과정에서 층 별로 입력 데이터 분포가 달라지는 현상
  • 배치 정규화: 한 번에 들어오는 배치 단위로 정규화하는 것
  • 배치 정규화는 추가 계산을 발생시켜 모델을 복잡하게 하기 때문에 예측 시 실행 시간이 느려지는 단점
  • 너무 작은 배치 크기에서는 잘 동작하지 않을 수 있기 때문에 미니 배치 크기에 의존적임
  • RNN은 각 시점마다 다른 통계치를 가지기 때문에 RNN에 적용하기 어려움

Keras API

  • Sequential API: 단순하게 층을 쌓는 방식, 다수의 입출력 및 층 간 연산을 구현하기 어려움
  • Functional API: 입력의 크기(shape)를 명시한 입력층을 모델의 앞단에 정의
  • Subclassing API: Functional API로도 구현할 수 없는 모델들도 구현 가능

texts_to_matrix()

  • tokenizer.texts_to_matrix(texts, mode='count'): DTM 생성
  • tokenizer.texts_to_matrix(texts, mode='binary'): DTM과 유사하지만 단어의 개수는 무시
  • tokenizer.texts_to_matrix(texts, mode='tfidf'): TF-IDF 행렬 생성
  • tokenizer.texts_to_matrix(texts, mode='freq'):
    각 문서에서의 단어 등장 횟수를 분자로, 문서의 크기를 분모로 하는 표현하는 방법

NNLM

  • 피드 포워드 신경망 언어 모델, 신경망 언어 모델의 시초로, RNNLM, BiLM 등으로 발전
  • 기존 N-gram 언어 모델은 충분한 데이터를 관측하지 못하면 언어를 정확히 모델링하지 못하는 희소 문제를 가짐
  • NNLM은 N-gram 언어 모델처럼 정해진 개수(window size)의 단어만을 참고
  • NNLM은 N개의 input layer와 projection layer, hidden layer, output layer로 구성
  • Projection layer의 크기가 M일 때, 각 입력 단어들은 V x M 크기의 가중치 행렬과 곱해짐
  • 충분한 양의 훈련 코퍼스를 학습한다면 단어 간 유사도를 구할 수 있는 임베딩 벡터값을 얻을 수 있음

08-01. RNN

  • 입력과 출력을 시퀀스 단위로 처리하는 시퀀스 모델
  • 은닉층의 노드에서 활성화 함수를 통해 나온 결과값을 다시 은닉층 노드의 다음 계산 입력으로 보내는 특징
  • 셀(메모리 셀, RNN 셀): 은닉층에서 활성화 함수를 통해 결과를 내보내는 역할을 하는 노드, 이전의 값을 기억하는 역할
  • Hidden state: 현재 시점을 t라 할 때, 메모리 셀이 다음 시점인 t+1의 자신에게 보내는 값
  • 입력과 출력의 길이를 다르게 설계할 수 있어 다양한 용도로 사용 가능
  • one-to-many: 하나의 이미지 입력에 대해서 사진의 제목인 시퀀스를 출력하는 이미지 캡셔닝 작업에 사용
  • many-to-one: 단어 시퀀스에 대해서 하나의 출력을 하는 감성 분류, 스팸 메일 분류 등에 사용
  • many-to-many: 사용자가 문장을 입력하면 대답 문장을 출력하는 챗봇이나 번역기에 사용

RNN Parameter

  • 현재 시점 $t$에서의 hidden state가 $h_t$라 할 때, 두 개의 가중치 $W_x$, $W_h$가 필요
  • $W_x$는 입력층을 위한 가중치, $W_h$는 $t-1$의 hidden state인 $h_{t-1}$을 위한 가중치
  • 은닉층 $h_t=tanh({W_x}{x_t}+{W_h}{h_{t-1}}+b)$, 출력층 $y_t=f({W_y}{h_t}+b)$
  • 출력층의 활성화 함수 $f$는 이진 분류에서 시그모이드 함수, 다중 클래스 분류에서 소프트맥스 함수 등 사용
  • RNN의 입력 $x_t$는 단어 벤터로 간주, 단어 벡터의 차원을 $d$, hidden state의 크기를 $D_h$라 할 때,
    메모리 셀 $h_t$ = $tanh({W_h}\times{h_{t-1}}\times{W_x}\times{x_t}+{b})$
$x_t$$W_x$$W_h$$h_{t-1}$$b$
$({d}\times{1})$$({D_h}\times{d})$$({D_h}\times{D_h})$$({D_h}\times{1})$$({D_h}\times{1})$

Keras RNN

  • hidden_units: hidden state의 크기 (output_dim)
  • timesteps: 입력 시퀀스(문장)의 길이 (input_length)
  • input_dim: 입력의 크기, 단어 벡터의 차원
  • RNN 층은 (batch_size, timesteps, input_dim) 크기의 3D 텐서를 입력으로 받음
  • 메모리 셀의 최종 시점의 hidden state만 리턴할 경우 (batch_size, output_dim) 크기의 2D 텐서 반환
  • 메모리 셀의 각 시점(time step)의 hidden state 값들을 모아 전체 시퀀스를 리턴할 경우 3D 텐서를 반환
  • return_sequences=True 옵션으로 반환값 설정
1
2
3
4
from tensorflow.keras.layers import SimpleRNN

model = Sequential()
model.add(SimpleRNN(hidden_units, input_shape=(timesteps, input_dim)))

메모리 셀에서 hidden state 계산은 다음과 같은 코드로 동작

1
2
3
4
hidden_state_t = 0 # 초기 은닉 상태를 0(벡터)로 초기화
for input_t in input_length: # 각 시점마다 입력을 받는다.
    output_t = tanh(input_t, hidden_state_t) # 각 시점에 대해서 입력과 은닉 상태를 가지고 연산
    hidden_state_t = output_t # 계산 결과는 현재 시점의 은닉 상태가 된다.

Deep RNN

  • 순환 신경망에서 은닉층이 1개 더 추가되어 은닉층이 2개인 깊은 구조
  • 첫번째 은닉층은 다음 은닉층이 존재하기 때문에 return_sequences=True를 설정하여 모든 시점을 전달
1
2
3
4
5
from tensorflow.keras.layers import SimpleRNN

model = Sequential()
model.add(SimpleRNN(hidden_units, input_shape=(timesteps, input_dim), return_sequences=True))
model.add(SimpleRNN(hidden_units, return_sequences=True))

Bidirectional RNN

  • t에서의 출력값을 예측할 때 이전 시점의 입력 뿐 아니라, 이후 시점의 입력 또한 예측
  • 빈칸 채우기 등의 문제에서 미래 시점의 입력에 힌트가 있기 때문에 이전과 이후의 시점을 모두 고려
  • 하나의 출력값을 예측하기 위해 두 개의 메모리 셀을 사용
  • 첫 번째 메모리 셀은 앞 시점의 hidden state를 전달받아 현재의 hidden state를 계산 (forward)
  • 두 번째 메모리 셀은 뒤 시점의 hidden state를 전달받아 현재의 hidden state를 계산 (backward)
  • 은닉층을 추가하면 학습할 수 있는 양이 많아지지만, 훈련 데이터 또한 많은 양이 필요
1
2
3
4
from tensorflow.keras.layers import Bidirectional

model = Sequential()
model.add(Bidirectional(SimpleRNN(hidden_units, return_sequences=True), input_shape=(timesteps, input_dim)))

08-02. LSTM

  • Valina RNN(Simple RNN)은 출력 결과가 이전의 계산 결과에 의존하여 비교적 짧은 시퀀스에서면 효과를 봄
  • 장기 의존성 문제: time step이 길어질수록 앞의 정보가 뒤로 충분히 전달되지 못하는 현상
  • LSTM(장단기 메모리)은 은닉층의 메모리 셀에 입력 게이트, 망각 게이트, 출력 게이트를 추가하여
    불필요한 기억을 지우고, 기억해야할 것들을 결정
  • 전통적인 RNN에서 cell state $C_t$를 추가하여 긴 시퀀스의 입력을 처리하는데 탁월한 성능
  • cell state 또한 이전 시점의 cell state가 다음 시점의 cell state를 구하기 위한 입력으로서 사용
  • 입력 게이트: 현재 정보를 기억하기 위한 게이트
  • 삭제 게이트: 기억을 삭제하기 위한 게이트, 시그모이드 함수의 반환값이 0에 가까울수록 많은 정보가 삭제
  • 셀 상태: 삭제 게이트에서 일부 기억을 잃은 상태, 입력 게이트에서 선택된 기억을 삭제 게이트의 결과값과 더함
  • 삭제 게이트는 이전 시점의 입력을 얼마나 반영할지 의미, 입력 게이트는 현재 시점의 입력을 얼마나 반영할지 결정
  • 출력 게이트: 현재 시점 $t$의 hidden state를 결정하는 일에 사용

08-03. GRU

  • LSTM의 장기 의존성 문제에 대한 해결책을 유지하면서, hidden state를 업데이트하는 계산을 감소
  • GRU는 업데이트 게이트와 리셋 게이트로 구성
  • 데이터의 양이 적을 때는 매개 변수의 양이 적은 GRU가 유리, 반대의 경우엔 LSTM이 유리

08-04. Keras RNN and LSTM

입력 생성

  • train_X가 2D 텐서의 형태일 경우 batch_size 1을 추가해 3D 텐서로 변경
1
2
3
train_X = [[[0.1, 4.2, 1.5, 1.1, 2.8], [1.0, 3.1, 2.5, 0.7, 1.1], [0.3, 2.1, 1.5, 2.1, 0.1], [2.2, 1.4, 0.5, 0.9, 1.1]]]
train_X = np.array(train_X, dtype=np.float32)
print(train_X.shape) # (1, 4, 5)

SimpeRNN

  • return_sequences=False일 경우 2D 텐서 반환
  • return_sequences=True일 경우 timesteps를 포함하는 3D 텐서 반환
  • return_state=True일 경우 return_sequences에 관계없이 마지막 시점의 hidden state 출력
1
2
rnn = SimpleRNN(3)
hidden_state = rnn(train_X) # shape(1, 3)
1
2
rnn = SimpleRNN(3, return_sequences=True)
hidden_states = rnn(train_X) # shape(1, 4, 3)
1
2
rnn = SimpleRNN(3, return_sequences=True, return_state=True)
hidden_states, last_state = rnn(train_X) # shape(1, 4, 3), shape(1, 3)

LSTM

  • return_state=True일 경우 마지막 cell state를 포함한 세 개의 결과를 반환
1
2
lstm = LSTM(3, return_sequences=False, return_state=True)
hidden_state, last_state, last_cell_state = lstm(train_X) # each shape(1, 3)

Bidirectional LSTM

  • 정방향과 역방향에 대한 hidden state와 cell state를 반환
  • return_sequences=False일 경우 정방향 LSTM의 마지막 시점 hidden state와
    역방향 LSTM의 첫번째 시점 hidden state가 연결된 채 반환
  • return_sequences=True일 경우 각각의 순서 맞게 연결된 hidden state 반환
1
2
3
4
bilstm = Bidirectional(LSTM(3, return_sequences=False, return_state=True, \
                            kernel_initializer=k_init, bias_initializer=b_init, recurrent_initializer=r_init))
hidden_states, forward_h, forward_c, backward_h, backward_c = bilstm(train_X)
# shape(1, 6), shape(1, 3), shape(1, 3)

08-05. RNNLM

  • NNLM과 달리 time step을 도입하여 입력의 길이가 고정되지 않는 언어 모델
  • Teaching Forcing: 테스트 과정에서 RNN 모델을 훈련시킬 때 사용하는 훈련 기법,
    모델이 t 시점에서 예측한 값을 t+1 시점에 입력으로 사용하지 않고 실제 알고 있는 정답(t 시점의 레이블)을 사용
  • 한 번 잘못 예측하면 뒤에서의 예측가지 영향을 미쳐 훈련 시간이 느려지기 때문에 교사 강요를 사용
  • 훈련 과정 동안 활성화 함수로는 softmax 함수, 손실 함수로는 cross-entropy 함수를 사용
  • one-hot vector $x_t$를 입력받으면 NNLM에서와 동일한 embedding layer를 거쳐
    ${V}\times{M}$ 크기의 embedding vector로 변환, $e_t=lookup(x_t)$
  • 이후 RNN과 동일한 과정을 거쳐 $\hat{y_t}$를 반환, $h_t=\tanh({W_x}{e_t}+{W_h}{h_{t-1}}+b)$
  • $\hat{y}_t$의 각 차원 안에서의 값은 $\hat{y}_t$의 j번째 인덱스가 가진 0과 1사이의 값이 j번째 단어가 다음 단어일 확률

08-06. Text Generation using RNN

데이터 전처리

1
2
3
4
# 원본 한국어 문장
text = """경마장에 있는 말이 뛰고 있다\n
그의 말이 법이다\n
가는 말이 고와야 오는 말이 곱다\n"""
1
2
3
4
5
# 단어 집합 생성
tokenizer = Tokenizer()
tokenizer.fit_on_texts([text])
vocab_size = len(tokenizer.word_index) + 1
print('단어 집합의 크기 : %d' % vocab_size) # 12
1
2
3
# 각 라인마다 texts_to_sequences() 함수를 적용해서 훈련 데이터 생성
print(sequences)
[[2, 3], [2, 3, 1], [2, 3, 1, 4], [2, 3, 1, 4, 5], [6, 1], [6, 1, 7], [8, 1], [8, 1, 9], [8, 1, 9, 10], [8, 1, 9, 10, 1], [8, 1, 9, 10, 1, 11]]
1
2
3
4
5
# 패딩 후 라벨 분리 (가장 우측에 있는 단어, [경마장에, 있는]에서 '있는' 등을 라벨로 지정)
sequences = pad_sequences(sequences, maxlen=max_len, padding='pre')
sequences = np.array(sequences)
X = sequences[:,:-1]
y = sequences[:,-1]
1
2
# 라벨에 대해서 one-hot encoding 수행
y = to_categorical(y, num_classes=vocab_size)

RNN 모델 설계

  • many-to-one 구조의 RNN을 사용
  • 모든 가능한 단어 중 마지막 시점에서 하나의 단어를 예측하는 다중 클래스 분류 문제 수행
  • 활성화 함수로는 softmax 함수, 손실 함수로는 cross-entropy 함수 사용
  • 첫 단어가 주어졌을 때, n번 동안 예측을 반복하면서 현재 단어와 문장에 예측 단어를 저장
1
2
3
4
5
6
7
8
9
embedding_dim = 10
hidden_units = 32

model = Sequential()
model.add(Embedding(vocab_size, embedding_dim))
model.add(SimpleRNN(hidden_units))
model.add(Dense(vocab_size, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X, y, epochs=200, verbose=2)

LSTM 모델 설계

  • 뉴욕 타임즈 기사 제목 데이터 전처리 (단어 집합 크기 3494, 샘플 최대 길이 24)
  • RNN과 동일한 작업을 수행할 LSTM 모델 설계, 예측 과정 또한 동일
1
2
3
4
5
6
7
8
9
embedding_dim = 10
hidden_units = 128

model = Sequential()
model.add(Embedding(vocab_size, embedding_dim))
model.add(LSTM(hidden_units))
model.add(Dense(vocab_size, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X, y, epochs=200, verbose=2)

08-07. Char RNN

  • 입출력의 단위를 word-level에서 character-level로 변경한 RNN
  • 문자 단위를 입출력으로 사용하기 때문에 embedding layer를 사용하지 않음
  • 이상한 나라의 앨리스 데이터 사용 (문자열 길이 159484, 문자 집합 크기 56)
  • 훈련 데이터에 apple이라는 시퀀스가 있고 입력의 길이가 4일 때, ‘appl’을 입력하면 ‘pple’을 예측할 것으로 기대
  • train_X.shape(2658, 60, 56), train_y.shape(2658, 60, 56)

Char RNN 모델 설계

1
2
3
4
5
6
7
8
9
hidden_units = 256

model = Sequential()
model.add(LSTM(hidden_units, input_shape=(None, train_X.shape[2]), return_sequences=True))
model.add(LSTM(hidden_units, return_sequences=True))
model.add(TimeDistributed(Dense(vocab_size, activation='softmax')))

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(train_X, train_y, epochs=80, verbose=2)