본문 바로가기
TIL _Today I Learned/2024.09

[DAY 49] 자연어 처리(NLP)

by gamdong2 2024. 9. 24.
[천재교육] 프로젝트 기반 빅데이터 서비스 개발자 양성 과정 9기
학습일 : 2024.09.24

📕 학습 목록

  • 정형 데이터 vs 비정형 데이터
  • 자연어 처리
  • Tokenizer : Okt, Kkma, Hannanum
  • 워드 클라우드
  • 정규식 처리

 

📗 기억할 내용

1) 정형 데이터 (Structured Data)

  • 정의 : 규격화된 데이터로, 행과 열 형태로 저장되며 관계형 데이터베이스(RDB)에 적합
  • 특징
    • 숫자텍스트를 포함하더라도 고정된 형식에 맞춰 저장됨
    • 분석처리가 쉽고, 계산이 가능하며, 빠른 검색이 가능
    • RDB(Relational Database) 또는 스프레드시트에서 주로 저장 및 관리
    • SQL을 사용하여 관리 및 분석이 용이
    • 데이터 마이닝 가능
  • 예시 : 엑셀 시트, 관계형 데이터베이스의 테이블 데이터, 금융 거래 기록, 학생 성적 데이터
  • 한계
    • 분석할 수 있는 패턴결과가 미리 정의된 틀 안에서만 가능
    • 이상치가 존재할 경우, 데이터의 품질이 쉽게 저하될 수 있음
    • 이미 많은 분석이 이루어져 새로운 분석 시도에 제한이 있음
    • 데이터 생성과 동시에 분석이 끝남 : 예를 들어, 학생 성적 데이터는 점수와 석차 등의 정보를 생성하면 즉시 분석할 수 있으며, 분석의 새로운 의미를 발견하기 어려움
    • 이미 분석이 완료된 데이터가 많음 : 많은 정형 데이터는 이미 분석되었기 때문에 새로운 통찰을 도출하기 어려움
    • 이상치 문제 : 정형 데이터에서 이상치가 존재하면 데이터 품질이 쉽게 저하될 수 있어, 신뢰성 있는 분석이 어려움

2) 비정형 데이터 (Unstructured Data)

  • 정의 : 고정된 구조가 없는 데이터로, RDB에 적합하지 않으며 다양한 형태를 가질 수 있음
  • 특징 
    • 숫자로 표현되지 않거나, 계산이 어려운 데이터를 포함
    • 데이터 수집 과정에서 어떤 데이터를 다룰 것인지 명확하게 정의하는 것이 중요
    • 전체 데이터의 80%를 차지할 만큼 양이 많음
    • 문서, 이미지, 비디오, 오디오 파일, 텍스트 데이터 등 다양한 형식이 존재
    • RDB에 저장하기 어려움
    • 비정형 데이터를 분석하려면, 데이터 전처리(정형 데이터 변환하거나 특수한 분석 기법이 필요)가 필수적이며, 제대로 처리하지 못할 경우 분석 결과의 신뢰도가 떨어질 위험이 큼
    • 데이터 간 일관성이 부족할 수 있으며, 전처리 과정이 필수적임
    • 개인정보 관련 데이터가 포함될 가능성이 높으며, 분리하기 어렵다는 문제가 있음
  • 예시 : 이메일, 소셜 미디어 게시물, PDF 문서, 이미지, 동영상 파일, 자유 형식 텍스트 데이터
  • 한계
    • 비정형 데이터를 분석하려면 추가적인 가공이 필요
    • 데이터 품질이 떨어질 수 있고, 일관성을 유지하기 어려움
    • 전처리가 제대로 이루어지지 않으면 분석 결과의 신뢰도가 낮아짐

3) 반정형 데이터 (Semi-Structured Data)

  • 정의: 데이터가 고정된 스키마를 따르지는 않지만, 데이터 내에 일부 구조적 요소가 존재하는 데이터
  • 특징:
    • 정형 데이터비정형 데이터의 중간 형태로, 특정 구조는 존재하지만 모든 요소가 규칙적으로 저장되지는 않음
    • 주로 태그, 키-값 쌍 등의 형식을 따르며, 분석을 위해서 약간의 구조화된 정보를 포함
    • 비정형 데이터처럼 특수한 방법으로 분석할 수 있음
  • 예시: JSON, XML, 로그 파일, CSV(일부 형태에서)
  • 분석:
    • 비정형 데이터처럼 처리하지만, 내부에 일부 구조적 정보가 있으므로 처리와 분석이 상대적으로 쉬움

 

