Corpus Preprocessing

corpus, tokenizing, regex, spacy, konlpy, mecab
Aug 20, 2023
Corpus Preprocessing
 

β… . μ „μ²˜λ¦¬

β…°. μ½”νΌμŠ€λž€?

β€˜λ§λ­‰μΉ˜β€™λΌκ³ λ„ λΆˆλ¦¬λŠ” μ½”νΌμŠ€λŠ” 보톡 μ—¬λŸ¬ λ‹¨μ–΄λ“€λ‘œ 이루어진 λ¬Έμž₯을 λœ»ν•œλ‹€.
NLPλΆ„μ•Όμ˜ λ¨Έμ‹ λŸ¬λ‹μ„ μˆ˜ν–‰ν•˜λ €λ©΄ train set이 ν•„μš”ν•œλ°, 보톡 λ‹€μˆ˜μ˜ λ¬Έμž₯으둜 κ΅¬μ„±λœ μ½”νΌμŠ€κ°€ ν•„μš”ν•˜λ‹€.
μ½”νΌμŠ€λ„ μ—¬λŸ¬κ°œμ˜ μ’…λ₯˜κ°€ μ‘΄μž¬ν•˜λ©° μ•„λž˜μ™€ κ°™λ‹€.
  • 단일 μ–Έμ–΄ μ½”νΌμŠ€(monolingual corpus) : ν•œ 가지 μ–Έμ–΄λ‘œ κ΅¬μ„±λœ μ½”νΌμŠ€λ₯Ό μ˜λ―Έν•œλ‹€.
  • 이쀑 μ–Έμ–΄ μ½”νΌμŠ€(bilingual corpus) : 2가지 μ–Έμ–΄λ‘œ κ΅¬μ„±λœ μ½”νΌμŠ€λ₯Ό μ˜λ―Έν•œλ‹€.
  • 닀쀑 μ–Έμ–΄ μ½”νΌμŠ€(multilingual corpus) : λ‹€μˆ˜μ˜ μ–Έμ–΄λ‘œ κ΅¬μ„±λœ μ½”νΌμŠ€λ₯Ό μ˜λ―Έν•œλ‹€.
  • 병렬 μ½”νΌμŠ€(parallel corpus) : μ–Έμ–΄ 간에 쌍으둜 κ΅¬μ„±λ˜λŠ” μ½”νΌμŠ€λ₯Ό μ˜λ―Έν•œλ‹€.
    • 영문
      ν•œκΈ€
      I love to go to school.
      λ‚˜λŠ” 학ꡐ에 κ°€λŠ” 것을 μ’‹μ•„ν•œλ‹€.
      I am a doctor.
      λ‚˜λŠ” μ˜μ‚¬μž…λ‹ˆλ‹€.

β…±. μ „μ²˜λ¦¬ κ³Όμ • κ°œμš”

NLP λΆ„μ•Όμ—μ„œμ˜ μ „μ²˜λ¦¬ 과정은 λͺ©μ μ— 따라 μ•½κ°„μ”© λ‹€λ₯΄μ§€λ§Œ λŒ€μ²΄λ‘œ μ•„λž˜μ™€ 같은 과정이닀.
  1. μ½”νΌμŠ€ μˆ˜μ§‘
  1. μ •μ œ
  1. λ¬Έμž₯ λ‹¨μœ„ λΆ„μ ˆ
  1. λΆ„μ ˆ
  1. 병렬 μ½”νΌμŠ€ μ •λ ¬(μƒλž΅κ°€λŠ₯)
  1. μ„œλΈŒμ›Œλ“œ λΆ„μ ˆ
 

1. μ½”νΌμŠ€ μˆ˜μ§‘

μ½”νΌμŠ€λ₯Ό κ΅¬ν•˜λŠ” 방식은 맀우 λ‹€μ–‘ν•˜λ‹€.
곡개된 데이터λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜(public data), κ΅¬λ§€ν•˜μ—¬ μ‚¬μš©ν•  수 있고, 논문을 μœ„ν•œ λ°μ΄ν„°μ—μ„œ λ°œμ·Œν•˜μ—¬ μ μš©ν•  μˆ˜λ„ μžˆλ‹€. λ˜ν•œ 크둀링을 ν†΅ν•œ μˆ˜μ§‘μ„ μ§„ν–‰ν• μˆ˜λ„ μžˆλ‹€.
단, μ›Ήμ‚¬μ΄νŠΈμ—μ„œ λ¬΄μž‘μ • μ½”νΌμŠ€λ₯Ό 크둀링할 경우 법적인 문제둜 μ΄μ–΄μ§ˆ 수 μžˆκΈ°λ•Œλ¬Έμ— 크둀링 μ—¬λΆ€λ₯Ό λ¨Όμ € ν™•μΈν•˜λŠ”κ²ƒμ΄ μ€‘μš”ν•˜λ‹€. β†’ robots.txt
ν•΄λ‹Ή μ‚¬μ΄νŠΈμ˜ 크둀링 ν—ˆμš© μ—¬λΆ€λŠ” μ‚¬μ΄νŠΈμ˜ robots.txtλ₯Ό 보면 확인할 수 μžˆλ‹€.
example) TED의 robot.txt
$ wget https://www.ted.com/robots.txt $ cat robots.txt User-agent: * Disallow: /latest Disallow: /latest-talk Disallow: /latest-playlist Disallow: /people Disallow: /profiles Disallow: /conversations User-agent: Baiduspider Disallow: /search Disallow: /latest Disallow: /latest-talk Disallow: /latest-playlist Disallow: /people Disallow: /profiles
robots.txt에 λŒ€ν•œ 더 μžμ„Έν•œ λ‚΄μš© β†’ http://www.robotstxt.org/
μ½”νΌμŠ€ μˆ˜μ§‘μ„ μœ„ν•œ 크둀링은 selenium 을 μ‚¬μš©ν•˜κ±°λ‚˜ beautiful soup 을 μ μš©ν•œλ‹€.
 

