본문 바로가기

딥러닝

딥러닝 - ex06_영화리뷰데이터 감성분류



오늘 학습내용

  • 텍스트 마이닝 절차
  • 토큰화 도구 (Okt) 사용
  • 텍스트 전처리 실습
  • 텍스트 감성 분류 (RNNm LSTM, Attension)

텍스트 마이닝의 과정

  • 수집된 텍스트 데이터 -> corpus -> sentence -> word (token) -> 형탸소(한글) - > 글자 -> 자소 (한글)
  • 전처리과정
    • 정제 (Cleaning) : 오류/ 오타 제거(학습에 사용되지 않는 문자를 삭제/ 수정하는 작업)
    • 토큰화 (tokenization) : corpus나 sentence로부터 단어나 형태소를 분리하는 작업
    • 정규화 (Normalization) : 표현방법이 다른 단어들을 통합하는 작업
      • 어간 (Stemming) 추출 : 의미 기반으로 통합 작업(예 ) 먼들다 , 만들어서, 만들고 -> 만듬
      • 표제어 (Lemmatization) 추출 : 대표단어로 통합하는 작업 (예) 죽다, 돌아가셨다, 가셨다 -> 죽다
      • 불용어(stopword) 처리 : 학습에 사용 되지 않는 단어를 제거하는 작업
      • 정규표현식을 사용한 필터링 : 원하는 형식으로 단어를 추출하는 작업
      • 내부단어(subword) 분리 : 여러개로 분리하는 작업(예) 스마트인재개발원 -> 스마트, 인재, 개발원
  • 전처리된 텍스트를 학습데이터로 만드는 과정
    • 인코딩 : 정수인코딩, 원핫인코딩, word embedding
    • padding : 데이터를 같은 길이로 만들어 주는 작업
  • 텍스트 데이터 학습
    • Embedding 층
    • RNN, LSTM, GRU 층 (텍스트를 시계열로 적용)
    • Attension : RNN의 단점을 보완한 것
    • Bert, GPT
  • 한글 데이터가 어려운 이유
    • 한글은 띄어쓰기를 하지 않아도 의미 전달이 되기때문에 띄어쓰기를 지키지 않는 경우가 많음 -> 빈공백 기준으로 토큰화가 어려움
    • 구조적으로 영어에 비해 복잡(자소, 품사 등)
    • 형용사 표현이 다양함 (의성어, 의태어 등)
    • 같은 의미인데 다른단어, 다른 의미인데 같은 단어 표현이 많음
    •  
# konlpy 설치 : 한글 형태소 분리기가 포함된 라이브러리
!pip install konlpy

이 코드는 한국어 형태소 분석 및 자연어 처리를 위한 오픈소스 라이브러리인 konlpy를 설치하는 코드입니다. 

 

konlpy는 한글 텍스트 데이터를 처리하는데 필요한 다양한 형태소 분석기를 제공합니다. 

이러한 형태소 분석기를 사용하면 한글 텍스트를 의미 있는 단위로 분리할 수 있으며, 

이를 통해 자연어 처리 과정에서 더 나은 성능을 얻을 수 있습니다.

konlpy를 설치하려면 이 코드를 실행하면 됩니다.

이후에는 konlpy를 사용하여 한글 텍스트 데이터를 처리할 수 있습니다.

  • 네이버 영화댓글 데이터
    • 20만개의 데이터로 구성
    • 긍정(1)/부정(0) 라벨이 부여

import pandas as pd

train_data = pd.read_table("/content/ratings_train.txt")
test_data = pd.read_table("/content/ratings_test.txt")

train_data[:5]

print(len(train_data))
print(len(test_data))

# 데이터 편향 확인
print(train_data["label"].value_counts())
print(test_data["label"].value_counts())

# 결측치 확인
print(train_data.isnull().sum())
print(test_data.isnull().sum())

이 코드는 두 개의 텍스트 데이터 파일(ratings_train.txt, ratings_test.txt)을 pandas의 DataFrame으로 읽어옵니다. 

각각의 파일은 train_data와 test_data로 저장됩니다. 

데이터는 영화 리뷰와 해당 리뷰의 긍정/부정 레이블로 구성되어 있습니다.

코드는 다음 작업들을 수행합니다:
처음 5개의 항목을 출력하여 train_data를 미리보기합니다.


train_data와 test_data의 길이(데이터의 개수)를 출력합니다.


긍정/부정 레이블의 분포를 확인하여 데이터 편향 여부를 확인합니다.


각 데이터셋의 결측치를 확인합니다.


 이를 통해 데이터셋의 크기, 레이블 분포 및 결측치 여부를 확인할 수 있습니다.

