본문 바로가기
Python/NLP

[파이썬, Python] 자연어처리 - 데이터 전처리 실습하기!

by coding-choonsik 2023. 7. 6.
728x90
반응형
SMALL

1. 데이터 전처리 실습

# 뉴스 기사 크롤링 라이브러리
!pip install newspaper3k

# 불러오기
import newspaper

#  라이브러리 지원 언어들 보기
newspaper.languages()

▲ 한국어 지원

 

# 웹 기사의 내용, 제목, 작성자, 날짜 등과 같은 다양한 정보를 추출
from newspaper import Article

 

✅ 기사의 URL을 가져와 읽어들이기

URL = 'https://v.daum.net/v/20230623105401779'

article = Article(URL, language='ko')

 

✅ 기사 다운로드하고 파싱하기 - 제목과 내용

article.download()
article.parse()  # 제목과 내용을 파싱하여 가져옴

print('title:', article.title)
print('content:', article.text)

▲기사의 제목과 내용

 

✅ 기사에 아래와 같은 내용을 추가함(전처리를 위해)

additional_info = [
    '※ 기자 김사과(apple@apple.com) 취재 반"하나(banana@banana.com)',
    '<h2>애플 헤드셋 쓰고, 바로 문자 번역하고 정보 얻는다</h2>',
    '이 기사는 임시 데이터임을 알립니다.',
    'Copyrights© Pressian.com',
    '<br> 👆 이 기사는 IT 섹션으로 분류 했습니다 ↑ </br>',
    '#기사 #IT #애플 #기술'
]
context = article.text.split('\n')
context += additional_info

print(context)

>>>['(지디넷코리아=이정현 미디어연구소)애플이 혼합현실(MR) 헤드셋 ‘비전 프로’ 전용 운영체제 ‘비전OS’ 베타 버전을 최근 공개하면서 헤드셋에 탑재된 기능들이 속속 공개되고 있다.', '', 'IT매체 맥루머스는 22일(현지시간) 비전OS에 아이폰·아이패드에 탑재된 비주얼 룩업(Visual Lookup) 기능과 유사한 ‘비주얼 서치’(Visual Search) 기능이 탑재됐다고 보도했다.', '', '애플 비전 프로 (사진=애플)', '', '아이폰의 비주얼 룩업 기능은 구글 렌즈나 네이버 스마트렌즈처럼 카메라로 사물이나 책, 식물, 랜드마크 등을 인식해 자동으로 추가 정보를 표시해주는 기능이다.', '', '비전OS에 탑재된 비주얼 서치는 헤드셋을 통해 사용자가 보는 사물에 대한 정보를 얻고, 주변 세계의 텍스트를 감지해 상호 작용할 수 있는 기능이다. 헤드셋 너머로 보이는 문자를 사용자가 복사해 앱에 붙여 넣은 다음 17개의 언어로도 번역할 수도 있다.', '', '구글 렌즈는 카메라로 사물 정보를 인식해 제공한다. (사진=구글)', '', '해당 기능으로 인쇄된 유인물에 웹사이트 주소가 있는 경우 헤드셋으로 링크를 읽어 들인 후 사파리 브라우저 창으로 웹 사이트를 볼 수 있으며, 레시피에서 요리 재료의 용량을 그램(g)이나 온스(oz) 단위로 변환이 가능하다.', '', '특히, 실시간 텍스트 번역은 여행 중이나 문자를 빠르게 번역해야 하는 상황에서 유용할 것이라고 해당 매체는 전했다. 이 기능은 아이폰 카메라가 사진 속 텍스트를 감지해 번역하는 것과 비슷하게 작동했다고 맥루머스는 덧붙였다.', '', '비전 OS 1.0 개발자용 베타는 21일 출시됐고, 같은 날 개발자 지원 개발자 키트(SDK)도 함께 출시됐다. 해당 SDK는 X코드 15 베타 2를 통해 접근할 수 있으며, 아직은 개발자가 비전 프로를 사용할 수 없지만 애플은 다음 달부터 테스트를 허용할 예정이다.', '', '이정현 미디어연구소(jh7253@zdnet.co.kr)', '※ 기자 김사과(apple@apple.com) 취재 반"하나(banana@banana.com)', '<h2>애플 헤드셋 쓰고, 바로 문자 번역하고 정보 얻는다</h2>', '이 기사는 임시 데이터임을 알립니다.', 'Copyrights© Pressian.com', '<br> 👆 이 기사는 IT 섹션으로 분류 했습니다 ↑ </br>', '#기사 #IT #애플 #기술']

 

✅ context를 인덱스번호와 내용을 같이 출력하기

for i, text in enumerate(context):
  print(i, text)


1-1. 불용어 제거

# 불용어 사전 정의
stopwords = ['이하', '바로', '@','👆', '↑', '※']