2. μ •μ œ

μ •μ œ(normalization)λŠ” ν…μŠ€νŠΈλ₯Ό μ‚¬μš©ν•˜κΈ°μ— μ•žμ„œ ν•„μˆ˜μ μΈ 과정이닀.
μ›ν•˜λŠ” 업무와 λ¬Έμ œμ— 따라, λ˜λŠ” μ‘μš© 뢄야에 따라 ν•„μš”ν•œ μ •μ œμ˜ μˆ˜μ€€μ΄λ‚˜ κΉŠμ΄κ°€ λ‹€λ₯Ό 수 μžˆλ‹€. 예λ₯Ό λ“€μ–΄ μŒμ„± 인식을 μœ„ν•œ μ–Έμ–΄ λͺ¨λΈμ˜ 경우 μ‚¬λžŒμ˜ μŒμ„±μ„ κ·ΈλŒ€λ‘œ λ°›μ•„ 적어야 ν•˜λ―€λ‘œ κ΄„ν˜Έ λ˜λŠ” λ³„ν‘œμ™€ 같은 κΈ°ν˜Έλ‚˜ νŠΉμˆ˜λ¬Έμžλ“€μ„ ν¬ν•¨ν•΄μ„œλŠ” μ•ˆλœλ‹€. λ˜ν•œ λ―Όκ°ν•œ 정보λ₯Ό 담은 λ°μ΄ν„°μ˜ 경우 λ³€μ‘°λ₯Ό λ™λ°˜ν•˜κΈ°λ„ ν•œλ‹€. μ•„λž˜ 효과적인 μ •μ œ 방식을 보자.
  • 전각 문자 제거
    • 일반적으둜 μ‚¬μš©λ˜λŠ” 반각 문자둜 λ³€ν™˜ν•΄μ£ΌλŠ” μž‘μ—…μ΄ ν•„μš”ν•˜λ‹€.
      notion image
      notion image
      ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
      즉, μœ„μ˜ μ „κ°λ¬Έμžλ“€μ„ 각 λ¬Έμžμ— ν•΄λ‹Ήν•˜λŠ” 반각문자둜 λ°”κΎΈμ–΄μ£ΌλŠ” μž‘μ—…μ΄ ν•„μš”ν•˜λ‹€.
       
  • λŒ€μ†Œλ¬Έμž 톡일
    • 일뢀 μ˜μ–΄ μ½”νΌμŠ€μ—μ„œλŠ” μ•½μžλ“±μ—μ„œ λŒ€μ†Œλ¬Έμž ν‘œν˜„μ΄ ν†΅μΌλ˜μ§€ μ•Šμ„ λ•Œκ°€ μžˆμŠ΅λ‹ˆλ‹€.
      예λ₯Ό λ“€μ–΄ New York City의 μ€„μž„λ§(μ•½μž)인 NYC의 경우 λ‹€μŒκ³Ό 같이 ν‘œν˜„ν•  수 μžˆλ‹€.
  • μ •κ·œ ν‘œν˜„μ‹μ„ μ‚¬μš©ν•œ μ •μ œ
    • ν†΅μƒμ μœΌλ‘œ 크둀링을 톡해 μ–»μ–΄λ‚Έ λŒ€λŸ‰μ˜ μ½”νΌμŠ€λ“€μ€ μž‘λ‹€ν•œ λ…Έμ΄μ¦ˆκ°€ μ„žμΌ λ•Œκ°€ λ§Žλ‹€. ν˜Ήμ€ μ›Ήμ‚¬μ΄νŠΈ 성격에 따라 μΌμ •ν•œ νŒ¨ν„΄μ„ μ§€λ‹ˆλŠ” κ²½μš°λ„ λ§Žλ‹€. 이λ₯Ό 효과적으둜 μ •μ œν•˜λŠ”λ° μžˆμ–΄ μ •κ·œν‘œν˜„μ‹(regex)을 잘 ν™œμš©ν•΄μ•Ό ν•œλ‹€.
       
      πŸ‘‰ μ •κ·œν‘œν˜„μ‹ μ‹œκ°ν™” μ‚¬μ΄νŠΈ : https://regexper.com/
      κ°„λ‹¨ν•œ λ¬Έλ²•λ§Œ μ •λ¦¬ν•΄λ³΄μž.
    • [ ] : λŒ€κ΄„ν˜Έ μ•ˆμ— λ“€μ–΄κ°€ μžˆλŠ” 각 μš”μ†Œλ₯Ό or둜 ν‘œκΈ°ν•œλ‹€.
      • [23456cde] β†’ 2 or 3 or 4 or 5 or 6 or c or d or e
    • - : μ—°μ†λœ 숫자 λ˜λŠ” μ•ŒνŒŒλ²³μ„ ν‘œν˜„ν•  수 μžˆλ‹€.
      • [2-5c-e] β†’ 2~5 and c~e
    • [^] : Not을 ^ 기호둜 ν‘œν˜„ν•  수 μžˆλ‹€.
      • [^2-5c-e] β†’ Not (2~5 and c~e)
    • ( ) : κ΄„ν˜Έλ₯Ό μ΄μš©ν•΄ 그룹을 λ§Œλ“€ 수 μžˆλ‹€.
      • (x)(yz) β†’ x와 yzλ₯Ό 각각의 그룹으둜 λ¬ΆλŠ”λ‹€.
    • | : | 기호λ₯Ό μ΄μš©ν•˜μ—¬ or을 ν‘œν˜„ν•  수 μžˆλ‹€.
      • (x|y) β†’ x or y
    • ?, *, + : 각 κΈ°ν˜Έμ— 따라 μ˜λ―Έν•˜λŠ” λ°”κ°€ λ‹€λ₯΄λ‹€.
      • ? : μ•žμ˜ μˆ˜μ‹ν•˜λŠ” 뢀뢄이 λ‚˜νƒ€λ‚˜μ§€ μ•Šκ±°λ‚˜ ν•œ 번만 λ‚˜νƒ€λ‚  λ•ŒλŠ” β€œ?”λ₯Ό μ‚¬μš©ν•œλ‹€.
        • x? β†’ xκ°€ λ‚˜νƒ€λ‚˜μ§€ μ•Šκ±°λ‚˜ ν•œ 번만 λ‚˜νƒ€λ‚¨.
      • + : μ•žμ— μˆ˜μ‹ν•˜λŠ” 뢀뢄이 ν•œ 번 이상 λ‚˜νƒ€λ‚  λ•Œ β€˜+’λ₯Ό μ‚¬μš©ν•œλ‹€.
        • x+ β†’ xκ°€ ν•œ 번 이상 λ‚˜νƒ€λ‚œλ‹€.
      • * : μ•žμ˜ μˆ˜μ‹ν•˜λŠ” 뢀뢄이 λ‚˜νƒ€λ‚˜μ§€ μ•Šκ±°λ‚˜ μ—¬λŸ¬ 번 λ‚˜νƒ€λ‚  λ•Œ β€˜*’λ₯Ό μ‚¬μš©ν•œλ‹€.
        • x* β†’ xκ°€ λ‚˜νƒ€λ‚˜μ§€ μ•Šκ±°λ‚˜ μ—¬λŸ¬λ²ˆ λ‚˜νƒ€λ‚œλ‹€.
    • {n}, {n,}, {n,m} : 각 κΈ°ν˜Έμ— 따라 μ˜λ―Έν•˜λŠ” λ°”κ°€ λ‹€λ₯΄λ‹€.
      • {n} : μ •ν™•ν•œ 반볡횟수λ₯Ό μ•Œκ³ μžˆμ„ λ•Œ ν‘œν˜„ν•œλ‹€.
        • x{n} β†’ xκ°€ n번 λ‚˜νƒ€λ‚œλ‹€.
      • {n,} : n번 이상 λ°˜λ³΅ν•  λ•Œ ν‘œν˜„ν•œλ‹€.
        • x{n,} β†’ xκ°€ n번 이상 λ‚˜νƒ€λ‚œλ‹€.
      • {n,m} : μ •ν™•ν•œ 반볡횟수의 λ²”μœ„λ₯Ό μ•Œκ³ μžˆμ„ λ•Œ ν‘œν˜„ν•œλ‹€.
        • x{n,.m} β†’ xκ°€ nλ²ˆμ—μ„œ m번 만큼 λ‚˜νƒ€λ‚œλ‹€.
    • . : 맀우 κ°•λ ₯ν•œ ν‘œν˜„. μ–΄λ–€ κΈ€μžλ˜ λ‹€ ν¬ν•¨ν•œλ‹€.
    • ^κ³Ό $ : β€˜[’과’]β€™μ•ˆμ— ν¬ν•¨λ˜μ§€ μ•Šμ€ β€˜^’은 라인의 μ‹œμž‘μ„ μ˜λ―Έν•˜λ©° β€˜$’은 라인의 μ’…λ£Œλ₯Ό μ˜λ―Έν•œλ‹€.
      • ^x$ β†’ (start of line) x (end of line)
    • μ§€μ •λ¬Έμž
      • \s : 곡백문자
      • \S : 곡백문자λ₯Ό μ œμ™Έν•œ λͺ¨λ“  문자
      • \w : alphanumeric(μ•ŒνŒŒλ²³+숫자)+’_’ (= [A-Za-z0-9_])
      • \W : nonalphnumeric 문자 및 β€˜_’ μ œμ™Έ(=[^A-Za-z0-9_])
      • \d : 숫자(=[0-9])
      • \D : 숫자λ₯Ό μ œμ™Έν•œ λͺ¨λ“  문자 (=[^0-9])
      # νŒŒμ΄μ¬μ—μ„œ μ •κ·œν‘œν˜„μ‹ μ‚¬μš© # Hello Ki, I would like to introduce regular expression in this section # ~~ # Thank you! # Sincerely, # Ki: +82-10-1234-5678 # β–² μœ„ ν…μŠ€νŠΈμ—μ„œ λ§ˆμ§€λ§‰μ€„μ„ λͺ¨λ“  κ²½μš°μ— λŒ€ν•΄ μ •μ œν•˜λŠ” μ •κ·œν‘œν˜„μ‹μ„ 짜보자. # λ§ˆμ§€λ§‰μ€„μ„ 보고 μ•„λž˜ κ·œμΉ™μ„ 생각해 λ³Ό 수 μžˆλ‹€. # - 이름이 μ „ν™”λ²ˆν˜Έ μ•žμ— λ‚˜μ˜¬ μˆ˜λ„ μžˆλ‹€. # - 이름 λ’€μ—λŠ” 콜둠이 λ‚˜μ˜¬ μˆ˜λ„ μžˆλ‹€. # - 콜둠 μ•ž/λ’€λ‘œλŠ” (탭을 ν¬ν•¨ν•œ) 곡백이 λ‹€μˆ˜ μ‘΄μž¬ν•  μˆ˜λ„ μžˆλ‹€. # - μ „ν™”λ²ˆν˜ΈλŠ” κ΅­κ°€λ²ˆν˜Έλ₯Ό 포함할 μˆ˜λ„ μžˆλ‹€. # - κ΅­κ°€λ²ˆν˜ΈλŠ” μ΅œλŒ€ 3μžλ¦¬μ΄λ‹€. # - κ΅­κ°€λ²ˆν˜Έμ˜ μ•žμ—λŠ” '+'κ°€ 뢙을 μˆ˜λ„ μžˆλ‹€. # - μ „ν™”λ²ˆν˜Έ 사이에 '-'κ°€ λ“€μ–΄κ°ˆ μˆ˜λ„ μžˆλ‹€. # - μ „ν™”λ²ˆν˜ΈλŠ” 빈칸 없이 ν‘œν˜„λœλ‹€. # - μ „ν™”λ²ˆν˜Έ 맨 μ•žκ³Ό μ§€μ—­λ²ˆν˜Έ(λ˜λŠ” 010)의 λ‹€μŒμ—λŠ” κ΄„ν˜Έκ°€ λ“€μ–΄κ°ˆ μˆ˜λ„ μžˆλ‹€. # - κ΄„ν˜ΈλŠ” ν•œμͺ½λ§Œ λ‚˜μ˜¬ μˆ˜λ„ μžˆλ‹€. # - μ§€μ—­λ²ˆν˜Έ 자리의 맨 μ²˜μŒμ— λ‚˜μ˜€λŠ” 0은 빠질 μˆ˜λ„ μžˆλ‹€. 즉, 2μžλ¦¬κ°€ 될 μˆ˜λ„ μžˆλ”°. # - μ§€μ—­λ²ˆν˜Έ λ‹€μŒ 번호 그룹은 3μ—μ„œ 4자리 μˆ«μžμ΄λ‹€. # - λ§ˆμ§€λ§‰μ€ 항상 4자리 μˆ«μžμ΄λ‹€. import re regex = r"([\w]+\s*:?\s*)?\(?\+?([0-9]{1,3})?\-?[0-9]{2,3}(\)|\-)?[0-9]{3,4}\-?[0-9]{4}" x = "Ki: +82-10-1234-5678" re.sub(regex, "REMOVED", x) # REMOVED
      # μΉ˜ν™˜μž μ‚¬μš© # μ•„λž˜ x에 λŒ€ν•œ ν…μŠ€νŠΈ 데이터 쀑 λ¬Έμžμ‚¬μ΄μ— μˆ«μžκ°€ λ“€μ–΄κ°„ κ²½μš°μ— λŒ€ν•΄μ„œλ§Œ μ •μ œν•΄λ³΄μž. x = """abcdefg 12345 ab12 a1bc2d 12ab a1b 1a2 a1 1a hijklmnop""" regex = r'([a-z])[0-9]+([a-z])' to = r'\1\2'#그룹을 λ³€μˆ˜λͺ…μ²˜λŸΌ ν™œμš© y = ' '.join([re.sub(regex, to, x_i) for x_i in x.split('\n')]) # 'abcdefg 12345 ab12 abcd 12ab ab 1a2 a1 1a hijklmnop'
 
  • λ¬Έμž₯ λ‹¨μœ„ λΆ„μ ˆ
    • 보톡 λ‹€λ£¨λ €λŠ” λ¬Έμ œλ“€μ€ μž…λ ₯ λ‹¨μœ„κ°€ λ¬Έμž₯ λ‹¨μœ„μΈ κ²½μš°κ°€ λ§Žλ‹€.
      즉, λŒ€λΆ€λΆ„μ˜ 경우 ν•œ 라인에 ν•œ λ¬Έμž₯만 μžˆμ–΄μ•Ό ν•œλ‹€. μ—¬λŸ¬ λ¬Έμž₯이 ν•œ 라인에 μžˆκ±°λ‚˜, ν•œ λ¬Έμž₯이 μ—¬λŸ¬ 라인에 걸쳐 μžˆλŠ” κ²½μš°μ—λŠ” λ¬Έμž₯ λ‹¨μœ„ λΆ„μ ˆμ΄ ν•„μš”ν•©λ‹ˆλ‹€.
      λ‹€λ§Œ, λ‹¨μˆœνžˆ λ§ˆμΉ¨ν‘œλ₯Ό κΈ°μ€€μœΌλ‘œ λ¬Έμž₯λ‹¨μœ„ λΆ„μ ˆμ„ μˆ˜ν–‰ν•˜λŠ”κ²ƒ 보단 널리 μ•Œλ €μ§„ μžμ—°μ–΄ 처리 νˆ΄ν‚·μΈ NLTK(3.2.5 version 이상)을 μ‚¬μš©ν•˜λŠ”κ²Œ μ’‹λ‹€.
    • λ¬Έμž₯ λ‹¨μœ„ λΆ„μ ˆ 예제
    • import sys, fileinput, re from nltk.tokenize import sent_tokenize if __name__=="__main__": for line in fileinput.input(): if line.strip() != "": line = re.sub(r'([a-z])\.([A-Z])', r'\1.\2', line.strip()) sentences = sent_tokenize(line.strip()) print("="*100) for s in sentences: if s != "": sys.stdout.write(s+"\n") # - κ²°κ³Ό - # > 적용 μ „ 데이터 # μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€. μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ¬Έμž₯을 λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수 있게 된 κ²ƒμž…λ‹ˆλ‹€. # ==================================================================================================== # > 적용 ν›„ 데이터 # μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€. # μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. # λ¬Έμž₯을 λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수 있게 된 κ²ƒμž…λ‹ˆλ‹€.
    • λ¬Έμž₯ ν•©μΉ˜κΈ° 및 λΆ„μ ˆ 예제
    • import sys, fileinput, re from nltk.tokenize import sent_tokenize if __name__=="__main__": # - 적용 μ „ 데이터 - # μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€.\n # μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ¬Έμž₯을 \n # λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수 \n # 있게 된 κ²ƒμž…λ‹ˆλ‹€. buf = [] text = ["μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€.\n", "μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ¬Έμž₯을\n", "λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수\n", "있게 된 κ²ƒμž…λ‹ˆλ‹€.\n"] for line in text: if line.strip() != "": buf += [line.strip()] sentences = sent_tokenize(" ".join(buf)) if len(sentences) > 1: buf = sentences[1:] sys.stdout.write(sentences[0] + '\n') sys.stdout.write(" ".join(buf) + "\n") # - κ²°κ³Ό - # > 적용 μ „ 데이터 # μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€.\n # μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ¬Έμž₯을 \n # λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수 \n # 있게 된 κ²ƒμž…λ‹ˆλ‹€. # ==================================================================================================== # > 적용 ν›„ 데이터 # μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€. # μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. # λ¬Έμž₯을 λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수 있게 된 κ²ƒμž…λ‹ˆλ‹€.
    • 전체 예제 톡합
    • import sys, fileinput, re from nltk.tokenize import sent_tokenize def seperate_sentence(): # - 적용 μ „ 데이터 - # μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€. μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ¬Έμž₯을 λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수 있게 된 κ²ƒμž…λ‹ˆλ‹€. for line in fileinput.input(): if line.strip() != "": line = re.sub(r'([a-z])\.([A-Z])', r'\1.\2', line.strip()) sentences = sent_tokenize(line.strip()) print("="*100) for s in sentences: if s != "": sys.stdout.write(s+"\n") def combine_sentence(): # - 적용 μ „ 데이터 - # μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€.\n # μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ¬Έμž₯을 \n # λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수 \n # 있게 된 κ²ƒμž…λ‹ˆλ‹€. buf = [] text = ["μžμ—°μ–΄μ²˜λ¦¬λŠ” 인곡지λŠ₯의 ν•œ 쀄기 μž…λ‹ˆλ‹€.\n", "μ‹œν€€μŠ€ 투 μ‹œν€€μŠ€μ˜ λ“±μž₯ μ΄ν›„λ‘œ λ”₯λŸ¬λ‹μ„ ν™œμš©ν•œ μžμ—°μ–΄μ²˜λ¦¬λŠ” μƒˆλ‘œμš΄ μ „κΈ°λ₯Ό λ§žμ΄ν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ¬Έμž₯을\n", "λ°›μ•„ λ‹¨μˆœνžˆ 수치둜 λ‚˜νƒ€λ‚΄λ˜ μ‹œμ ˆμ„ λ„˜μ–΄, μ›ν•˜λŠ”λŒ€λ‘œ λ¬Έμž₯을 λ§Œλ“€μ–΄λ‚Ό 수\n", "있게 된 κ²ƒμž…λ‹ˆλ‹€.\n"] for line in text: if line.strip() != "": buf += [line.strip()] sentences = sent_tokenize(" ".join(buf)) if len(sentences) > 1: buf = sentences[1:] sys.stdout.write(sentences[0] + '\n') sys.stdout.write(" ".join(buf) + "\n") if __name__=="__main__": combine_sentence() seperate_sentence()
  • λΆ„μ ˆ
    • λΆ„μ ˆμ˜ μ’…λ₯˜λŠ” ν˜•νƒœμ†Œ 뢄석과 λ‹¨μˆœλΆ„μ ˆλ‘œ ꡬ뢄지을 수 있고, 이λ₯Ό 톡해 μ •κ·œν™”λ₯Ό μˆ˜ν–‰ν•œλ‹€.
      그쀑 ν•œκ΅­μ–΄λŠ” 맀우 κΉŒλ‹€λ‘­κ³  μ–΄λ €μš΄ μƒμœ„μ–Έμ–΄μ— μ†ν•˜κ³  μ£Όμš” ν”„λ‘œκ·Έλž¨μœΌλ‘œ Mecabκ³Ό KoNLPyλ₯Ό μ΄μš©ν•œλ‹€.
  • 병렬 μ½”νΌμŠ€ μ •λ ¬
    • λŒ€λΆ€λΆ„μ˜ 병렬 μ½”νΌμŠ€λ“€μ€ μ—¬λŸ¬ λ¬Έμž₯ λ‹¨μœ„λ‘œ μ •λ ¬λœλ‹€.
      예λ₯Ό λ“€μ–΄, 영자 μ‹ λ¬Έμ—μ„œ ν¬λ‘€λ§ν•œ 영문 λ‰΄μŠ€ κΈ°μ‚¬λŠ” ν•œκΈ€ λ‰΄μŠ€κΈ°μ‚¬μ— λ§΅ν•‘λ˜μ§€λ§Œ, λ¬Έμ„œμ™€ λ¬Έμ„œ λ‹¨μœ„μ˜ 맡핑일 뿐 λ¬Έμž₯ λŒ€ λ¬Έμž₯에 κ΄€ν•œ 정렬은 이루어져 μžˆμ§€ μ•Šλ‹€.
      ν•΄λ‹Ή 뢀뢄에 λŒ€ν•΄μ„œλŠ” seq2seqλͺ¨λΈκ³Ό Transformer λͺ¨λΈ ν•™μŠ΅μ‹œκΈ°μ— λ‹€μ‹œν•œλ²ˆ μ‚΄νŽ΄λ³΄κ² λ‹€.
       