이러한 정보는 데이터 전처리 및 모델 학습에 있어 중요한 역할을 합니다.


# 결측치 삭제
# how= "any" : 하나의 컬럼이라도 결측치가 있다면 해당 데이터를 삭제
train_data = train_data.dropna(how= "any")
test_data = test_data.dropna(how= "any")

# 결측치 확인
print(train_data.isnull().sum())
print(test_data.isnull().sum())

이 코드는 train_data와 test_data 데이터프레임에서 결측치를 제거하는 작업을 수행합니다. 

dropna() 함수의 how="any" 옵션을 사용하여 각 데이터프레임의 행 중 하나라도
 결측치가 있는 경우 해당 행을 삭제합니다. 
이후 결측치가 없어졌는지 확인하기 위해 다시 각 데이터셋의 결측치를 출력합니다.

코드는 결측치를 제거하는 목적에 적합하게 작성되어 있으며 문제가 없습니다. 
이 작업을 통해 머신 러닝 모델 학습에 사용되는 데이터가 결측치가 없도록 전처리할 수 있습니다.


  • 정규식을 이용해서 한글만 추출
# [^ㄱ-ㅎㅏ-ㅣ가-힣 ] :  한글이 아닌 데이터
# ^ : 부종(아닌것)
# ㄱ-ㅎ : ㄱ부터 ㅎ까지
# ㅏ-ㅣ : ㅏ 부터 ㅣ까지
# 가 -힣 : 가부터 힣 까지
# (빈공백) 도 제거
train_data["document"] = train_data["document"].str.replace("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]", "")
test_data["document"] = test_data["document"].str.replace("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]", "")

train_data[:5]

# 결측치 확인
print(train_data.isnull().sum())
print(test_data.isnull().sum())

# 댓글이 삭제된 데이터 확인
train_data.loc[train_data["document"] == ""]

# 빈 댓글을 제외한 댓글만 다시저장
train_data = train_data[train_data["document"]!=""]
test_data = test_data[test_data["document"]!=""]

test_data.loc[test_data["document"] == ""]

이 코드는 train_data와 test_data 데이터프레임의 "document" 열에서 

한글이 아닌 문자와 빈 공백을 제거하는 작업을 수행합니다. 정규 표현식 [^ㄱ-ㅎㅏ-ㅣ가-힣 ]을 

사용하여 한글 문자가 아닌 문자를 찾아냅니다. 

그런 다음, str.replace() 함수를 사용하여 찾아낸 문자들을 빈 문자열로 대체합니다.

이 작업을 수행한 후, 각 데이터셋의 결측치를 확인하고 댓글이 삭제된 데이터가 있는지 확인합니다. 

빈 댓글이 있는 경우 해당 데이터를 제거하고 결과를 출력합니다.

코드는 한글 문자가 아닌 문자를 제거하고 빈 댓글을 처리하는 목적에 적합하게 작성되어 있으며, 

이 작업을 통해 머신 러닝 모델 학습에 사용되는 

데이터가 한글 문자와 빈 공백으로만 구성될 수 있도록 전처리할 수 있습니다.


  • 형태소 분리, 불용어 처리, 어간추출
import konlpy
from konlpy.tag import Okt
from tqdm import tqdm

okt = Okt()

X_train = []
X_test = []

# 불용어 목록
stopword = ['의','가','이','은','들','는','좀','잘','걍',
            '과','도','를','으로','자','에','와','한','하다']

for sen in tqdm(train_data["document"]) :
  temp_X = []

  # 형태소 분리
  temp_X = okt.morphs(sen)

  # 불용어 처리
  temp_X = [word for word in temp_X if word not in stopword]

  X_train.append(temp_X)

for sen in tqdm(test_data["document"]) :
  temp_X = []

  # 형태소 분리
  temp_X = okt.morphs(sen)

  # 불용어 처리
  temp_X = [word for word in temp_X if word not in stopword]

  X_test.append(temp_X)

이 코드는 train_data와 test_data의 "document" 열에 있는 문장을 형태소로 

분리하고 불용어를 제거하는 작업을 수행합니다. konlpy 라이브러리를 

사용하여 형태소 분석기인 Okt 객체를 생성합니다.

불용어 목록은 stopword 리스트에 저장되어 있습니다. 

코드는 train_data와 test_data의 각 문장에 대해 다음 작업을 수행합니다:

형태소 분리: 

Okt 객체의 morphs() 메서드를 사용하여 문장의 형태소를 분리하고, 

그 결과를 temp_X 리스트에 저장합니다.


불용어 처리: 

형태소로 분리된 단어들 중에서 불용어 목록에 포함되지 않은 단어들만 선택하여 temp_X 리스트에 저장합니다.


