11장 자연어처리 1부

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

tensorflow 버전과 GPU 확인

주요내용

11.1 자연어처리 소개

파이썬, 자바, C, C++, C#, 자바스크립트 등 컴퓨터 프로그래밍언어와 구분하기 위해 일상에서 사용되는 한국어, 영어 등을 자연어(natural language)라 부른다.

자연어의 특성상 정확한 분석을 위한 알고리즘을 구현하는 일은 사실상 매우 어렵다. 딥러닝 기법이 활용되기 이전깢지 적절한 규칙을 구성하여 자연어를 이해하려는 수 많은 시도가 있어왔지만 별로 성공적이지 않았다.

1990년대부터 인터넷으로부터 구해진 엄청난 양의 텍스트 데이터에 머신러닝 기법을 적용하기 시작했다. 단, 주요 목적이 언어의 이해가 아니라 아래 예제들처럼 입력 텍스트를 분석하여 통계적으로 유용한 정보를 예측하는 방향으로 수정되었다.

이와 같은 분석을 자연어처리(NLP, Natural Language Processing)이라 하며 단어(words), 문장(sentences), 문단(paragraphs) 등에서 찾을 수 있는 패턴(pattern)을 인식하려 시도한다.

머신러닝 활용

자연어처리를 위해 1990년대부터 시작된 머신러닝 활용의 변화과정은 다음과 같다.

11.2 텍스트 벡터화

딥러닝 모델은 텍스트 자체를 처리할 수 없다. 따라서 택스트를 수치형 텐서(numeric tensors)로 변환하는 텍스트 벡터화(text vectorization) 과정이 요구되며 보통 다음 세 단계를 따른다.

  1. 텍스트 표준화(text standardization): 소문자화, 마침표 제거 등등
  2. 토큰화(tokenization): 기본 단위의 유닛(units)으로 쪼개기
    • 토큰 예제: 문자, 단어, 단언들의 집합 등등
  3. 어휘 색인화(vocabulary indexing): 토큰 각각을 하나의 수치형 벡터(numerical vector)로 변환.

아래 그림은 텍스트 벡터화의 기본적인 과정을 잘 보여준다.

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

텍스트 표준화

다음 두 문장을 표준화를 통해 동일한 문장으로 변환해보자.

예를 들어 다음 표준화 기법을 사용할 수 있다.

그러면 위 두 문장 모두 아래 문장으로 변환된다.

표준화 과정을 통해 어느 정도의 정보를 상실하게 되지만 학습해야할 내용을 줄여 일반화 성능이 보다 좋은 모델을 훈련시키는 장점이 있다. 하지만 분석 목적에 따라 표준화 기법은 경우에 따라 달라질 수 있음에 주의해야 한다. 예를 들어 인터뷰 기사의 경우 물음표(?)는 제거하면 안된다.

토큰화

텍스트 표준화 이후 데이터 분석의 기본 단위인 토큰으로 쪼개야 한다. 보통 아래 세 가지 방식 중에 하나를 사용한다.

일반적으로 문자 기준 토큰화는 잘 사용되지 않는다. 여기서도 단어 기준 또는 N-그램 토큰화만 이용한다.

단어주머니(bag-of-words)는 N-토큰으로 구성된 집합을 의미하며 N-그램 주머니(bag-of-N-grams)라고 불리기도 한다. 예를 들어 "the cat sat on the mat." 문장에 대한 2-그램 집합과 3-그램 집합은 각각 다음과 같다.

{"the", "the cat", "cat", "cat sat", "sat",
 "sat on", "on", "on the", "the mat", "mat"}
{"the", "the cat", "cat", "cat sat", "the cat sat",
 "sat", "sat on", "on", "cat sat on", "on the",
 "sat on the", "the mat", "mat", "on the mat"}

어휘 색인화

일반적으로 먼저 훈련셋에 포함된 모든 토큰들의 색인(인덱스)을 작성한다. 생성된 색인을 각 토큰을 바탕으로 원-핫, 멀티-핫 인코딩 등의 방식을 사용하여 수치형 텐서로 변환한다.

4장5장에서 설명한 대로 보통 사용 빈도수가 높은 2만 또는 3만 개의 단어만을 대상으로 어휘 색인화를 진행한다. 당시에 num_words=10000을 사용하여 사용 빈도수가 상위 1만 등 안에 드는 단어만을 대상으로 훈련셋을 구성하였다.

from tensorflow.keras.datasets import imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)

