library(ggplot2) # 시각화 코드 # install.packages("dplyr") # install.packages("tidyr") library(dplyr) # 데이터 가공 library(reshape) # 데이터 가공 <-- tidyr library(readr) # 파일 입출력
# ---- 훈련 검증용 데이터 분류 ---- set.seed(1234) idx = sample(1:nrow(pos_final_df), nrow(pos_final_df) * 0.7, replace = FALSE) train = pos_final_df[idx, ] test = pos_final_df[-idx, ]
Logistic Regression Model Develop
1 2 3 4 5 6 7 8 9 10 11
# --- 로지스틱 회귀 모형 개발 ---
start_time = Sys.time()
glm_model = step(glm(pos_binary ~ ., data = train[-1], family = binomial(link = "logit")), direction = "backward") # 후진소거법
End_time = Sys.time() difftime(End_time, start_time, units = "secs")
Step: AIC=2202.56
Logistic regression 안의 평가 기준
낮을 수록 좋다.
Step: AIC=2202.2 pos_binary ~ love + veri + just + size + dress + fit + will + back + like + tri + flatter + top + length + realli + shirt + materi
모형 성능 측정
1 2 3 4 5 6
# ---- 모형 성능 측정 ---- # install.packages("pROC") library(pROC) preds = predict(glm_model, newdata = test, type = "response") roc_glm = roc(test$pos_binary, preds) plot.roc(roc_glm, print.auc=TRUE)
정리
1. 정형 데이터 가져 오기
2. 정형 데이터 가공
- 좋아요 수를 활용하여 긍정/부정 data 나눔
3. 정형 데이터 분리 : 텍스트 데이터 따로 분리
4. 텍스트 데이터 처리 (전처리, 토큰화, 코퍼스, DTM)
5. 텍스트 데이터 + 기존 data 합침
6. ML 모형 진행 (다른 모형을 진행 해도 된다. )
하지만, 혹시 지금까지 배운 내용이 너무 어렵다면 python으로만 하는 것도 나쁘지 않다.
Cloning into 'Mecab-ko-for-Google-Colab'...
remote: Enumerating objects: 91, done.[K
remote: Total 91 (delta 0), reused 0 (delta 0), pack-reused 91[K
Unpacking objects: 100% (91/91), done.
/content/Mecab-ko-for-Google-Colab
Installing konlpy.....
Collecting konlpy
Downloading konlpy-0.5.2-py2.py3-none-any.whl (19.4 MB)
[K |████████████████████████████████| 19.4 MB 2.4 MB/s
[?25hCollecting JPype1>=0.7.0
Downloading JPype1-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl (448 kB)
[K |████████████████████████████████| 448 kB 23.5 MB/s
[?25hRequirement already satisfied: lxml>=4.1.0 in /usr/local/lib/python3.7/dist-packages (from konlpy) (4.2.6)
Collecting colorama
Downloading colorama-0.4.4-py2.py3-none-any.whl (16 kB)
Requirement already satisfied: tweepy>=3.7.0 in /usr/local/lib/python3.7/dist-packages (from konlpy) (3.10.0)
Requirement already satisfied: numpy>=1.6 in /usr/local/lib/python3.7/dist-packages (from konlpy) (1.19.5)
Collecting beautifulsoup4==4.6.0
Downloading beautifulsoup4-4.6.0-py3-none-any.whl (86 kB)
[K |████████████████████████████████| 86 kB 2.4 MB/s
[?25hRequirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from JPype1>=0.7.0->konlpy) (3.10.0.2)
Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.7/dist-packages (from tweepy>=3.7.0->konlpy) (1.3.0)
Requirement already satisfied: requests[socks]>=2.11.1 in /usr/local/lib/python3.7/dist-packages (from tweepy>=3.7.0->konlpy) (2.23.0)
Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.7/dist-packages (from tweepy>=3.7.0->konlpy) (1.15.0)
Requirement already satisfied: oauthlib>=3.0.0 in /usr/local/lib/python3.7/dist-packages (from requests-oauthlib>=0.7.0->tweepy>=3.7.0->konlpy) (3.1.1)
Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests[socks]>=2.11.1->tweepy>=3.7.0->konlpy) (1.24.3)
Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests[socks]>=2.11.1->tweepy>=3.7.0->konlpy) (3.0.4)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests[socks]>=2.11.1->tweepy>=3.7.0->konlpy) (2021.10.8)
Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests[socks]>=2.11.1->tweepy>=3.7.0->konlpy) (2.10)
Requirement already satisfied: PySocks!=1.5.7,>=1.5.6 in /usr/local/lib/python3.7/dist-packages (from requests[socks]>=2.11.1->tweepy>=3.7.0->konlpy) (1.7.1)
Installing collected packages: JPype1, colorama, beautifulsoup4, konlpy
Attempting uninstall: beautifulsoup4
Found existing installation: beautifulsoup4 4.6.3
Uninstalling beautifulsoup4-4.6.3:
Successfully uninstalled beautifulsoup4-4.6.3
Successfully installed JPype1-1.3.0 beautifulsoup4-4.6.0 colorama-0.4.4 konlpy-0.5.2
Done
Installing mecab-0.996-ko-0.9.2.tar.gz.....
Downloading mecab-0.996-ko-0.9.2.tar.gz.......
from https://bitbucket.org/eunjeon/mecab-ko/downloads/mecab-0.996-ko-0.9.2.tar.gz
--2021-12-15 08:19:45-- https://bitbucket.org/eunjeon/mecab-ko/downloads/mecab-0.996-ko-0.9.2.tar.gz
Resolving bitbucket.org (bitbucket.org)... 104.192.141.1, 2406:da00:ff00::22c0:3470, 2406:da00:ff00::22e9:9f55, ...
Connecting to bitbucket.org (bitbucket.org)|104.192.141.1|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://bbuseruploads.s3.amazonaws.com/eunjeon/mecab-ko/downloads/mecab-0.996-ko-0.9.2.tar.gz?Signature=Djk%2BX4VYfoZUGHzDRgTrcVVdFvE%3D&Expires=1639557778&AWSAccessKeyId=AKIA6KOSE3BNJRRFUUX6&versionId=null&response-content-disposition=attachment%3B%20filename%3D%22mecab-0.996-ko-0.9.2.tar.gz%22&response-content-encoding=None [following]
--2021-12-15 08:19:46-- https://bbuseruploads.s3.amazonaws.com/eunjeon/mecab-ko/downloads/mecab-0.996-ko-0.9.2.tar.gz?Signature=Djk%2BX4VYfoZUGHzDRgTrcVVdFvE%3D&Expires=1639557778&AWSAccessKeyId=AKIA6KOSE3BNJRRFUUX6&versionId=null&response-content-disposition=attachment%3B%20filename%3D%22mecab-0.996-ko-0.9.2.tar.gz%22&response-content-encoding=None
Resolving bbuseruploads.s3.amazonaws.com (bbuseruploads.s3.amazonaws.com)... 52.216.113.163
Connecting to bbuseruploads.s3.amazonaws.com (bbuseruploads.s3.amazonaws.com)|52.216.113.163|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1414979 (1.3M) [application/x-tar]
Saving to: ‘mecab-0.996-ko-0.9.2.tar.gz’
mecab-0.996-ko-0.9. 100%[===================>] 1.35M 1.07MB/s in 1.3s
2021-12-15 08:19:48 (1.07 MB/s) - ‘mecab-0.996-ko-0.9.2.tar.gz’ saved [1414979/1414979]
Done
Unpacking mecab-0.996-ko-0.9.2.tar.gz.......
Done
Change Directory to mecab-0.996-ko-0.9.2.......
installing mecab-0.996-ko-0.9.2.tar.gz........
configure
make
make check
make install
ldconfig
Done
Change Directory to /content
Downloading mecab-ko-dic-2.1.1-20180720.tar.gz.......
from https://bitbucket.org/eunjeon/mecab-ko-dic/downloads/mecab-ko-dic-2.1.1-20180720.tar.gz
--2021-12-15 08:21:19-- https://bitbucket.org/eunjeon/mecab-ko-dic/downloads/mecab-ko-dic-2.1.1-20180720.tar.gz
Resolving bitbucket.org (bitbucket.org)... 104.192.141.1, 2406:da00:ff00::6b17:d1f5, 2406:da00:ff00::22cd:e0db, ...
Connecting to bitbucket.org (bitbucket.org)|104.192.141.1|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://bbuseruploads.s3.amazonaws.com/a4fcd83e-34f1-454e-a6ac-c242c7d434d3/downloads/b5a0c703-7b64-45ed-a2d7-180e962710b6/mecab-ko-dic-2.1.1-20180720.tar.gz?Signature=ZNAR2x6%2FNWxJ4p%2BOkG%2BjdG77Dqk%3D&Expires=1639558279&AWSAccessKeyId=AKIA6KOSE3BNJRRFUUX6&versionId=tzyxc1TtnZU_zEuaaQDGN4F76hPDpyFq&response-content-disposition=attachment%3B%20filename%3D%22mecab-ko-dic-2.1.1-20180720.tar.gz%22&response-content-encoding=None [following]
--2021-12-15 08:21:19-- https://bbuseruploads.s3.amazonaws.com/a4fcd83e-34f1-454e-a6ac-c242c7d434d3/downloads/b5a0c703-7b64-45ed-a2d7-180e962710b6/mecab-ko-dic-2.1.1-20180720.tar.gz?Signature=ZNAR2x6%2FNWxJ4p%2BOkG%2BjdG77Dqk%3D&Expires=1639558279&AWSAccessKeyId=AKIA6KOSE3BNJRRFUUX6&versionId=tzyxc1TtnZU_zEuaaQDGN4F76hPDpyFq&response-content-disposition=attachment%3B%20filename%3D%22mecab-ko-dic-2.1.1-20180720.tar.gz%22&response-content-encoding=None
Resolving bbuseruploads.s3.amazonaws.com (bbuseruploads.s3.amazonaws.com)... 54.231.82.195
Connecting to bbuseruploads.s3.amazonaws.com (bbuseruploads.s3.amazonaws.com)|54.231.82.195|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 49775061 (47M) [application/x-tar]
Saving to: ‘mecab-ko-dic-2.1.1-20180720.tar.gz’
mecab-ko-dic-2.1.1- 100%[===================>] 47.47M 13.0MB/s in 4.5s
2021-12-15 08:21:25 (10.5 MB/s) - ‘mecab-ko-dic-2.1.1-20180720.tar.gz’ saved [49775061/49775061]
Done
Unpacking mecab-ko-dic-2.1.1-20180720.tar.gz.......
Done
Change Directory to mecab-ko-dic-2.1.1-20180720
Done
installing........
configure
make
make install
apt-get update
apt-get upgrade
apt install curl
apt install git
bash <(curl -s https://raw.githubusercontent.com/konlpy/konlpy/master/scripts/mecab.sh)
Done
Successfully Installed
Now you can use Mecab
from konlpy.tag import Mecab
mecab = Mecab()
사용자 사전 추가 방법 : https://bit.ly/3k0ZH53
NameError: name 'Tagger' is not defined 오류 발생 시 런타임을 재실행 해주세요
블로그에 해결 방법을 남겨주신 tana님 감사합니다.
네이버 쇼핑 리뷰 데이터에 대한 이해와 전처리
1 2 3 4 5 6 7 8 9 10
import re import pandas as pd import numpy as np import matplotlib.pyplot as plt import urllib.request from collections import Counter from konlpy.tag import Mecab from sklearn.model_selection import train_test_split from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences
('ratings_total.txt', <http.client.HTTPMessage at 0x7f7d3557f750>)
해당 데이터에는 열 제목이 별도로 없음. 그래서 임의로 두 개의 열제목인 “ratings”와 “reviews” 추가
1 2 3
# (1) 데이터 불러오고, 전체 리뷰 개수 출력 # 200,000 totalDt = pd.read_table('ratings_total.txt', names=['ratings', 'reviews']) print('전체 리뷰 개수 :',len(totalDt)) # 전체 리뷰 개수 출력
ratings열의 경우 1, 2, 4, 5라는 네 가지 값을 가지고 있습니다. reviews열에서 중복을 제외한 경우 199,908개입니다. 현재 20만개의 리뷰가 존재하므로 이는 현재 갖고 있는 데이터에 중복인 샘플들이 있다는 의미입니다. 중복인 샘플들을 제거해줍니다.
1 2 3
# (2) review열에서 중복 데이터 제거 drop_duplicates() 함수 활용 totalDt.drop_duplicates(subset=['reviews'], inplace=True) print('총 샘플의 수 :',len(totalDt))
# 한글과 공백을 제외하고 모두 제거 # (4) 한글 및 공백 제외한 모든 글자 제거 train_data['reviews'] = train_data['reviews'].str.replace("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]","") train_data['reviews'].replace('', np.nan, inplace=True) print(train_data.isnull().sum())
ratings 0
reviews 0
label 0
dtype: int64
테스트 데이터에 대해서도 같은 과정을 거칩니다.
1 2 3 4 5 6 7 8 9 10
# (5) 데스트 데이터에 적용하기 # 코드 1 중복 제거 # 코드 2 정규 표현식 수행 # 코드 3 공백은 Null 값으로 변경 # 코드 4 Null 값 제거 test_data.drop_duplicates(subset = ['reviews'], inplace=True) # 중복 제거 test_data['reviews'] = test_data['reviews'].str.replace("[^ㄱ-ㅎㅏ-ㅣ가-힣 ]","") # 정규 표현식 수행 test_data['reviews'].replace('', np.nan, inplace=True) # 공백은 Null 값으로 변경 test_data = test_data.dropna(how='any') # Null 값 제거 print('전처리 후 테스트용 샘플의 개수 :',len(test_data))
전처리 후 테스트용 샘플의 개수 : 49977
토큰화
형태소 분석기 Mecab을 사용하여 토큰화 작업을 수행한다.
1 2 3
# (6) Mecab 클래스 호출하기 mecab = Mecab() print(mecab.morphs('와 이런 것도 상품이라고 차라리 내가 만드는 게 나을 뻔'))
train_data['tokenized'] = train_data['reviews'].apply(mecab.morphs) train_data['tokenized'] = train_data['tokenized'].apply(lambda x: [item for item in x if item notin stopwords])
1 2
test_data['tokenized'] = test_data['reviews'].apply(mecab.morphs) test_data['tokenized'] = test_data['tokenized'].apply(lambda x: [item for item in x if item notin stopwords])
단어와 길이 분포 확인하기
긍정 리뷰에는 주로 어떤 단어들이 많이 등장하고, 부정 리뷰에는 주로 어떤 단어들이 등장하는지 두 가지 경우에 대해서 각 단어의 빈도수를 계산해보겠습니다. 각 레이블에 따라서 별도로 단어들의 리스트를 저장해줍니다.
이제 기계가 텍스트를 숫자로 처리할 수 있도록 훈련 데이터와 테스트 데이터에 정수 인코딩을 수행해야 합니다. 우선, 훈련 데이터에 대해서 단어 집합(vocaburary)을 만들어봅시다.
1 2 3
# (9) 정수 인코딩 클래스 호출 및 X_train 데이터에 적합하기 tokenizer = Tokenizer() tokenizer.fit_on_texts(X_train)
단어 집합이 생성되는 동시에 각 단어에 고유한 정수가 부여되었습니다. 이는 tokenizer.word_index를 출력하여 확인 가능합니다. 등장 횟수가 1회인 단어들은 자연어 처리에서 배제하고자 합니다. 이 단어들이 이 데이터에서 얼만큼의 비중을 차지하는지 확인해봅시다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
threshold = 2 total_cnt = len(tokenizer.word_index) # 단어의 수 rare_cnt = 0# 등장 빈도수가 threshold보다 작은 단어의 개수를 카운트 total_freq = 0# 훈련 데이터의 전체 단어 빈도수 총 합 rare_freq = 0# 등장 빈도수가 threshold보다 작은 단어의 등장 빈도수의 총 합
# 단어와 빈도수의 쌍(pair)을 key와 value로 받는다. for key, value in tokenizer.word_counts.items(): total_freq = total_freq + value
# 단어의 등장 빈도수가 threshold보다 작으면 if(value < threshold): rare_cnt = rare_cnt + 1 rare_freq = rare_freq + value
print('단어 집합(vocabulary)의 크기 :',total_cnt) print('등장 빈도가 %s번 이하인 희귀 단어의 수: %s'%(threshold - 1, rare_cnt)) print("단어 집합에서 희귀 단어의 비율:", (rare_cnt / total_cnt)*100) print("전체 등장 빈도에서 희귀 단어 등장 빈도 비율:", (rare_freq / total_freq)*100)
단어 집합(vocabulary)의 크기 : 39998
등장 빈도가 1번 이하인 희귀 단어의 수: 18213
단어 집합에서 희귀 단어의 비율: 45.53477673883694
전체 등장 빈도에서 희귀 단어 등장 빈도 비율: 0.7935698749320282
단어가 약 40,000개가 존재합니다. 등장 빈도가 threshold 값인 2회 미만. 즉, 1회인 단어들은 단어 집합에서 약 45%를 차지합니다. 하지만, 실제로 훈련 데이터에서 등장 빈도로 차지하는 비중은 매우 적은 수치인 약 0.8%밖에 되지 않습니다. 아무래도 등장 빈도가 1회인 단어들은 자연어 처리에서 별로 중요하지 않을 듯 합니다. 그래서 이 단어들은 정수 인코딩 과정에서 배제시키겠습니다.
등장 빈도수가 1인 단어들의 수를 제외한 단어의 개수를 단어 집합의 최대 크기로 제한하겠습니다.
1 2 3 4
# 전체 단어 개수 중 빈도수 2이하인 단어 개수는 제거. # 0번 패딩 토큰과 1번 OOV 토큰을 고려하여 +2 vocab_size = total_cnt - rare_cnt + 2 print('단어 집합의 크기 :',vocab_size)
단어 집합의 크기 : 21787
이제 단어 집합의 크기는 21,787개입니다. 이를 토크나이저의 인자로 넘겨주면, 토크나이저는 텍스트 시퀀스를 숫자 시퀀스로 변환합니다. 이러한 정수 인코딩 과정에서 이보다 큰 숫자가 부여된 단어들은 OOV로 변환하겠습니다.
이제 서로 다른 길이의 샘플들의 길이를 동일하게 맞춰주는 패딩 작업을 진행해보겠습니다. 전체 데이터에서 가장 길이가 긴 리뷰와 전체 데이터의 길이 분포를 알아보겠습니다.
1 2 3 4 5 6
print('리뷰의 최대 길이 :',max(len(l) for l in X_train)) print('리뷰의 평균 길이 :',sum(map(len, X_train))/len(X_train)) plt.hist([len(s) for s in X_train], bins=35, label='bins=35', color="skyblue") plt.xlabel('length of samples') plt.ylabel('number of samples') plt.show()
리뷰의 최대 길이 : 85
리뷰의 평균 길이 : 15.307521459871541
리뷰의 최대 길이는 85, 평균 길이는 약 15입니다.
그리고 그래프로 봤을 때, 전체적으로는 60이하의 길이를 가지는 것으로 보입니다.
1 2 3 4 5 6 7 8
defbelow_threshold_len(max_len, nested_list): count = 0 for sentence in nested_list: if(len(sentence) <= max_len): count = count + 1 print('전체 샘플 중 길이가 %s 이하인 샘플의 비율: %s'%(max_len, (count / len(nested_list))*100))
최대 길이가 85이므로 만약 80으로 패딩할 경우, 몇 개의 샘플들을 온전히 보전할 수 있는지 확인해봅시다.
from tensorflow.keras.layers import Embedding, Dense, GRU from tensorflow.keras.models import Sequential from tensorflow.keras.models import load_model from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
embedding_dim = 100 hidden_units = 128
model = Sequential() model.add(Embedding(vocab_size, embedding_dim)) model.add(GRU(hidden_units)) model.add(Dense(1, activation='sigmoid'))
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=4) mc = ModelCheckpoint('best_model.h5', monitor='val_acc', mode='max', verbose=1, save_best_only=True)