[데이터분석] 2주차 강의노트 (1)
기본 세팅 - colab에는 한글 세팅이 안 되어 있으므로 코드로 한글 세팅을 해줘야 함 import matplotlib as mpl import matplotlib.pyplot as plt %config InlineBackend.figure_format = 'retina' !apt -qq -y in..
yeahhh.tistory.com
[데이터분석] 2주차 강의노트 (2)
* 해당 자료는 스파르타코딩 데이터분석 종합반 수업 강의를 정리한 겁니다. 스파르타에서 제공한 데이터베이스를 사용했습니다. 워드 클라우드 - 입력 : from wordcloud import WordCloud - 워드 클라우
yeahhh.tistory.com
* 해당 자료는 스파르타코딩 데이터분석 종합반 수업 강의를 정리한 겁니다. 스파르타에서 제공한 데이터베이스를 사용했습니다.
머신러닝 원리
선형회귀
- "공부시간 * a + b = 성적" 형태의 방정식을 가정, 수많은 공부시간과 성적 데이터를 토대로 a,b 값을 추정하는 걸 '학습'이라고 함
- 인공지능 모델을 '구축한다, 학습시킨다' = 대량의 데이터를 토대로 기계한테 a,b 값을 알아내라고 시키는 것
- but, 모든 성적 데이터에 정확하게 들어맞는 직선을 그리는 건 사실상 불가함. 오차를 최대한 줄이는 게 좋은 예측 모델!
- a,b 값을 무작위로 설정했다가 빠르속도로 연산을 해 오차를 비교하면서 최적의 a,b 값을 찾는 걸 '최적화'라고 함
머신러닝
- 벡터화(vectorization) : 기계가 이해할 수 있게 단어를 숫자로 치환하는 것. 단어들을 특정한 관계에 따라 3차원 공간에 흩뿌려놓는 것. 특정 단어가 공간에서 어디에 위치하는지를 '벡터'라는 숫자가 알려준다. 이를 토대로 숫자만으로 해당 단어의 의미(상대적 관계)를 이해할 수 있게 됨.
- 모델(model) : 학습시킨 결과
텍스트 분류
- 이진 분류(Binary Classification) : 클래스가 2개인 경우
ex) 스펨 메일 자동 분류
- 다중 클래스 분류(Multiclass Classification) : 클래스가 3개 이상인 경우
ex) 사용자 리뷰로부터 긍/부정을 판단하는 감성 분류, 뉴스 카테고리 분류
영화 줄거리를 이용해 장르 분류하기
파일 불러오기
- 훈련 데이터 : 기계에 일정한 내용을 학습시키기 위한 데이터. 각 줄거리의 장르가 어떤 건지 기계에게 알려주기 위한 데이터.
- 테스트 데이터 : 줄거리를 주고 장르를 예측해보라고 주기 위한 데이터. 정확도와 관련. 학습하지 않음
- 텍스트 파일 자체가 ::: 기준으로 번호 / 영화 제목 / 장르 / 줄거리가 구분돼 있으므로 이를 이용해 파일을 불러온다.
import pandas as pd
# sep=":::" 을 이용해서, 열 구분을 ::: 로 해서 불러오라고 말해준다.
train = pd.read_table('train_data.txt', sep=":::", names=['Index', 'Title', 'Genre', 'Content'])
test = pd.read_table('test_data_solution.txt', sep=":::", names=['Index', 'Title', 'Genre', 'Content'])
- train.head(3)으로 세 번째까지 불러와 확인!
- 각 파일의 정보를 보려면 train.info() / test.info()
- 장르 하나하나(분류값)를 class라고도 한다.
전처리
- 줄거리(독립 변수=x) 이용해 장르(종속 변수=y) 예측
train['Genre'].unique() # 중복 제외했을 때 해당 열에 존재하는 모든 값 확인
y_train = train['Genre'] # 훈련 데이터의 장르 부분을 y_train 이라는 이름으로 저장
x_train = train['Content'] # 훈련 데이터의 줄거리 부분을 x_train 이라는 이름으로 저장
y_test = test['Genre']
x_test = test['Content']
- 기계는 바로 단어를 이해할 수 없기 때문에 줄거리와 장르 모두 숫자나 벡터 형태로 변형해야 함.
- 이 경우 장르 자체의 의미나 장르 간의 관계가 중요한 게 아니라서 replace 함수 사용해 숫자로 대치시켜도 괜찮
mapping = {' drama ':1, ' thriller ':2, ' adult ':3, ' documentary ':4, ' comedy ':5,
' crime ':6, ' reality-tv ':7, ' horror ':8, ' sport ':9, ' animation ':10,
' action ':11, ' fantasy ':12, ' short ':13, ' sci-fi ':14, ' music ':15,
' adventure ':16, ' talk-show ':17, ' western ':18, ' family ':19, ' mystery ':20,
' history ':21, ' news ':22, ' biography ':23, ' romance ':24, ' game-show ':25,
' musical ':26, ' war ':27}
y_train = y_train.replace(mapping)
y_test = y_test.replace(mapping)
벡터화
- 벡터란 서로 유사도를 구하거나 연산도 가능한 숫자들의 나열
1) DTM(Document-Term Matrix)
- 문서 단어 행렬 : 문서를 행으로 하고 각 문서에서 등장하는 각 단어들의 등장 횟수를 행렬로 표현한 것
- 문서들의 유사도를 구할 수 있음
- 한계 : 영어로 dtm을 만들면 정관사 the는 어느 문서든 자주 등장함. the의 빈도수가 높다고 해서 유사하다고 할 수 없음. 이런 중요하지 않은 단어들 혹은 모든 문서에서 자주 등장하는 단어들에 가중치를 낮게 주는 방식이 TF_IDF
2) TF-IDF(Term Frequency-Inverse Document Frequency)
- 우선 DTM을 만들고 단어의 빈도와 문서의 빈도 역수를 사용해 DTM 내의 각 단어들마다 중요한 정도를 가중치로 주는 방법
- 문서의 유사도를 구하는 작업, 검색 시스템에서 검색 결과의 중요도를 정하는 작업, 문서 내에서 특정 단어의 중요도를 구하는 작업 등에 쓰임
# d = 문서 / t = 단어 / n = 문서의 총 개수
- tf(d,t) : 특정 문서 d에서의 특정 단어 t의 등장 횟수
- df(t) : 특정 단어 t가 등장한 문서의 수
- idf(d,t) : df(t)에 반비례하는 수
- 자연 로그 : 로그의 밑을 자연 상수 e(e=2.718281...)를 사용하는 로그
- IDF 계산을 위해 사용하는 로그의 밑은 TF-IDF를 사용하는 사용자가 임의로 정할 수 있는데, 여기서 로그는 기존의 값에 곱해 값의 크기를 조절하는 상수 역할을 함
- 각종 프로그래밍 언어니 프로그램 패키지로 지원하는 TF-IDF 로그는 대부분 자연 로그를 사용함. 자연 로그는 In으로 표현!
DTM, TF-IDF 만들기
- 사이킷 런(scikit-learn) : 머신러닝을 위한 각종 기능을 제공하는 모듈
- 머신러닝의 큰 분류 : 회귀, 분류
- DTM을 만들 때는 사이킷런의 CountVectorizer 사용, TF-IDF 행렬은 TfidfTransformer 사용
- 둘 다 객체를 만든 다음에는 fit_transform() 이라는 함수를 사용해 실행함
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
corpus = [
'you know I want your love',
'I like you',
'what should I do ',
]
vector = CountVectorizer() # DTM 벡터화를 위한 객체 생성
x_train_dtm = vector.fit_transform(corpus) # 해당 단어들을 벡터화 진행
print(x_train_dtm.toarray()) # 벡터가 어떻게 생겼는지 확인
-> DTM 완성!
[[0 1 0 1 0 1 0 1 1]
[0 0 1 0 0 0 0 1 0]
[1 0 0 0 1 0 1 0 0]]
- 여기서 TfidfTransformer()을 사용하면 TF-IDF 행렬을 추가적으로 학습할 수 있음
tfidf_transformer = TfidfTransformer() # tfidf 벡터화를 위한 객체 생성
tfidfv = tfidf_transformer.fit_transform(x_train_dtm) # x_train_dtm에 대해서 벡터화 진행
print(tfidfv.toarray()) # 벡터가 어떻게 생겼는지 확인
-> TF-IDF 행렬 완성
[[0. 0.46735098 0. 0.46735098 0. 0.46735098
0. 0.35543247 0.46735098]
[0. 0. 0.79596054 0. 0. 0.
0. 0.60534851 0. ]
[0.57735027 0. 0. 0. 0.57735027 0.
0.57735027 0. 0. ]]
# 벡터화
머신러닝(예측 모델 구축)
기본 작업
- 훈련용 줄거리 데이터(x_train)에 대해 DTM 생성
- DTM으로부터 TF-IDF 행렬 만들기
- 테스트용 줄거리 데이터(x-test)에 대해서도 DTM과 TF-IDF 생성
나이브 베이즈 분류기 설치
- P(A) = A가 일어날 확률
- P(B) = B가 일어날 확률
- P(B|A) = A가 일어나고 B가 일어날 확률 = A -> B
- P(A|B) = B가 일어나고 A가 일어날 확률 = B -> A
- 이때 P(B|A)를 쉽게 구할 수 있는 상황이라면 아래 '베이즈 정리'를 통해 P(A|B)도 쉽게 구할 수 있다.
- '|'를 조건부 확률이라고 함
- 나이브 베이즈 분류기는 사이킷런의 MultinomialNB() 사용
- 사이킷런이 제공하는 머신러닝 모델들은 공통적으로 fit()라는 함수를 제공
- 훈련 데이터와 해당 훈련 데이터에 대한 레이블을 인자로 사용하면 모델이 학습하게 됨
mod = MultinomialNB()
mod.fit(tfidfv, y_train)
- 테스트 데이터에 대해서 정확도를 측정하기 위해서는 훈련 데이터와 동일한 전처리를 거쳐야 함. 테스트 데이터에 대해서도 TF-IDF 행렬로 변환해주고 predict()라는 함수로 예측값을 얻어 정확도를 측정!
predicted = mod.predict(tfidfv_test) #테스트 데이터에 대한 예측
print("정확도:", accuracy_score(y_test, predicted)) #예측값과 실제값 비교
로지스틱 회귀(Logistic Regression)
- 소프트맥스(softmax) 함수를 사용한 다중 클래스 분류 알고리즘을 지원
- 클래스가 N개일 때, 각 클래스가 정답일 확률을 표현하도록 하는 함수
lr = LogisticRegression(C=10000, penalty='l2') #c와 penalty의 의미는 4주차에서 배울 예정입니다!
lr.fit(tfidfv, y_train)
predicted = lr.predict(tfidfv_test) #테스트 데이터에 대한 예측
print("정확도:", accuracy_score(y_test, predicted)) #예측값과 실제값 비교
ERROR
- 왤까..? 모듈 문제 같은데, 위로 거슬러 올라가면서 보니까 y_test[3]을 했는데 1이 아니라 drama가 나옴. 전처리가 안 됨
- read가 안 먹히길래 beautifulsoup4 설치하고 requests 임포트 해보니 1로 뜸
- 그러고 다시 차례차례 실행해보니 정상적으로 작동함! 인내심을 가지고 기다리자..
- 알고 보니 l2를 12라고 적었다...ㅎㅎ
선형 Support Vector Machine
- 결정 경계(분류를 위한 기준 선)을 정의하는 모델
- 분류하지 않은 새로운 점이 나타나면 경계의 어느 쪽에 속하는지 확인해서 분류 과제를 수행할 수 있게 됨
lsvc = LinearSVC(C=1000, penalty='l2', max_iter=500)
lsvc.fit(tfidfv, y_train)
predicted = lsvc.predict(tfidfv_test) #테스트 데이터에 대한 예측
print("정확도:", accuracy_score(y_test, predicted)) #예측값과 실제값 비교
** 더 높은 정확도를 얻기 위해서는 자연어처리(NLP)를 더 깊게 공부해야 함. 벡터 임베딩 여러 기법을 시도하고 딥러닝까지 적용해보면 훨씬 더 높은 정확도를 만들어낼 수 있음
장르 구분 최종
x_test_dtm = dtmvector.transform(['줄거리~~']) #테스트 데이터를 DTM으로 변환
tfidfv_test = tfidf_transformer.transform(x_test_dtm) #DTM을 TF-IDF 행렬로 변환
predicted = lr.predict(tfidfv_test) #테스트 데이터에 대한 예측
print(predicted)
-> 장르에 해당하는 숫자가 나옴. 각 숫자가 의미하는 바를 대조해보며 확인해보기
불용어 제거 활용
- 사이킷런에서 자동적으로 불용어를 제거해줌
- 따라서 countVectorizer 쓸 때 stop_words를 써주면 된다
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
# dtm, tfidf 벡터 생성을 위한 객체 생성
dtmvector = CountVectorizer(stop_words="english") # 영어 스탑워드를 제거해달라는 뜻!
tfidf_transformer = TfidfTransformer()
# x_train에 대해서 dtm, tfidf 벡터 생성
x_train_dtm = dtmvector.fit_transform(x_train)
tfidfv = tfidf_transformer.fit_transform(x_train_dtm)
- 다음 머신러닝 학습 시키기
# 나이브 베이즈 분류기로 학습 진행
mod = MultinomialNB()
mod.fit(tfidfv, y_train)
# x_test에 대해서 dtm, tfidf 벡터 생성
x_test_dtm = dtmvector.transform(x_test) #테스트 데이터를 DTM으로 변환
tfidfv_test = tfidf_transformer.transform(x_test_dtm) #DTM을 TF-IDF 행렬로 변환
predicted = mod.predict(tfidfv_test) #테스트 데이터에 대한 예측
print("정확도:", accuracy_score(y_test, predicted)) #예측값과 실제값 비교
- 정확도가 44% 나옴. 불용어 제거하기 전과 큰 차이 없음. 나이브 베이즈 분류기는 원론적인 모델이라 불용어 제거가 영향을 미치지 않은 것!
과제
1) 네이버 쇼핑 리뷰 데이터를 이용해 긍/부정 분류 모델 만들기
- 한글 사용을 위한 세팅
import matplotlib as mpl
import matplotlib.pyplot as plt
%config InlineBackend.figure_format = 'retina'
!apt -qq -y install fonts-nanum
import matplotlib.font_manager as fm
fontpath = '/usr/share/fonts/truetype/nanum/NanumBarunGothic.ttf'
font = fm.FontProperties(fname=fontpath, size=9)
plt.rc('font', family='NanumBarunGothic')
mpl.font_manager._rebuild()
# 해당 코드 실행 후 반드시 상단의 런타임 > 런타임 다시 시작을 눌러주세요.
- 데이터 로드해서 df로 만들기
import pandas as pd
import numpy as np
#판다스를 사용하여 네이버 쇼핑 리뷰 데이터가 존재하는 URL을 입력하고 다운로드
df = pd.read_table('https://raw.githubusercontent.com/bab2min/corpus/master/sentiment/naver_shopping.txt', names=['ratings', 'reviews'])
df
- 링크 바로 달아도 되고, 파일 형태로 넣어도 괜찮다. 파일로 넣으려면 아래처럼 하면 된다.
df = pd.read_table('naver_shopping.txt', names=['ratings', 'reviews'])
⭐️ 'A를 이용해 -> B 예측' : A는 벡터화를 진행하고, B는 단순한 번호로 치환하는 인덱스화를 진행 ⭐️
- 이 경우 리뷰가 A, 긍/부정은 B
- 리뷰는 DTM, TF-IDF로 만드는 벡터화, 긍/부정 여부는 이분법이니까 1과 0으로 단순 치환
1-1. 긍/부정(B) 인덱스화
- 평점(ratings)이 3보다 크면 긍정(=1), 3과 같거나 작으면 부정(=0) 리뷰로 지정
df['label'] = np.select([df.ratings > 3], [1], default=0)
1-2. 훈련 데이터와 테스트 데이터 구분
- train_test_split 함수 활용
- 왼쪽에는 변수를 적어주고
- x_train (훈련에 사용할 리뷰 데이터) -> A
- x_test (테스트에 사용할 리뷰 데이터) -> A
- y_train (훈련에 사용할 긍정/부정 여부) -> B
- y_test (테스트에 사용할 긍정/부정 여부) -> B
- 오른쪽에는 train_test_split(예측에 사용할 데이터: 리뷰(A), 예측할 데이터: 긍/부정 여부(B), test_size=원하는 테스트 데이터 셋의 크기)
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df['reviews'], df['label'], test_size = 0.3)
- test_size 크기 0.3은 전체 데이터 중 70%는 훈련 데이터에, 30%는 테스트 데이터에 담는다는 말
1-3. 훈련용 데이터 = x_train = 리뷰 = A 벡터화
- 모듈 설치
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
- DTM 만들기
dtmvector = CountVectorizer() # DTM 벡터화를 위한 객체 생성
x_train_dtm = dtmvector.fit_transform(x_train)
- 벡터가 어떻게 생겼나 확인하기
print(x_train_dtm.toarray())
- DTM을 이용해 TF-IDF 벡터 만들기
tfidf_transformer = TfidfTransformer() # tfidf 벡터화를 위한 객체 생성
tfidfv = tfidf_transformer.fit_transform(x_train_dtm) # x_train_dtm에 대해 벡터화 진행
1-4. 예측 모델 구축
- 예측 모델에 필요한 모듈 임포트
from sklearn.naive_bayes import MultinomialNB # 다항분포 나이브 베이즈 모델
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score # 정확도 계산
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
- 모델 학습
# 로지스틱 회귀
lr = LogisticRegression(C=10000, penalty='l2')
lr.fit(tfidfv, y_train)
# 나이브 베이즈 분류
mod = MultinomialNB()
mod.fit(tfidfv, y_train)
# 선형 서포트 벡터 머신
lsvc = LinearSVC(C=1000, penalty='l2', max_iter=500)
lsvc.fit(tfidfv, y_train)
- 테스트 데이터 활용해 정확도 확인
x_test_dtm = dtmvector.transform(x_test) #테스트 데이터를 DTM으로 변환
tfidfv_test = tfidf_transformer.transform(x_test_dtm) #DTM을 TF-IDF 행렬로 변환
predicted = lr.predict(tfidfv_test) #테스트 데이터에 대한 예측
print("정확도:", accuracy_score(y_test, predicted)) #예측값과 실제값 비교
- 로지스 회귀, 나이브 베이즈, 선형 서포트 벡터 머신 각각 정확도 구하기
- 로지스틱 : 84%
- 나이브 베이즈 : 88%
- 선형 서포트 벡터 머신 : 80%
-> 나이브 베이즈가 가장 정확도가 높음!
2) 네이버 쇼핑 리뷰 데이터를 이용해 각각 긍정/부정 리뷰의 워드 클라우드를 만들기
print(df['reviews'].nunique())
df.drop_duplicates(subset=['reviews'], inplace=True)
!pip install konlpy
from konlpy.tag import Okt
tokenizer = Okt()
df['tokenized'] = df['reviews'].apply(tokenizer.nouns)
df['label'] = np.select([df.ratings > 3], [1], default=0)
# hstack은 가로 결합
positive_reviews = np.hstack(df[df['label']==1]['tokenized'].values)
negative_reviews = np.hstack(df[df['label']==0]['tokenized'].values)
# 가장 많이 등장하는 단어와 그 개수를 구할 때는 collectios 모듈의 Counter 클래스
from collections import Counter
positive_reviews_word_count = Counter(positive_reviews)
negative_reviews_word_count = Counter(negative_reviews)
import numpy as np
from wordcloud import WordCloud
plt.figure(figsize = (15,15))
temp_data1 = ' '.join(positive_reviews)
temp_data2 = ' '.join(negative_reviews)
wc = WordCloud(max_words = 2000 , width = 1600 , height = 800, font_path = fontpath).generate(temp_data)
plt.imshow(wc, interpolation = 'bilinear')
회고
Keep | 안 되면 처음부터 다시해보기. 될 때까지 이것저것 만져본다. 어쩌다 얻어 걸리면서 다음에 같은 실수 안 하면 됨! |
Problem | 인내심을 가지고,, 처음부터 차례차례 모듈, 라이브러리 설치 까먹지 말자. 안 될 때 처음부터라는건 모듈 설치부터.. l2를 12라고 적은 letter 실수 은근 많이 나온다. 꼼꼼하게 살펴 보기!! |
Try | 메커니즘을 익혀야 안 막힌다. 한 군데가 막히면 뒤에가 쭉 다 안 된다. 원리를 이해해보자! 아직은 용어가 헷갈림. 모듈, 라이브러리, 패키지, 각 함수의 기능 하나씩 구분할 수 있게 용어를 익히자 |
'Python' 카테고리의 다른 글
[데이터 분석] 3주차 강의노트 (1) 데이터프레임 (0) | 2022.07.09 |
---|---|
[데이터분석] 2주차 강의노트 (2) (0) | 2022.07.07 |
[데이터분석] 2주차 강의노트 (1) (0) | 2022.06.23 |
[데이터분석] 1주차 강의노트 (0) | 2022.06.23 |