관리 메뉴

클라이언트/ 서버/ 엔지니어 "게임 개발자"를 향한 매일의 공부일지

순환 신경망(RNN) 3 - 자연어 처리 1 : 자연어 처리 방법 본문

인공지능/딥러닝

순환 신경망(RNN) 3 - 자연어 처리 1 : 자연어 처리 방법

huenuri 2024. 12. 18. 19:03

자연어 처리(National Language Processing, NLP)는 스탬, 뉴스/쇼핑 카테고리 분류, 텍스트 요약, 문장 생성, 기계 번역, 챗봇 등 다양한 분야에 활용되고 있다.

자연어를 처리하는 방법을 살펴본 후 순환신경망을 통해 감정 분류, 문장 생성 등 모델을 직접 만들어 보기로 하자.


 

 

 

 

자연어 처리 방법

텍스트 데이터는 테이블 데이터와 같이 구조화되거나 데이터의 길이가 일정하지 않다는 특성을 갖는다. 따라서 문장의 길이가 다른 경우 딥러닝 모델에 입력으로 넣기 위해서는 길이를 동일하게 맞추는 작업이 선행되어야 한다. 가장 긴 문장의 길이에 맞출 수도 있고, 가장 짧은 문장 길이에 맞춰 넘치는 부분을 잘라낼 수도 있다.

 

또한 한글을 입력 데이터로 활용하기 위해서는 숫자로 변환해야 하고, 한글과 숫자를 일대일 매칭하는 방식으로 단어 사전을 만든다. 여기서 말하는 사전은 “텐서플로” : “2021”, “딥러닝” : “2022”와 같이 모든 단어(토큰)를 숫자로 매핑한 사전이다. 사전을 통해 단어를 숫자로, 숫자를 단어로 변경할 수 있다.

 

이렇게 단어(토큰)를 숫자로 변경하기 위해서는 문장을 특정한 기준으로 잘라내야 하는데, 이렇게 잘라낸 조각을 토큰이라고 표현한다. 영어에서는 띄어쓰기를 기준으로 잘라내더라도 큰 문제가 없으나 한국어에서는 띄어쓰기가 잘 되어 있지 않고, “~이(가)”, “~을(를)”, “~에게” 등 조사가 붙어서 다른 말과의 관계를 나타내거나 특별한 뜻을 더해 주는 품사가 붙어 있어 잘라내는 것에 어려움이 있다.

예를 들어 “텐서플로가”, “텐서플로를”, “텐서플로는”이 있다면, 컴퓨터는 3가지 모두 다른 단어로 인식한다. 이에 띄어쓰기는 되어 있지 않지만 “텐서플로”를 구분하는 토크나이저를 활용하거나 불용어(stopword) 처리를 통해 “가”, “를”, “는” 등 조사나 반복되는 불필요한 단어를 제거해야 한다.


 

 

 

다음 예제에서는 문장 텍스트 데이터를 처리해 딥러닝 모델에 입력으로 넣기 직전의 데이터셋을 준비하는 방법을 알아본다.

  • 토큰화(문장을 띄어쓰기 기준으로 나눔) + 단어 사전(단어와 숫자 매칭)
  • 문자 인코딩(사전을 바탕으로 문장들을 숫자로 변경)
  • 인코딩된 문장 길이를 동일하게 변경(패딩)

[그림 5-19]에서는 2개의 문장을 인코딩과 패딩을 통해 동일한 길이의 데이터로 만들고 있다. 문장을 숫자로 변환할 때 단어의 순서대로가 아니라 “영실이는”, “정말”이 앞으로 온 것을 확인할 수 있다. 조금만 자세히 문장을 살펴보면 이유를 금방 알 수 있다. “영실이는”, “정말” 토큰은 2번 등장한 토큰이다. 일반적으로 단어 사전을 만들 때 빈도수가 높을수록 앞 번호를 갖는다.

 

그림 설명

  1. 단어 사전 생성
    • “영실이는 나를 정말 정말 좋아해” → 단어 사전에서 인덱스를 부여
      영실이는(1), 정말(2), 좋아해(3), 나를(4), 영화(5)
  2. 문장 인코딩
    • 문장을 단어 인덱스로 변환
      첫 번째 문장: [1, 4, 2, 2, 3]
      두 번째 문장: [1, 5, 3]
  3. 패딩 적용
    • 문장 길이를 동일하게 맞춤(빈 부분은 0으로 채움)
      첫 번째 문장: [1, 4, 2, 2, 3]
      두 번째 문장: [0, 0, 1, 5, 3]

 

텐서플로(케라스)에서 제공하는 Tokenizer는 띄어쓰기를 기준으로 단어 인코딩 사전을 생성하고, 단어를 쉽게 인코딩할 수 있게 도와준다. 앞에서 그림으로 설명한 문장을 실제 케라스를 활용해서 단어 사전으로 변환한다. Tokenizer 객체를 생성하고, fit_on_texts() 함수에 인코딩할 문장들을 입력하면 단어 토큰을 만들고 각각 인덱스를 지정한다. 단어 ‘정말’은 인덱스 2와 같이 표현된다.


 

 

 

 

texts_to_sequences() 함수는 입력된 문장을 단어 인덱스를 사용하여 숫자 벡터로 변환한다.

 

 

 

 

만약 사전에 없는 새로운 단어가 등장하면 새로운 단어는 인코딩할 때 무시한다. 다음 코드를 보면 '경록이와'라는 단어는 앞에서 만든 사전에 없으므르로 인코딩된 숫자에서 빠졌음을 확인할 수 있다. 띄어쓰기로 나누었을 때 4개의 토큰이지만 인코딩된 결과는 3개이다.

 

 

 

사전에 존재하지 않는 단어를 OOV(Out Of Vocabulary) token이라고 부른다. 케다스 Tokenizer는 이를 처리하기 위해 oov_token 파라미터 값을 설정할 수 있다. 다음 코드 결과를 보면 4개의 값이 토큰으로 인코딩된 것을 볼 수 있다.

 

 

 

 

텍스트 데이터셋에 빈도수가 작은 단어가 많이 존재하는 경우에는 이들 단어를 제외하는 것이 일반적이다. 즉, 문장을 토근으로 인코딩할 때 빈도수가 많은 순서대로 최대 사전 개수를 정하고 빈도수가 적은 단어를 제외한다. 최대 사전 개수는 num_words 파라미터를 통해 설정한다.

 

 

 

 

 


 

 

 

 

 

한편, 뒤쪽에 0을 채우기 위해서는 padding 파라미터 값을 'post'로 설정한다. 값을 출력해보면 뒤쪽으로 부족한 개수만큼 0으로 채워지는 것을 확인할 수 있다.

 

 

 

만약 몇몇 문장만 길이가 길고 대부분의 문장 길이(단어 개수)가 4 이하라면 최대값을 4로 설정할 수 있다.

 

 

 

 

이때 최대 길이인 4보다 긴 문장의 경우 잘라내야 하며, padding과 동일하게 기본값은 앞부분이 잘리게 된다. 뒷부분을 자르고 싶을 때는 truncating 파라미터 값을 'post'로 설정하면 된다.

 

 

 


 

 

 

 

학습을 마치고

RNN 공부를 할까 말까 고민을 하다 오후에는 놀다가 저녁 무렵에 공부를 시작해본다. 지금도 고민이 되지만 방송을 보며 조금은 놀면서라도 공부하는 걸 계속 하기로 했다.

재미는 없지만 나중에 꼭 필요가 있을 것이다.