11장 자연어처리 2부

감사말: 프랑소와 숄레의 Deep Learning with Python, Second Edition 10장에 사용된 코드에 대한 설명을 담고 있으며 텐서플로우 2.6 버전에서 작성되었습니다. 소스코드를 공개한 저자에게 감사드립니다.

tensorflow 버전과 GPU 확인

11.3.3 시퀀스 모델 기법

앞서 살펴본 대로 바이그램(bigrams) 등을 이용하여 단어들 사이의 순서 정보를 함께 활용하면 기본적으로 훈련된 모델의 성능이 향상된다. 하지만 N-그램 등은 일종의 수동으로 진행하는 일종의 특성공학(feature engineering)이며, 딥러닝은 그런 특성공학을 가능하면 진행하지 않는 방향으로 발전해왔다. 여기서는 단언들의 순서를 그대로 함께 전달만 하고 나머지 특성은 모델 스스로 찾아내도록 하는 시퀀스 모델의 활용법을 살펴본다.

IMDB 데이터셋 다운로드 및 준비

1부와 동일하다.

정수 시퀀스 데이터셋 준비

훈련셋의 모든 리뷰 문장을 정수들의 벡터로 변환한다. 단, 리뷰 문장이 최대 600개의 단어만 포함하도록 한다. 또한 사용되는 어휘는 빈도 기준 최대 2만개로 제한한다.

변환된 첫째 배치의 입력과 타깃 데이터의 정보는 다음과 같다. output_sequence_length=600으로 지정하였기에 모든 문장은 단어를 최대 600개에서 잘린다. 따라서 생성되는 정수들의 벡터는 길이가 모두 600으로 지정된다. 물론 문장이 600개보다 적은 수의 단어를 사용한다면 나머지는 0으로 채워진다. 또한 벡터에 사용된 정수는 2만보다 작은 값이며, 이는 빈도가 가장 높은 2만개의 단어만을 대상(max_tokens=20000)으로 했기 때문이다.

리뷰 문장의 길이를 600개의 단어로 제한한 이유는 리뷰가 평균적으로 233개의 단어를 사용하기 때문이다. 그리고 600 단어 이상을 사용하는 리뷰는 전체의 5% 정도에 불과하다.

벡터 원-핫 인코딩

아래에서 소개하는 시퀀스 모델은 정수들의 벡터를 원-핫 인코딩된 벡터들의 시퀀스로 변환해서 사용한다. 예를 들어 [2, 1, 4, 0, 0] 벡터를 원-핫 인코딩하면 아래 결과를 얻는다. 단, 벡터에 사용된 정수는 0에서 4까지라고 가정한다.

[[0, 0, 1, 0, 0],
 [0, 1, 0, 0, 0],
 [0, 0, 0, 0, 1],
 [1, 0, 0, 0, 0],
 [1, 0, 0, 0, 0]]

tf.one_hot() 함수가 원-핫 인코딩을 실행한다. 에를 들어 위 결과는 아래 방식으로 얻어진다.

tf.one_hot(indices=[2, 1, 4, 0, 0], depth=5)

시퀀스 모델 예제 1

모델 훈련

모델 훈련이 매우 느리다. 이유는 입력 데이터가 너무 많은 특성을 갖기 때문이다. 입력 데이터 하나의 모양과 특성 수는 다음과 같다.

양방향 LSTM은 엄청난 양의 반복을 실행하기에 당연히 훈련 시간이 길어진다. 게다가 훈련된 모델의 성능이 별로 좋지 않다. 테스테셋에 대한 정확도가 87% 정도에 불과해서 바이그램 모델보다 성능이 낮다.

주의사항: 모델 훈련과정을 한 번 보기만 하려면 epochs=1로 설정하는 것을 권장한다. 책에서는 원래 epochs=10을 사용하였는데 컴퓨터 성능에 따라 몇 시간이 소요될 수 있다.

시퀀스 모델 예제 2: 단어 임베딩 활용

앞서 보았듯이 원-핫 인코딩은 별로 적절하지 않다. 원-핫 인코딩은 단어들의 순서는 잘 반영하지만 단어들 사이의 관계는 전혀 반형하지 못한다.

그림 출처: Deep Learning with Python(Manning MEAP)

반면에 단어 임베딩(word embedding)은 단어들 사이의 관계를 모델 스스로 학습과정에서 찾도록 유도한다. 단어 임베딩을 활용하는 방법은 일반적으로 다음 두 가지이다.

케라스의 Embedding 층 활용

케라스의 Embedding 층은 일종의 사전처럼 작동한다. 하나의 문장에 해당하는 정수들의 벡터가 입력값으로 들어오면 단어들간에 존재하는 연관성을 (어떤식으로라도) 담은 부동소수점들의 벡터로 이루어진 시퀀스를 반환한다. 아래 그림은 원-핫 인코딩 방식과 단어 임베딩 방식의 차이점을 보여준다.

그림 출처: Deep Learning with Python(Manning MEAP)

예를 들어, 600 단어로 이루어진 문장을 단어 임베딩할 때 무엇인지 모르지만 단어들 사이의 연관성을 256개 찾으라 하면 (600, 256) 모양의 텐서(단어 벡터)를 생성한다. 즉, 600개의 단어 각각이 총 2만개의 어휘 색인에 포함된 단어들과의 연관성을 256개 찾는다.

방금 설명한 것을 아래 코드가 실행한다.

layers.Embedding(input_dim=20000, output_dim=256)

아래 코드는 단어 임베딩을 모델 구성에 직접 활용하는 것을 보여준다. 여전히 양방향 LSTM 층을 사용한다.

훈련은 원-핫 인코딩 방식보다 훨씬 빠르게 이루어지며 성능은 87% 정도로 비슷하다. 바이그램 모델보다 성능이 여전히 떨어지는 이유 중에 하나는 리뷰에 사용된 단어의 수를 600개로 제한하였기 때문이다.

패딩(padding)과 마스킹(masking)

반면에 리뷰 문장의 길이가 600이 되지 않는 경우 나머지는 패딩(padding)에 의해 0으로 채워진다. 하지만 이렇게 의미 없이 추가된 0이 훈련에 좋지 않은 영향을 미친다. 따라서 모델이 패딩을 위해 차가된 0이 있다는 사실을 인식하도록 도와주는 마스킹(masking) 기능을 활용하면 좋다.

아래 코드는 마스킹을 활용하는 방식을 보여준다.

모델 성능이 88% 정도로 살짝 향상된다.

훈련된 단어 임베딩 활용

합성곱 신경망에서 이미지넷 등의 대용량 데이터셋을 활용하여 잘 훈련된 모델을 재활용하였던 것처럼 잘 구성된 대용량의 어휘 색인을 활용할 수 있다. 여기서는 수 백만 개의 단어를 활용하여 생성된 2014년에 스탠포드 대학교의 연구자들이 생성한 GloVe(Gloval Vectors for Word Representation) 단어 임베딩을 활용한다.