3. ν† μΉ˜ν…μŠ€νŠΈ

ν† μΉ˜ν…μŠ€νŠΈ(torchtext)λŠ” μžμ—°μ–΄ 처리 문제 λ˜λŠ” ν…μŠ€νŠΈμ— κ΄€ν•œ λ¨Έμ‹ λŸ¬λ‹μ΄λ‚˜ λ”₯λŸ¬λ‹μ„ μˆ˜ν–‰ν•˜λŠ” 데이터λ₯Ό 읽고 μ „μ²˜λ¦¬ν•˜λŠ” μ½”λ“œλ₯Ό λͺ¨μ•„λ‘” λΌμ΄λΈŒλŸ¬λ¦¬μ΄λ‹€.
NLPμ—μ„œ μ‚¬μš©ν•˜λŠ” ν•™μŠ΅λ°μ΄ν„°λŠ” 크게 3가지 ν˜•νƒœλ‘œ λΆ„λ₯˜ν•  수 μžˆλ‹€.
X data
Y data
ν™œμš©λΆ„μ•Ό
μ½”νΌμŠ€
클래슀
ν…μŠ€νŠΈλΆ„λ₯˜, 감성뢄석
μ½”νΌμŠ€
-
μ–Έμ–΄ λͺ¨λΈ
μ½”νΌμŠ€
μ½”νΌμŠ€
κΈ°κ³„λ²ˆμ—­, μš”μ•½, μ§ˆμ˜μ‘λ‹΅