케라스의 imdb 데이터셋은 이미 정수들의 시퀀스로 전처리가 되어 있다. 하지만 여기서는 원본 imdb 데이터셋을 대상으로 전처리를 직접 수행하는 단계부터 살펴볼 것이다. 이를 위해 아래 사항을 기억해 두어야 한다.

케라스의 TextVectorization 층 활용

지금까지 설명한 텍스트 벡터화를 위해 케라스의 TextVectorization 층을 활용할 수 있으며 기본 사용법은 다음과 같다.

TextVectorization 층 구성에 사용되는 주요 기본 설정은 다음과 같다.

표준화와 토큰화 방식을 임의로 지정해서 활용할 수도 있다. 다만, 파이썬의 기본 문자열 자료형인 str 대신에 tf.string 텐서를 활용해야 함에 주의해야 한다. 표준화와 토큰화의 기본값은 아래 두 함수를 활용하는 것과 동일하다.

예제

아래 데이터셋을 대상으로 텍스트 벡터화를 진행해보자.

생성된 어휘 색인은 다음과 같다.

생성된 어휘 색인을 활용하여 새로운 문장을 벡터화 해보자.

벡터화된 텐서로부터 문장을 복원하면 표준화된 문장이 생성된다.

TextVectorization 층 사용법

TextVectorization 층은 GPU 또는 TPU에서 지원되지 않는다. 따라서 모델 구성에 직접 사용하는 방식은 모델의 훈련을 늦출 수 있기에 권장되지 않는다. 여기서는 대신에 데이터셋 전처리를 모델 구성과 독립적으로 처리하는 방식을 이용한다.

하지만 훈련이 완성된 모델을 실전에 배치할 경우 TextVectorization 층을 완성된 모델에 추가해서 사용하는 게 좋다. 이에 대한 자세한 설명은 잠시 뒤에 부록에서 설명한다.

11.3 단어 모음 표현법: 집합과 시퀀스

앞서 언급한 대로 자연어처리 모델에 따라 단어 모음을 다루는 방식이 다르다.

여기서는 IMDB 영화 리뷰 데이터를 이용하여 두 모델 방식의 활용법과 차이점을 소개한다.

11.3.1 IMDB 영화 리뷰 데이터 준비

이전과는 달리 여기서는 IMDB 데이터셋을 직접 다운로드하여 전처리하는 과정을 살펴본다.

준비 과정 1: 데이터셋 다운로드 압축 풀기

압축을 풀면 아래 구조의 디렉토리가 생성된다.

aclImdb/
...train/
......pos/
......neg/
...test/
......pos/
......neg/

trainposneg 서브디렉토리에 각각 12,500개의 긍정과 부정 리뷰가 포함되어 있다.

주의사항: 아래 코드는 윈도우의 경우 10 최신 버전 또는 11부터 지원된다.

aclImdb/train/unsup 서브디렉토리는 필요 없기에 삭제한다.

긍정 리뷰 하나의 내용을 살펴보자. 모델 구성 이전에 훈련 데이터셋을 살펴 보고 모델에 대한 직관을 갖는 과정이 항상 필요하다.

준비 과정 2: 검증셋 준비

훈련셋의 20%를 검증셋으로 떼어낸다. 이를 위해 aclImdb/val 디렉토리를 생성한 후에 긍정과 부정 훈련셋 모두 무작위로 섞은 후 그중 20%를 검증셋 디렉토리로 옮긴다.

준비 과정 3: 텐서 데이터셋 준비

text_dataset_from_directory() 함수를 이용하여 훈련셋, 검증셋, 테스트셋을 준비한다. 자료형은 모두 Dataset이며, 배치 크기는 32를 사용한다.

각 데이터셋은 배치로 구분되며 입력은 tf.string 텐서이고, 타깃은 int32 텐서이다. 크기는 모두 32이며 지정된 배치 크기이다. 예를 들어, 첫째 배치의 입력과 타깃 데이터의 정보는 다음과 같다.

11.3.2 단어주머니 기법

단어주머니에 채울 토큰으로 어떤 N-그램을 사용할지 먼저 지정해야 한다.

방식 1: 유니그램 바이너리 인코딩

예를 들어 "the cat sat on the mat" 문장을 유니그램으로 처리하면 다음 단어주머니가 생성된다. 집합으로 처리되기에 단어들의 순서는 완전히 무시된다.

{"cat", "mat", "on", "sat", "the"}

