[NLP]텍스트 전처리

5 minute read

자연어 처리: 텍스트 전처리

자연어 처리란

  • 자연어의 의미를 컴퓨터로 분석해서 특정 작업을 위해 사용할 수 있도록 하는 것
  • 응용분야
    • 기계번역, 감성분석, 문서분류, 질의응답시스템, 챗봇, 언어생성, 음성인식, 추천시스템 원래는 NLP에 컴퓨터비전 기술을 적용시켜 발전해왔는데, 이제는 또 NLP의 기술을 컴퓨터비전에 적용시켜 발전하고 있다!

참고

단어(Word)

  • 문장에서 단어의 수를 셀 때, 문장부호를 단어에 포함시켜야 할까?
    • 문장부호(,)가 여러 단어, 구 사이의 관계를 지을 경우 문장부호를 단어에 포함시키는 것이 사물들 사이의 경계를 잘 구분할 수 있는 이점이 있다.
    • 문장부호가 문장의 의미에 영향을 주는 경우(? 또는 !) 문장부호를 단어에 포함시키는 것이 좋다.
  • 구어체 문장의 경우, fragments(깨어진 단어, 단어를 더듬으면서 말한다던지), filed pauses(hu, um…)를 단어에 포함시켜야 할까?
    • 빼는게 좋다고 생각할 지 모르나, 포함하는게 좋을 수도 있다! 음성인식의 경우 fragments나 filed pauses가 나타나는 경우 문장의 일부분을 다시 반복할 가능성이 높다. 따라서 뒷내용을 예측하는 데 도움이 될 수 있다.
    • 사람마다 um, ah등 중간에 말이 비는 부분들을 채우는 방식이 다르다. 따라서 화자가 누구인지 디텍트하는데 도움이 된다.
    • 음성을 텍스트로 만들어줄 때 또한 단어로 포함시켜 제거해주는게 좋다!
  • 표제어(lemma): 여러 단어들이 공유하는 뿌리 단어
  • 단어형태(wordform): 같은 표제어를 공유하지만 다양한 형태를 가질 수 있음
  • 예를들어 cat/cats: 두 가지의 형태를 가지고 있지만 동일한 표제어 cat을 공유함
  • Vocabulary: 단어의 집합
  • Type: Vocabulary의 한 원소(Token에서 중복제거)
  • Token: 문장 내에 나타나는 한 단어
  • They picnicked by the pool, then lay back on the grass and looked at the stars.
    • 16 tokens
    • 14 types(the가 반복됨)

말뭉치(Corpus)

  • 하나의 말뭉치는 일반적으로 대용량 문서들의 집합이다.
  • 말뭉치의 특성은 아래의 요소들에 따라 달라진다
    • 언어, 방언, 장르(뉴스, 소설, 과학기술문서 등), 글쓴이의 인구통계적 속성(나이, 성별, 인종 등)

텍스트 정규화

  • 모든 자연어 처리는 텍스트 정규화를 필요로 한다
    • 토큰화(tokenizing words)
    • 단어정규화(normalizing word formats)
    • 문장분절화(segmenting sentences): 하나의 문서를 여러개의 문장으로 구분해주는 것

커맨드라인으로 Unix 또는 윈도우즈에서 간단하게 토큰화하기

  • 토큰화: 문서가 주어졌을 때 단어들의 시퀀스로 표현하는 것
  • 셰익스피어의 햄릿을 토큰화 해보자
    • tr -sc ‘A-Za-z’ ‘\in’ < hamlet.txt
      • tr은 한 패턴을 다른 패턴으로 교체해주는 작업이다
      • c 옵션은 ‘A-Za-z’ 패턴을 제외한 모든 나머지 패턴을 뒤세 나타나는 또다른 패턴 ‘\n’으로 교체한다.
      • ‘A-Za-z’는 모든 알파벳 대문자와 소문자를 말한다
      • s 옵션은 중복되는 단어들을 하나로 합쳐준다.
      • 즉 주어진 텍스트에 나타나는 모든 단어들에 대해서 알파벳 대문자/소문자 외의 모든 다른 글자들을 엔터로 교체해준다. 이 문서 안에 나타나는 단어들을 모아서 vocabulary를 만들어보자.
  • 빈도수로 정렬
    • tr -sc ‘A-Za-z’ ‘\in’ < hamlet.txt sort uniq -c sort -n -r
    • uniq는 단어들의 빈도수
    • sort는 가장 많이 나타난 단어 순으로 정렬 대문자를 소문자로 통일해서 정렬해보자.
  • 소문자로 변환해서 정렬
    • tr -sc ‘A-Z’‘a-z’ < hamlet.txt tr -sc ‘a-z’‘\in’ sort uniq -c sort -n -r
      • tr -sc ‘A-Z’‘a-z’: 모든 대문자를 소문자로 바꾼다

