데이터 스터디/DL

[코드 분석] Bag of Words for IMDB movie review

케이와이엠 2023. 2. 1. 11:12

Kaggle : Bag of Words Meets Bags of Popcorn 커널 필사

  • 대회 소개
  • 데이터 파일
    • labeledTraindata.csv - id/sentiment/review 3개의 칼럼- Id 글쓴이, sentiment 1이면 긍정, 0이면 부정적인 리뷰를 뜻하고 review 직접  리뷰 
    • testData.csv - sentiment 빠진 2개의 

 

 

Bag of Words Meets Bags of Popcorn | Kaggle

 

www.kaggle.com

※ PC 화면에 최적화되어 있습니다.


데이터 준비

> 1. Import Libraries & Modules

import pandas as pd

"""
header = 0 은 파일의 첫 번째 줄에 열 이름이 있음을 나타내며
delimiter = \t 는 필드가 탭으로 구분되는 것을 의미한다.
quoting = 3은 쌍따옴표를 무시하도록 한다.
"""
# QUOTE_MINIMAL (0), QUOTE_ALL (1),
# QUOTE_NONNUMERIC (2) or QUOTE_NONE (3).

# 레이블인 sentiment 가 있는 학습 데이터
train = pd.read_csv('/content/drive/MyDrive/21-2 kubig 코드 분석/211111/labeledTrainData.tsv', header=0, delimiter='\t', quoting=3)
# 레이블이 없는 데스트 데이터
test = pd.read_csv('/content/drive/MyDrive/21-2 kubig 코드 분석/211111/testData.tsv', header=0, delimiter='\t', quoting=3)

레이블인 감정(sentiment)가 있는 labeled train data와 레이블이 빠진 test data를 불러와줍니다.

 


전처리 : data cleaning & text preprocessing

기계가 텍스트를 이해할 수 있도록 텍스트를 정제해 줍니다.

텍스트의 전처리 순서는 [ 토큰화 -> 불용어처리 -> 어간 추출 ->벡터화 ] 로 진행됩니다.

 

해당 커널에서는 아래와 같은 순서로 전처리할 예정입니다. 

 

1. BeautifulSoup을 통해 HTML 태그를 제거
2. 정규표현식으로 알파벳 이외의 문자를 공백으로 치환
3. 토큰화
4. NLTK 데이터를 사용해 Stopword를 제거
5. 어간추출(Stemming)과 음소표기법(Lemmatizing)의 개념을 이해하고 SnowballStemmer를 통해 어간을 추출
6. Lemmatization 음소표기법

 

> 1. BeautifulSoup을 통해 HTML 태그를 제거

# 설치 및 버전확인
!pip3 show BeautifulSoup4
# review의 첫 번째 데이터의 HTML 태그 제거 
from bs4 import BeautifulSoup

example1 = BeautifulSoup(train['review'][0], "html5lib")
print(train['review'][0][:700])
example1.get_text()[:700]

리뷰를 보면 중간중간 특수문자  html태그가 들어있는 것을   있습니다. 여기서  특수문자나 html태그는 문장의 의미에  영향을 주지 않으므로 제거를 해주어야 합니다Html 태그는 beautifulsoup 설치해서 제거해줄  있습니다.

 

> 2. 정규표현식으로 알파벳 이외의 문자를 공백으로 치환

# 정규표현식을 사용해서 특수문자를 제거
import re
# 소문자와 대문자가 아닌 것은 공백으로 대체한다.
# ^ : not을 의미
letters_only = re.sub('[^a-zA-z]', ' ', example1.get_text())
letters_only[:700]

Re import해서 소문자 대문자 알파벳이 아닌 것은 공백을 대체해서 꽤나 리뷰창이 깔끔해진 것을 확인할  있습니다.

 

> 3. 토큰화

# 모두 소문자로 변환한다.
lower_case = letters_only.lower()
# 문자를 나눈다. => 토큰화
words = lower_case.split()
print(len(words))
words[:10]

순수한 알파벳들만 남은 텍스트들을, 단어를 기준으로 잘라서 리스트를 만들어줍니다. 

문자열을 토큰으로 분리하는 함수를 (tokenizer)라고 하는데tokenizer 함수를 쓰지않고 split으로 처리해주었습니다

 

> 4.  NLTK 데이터를 사용해 Stopword를 제거

# stopwords 불러오기
import nltk
from nltk.corpus import stopwords
nltk.download('stopwords')
stopwords.words('english')[:10]

# stopwords 를 제거한 토큰들
words = [w for w in words if not w in stopwords.words('english')]
print(len(words))
words[:10]

stopwords 통해서 문장에서 중요하지 않은 단어들을 제거해줍니다.  불용어들은 학습모델에서  다른 기여를 하지 못하기 때문에 토큰화된 텍스트들 중에서 불용어를 제거해주는데요, 예를 조사나 접미사 등등이 있습니다. 

이때 NLTK 패키지 안에 179개의 불용어가 리스트로 저장이 되어 있어 stopwords안에 포함 안되는 단어들만 저장해주었습니다. 

 

>5. SnowballStemmer를 통해 어간을 추출

from nltk.stem.snowball import SnowballStemmer

stemmer = SnowballStemmer('english')
words = [stemmer.stem(w) for w in words]

# 처리 후 단어
words[:10]