📘 코드 실습

 

1. 자연어 처리 및 시각화(워드 클라우드)

# 1. 패키지 임포트
import pandas as pd
from konlpy.tag import Okt  # Okt : 형태소 분석기(tokenizer)

# 2. Okt 객체 초기화
# Okt : 클래스 -> 초기화 해줘야 함
# tokenizer : Okt 의 객체(인스턴스)
# 텍스트 데이터를 토큰(형태소 단위)으로 분리하는 역할
tokenizer = Okt()

# 3. 샘플 텍스트 설정
sample_text = "오, 그녀는 정말 횃불에게 찬란히 타오르는 법을 가르치누나!"

# 4. 형태소 분석 수행 (morphs() 사용)
# morphs() : 텍스트를 형태소 단위로 분리하는 메서드
tokenizer.morphs(sample_text)

# 5. 다양한 형태소 분석기 사용
from konlpy.tag import Okt, Kkma, Hannanum  # 여러 형태소 분석기 종류

# 6. 각 형태소 분석기 초기화 및 사용 예시
okt = Okt()
kkma = Kkma()
han = Hannanum()

# 각 형태소 분석기의 분석 결과를 출력
for tokenizer in [okt, kkma, han]:
    print(tokenizer, "를 사용한 결과")
    print(tokenizer.morphs(sample_text))
    print("-----------------------------------")

# 7. 형태소와 품사 태깅 (pos 메서드 사용)
# pos : 품사 태깅
okt.pos(sample_text)

# Okt의 품사 태그셋 확인
okt.tagset  # Okt 토크나이저가 사용하는 품사 목록 확인

# 8. 형태소 분석 결과를 딕셔너리로 정리
# 딕셔너리 형태로 형태소를 저장
tag_dict = {}

for k in okt.tagset.keys():
    tag_dict[k] = []

for p in okt.pos(sample_text):
    tag_dict[p[-1]].append(p[0])

# 9. 딕셔너리 데이터를 pandas 데이터프레임으로 변환 및 저장
df = pd.DataFrame.from_dict(tag_dict, orient="index")  # key -> 인덱스, value -> 컬럼
df

# CSV 파일로 저장
df.to_csv('./token.csv', encoding='cp949')

# 명사만 출력
df.loc['Noun']

# 10. 불용어 제거 작업
# 불용어 : 의미 없는 단어들 제거 (stopwords)
stopwords = ['오', ',', '!', '가르치', '누나', '은', '는', '를', '에게']

# 불용어 제거 후 형태소 분석
token = okt.morphs(sample_text)
[word for word in token if word not in stopwords]

# 11. ckonlpy를 이용한 불용어 제거 자동화
from ckonlpy.tag import Twitter, Postprocessor

# 클래스 초기화
twitter = Twitter()
postprocessor = Postprocessor(twitter, stopwords=stopwords)

# pos 메서드에서 불용어 자동 제거
postprocessor.pos(sample_text)

# 12. 사용자 정의 품사 태깅
# '타오르는'을 명사로 추가
twitter.add_dictionary('타오르는', 'Noun')
twitter.pos(sample_text)

# 다른 샘플 텍스트로 테스트
sample_text2 = '미주는 대학 졸업 후 반년 이상 지옥과도 같은 취업난을 몸으로 부딪히며 참 많이도 좌절해야 했었다.'
twitter.pos(sample_text2)

# 사용자 정의 품사 태깅: '취업난'을 하나의 명사로 처리
twitter.add_dictionary('취업난', 'Noun')
twitter.pos(sample_text2)

# 13. 2그램 분석법 적용 (n-gram)
ngrams = [
    (('대학', '졸업'), 'Noun'),
    (('지옥', '과도'), 'Noun'),
    (('합격자', '명단'), 'Noun'),
]

# Postprocessor로 n-gram 적용
postprocessor = Postprocessor(twitter, ngrams=ngrams)
postprocessor.pos(sample_text2)

# 14. 품사별 등장 횟수 카운팅
# 품사의 등장 횟수를 저장하는 딕셔너리 생성
tag_dict = {}

for k in okt.tagset.keys():
    tag_dict[k] = 0

tag_dict['Modifier'] = 0  # 수식어를 수동으로 추가

# 각 품사의 등장 횟수를 딕셔너리에 저장
for p in okt.pos(sample_text2):
    tag_dict[p[-1]] += 1