이제 모든 문장은 어휘색인에 포함된 단어들의 수만큼 긴 1차원 이진 텐서(binary tensor)로 처리된다. 즉, 멀티-핫(multi-hot) 인코딩 방식을 사용해서 텐서로 변환된다. 4장5장에서 문장을 인코딩 방식과 동일하다.

TextVectorization 클래스의 output_mode="multi_hot" 옵션을 이용하면 방금 설명한 내용을 그대로 처리해준다.

생성된 어휘색인을 이용하여 훈련셋, 검증셋, 테스트셋 모두 벡터화한다.

변환된 첫째 배치의 입력과 타깃 데이터의 정보는 다음과 같다. max_tokens=20000으로 지정하였기에 모든 문장은 길이가 2만인 벡터로 변환되었다.

밀집 모델 지정

단어주머니 모델로 여기서는 밀집 모델을 사용한다. get_model() 함수가 컴파일 된 단순한 밀집 모델을 반환한다. 모델의 출력값은 긍정일 확률이며, 최상위 층의 활성화 함수로 sigmoid를 사용한다.

모델 훈련

밀집 모델 훈련과정은 특별한 게 없다. 훈련 후 테스트셋에 대한 정확도가 89% 보다 조금 낮게 나온다. 최고 성능의 모델이 테스트셋에 대해 95% 정도 정확도를 내는 것보다는 낮지만 무작위로 찍는 모델보다는 훨씬 좋은 모델이다.

방식 2: 바이그램 바이너리 인코딩

바이그램(2-grams)을 유니그램 대신 이용해보자. 예를 들어 "the cat sat on the mat" 문장을 바이그램으로 처리하면 다음 단어주머니가 생성된다.

{"the", "the cat", "cat", "cat sat", "sat",
 "sat on", "on", "on the", "the mat", "mat"}

TextVectorization 클래스의 ngrams=N 옵션을 이용하면 N-그램들로 이루어진 어휘색인을 생성할 수 있다.

어휘색인 생성과 훈련셋, 검증셋, 테스트셋의 벡터화 과정은 동일하다.

훈련 후 테스트셋에 대한 정확도가 90%를 조금 웃돌 정도로 많이 향상되었다.

방식 3: 바이그램 TF-IDF 인코딩

N-그램을 벡터화할 때 사용 빈도를 함께 저장하는 방식을 사용할 수 있다. 단어의 사용 빈도가 아무래도 문장 평가에 중요한 역할을 수행할 것이기 때문이다. 아래 코드에서처럼 output_mode="count" 옵션을 사용하면 된다.

그런데 이렇게 하면 "the", "a", "is", "are" 등의 사용 빈도는 매우 높은 반면에 "Chollet" 등의 단어는 빈도가 거의 0에 가깝게 나온다. 또한 생성된 벡터의 대부분은 0으로 채워질 것이다. max_tokens=20000을 사용한 반면에 하나의 문장엔 많아야 몇 십개 정도의 단어만 사용되었기 때문이다.

inputs[0]: tf.Tensor([1. 1. 1. ... 0. 0. 0.], shape=(20000,), dtype=float32)

이 점을 고려해서 사용 빈도를 정규화한다. 평균을 원점으로 만들지는 않고 TF-IDF 값으로 나누기만 실행한다. 이유는 평균을 옮기면 벡터의 대부분의 값이 0이 아니게 되어 훈련에 보다 많은 계산이 요구되기 때문이다.

TF-IDF의 의미는 다음과 같다.

output_mode="tf_idf" 옵션을 사용하면 TF-IDF 인코딩을 지원한다.

훈련 후 테스트셋에 대한 정확도가 다시 89% 아래로 내려간다. 여기서는 별 도움이 되지 않았지만 많은 텍스트 분류 모델에서는 1% 정도의 성능 향상을 가져온다.

주의사항: 아래 코드는 현재(Tensorflow 2.6과 2.7) GPU를 사용하지 않는 경우에만 작동한다. 이유는 아직 모른다(여기 참조).

부록: 문자열 벡터화 전처리를 함께 처리하는 모델 내보내기

훈련된 모델을 실전에 배치하려면 텍스트 벡터화도 모델과 함께 내보내야 한다. 이를 위해 TextVectorization 층의 결과를 재활용만 하면 된다.

inference_model은 일반 텍스트 문장을 직접 인자로 받을 수 있다. 예를 들어 "That was an excellent movie, I loved it."라는 리뷰는 긍정일 확률이 매우 높다고 예측된다.