해당 게시물은 '박조은'강사님의 인프런 강의, [NLP] IMDB 영화리뷰 감정 분석을 통한 파이썬 텍스트 분석과 자연어 처리를 정리한 게시글입니다.
이전 게시글에서 리뷰에 태그나 여러가지 기호가 아닌 것들을 정제해야함을 언급했다.
이번 게시글에서는 그러한 것들을 제거하여 데이터를 정제해보자.
데이터 정제 Data Cleaning and Text Preprocessing
기계가 텍스트를 이해할 수 있도록 텍스트를 정제해 준다.
신호와 소음을 구분한다. 아웃라이어데이터로 인한 오버피팅을 방지한다.
- BeautifulSoup(뷰티풀숩)을 통해 HTML 태그를 제거
- 정규표현식으로 알파벳 이외의 문자를 공백으로 치환
- NLTK 데이터를 사용해 불용어(Stopword)를 제거
- 어간추출(스테밍 Stemming)과 음소표기법(Lemmatizing)의 개념을 이해하고 SnowballStemmer를 통해 어간을 추출
텍스트 데이터 전처리 이해하기
(출처 : 트위터 한국어 형태소 분석기)
정규화 normalization (입니닼ㅋㅋ -> 입니다 ㅋㅋ, 샤릉해 -> 사랑해)
- 한국어를 처리하는 예시입니닼ㅋㅋㅋㅋㅋ -> 한국어를 처리하는 예시입니다 ㅋㅋ
토큰화 tokenization
- 한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하는Verb, 예시Noun, 입Adjective, 니다Eomi ㅋㅋKoreanParticle
어근화 stemming (입니다 -> 이다)
- 한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어Noun, 를Josa, 처리Noun, 하다Verb, 예시Noun, 이다Adjective, ㅋㅋKoreanParticle
어구 추출 phrase extraction
- 한국어를 처리하는 예시입니다 ㅋㅋ -> 한국어, 처리, 예시, 처리하는 예시
* 뷰티풀숩 라이브러리를 이용할 것이다. 없다면 설치해주자
- 주피터 노트북에서 해당 라이브러리를 설치하는 코드는 다음과 같다.
!pip install BeautifulSoup4
느낌표를 pip앞에 붙여서 셀을 실행시키면 해당 파일에서 아나콘다에 바로 모듈을 설치할 수 있다.
- BeautifulSoup을 이용해서 텍스트를 추출하는 방법
- get_text()
- string
get_text()
get_text()를 이용하면 한방에 현재 HTML 문서의 모든 텍스트를 추출할 수 있습니다. 조금 더 정확히 표현하면 get_text() 메서드는 현재 태그를 포함하여 모든 하위 태그를 제거하고 유니코드 텍스트만 들어있는 문자열을 반환합니다.
soup.select_one('.item_price').get_text()
# >>> '\n\n<27,000원\n\n\n12,900원\n\n'
태그가 모두 사라지고 텍스트만 남은것을 볼 수 있습니다. 이제 12,900원만 추출하면 됩니다. 하지만 이렇게 생긴 텍스트 묶음에서 원하는 텍스트를 추출하는 것은 번거로운 일이기 때문에, 태그 선택부터 다시 하겠습니다.
get_text() 메서드는 항상 마지막 태그에 사용해야 합니다.
soup.select_one('.item_price > .s-price span').get_text()
# >>> '12,900원'
string
get_text()를 사용하더라도 정확하게 문자열을 추출하기 위해서는 항상 마지막 태그에 메서드를 사용해야 합니다. 개인적으로 애초에 더 명확하게 사용해야 하는 string 속성을 이용해서 문자열을 추출하는 것을 선호합니다.
string 속성은
- 태그(tag) 내 문자열을 반환합니다.
soup.select_one('.s-price > strong > span').string
# >>> '12,900원'
- 태그(tag) 내 자식 태그가 둘 이상이면, 무엇을 반환해야 하는지 명확하지 않기 때문에 None을 반환합니다.
soup.select_one('.s-price').string
# >>> None
- 단 자식 태그가 하나이면서, 그 자식 태그가 .string 값을 가지고 있다면 자식 태그의 문자열을 반환합니다.
soup.select_one('.s-price > strong').string
# >>> '12,900원'
- 정규표현식을 사용해서 특수문자를 제거
정규 표현식 문법 간략 정리
정규표현식
- . 줄바꿈 문자를 제외한 1글자
- ^ : 문자열의 시작을 의미 또는 not
- $ : 문자열의 끝을 의미
- | : or
- * : 0회 이상 반복
- + : 1회 이상 반복
- ? : 0회 또는 1회 반복
- [] : 문자열의 집합
예시
[abc] : a또는 b또는 c중 하나
[a-z] : a부터 z까지
[^a] : a를 제외한 모든 문자 - () : 어떤 정규식을 하나의 그룹으로 묶음
- {n} : 앞의 문자가 n회 반복
- {m,n} : 앞의 문자가 m회 이상 n회 반복
- {m,} : 앞의 문자가 m회 이상 반복
- \w : (unicode) 숫자, _를 포함하는 모든 문자
- \W : (unicode) 숫자, _를 제외한 나머지 문자
- \d : (unicode) 숫자
- \D : (unicode) 숫자를 제외
- \b : (unicode) 단어의 시작과 끝에 존재하는 공백
- \B : (unicode) 단어의 시작과 끝 외에 존재하는 공백
re.sub()
import re
sub을 사용하기 위해서는 re module을 import하여야 한다.
re.sub(pattern, new, string)
string에서 pattern에 해당하는 부분을 new로 대체하라는 의미
# 정규표현식을 사용해서 특수문자를 제거
import re
# 소문자와 대문자가 아닌 것은 공백으로 대체한다.
letters_only = re.sub('[^a-zA-Z]', ' ', example1.get_text())
letters_only[:700]
# 모두 소문자로 변환한다.
lower_case = letters_only.lower()
# 문자를 나눈다. => 토큰화
words = lower_case.split()
print(len(words))
words[:10]
- NLTK 를 이용하여 불용어 제거(Stopword Removal)
일반적으로 코퍼스에서 자주 나타나는 단어는 학습 모델로서 학습이나 예측 프로세스에 실제로 기여하지 않아 다른 텍스트와 구별하지 못한다.
예를들어 조사, 접미사, i, me, my, it, this, that, is, are 등 과 같은 단어는 빈번하게 등장하지만 실제 의미를 찾는데 큰 기여를 하지 않는다. Stopwords는 "to"또는 "the"와 같은 용어를 포함하므로 사전 처리 단계에서 제거하는 것이 좋다.
NLTK에는 153 개의 영어 불용어가 미리 정의되어 있다. 17개의 언어에 대해 정의되어 있으며 한국어는 없다.
불용어가 많은 경우에는 코드 내에서 직접 정의하지 않고 txt 파일이나 csv 파일로 정리해놓고 이를 불러와서 사용하기도 한다.
NLTK 사이트 링크
한국어 불용어 관련 정리 링크
- https://deep.chulgil.me/hangugeo-bulyongeo-riseuteu/
- NLTK를 이용한 스테밍(어간추출, 형태소 분석)
정규화 기법 중 코퍼스에 있는 단어의 개수를 줄일 수 있는 기법에는 표제어 추출(lemmatization)과 어간 추출(stemming)이 있다.
먼저, 어간 추출 부터 보면
출처 : 어간 추출 - 위키백과, 우리 모두의 백과사전
- 어간 추출(語幹 抽出, 영어: stemming)은 어형이 변형된 단어로부터 접사 등을 제거하고 그 단어의 어간을 분리해 내는 것
- "message", "messages", "messaging" 과 같이 복수형, 진행형 등의 문자를 같은 의미의 단어로 다룰 수 있도록 도와준다.
- stemming(형태소 분석): 여기에서는 NLTK에서 제공하는 형태소 분석기를 사용한다. 포터 형태소 분석기는 보수적이고 랭커스터 형태소 분석기는 좀 더 적극적이다. 형태소 분석 규칙의 적극성 때문에 랭커스터 형태소 분석기는 더 많은 동음이의어 형태소를 생산한다.
포터 VS 랭커스터 스태머 알고리즘 비교
어간 추출 속도는 표제어 추출보다 일반적으로 빠른데, 포터 어간 추출기는 정밀하게 설계되어 정확도가 높으므로 영어 자연어 처리에서 어간 추출을 하고자 한다면 가장 준수한 선택입니다. NLTK에서는 포터 알고리즘 외에도 랭커스터 스태머(Lancaster Stemmer) 알고리즘을 지원합니다. 이번에는 포터 알고리즘과 랭커스터 스태머 알고리즘으로 각각 어간 추출을 진행했을 때, 이 둘의 결과를 비교해보겠습니다.
from nltk.stem import PorterStemmer
from nltk.stem import LancasterStemmer
porter_stemmer = PorterStemmer()
lancaster_stemmer = LancasterStemmer()
words = ['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives', 'fly', 'dies', 'watched', 'has', 'starting']
print('어간 추출 전 :', words)
print('포터 스테머의 어간 추출 후:',[porter_stemmer.stem(w) for w in words])
print('랭커스터 스테머의 어간 추출 후:',[lancaster_stemmer.stem(w) for w in words])
어간 추출 전 : ['policy', 'doing', 'organization', 'have', 'going', 'love', 'lives', 'fly', 'dies', 'watched', 'has', 'starting']
포터 스테머의 어간 추출 후: ['polici', 'do', 'organ', 'have', 'go', 'love', 'live', 'fli', 'die', 'watch', 'ha', 'start']
랭커스터 스테머의 어간 추출 후: ['policy', 'doing', 'org', 'hav', 'going', 'lov', 'liv', 'fly', 'die', 'watch', 'has', 'start']
동일한 단어들의 나열에 대해서 두 스태머는 전혀 다른 결과를 보여줍니다. 두 스태머 알고리즘은 서로 다른 알고리즘을 사용하기 때문입니다. 그렇기 때문에 이미 알려진 알고리즘을 사용할 때는, 사용하고자 하는 코퍼스에 스태머를 적용해보고 어떤 스태머가 해당 코퍼스에 적합한지를 판단한 후에 사용하여야 합니다.
이런 규칙에 기반한 알고리즘은 종종 제대로 된 일반화를 수행하지 못 할 수 있습니다. 어간 추출을 하고나서 일반화가 지나치게 되거나, 또는 덜 되거나 하는 경우입니다. 예를 들어 포터 알고리즘에서 organization을 어간 추출했을 때의 결과를 봅시다.
organization → organ
organization과 organ은 완전히 다른 단어 임에도 organization에 대해서 어간 추출을 했더니 organ이라는 단어가 나왔습니다. organ에 대해서 어간 추출을 한다고 하더라도 결과는 역시 organ이 되기 때문에, 두 단어에 대해서 어간 추출을 한다면 동일한 어간을 갖게 됩니다. 이는 의미가 동일한 경우에만 같은 단어를 얻기를 원하는 정규화의 목적에는 맞지 않습니다. 마지막으로 동일한 단어에 대해서 표제어 추출과 어간 추출을 각각 수행했을 때, 결과에서 어떤 차이가 있는지 간단한 예를 보겠습니다.
Stemming
am → am
the going → the go
having → hav
Lemmatization
am → be
the going → the going
having → have
reference
랭커스터 스태머는 임포트를 하여 사용함
Snowball stammer: Porter2 형태소 분석 알고리즘으로도 알려진 형태소 분석 알고리즘
Lemmatization 음소표기법
언어학에서 음소 표기법 (또는 lemmatization)은 단어의 보조 정리 또는 사전 형식에 의해 식별되는 단일 항목으로 분석 될 수 있도록 굴절 된 형태의 단어를 그룹화하는 과정이다. 예를 들어 동음이의어가 문맥에 따라 다른 의미를 갖는데
1) *배*가 맛있다.
2) *배*를 타는 것이 재미있다.
3) 평소보다 두 *배*로 많이 먹어서 *배*가 아프다.
위에 있는 3개의 문장에 있는 "배"는 모두 다른 의미를 갖는다.
레마타이제이션은 이때 앞뒤 문맥을 보고 단어의 의미를 식별하는 것이다. 영어에서 meet는 meeting으로 쓰였을 때 회의를 뜻하지만 meet 일 때는 만나다는 뜻을 갖는데 그 단어가 명사로 쓰였는지 동사로 쓰였는지에 따라 적합한 의미를 갖도록 추출하는 것이다.
문자열 처리하기
지금까지 나온 내용들을 바탕으로 문자열 처리
def review_to_words( raw_review ):
# 1. HTML 제거
review_text = BeautifulSoup(raw_review, 'html.parser').get_text()
# 2. 영문자가 아닌 문자는 공백으로 변환
letters_only = re.sub('[^a-zA-Z]', ' ', review_text)
# 3. 소문자 변환
words = letters_only.lower().split()
# 4. 파이썬에서는 리스트보다 세트로 찾는게 훨씬 빠르다.
# stopwords 를 세트로 변환한다.
stops = set(stopwords.words('english'))
# 5. Stopwords 불용어 제거
meaningful_words = [w for w in words if not w in stops]
# 6. 어간추출
stemming_words = [stemmer.stem(w) for w in meaningful_words]
# 7. 공백으로 구분된 문자열로 결합하여 결과를 반환
return( ' '.join(stemming_words) )
25000개의 데이터를 효율적으로 처리하기위해 코드 작성
# 참고 : https://gist.github.com/yong27/7869662
# http://www.racketracer.com/2016/07/06/pandas-in-parallel/
from multiprocessing import Pool
import numpy as np
def _apply_df(args):
df, func, kwargs = args
return df.apply(func, **kwargs)
def apply_by_multiprocessing(df, func, **kwargs):
# 키워드 항목 중 workers 파라메터를 꺼냄
workers = kwargs.pop('workers')
# 위에서 가져온 workers 수로 프로세스 풀을 정의
pool = Pool(processes=workers)
# 실행할 함수와 데이터프레임을 워커의 수 만큼 나눠 작업
result = pool.map(_apply_df, [(d, func, kwargs)
for d in np.array_split(df, workers)])
pool.close()
# 작업 결과를 합쳐서 반환
return pd.concat(list(result))
워드 클라우드
- 단어의 빈도 수 데이터를 가지고 있을 때 이용할 수 있는 시각화 방법
- 단순히 빈도 수를 표현하기 보다는 상관관계나 유사도 등으로 배치하는 게 더 의미 있기 때문에 큰 정보를 얻기는 어렵다.
from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
# %matplotlib inline 설정을 해주어야지만 노트북 안에 그래프가 디스플레이 된다.
%matplotlib inline
def displayWordCloud(data = None, backgroundcolor = 'white', width=800, height=600 ):
wordcloud = WordCloud(stopwords = STOPWORDS,
background_color = backgroundcolor,
width = width, height = height).generate(data)
plt.figure(figsize = (15 , 10))
plt.imshow(wordcloud)
plt.axis("off")
plt.show()
스태밍처리를 한 단어 기준으로 어떤 단어가 많이 등장하는지 시각화해서 볼 수 있다.
하지만 현업에서 워드클라우드를 많이 사용하진 않는다고 한다..
중복을 제거하고 단어를 본다.
reference
'공부정리 > NLP' 카테고리의 다른 글
[NLP] 캐글 영화 리뷰 분석 튜토리얼 2 - (1) 딥러닝 기법 Word2Vec 소개 - 강의 정리 (0) | 2022.08.29 |
---|---|
[NLP] 캐글 영화 리뷰 분석 튜토리얼 (4) 랜덤 포레스트로 영화 감성 예측 평가 ROC / AUC - 강의 정리 (0) | 2022.08.29 |
[NLP] 캐글 영화 리뷰 분석 튜토리얼 (3) CountVectorizer 로 텍스트 데이터 벡터화 - 강의 정리 (0) | 2022.08.29 |
[NLP] 캐글 영화 리뷰 분석 튜토리얼 (1) 데이터 확인하기 - 강의 정리 (0) | 2022.08.18 |
[NLP] 캐글 머신러닝 자연어처리 NLP 튜토리얼 - 강의 정리 (0) | 2022.08.18 |