# 15. 품사별 등장 횟수를 시각화
import matplotlib.pyplot as plt
import koreanize_matplotlib

plt.subplots(figsize=(5,2))
plt.bar(tag_dict.keys(), tag_dict.values())  # 막대 그래프 생성
plt.xticks(list(okt.tagset.keys()) + ['Modifier'], list(okt.tagset.values()) + ['수식어'], rotation=90)
plt.title('품사 개수')
plt.show()

# 16. 워드 클라우드 생성
from nltk import Text
from wordcloud import WordCloud

# 워드 클라우드 생성
font_path = 'C:/Windows/Fonts/malgun.ttf'
sample_text3 = "미주는 대학 졸업 후 반년 이상 지옥과도 같은 취업난을 몸으로 부딪히며 참 많이도 좌절해야 했었다."
text_count = Text(okt.nouns(sample_text3), name='장미와 찔레')
wc = WordCloud(width=640, height=360, background_color='white', font_path=font_path, max_words=100, min_word_length=3)
cloud = wc.generate_from_frequencies(text_count.vocab())

plt.imshow(cloud)
plt.axis('off')
plt.show()


2. 정규식 처리

# 1. 기본 문자열 처리
reg_text = '안녕, 나는 잎싹이야. 너도 잎싹이고, 쟤도 잎싹이야. 우리 모두 잎싹이야.'

# replace() 메서드 : '잎싹'이라는 문자열을 '절대잎싹아님'으로 대체
reg_text.replace('잎싹', '절대잎싹아님')

# 2. 정규 표현식 (Regular Expression)
import re

# [정규 표현식을 사용하여 숫자를 제거]
# re.sub() : 문자열 내에서 정규 표현식 패턴에 일치하는 부분을 찾아 대체하는 함수
# '[0-9]' : 숫자(0-9) 범위 내에서 검색
reg_text = '안녕, 나는 잎싹이야. 너도 잎1싹이고, 쟤도 잎123싹이야. 우리 모두 잎567싹이야.'  # 숫자 포함된 텍스트

# 숫자를 제거한 텍스트로 변환
re.sub('[0-9]', '', reg_text)

# 3. 주민등록번호 마스킹
id_text = '안녕하세요, 저는 ABC에요. 제 주민번호는 123456-7894561 이에요. 제 주소는 XX시 OO구 123-45번지예요.'

# '0-9' 범위를 의미하는 '\d' 사용. {6}은 숫자 6개, {7}은 숫자 7개를 의미
# 주민등록번호 패턴을 찾아서 '******-*******'로 대체
re.sub('\d{6}-\d{7}', '******-*******', id_text)

# 4. 구두점이 있을 때 줄바꿈 처리
eng_text = 'Squire Trelawney, Dr. Livesey, and the rest of these gentlemen having asked me to write down the whole particulars about Treasure Island,'

# 쉼표(,)와 마침표(.)를 줄바꿈('\n')으로 대체
result = re.sub(r'[,.]', '\n', eng_text)
print(result)

# 5. Dr. 에서는 줄바꿈이 되지 않도록 수정
# (?<!Dr) : 부정형 후방탐색 (Dr로 끝나지 않는 경우)
result = re.sub(r'(?<!Dr)\. ', '\n', eng_text)  # Dr.를 제외하고 마침표 뒤에 줄바꿈
result = re.sub(r',', '\n', result)  # 쉼표 뒤에 줄바꿈
print(result)

# 6. 주민등록번호 뒷자리 처리 (특정 조건 하에 마스킹)
id_text = '안녕하세요, 저는 ABC에요. 제 주민번호는 990101-1234567 이에요. 제 주소는 XX시 OO구 123-45번지예요.'

# 주민번호 뒷자리가 1-4로 시작하는 경우에만 뒷부분을 마스킹
re.sub(r'-[1-4]\d{6}', lambda x: x.group(0)[0:2] + '******', id_text)

 

📙 내일 일정

  • 구문 분석 학습

 

 

 

 

'TIL _Today I Learned > 2024.09' 카테고리의 다른 글

[DAY 51] TF-IDF, Deep Learning 실습  (0) 2024.09.26
[DAY 50] 구문 분석 (Syntactic Parsing)  (0) 2024.09.25
[DAY 48] Deep Learning 실습  (0) 2024.09.23
[DAY 47] Deep Learning 실습  (0) 2024.09.20
[DAY 46] Deep Learning 실습  (0) 2024.09.19