# 불용어 제거 함수
def delete_stopwords(context):
  preprocessed_text = []
  for text in context:
    text = [w for w in text.split(' ') if w not in stopwords] # text를 띄어쓰기로 구분하고 불용어사전에 없는 데이터만 리스트에 저장
    preprocessed_text.append(' '.join(text)) # 띄어쓰기를 적용하여 리스트에 추가
  return preprocessed_text
  
processed_context = delete_stopwords(context)

for i, text in enumerate(processed_context):
  print(i, text)

▲ 불용어 제거


1-2. HTML 태그 제거

# 정규식을 사용하는 라이브러리
import re

# HTML 태그를 제거하는 함수
# re.sub(r'정규식', '대체될 텍스트', '문자열'): 문자열에서 패턴을 검색하고, 해당 패턴을 다른 문자열로 대체하는 역할
def delete_html_tag(context):
    processed_context = []
    for text in context:
        text = re.sub(r'<[%>]+Ws+(?=<)|<[^>]+>', '', text).strip()  # text에서 정규식에 해당하는 조건이 True가 되면 ''(제거)
        if text:
          processed_context.append(text)

    return processed_context
    
processed_context = delete_html_tag(processed_context)

for i, text in enumerate(processed_context):
  print(i, text)


1-3. 문장 분리

  • 학습 데이터를 구성할 때 입력 데이터의 단위를 설정하기 애매해지므로 문장 단위로 모델이 학습하도록 유도하기 위해 문장 분리가 필요
  • 한국어 문장 분리기 중, 가장 성능이 우수한 것으로 알려지 kss 라이브러리를 사용
  • https://github.com/hyunwoongko/kss
 

GitHub - hyunwoongko/kss: Kss: A Toolkit for Korean sentence segmentation

Kss: A Toolkit for Korean sentence segmentation. Contribute to hyunwoongko/kss development by creating an account on GitHub.

github.com

 

!pip install kss

import kss

# 문장을 분리하기 위한 함수
def sentence_separator(processed_context):
  splited_context = []

  for text in processed_context:
    text = text.strip()  # 양쪽 공백 제거
    if text:
      splited_text = kss.split_sentences(text)   # 문장 단위로 분리
      splited_context.extend(splited_text)
  return splited_context
  
splited_context = sentence_separator(processed_context)

for i, text in enumerate(splited_context):
  print(i, text)

▲ 15줄에서 18줄로 문장이 분리되어 증가됨


1-4. 이메일 제거

# 정규식을 통해 이메일 제거하는 함수
def delete_email(context):
    preprocessed_text = []
    for text in context:
        text = re.sub(r'[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+', '', text).strip()
        if text:
            preprocessed_text.append(text)
    return preprocessed_text
    
processed_context = delete_email(splited_context)

for i, text in enumerate(processed_context):
  print(i, text)

▲ 이메일 제거


1-5. 해시태그 제거

# 정규식을 통한 해시태그 제거
def delete_hashtag(context):
  preprocessed_text = []
  for text in context:
    text = re.sub(r'#\S+', '', text).strip()
    if text:
      preprocessed_text.append(text)
  return preprocessed_text
  
processed_context = delete_hashtag(processed_context)

for i, text in enumerate(processed_context):
  print(i, text)

▲ 해시태그가 있던 문장 제거


1-6. 저작권 관련 텍스트 제거

def delete_copyright(context):
    re_patterns = [
        r'\<저작권자(\(c\)|©|(C)|(\(C\))).+?\>',
        r'(Copyrights)|(\(c\))|(\(C\))|©|(C)|'
    ]
    preprocessed_text = []
    for text in context:
        for re_pattern in re_patterns:  # 위 패턴들만큼 반복
            text = re.sub(re_pattern, "", text).strip()
        if text:
            preprocessed_text.append(text)
    return preprocessed_text
    
processed_context = delete_copyright(processed_context)

for i, text in enumerate(processed_context):
  print(i, text)

▲ 저작권표시 제거


1-7. 반복 횟수가 많은 문자 정규화

# soynlp: 한국어 텍스트 데이터를 분석하고 처리하는 라이브러리
!pip install soynlp

from soynlp.normalizer import *

# num_repeats=2: 두 번 이상 반복되는 글자를 정규화
print(repeat_normalize('ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ', num_repeats=2))
>>> ㅋㅋ

# 특수문자는 제외, 자음/모음에 한하여 정규화
print(repeat_normalize('야!!!!!!!!!!!너!!!!!!!!!지금 뭐함???????????ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ',num_repeats=2))
>>> 야!!!!!!!!!!!너!!!!!!!!!지금 뭐함???????????ㅠㅠ

1-8. 중복 문장 정규화

# OrderedDict: 순서가 있는 딕셔너리(Dictionary) 타입
# 기존의 딕셔너리와는 달리 아이템이 추가된 순서를 기억
from collections import OrderedDict

