NLP 텍스트 전처리, 정규 표현식

텍스트 전처리 이론

  • ex) James is working at Disney in London

  • 입력: 벡터화된 텐서(숫자) --> NLP모델

  • 토큰 정제: 토큰이 많다. 필요한 토큰만 남기자
    (노이즈/불용어 제거, 원형복원, 품사태깅, 개체명인식)

  1. 토큰화(분절)
    ['James', 'is', 'working', 'at', 'Disney', 'in', 'London']

  2. 노이즈(불필요)/불용어(현재불필요) 제거:
    의미적으로 중요한 토큰만 남김 (ex: 'at', 'in' 제거)
    ['James', 'is', 'working', 'at', 'Disney', 'in', 'London'] 

  3. 원형복원:
    어간 및 표제어 추출 (시제 단순화, 과거/미래를 기본 시제로)
    ['James', 'work', 'Disney', 'London']

  4. 품사태깅:
    추론 목적에 따라 특정 품사를 탈락할 수 있다.
    [('James', 명사), ('work', 동사), ('Disney', 명사), ('London', 명사)]

  5. 개체명 인식:
    [('James', 사람), ('work', 동작), ('Disney', 단체), ('London', 도시)]

  6. 벡터화:
    [(1 0 0 0), (0 1 0 0), (0 0 1 0), (0 0 0 1)]


영문 전처리 실습

0. 영문 텍스트전처리 라이브러리
  • !pip install nltk
  • import nltk
  • nltk.download('punkt_tab') #말뭉치 데이터
  • nltk.download('omw-1.4')

1. 영문토큰

  • 토큰화 함수1
    • from nltk.tokenize import word_tokenize
    • word_tokens = word_tokenize(text)
    • 단어와 구두점(온점, 컴마, 물음표, 세미콜론, 느낌표 등과 같은 기호)으로
  • 토큰화 함수2
    • from nltk.tokenize import WordPunctTokenizer
    • wordpuncttoken = WordPunctTokenizer().tokenize(text)
    • 알파벳과 알파벳이 아닌 문자를 구분하여 토큰화
  • 토큰화 함수3
    • from nltk.tokenize import TreebankWordTokenizer
    • treebankwordtoken = TreebankWordTokenizer().tokenize(text)
    • 정규표현식에 기반한 토큰화


2-1. 불용어 처리

  • stop_words = ["at", "be", "able", "in"]
  • word_tokens = []
  • for tag in treebankwordtoken:
    • if len(tag) > 1:
      • if tag not in stop_words:
        • word_tokens.append(tag)
        • print(word_tokens)

2-2. 빈도를 기준으로 한 노이즈 제거
  • from collections import Counter
  • Counter(word_tokens).most_common()
  • [('James', 1), ('is', 1), ('working', 1), ('Disney', 1), ('London', 1)]


3-1. 원형복원, 어간추출

  • 어간이 완성형 단어로 남는 것은 아니다. 그러나 같은 의미의 토큰으로 부여할 수 있다.
  • from nltk.stem import PorterStemmer
  • ps = PorterStemmer()
  • print("believes -> "+ps.stem('believes'))
  • believes -> believ


3-2. 원형복원, 표제어추출

  • nltk.download('wordnet')
  • from nltk.stem import WordNetLemmatizer
  • wl = WordNetLemmatizer()
  • print('studies -> '+ wl.lemmatize("studies"))
  • studies -> study


4. 영문 품사 부착: PoS

  • from nltk import pos_tag
  • nltk.download('averaged_perceptron_tagger_eng')
  • tagged_tokens = pos_tag(word_tokens)
  • print(tagged_tokens)
  • [('jame', 'NN'), ('is', 'VBZ'), ('work', 'JJ'), ('disney', 'NN'), ('london', 'NN')]


5. 개체명 인식

  • nltk.download('words')
  • nltk.download('maxent_ne_chunker_tab')
  • from nltk import ne_chunk
  • ne_tokens = ne_chunk(tagged_tokens)
  • print(ne_tokens)


한글 전처리 실습

  • 한글에서 의미의 최소 단위는 형태소
    • 음절(글자) < 형태소 < 어절(단어, 띄어쓰기)