4. 토큰화(Tokenizing)

μžμ—°μ–΄ μ²˜λ¦¬λŠ” 일반적으둜 토큰화, 단어 집합(=vocabulary) 생성, μ •μˆ˜ 인코딩, νŒ¨λ”©, λ²‘ν„°ν™”μ˜ 과정을 κ±°μΉœλ‹€. 주어진 ν…μŠ€νŠΈλ₯Ό 단어 λ˜λŠ” 문자 λ‹¨μœ„λ‘œ 자λ₯΄λŠ” 것을 토큰화라고 ν•œλ‹€.
μ˜μ–΄μ˜ 경우 토큰화λ₯Ό μ‚¬μš©ν•˜λŠ” λ„κ΅¬λ‘œμ„œ λŒ€ν‘œμ μœΌλ‘œ spaCy와 NLTKκ°€ μžˆλ‹€.
import spacy spacy_en = spacy.load('en_core_web_sm') def tokenize(en_text): return [tok.text for tok in spacy_en.tokenizer(en_text)] en_text = "A Dog Run back corner near spare bedrooms" print(tokenize(en_text))
notion image
notion image
 
import nltk from nltk.tokenize import word_tokenize nltk.download('punkt') en_text = "A Dog Run back corner near spare bedrooms" print(f'NLTK : {word_tokenize(en_text)}')
notion image
notion image
μ˜μ–΄λŠ” μœ„μ™€ 같은 λ°©μ‹μ΄λ‚˜ 띄어쓰기λ₯Ό μ΄μš©ν•œ λΆ„μ ˆλ„ μΆ©λΆ„νžˆ κ°€λŠ₯ν•˜μ§€λ§Œ ν•œκ΅­μ–΄μ˜ 경우 토큰화 μž‘μ—…μ΄ 훨씬 κΉŒλ‹€λ‘­λ‹€. μ™œλƒν•˜λ©΄ ν•œκ΅­μ–΄μ˜ 경우 쑰사, 접사 λ“±μœΌλ‘œ 인해 λ‹¨μˆœ 띄어쓰기 λ‹¨μœ„λ‘œ λ‚˜λˆ„λ©΄ 같은 단어가 λ‹€λ₯Έ λ‹¨μ–΄λ‘œ μΈμ‹λ˜μ–΄μ„œ 단어 집합(vocabulary)의 크기가 λΆˆν•„μš”ν•˜κ²Œ 컀지기 λ•Œλ¬Έμ΄λ‹€.
  • 단어집합(vocabulary) : 쀑볡을 μ œκ±°ν•œ ν…μŠ€νŠΈμ˜ 총 단어 집합(set)을 μ˜λ―Έν•œλ‹€.