# 중복된 문장을 제거하는 함수
def duplicated_sentence_normalizer(context):
  context = list(OrderedDict.fromkeys(context))
  return context
  
normalized_context = duplicated_sentence_normalizer(processed_context)

for i, text in enumerate(normalized_context):
  print(i, text)

▲ 중복된 문장이 없어서 제거 확인이 안됨,,


1-9. Cleaning

  • 형태소 분석 기반 필터링
  • 데이터 후처리
!pip install konlpy

from konlpy.tag import Mecab

!pip install mecab-python
!bash <(curl -s https://raw.githubusercontent.com/konlpy/konlpy/master/scripts/mecab.sh)

# 객체 생성
mecab = Mecab()
morphs = mecab.pos('아버지가방에들어가신다', join=False)
print(morphs)

>>> [('아버지', 'NNG'), ('가', 'JKS'), ('방', 'NNG'), ('에', 'JKB'), ('들어가', 'VV'), ('신다', 'EP+EC')]
# 형태소 분석 필터링
# 명사(NN), 동사(V), 형용사(J)의 포함 여부에 따라 문장 필터링
# 주어진 텍스트에서 명사, 동사, 형용사가 모두 포함된 텍스트들만을 추출하여 반환
def morph_filter(context):
  NN_TAGS = ['NNG', 'NNP', 'NNB', 'NP']
  V_TAGS = ['VV', 'VA', 'VX', 'VCP', 'VCN', 'XSN', 'XSA', 'XSV']
  J_TAGS = ['JKS', 'J', 'JO', 'JK', 'JKC', 'JKG', 'JKB', 'JKV', 'JKQ',
              'JX', 'JC', 'JKI', 'JKO', 'JKM', 'ETM']
  preprocessed_text = []
  for text in context:
      morphs = mecab.pos(text, join=False)

      # 존재하는지에 대한 여부
      nn_flag = False
      v_flag = False
      j_flag = False
      for morph in morphs:
        pos_tags = morph[1].split("+")   # ('신다', 'EP+EC') -> ['EP', 'EC']
        for pos_tag in pos_tags:
          if not nn_flag and pos_tag in NN_TAGS:  # True면 False
              nn_flag = True
          if not v_flag and pos_tag in V_TAGS:
              v_flag = True
          if not j_flag and pos_tag in J_TAGS:
              j_flag = True
        if nn_flag and v_flag and j_flag:   # 모두 True라면
          preprocessed_text.append(text)
          break
  return preprocessed_text
  
  post_processed_context = morph_filter(normalized_context)
  
  post_processed_context


1-10. 문장 길이 기반 필터링

def min_max_filter(min_len, max_len, context):
  filtered_text = []

  for text in context:
    if min_len <= len(text) and len(text) < max_len:
      filtered_text.append(text)

  return filtered_text
  
  post_processed_context = min_max_filter(20, 60, post_processed_context) # 20글자 이상 60글자 미만의 문장들만 반환
  
  post_processed_context
  
  >>> ['헤드셋 너머로 보이는 문자를 사용자가 복사해 앱에 붙여 넣은 다음 17개의 언어로도 번역할 수도 있다.',
 '구글 렌즈는 카메라로 사물 정보를 인식해 제공한다. (사진=구글)',
 '이 기능은 아이폰 카메라가 사진 속 텍스트를 감지해 번역하는 것과 비슷하게 작동했다고 맥루머스는 덧붙였다.',
 '이 기사는 임시 데이터임을 알립니다.',
 '이 기사는 IT 섹션으로 분류 했습니다']

1-11. 토큰화

from tensorflow.keras.preprocessing.text import Tokenizer

tokenizer = Tokenizer()

# fit_on_texts: 토큰끼리 분리하여 문장을 처리하여 학습
tokenizer.fit_on_texts(post_processed_context)  

tokenizer
>>> <keras.preprocessing.text.Tokenizer at 0x7fad280277c0>

# word_index: 토큰화된 토큰들이 인덱스화됨
word2idx = tokenizer.word_index  # 토큰화된 토큰들이 인덱스화됨
word2idx

 # word2idx의 데이터들은 key와 value로 나누어 딕셔너리로 저장
idx2word = {value:key for key, value in word2idx.items()}

idx2word  # 인덱스값: 단어 형태로 출력

 

# 토큰 인코딩
encoded = tokenizer.texts_to_sequences(post_processed_context)
encoded   # 토큰값으로 인코딩하여 나옴

 

# 임베딩
vocab_size = len(word2idx) +1
print(f'단어 사전의 크기는: {vocab_size}개')

>>> 단어 사전의 크기는: 45개

print(encoded[0]) # post_preproceesed_context의 첫번째 문장의 인덱스 값들
>>> [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

 

728x90
반응형
LIST