후기 긍정부정 중립 분류 최최최종 파일
크롤링사이트 다나와 로봇청소기 - 에브리봇
라이브러리
import pandas as pd
import numpy as np
폰트 설정
import matplotlib.pyplot as plt
from matplotlib import font_manager, rc
font_path = "C:/Windows/Fonts/NGULIM.TTF"
font = font_manager.FontProperties(fname=font_path).get_name()
rc('font', family=font)
%matplotlib inline
리뷰 크롤링¶
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
driver = webdriver.Chrome('chromedriver.exe')
driver.implicitly_wait(6)
url = "https://prod.danawa.com/info/?pcode=12058100&keyword=%EC%97%90%EB%B8%8C%EB%A6%AC%EB%B4%87&cate=103744#bookmark_cm_opinion"
driver.get(url)
# version_2 : 10페이지 출력후 다음버튼 누름,
from selenium.webdriver.common.by import By
import pandas as pd
import time
review_list = []
star_list = []
for page in range(1, 42):
reviews = driver.find_element(By.CLASS_NAME,'rvw_list')
titles = reviews.find_elements(By.CLASS_NAME, 'tit')
stars = reviews.find_elements(By.CLASS_NAME, 'star_mask')
print(page, titles[0].text)
for title, star in zip(titles, stars):
review_list.append(title.text)
star_list.append(star.text)
# print(star.text, '---', title.text)
#
# if page<10:
# driver.find_element(By.XPATH, f'//*[@id="danawa-prodBlog-companyReview-content-list"]/div/div/div/a[{page}]').click()
# time.sleep(3)
# else:# 다음 버튼 누르기
# driver.find_element(By.XPATH, f'//*[@id="danawa-prodBlog-companyReview-content-list"]/div/div/a').click()
if page % 10 == 0:
#페이지 10, 20번이면 다음버튼 클릭
driver.find_element(By.XPATH, f'//*[@id="danawa-prodBlog-companyReview-content-list"]/div/div/a').click()
time.sleep(5)
else:
# 페이지 10, 20 아닐때,
driver.find_element(By.XPATH, f'//*[@id="danawa-prodBlog-companyReview-content-list"]/div/div/div/a[{page%10}]').click()
time.sleep(5)
C:\Users\82105\AppData\Local\Temp\ipykernel_21988\170368705.py:4: DeprecationWarning: executable_path has been deprecated, please pass in a Service object
driver = webdriver.Chrome('chromedriver.exe')
1 최고예요
2 와우 판타스틱!!!
3 만족합니다~~
4 작동이 안되요
5 최고예요
6 소음이 좀 있긴하지만 청소 잘함 앱을 쓰지 않아서인
7 최고예요
8 소리가 매우 시끄러워요
9 최고예요
10 최고예요
11 가성비 좋네요
12 최고예요
13 빠르게 도착했어요 ㅎㅎ
14 좋아요
15 좋아요
16 맘에 쏙 듭니다ㆍ
17 물걸레는 걍그런데 청소는 잘 하네요
18 좋아요
19 혼자 돌아다니면서 청소 잘하네요대만족입니다
20 좋아요
21 최고예요
22 최고예요
23 만족합니다~~
24 작동이 안되요
25 최고예요
26 소음이 좀 있긴하지만 청소 잘함 앱을 쓰지 않아서인
27 최고예요
28 소리가 매우 시끄러워요
29 최고예요
30 최고예요
31 가성비 좋네요
32 최고예요
33 빠르게 도착했어요 ㅎㅎ
34 좋아요
35 좋아요
36 맘에 쏙 듭니다ㆍ
37 물걸레는 걍그런데 청소는 잘 하네요
38 좋아요
39 혼자 돌아다니면서 청소 잘하네요대만족입니다
40 좋아요
41 최고예요
데이터 프레임 형식 저장¶
review_df = pd.DataFrame(zip(review_list, star_list), columns=['title','star'])
review_df.to_csv('danawa_review.csv',index=False)
review_df
title | star | |
---|---|---|
0 | 최고예요 | 100점 |
1 | 반품했습니다. 제품은 집 구조가 원룸이거나 단순하지 않... | 20점 |
2 | 최고예요 | 100점 |
3 | 좋아요 | 80점 |
4 | 청소영역을 스캔해서 빠짐없이 청소하는 것은 맘에 드네요... | 80점 |
... | ... | ... |
405 | 최고예요 | 100점 |
406 | 최고예요 | 100점 |
407 | 최고예요 | 100점 |
408 | 최고예요 | 100점 |
409 | 최고예요 | 100점 |
410 rows × 2 columns
똑같은 리뷰 제거¶
# 중복 문장 제거 함수 drop_duplicates
review_df.drop_duplicates(subset='title', inplace=True)
len(review_df)
93
별점기반 등급 나누기 "group"¶
# 3등급 나누기
review_df['star']=review_df['star'].str.strip('점') # 문장 중 '점' 인 단어 삭제
review_df['star'] = pd.to_numeric(review_df['star']) # to_numeric -> str를 숫자로 변경
def get_group(v):
if v < 60:
group = 3
elif v < 100:
group = 2
else:
group = 1
return group
review_df["group"] = review_df["star"].apply(lambda v: get_group(v))
review_df
title | star | group | |
---|---|---|---|
0 | 최고예요 | 100 | 1 |
1 | 반품했습니다. 제품은 집 구조가 원룸이거나 단순하지 않... | 20 | 3 |
3 | 좋아요 | 80 | 2 |
4 | 청소영역을 스캔해서 빠짐없이 청소하는 것은 맘에 드네요... | 80 | 2 |
9 | 신세계를 맛보고 있습니다 | 100 | 1 |
... | ... | ... | ... |
193 | 배송도 빠르고 소음이 조금 크지만 잘 돌아가네요 | 100 | 1 |
194 | 제품은 만족하나 판매자 배송 시스템은 문제가 많은 듯 ... | 60 | 2 |
195 | 청소는 잘 하는데 너무 시끄러워요ㅠ | 100 | 1 |
196 | 제가 사용하다가 친정부모님 사드렸는데 마음에 들어하시네... | 100 | 1 |
197 | 생각보다 크기가 커요 선물했는데 만족해해서 좋아요! | 100 | 1 |
93 rows × 3 columns
review_df=review_df.reset_index(drop=True)
review_df
title | star | group | |
---|---|---|---|
0 | 최고예요 | 100 | 1 |
1 | 반품했습니다. 제품은 집 구조가 원룸이거나 단순하지 않... | 20 | 3 |
2 | 좋아요 | 80 | 2 |
3 | 청소영역을 스캔해서 빠짐없이 청소하는 것은 맘에 드네요... | 80 | 2 |
4 | 신세계를 맛보고 있습니다 | 100 | 1 |
... | ... | ... | ... |
88 | 배송도 빠르고 소음이 조금 크지만 잘 돌아가네요 | 100 | 1 |
89 | 제품은 만족하나 판매자 배송 시스템은 문제가 많은 듯 ... | 60 | 2 |
90 | 청소는 잘 하는데 너무 시끄러워요ㅠ | 100 | 1 |
91 | 제가 사용하다가 친정부모님 사드렸는데 마음에 들어하시네... | 100 | 1 |
92 | 생각보다 크기가 커요 선물했는데 만족해해서 좋아요! | 100 | 1 |
93 rows × 3 columns
"label" 구분 위한 단어 뭉치 생성 (positive, negative)¶
from konlpy.tag import Hannanum
hunnanum = Hannanum() # 문장중 명사만 뽑아낸다.
positive = review_df[review_df['group']==1].title.tolist() # 그룹이 1인 행만 뽑기
positive
positive1 = list(map(lambda title: hunnanum.nouns(title), positive)) # 명사인 단어만 추출 # nouns : 명사 추출 morphs : 형태소 추출 pos : 품사 부착
positive1 = np.concatenate(positive1).tolist() # [[],[]]의 리스트 형태를 []로 리스트들을 합쳐 준다 그래서 앞에 concat이 붙는다
positive1#
['최고',
'신세계',
'와우',
'판타스틱',
'사용',
'사용후',
'기대반',
'의심반',
'구매',
'완전',
'신',
'만족',
'사용중이',
'생각',
'잘쓰',
'34평아파트',
'걸레청소모드',
'배송',
'정확',
'배송',
'디자인',
'추천',
'생각',
'가동시간',
'짧.',
'은',
'느낌',
'만족',
'청소',
'에브리봇',
'엣지물걸레로봇청소',
'너무좋아서3ipop',
'가격대비',
'시대',
'소음',
'있긴하',
'청소',
'앱',
'않아',
'처음',
'지',
'완성',
'안되서5번넘',
'지도완성',
'통',
'먼지통',
'분리',
'3i',
'청소시간',
'반',
'방안',
'깨끗',
'에브리봇',
'3i,',
'엣지',
'처음',
'한곳',
'여러번',
'적응',
'시간',
'만족',
'우리집',
'효도쟁',
'청소',
'물걸레',
'완전',
'대만족',
'ㅎㅎ',
'애',
'고집',
'스마트',
'청소기',
'가격',
'저렴하게구입했어요',
'소음',
'편',
'청소',
'것',
'가성비',
'처음',
'내손',
'신통방통',
'에브리봇,',
'좋아요맞벌이부부들',
'정말정말',
'에브리봇',
'구입',
'에브리봇엣지',
'사용',
'너무좋게잘쓰고있어서',
'이제품도추',
'가격',
'비교',
'쿠폰',
'저렴',
'구매',
'가격대비',
'일단',
'디자인이나색상',
'만족스럽네요받고',
'충전',
'도착',
'ㅎㅎ',
'먼지걱정',
'바닥',
'깔끔하네요에브리이모',
'감사',
'도착',
'턱',
'청소',
'물걸레질',
'너',
'청소걱정',
'끝',
'로봇청소기',
'삶의질',
'만족해요청소잘해요',
'어머니',
'걸레질',
'깨끗',
'됩니딘',
'구매해서',
'선물해',
'드렸어요너무',
'좋아하시네요빅스마일데이',
'동영상',
'업로드',
'듭니다ㆍ',
'살걸ㅠ',
'신세계',
'한번',
'사용',
'지',
'집',
'작동',
'좋다네요',
'청소',
'물걸레',
'걍그런데',
'청소',
'물걸레',
'청소기',
'물걸레겸용',
'청소도잘되',
'빠른배송',
'감사',
'저렴',
'것',
'수',
'청소',
'에브리봇3i',
'청소',
'잘하네요대만족',
'울집',
'청소부',
'만족합니다',
'배송',
'소음',
'청소',
'시끄러워요ㅠ',
'저',
'사용',
'친정부모님',
'마음',
'생각',
'선물',
'만족']
# 애는 긍정 부정이아닌 전체 리뷰를 명사화 시킨것
from konlpy.tag import Hannanum
from konlpy.tag import Okt
import numpy as np
hunnanum = Hannanum()
t = Okt()
# list(map(lambda title: t.nouns(title), review_list))
han_noun_review = list(map(lambda title: hunnanum.nouns(title), review_list))
han_noun_review_1 = np.concatenate(han_noun_review).tolist()
긍정 단어들 리스트를 텍스트 파일에 저장¶
with open('positive.txt','w',encoding='UTF-8') as f:
for po in positive1:
f.write(po+'\n')
긍정과 동일하게 부정단어들도 전체 리뷰에서 가져와 단어 토큰화 시킴¶
negative = review_df[review_df['group'] == 3].title.tolist()
negative1 = list(map(lambda title: hunnanum.nouns(title), negative))
negative1 = np.concatenate(negative1).tolist()
negative1
['반품',
'제품',
'집',
'구조',
'원룸',
'단순',
'않.',
'최악',
'별',
'모서리만돌면서청소하고거실가운데는하지도못함쇼파밑에서']
with open('negative.txt','w',encoding='UTF-8') as f:
for po in negative1:
f.write(po+'\n')
긍정, 부정 단어 직접 수정 후, 불러오기
with open("negative.txt", encoding='utf-8') as neg:
negative = neg.readlines()
negative = [neg.replace("\n", "") for neg in negative]
with open("positive.txt", encoding='utf-8') as pos:
positive = pos.readlines()
negative = [neg.replace("\n", "") for neg in negative]
positive = [pos.replace("\n", "") for pos in positive]
review_df
title | star | group | |
---|---|---|---|
0 | 최고예요 | 100 | 1 |
1 | 반품했습니다. 제품은 집 구조가 원룸이거나 단순하지 않... | 20 | 3 |
2 | 좋아요 | 80 | 2 |
3 | 청소영역을 스캔해서 빠짐없이 청소하는 것은 맘에 드네요... | 80 | 2 |
4 | 신세계를 맛보고 있습니다 | 100 | 1 |
... | ... | ... | ... |
88 | 배송도 빠르고 소음이 조금 크지만 잘 돌아가네요 | 100 | 1 |
89 | 제품은 만족하나 판매자 배송 시스템은 문제가 많은 듯 ... | 60 | 2 |
90 | 청소는 잘 하는데 너무 시끄러워요ㅠ | 100 | 1 |
91 | 제가 사용하다가 친정부모님 사드렸는데 마음에 들어하시네... | 100 | 1 |
92 | 생각보다 크기가 커요 선물했는데 만족해해서 좋아요! | 100 | 1 |
93 rows × 3 columns
라벨붙이기¶
from tqdm import tqdm
import re
labels = []
title_data = list(review_df['title']) # review_df.title은 전체 리뷰이며 토큰화 전인 문장이다.
for title in tqdm(title_data):
clean_title = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…\"\“》]', '', title) # 리뷰를 특수문자는 공백으로 변환
#negative_flag = False
label = 0 # 기본 레이블 0 으로 중립
for i in range(len(negative)):
if negative[i] in clean_title: # 리뷰 문장 중 부정 단어목록에 있는 단어가 있다면
label = -1 # 레이블은 -1로 한다.
#negative_flag = True # False를 True로 바꾼다.
print("negative 비교단어 : ", negative[i], "clean_title : ", clean_title)
break
#if negative_flag == False: # negative_flag가 False이고
for i in range(len(positive)):
if positive[i] in clean_title: # 리뷰 문장 중 긍정 단어목록에 있는 단어가 있다면
label = 1 #레이블은 1로 한다.
print("positive 비교단어 : ", positive[i], "clean_title : ", clean_title)
break
labels.append(label)# 긍정이면 1 부정이면 -1 중립이면 0으로 리스트에 넣어준다.
review_df['label'] = labels
100%|████████████████████████████████████████████████████████████████████████████████| 93/93 [00:00<00:00, 6070.85it/s]
positive 비교단어 : 최고 clean_title : 최고예요
negative 비교단어 : 반품 clean_title : 반품했습니다 제품은 집 구조가 원룸이거나 단순하지 않
positive 비교단어 : 은 clean_title : 반품했습니다 제품은 집 구조가 원룸이거나 단순하지 않
positive 비교단어 : 은 clean_title : 청소영역을 스캔해서 빠짐없이 청소하는 것은 맘에 드네요
positive 비교단어 : 신세계 clean_title : 신세계를 맛보고 있습니다
positive 비교단어 : 와우 clean_title : 와우 판타스틱
negative 비교단어 : 최악 clean_title : 최악이에요
positive 비교단어 : 사용 clean_title : 잘 사용하고 있습니다
positive 비교단어 : 사용 clean_title : 사용후기를 보고 기대반 의심반으로 구매했는데 완전 신
positive 비교단어 : 만족 clean_title : 만족합니다
positive 비교단어 : 추천 clean_title : 추천하지만 아쉬운 점
positive 비교단어 : 사용 clean_title : 지금 사용중이에요
negative 비교단어 : 별 clean_title : 별로예요
positive 비교단어 : 생각 clean_title : 생각보다 잘쓰고 있습니다 34평아파트 걸레청소모드로
positive 비교단어 : 작동 clean_title : 작동이 안되요
positive 비교단어 : 편 clean_title : 편한건 좋으나 제가 직접할 때보단 오래 걸려요했던데
positive 비교단어 : 배송 clean_title : 배송은 아주 빠르고 정확하게 배송되었습니다디자인이
positive 비교단어 : 통 clean_title : 보통이에요
positive 비교단어 : 구매 clean_title : 물걸레만 구매하려다가 공용으로 구매했는데 걸레가 얇고
positive 비교단어 : 추천 clean_title : 추천합니다
positive 비교단어 : 만족 clean_title : 생각보다 가동시간이 좀 짧은 느낌이지만 만족하고요
positive 비교단어 : 청소 clean_title : 정말 청소 잘하네요
positive 비교단어 : 청소 clean_title : 에브리봇 엣지물걸레로봇청소기 쓰다가 너무좋아서3ipop
positive 비교단어 : 가격대비 clean_title : 가격대비 정말 좋아요
positive 비교단어 : 시대 clean_title : 역시 시대가 좋네요
positive 비교단어 : 지 clean_title : 아직 넘어야 할산이 많네요어플 연동 오류가 있고 지도
positive 비교단어 : 청소 clean_title : 소음이 좀 있긴하지만 청소 잘함 앱을 쓰지 않아서인
positive 비교단어 : 처음 clean_title : 처음엔 지도 완성이 잘 안되서5번넘게 해서 지도완성
positive 비교단어 : 지 clean_title : 물통이랑 먼지통이랑 따로 분리되어 있어 3i po
positive 비교단어 : 청소 clean_title : 흡입청소는 잘하네요 옆에서 바람이 느껴져서 먼지가 흗
negative 비교단어 : 모서리만돌면서청소하고거실가운데는하지도못함쇼파밑에서 clean_title : 모서리만돌면서청소하고거실가운데는하지도못함쇼파밑에서
positive 비교단어 : 청소 clean_title : 모서리만돌면서청소하고거실가운데는하지도못함쇼파밑에서
positive 비교단어 : 청소 clean_title : 청소시간이 반으로 줄었네요 자주 돌려주니 방안도 깨끗
positive 비교단어 : 청소 clean_title : 청소기
positive 비교단어 : 너 clean_title : 너무 좋아요
positive 비교단어 : 청소 clean_title : 물걸레 기능시 제자리 헛돌기를 합니다 일반 청소 기능
positive 비교단어 : 에브리봇 clean_title : 에브리봇 3i 엣지 쓰다가
positive 비교단어 : 처음 clean_title : 처음에는 한곳만 여러번 닦아 적응하는데 시간이 걸렸는데
positive 비교단어 : 청소 clean_title : 맘에 듭니다 ㅋㅋ 자동청소 돌려놓고 베란다에서 도촬함
positive 비교단어 : 만족 clean_title : 만족
negative 비교단어 : 집 clean_title : 우리집 효도쟁이 청소도 잘되고 물걸레도 완전 대만족 가
positive 비교단어 : 완전 clean_title : 우리집 효도쟁이 청소도 잘되고 물걸레도 완전 대만족 가
negative 비교단어 : 집 clean_title : ㅎㅎ 꼭 사세요 애가 참 똑똑해요 고집은 쎄서 했던
positive 비교단어 : 은 clean_title : ㅎㅎ 꼭 사세요 애가 참 똑똑해요 고집은 쎄서 했던
positive 비교단어 : 청소 clean_title : 스마트한 청소기
positive 비교단어 : 가성비 clean_title : 가성비굿
positive 비교단어 : 은 clean_title : 가격 저렴하게구입했어요소음은 큰편인데 청소 잘되는것
positive 비교단어 : 편 clean_title : 아주 편해요
positive 비교단어 : 가성비 clean_title : 가성비 좋네요
negative 비교단어 : 집 clean_title : 역시 좋네요 처음에 집지도 그리는데헤매지만 내손
positive 비교단어 : 처음 clean_title : 역시 좋네요 처음에 집지도 그리는데헤매지만 내손
positive 비교단어 : 신 clean_title : 신통방통한 에브리봇 넘 맘에 들고 좋아요맞벌이부부들
positive 비교단어 : 정말정말 clean_title : 정말정말 좋아요
positive 비교단어 : 청소 clean_title : 로봇청소기
positive 비교단어 : 에브리봇 clean_title : 에브리봇 구입
positive 비교단어 : 은 clean_title : 흡입력은마음에들고배터리시간이 짧아 중간에 충전찾아가는
negative 비교단어 : 제품 clean_title : 에브리봇엣지 사용하면서 너무좋게잘쓰고있어서 이제품도추가
positive 비교단어 : 사용 clean_title : 에브리봇엣지 사용하면서 너무좋게잘쓰고있어서 이제품도추가
positive 비교단어 : 구매 clean_title : 가격 비교하다가 쿠폰으로 저렴하게 구매했어요 가격대비
positive 비교단어 : 만족 clean_title : 일단 디자인이나색상 만족스럽네요받고 충전중입니다
positive 비교단어 : ㅎㅎ clean_title : 빠르게 도착했어요 ㅎㅎ
positive 비교단어 : 지 clean_title : 먼지걱정이 없어졌네요 바닥이 깔끔하네요에브리이모 감사
positive 비교단어 : 도착 clean_title : 잘 도착했어요
positive 비교단어 : 은 clean_title : 턱은 못 넘지만 청소 잘 되고 물걸레질도 잘 되니까 너
positive 비교단어 : 청소 clean_title : 청소걱정 끝이네요
positive 비교단어 : 만족 clean_title : 로봇청소기 들이니 삶의질이 다르네요 만족해요청소잘해요
positive 비교단어 : 깨끗 clean_title : 어머니가 너무너무 좋아하세요 걸레질 깨끗하게 됩니딘
positive 비교단어 : 구매 clean_title : 구매해서 선물해 드렸어요너무 좋아하시네요빅스마일데이
positive 비교단어 : 동영상 clean_title : 동영상 업로드는 안되네요
positive 비교단어 : 만족 clean_title : 아주 똑똑하진 못하지만 만족해요 그래도 옛날보다는 많
positive 비교단어 : 살걸ㅠ clean_title : 진즉 살걸ㅠ
positive 비교단어 : 신세계 clean_title : 신세계네요
negative 비교단어 : 집 clean_title : 아직은 한번밖에 사용하지 않아 좋은지 잘 모르겠지만 집
positive 비교단어 : 사용 clean_title : 아직은 한번밖에 사용하지 않아 좋은지 잘 모르겠지만 집
positive 비교단어 : 청소 clean_title : 잘 작동되고 좋다네요청소만 잘되면 좋아요
positive 비교단어 : 청소 clean_title : 물걸레는 걍그런데 청소는 잘 하네요
positive 비교단어 : 청소 clean_title : 물걸레 청소기
positive 비교단어 : 청소 clean_title : 물걸레겸용으로 청소도잘되고 좋아요
positive 비교단어 : 생각 clean_title : 배송이 생각보다 빠르고 좋습니다가격대비 좋은 상품
positive 비교단어 : 배송 clean_title : 빠른배송 감사 합니다저렴하게 잘 산것 같아요수
positive 비교단어 : 청소 clean_title : 정말 청소가 편해졌어요
positive 비교단어 : 에브리봇 clean_title : 에브리봇3i
positive 비교단어 : 만족 clean_title : 혼자 돌아다니면서 청소 잘하네요대만족입니다
negative 비교단어 : 집 clean_title : 울집 청소부
positive 비교단어 : 청소 clean_title : 울집 청소부
positive 비교단어 : 만족 clean_title : 아주 만족합니다
positive 비교단어 : 사용 clean_title : 아직 사용전 구동까지 생각보다 시간이 필요하네요
positive 비교단어 : 배송 clean_title : 배송도 빠르고 소음이 조금 크지만 잘 돌아가네요
negative 비교단어 : 제품 clean_title : 제품은 만족하나 판매자 배송 시스템은 문제가 많은 듯
positive 비교단어 : 만족 clean_title : 제품은 만족하나 판매자 배송 시스템은 문제가 많은 듯
positive 비교단어 : 청소 clean_title : 청소는 잘 하는데 너무 시끄러워요ㅠ
positive 비교단어 : 사용 clean_title : 제가 사용하다가 친정부모님 사드렸는데 마음에 들어하시네
positive 비교단어 : 만족 clean_title : 생각보다 크기가 커요 선물했는데 만족해해서 좋아요
생성된 라벨 빈도 분석¶
import matplotlib.pyplot as plt
review_df['group'].value_counts().plot(kind='bar')
plt.title('그룹화한거')
plt.show()
review_df['label'].value_counts().plot(kind='bar')
plt.title('라벨붙인거')
plt.show()
Train set: 무에서 유로 가기 위해 요구되는 필수 데이터
80%는 학습량으로!
우선 앞선 Lending Club, Pitney Bowes 게시글에도 나와있듯 데이터는 Train set 과 Test set으로 쪼개져 모델링에 활용됩니다. 여기서 둘의 비율은 일반적으로 train:test = 80: 20 으로 할당됩니다. 학습량의 비중이 월등히 높죠. 원래 시험을 앞두고 긴 시간동안 여러 교재를 아우르며 준비하지만, 시험은 짧은 시간 내 몇 페이지로 보는 경우가 비일비재하잖아요?
train test¶
from sklearn.model_selection import train_test_split
data = review_df['title']
target = review_df['label']
# train_test_split
x_train, x_test, label_train, label_test = train_test_split(data, target, test_size=0.2, shuffle=True, stratify=target, random_state=34)
x_train
81 정말 청소가 편해졌어요
40 처음에는 한곳만 여러번 닦아 적응하는데 시간이 걸렸는데
88 배송도 빠르고 소음이 조금 크지만 잘 돌아가네요
22 생각보다 가동시간이 좀 짧...은 느낌이지만 만족하고요
68 구매해서 선물해 드렸어요너무 좋아하시네요빅스마일데이...
...
87 깔끔하게
13 별로예요
28 아직 넘어야 할산이 많네요어플 연동 오류가 있고 지도...
14 생각보다 잘쓰고 있습니다. 34평아파트 걸레청소모드로
56 에브리봇 구입
Name: title, Length: 74, dtype: object
불용어 제거¶
stopwords = ['의', '가', '이', '은', '들', '는', '좀', '잘', '걍', '과', '도', '를', '으로', '자', '에', '와', '한', '하다','은','는','이','가','하','아','것','들','의','있','되','수','보','주','등','한']
데이터 전처리¶
import konlpy
from konlpy.tag import Okt
okt = Okt()
clean_X_train = []
for sentence in x_train:
temp_X = []
temp_X = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…\"\“》]', '', sentence)
temp_X = re.sub('[^가-힣ㄱ-ㅎㅏ-ㅣ\\s]','',sentence)
temp_X = okt.morphs(sentence, stem=True) # 토큰화
temp_X = [word for word in temp_X if not word in stopwords] # 불용어 제거
clean_X_train.append(temp_X)
clean_X_test = []
for sentence in x_test:
temp_X = []
temp_X = re.sub('[-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…\"\“》]', '', sentence)
temp_X = re.sub('[^가-힣ㄱ-ㅎㅏ-ㅣ\\s]','',sentence)
temp_X = okt.morphs(sentence, stem=True) # 토큰화
temp_X = [word for word in temp_X if not word in stopwords] # 불용어 제거
clean_X_test.append(temp_X)
len(clean_X_train)
74
단어들을 정수 인코딩¶
from keras.preprocessing.text import Tokenizer # 텍스트를 나누는 단위를 토큰 , 텍스트를 토큰으로 나누는것이 토큰화
max_words = 35000
tokenizer = Tokenizer(num_words = max_words)
tokenizer.fit_on_texts(clean_X_train)
X_train = tokenizer.texts_to_sequences(clean_X_train) # 단어 토큰화
X_test = tokenizer.texts_to_sequences(clean_X_test)
X_train[:3]
[[25, 2, 26],
[27, 75, 76, 17, 77, 41, 78, 79, 10, 42],
[18, 19, 28, 80, 29, 5, 81]]
딥러닝 라이브러리는 데이터를 백터화 하여 표현한다,
가변길이 시퀀스 예측 문제의 경우 각 시퀀스 길이를 같게 해야함으로 데이터를 변형해야 한다,
시퀀스 패딩
import numpy as np
y_train = []
y_test = []
# label_train 은 긍정부정레이블만 가지고 있는 리스트이다.
for i in range(len(label_train)):
if label_train.iloc[i] == 1: # 레이블이 긍정이라면 [0,0,1]
y_train.append([0, 0, 1])
elif label_train.iloc[i] == 0: # 레이블이 중립이라면 [0,1,0]
y_train.append([0, 1, 0])
elif label_train.iloc[i] == -1:# 레이블이 부정이라면 [1,0,0]
y_train.append([1, 0, 0])
for i in range(len(label_test)):
if label_test.iloc[i] == 1:
y_test.append([0, 0, 1])
elif label_test.iloc[i] == 0:
y_test.append([0, 1, 0])
elif label_test.iloc[i] == -1:
y_test.append([1, 0, 0])
y_train = np.array(y_train) # 넘파이형식으로 저장
y_test = np.array(y_test)
pad_sequences¶
- 딥러닝 라이브러리는 데이터를 백터화 하여 표현한다,
- 가변길이 시퀀스 예측 문제의 경우 각 시퀀스 길이를 같게 해야함으로 데이터를 변형해야 한다,
- 가변길이를 채워주는 시퀀스 패딩
먼저 시퀀스 길이를 몇으로 할지 모든 시퀀스의 최대길이를 알아보자¶
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=50)
plt.xlabel('length of Data')
plt.ylabel('number of Data')
plt.show()
제목의 최대 길이 : 13
제목의 평균 길이 : 6.135135135135135
X_test¶
print("제목의 최대 길이 : ", max(len(l) for l in X_test))
print("제목의 평균 길이 : ", sum(map(len, X_test))/ len(X_test))
plt.hist([len(s) for s in X_test], bins=50)
plt.xlabel('length of Data')
plt.ylabel('number of Data')
plt.show()
제목의 최대 길이 : 9
제목의 평균 길이 : 4.052631578947368
시퀀스 길이를 13으로 맞춰준다,¶
from keras.layers import Embedding, Dense, LSTM
from keras.models import Sequential
from keras_preprocessing.sequence import pad_sequences
max_len = 13 # 전체 데이터의 길이를 13로 맞춘다
X_train = pad_sequences(X_train, maxlen=max_len)
X_test = pad_sequences(X_test, maxlen=max_len)
X_train
array([[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 2, 26],
[ 0, 0, 0, 27, 75, 76, 17, 77, 41, 78, 79, 10, 42],
[ 0, 0, 0, 0, 0, 0, 18, 19, 28, 80, 29, 5, 81],
[ 0, 0, 0, 0, 11, 12, 82, 10, 43, 1, 83, 84, 6],
[ 0, 0, 0, 0, 13, 44, 85, 20, 86, 87, 88, 89, 1],
[ 0, 90, 17, 91, 2, 92, 93, 94, 95, 96, 97, 30, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 45],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99],
[ 0, 0, 0, 31, 100, 101, 14, 32, 3, 5, 102, 33, 1],
[ 0, 0, 0, 7, 46, 103, 104, 105, 21, 106, 2, 46, 1],
[ 0, 0, 0, 22, 107, 47, 6, 4, 108, 109, 110, 48, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, 113],
[ 0, 0, 0, 0, 0, 0, 0, 0, 2, 49, 114, 115, 50],
[ 0, 0, 0, 0, 0, 23, 116, 117, 51, 13, 4, 23, 52],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 119, 15],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[ 0, 0, 0, 0, 0, 0, 0, 0, 7, 120, 2, 8, 3],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 34],
[ 0, 0, 0, 0, 9, 16, 53, 14, 20, 3, 54, 55, 122],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
[ 0, 0, 0, 26, 3, 123, 124, 125, 126, 127, 42, 4, 1],
[ 0, 0, 56, 128, 129, 57, 130, 10, 43, 131, 132, 133, 1],
[ 0, 0, 0, 0, 0, 0, 9, 16, 35, 36, 21, 53, 58],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 134, 135, 59],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 136, 14, 137, 34],
[ 0, 0, 0, 2, 10, 60, 138, 4, 139, 140, 141, 142, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 3],
[ 0, 0, 0, 14, 143, 144, 145, 146, 147, 60, 13, 148, 149],
[ 0, 0, 0, 5, 61, 8, 3, 4, 2, 17, 8, 3, 4],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 150, 34],
[ 0, 0, 0, 0, 0, 11, 12, 151, 29, 44, 6, 3, 152],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 153],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 25, 3],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62],
[ 0, 0, 0, 0, 18, 22, 19, 154, 18, 63, 4, 155, 1],
[ 0, 0, 0, 0, 64, 156, 157, 158, 6, 2, 5, 159, 50],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 65, 160],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 65],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 161, 66],
[ 0, 162, 163, 9, 16, 21, 24, 37, 57, 3, 164, 165, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 166, 167, 168],
[ 0, 0, 0, 169, 4, 55, 33, 170, 171, 172, 173, 32, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174, 33, 175],
[ 0, 0, 176, 3, 4, 27, 177, 178, 179, 180, 181, 182, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 6, 183],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 35, 36],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, 185],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 38],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67],
[ 0, 0, 0, 0, 0, 0, 0, 186, 187, 2, 188, 67, 189],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 45],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 190, 2, 5],
[ 0, 0, 18, 11, 12, 19, 3, 4, 23, 52, 3, 191, 1],
[ 0, 27, 192, 39, 68, 5, 38, 193, 41, 24, 39, 68, 1],
[ 0, 0, 194, 47, 24, 2, 5, 8, 7, 195, 5, 8, 196],
[ 0, 0, 0, 0, 0, 0, 0, 0, 37, 197, 69, 70, 198],
[ 0, 0, 23, 51, 71, 4, 28, 29, 199, 200, 2, 8, 1],
[ 0, 0, 0, 0, 7, 17, 13, 201, 13, 21, 72, 202, 1],
[203, 73, 40, 204, 73, 205, 206, 63, 15, 35, 36, 207, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 26],
[ 0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 20, 59, 208],
[ 0, 0, 0, 0, 56, 2, 209, 30, 210, 211, 40, 212, 1],
[ 0, 0, 31, 14, 213, 4, 214, 215, 11, 12, 10, 216, 4],
[ 0, 0, 37, 69, 70, 217, 218, 2, 219, 220, 30, 221, 222],
[ 0, 0, 0, 0, 40, 49, 223, 224, 74, 9, 225, 226, 1],
[ 0, 0, 0, 0, 0, 28, 15, 2, 227, 228, 58, 32, 229],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 231, 38],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 233, 234],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 235, 66],
[ 0, 0, 0, 31, 24, 236, 48, 237, 238, 239, 15, 39, 1],
[ 0, 11, 12, 54, 15, 4, 240, 241, 242, 72, 2, 243, 244],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 71]])
validation_split=0.2 를 지정하기 위한 비율 계산¶
max_words
35000
model = Sequential() #계층을 선형으로 쌓은 것입니다. , 순차적으로 쌓는다
# ADD를 통해 계층 추가
model.add(Embedding(max_words, 100))
# 분류기 추가
model.add(LSTM(128)) # 시퀀스 분류를 위한 LSTM
model.add(Dense(3, activation='softmax')) # 긍정부정 중립으로 3가지를 분류해야함으로 softmax
model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
# 최적화기(optimizer)에서 rmsprop는 문자열식별자에 대한 사용이 가능
# 손실함수(loss)에서 categorical_crossentropy -> 모델에 최적화에 사용하는 문자열식별자에 사용가능
# accuracy 분류 문제에 대해 사용 가능 , 기준은 문자열 식별자로 사용할수 있다.
history = model.fit(X_train, y_train, epochs=10, batch_size=10, validation_split=0.2)
print("\n Accuracy: %.4f" % (model.evaluate(X_train, y_train)[1]))
Epoch 1/10
6/6 [==============================] - 8s 206ms/step - loss: 0.9180 - accuracy: 0.8305 - val_loss: 0.6982 - val_accuracy: 0.8667
Epoch 2/10
6/6 [==============================] - 0s 19ms/step - loss: 0.5107 - accuracy: 0.8814 - val_loss: 0.7517 - val_accuracy: 0.8667
Epoch 3/10
6/6 [==============================] - 0s 20ms/step - loss: 0.4580 - accuracy: 0.8814 - val_loss: 0.5993 - val_accuracy: 0.8667
Epoch 4/10
6/6 [==============================] - 0s 19ms/step - loss: 0.4520 - accuracy: 0.8814 - val_loss: 0.5880 - val_accuracy: 0.8667
Epoch 5/10
6/6 [==============================] - 0s 19ms/step - loss: 0.4171 - accuracy: 0.8814 - val_loss: 0.5309 - val_accuracy: 0.8667
Epoch 6/10
6/6 [==============================] - 0s 18ms/step - loss: 0.3833 - accuracy: 0.8814 - val_loss: 0.4814 - val_accuracy: 0.8667
Epoch 7/10
6/6 [==============================] - 0s 18ms/step - loss: 0.3678 - accuracy: 0.8814 - val_loss: 0.4779 - val_accuracy: 0.8667
Epoch 8/10
6/6 [==============================] - 0s 18ms/step - loss: 0.3120 - accuracy: 0.8814 - val_loss: 0.4194 - val_accuracy: 0.8667
Epoch 9/10
6/6 [==============================] - 0s 18ms/step - loss: 0.2864 - accuracy: 0.8814 - val_loss: 0.4682 - val_accuracy: 0.8667
Epoch 10/10
6/6 [==============================] - 0s 18ms/step - loss: 0.2775 - accuracy: 0.8814 - val_loss: 0.4695 - val_accuracy: 0.8667
3/3 [==============================] - 0s 4ms/step - loss: 0.3127 - accuracy: 0.8784
Accuracy: 0.8784
print('\n 테스트 정확도 : {:.2f}%'.format(model.evaluate(X_test,y_test)[1]*100))
1/1 [==============================] - 0s 25ms/step - loss: 0.8896 - accuracy: 0.8095
테스트 정확도 : 80.95%
워드클라우드¶
f = open("words.txt", "at")
for code in range(len(han_noun_review_1)):
f.writelines(f'{han_noun_review_1[code]} \n')
from wordcloud import WordCloud
from konlpy.tag import Twitter
from collections import Counter
# open으로 txt파일을 열고 read()를 이용하여 읽는다.
text = open('txt.txt').read()
twitter = Twitter()
# twitter함수를 통해 읽어들인 내용의 형태소를 분석한다.
sentences_tag = []
sentences_tag = twitter.pos(text)
noun_adj_list = []
# tag가 명사이거나 형용사인 단어들만 noun_adj_list에 넣어준다.
for word, tag in sentences_tag:
if tag in ['Noun' , 'Adjective']:
noun_adj_list.append(word)
# 가장 많이 나온 단어부터 40개를 저장한다.
counts = Counter(noun_adj_list)
tags = counts.most_common(40)
font='Light.ttf'
# WordCloud를 생성한다.
# 한글을 분석하기위해 font를 한글로 지정해주어야 된다. macOS는 .otf , window는 .ttf 파일의 위치를
# 지정해준다. (ex. '/Font/GodoM.otf')
wc = WordCloud(font_path=font,background_color="white", max_font_size=60)
cloud = wc.generate_from_frequencies(dict(tags))
# 생성된 WordCloud를 test.jpg로 보낸다.
cloud.to_file('test13.jpg')
C:\Users\82105\anaconda3\lib\site-packages\konlpy\tag\_okt.py:17: UserWarning: "Twitter" has changed to "Okt" since KoNLPy v0.4.5.
warn('"Twitter" has changed to "Okt" since KoNLPy v0.4.5.')
<wordcloud.wordcloud.WordCloud at 0x275c2ed3fd0>
프로젝트를 하면서 느낀 점¶
- 조원 정혜진
크롤링을 이해하는데 많은 시간이 필요했지만 이번 팀 프로젝트에서 필요한 데이터를 직접 수집하고
그것을 바탕으로 분석을 진행하니 지금까지 해왔던 프로젝트와는 다른 새로운 경험이였습니다.
분석을 진행하면서 생각처럼 진행되지 않는 부분들도 있었지만 팀원들과 끝까지 마무리하며 많은 것을 배웠습니다.
- 박가온
최종분석 결과를 나타내기 위한 시각화로 긍정/부정으로 분류된 리뷰들에 대한 워드클라우드 제작을 하지 못한 것이 아쉽습니다.
그렇지만 이 분석을 통해 동적 웹크롤링을 사용하면서 웹의 이해도가 높아졌습니다.
또한 형태소 분석기 konlpy를 사용해 보면서 여러 분석기의 비교 또한 할 수 있었고,
텍스트분석을 위한 전처리 과정도 수업보다 실전으로 실습하면서 더욱 이해도가 높아졌습니다.
row data의 질과 양 또한 딥러닝 분석에 중요한 작용을 하는 것 또한 체감할 수 있었던 분석이었습니다.
선생님의 걱정어린 설명과 지도 덕분에 분석을 마칠 수 있었습니다. 감사합니다.
- 심형준
프로젝트를 하면서 팀원들과 같이 협동하는 과정을 거쳐 좋은 경험이 되었습니다.
기존의 크롤링기법이 아닌 새로운 크롤링을 통해 다양한 기법을 배울 수 있었습니다.
배울 때는 이해가 되지 않던 것들이 실제 프로젝트를 통해 하면서 이해할 수 있었습니다.
분석을 하면서 다양한 시도를 통해 딥러닝과 더 가까워질수 있었던 시간이었던 같습니다.
자연어 처리가 어려웠으나 같이 문제를 찾을 수 있는 힘을 기를수 있어서 좋았습니다.