문제점들

  • 문장부호(punctuation)들을 항상 무시할 수는 없다
    • 단어 안에 나타나는 문장부호들: m.p.h, AT&T, Ph.D
    • 화폐단위($10.00), 날짜(2021/02/15), URLs, hashtags(#), 이메일주소
    • 문장부호가 단어의 의미를 명확하게 하는 경우는 제외시키지 않는 것이 좋다.
  • 접어(clitics): 다른 단어에 붙어서 존재하는 형태
    • we’re -> we are
  • 여러 개의 단어가 붙어야 의미가 있는 경우
    • New York, rock’n’roll

중국어의 경우

  • 여러개의 글자가 하나로, 띄어쓰기 없이 씀.
  • 글자 단위로 vocabulary를 만들면 사이즈를 좀 줄일 수 있다.

한국어의 경우

  • 한국어의 경우 토큰화가 복잡함
  • 띄어쓰기가 잘 지켜지지 않고 띄어쓰기가 제대로 되었더라도 한 어절은 하나 이상의 의미 단위들이 있을 수 있다.
  • 형태소(morpheme): 뜻을 가진 가장 작은 말의 단위
    • 자립형태소: 명사, 대명사, 부사 등
    • 의존형태소: 다른 형태소와 결합하여 사용되는 형태소. 접사, 어미, 조사 등
  • 단어보다 작은 단위(subword)로 토큰화가 필요하다. 한국어 뿐만 아니라 영어, 다른언어에도 필요!

Subword Tokenization이 왜 필요한가?

  • 만약 학습데이터에서 보지 못했던 새로운 단어가 나타난다면?
    • 학습 데이터: low, new, newer
    • 테스트데이터: lower
    • -er, -est 등과 같은 형태소를 분리할 수 있으면 좋을 것이다
  • Subword toeknization algorithms
    • Byte-Pair Encoding(BPE)
    • WordPiece: BPE와 유사한데 확률적인 개념을 이용해 발전시켰다
    • Unigram language modeling: 완전히 확률 모델에 기반한 알고리즘
  • 모든 토크나이제이션 알고리즘은 두 가지 구성요소를 갖는다.
    • Token learner: 말뭉치에서 vocabulary(token 들의 집합)을 만들어 냄. 학습하는 부분
    • Token segmenter: 새로운 문장을 토큰화함. 학습한 것을 새로운 문장에 적용하는 부분.

Subword Tokenization - Byte-Pair Encoding(BPE)

  • Vocabulary를 단일 문자들의 집합으로 초기화한다
  • 다음을 반복한다.
    • 말뭉치에서 연속적으로 가장 많이 발생하는 두 개의 기호들(vocabulary 내의 원소들)을 찾는다
    • 두 기호들을 병합하고 새로운 기호로 vocabulary에 추가한다
    • 말뭉치에서 그 두 기호들을 병합된 기호로 모두 교체한다
  • 위 과정을 k번의 병합이 일어날 때까지 반복한다
  • 기호병합은 단어안에서만 이루어진다. 이것을 위해서 단어끝을 나타내는 특수기호 ‘_‘을 단어 뒤에 추가한다. 그리고 각 단어를 문자단위로 쪼갠다.
    • 말뭉치(corpus)
      • l o w_ 5번, l o w e s t_ 2번, n e w er_ 6번, w i d er_ 3번, n e w_ 2번 -> 말뭉치에서 연속적으로 가장 많이 발생하는 두 개의 기호는 e r이므로, er로 병합하여 vocabulary에 넣는다. 그리고 말뭉치에 병합된 기호를 대체한다.
    • vocabulary(특수기호 _까지 포함함!)
      • _, d, e, i, l, n, o, r, s, t, w, er(er 추가됨!)
    • 위의 과정을 반복하다보면 er_, ne, new, lo, low, newer, low_의 토큰들이 추가로 병합이 일어나 vocabulary에 추가된다.
    • vocabulary 사이즈를 너무 크게 늘리면 안되기 때문에 적절한 때에 멈춘다.
  • Token segmenter
    • 새로운 단어가 주어졌을 때 어떻게 토큰화할 것인지?
    • Greedy한 적용: 병합을 학습한 순서대로 적용(“e r” -> “er”)
    • 자주 나타나는 단어는 하나의 토큰으로 병합됨
    • 드문 단어는 subword 토큰들로 분할됨
  • 하나의 단어 “n e w e r “은 하나의 토큰 “newer“로 토큰화 됨
  • 하나의 단어 “l o w e r “은 하나의 토큰 “low er“로 토큰화 됨

Subword Tokenization - Wordpiece

  • 기호들의 쌍을 찾을 때 빈도수 대신에 likelihood를 최대화시키는 쌍을 찾는다.(나타날 확률 계산)
  • 예를 들어, 말뭉치에서 lo이 일어날 확률보다 er이 일어날 확률이 크면 “er” 병합 토큰을 vocabulary에 추가한다.
  • BPE에 비해 확률적인 부분이 추가되었지만 완전히 확률적인 모델은 아님
  • 병합의 candidate들 중에서 고르는 방식이 greedy하다.

Subword Tokenization - Unigram

  • 병합을 하나씩 순차적으로 고르는 것이 아니라,
  • 확률모델(언어모델)을 사용한다.
  • 학습데이터내의 문장을 관측(observed) 확률변수로 정의한다
    • 연속적인(sequential) 변수
  • 데이터의 주변 우도(marginal likelihood)를 최대화시키는 tokenization을 구한다.
    • EM(expectation maximization)을 사용. 잠재적인 변수가 문제에 개입이 될 때 데이터의 우도를 최대화하는 문제를 풀 때 EM방식을 사용한다.
      • marginal likelihood 계산은 P(관측된 변수 x, 잠재변수 z)을 z에 대해서 sumation을 하는 것이다. 이것이 P(x), marginal likelihood가 된다.
    • Maximization step에서 Viterbi 알고리즘을 사용(wordpiece는 greedy하게 likelihood를 향상)
      • 비터비를 사용해 가장 최선의 토크나이제이션을 한다.
      • 워드피스도 우도를 최대화시키기 때문에 unigram과 비슷하지만, 워드피스는 greedy하게 구하는게 차이점이다. 다양한 토크나이제이션의 병합 가능성을 배제하고 하나씩 한다.

단어정규화

  • 단어들을 정규화된 형식으로 표현
    • U.S.A. or USA or US
    • uhhuh or uh-huh
    • Fed or fed
    • am, is, be, are
  • 검색엔진에서 문서들을 추출할 때 유용할 수 있다
    • 검색어: “USA”, 문서: 단어 “US”만을 포함하더라도 검색될 수 있도록

Case folding

  • 모든 문자들을 소문자화함
    • 일반화를 위해서 유용: 학습데이터와 테스트데이터 사이의 불일치 문제에 도움
    • 정보검색, 음성인식 등에서 유용
    • 감성분석 등의 문서분류 문제에서는 오히려 대소문자 구분이 유용할 수 있음(국가이름 “US” vs 대명사 “us”)

Lemmatization

  • 어근을 사용해서 표현
    • am, are, is -> be라는 의미
    • car, cars, car’s, cars’ -> car라는 의미
    • 이처럼 하나로 통합해주면
  • He is reaading detective stories -> He be read detective story

최근 경향

  • 최근에는 단어 정규화를 aggressive하게 하지 않는다. 토크나이제이션은 많이 사용하지만, 그 이후 더 정규화하는 부분은 프로세싱을 잘 하지 않는다.
  • 단어정규화가 필요한 근본적인 이유
    • 단어들 사이의 유사성을 이해해야하기 때문
    • 단어정규화 작업을 같은 의미를 가진 여러 형태의 단어들을 하나의 단어롤 대응시키는 것으로 이해할 수 있다(위의 Lemmatization처럼)
  • 단어를 vocabulary로 정의된 공간(고차원 희소 벡터)이 아닌 저차원 밀집(dense) 벡터로 대응시킬 수 있다면?
    • car => [0.13, 0.52, 0.01] 원소가 3개인 벡터
    • cars => [0.15, 0.49, 0.02]
    • 각각의 원소가 서로 비슷하면 벡터가 전체적으로 서로 비슷함을 알 수 있다.
    • 단어임베딩을 사용해서 단어를 표현하게 되면 단어정규화의 필요성이 줄어듦