🤔 전처리 실습
자연어 처리 중 전처리 실습을 진행해보겠습니다.
참고도서
김기현의 자연어 처리 딥러닝 캠프-파이토치편(김기현, 한빛미디어)
Do it! BERT와 GPT로 배우는 자연어처리(이기창, 이지스퍼블리싱)
전처리에 대한 이론은 아래의 링크를 통해 확인하실 수 있습니다.
https://2t-hong.tistory.com/28
작성하기에 앞서 [ICT COG Academy] 인공지능 고급(언어)과정을 수강하며 복습을 위해 작성한 글임을 명시합니다.
🔎 코퍼스 수집하기
코퍼스를 수집하는 방법은 아래와 같습니다.
- 데이터를 직접 생성하는 방법
- 유료 데이터를 구매하는 방법
- 공개 데이터를 다운로드하여 활용하는 방법
- 웹 크롤링 등을 이용하여 수집하는 방법
- 웹 크롤링의 경우 잘못 사용하면 법적인 문제가 발생할 수 있습니다.
- 웹 사이트의 크롤링 허용 여부는 사이트의 robots.txt에서 확인 가능합니다.
간단한 크롤링을 해보겠습니다.
- urllib.request : 표준 파이썬 라이브러리
- 웹을 통해 데이터를 요청하는 함수, 쿠키 처리, 메타데이터 조작 등의 함수를 가지고 있음
- BeautifulSoup : bs4라고도 함
- 잘못된 HTML 수정 등의 기능을 가지며, HTML을 쉽게 탐색할 수 있는 XML형식의 파이썬 객체로 변환. 웹 탐색에 유용함
- Colab에는 기본적으로 BeautifulSoup가 설치되어 있음
from urllib.request import urlopen
bsObj
import requests
from bs4 import BeautifulSoup
import numpy as np
import pandas as pd
url_tmpl = 'https://finance.naver.com/item/main.nhn?code=%s'
url = url_tmpl % ('005930') # 삼성전자 코드
item_info = requests.get(url).text
soup = BeautifulSoup(item_info, 'html.parser')
finance_info = soup.select('div.section.cop_analysis div.sub_section')[0]
th_data = [item.get_text().strip() for item in finance_info.select('thead th')]
annual_date = th_data[3:7]
quarter_date = th_data[7:13]
finance_index = [item.get_text().strip() for item in finance_info.select('th.h_th2')][3:]
finance_data = [item.get_text().strip() for item in finance_info.select('td')]
finance_data = np.array(finance_data)
finance_data.resize(len(finance_index), 10)
finance_date = annual_date + quarter_date
finance = pd.DataFrame(data=finance_data[0:,0:], index=finance_index, columns=finance_date)
finance
삼성전자의 매출액, 영업이익 등의 정보를 가지고 있는 페이지를 크롤링하여 DataFrame의 형태로 불러왔습니다.
item_info
🔎 정규 표현식 사용하기
참고사이트 : http://pythonstudy.xyz/python/article/401-정규-표현식-Regex
Regex를 위한 모듈을 임포트 한 뒤 정규 표현식을 지정합니다.
# Regex를 위한 모듈 임포트
import re
# 정규 표현식 지정
regex1 = r"([\w]+\s*:? \s*)?\(?\+?([0-9]{1,3})?\-?[0-9]{2,3}(\)|\-)?[0-9]{3,4}\-?[0-9]{4}"
이후 해당 정규 표현식을 만족하는 경우 REMOVED로 치환합니다.
정규 표현식을 만족하지 않는 부분은 그대로 출력합니다.
x1 = "Seokhwan: +82-10-1234-5678"
print(re.sub(regex1, "REMOVED", x1)) # 해당 정규 표현식을 만족하는 경우 "REMOVED"로 치환
REMOVED
x2 = "Seok hwan: 010-1234-5678" # 앞의 "Seok공백"은 해당 정규 표현식을 만족하지 않고 hwan부터 이후까지는 만족함
print(re.sub(regex1, "REMOVED", x2))
Seok REMOVED
x2 = "Seok hwan: 010-1234-5678 KOREA" # "공백KOREA" 부분도 해당 정규 표현식을 만족하지 않음
print(re.sub(regex1, "REMOVED", x2))
Seok REMOVED KOREA
이러한 방법은 개인정보를 지우는 데에 사용됩니다.
# 단순히 숫자, -의 배열만으로 구성된 정규 표현식 지정
regex2 = r"[0-9]{3}-[0-9]{4}-[0-9]{4}"
x2 = "Seokhwan: 010-1234-5678"
print(re.sub(regex2, "***-****-****", x2))
Seokhwan: ***-****-****
x2 = "010-1234-5678 : Seokhwan"
print(re.sub(regex2, "***-****-****", x2))
***-****-**** : Seokhwan
text = "문의사항이 있으면 032-232-3245 으로 연락주시기 바랍니다."
regex = re.compile(r'(\d{3})-(\d{3}-\d{4})')
matchobj = regex.search(text)
areaCode = matchobj.group(1)
num = matchobj.group(2)
fullNum = matchobj.group()
print(areaCode, num) # 032 232-3245
032 232-3245
인덱싱 대신 그룹 명을 지정하여 사용할 수도 있습니다.(Named Capturing Group)
Named Capturing Group을 사용하는 방법은 (?P<그룹명>정규식) 와 같이 정규식 표현 앞에 ?P<그룹명>을 사용합니다.
이후 MatchObject에서 group('그룹명') 을 호출하면 캡쳐된 그룹 값을 얻을 수 있습니다.
Named Capturing Group을 사용하는 방법은 (?P<그룹명>정규식) 와 같이 정규식 표현 앞에 ?P<그룹명>을 사용합니다.
이후 MatchObject에서 group('그룹명') 을 호출하면 캡쳐된 그룹 값을 얻을 수 있습니다.
regex = re.compile(r'(?P<area>\d{3})-(?P<num>\d{3}-\d{4})')
matchobj = regex.search(text)
areaCode = matchobj.group("area")
num = matchobj.group("num")
print(areaCode, num) # 032 232-3245
032 232-3245
🔎 문장 단위 분절
- NLTK (Natural Language Toolkit)을 사용해보겠습니다.
- Punkt Sentence Tokenizer 모듈
- sent_tokenize(): 파이썬에서 문자열로 인식하는 텍스트는 무엇이든지 받아서 문장 별로 토큰화 할 수 있다.
- word_tokenize(): 파이썬에서 문자열로 인식하는 텍스트는 무엇이든지 받아서 단어 별로 토큰화 할 수 있다.
- Punkt Sentence Tokenizer 모듈
import nltk
# NLTK의 Punkt sentence tokenizer 모듈 다운로드
nltk.download('punkt') # NLTK에서 사용하고자 하는 데이터를 따로 다운받아와야 한다. NLTK가 매우 크기 때문
NLTK를 간단히 이용해서 문장 단위로 나눠보겠습니다.
# import nltk
text = "The Matrix is everywhere its all around us, here even in this room. You can see it out your window or on your television. You feel it when you go to work, or go to church or pay your taxes."
tokens = nltk.sent_tokenize(text)
print(tokens)
['The Matrix is everywhere its all around us, here even in this room.', 'You can see it out your window or on your television.', 'You feel it when you go to work, or go to church or pay your taxes.']
text = "자연어 처리는 인공지능의 한 줄기 입니다. 시퀀스 투 시퀀스의 등장 이후로 딥러닝을 활용한 자연어 처리는 새로운 전기를 맞이하게 되었습니다. 문장을 받아 단순히 수치로 나타내던 시절을 넘어, 원하는대로 문장을 만들어낼 수 있게 된 것입니다."
tokens = nltk.sent_tokenize(text)
print(tokens)
['자연어 처리는 인공지능의 한 줄기 입니다.', '시퀀스 투 시퀀스의 등장 이후로 딥러닝을 활용한 자연어 처리는 새로운 전기를 맞이하게 되었습니다.', '문장을 받아 단순히 수치로 나타내던 시절을 넘어, 원하는대로 문장을 만들어낼 수 있게 된 것입니다.']
파일을 읽어들여서 나눠보겠습니다.
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
ROOT_PATH = '/content/drive/MyDrive/2022/딥러닝/자연어처리/실습자료/data'
import nltk
# NLTK의 Punkt sentence tokenizer 모듈 다운로드
# punkt: 기본적인 문장 분절을 위한 사전학습을 수행한 라이브러리
nltk.download('punkt')
한 라인에 여러 문장이 들어있는 경우인 아래와 같은 텍스트를 분절해보겠습니다.
import re
from nltk.tokenize import sent_tokenize
file_path = ROOT_PATH + "input_ko.txt"
file = open(file_path, "r")
file_text = file.read()
file_text
sentences = sent_tokenize(file_text)
print(sentences)
['자연어처리는 인공지능의 한 줄기 입니다.', '시퀀스 투 시퀀스의 등장 이후로 \n딥러닝을 활용한 자연어처리는 새로운 전기를 맞이하게 되었습니다.', '문장을 \n받아 단순히 수치로 나타내던 시절을 넘어, 원하는대로 문장을 만들어낼 수 \n있게 된 것 입니다.', '이에 따라 이전까지 큰 변화가 없었던 자연어처리 분야\n의 연구는 폭발적으로 늘어나기 시작하여, 곧 기계번역 시스템은 신경망에 \n의해 정복 당하였습니다.', '또한 attention 기법의 고도화로 전이학습이 발전\n하면서, QA 문제도 사람보다 정확한 수준이 되었습니다.']
아래와 같이 열 바꿈과 문장의 끝 부분으로 분절된 것을 알 수 있습니다.
if file_text.strip() != "":
line = re.sub(r'([a-z])\.([A-Z])', r'\1. \2', file_text.strip())
sentences = sent_tokenize(line)
for s in sentences:
if s != "":
print(s)
자연어처리는 인공지능의 한 줄기 입니다.
시퀀스 투 시퀀스의 등장 이후로
딥러닝을 활용한 자연어처리는 새로운 전기를 맞이하게 되었습니다.
문장을
받아 단순히 수치로 나타내던 시절을 넘어, 원하는대로 문장을 만들어낼 수
있게 된 것 입니다.
이에 따라 이전까지 큰 변화가 없었던 자연어처리 분야
의 연구는 폭발적으로 늘어나기 시작하여, 곧 기계번역 시스템은 신경망에
의해 정복 당하였습니다.
또한 attention 기법의 고도화로 전이학습이 발전
하면서, QA 문제도 사람보다 정확한 수준이 되었습니다.
# 정규화 과정을 통해서 정리하지 않아도 잘 동작함 : NLTK.sent_tokenize()에 이미 내장된 상태
여러 라인에 걸쳐 한 문장이 들어있는 경우의 예시를 알아보겠습니다.
file_path = ROOT_PATH + "input_ko2.txt"
file = open('/content/drive/MyDrive/2022/딥러닝/자연어처리/실습자료/data/input_ko.txt', "r")
file_text = file.read()
file_text
strip() 메서드를 통해 "\n"을 기준으로 나누어진 리스트를 추가합니다.
Python String strip()은 다음을 반환합니다.
- 제거할 문자가 지정되지 않은 경우 시작과 끝에서 공백이 제거된 원래 문자열입니다.
- 문자열의 시작 또는 끝에 공백이 없는 경우 문자열을 있는 그대로 반환하고 원래 문자열과 일치시킵니다.
- 문자 매개변수가 제공되고 문자가 일치하면 문자열의 시작 또는 끝에 있는 문자가 원래 문자열에서 제거되고 나머지 문자열이 반환됩니다.
- 주어진 문자가 원래 문자열의 시작 또는 끝과 일치하지 않는 경우 문자열을 있는 그대로 반환합니다.
아래의 경우 문자열의 시작 또는 끝에 공백이 없는 경우 그대로 반환하여 buf에 해당 문자열을 추가합니다.
즉 "자연어처리는 인공지능의 한 줄기 입니다. 시퀀스 투 시퀀스의 등장 이후로\n"가 for 문의 첫 번째 line에 삽입된 이후 문장의 한 문장 씩 출력되는 것을 알 수 있습니다.
buf = []
text = file_text.split("\n")
for line in text:
if line.strip() != "":
buf += [line.strip()]
sentences = sent_tokenize(" ".join(buf))
if len(sentences) > 1:
buf = sentences[1:]
print(sentences[0])
자연어처리는 인공지능의 한 줄기 입니다.
시퀀스 투 시퀀스의 등장 이후로 딥러닝을 활용한 자연어처리는 새로운 전기를 맞이하게 되었습니다.
문장을 받아 단순히 수치로 나타내던 시절을 넘어, 원하는대로 문장을 만들어낼 수 있게 된 것 입니다.
이에 따라 이전까지 큰 변화가 없었던 자연어처리 분야 의 연구는 폭발적으로 늘어나기 시작하여, 곧 기계번역 시스템은 신경망에 의해 정복 당하였습니다.
# 그런데 그냥 이렇게 해결해도 문제 없음
text = file_text.replace("\n", "")
sentences = sent_tokenize(text)
sentences
['자연어처리는 인공지능의 한 줄기 입니다.',
'시퀀스 투 시퀀스의 등장 이후로 딥러닝을 활용한 자연어처리는 새로운 전기를 맞이하게 되었습니다.',
'문장을 받아 단순히 수치로 나타내던 시절을 넘어, 원하는대로 문장을 만들어낼 수 있게 된 것 입니다.',
'이에 따라 이전까지 큰 변화가 없었던 자연어처리 분야의 연구는 폭발적으로 늘어나기 시작하여, 곧 기계번역 시스템은 신경망에 의해 정복 당하였습니다.',
'또한 attention 기법의 고도화로 전이학습이 발전하면서, QA 문제도 사람보다 정확한 수준이 되었습니다.']
🔎 분절
nltk.download('perluniprops')
# 버전 문제로 인해 moses를 import 하기 위해 3.2.5버전 설치
!pip install nltk==3.2.5
import sys, fileinput
from nltk.tokenize.moses import MosesTokenizer
import sys, fileinput
from nltk.tokenize.moses import MosesTokenizer
nltk.download('nonbreaking_prefixes')
t = MosesTokenizer()
file_path = ROOT_PATH + "input_en.txt"
file = open(file_path, "r")
file_text = file.read()
file_text
sentences = sent_tokenize(file_text)
tokens = t.tokenize(sentences, escape=False)
tokens
['[',
'"',
'Natural',
'language',
'processing',
'is',
'one',
'of',
'biggest',
'stream',
'in',
'artificial',
'intelligence',
',',
'and',
'it',
'becomes',
'very',
'popular',
'after',
'seq2seq',
"'s",
'invention',
'.',
'"',
',',
"'",
'However',
',',
'in',
'order',
'to',
'make',
'a',
'strong',
'A.I',
'.',
',',
'there',
'are',
'still',
'many',
'challenges',
'remain',
'.',
"'",
',',
"'",
'I',
'believe',
'that',
'we',
'can',
'breakthrough',
'these',
'barriers',
'to',
'get',
'strong',
'artificial',
'intelligence',
'.',
"'",
']']
한글을 영문처럼 띄어쓰기로 분절하고자 하면 NLTK에서는 잘 되지 않는데 이유는 아래와 같습니다.
- 토크나이저에 충분한 정보, 데이터가 반영되어 있지 않기 때문
- KoNLPy 등의 한국어 지원 토크나이저가 필요한 이유
file_path = ROOT_PATH + "input_ko.txt"
file = open(file_path, "r")
file_text = file.read()
file_text
sentences = sent_tokenize(file_text)
sentences
['자연어처리는 인공지능의 한 줄기 입니다.',
'시퀀스 투 시퀀스의 등장 이후로 \n딥러닝을 활용한 자연어처리는 새로운 전기를 맞이하게 되었습니다.',
'문장을 \n받아 단순히 수치로 나타내던 시절을 넘어, 원하는대로 문장을 만들어낼 수 \n있게 된 것 입니다.',
'이에 따라 이전까지 큰 변화가 없었던 자연어처리 분야\n의 연구는 폭발적으로 늘어나기 시작하여, 곧 기계번역 시스템은 신경망에 \n의해 정복 당하였습니다.',
'또한 attention 기법의 고도화로 전이학습이 발전\n하면서, QA 문제도 사람보다 정확한 수준이 되었습니다.']
tokens = t.tokenize(sentences, escape=False)
tokens
['[',
"'",
'자',
'연',
'어',
'처',
'리',
'는',
'인',
'공',
'지',
'능',
'의',
'한',
'줄',
'기',
'입',
'니',
'다',
'.',
"'",
',',
"'",
'시',
'퀀',
'스',
'투',
'시',
'퀀',
'스',
'의',
'등',
'장',
'이',
'후',
'로',
'\\',
'n',
'딥',
'러',
'닝',
'을',
'활',
'용',
'한',
'자',
'연',
'어',
'처',
'리',
'는',
'새',
'로',
'운',
'전',
'기',
'를',
'맞',
'이',
'하',
'게',
'되',
'었',
'습',
'니',
'다',
'.',
"'",
',',
"'",
'문',
'장',
'을',
'\\',
'n',
'받',
'아',
'단',
'순',
'히',
'수',
'치',
'로',
'나',
'타',
'내',
'던',
'시',
'절',
'을',
'넘',
'어',
',',
'원',
'하',
'는',
'대',
'로',
'문',
'장',
'을',
'만',
'들',
'어',
'낼',
'수',
'\\',
'n',
'있',
'게',
'된',
'것',
'입',
'니',
'다',
'.',
"'",
',',
"'",
'이',
'에',
'따',
'라',
'이',
'전',
'까',
'지',
'큰',
'변',
'화',
'가',
'없',
'었',
'던',
'자',
'연',
'어',
'처',
'리',
'분',
'야',
'\\',
'n',
'의',
'연',
'구',
'는',
'폭',
'발',
'적',
'으',
'로',
'늘',
'어',
'나',
'기',
'시',
'작',
'하',
'여',
',',
'곧',
'기',
'계',
'번',
'역',
'시',
'스',
'템',
'은',
'신',
'경',
'망',
'에',
'\\',
'n',
'의',
'해',
'정',
'복',
'당',
'하',
'였',
'습',
'니',
'다',
'.',
"'",
',',
"'",
'또',
'한',
'attention',
'기',
'법',
'의',
'고',
'도',
'화',
'로',
'전',
'이',
'학',
'습',
'이',
'발',
'전',
'\\',
'n',
'하',
'면',
'서',
',',
'QA',
'문',
'제',
'도',
'사',
'람',
'보',
'다',
'정',
'확',
'한',
'수',
'준',
'이',
'되',
'었',
'습',
'니',
'다',
'.',
"'",
']']
🔎 병렬 코퍼스 정렬
- 주로 CTK(Champollion Tool kit, Perl로 작성됨) 등의 도구를 활용합니다
- 직접 구현하려면 많은 시간과 노력을 요구합니다
🔎 서브워드 분절
- Tokenizing 부분에서 다루겠습니다.
참조
다음 글에서는 토큰화 실습을 진행해보겠습니다.
'AI > 자연어 처리' 카테고리의 다른 글
[AI] 자연어 처리 - 단어의 표현(1) (0) | 2022.08.11 |
---|---|
[AI] 자연어 처리 - 전처리와 토큰화(3) (0) | 2022.08.11 |
[AI] 자연어 처리 - 전처리와 토큰화(2) (2) | 2022.08.05 |
[AI] 자연어 처리 - 전처리와 토큰화(1) (2) | 2022.08.01 |
[AI] 자연어 처리 - PyTorch 실습 (0) | 2022.07.30 |