κ·Έλ ‡λ‹€λ©΄ ν•œκ΅­μ–΄ 띄어쓰기 ν† ν°ν™”λŠ” μ–΄λ–»κ²Œ 진행해야 ν•˜λŠ”κ°€?
λŒ€ν‘œμ μΈ ν˜•νƒœμ†Œ λΆ„μ„κΈ°λ‘œ mecab이 μžˆλ‹€.
λ‹€λ§Œ μœˆλ„μš° ν™˜κ²½μ—μ„œ konlpy의 Mecab() ν΄λž˜μŠ€κ°€ μ •μƒμž‘λ™ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 예제만 보이겠닀. λ§Œμ•½ μœˆλ„μš° ν™˜κ²½μ—μ„œ Mecab() 을 μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ eunjeon 을 μ‚¬μš©ν•˜λ©΄ λœλ‹€.
from konlpy.tag import Mecab tokenizer = Mecab() print(tokenizer.morphs(kor_text)) #['사과', '의', 'λ†€λΌμš΄', '효λŠ₯', '이', 'λΌλŠ”', 'κΈ€', '을', 'λ΄€', 'μ–΄', '.', 'κ·Έλž˜μ„œ', '였늘', '사과', 'λ₯Ό', 'λ¨Ή', '으렀고', 'ν–ˆ', 'λŠ”λ°', '사과', 'κ°€', '썩', 'μ–΄μ„œ', '슈퍼', '에', 'κ°€', 'μ„œ', '사과', 'λž‘', 'μ˜€λ Œμ§€', '사', 'μ™”', 'μ–΄']
 