단어의 핵심인 어간만 추출하는 스테밍(steming)작업입니다. 이는 어미를 제거해서 단어의 원형에 가까워지도록 만들어줍니다. NLTK페키지안에는 포터 스태머, 그리고 랭커스터 스태머 등을 제공하고 있습니다.

여기서는 Snowball 스테머를 사용해주었습니다.

 

>6. Lemmatization 음소표기법

import nltk
nltk.download('wordnet')
from nltk.stem import WordNetLemmatizer
wordnet_lemmatizer = WordNetLemmatizer()

print(wordnet_lemmatizer.lemmatize('fly'))
print(wordnet_lemmatizer.lemmatize('flies'))

words = [wordnet_lemmatizer.lemmatize(w) for w in words]

# 처리 후 단어
words[:10]

다음으로는 lemmatization입니다. 이는 문맥을 보고 동음이의어를 구별해주는 장점이 있습니다.

Wordnet lemmatizer import해서 다시 word 저장을 해주었습니다.

 

>7. 문자열 처리 

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))
clean_review = review_to_words(train['review'][0])
clean_review

review_to_words라는 함수를 만들어 새로 처리한 단어들을 clean_review 저장해주었습니다

 


벡터화 : Bag of Words 기법

clean_train_reviews = []
for i in range(0, num_reviews):
     if (i + 1) % 5000 == 0 :  #실행이 잘되는지 확인하기 위해 5000개 실행될때마다 확인문구
         print('Review {} of {}'.format(i+1, num_reviews))
     clean_train_reviews.append(review_to_words(train['review'][i]))

%time train['review_clean'] = train['review'].apply(review_to_words)

정제한 텍스트들을 컴퓨터가 이해할  있는 숫자 형식의 벡터값으로 만드는 과정이 필요합니다. 바로 벡터화 과정을 뜻하죠. 여기서 Bag of words 기법은 주머니 속에 단어들을 넣고  문장마다 구문 상관없이 단순히 단어들이   나오는지 세어주는 방식입니다. 이때 단어의 순서가 앞뒤가 달라져도  같은 문장으로 인식한다는 한계가 있는데, 이를 막기 위해서 n-gram 이용해줍니다.

 

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import Pipeline

# 튜토리얼과 다르게 파라미터 값을 수정
# 파라미터 값만 수정해도 캐글 스코어 차이가 많이 남
vectorizer = CountVectorizer(analyzer = 'word', 
                             tokenizer = None,
                             preprocessor = None, 
                             stop_words = None, 
                             min_df = 2, # 토큰이 나타날 최소 문서 개수
                             ngram_range=(1, 3),
                             max_features = 5000)
vectorizer

 

벡터화는 사이킷런의 countvectorizer 사용해줍시다. 이때 정제 과정에서 tokenizer/preprocesssor/stopwords 단계를 이미  거쳤기 때문에 none으로 지정해주었고, n-gram_range (1에서 3)까지 지정을 함으로써 unigram / bigram/trigram 학습했습니다. 그리고 빈도수가 높은 단어 5000개만 선정해주었습니다.


모델링

from sklearn.ensemble import RandomForestClassifier

# 랜덤포레스트 분류기를 사용
forest = RandomForestClassifier(
    n_estimators = 100, n_jobs = -1, random_state=2018)

forest
%time forest = forest.fit(train_data_features, train['sentiment'])

from sklearn.model_selection import cross_val_score
%time score = np.mean(cross_val_score(\
    forest, train_data_features, \
    train['sentiment'], cv=10, scoring='roc_auc'))
score

랜덤포레스트를 이용해서 학습하고 예측을 해보았습니다. Scoring roc커브로 지정해주고 train 학습시킨 결과 정확도가  0.92 나왔습니다.

 

clean_test_reviews = []
for i in range(0, num_reviews):
     if (i + 1) % 5000 == 0 :  #실행이 잘되는지 확인하기 위해 5000개 실행될때마다 확인문구
         print('Review {} of {}'.format(i+1, num_reviews))
     clean_test_reviews.append(review_to_words(test['review'][i]))
test_data_features = vectorizer.fit_transform(clean_test_reviews)
test_data_features = test_data_features.toarray()

result = forest.predict(test_data_features)
output = pd.DataFrame(data={"id":test["id"], "sentiment" : result, "review":test["review"]})
output    # kaggle score : 0.85360

 후에 test 똑같이 정제해주고 벡터화시켜준 과정을 거친 후에 동일한 예측 모델을 가지고 모델을 적용시켜주면 이를 저장해서 최종적으로 제출하면 되겠습니다.


워드클라우드 만들기

train_data_features = vectorizer.fit_transform(clean_train_reviews)
train_data_features.shape
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()
# train 데이터의 모든 단어에 대한 워드 클라우드를 그려본다.
%time displayWordCloud(' '.join(clean_train_reviews))

추가적으로 워드 클라우드를 만드는 시각화 방법입니다.

단순히 빈도 수를 표현하기 보다는 상관관계나 유사도 등으로 배치하는   의미 있기 때문에  정보를 주진 않지만

코드분석을 위해 한 번 작성해보면 좋을 것 같습니다 :)

'데이터 스터디 > DL' 카테고리의 다른 글

CNN - convolution 연산 이해하기  (0) 2023.11.10
07. Back Propagation - 역전파 이해하기  (0) 2023.11.10
TF-IDF  (0) 2022.07.17
N-gram 언어 모델  (0) 2022.07.16
언어 모델 - 통계 기반 언어모델  (0) 2022.07.16