처리된 문장을 각각 X_train과 X_test 리스트에 추가합니다.


형태소 분리와 불용어 처리 작업을 적절하게 수행합니다.

이를 통해 머신 러닝 모델 학습에 사용될 데이터가 형태소로 분리되고 불용어가 제거된 상태로 전처리됩니다.


  • 중간 결과 저장
import pickle

with open("/content/drive/MyDrive/Colab Notebooks/X_train.pkl", "wb") as f:
    pickle.dump(X_train, f)

with open("/content/drive/MyDrive/Colab Notebooks/X_test.pkl", "wb") as f:
    pickle.dump(X_train, f)

이 코드는 전처리된 텍스트 데이터인 X_train과 X_test를 각각 pickle 파일로 저장합니다.

 pickle.dump() 함수를 사용하여 데이터를 파일에 저장합니다. 저장할 파일 경로는 Google Drive 내의 "Colab Notebooks" 폴더에 위치하며, 각각 "X_train.pkl"과 "X_test.pkl"라는 파일 이름으로 저장됩니다.

코드는 잘 작성되어 있으나, 한 가지 작은 문제가 있습니다. X_test를 저장하는 부분에서 변수가 잘못되어 X_train이 저장되고 있습니다. 아래와 같이 코드를 수정해야 합니다.

with open("/content/drive/MyDrive/Colab Notebooks/X_test.pkl", "wb") as f:
    pickle.dump(X_test, f)

위와 같이 수정하면, 코드는 정상적으로 작동하여 전처리된 텍스트 데이터를 pickle 파일로 저장할 수 있습니다.


  • 저장된 파일 읽기
import pickle

X_train = []
X_test = []

with open("/content/drive/MyDrive/Colab Notebooks/X_train.pkl", "rb") as f :
  X_train = pickle.load(f)
with open("/content/drive/MyDrive/Colab Notebooks/X_test.pkl", "rb") as f :
  X_test = pickle.load(f)
  
  
print(X_train[:5])
print(X_test[:5])

이 코드는 저장된 pickle 파일에서 전처리된 텍스트 데이터인 X_train과 X_test를 로드합니다.

 pickle.load() 함수를 사용하여 각각의 데이터를 파일로부터 읽어옵니다. 

읽어올 파일 경로는 Google Drive 내의 "Colab Notebooks" 폴더에 위치하며, 

각각 "X_train.pkl"과 "X_test.pkl"라는 파일 이름으로 저장되어 있습니다.

실행하면 저장된 X_train과 X_test 데이터가 정상적으로 로드됩니다.


  • 인코딩
    • 빈도수 분석(CounVectorizer, TfidfVecterizer, Token 등을 활용)
    • 빈도수 별로 정렬
    • 정렬된 순으로 인덱스 부여 (1부터)
  • padding : 같은 크기로 만들어줌 (pad_squence)
  • word embedding : 단어들의 관계성/ 유사도에 따라 벡터 공간에 배치(실수)
# 인코딩
from tensorflow.keras.preprocessing.text import Tokenizer

# 빈도수가 가장 높은 사용할 단어 수
max_word = 35000

# Tokenizer 객체 생성
tokenizer = Tokenizer(num_words=max_word)

# 빈도수 분석 - 훈련 데이터에만 적용
tokenizer.fit_on_texts(X_train)

# 인코딩
X_train = tokenizer.texts_to_sequences(X_train)
X_test = tokenizer.texts_to_sequences(X_test)


print(X_train[:5])
print(X_test[:5])

이 코드는 텍스트 데이터를 정수 인코딩하기 위해 Tokenizer를 사용합니다.

 빈도수가 높은 상위 35,000개의 단어를 사용하여 토크나이저를 초기화하고, 

훈련 데이터를 기반으로 빈도수를 분석합니다.

 그 다음, 훈련 데이터와 테스트 데이터를 각 단어의 정수 인덱스로 인코딩합니다.

텍스트 데이터를 정수 인코딩으로 변환하는 데 필요한 과정을 수행합니다.

 실행하면 X_train과 X_test의 첫 5개 요소가 정수 인덱스로 인코딩된 리스트로 출력됩니다.


# 같은 길이로 변환
from tensorflow.keras.preprocessing.sequence import pad_sequences

# 몇 개의 단어로 맞추는 지 설정
word_len = 30

X_train = pad_sequences(X_train, maxlen = word_len)
X_test = pad_sequences(X_test, maxlen = word_len)


print(X_train[1])
print(X_train[2])

y_train = train_data["label"]
y_test = test_data["label"]

코드는 올바르게 작성되어 있으며, pad_sequences를 사용하여 입력 시퀀스를 동일한 길이로 맞춥니다.

 이 과정에서 누락되거나 잘못된 부분은 없습니다.