μ΄μ œκΉŒμ§€ μ•Œμ•„λ³Έ κ°œλ…μ„ ν† λŒ€λ‘œ vocabularayλ₯Ό λ§Œλ“œλŠ” μ‹€μŠ΅μ„ μ‚΄νŽ΄λ³΄μž.
import urllib.request import pandas as pd from eunjeon import Mecab from nltk import FreqDist import numpy as np import matplotlib.pyplot as plt urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings.txt", filename="ratings.txt") data = pd.read_table('ratings.txt') print(data[:10]) print('전체 μƒ˜ν”Œμ˜ 수 : {}'.format(len(data))) sample_data = data[:100] sample_data['document'] = sample_data['document'].str.replace("[^γ„±-γ…Žγ…-γ…£κ°€-힣]", "") stop_words = ['의','κ°€','이','은','λ“€','λŠ”','μ’€','잘','걍','κ³Ό','도','λ₯Ό','으둜','자','에','와','ν•œ','ν•˜λ‹€'] tokenizer = Mecab() tokenized = [] for sentence in sample_data['document']: temp = tokenizer.morphs(sentence) temp = [word for word in temp if not word in stop_words] tokenized.append(temp) # print(tokenized[:10]) vocab = FreqDist(np.hsatck(tokenized)) # print(f'단어 μ§‘ν•©μ˜ 크기 : {len(vocab)}') vocab_size = 500 vocab = vocab.most_common(vocab_size) # μƒμœ„ vocab_size개의 λ‹¨μ–΄λ§Œ 보쑴 word_to_index = {word[0]:index+2 for index, word in enumerate(vocab)} word_to_index['pad'] = 1 word_to_index['unk'] = 0 encoded = [] for line in tokenized: temp = [] for w in line: try: temp.append(word_to_index[w]) except KeyError: temp.append(word_to_index['unk']) encoded.append(temp) max_len = max(len(l) for l in encoded) for line in encoded: if len(line) < max_len: line += [word_to_index['pad']] * (max_len - len(line))
 