0. 한글 자연어 처리

  • https://konlpy-ko.readthedocs.io/ko/v0.4.3/

  • morphs : 형태소 토큰화
  • nouns : 명사 토큰만 반환
  • pos : 형태소에 품사 태깅하여 반환

1. 한글토큰

  • Hannanum : KAIST에서 개발
    • analyze : 다양한 형태학적 후보를 반환

  • Kkma : 서울대에서 개발
    • sentences : 문장 토큰화

  • Komoran : Shineware팀에서 개발

  • Mecab : 교토대에서 일본어용으로 개발된 분석기, 은전 프로젝트에 의해 한국어용 개발

  • Okt(Twitter) : Will Hohyon Ryu가 개발
    • phrases : 다양한 형태학적 후보를 반환

  • #Okt 토큰화
  • from konlpy.tag import Okt
  • okt = Okt()
  • okt_tokens = okt.morphs(kor_text)
  • print(okt_tokens)

    

2. 한글 품사 부착

  • # Okt 품사 태깅
  • okt_tags = []
  • for token in okt_tokens:
    •   okt_tags += okt.pos(token)
  • print(okt_tags)

3. 노이즈 및 불용어 처리

  • 불용어 처리
    • stop_pos = ['Suffix','Punctuation','Josa','Foreign','Alpha','Number']

  • 최빈어 조회: 최빈어를 조회하여 불용어 제거 대상을 선정
    • from collections import Counter
    • Counter(okt_tags).most_common()

정규표현식

  • import re
    • 숫자: \d
    • 숫자열: \d+
    • 문자: \w
    • 모든문자: .

  • re.search(pattern, string)
    • string에서 pattern(정규표현식) 찾기. 여러개 라면 첫번째 것만

  • re.findall(pattern, string)
    • 정규표현식 패턴과 일치하는 모든 부분을 찾아 리스트로 반환

  • re.match(pattern, string)
    • str의 앞부분이 매치하는지 확인

  • re.sub(pattern, repl, string) <-> re.findall()
    • 패턴과 일치하는 모든 부분을 대체 문자열로 치환

  • re.compile(pattern)
    • 정규표현식 패턴을 재사용 가능한 패턴 객체로 생성
    • pattern = re.compile(r'\d+')
    • matches = pattern.findall('abc123def456')
    • matches # ['123', '456']



  • a? 에 쓸 수 있고 묶어서 [abc]? 로 쓸 수 있다.
  • a* 은 '', 'a', 'aa', 'aaa', 'aaaa', 'aaaa...'
  • a+ 은 'a', 'aa', 'aaa', 'aaaa, 'aaaa...'

  • 그밖에
    • | = or
      • text5 = "I like apple and cherry but not banana"
      • print(re.findall(r'apple|banana|cherry', text5))  # ['apple', 'cherry', 'banana']
      • # ['apple', 'cherry', 'banana']

    • ^ = not
      • a부터 z까지가 아닌 것 중 하나
      • re.findall(r'[^A-Za-z]+', text6)

    • ( ) = 전체를 인식해서 ,  ( ) 안에 있는 것만 가져온다.
      • text = "<사과>, 사과"
      • pattern = r'<(사과)>'
      • fruits = re.findall(pattern, text)
      • print(fruits) # 사과

      • text = "<사과>, <바나나>, <딸기>, <수박>, <오렌지>"
      • pattern = r'<(사과|바나나|오렌지)>'
      • fruits = re.findall(pattern, text)
      • print(fruits) # 사과, 바나나, 오렌지

    • \ = 회피용법
      • 정규표현식이 아니라 문자 자체로 사용하고 싶을 때... 
      • ex( ?, *, + )


Greedy Q VS Reluctant Q

  • Greedy: \(.+\)
    • start: '('
    • end: ')' 를 만나도 끝까지 갔다가, 마지막 ')'를 찾는다.
       
  • Reluctant: \(.+?\)
    • start: '('
    • end: ')' 를 만나면 우선 반환한다. 그리고 또 찾는다.


정규 표현식을 잘 사용하는 법
  • Quantifier(간접명시, 위치명시)부터 신경쓰고 직접명시를 사용하자.

댓글 쓰기

다음 이전