주요 코드 내용:
pad_sequences를 사용하여 X_train 및 X_test 시퀀스를 word_len 길이로 패딩합니다.

 word_len은 30으로 설정되어 있습니다.


패딩된 X_train의 두 번째 및 세 번째 시퀀스를 출력하여 확인합니다.
y_train 및 y_test는 각각 train_data와 test_data의 "label" 열 값을 사용합니다.


  • 신경망으로 학습(RNN, LTSM, GRU, CNN, Attension)
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout, Embedding, SimpleRNN, LSTM, GRU

model1 = Sequential()

# 최대단어수, 차원수(한단어가 연계되는 최대단어수), 데이터의 길이
model1.add(Embedding(max_word, 100, input_length=word_len))

# 시계열 특성 추출
model1.add(LSTM(128))

# 분류기
model1.add(Dense(1, activation="sigmoid"))

model1.summary()

model1.compile(loss = "binary_crossentropy",
               optimizer = "rmsprop",
               metrics = ["accuracy"])
               
h1 = model1.fit(X_train, y_train, epochs = 2, batch_size= 20,
                validation_data=(X_test, y_test))

이 코드는 텍스트 데이터를 처리하기 위해 LSTM을 사용한 시퀀스 분류 모델을 만들고 

학습하는 과정을 보여줍니다. 코드에는 올바른 구성이 포함되어 있으며 다음 단계들을 수행합니다.

Sequential 모델을 정의합니다.


Embedding 층을 추가하여 단어 인덱스를 고정 크기의 벡터로 변환합니다. 
이 층은 모델에 입력되는 텍스트 데이터를 처리합니다.

LSTM 층을 추가하여 시계열 데이터의 특성을 추출합니다. 
LSTM은 RNN의 변형으로 긴 시퀀스에서 장기 의존성을 학습하는 데 도움이 됩니다.

Dense 층을 추가하여 분류기를 구성하고, 
시그모이드 활성화 함수를 사용하여 이진 분류 문제를 해결합니다.


모델의 구조를 출력합니다.

손실 함수로 binary_crossentropy를, 옵티마이저로 rmsprop를 사용하여 모델을 컴파일합니다.
 평가 지표로는 정확도를 사용합니다.

X_train과 y_train을 사용하여 모델을 학습하고, 
X_test와 y_test를 사용하여 검증 데이터를 평가합니다.

 학습은 2 에포크 동안 진행되며 배치 크기는 20으로 설정됩니다.


  • 예측하기
from konlpy.tag import Okt

sen = input("댓글 입력 ==> ")

# 한글과 빈공백 추출
sen = sen.replace("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]", "")

new_test = []

okt = Okt()

# 토큰화 (형태소 분리)
temp = okt.morphs(sen)

stopwords=['의','가','이','은','들','는','좀','잘','걍','과','도','를','으로','자','에','와','한','하다']

# 불용어 제거
temp = [word for word in temp if word not in stopwords]
new_test.append(temp)

# 인코딩
new_test = tokenizer.texts_to_sequences(new_test)

# 같은 길이로 변환
new_test = pad_sequences(new_test, maxlen=word_len)

# 분류
pred = model1.predict(new_test)

if (pred > 0.5) :
  print("긍정")
else :
  print("부정")

이 코드는 사용자가 입력한 댓글에 대해 긍정 또는 부정적인 감성을 분류하는 과정을 보여줍니다. 하지만, 이전에 언급했던 수정사항이 반영되지 않았습니다. 정규 표현식을 사용하여 한글과 빈 공백만 추출하는 부분을 수정해야 합니다.

다음과 같이 수정해야 합니다:

import re
sen = re.sub("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]", "", sen)
from konlpy.tag import Okt
import re

sen = input("댓글 입력 ==> ")

# 한글과 빈공백 추출
sen = re.sub("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]", "", sen)

new_test = []

okt = Okt()

# 토큰화 (형태소 분리)
temp = okt.morphs(sen)

stopwords=['의','가','이','은','들','는','좀','잘','걍','과','도','를','으로','자','에','와','한','하다']

# 불용어 제거
temp = [word for word in temp if word not in stopwords]
new_test.append(temp)

# 인코딩
new_test = tokenizer.texts_to_sequences(new_test)

# 같은 길이로 변환
new_test = pad_sequences(new_test, maxlen=word_len)

# 분류
pred = model1.predict(new_test)

if (pred > 0.5) :
  print("긍정")
else :
  print("부정")

사용자가 입력한 댓글에 대해 긍정 또는 부정적인 감성을 분류할 수 있습니다.

파일 다운 경로 :https://github.com/yoonhyochang/Deep_learning