5. torchtextλ₯Ό μ΄μš©ν•œ μ „μ²˜λ¦¬ κ°„μ†Œν™”

torchtextλ₯Ό μ΄μš©ν•˜λ©΄ μ „μ²˜λ¦¬λ₯Ό 더 쉽고 κ°„νŽΈν•˜κ²Œ 진행할 수 μžˆλ‹€.
μ•„λž˜λŠ” IMDB 데이터셋을 μ΄μš©ν•œ torchtext μ „μ²˜λ¦¬μ΄λ‹€.
from torchtext import data from torchtext.data import TabularDataset, Iterator import urllib.request import pandas as pd urllib.request.urlretrieve("https://raw.githubusercontent.com/LawrenceDuan/IMDb-Review-Analysis/master/IMDb_Reviews.csv", filename="IMDb_Reviews.csv") df = pd.read_csv('IMDb_Reviews.csv', encoding='latin1') train_df = df[:25000] test_df = df[25000:] train_df.to_csv("train_data.csv", index=False) test_df.to_csv("test_data.csv", index=False) # field Definition TEXT = data.Field(sequential=True, use_vocab=True, tokenize=str.split, lower=True, batch_first=True, fix_length=20) LABEL = data.Field(sequential=False, use_vocab=False, batch_first=False, is_target=True) # sequential : μ‹œν€€μŠ€ 데이터 μ—¬λΆ€. (Trueκ°€ κΈ°λ³Έκ°’) # use_vocab : 단어 집합을 λ§Œλ“€ 것인지 μ—¬λΆ€. (Trueκ°€ κΈ°λ³Έκ°’) # tokenize : μ–΄λ–€ 토큰화 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  것인지 지정. (string.split이 κΈ°λ³Έκ°’) # lower : μ˜μ–΄ 데이터λ₯Ό μ „λΆ€ μ†Œλ¬Έμžν™”ν•œλ‹€. (Falseκ°€ κΈ°λ³Έκ°’) # batch_first : λ―Έλ‹ˆ 배치 차원을 맨 μ•žμœΌλ‘œ ν•˜μ—¬ 데이터λ₯Ό 뢈러올 것인지 μ—¬λΆ€. (Falseκ°€ κΈ°λ³Έκ°’) # is_target : λ ˆμ΄λΈ” 데이터 μ—¬λΆ€. (Falseκ°€ κΈ°λ³Έκ°’) # fix_length : μ΅œλŒ€ ν—ˆμš© 길이. 이 길이에 λ§žμΆ°μ„œ νŒ¨λ”© μž‘μ—…(Padding)이 μ§„ν–‰λœλ‹€. # TabularDataset은 데이터λ₯Ό λΆˆλŸ¬μ˜€λ©΄μ„œ ν•„λ“œμ—μ„œ μ •μ˜ν–ˆλ˜ 토큰화 λ°©λ²•μœΌλ‘œ 토큰화λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. train_data, test_data = TabularDataset.splits( path=".", trainn='train_data.csv', test='test_data.csv', format='csv', fields=[('text', TEXT), ('label', LABEL)], skip_header=True) # path : 파일이 μœ„μΉ˜ν•œ 경둜. # format : λ°μ΄ν„°μ˜ 포맷. # fields : μœ„μ—μ„œ μ •μ˜ν•œ ν•„λ“œλ₯Ό 지정. 첫번째 μ›μ†ŒλŠ” 데이터 μ…‹ λ‚΄μ—μ„œ ν•΄λ‹Ή ν•„λ“œλ₯Ό ν˜ΈμΉ­ν•  이름, λ‘λ²ˆμ§Έ μ›μ†ŒλŠ” 지정할 ν•„λ“œ. # skip_header : λ°μ΄ν„°μ˜ 첫번째 쀄은 λ¬΄μ‹œ. TEXT.build_vocab(train_data, min_freq=10, max_size=10000) # min_freq : 단어 집합에 μΆ”κ°€ μ‹œ λ‹¨μ–΄μ˜ μ΅œμ†Œ λ“±μž₯ λΉˆλ„ 쑰건을 μΆ”κ°€. # max_size : 단어 μ§‘ν•©μ˜ μ΅œλŒ€ 크기λ₯Ό 지정. batch_size = 5 train_loader = Iterator(dataset=train_data, batch_size=batch_size) test_loader = Iterator(dataset=test_data, batch_size=batch_size) batch = next(iter(train_loader)) #첫번째 λ―Έλ‹ˆλ°°μΉ˜
 
Share article

Goodmorning, Ash πŸ‘‹