from bs4 import BeautifulSoup #HTML 파싱
import urllib #검색어 인코딩
import time #슬립 사용
import requests #HTML 가져오기
from tqdm import tqdm_notebook
#크롤링 결과 저장할 변수
present_candi_text = []
html = 'https://search.naver.com/search.naver?where=kin&sm=tab_jum&ie=utf8&query=' + urllib.parse.quote('고양이') + '&start='
for n in range(1, 1000, 10):
response = requests.get(html + str(n))
soup = BeautifulSoup(response.text, "html.parser")
tmp = soup.select('div.question_area > div.question_group > a') #선택자
for line in tmp:
#print(line.text)
present_candi_text.append(line.getText())
time.sleep(0.5)
print(present_candi_text)
...
# 강아지 질문 데이터
2. 형태소 분석
#고양이
import nltk
from konlpy.tag import Okt
okt = Okt()
present_text = ''
for each_line in present_candi_text[:10000]:
present_text = present_text + each_line + '\n'
tokens_ko = okt.morphs(present_text)
print(tokens_ko)
#강아지
3. 단어별 등장횟수 확인
ko = nltk.Text(tokens_ko, name='고양이')
print(ko.vocab().most_common(100))
import pytagcloud
data = ko.vocab().most_common(101)
taglist = pytagcloud.make_tags(data, maxsize=200)
for i in taglist:
if i["tag"] == '고양이':
taglist.remove(i)
#태그 클라우드 생성
pytagcloud.create_tag_image(taglist, 'wordcloud.png', size=(2000, 2000), fontname='Korean', rectangular=False)
import matplotlib.pyplot
import matplotlib.image
img = matplotlib.image.imread('wordcloud.png')
imgplot = matplotlib.pyplot.imshow(img)
matplotlib.pyplot.show()
# 강아지
7. 단어 추천
!pip install gensim
from gensim.models import word2vec
okt = Okt()
results = []
lines = present_candi_text
for line in lines:
malist = okt.pos(line, norm=True, stem=True)
r = []
#조사, 어미, 구두점을 제거하고 r에 추가
for word in malist:
if not word[1] in ["Josa", "Eomi", "Punctuation"]: #품사가 아닌 경우
r.append(word[0])
#앞뒤에 있는 좌우 공백 제거
r1 = (" ".join(r)).strip()
results.append(r1)
print(r1)
print(results)
['애기 고양이 상 좋다 건가 고양이 상 귀엽다 고양이 상 애기 고양이 상 들다 보다 그냥 고양이 상도 아니다 애기 고양 이상은 뭔 뜻', '고양이 간식 급여 이 거 고양이 주다 되다 이마트 구매 하다 고양이 강아지 코너 사이 있다 고양이 끄다 알 구 먀 하다 아니다 있다 생각 들어서다', '강아지 고양이 사료 강아지 고양이 같이 키우다 자율 급식 고양이 강아지 사료 각 각 퍼 놓다 근데 강아지 고양이 사료 먹다 고양이 강아지 사료 먹다 괜찮다', '고양이 방 집착 하다 많다 고양이 해 되다 방뮨 닫다 지내다 저희 고양이 친척 안 좋아하다 근데 친척 이번 설 고양이 되게 쌔 혼내다 많다 고양이 해 되다 방문 닫다 사용 싶다 고양이 화장실 밥 먹다 때 문 열다 닫다 하다 돼다 걱정 많다 고양이', '고양이 꿈 해몽 부탁드리다 제 방금 일어나다 고양이 꿈 생생하다 궁금하다 일단 일어나다 제 집 키우다 고양이 마당 앉다 집 들어오다 문 열다 모르다 고양이 들 ....
['강아지 적응 ㅠㅠ 무엇 제일 걸리다 강아지 네 물론 제 이직 하다 되어다 집 어머니 케어 해 주다 하지만 강아지 워낙 저 좋아하다 저 강아지 너무 좋아하다 두다 떠나다 마음 안좋다 강아지 제 없다 금방 적응 하다 요 물론 집 강아지 엄마 간식 맛있다', '강아지 꿈 해석 해주다 강아지 들 엘베 타다 강아지 못 타다 줄이다 올라가다 강아지 매달리다 사고 있다 그걸 꾸다 사람 들 이 줄 빨리 끊다 하니 끊다 안되다 하다 강아지 높이 올라가다 수록 더 아프다 20 층 멈추다 의미 있다', '강아지 털 알르레기 없애다 법 제 강아지 털 알르레기 있다 근데 강아지 너무 키우다 강아지 너무 좋아하다 ㅠㅠ 어리다 때 강아지 너무 좋아하다 강아지 훈련사 꾸다 ㅠㅠ 진짜 강아지 넘다 키우다 싶다 ㅠㅠ 엄마 키우다 되다 강아지 알르레기 땜 ㅠㅠㅠ 내공 40', '강아지 너무 키우다 싶다 저 5-6년 정도 강아지 키우다 싶다 마음 크다 엄마 강아지 침 알레르기 좀 있다 안되다 거 같다 저 엄마 신경 쓰다 진짜 강아지 자다 키우다 자신 있다 ㅜㅠ 강아지 키우다 정말로 힘들다 그렇다 거 .....
# Word2Vec 적용
data_file = 'cat.data'
with open(data_file, 'w', encoding='utf-8') as fp:
fp.write("\n".join(results))
data = word2vec.LineSentence(data_file)
model = word2vec.Word2Vec(data, vector_size=200, window=10, hs=1,min_count=2, sg=1)
model.save('cat.model')
model = word2vec.Word2Vec.load("cat.model")
model.wv.most_similar(positive=['고양이'])
#정규식 모듈
import re
# <br> html 태그 -> 공백으로 변환
review_df['review'] = review_df['review'].str.replace('<br />',' ')
# 파이썬의 정규 표현식 모듈인 re를 이용하여 영어 문자열이 아닌 문자는 모두 공백으로 변환
review_df['review'] = review_df['review'].apply( lambda x : re.sub("[^a-zA-Z]", " ", x) )
print(review_df['review'].head())
0 With all this stuff going down at the moment ... 1 The Classic War of the Worlds by Timothy ... 2 The film starts with a manager Nicholas Bell... 3 It must be assumed that those who praised thi... 4 Superbly trashy and wondrously unpretentious ... Name: review, dtype: object
a-zA-Z가 아닌 글자를 공백으로 치환
한글을 제외한 글자 제거: [^가-힣]
3. 훈련/테스트 데이터 분리
- 지도학습 기반의 분류이므로 훈련 데이터를 이용해서 훈련하고, 테스트 데이터로 확인하는 것을 권장
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, roc_auc_score
ngram을 설정하면 하나의 단어를 하나로 인식하지 않고, n개의 단어까지 하나의 단어로 인지한다. 영어는 2나 3을 설정: 사람 이름 등 (I am a boy -> I am, am a, a boy) 정확하게 일치하는 것이라면 예측도 가능하다. (I am 다음은 a/an 이구나) 그래서 생성형 AI는 ngram을 설정하는게 좋다.
score review y 0 5 친절하시고 깔끔하고 좋았습니다 1 1 5 조용하고 고기도 굿 1 2 4 갈비탕과 냉면, 육회비빔밥이 맛있습니다. 1 3 4 대체적으로 만족하나\n와인의 구성이 살짝 아쉬움 1 4 5 고기도 맛있고 서비스는 더 최고입니다~ 1
2. 데이터 전처리
한글 추출: (가-힣)
모음과 자음만으로 구성된 텍스트도 추출하려면 (ㄱ-ㅣ, 가-힣)
#한글을 제외한 글자 전부 제거
import re
# 텍스트 정제 함수 : 한글 이외의 문자는 전부 제거
def text_cleaning(text):
# 한글의 정규표현식으로 한글만 추출합니다.
hangul = re.compile('[^ ㄱ-ㅣ가-힣]+')
result = hangul.sub('', text)
return result
df['ko_text'] = df['review'].apply(lambda x: text_cleaning(x))
del df['review']
df.head()
데이터 용량을 줄이기 위해 바로바로 del
3. 형태소분석
from konlpy.tag import Okt
# konlpy라이브러리로 텍스트 데이터에서 형태소를 '단어/품사'로 추출
def get_pos(x):
tagger = Okt()
pos = tagger.pos(x) # PartOfSpeech
pos = ['{}/{}'.format(word,tag) for word, tag in pos]
return pos
# 형태소 추출 동작을 테스트합니다.
result = get_pos(df['ko_text'][0])
print(result)
from sklearn.feature_extraction.text import CountVectorizer
# 형태소를 벡터 형태의 학습 데이터셋(X 데이터)으로 변환
index_vectorizer = CountVectorizer(tokenizer = lambda x: get_pos(x))
X = index_vectorizer.fit_transform(df['ko_text'].tolist())
X.shape
from sklearn.feature_extraction.text import TfidfTransformer
# TF-IDF 방법으로, 형태소를 벡터 형태의 학습 데이터셋(X 데이터)으로 변환합니다.
tfidf_vectorizer = TfidfTransformer()
X = tfidf_vectorizer.fit_transform(X)
print(X.shape)
print(X[0])
분류 모델은 데이터를 많이 모으는게 중요하고 특히 비율이 안맞는 경우 비율이 낮은 쪽의 데이터를 많이 모아야 한다.
7. 피처의 중요도 확인
트리 게열의 모델들은 feature_importance_라는 속성에 각 피처의 중요도를 가지고 잇음
트리 계열이 아닌 모델들은 회귀 계수를 가지고 판단
모델의 회귀계수는 coef_라는 속성에 저장됨
print(lr.coef_[0])
[ 0. 0. 0.18171898 ... 0. -0.12358162 0. ]
각 피처에 대한 회귀 계수 = 단어
#회귀 계수 내림차순 정렬
coef_pos_index = sorted(((value, index) for index, value in enumerate(lr.coef_[0])),
reverse = True)
#단어와 매핑
invert_index_vectorizer = {v:k for k, v in index_vectorizer.vocabulary_.items()}
print(invert_index_vectorizer)
- 차원을 축소하면 일부 정보가 유실되기 때문에 훈련 속도가 빨라지기는 하지만 성능은 나빠질 가능성이 높다.
2. 투영 (Projection)
물체의 그림자를 어떤 물체 위에 비추는 일 또는 그 비친 그림자를 의미
어떤 물체든 그림자는 2차원으로 표현될 수 있다는 원리
투영을 이용해서 구현된 알고리즘이 PCA, LDA 등
1) 특징
MNIST 같은 이미지에는 잘 적용이 되지만 스위스 롤 같은 데이터에는 적용할 수 없다.
특성들의 값이 거의 비슷하고 다른 특성의 값이 많이 다르면 적용하기가 좋은데 그렇지 않은 경우는 적용하기 어렵다.
높이가 별 차이 안나니까 눌러서 높이를 없애버려도 차이가 없으니 두개만으로도 설명이 된다.
MNIST 이미지 - 바깥 쪽은 거의 다 똑같다. (테두리는 항상 흰색)
스위스롤: 누르면 안됨. 부딪혀버린다.
실제 값 원했던 것
3. Manifold 방식
차원을 축소하는데 특정 차원을 없애지 않고 새로운 차원을 만들어 내는 방식
알고리즘: LLE, t-SNE, Isomap, Autoencoer
직선으로 구분하는 건 불가해졌지만, 곡선으로 나누면 됨 => PCA
4. PCA (Principal Component Analysis - 주성분 분석)
여러 변수 간에 존재하는 상관관계를 이용하고 이를 대표하는 주성분을 추출해서 차원을 축소하는 기법
차원을 축소할 때 데이터의 정보는 최대한 유지하면서 고차원에서 저차원으로 축소를 하는 방식
데이터의 다른 부분을 보존하는 방식으로 분산을 최대한 유지하는 축을 생성
이 방식은 데이터의 분포가 표준정규분포가 아닌 경우에는 적용이 어려움
- 동작 원리
학습 데이터 셋에서 분산(변동성)이 최대인 축 (PC1)을 찾음
PC1과 수직이면서 분산이 최대인 축 (PC2)를 찾음
첫 번째 축과 두 번째 축에 직교하고 분산을 최대한 보존하는 세 번째 축을 찾음
1~3의 방법으로 차원 수만큼 찾음
데이터 구분: 차이 구분되는 성질을 가장 적게 잃어야하기 때문에, 가장 많이 차이가 나는 것을 찾아야 한다. 애-어른을 구분하는 가장 큰 차이는 나이, 그게 첫번째 주성분이 된다. 그러면 반대편은 설명하지 못하기 때문에, 두번째 주성분은 첫번째 주성분과 직교한다. 2-3가지 정도로 판단하자!
#원본 데이터와 복원된 데이터를 비교
print(np.allclose(X3D_inv, X))
#차이의 평균
print(np.mean(np.sum(np.square(X3D_inv - X), axis=1)))
False 0.07836731698475538
원본과 복원결과는 차이가 있음, 일부 데이터는 소실됨
4-2) 적절한 차원 수 선택
시각화를 위해서 주성분 분석을 하는 경우 2~3개로 수행
시각화 이외의 목적인 경우는 일반적으로 주성분의 개수를 지정하기 보다 분산의 비율을 이용해서 개수 설정 n_components에 성분의 개수가 아닌 0.0 ~ 1.0 사이의 비율을 설정하면 비율이 되는 주성분의 개수로 분석 수행
시각화를 수행해서 엘보가 만들어지는 지점이 있다면 엘보를 이용해서 주성분 개수 설정
#데이터 가져오기
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
#이미지 데이터 전부 가져오기
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
mnist.target = mnist.target.astype(np.uint8)
X = mnist.data
y = mnist.target
X_train, X_test, y_train, y_test = train_test_split(X, y)
#X_train 은 784개의 피처를 가진 데이터
print(X_train.shape)
(52500, 784)
#차원수를 지정해서 PCA 수행: 154개의 피처로 주성분분석 수행
pca = PCA(n_components=154)
pca.fit_transform(X_train)
#분산의 비율 확인
print(pca.explained_variance_ratio_)
...
#분산 비율의 합계 - 95%
print(pca.explained_variance_ratio_.sum())
배치 처리가 미니 배치 방식보다 좋은 점은 자원을 효율적으로 사용할 수 있고 작업 시간은 단축 시킬 수 있다는 것
미니 배치 방식이 좋은 점은 훈련을 실시간으로 수행하기 때문에 훈련이 끝나는 시점만 따져보면 더 빠르다.
* 파이썬에서 메모리 정리 방법: (1) 가리키는 데이터가 없을 때 자동으로 정리. (2) 기존 변수가 라키는 데이터가 없도록 del 변수명 을 사용
4-5) 3가지 PCA 훈련 시간 비교
# 일반 PCA
import time
start = time.time()
pca = PCA(n_components=154, svd_solver="full")
pca.fit_transform(X_train)
end = time.time()
print("PCA 수행 시간:", (end-start))
print(pca.explained_variance_ratio_.sum())
PCA 수행 시간: 4.382986545562744 0.9504506731634907
# 랜덤 PCA
import time
start = time.time()
pca = PCA(n_components=154, svd_solver="randomized")
pca.fit_transform(X_train)
end = time.time()
print("랜덤 PCA 수행 시간:", (end-start))
print(pca.explained_variance_ratio_.sum())
랜덤 PCA 수행 시간: 4.955554962158203 0.9500787743598854
svd_solver="randomized"를 생략해도 Random PCA - 피처의 500개가 넘으면 자동
# 점진적 PCA
from sklearn.decomposition import IncrementalPCA
#배치 사이즈: 데이터를 몇 개 씩 훈련
n_batches = 100
start = time.time()
inc_pca = IncrementalPCA(n_components=154)
for X_batch in np.array_split(X_train, n_batches):
inc_pca.partial_fit(X_batch)
X_reduced = inc_pca.transform(X_train)
end = time.time()
print("점진적 PCA 수행 시간:", (end-start))
print(inc_pca.explained_variance_ratio_.sum())
점진적 PCA 수행 시간: 29.47389006614685 0.9497567304638014
4-6) Kernel PCA
- 커널 트릭 PCA: 비선형 데이터에 다항을 추가하는 것 처럼 해서 비선형 결정 경계를 만드는 PCA
일반 PCA를 수행해서 분산의 비율이 제대로 표현되지 않는 경우 사용
- KernelPCA클래스 (kernel, gamma)
kernel = rbf: 가우시안 정규 분포 형태의 커널을 적용
kernel = sigmoid: 로그 함수 형태
gamma: 복잡도를 설정. 0.0 ~ 1.0 사이의 값
- 하이퍼 파라미터 튜닝을 거의 하지 않지만 주성분 분석을 분류나 회귀의 전처리 과정으로 사용하는 경우에 가장 좋은 성능을 발휘하는 kernel이나 gamma 값을 찾기 위해서 튜닝 하는 경우도 있다.
#데이터 생성
from sklearn.datasets import make_swiss_roll
# X는 3차원 피처, t 는 범주
X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)
print(X)
Decision Tree ----------------sklearn - RandomF: 랜덤추출 - Ada - GB : 경사하강법 - HistGB : 피처(연속형)의 구간(정수)화하여 경사하강법 ---------외부라이브러리 - XGBoost - LGBM - CAT 무작위성 추가, 속도 향상 등등 딥러닝 - 데이터가 많을 때 성능 날뜀
트리 모델을 쓸 때 조심할 점 1. 트리를 만들 때는 균형을 맞춰야 한다. - 깊이가 깊어지면 찾기가 더 힘들어지기 때문이다. 트리가 한쪽으로 치우치면 평균 조회횟수가 높아진다. - 그래서 Balaned Tree를 만들려고 애쓰는데, LGBM은 이걸 신경쓰지 않기 때문에 훈련속도가 빠르다.
2. 이상치나 과대적합 가능성 - 하나하나는 조건인데 만약 샘플이 한개밖에 없다면 이상치이거나 과대적합의 가능성이 높다. - 10만개 중 1개의 데이터를 맞추기 위해 알고리즘을 생성했다면 (상황에 따라 필요한 경우도 있지만) 과대적합의 가능성이 높다. - 샘플이 적은데 비율 차이가 많이 나면 그냥 하면 안됨. 추출을 할 때 반드시 100:1로 추출해! 그럼 선택이 안되는 상황은 줄어든다. => 층화추출 max_depth 트리의 깊이가 줄어들면 조건이 세분화되지 않으니까 배치되는 샘플이 많아진다. 얕게 여러번 하자! mean_leaf_nodes / samples같은게 있으면 배치할 수 있는 샘플 수가 있다. 최소 10개는 주자~
<500000x16552 sparse matrix of type '<class 'numpy.float64'>' with 11500000 stored elements in Compressed Sparse Row format>
5) 훈련 데이터와 테스트 데이터 생성
num_train = len(train)
X_train = all_data_encoded[:num_train]
#답안 생성을 위한 데이터 - 새로운 데이터
X_test = all_data_encoded[num_train:]
y = train['target']
#타겟의 비율에 따라 층화추출
from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X_train, y, test_size =0.3,
stratify=y, random_state=42)
# 회귀 모델을 만들어서 훈련하고 ROC, AUC 점수 출력
from sklearn.linear_model import LogisticRegression
#로지스틱 회귀를 이용해서 훈련
logistic_model = LogisticRegression(max_iter=1000, random_state=42)
logistic_model.fit(X_train, y_train)
#ROC AUC 점수 출력
y_valid_preds = logistic_model.predict_proba(X_valid)[:, 1]
from sklearn.metrics import roc_auc_score
roc_auc = roc_auc_score(y_valid, y_valid_preds)
print('ROC AUC:', roc_auc)
from sklearn.preprocessing import MinMaxScaler
ord_features = ['ord_' + str(i) for i in range(6)]
all_data[ord_features] = MinMaxScaler().fit_transform(all_data[ord_features])
#명목형 피처와 날짜 피처 합치기
from scipy import sparse
#원핫 인코딩 한 결과가 sparse matrix라서 희소 행렬을 합치는 API 사용
all_data_sprs = sparse.hstack([sparse.csr_matrix(all_data),
encoded_nom_matrix,
encoded_date_matrix], format='csr')
all_data_sprs
<500000x16306 sparse matrix of type '<class 'numpy.float64'>' with 9163718 stored elements in Compressed Sparse Row format>
- 현재 작업
이진 피처는 숫자 0과 1로 생성
명목 피처는 원핫인코딩
순서형 피처는 순서를 만들어서 번호를 부여하거나 일련번호 형태로 인코딩한 후 스케일링 작업 수행
데이터 분할
#훈련 데이터의 개수
num_train = len(train)
#훈련용
X_train = all_data_sprs[:num_train]
#답안 제출용
X_test = all_data_sprs[num_train:]
#훈련용
y = train['target']
#ROC AUC 점수를 확인하라고 했으므로
#훈련 데이터를 다시 모델 훈련 데이터와 평가 훈련 데이터로 분할
X_train, X_valid, y_train, y_test = train_test_split(X_train, y,
test_size=0.3,
stratify=y,
random_state=42)
- 무작위로 선택된 수천명의 사람에게 복잡한 질문을 하고 대답을 모은다고 가정하면 이렇게 모은 답이 전문가의 답보다 나을 가능성이 높은데, 이를 대중의 지혜 혹은 집단지성이라고 한다. - 하나의 좋은 예측기를 이용하는 것보다 일반적인 여러 예측기를 이용해서 예측을 하면 더 좋은 결과를 만들 수 있다는 것을 앙상블 기법이라고 한다. - Decision Tree는 전체 데이터를 이용해서 하나의 트리를 생성해서 결과를 예측하지만, Random Forest는 훈련 세트로부터 무작위로 각기 다른 서브 세트를 이용해서 여러개의 트리 분류기를 만들고 예측할 때 가장 많은 선택을 받은 클래스나 평균 이용 - 머신러닝에서 가장 좋은 모델은 앙상블을 이용하는 모델
2. 투표기반 분류기
- 분류기 여러개를 가지고 훈련을 한 후 투표를 해서 다수결의 원칙으로 분류하는 방식 - law of large numbers(큰 수의 법칙)
동전을 던졌을 때 앞면이 나올 확률이 51%이고 뒷면이 나올 확률이 49%인 경우 일반적으로 1000번을 던진다면 앞면이 510번 뒷면이 490번 나올 것이다.
이런 경우 앞면이 다수가 될 가능성은 확률적으로 75% 정도 된다.
이를 10000번으로 확장하면 확률은 97%가 된다.
- 앙상블 기법을 이용할 때 동일한 알고리즘을 사용하는 분류기를 여러 개 만들어도 되고 서로 다른 알고리즘의 분류기를 여러개 만들어도 됨
동일한 알고리즘을 사용하는 분류기를 여러개 만들어서 사용할 때는 훈련 데이터가 달라야 한다.
2-1) 직접 투표 방식
- 분류를 할 때 실제 분류된 클래스를 가지고 선정
2-2) 간접 투표 방식
- 분류를 할 때 클래스 별 확률 가지고 선정 - 이 방식을 사용할 때는 모든 분류기가 predict_proba 함수 사용 가능 - 이 방식의 성능이 직접 투표 방식보다 높음
2-3) API
- sklearn.ensemble.VotingClassifier 클래스 - 매개변수로는 estimators 가 있는데 여기에 list로 이름과 분류기를 튜플의 형태로 묶어서 전달하고 voting 매개변수에 hard와 soft를 설정해서 직접 투표 방식인지 간접 투표 방식인지 설정
2-4) 투표 기반 분류기 (클래스를 가지고 선정)
# 직접 투표 방식
# 데이터 생성
from sklearn.model_selection import train_test_split #훈련 데이터와 테스트 데이터 분할
from sklearn.datasets import make_moons #샘플 데이터 생성
X, y = make_moons(n_samples=500, noise=0.30, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
#개별 분류기
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
#모델 생성 및 훈련
log_clf = LogisticRegression(solver="lbfgs", random_state=42)
rnd_clf = RandomForestClassifier(n_estimators=100, random_state=42)
svm_clf = SVC(gamma="scale", random_state=42)
#직접 투표 기반 분류기
from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(
estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
voting='hard')
#평가 지표 확인
from sklearn.metrics import accuracy_score
for clf in (log_clf, rnd_clf, svm_clf, voting_clf):
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(clf.__class__.__name__, accuracy_score(y_test, y_pred))
100%는 아닐테니까 오류가 발생하고, 다른 모델에서 공통으로 발생할 가능성은 크지 않으니까 여러개를 돌리면 효율이 좋아질 것이라는게 투표기반 모델의 가정.
2-5) 배깅과 페이스팅
- 동일한 알고리즘을 사용하고 훈련 세트에 서브 세트를 무작위로 구성해서 예측기를 각기 다르게 학습시키는 것 - bagging(bootstrap aggregating): 훈련 세트에서 중복을 허용하고 샘플링하는 방식 - pasting: 훈련 세트에서 중복을 허용하지 않고 샘플링하는 방식
하나의 샘플링 데이터는 여러개의 예측기에 사용 가능한데 bagging은 하나의 예측기에 동일한 샘플이 포함될 수 있다. pasting은 하나의 예측기 안에는 동일한 샘플이 포함될 수 없다.
모든 예측기가 훈련을 마치면 모든 예측을 모아서 새로운 샘플에 대한 예측을 생성하는데, 이때 수집 함수는 분류일 때 최빈값이고 회귀일 때 평균을 계산
개별 예측기는 원본 데이터 전체로 훈련한 것보다 편향이 심하지만 수집 함수를 통과하면 편향과 분산이 모두 감소
예측기들은 동시에 다른 CPU 코어나 컴퓨터에서 병렬로 학습 가능
- API: BaggingClassifier
기본적으로 bagging을 사용. bootstrap 매개변수를 False로 설정하면 페이스팅 수행
샘플이 많으면 페이스팅 사용, 샘플의 개수가 적으면 bagging 사용
n_jobs 매개변수: sklearn이 훈련과 예측에 사용할 CPU 코어 수를 설정 (-1을 설정하면 모든 코어 사용)
max_features, bootstrap_features라는 매개변수 이용해서 특성 샘플링 지원
최대 사용하는 피처의 개수와 피처의 중복 허용 여부를 설정
각 예측기는 무작위로 선택한 입력 특성의 일부분으로 훈련된다.
이미지와 같은 고차원의 데이터 세트에서 이용
훈련 특성과 샘플을 모두 샘플링하는 것을 Random Patches Method라고 한다.
샘플을 모두 사용하고 특성만 샘플링하는 것은 Random Subspaces Method라고 한다.
특성 샘플링을 하게 되면 더 다양한 예측기가 만들어지므로 편향을 늘리는 대신 분산을 낮춘다.
3. RandomForest
- 같은 알고리즘으로 여러 개의 분류기를 만들어서 보팅으로 최종 결정하는 배깅의 대표적인 알고리즘 - 알고리즘은 DecisionTree를 이용 - bootstrap 샘플을 생성해서 샘플 데이터 각각에 결정 트리를 적용한 뒤 학습 결과를 취합하는 방식으로 작동 - 각각의 DecisionTree들은 전체 특성 중 일부만 학습 - random_state 값에 따라 예측이 서로 다른 모델이 만들어지는 경우가 있는데 트리의 개수를 늘리면 변동이 적어진다. - 각 트리가 별개로 학습되므로 n_jobs를 이용해서 동시에 학습하도록 할 수 있다.
3-1) 장점
- 단일 트리의 단점 보완 - 매개변수 튜닝을 하지 않아도 잘 작동하고 데이터의 스케일을 맞출 필요가 없다. - 매우 큰 데이터 세트에도 잘 작동 - 여러 개의 CPU 코어를 사용하는 것이 가능
3-2) 단점
- 대량의 데이터에서 수행하면 시간은 많이 걸린다. - 차원이 매우 높고 (특성의 개수가 많음) 희소한 데이터(유사한 데이터가 별로 없는) 일수록 잘 작동하지 않는다.
이런 경우 선형 모델을 사용하는 것이 적합
희소한 데이터는 리프 노드에 있는 샘플의 개수가 적은 경우
3-3) 배깅과 랜덤 포레스트 비교
- 랜덤 포레스트는 Decision Tree의 배깅
배깅에서 부트 스트래핑을 사용하고 특성 샘플링을 이용하는 것이 랜덤 포레스트
부트 스트래핑은 하나의 훈련 세트에서 일부분의 데이터를 추출하는데 복원 추출 이용해서 수행
특성 샘플링은 비율을 설정해서 특성을 전부 이용하지 않고 일부분만을 이용해서 학습하는 방식
랜덤 포레스트에서는 특성의 비율을 기본적으로 제곱근만큼 사용
#배깅을 이용한 random forest 만들기
bag_clf = BaggingClassifier(
DecisionTreeClassifier(max_features="sqrt", max_leaf_nodes=16),
n_estimators=500, random_state=42)
bag_clf.fit(X_train, y_train)
y_pred = bag_clf.predict(X_test)
- RandomForest 알고리즘은 트리의 노드를 분할할 때 전체 특성 중에서 최선의 특성을 찾는 것이 아니라 무작위로 선택한 특성 후보 중에서 최적의 특성을 찾는 식으로 무작위성 주입
이러한 방식은 트리의 다양성을 높여서 편향을 손해보는 대신에 분산을 낮추는 방식으로 더 좋은 모델을 만들어간다.
3-4) 특성 중요도
- 트리 모델은 특성의 상대적 중요도를 측정하기 쉬움 - sklearn은 어떤 특성을 사용한 노드가 평균적으로 불순도를 얼마나 감소시켰는지 확인해서 특성의 중요도 판단
가중치의 평균이며 노드의 가중치는 연관된 훈련 샘플의 수와 같음
훈련 샘플의 수를 전체 합이 1이 되도록 결과값을 정규화한 후 feature_importances_에 저장
from sklearn.datasets import load_iris
iris = load_iris()
#data라는 속성에 피처 4가지 있음
#target이라는 속성의 꽃의 종류에 해당하는 범주형 데이터 3가지
#featre_names 속성에 피처 이름이 저장되어 있고 class_names에 클래스 이름 저장
rnd_clf = RandomForestClassifier(n_estimators=500, random_state=42)
rnd_clf.fit(iris["data"], iris["target"])
for name, score in zip(iris["feature_names"], rnd_clf.feature_importances_):
print(name, score)
Bagging이나 Pasting은 여러 개의 학습기를 가지고 예측한 후 그 결과를 합산해서 예측
앞의 모델을 보완해 나가면서 예측기를 학습
종류는 여러가지
4-1) Ada Boost
이전 모델이 과소 적합했던 훈련 샘플의 가중치를 더 높이는 것으로 이 방식을 이용하면 새로 만들어진 예측기는 학습하기 어려운 샘플에 점점 더 맞춰지게 된다.
먼저 알고리즘의 기반이 되는 첫번째 분류기를 훈련 세트로 훈련시키고 예측을 만들고 그 다음에 알고리즘이 잘못 분류한 훈련 샘플의 가중치를 상대적으로 높인다.두번째 분류기는 업데이트된 가중치를 사용해서 훈련 세트에서 다시 훈련하고 다시 예측을 만들고 그 다음에 가중치를 업데이트 하는 방식
투표 방식이 아닌 것임
- API: sklearn. AdaBoostClassifier
기반 알고리즘이 predict_proba 함수를 제공한다면 클래스 확률을 기반으로 분류가 가능
algorithm 매개변수에 SAMME.R 값 설정
확률을 이용하는 방식이 예측 값을 이용하는 방식보다 성능이 우수할 가능성이 높다.
# moons 데이터에 AdaBoost 적용
X, y = make_moons(n_samples=500, noise=0.30, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)
from sklearn.ensemble import AdaBoostClassifier
ada_clf = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=1), n_estimators=200,
algorithm="SAMME.R", learning_rate=0.5, random_state=42)
ada_clf.fit(X_train, y_train)
plot_decision_boundary(ada_clf, X, y)
learning_rate(학습률)는 훈련을 할 때 스텝의 크기로 이 값이 크면 최적화에 실패할 가능성이 높아지고, 낮으면 최적화에는 성공할 가능성이 높지만 훈련 속도가 느려지고 과대적합 가능성이 높아진다.
따라서 학습률은 그리드서치로 최적값을 찾을 필요가 있다.
4-2) Gradient Boosting
여러 개의 결정 트리를 묶어서 강력한 모델을 만드는 앙상블 기법
회귀와 분류 모두 가능
RandomForest도 여러 개의 결정 트리를 이용하는 것은 같지만, Gradient Boosting은 이전 트리의 오차를 보완하는 방식으로 순차적으로 트리 생성
무작위성이 없다. 이전에 오분류한 샘플에 가중치를 부여한다. 대신 강력한 사전 가지 치기를 사용한다.
보통 1~5 정도 깊이의 트리를 사용하기 때문에 메모리를 적게 사용하고 에측도 빠르다.
RandomForest 보다 매개변수 설정에 조금 더 민감하지만 잘 조정하면 더 높은 정확도를 제공한다.
AdaBoost 가중치 업데이트를 경사 하강법을 이용해서 조정한다.
예측 속도는 빠르지만 훈련 속도(수행 시간)는 느리고 하이퍼 파라미터 튜닝도 좀더 어렵다.
랜덤 포레스트는 각각 훈련하기 때문에 병렬이 된다. n_jobs여럿 지정 가능. 그런데 부스팅은 순차적으로 가야하기 때문에 병렬이 불가하다. GB의 장점은 메모리가 적고 예측도 빠르고 성능이 좋지만 훈련 시간이 문제임.
- API: sklean. GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingClassifier
import time
# GBM 수행 시간 측정을 위함. 시작 시간 설정.
start_time = time.time()
gb_clf = GradientBoostingClassifier(random_state=0)
gb_clf.fit(X_train , y_train)
gb_pred = gb_clf.predict(X_test)
gb_accuracy = accuracy_score(y_test, gb_pred)
print('GBM 정확도: {0:.4f}'.format(gb_accuracy))
print("GBM 수행 시간: {0:.1f} 초 ".format(time.time() - start_time))
GBM 정확도: 0.8880 GBM 수행 시간: 0.2 초
- 하이퍼 파라미터
max_depth: 트리의 깊이
max_features: 사용하는 피처의 비율
n_estimators: 학습기의 개수로 기본은 100
learning_rate: 학습률. 기본은 0.1이며 0~1사이로 설정 가능
subsample: 샘플링 비율. 기본 값은 1인데 과대적합 가능성이 보이면 숫자를 낮추면 된다.기본은 모든 데이터로 학습
deck 열에는 결측치가 많다. embarked와 embark_town은 동일한 의미를 가진 컬럼
# 전처리
# load_dataset 함수를 사용하여 데이터프레임으로 변환
df = sns.load_dataset('titanic')
# IPython 디스플레이 설정 - 출력할 열의 개수 한도 늘리기
pd.set_option('display.max_columns', 15)
# NaN값이 많은 deck 열을 삭제, embarked와 내용이 겹치는 embark_town 열을 삭제
rdf = df.drop(['deck', 'embark_town'], axis=1)
# age 열에 나이 데이터가 없는 모든 행을 삭제 - age 열(891개 중 177개의 NaN 값)
rdf = rdf.dropna(subset=['age'], how='any', axis=0)
# embarked 열의 NaN값을 승선도시 중에서 최빈값으로 치환하기
# 각 범주의 개수를 구한 후 가장 큰 값의 인덱스를 가져오기
most_freq = rdf['embarked'].value_counts(dropna=True).idxmax()
# 치환
rdf['embarked'].fillna(most_freq, inplace=True)
# 속성(변수) 선택
X=ndf[['pclass', 'age', 'sibsp', 'parch', 'female', 'male',
'town_C', 'town_Q', 'town_S']] #독립 변수 X
# X=ndf.drop(['survived'], axis=1)
y=ndf['survived'] #종속 변수 Y
#피처의 스케일 확인
X.describe() #숫자 컬럼의 기술 통계량 확인
숫자 컬럼의 값들의 범위가 차이가 많이 나면 scaling을 수행하는 것이 좋다.
# 스케일링
# 설명 변수 데이터를 정규화(normalization)
from sklearn import preprocessing
X = preprocessing.StandardScaler().fit(X).transform(X)
# 훈련
# train data 와 test data로 구분(7:3 비율)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)
- 분류에서는 잘못 분류된 데이터를 다음 학습기에서 다시 예측하는 형태로 구현 - 회귀에서는 첫번째 결정 트리 모델을 가지고 학습한 후 잔차를 구하여 다음 결정 트리 모델이 잔차를 타겟으로 해서 다시 학습을 수행하고 잔차를 구한 후 이 결과를 가지고 다음 결정 트리 모델이 훈련을 하는 방식으로 구현
예측값은 모든 결정 트리 모델의 예측을 더하면 됨
- GradientBoostingRegressor 제공
#샘플 데이터 생성
np.random.seed(42)
X = np.random.rand(100, 1) - 0.5
y = 3*X[:, 0]**2 + 0.05 * np.random.randn(100)
print(X[0])
print(y[0])
[-0.12545988] 0.05157289874841034
#첫번째 트리로 훈련
from sklearn.tree import DecisionTreeRegressor
#의사 결정 나무를 이용한 회귀
tree_reg1 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg1.fit(X, y)
print(y[0])
print(tree_reg1.predict(X[0].reshape(1, 1)))
0.05157289874841034 [0.12356613]
#첫번째 예측기에서 생긴 잔여 오차에 두번째 DecisionTreeRegresssor를 훈련
y2 = y - tree_reg1.predict(X)
#잔차를 타겟으로 훈련
tree_reg2 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg2.fit(X, y2)
print(tree_reg2.predict(X[0].reshape(1, 1)))
#이제 세 개의 트리를 포함하는 앙상블 모델이 생겼습니다.
#새로운 샘플에 대한 예측을 만들려면 모든 트리의 예측을 더하면 됩니다.
X_new = np.array([[0.8]])
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))
print(y_pred)
[0.75026781]
#이제 세 개의 트리를 포함하는 앙상블 모델이 생겼습니다.
#새로운 샘플에 대한 예측을 만들려면 모든 트리의 예측을 더하면 됩니다.
X_new = np.array([[0.05]])
y_pred = sum(tree.predict(X_new) for tree in (tree_reg1, tree_reg2, tree_reg3))
print(y_pred)
Gradient Boosting에 기반하지만 느린 훈련속도와 과적합 규제에 대한 부분 보완
병렬 학습 가능
자체 내장된 교차 검증 수행
결측값도 자체 처리
5-1) 하이퍼 파라미터
- 일반 파라미터: 일반적으로 실행 시 스레드의 개수 silent 모드 (로그 출력x) 등의 선택을 위한 파라미터로 기본 값을 거의 변경하지 않음 - 부스터 파라미터: 트리 최적화, 규제 등의 파라미터 - 학습 태스크 파라미터: 학습 수행 시의 객체 함수나 평가를 위한 지표 등을 설정
#설치
pip install xgboost
5-2) 훈련과 검증에 사용하는 데이터
- API: DMatrix
data 매개변수에 피처, label 매개변수에 타겟 설정
- 위스콘신 유방암 데이터
X-ray 촬영한 사진을 수치화한 데이터
사진을 직접 이용하는게 아니라 사진에서 필요한 데이터를 추출해서 수치화해서 사용하는 경우가 많다.
이러한 라벨링 작업에 Open CV를 많이 활용한다.
타겟이 0이면 악성(malignant), 1이면 양성(benign)
# 데이터 가져오기
import xgboost as xgb
from xgboost import plot_importance
from sklearn.datasets import load_breast_cancer
dataset = load_breast_cancer()
X_features= dataset.data
y_label = dataset.target
cancer_df = pd.DataFrame(data=X_features, columns=dataset.feature_names)
cancer_df['target']= y_label
cancer_df.head(3)
# 예측 확률이 0.5 보다 크면 1 , 그렇지 않으면 0 으로 예측값 결정하여 List 객체인 preds에 저장
preds = [ 1 if x > 0.5 else 0 for x in pred_probs ]
print('예측값 10개만 표시:',preds[:10])
예측값 10개만 표시: [1, 0, 1, 0, 1, 1, 1, 1, 1, 0]
#범주형 평가 지표 출력
정확도, 정밀도, 재현율, 정밀도와 재현율의 조화 평균(f1-score), roc_auc
from sklearn.metrics import confusion_matrix, accuracy_score
from sklearn.metrics import precision_score, recall_score
from sklearn.metrics import f1_score, roc_auc_score
#오차 행렬 출력
confusion = confusion_matrix( y_test, preds)
print('오차행렬:\n', confusion)
#정확도: 전체 데이터에서 맞게 분류한 것의 비율
accuracy = accuracy_score(y_test , preds)
print('정확도:', accuracy)
#정밀도: 검색된 문서들 중 관련있는 문서들의 비율 (True로 판정한 것 중 실제 True인 것의 비율)
precision = precision_score(y_test , preds)
print('정밀도:', precision)
#재현율: 관련된 문서들 중 검색된 비율 (실제 True인 것 중 True로 판정한 것의 비율)
recall = recall_score(y_test , preds)
print('재현율:', recall)
#recall과 precision의 조화 평균 (f1_score)
f1 = f1_score(y_test,preds)
print('f1_score:', f1)
#roc_auc score - area under curve. 그래프 곡선 아래 면적
#1에 가까울수록 좋은 성능
roc_auc = roc_auc_score(y_test, pred_probs)
print('roc_auc:', roc_auc)
# plot_importance( )를 이용하여 feature 중요도 시각화
from lightgbm import plot_importance
fig, ax = plt.subplots(figsize=(10, 12))
plot_importance(lgbm_wrapper, ax=ax)
6. Stacking
- 부스팅이나 랜덤 포레스트를 예측기의 예측을 취합해서 무언가 동작(투표, 확률, 평균 등)을 수행하는데 예측을 취합하는 모델을 훈련시키려고 하는 방식
개별 알고리즘의 예측 결과 데이터 세트를 최종적인 메타 데이터를 만들고 이 데이터를 가지고 별도의 ML 알고리즘으로 최종 학습을 수행하고 테스트 데이터를 기반으로 다시 최종 예측을 수행하는 방식
개별 알고리즘의 예측 결과를 가지고 훈련해서 최종 결과를 만들어내는 예측기 Blender 생성
블랜더는 홀드 아웃 세트를 이용해서 학습
스태킹에서는 두 종류의 모델이 필요한데 하나는 개별적인 기반 모델이고 다른 하나는 기반 모델의 예측 데이터를 학습 데이터로 만들어서 학습하는 최종 메타 모델
- API: sklearn 스태킹 지원 안함. 직접 구현하거나 오픈소스 시용
6-1) 위스콘신 유방암 데이터를 이용해서 스태킹 구현
- 개별 학습기: KNN, RandomForest, AdaBoost, DecisionTree - 최종 학습기: LogisticRegression
# 데이터 가져오기
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
cancer_data = load_breast_cancer()
X_data = cancer_data.data
y_label = cancer_data.target
X_train , X_test , y_train , y_test = train_test_split(X_data , y_label , test_size=0.2 , random_state=0)
# 예측기 생성
# 개별 ML 모델을 위한 Classifier 생성.
knn_clf = KNeighborsClassifier(n_neighbors=4)
rf_clf = RandomForestClassifier(n_estimators=100, random_state=0)
dt_clf = DecisionTreeClassifier()
ada_clf = AdaBoostClassifier(n_estimators=100)
# 최종 Stacking 모델을 위한 Classifier생성.
lr_final = LogisticRegression(C=10)
pred = np.array([knn_pred, rf_pred, dt_pred, ada_pred])
print(pred.shape)
# transpose를 이용해 행과 열의 위치 교환. 컬럼 레벨로 각 알고리즘의 예측 결과를 피처로 만듦.
pred = np.transpose(pred)
print(pred.shape)
(4, 114) (114, 4)
#최종 모델의 정확도 확인
lr_final.fit(pred, y_test)
final = lr_final.predict(pred)
print('최종 메타 모델의 예측 정확도: {0:.4f}'.format(accuracy_score(y_test , final)))
KNN을 생성할 때 weights 매개변수에 distance를 설정하면 거리에 따른 가중치 적용
- KNN은 게으른 알고리즘
데이터를 예측하기 위해서 모든 데이터가 메모리에 상주해야 하는 알고리즘
- 이해하기 쉬운 화이트 박스 알고리즘이고 별도의 훈련을 거치지 않는 간단한 알고리즘이라서 데이터 전처리(결측치 대체나 새로운 피처 추가)에 많이 이용
결측치나 새로운 피처 추가: 지역적 정보를 이용하기 때문에 다중공선성 문제는 고려할 필요가 없음. 전체 데이터를 사용하는게 아니라서 실제 예측하는 데는 사용하지 않음.
화이트 박스 알고리즘: 내가 보고 이해하기 쉬운 알고리즘, 대표적으로 KNN과 트리 / 블랙박스 알고리즘: 딥러닝
- API는 sklearn.neighbors.KNeighborsRegressor
.
- KNN을 사용할 때는 스케일링 반드시 수행
3) Decision Tree
- 트리 기반 - 불순도 지표
지니계수 (오분류 오차)
1 - (예측한 개수 / 전체 개수 - (예측한 개수 / 전체 개수) ... 한쪽으로만 분류가 되면 0: 가장 좋은 형태 가장 나쁜 값이 0.5
엔트로피
- API: DecisionTreeRegressor - RandomForest나 GBM, XGBoost, LightGBM의 기반이 되는 모델 - 다양한 파라미터가 존재하기 때문에 하이퍼 파라미터 튜닝 필수 - 스케일링 과정은 필요 없음
m = 200
X = np.random.rand(m, 1)
y = 4 * (X - 0.5) ** 2 + np.random.randn(m, 1)/10
from sklearn.tree import DecisionTreeRegressor
tree_reg = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg.fit(X, y)
#시각화
from graphviz import Source
from sklearn.tree import export_graphviz
#decision_tree_dot 이라는 파일로 저장
export_graphviz(tree_reg, out_file='decision_tree.dot',
feature_names=['X'], class_names=['y'],
rounded=True, filled=True)
#파일을 출력
with open('decision_tree.dot') as f:
dot_graph = f.read()
src = Source(dot_graph)
src
- 분류에서는 가장 가까운 데이터와의 거리가 가장 먼 결정 경계를 만들어가는 방식 - 회귀에서는 제한된 마진 오류 안에서 가능한 많은 샘플이 들어가도록 학습
마진 오류의 폭은 epsilion 파라미터로 설정
#시드 고정 후 가우시안 분포를 따르는 데이터셋을 만듭니다.
np.random.seed(42)
m = 50
X = 2 * np.random.rand(m,1)
y = (4 + 3 * X + np.random.randn(m,1)).ravel()
X_train = X[:40]
X_test = X[40:]
y_train = y[:40]
y_test = y[40:]
# 모델 생성 과 훈련
from sklearn.svm import LinearSVR
epsilons = [0.1, 0.5, 1.0, 1.5, 2.0]
for epsilon in epsilons:
svm_reg = LinearSVR(epsilon=1.5, random_state=42)
svm_reg.fit(X_train,y_train)
# svm_reg = 마진이 큰 모형(epsilon=1.5)
y_pred = svm_reg.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
print(epsilon, ":", rmse)
- 데이터를 가지고 어떤 결정을 해야 하는 문제를 접하는 경우, 결정해야 하는 Target이 이미 알려진 범주형일 때 이미 알려져있으므로 지도학습 - 분류의 유형은 이진분류(2가지 중 하나)와 다중분류(3가지 이상 중 하나)로 분류하기도 하고, 선형분류와 비선형분류와 나누기도 함 - sklearn의 분류기들은 예측하기 위한 함수로 2가지 사용
predict: 분류 결과
predict_proba: 각 클래스에 대한 확률(확률이 가장 높은 결과가 predict의 결과
1-1) 분류 알고리즘
- 판별 분석 - 랜덤 분류 - KNN - Support Vector Machine - 나이브 베이즈 - 로지스틱 회귀 - 결정 트리 - 최소 근접 - 신경망 - 앙상블
2. MNIST 데이터
- 0부터 9까지의 숫자 이미지 70,000개로 이루어진 데이터, 레이블이 존재 - sklearn을 이용해서 다운로드 받으면 data 속성에 피처가 존재하고 target 속성으로 레이블을 제공
1-1) MNIST 데이터 가져오기
from sklearn.datasets import fetch_openml
mnist = fetch_openml('mnist_784', version=1, as_frame=False)
X, y = mnist['data'], mnist['target']
print(X.shape)
print(y.shape)
(70000, 784) (70000,)
이미지가 7만개 존재, 각 이미지에는 784개의 특성 존재
특성은 픽셀의 개수 (28x28)
#이미지 출력
#이미지 데이터 1개 가져오기
some_digit = X[0]
#이미지를 다시 2차원으로 변경
some_digit_image = some_digit.reshape(28, 28)
#출력
plt.imshow(some_digit_image, cmap=mpl.cm.binary)
plt.axis('off')
plt.show()
1-2) 훈련 데이터와 테스트 데이터 분리
print(y.dtype)
object
정수로 형변환
#레이블 자료형을 정수로 변경
y = y.astype(np.uint8)
uint8
3. 이진 분류
3-1) 이진 분류를 위한 데이터 준비
- 타겟이 True 또는 False - 이진 분류는 맞다 틀리다를 구분하는 것 - 레이블의 값을 True와 False로 생성
- 확률적 경사 하강법 (Stochastic Gradient Descent-SGD) 사용하는 분류 모델 클래스
타겟을 찾아갈 때 한번에 찾아가지 않고 학습률 값을 이용해서 작게 분할해서 타겟을 찾아가는 방식
- 매우 큰 데이터 세트를 효율적으로 처리 - 한번에 하나씩 훈련 샘플을 독립적으로 처리하기 때문에 온라인 학습에 적합
온라인 학습: 실시간으로 데이터가 주어지는 형태의 학습 ↔ 배치 학습
- 하이퍼 파라미터
max_iter: 최대 수행 횟수
tol: 중지기준. 이 값보다 손실의 값이 작으면 훈련 중지 (어느정도까지 틀릴 것인지?)
random_state
# SGDClassifier를 이용한 훈련
#모델 생성 및 학습
from sklearn.linear_model import SGDClassifier
#max_iter는 작업 횟수이고 tol은 중지 기준으로 loss가 tol 보다 작으면 훈련 중지 random_state는 seed 값
sgd_clf = SGDClassifier(max_iter=1000, tol=1e-3, random_state=42)
#훈련
sgd_clf.fit(X_train, y_train_5)
#첫 번째 데이터를 이용해 이미지 감지\
sgd_clf.predict([some_digit])
array([ True])
3-3) 분류의 평가 지표
- 오차 행렬: 정답이 True와 False로 나누어져 있고 분류 결과도 True와 False로 나누어서 표로 만든 형태
from sklearn.model_selection import cross_val_predict
from sklearn.metrics import confusion_matrix
#예측값 생성
y_train_pred = cross_val_predict(sgd_clf, X_train, y_train_5, cv=3)
#오차행렬 생성
confusion_matrix(y_train_5, y_train_pred)
array([[53892, 687], [ 1891, 3530]], dtype=int64)
53892는 5가 아닌 것으로 제대로 분류
687는 5라고 잘못 분류한 것
1891은 5 이미지가 맞는데 5가 아니라고 분류한 개수
3550은 5라고 맞게 분류한 데이터
- Accuracy (정확도)
True를 True로, False를 False로 예측한 비율
옳은 경우를 고려하는 지표
정확도는 가장 직관적으로 모델의 성능을 평가할 수 있는 지표
정확도를 사용할 때는 타겟의 비율을 확인해야 함
타겟의 비율이 어느정도 고르게 분포되어 있다면 고려할 수 있는 평가지표이지만, 타겟의 비율이 고르지 않다면 다른지표를 사용해야할 수 있다.
어느 곳의 날씨가 99일은 맑고 1일은 비가 온다면, 무조건 맑다고 예측할 때 정확도가 99%가 된다.
- Recall (재현율)
True를 True라고 예측한 비율
실제 날씨가 맑은데 맑다고 예측한 비율
sensitivity(민감도) 또는 hit rate 라고도 한다.
- Precision (정밀도)
True라고 예측한 것 중에서 실제 True인 것의 비율
날씨가 맑다고 예측했는데 실제로 맑은 비율
정밀도와 재현율은 같이 사용하는 경우가 많다.
- F1 Score
Precision과 Recall의 조화 평균
Target의 비율이 불균형 구조일 때 모델의 성능 지표로 많이 이용
- sklearn.metrics에서 지표계산 API 제공
대입해야 하는 데이터: 실제 값과 예측한 값의 배열
- 보통의 경우 F1 Score가 좋으면 성능이 좋은 분류기라고 하지만, 상황에 따라서는 다른 지표 사용 고려
감시 카메라를 이용해서 도둑을 잡아내는 분류기를 훈련시킨다고 하면 정확도가 낮더라도 재현율이 좋은 것이 좋은 분류기가 될 수 있다.
어린 아이에게 동영상을 추천하는 시스템의 경우는 어린 아이에게 성인이 볼 영상을 추천하면 안됨 이런 경우에는 안전한 것들만 추천해줘야 하므로 정밀도가 높은 것이 좋은 분류기가 될 수 있다.
일반적으로 정밀도를 올리면 재현율이 떨어지고, 재현율을 높이면 정밀도가 떨어진다. 이를 정밀도와 재현율의 트레이드 오프라고 한다.
#예측 - 양성 클래스의 확률
y_scores_forest = y_probas_forest[:, 1]
print(y_scores_forest[1])
0.01
y_scores_forest를 가지고 평가 지표를 구해야 함
print(roc_auc_score(y_train_5, y_scores_forest))
0.9983436731328145
RandomForest를 이용한 모델의 ROC 값이 이전 모델보다 높게 나옴
3-5) 교차 검증
- 여러 개의 fold로 나누어서 모델을 생성하고 훈련한 후 평가지표를 얻어서 평균을 구하기도 함
#교차 검증
from sklearn.model_selection import cross_val_score
#3번 수행해서 각 검증의 정확도 확인
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy")
array([0.95, 0.96, 0.96])
#교차 검증
from sklearn.model_selection import cross_val_score
#3번 수행해서 각 검증의 정확도 확인
cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring="accuracy")
array([0.95, 0.96, 0.96])
4. 다중 분류
- 둘 이상의 클래스를 구별하는 작업 - SGD, RandomForest, Naive Bayes 알고리즘은 여러개의 범주를 직접 처리할 수 있지만 - Logistic Regression, Support Vector Machine 같은 알고리즘은 이진분류만 가능
이진 분류기를 여러개 결합하면 다중 분류가 가능
4-1) 이진 분류 알고리즘을 이용한 다중 분류
- OvR(One-versus-the-Rest)
범주의 개수만큼 이진 분류기를 만들어서 가장 높은 결정 점수를 가진 분류기가 분류한 것을 선택
숫자분류기라면 숫자는 0부터 9까지 총 10개가 있으므로 분류기를 10개 만든다.
각 숫자에 해당하는 확률을 구해서 그 확률 중에서 가장 높은 것을 선택한다.
- OvO(One-versus-One)
두가지로 분류하는 이진분류기를 모두 (범주개수 * 범주개수-1) / 2 생성한 후 가장 많이 양성으로 분류된 범주를 선택
0/1, 0/2, 0/3, ... 8/9 구분까지 생성
그 중에서 가장 많이 분류된 클래스 선택
sklearn에서는 다중 클래스 분류에 이진 분류 알고리즘을 사용하는 분류기를 선택하면 알아서 자동으로 알고리즘 선택
강제로 알고리즘을 선택할 수도 있다.
- SVM(Support Vector Machine)을 이용한 다중 분류
SVM은 이진 분류기: 기본적으로 다중 분류를 하지 못함
이 분류기를 이용해서 다중 분류를 위한 데이터를 학습시키면 스스로 알고리즘을 선택해서 다중분류 결과 예측 가능
### 이진 분류기를 이용한 다중 분류
from sklearn.svm import SVC
#모델 생성
svm_clf = SVC(random_state=42)
#훈련
svm_clf.fit(X_train[:1000], y_train[:1000])
#예측
svm_clf.predict([some_digit])
- 분류기가 샘플 별로 여러개의 결과를 출력하는 경우 - 이미지에서 객체를 탐지하는 경우 하나의 객체만 탐지하는 것이 아니고 여러개의 객체를 탐지해야 하는 경우나 얼굴에서 얼굴의 각 부위를 탐지하는 경우 등
#다중 레이블 생성
y_train_large = (y_train >= 7)
y_train_odd = (y_train % 2 == 1)
y_multilabel = np.c_[y_train_large, y_train_odd]
print(y_multilabel[0])
[False True]
6. 판별 분석 (Discriminant Analysis)
- 2개 이상의 모집단에서 추출된 표본들이 지니고 있는 정보를 이용해서 이 표본들이 어느 모집단에서 추출된 것인지를 결정해줄 수 있는 기준을 찾는 분석
은행에서 부동산 담보 대출할 때 이 고객이 대출을 상환할 것인가 아니면 상환하지 않을 것인가를 판별하는 경우 은행에서는 기존의 대출을 상환하지 않은 고객들의 특성과 대출을 상환한 고객들의 특성을 별도로 만들어두고 이 고객의 특성이 어느쪽에 더 가까운건지 파악할 수 있다.
- 초창기에는 LDA(Linear Discriminant Analysis - 선형 판별 분석)를 많이 사용
최근에는 신경망 등이 등장하면서 사용빈도가 낮아졌다.
아직도 다른 머신러닝 알고리즘의 기반 알고리즘으로 사용됨
- 전체 표본의 크기가 독립 변수의 개수보다 3배 이상 많아야 함
6-1) LDA
- 내부 제곱합에 대한 사이 제곱합의 비율을 최대화하는 것이 목적
그룹 내부의 제곱합은 적게, 그룹과 그룹 사이의 제곱합은 크게 만드는 알고리즘
클러스터링의 기반이 되는 방법
- sklearn.discriminant.LinearDicriminantAnalysis 클래스 이용
from sklearn.neighbors import KNeighborsClassifier
knn_clf = KNeighborsClassifier()
#훈련할 때 여러개의 label인 묶인 타겟을 설정하면 다중 레이블 분류
knn_clf.fit(X_train, y_multilabel)
knn_clf.predict([some_digit])
array([[False, False]])
some_digit = X[0]
knn_clf.predict([some_digit])
array([[False, True]])
7. DummyClassifier
- 랜덤하게 분류 - 타겟의 비율을 보고 그대로 랜덤하게 예측하는 방식
8. KNN(K-Nearest Neighbor) 최근접 이웃
- 특징들이 가장 유사한 K개의 데이터를 찾아서 K개의 데이터를 가지고 다수결로 클래스를 선택해서 할당 - 회귀에 사용할 때는 그 값의 평균을 구해서 예측
8-1) 특징
- 간단: 전처리 과정에서 결측치를 대체하는 데 사용하기도 함 - 모델을 피팅하는 과정이 없음 - 모든 예측 변수들은 수치형이어야 함
거리를 계산해야 하기 때문
이 경우 범주형 데이터는 특별한 경우가 아니면 원핫 인코딩을 수행해야 함
- 게으른 알고리즘이라고 하는데 훈련 데이터 세트를 메모리에 전부 저장하고 거리 계산을 수행
온라인 처리가 안됨
8-2) API
- sklearn.neighbors.KNeighborsClassifier 클래스
인스턴스를 생성할 때 n_neighbors를 이용해서 이웃의 개수 설정
metric을 이용해서 거리 계산 알고리즘을 설정하는데 1이면 맨하튼 거리, 2이면 유클리드 거리, 설정하지 않으면 mincowski 거리
- 데이터: loan_200.csv
payment_inc_ratio: 소득에 따른 대출 상환 비율
dti: 소득에 대한 부채 비율
outcome: 상환 여부
# KNeighborsClassifier를 이용해서 payment_inc_ratio와 dit에 따른 outcome 분류
#데이터 분리
#테스트를 위한 데이터 1개 추출
newloan = loan200.loc[0:0, ['payment_inc_ratio', 'dti']]
#피처 추출
X = loan200.loc[1:, ['payment_inc_ratio', 'dti']]
#타겟 추출
y = loan200.loc[1:, 'outcome']
from sklearn.neighbors import KNeighborsClassifier
# 인스턴스 생성 - 필수적인 파라미터는 n_neighbors
# 모델 생성
knn = KNeighborsClassifier(n_neighbors=21)
#훈련
knn.fit(X, y)
#예측
knn.predict(newloan)
array(['paid off'], dtype=object)
#클래스별 예측 확률
print(knn.predict_proba(newloan))
[[0.476 0.524]]
predict 함수는 거의 모든 분류 모델이 소유하고 있지만
predict_proba 함수는 없을 수 있다.
8-3) 거리 지표
- 유클리드 거리
서로의 차이에 대한 제곱합을 구한 뒤 그 값의 제곱근을 취하는 방식
유클리드 거리를 사용할 때는 수치형 데이터의 범위를 확인
- 맨하튼 거리
서로의 차이에 대한 절대값을 구한 뒤 모두 더한 거리
- 마할라노비스 거리
두 변수 간의 상관관계를 사용
유클리드 거리나 맨하튼 거리는 상관성을 고려하지 않기 때문에 상관관계가 있는 피처들의 거리를 크게 반영
주성분간의 유클리드 거리를 의미
많은 계산이 필요하고 복잡성이 증가
피처들의 상관관계가 높을 때 사용
- 민코프스키 거리
1차원 공간에서는 맨하튼 거리를 사용하고 2차원 공간에서는 유클리드 거리를 사용
8-4) 표준화
- 거리의 개념을 이용하므로 스케일링이나 표준화를 수행을 해주어야 함 - 표준화를 했을 때와 그렇지 않을 때 이웃이 달라지게 됨
10000 1 1 1 1 20000 1 1 1 1 11000 100 100 100 100 제곱을 하니까 단위 자체가 다름
8-5) 피처 엔지니어링
- KNN은 구현이 간단하고 직관적 - 성능은 다른 분류 알고리즘에 비해서 그렇게 우수한 편은 아니다. - 다른분류 방법들의 특정 단계에 사용할 수 있게 모델에 지역적 정보를 추가하기 위해서 사용하는 경우가 많음
새로운 피처를 만드는 데 많이 사용
기존의 피처를 이용해서 새로운 피처를 만드는 것이라서 다중 공선성 문제를 야기할 것 같은데 KNN으로 만들어진 피처는 다중 공선성 문제가 거의 발생하지 않는다.
KNN은 피처 전체를 이용하는 것이 주위 데이터 몇개만 이용하기 때문에 매우 지엽적인 정보 이용
- loan_data.csv.gz 데이터에서 새로운 피처 추가
csv 파일의 크기가 너무 커지는 경우 파일을 gz 타입으로 압축해서 용량 줄일 수 있음
pandas는 gz로 압축된 csv 파일의 내용을 읽을 수 있다.
python은 zip이나 tar로 압축된 파일을 압축을 해제할 수 있고 여러 파일을 압축할 수 있는 API를 제공
# outcome 컬럼을 타겟으로 하고 나머지를 피처 특성으로 만들고, 피처 특성으로 타겟을 예측하도록 KNN으로 학습한 후 그 때의 예측 확률을 새로운 피처로 추가
- 일반적인 머신러닝 기법 중에서 매우 강력하고 선형 또는 비선형, 회귀, 이상치 탐색에도 사용하는 머신러닝 모델 - 초창기에는 가장 인기있는 모델에 속함, 현재는 아님 - 복잡한 분류 문제에 적합했고, 작거나 중간 크기의 데이터 세트에 적합 - 모든 속성을 활용하는 전역적 분류 모형 - 군집별로 초평면을 만드는데 이 초평면은 다른 초평면의 가장 가까운 자료까지의 거리가 가장 크게 만드는 것
11-1) 특징
- 장점
에러율이 낮음
결과를 해석하기 용이
- 단점
파라미터 및 커널 선택에 민감
이진분류만 가능
특성의 스케일에 굉장히 민감. 반드시 스케일링 수행해야 함
11-2) 하드마진과 소프트마진
- 하드마진
모든 데이터가 정확하게 올바르게 분류된 것
하드마진이 성립되려면 데이터가 선형적으로 구분될 수 있어야 하고, 이상치에 민감
- 소프트마진
이상치로 인해 발생하는 문제를 피하기 위해서 좀더 유연한 모델을 생성
어느정도의 오류를 감안하고 결정경계를 만들어내는 방식
SVM에서 매개변수 C로 마진 오류 설정
마진 오류는 적은 것이 좋지만 너무 적게 설정하면 일반화가 잘 안됨 *일반화: 지금껏 보지 못한 데이터에 올바르게 적용되는 성질
11-3) 선형 SVM 모델을 이용한 붓꽃 분류
- Pipeline을 사용
피처 스케일링과 모델 훈련을 하나의 파이프라인으로 묶어서 수행
#피처 스케일링
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
#모델 생성해서 훈련
from sklearn.svm import LinearSVC
svc_clf = LinearSVC(C=1, random_state=42)
svc_clf.fit(X, y)
#예측
svc_clf.predict([[5.5, 1.7]])
array([1.])
- 모든 컬럼에 스케일링하는 것이 동일하다면 파이프라인으로 묶는 것이 가능
#피처 스케일링
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline
#스케일러와 분류기를 하나로 묶어서 사용
svm_clf = Pipeline([
('scaler', StandardScaler()),
('linear_SVC', LinearSVC(C=1, random_state=42))
])
svm_clf.fit(X, y)
svc_clf.predict([[5.5, 1.7]])
array([1.])
11-4) 비선형SVM
- 선형 SVM 분류기가 효율적이지만 선형적으로 분류할 수 없는 데이터가 많음 - 선형 SVM(커널)의 문제점중 하나가 XOR을 구분할 수 없는 점 => 다항식을 이용하여 해결 - 다항식을 이용하는 SVM을 생성하기 위해서는 PolynomialFeatures 변환기를 추가하면 됨
#데이터 생성
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=100, noise=0.14, random_state=42)
#데이터 시각화
def plot_dataset(X, y, axes):
plt.plot(X[:, 0][y==0], X[:, 1][y==0], "bs")
plt.plot(X[:, 0][y==1], X[:, 1][y==1], "g^")
plt.axis(axes)
plt.grid(True, which='both')
plt.xlabel("x_1", fontsize=20)
plt.ylabel("x_2", fontsize=20, rotation=0)
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
숫자가 높아지면 잘 분류할 가능성은 높아지지만 학습할 피처의 개수가 늘어나고, 이로인해 훈련 속도도 느려지고 학습 속도가 느려진다.
OVerfitting 될 가능성이 높아진다.
11-5) 다항식 커널
- 다항식 특성을 추가하면 성능이 좋아졌는데 낮은 차수의 다항식은 매우 복잡한 데이터 세트에는 잘 맞지 않을 것이고 높은 차수의 다항식은 굉장히 많은 특성을 추가하므로 모델을 느리게 함 - Kernel, Trick이라는 수학적 기교를 이용해서 실제로는 특성을 추가하지 않으면서 다항식 특성을 추가한 것과 같은 효과를 얻는 방법 사용 - 이 기법을 사용할 때는 SVC 클래스에서 매개변수 kernel에 poly 설정, 매개변수 degree에 적용하고자 하는 차수를 설정, 매개변수 coef()에 정수값을 설정. 낮은 차수와 높은 차수 중에서 어떤 다항식에 영향을 받을지 설정
#다항식 커널은 실제로 다항식을 추가하는 것이 아니라 추가하는 것과 같은 효과를 냄
from sklearn.svm import SVC
#스케일러와 분류기를 하나로 묶어서 사용
poly_kernel_svm_clf = Pipeline([
('scaler', StandardScaler()),
('svm_SVC', SVC(kernel='poly', degree=10, coef0=1, C=1, random_state=42))
])
poly_kernel_svm_clf.fit(X, y)
plot_predictions(poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
plt.show()
11-6) 유사도 특성을 추가하는 가우시안 RBF 커널
- 몇 개의 랜드마크를 추가한 후 이 랜드마크와의 유사도를 측정해서 그 값을 특성으로 추가 - SVC로 생성, 매개변수 kernel에 RBF, gamma 설정
gamma를 너무 적은 수로 설정하면 선형 분류에 가까워지고 높게 설정하면 다항식을 이용하는 분류에 가까워짐
하나의 영역에 2가지 클래스가 배정된 경우 하나의 영역에 16개의 데이터가 배정었고 1번이 10개, 2번이 6개가 배정되면 10/16 * log2(10/16) - 6/16 * log2(6/16)
앞뒤 비율이 비슷해질수록 오분류 가능성이 커진다.
한쪽으로만 데이터가 배정되면 앞의 값이 0이 되고 뒤의 값도 0이 되어 결국 0이 됨. 0에 가까운 값이 좋은 값이다.
12-4) 규제 매개변수
- 결정 트리는 훈련 데이터에 대한 제약사항이 거의 없음 - 규제를 하지 않으면 트리가 훈련 데이터에 아주 가깝게 맞추려고 해서 과대적합이 발생 - 결정 트리는 모델의 하이퍼 파라미터가 없는 것이 아니라 훈련되기 전에 파라미터 수가 결정되지 않기 때문에 모델 구조가 데이터에 맞춰져서 고정되지 않고 자유로움
하이퍼 파라미터의 기본값이 대부분 Max 값이거나 None이다.
이전의 다른 모델은 하이퍼 파라미터의 값이 설정되어 있음
하이퍼 파라미터의 값이 기본값을 가지는 경우는 과대 적합될 가능성은 낮지만 과소 적합될 가능성이 있고 하이퍼 파라미터의 값이 기본값을 가지지 않고 MAX 값을 가지는 경우 과대 적합될 가능성은 높지만 과소 적합될 가능성은 낮다.
비선형 커널에서 degree라는 하이퍼 파라미터가 있다. degree가 높으면 데이터를 정확하게 분류할 확률이 높다. degree를 -1로 설정할 수 있다고 하면 정확하게 분류할 가능성은 높아지지만
- 결정 트리의 형태를 제한하는 매개변수
max_depth: 최대 깊이, 특성의 개수를 확인해서 적절하게 설정
min_samples_split: 분할을 할 때 가져야 하는 데이터의 최소 개수
min_samples_leaf: 하나의 터미널이 가져야 하는 최소 개수
min_weight_fraction_leaf: 개수가 아니라 비율로 설정
max_leaf_nodes: 터미널의 개수
max_features: 분할에 사용할 특성의 개수
min으로 시작하는 매개변수를 증가시키거나 max로 시작하는 매개변수를 감소시키면 규제가 커짐
규제가 커지면 오차가 발생할 확률은 높아진다.
12-5) 트리 모델의 단점
- 훈련 데이터의 작은 변화에도 매우 민감
조건문을 사용하는 방식이기 때문에 하나의 데이터가 추가되었는데 이 데이터가 조건에 맞지 않으면 트리를 처음부터 다시 만들어야 할 수도 있다.
트리 모델을 훈련시켜서 다른트리 모델과 비교하고자 할 때는 반드시 random_state를 고정시켜야 한다.
12-6) 피처의 중요도 확인
- 트리 모델에는 feature_importance
#피처의 중요도 확인
print(tree_clf.feature_importances_)
print(iris.feature_names[2:])
from sklearn.datasets import load_iris
#붓꽃 데이터 가져오기
iris = load_iris()
#피처 선택
#iris.data - 열의 개수가 4개인 2차원 배열
#iris.feature_names - 피처 이름 확인
#print(iris.feature_names)
#피처 선택
X = iris.data
#타켓 선택
y = iris.target
#결정 트리를 생성해서 훈련
from sklearn.tree import DecisionTreeClassifier
#max_depth 가 깊으면 정확한 분류를 해낼 가능성은 높지만 시간이 오래 걸리고
#과대 적합 문제 발생 가능성이 있음
#트리 모델은 트리를 한 개 생성하는 것이 아니고 여러 개 생성하므로
#각 트리가 동일한 데이터를 가지고 훈련해야 하므로 random_state를 고정시켜야 합니다.
tree_clf = DecisionTreeClassifier(max_depth=2, random_state=42)
tree_clf.fit(X, y)
12-8) 예측
#예측
result = tree_clf.predict([[5.1, 3.5, 1.4, 0.2]])
print(result)
12-9) 트리 모델 시각화
1) 트리 모델의 강점
- 화이트박스, 이해가 쉽다.
트리 구조 시각화
- 피처의 중요도 파악이 쉽다 - 별도의 파라미터 튜닝을 하지 않아도 좋은 성능 - 데이터 전처리가 거의 필요 없음
2) 시각화 준비
- graphviz 설치: 화면에 트리를 출력하는 것이 목적 - Windows https://graphviz.org/download/#windows 에서 graphviz를 다운로드 받아서 설치 graphviz 가 설치된 디렉토리의 bin 디렉토리 와 bin 안에 있는 dot.exe 경로를 path에 추가
전문가의 도움을 받아서 개발자가 알고리즘을 작성해서 컴퓨터에 저장하고 이 알고리즘을 따라서 문제 해결
컴퓨터의 역할은 결과를 만들어내는 것
- Machine Learning
Data와 Output을 주면 컴퓨터가 알고리즘을 만들어내는 방식
인공지능과 머신러닝의 관계
- 인공지능 > 기계학습(머신러닝) > 딥러닝, 강화학습 - 전문가 시스템 -> 머신 러닝 -> 딥러닝, 강화학습
딥러닝은 이겼다, 졌다로 판정해주지만 강화학습은 순간순간 확률을 보여준다.=> 게임에 적합
이미지 데이터가 존재하는 경우 머신 러닝은 이미지 데이터 1개를 데이터 1개로 바라보지만 딥러닝은 이를 작게 쪼개서 그 안에서 알고리즘을 찾을 수 있음
2. Machine Learning
- 데이터를 가지고 학습하도록 컴퓨터를 프로그래밍하는 과학 - 명시적으로 프로그램 되는 것이 아니라 훈련되며 작업과 관련있는 샘플을 제공하면 이 데이터에서 통계적 구조를 찾아 그 작업을 자동화하기 위한 규칙을 만들어내는 것 - 필요 요소
입력 데이터 포인트: 최근에는 여러 입력 데이터 포인트로 얻어진 데이터를 한 곳에 모아서 처리하는 부분에 대해 중점
데이터 발생지가 여러 곳인 경우 별도로 처리하는 것이 어렵기 때문에 한 곳에 잘 정리를 해서 모으는 것이 중요
기대 출력: 어떤 결과를 원하는지
알고리즘 성능 측정 방법: 평가 지표
1) 사용하는 이유
- 전통적인 방식으로는 너무 복잡하거나 알려진 알고리즘이 없는 문제
음성인식은 직접 알고리즘을 만들기에는 너무 복잡해서 머신러닝을 이용
- 머신 러닝을 통해 학습을 할 수 있기 때문 대용량의 데이터를 분석하다보면 기존에 알고 있지 않은 또는 겉으로는 보이지 않는 패턴 발견 가능 - 데이터 마이닝
2) 역사
- 확률적 모델링
나이브 베이즈
로지스틱 회귀
- 신경망
등장은 1950년대, 이때는 컴퓨터 성능이 좋지 못해서 효과적인 훈련 방법을 찾지 못했다.
1989년 얀 르쿤에 의해 초창기 합성곱 신경망이 등장하면서 다시 각광
- 커널 방법
분류에 사용되었는데 선형이 아닌 비선형으로 결정 경계 만들기 시작
- Decision Tree, Random Forest, Gradient Boosting: 앙상블 모형
컴퓨터 성능이 좋아지면서 하나의 알고리즘을 부트스트랩을 이용하거나 여러 개의 알고리즘을 한꺼번에 학습하는 방법
- 신경망 - 생성형 AI
3) 분류
- 레이블(정답)의 존재 여부에 따른 분류
지도학습: 레이블 존재 (회귀 or 분류)
비지도학습: 레이블 없음 (주성분분석, 군집, 연관분석 등)
준지도학습: 레이블을 만드는 작업 (레이블이 일부분밖에 없어서)
강화학습: 보상이 주어지는 방식
- 실시간 점진적으로 학습을 할 수 있는지 여부
온라인학습: 점진적 학습 가능
실시간으로 데이터가 들어오는 경우도 학습이 가능하다.
배치학습: 점진적 학습 불가능
데이터가 확정이 되어있어야 한다.
- 사례 기반 학습과 모델 기반 학습
사례 기반 학습: 알고 있는 데이터와 새 데이터를 비교하는게 목적
모델 기반 학습: 패턴을 발견해서 예측 모델을 만드는 방식
4) 지도학습 (Supervised Learning)
- 레이블이 존재하는 학습 - 입력-출력 쌍들을 매핑해주는 함수 학습 - 종류
출력이 이산적일 때: 분류
출력이 연속적일 때: 회귀
출력이 확률인 경우: 추정 (Deep Learning)
- 단점
사용할 수 있는 데이터에 한계
데이터를 생성하는 데 많은 비용
- 선형회귀 - 로지스틱 회귀 - k-최근접 이웃 - 서포트 벡터 머신 - 결정 트리 - 랜덤 포레스트 - 신경망
- 로지스틱 회귀를 제외하고는 분류와 회귀에 모두 사용 가능
로지스틱 회귀는 분류만 가능
5) 비지도 학습(Unsupervised Learning)
- 레이블이 존재하지 않는 학습 - 데이터에 내재된 고유의 특징을 탐색하기 위해서 사용 - 지도 학습에 비해서 학습하기 어려움
- 군집
k means
DBSCAN
계층군집
이상치 탐지와 특이치 탐지
원클래스
아이솔레이션 포레스트
- 시각화와 차원 축소
PCA (주성분 분석)
LLE (지역적 선형 임베딩)
t-SNE
- 연관 규칙 학습
Apriori
eclat
6) 준지도 학습
- 라벨링이 일부분만 되어있어서 그 데이터를 이용해서 라벨이 없는 데이터에 라벨을 붙이기 위해서 사용
7) 강화 학습
- 결과가 바로 주어지지 않고 시간이 지나서 주어지는 방식 - 최근에 로봇 같은 분야에서 많이 이용
8) 애플리케이션 사례
- Netflix의 영화 추천 시스템: 고객의 평점작성 내역과 구매 내역을 이용해서 추천 - 미국 국가 안보국의 SKYNET: 파키스탄의 테러리스트를 식별해서 사살하기 위한 프로그램
휴대 전화 기록을 이용하여 휴대 전화를 자주 끄거나 USIM을 변경하는 사람을 테러리스트로 식별해서 사살했는데 잘못된 알고리즘으로 무고한 사람이 희생됨
3. scikit learn
- 파이썬 머신러닝 패키지 중 가장 많이 사용되는 라이브러리 - 가장 Python스러운 API - 패키지: sklearn
아나콘다는 내장
4. 데이터 표현 방식
1) 테이블로서의 데이터
- 기본 테이블은 2차원 데이터 그리드 형태 - 행: 데이터 세트의 개별 요소, sample이라 부르고 행의 개수는 n_sample이라 표현 - 열:각 요소와 관련된 수량, feature 또는 target이라고 부르는 경우가 많고 열의 개수를 n_features라고 표현
feature: 독립적인 데이터
Target : feature로 인해서 만들어지는 데이터로 label이라고도 함
2) feature
- 보통은 X라는 변수에 저장 - 특징 행렬이라는 표현을 사용. [n_sampels, n_features]의 모양을 가진 2차원 행렬이라 가정하며 실제 자료형은 numpy의 ndarray나 pandas의 DataFrame으로 되어 있는 경우가 많은데 가끔 sklearn의 희소 행렬인 경우도 있음 - 정량적인 데이터여야 하기 때문에 대부분의 경우는 실수지만 이산적인 데이터아 부울도 가ㅡㄴㅇ
3) target
- 대상 행렬이라고 하는데 y로 표시 - numpy의 1차원 ndarray나 pandas의 Series인 경우가 많음 - 연속적인 수치가 이산 클래스를 가질 수 있음
import seaborn as sns
iris = sns.load_dataset('iris')
템플릿 메소드 패턴: 공통으로 사용될 것 같은 메소드를 인터페이스에 등록하고 이를 클래스에서 구현해서 사용
- 검사 (inspection)
parameter: 함수나 기능을 수행하기 위해서 내부적으로 사용하는 데이터. argument, 인수, 인자, 매개변수라고 하기도 함
hyper parameter: 개발자가 직접 설정하는 파라미터.
hyper parameter 튜닝은 값을 변경해서 더 좋은 모델을 만들거나 최적의 값을 찾아가는 작업이다.
모든 hyper parameter를 public 속성으로 노출해서 확인이 가능하도록 함
- 제한된 객체 계층 구조
알고리즘만 python 클래스에 의해 표현, 데이터세트는 표준 포맷으로 표현, 매개변수 이름은 문자열
표준 포맷(numpy의 ndarray, pandas의 DataFrame, scipy의 Sparse Matrix)
- 구성
대부분의 머신러닝 작업은 기본 알고리즘의 시퀀스로 나타낼 수 있음 (순서대로)
- 합리적인 기본값
대다수의 하이퍼 파라미터는 라이브러리가 적절한 기본값을 가지도록 정의
2) 사용 방법
- 적절한 모델 클래스 mport - 모델 클래스를 인스턴스화 할 때 적절한 하이퍼 파라미터를 설정 - 데이터를 특징 배열과 타겟 배열로 생성 - 모델 클래스의 인스턴스의 fit 메소드를 호출해서 모델을 데이터에 적합하도록 훈련 - 모델을 새 데이터에 적용
지도 학습의 경우:predict 함수에 새로운 데이터 사용해서 예측
비지도 학습의 경우: transform이나 predict를 이용해서 데이터의 속성을 변환하거나 예측
3) 선형 회귀 수행
- sklearn.linear_model.LinearRegression
#샘플 데이터 생성
rng = np.random.RandomState(42)
#데이터 50개 생성
x = 10 * rng.rand(50)
#데이터를 이용해서 타겟 데이터 생성 - rng.randn(50)은 잡음
y = 2 * x -1 + rng.randn(50)
#x 데이터를 특징 행렬로 변환
print(x.shape) #1차원 배열 - 특성 배열은 2차원 배열, DataFrame, 희소 행렬
X = x.reshape(x.shape[0], -1) #x[:, np.newaxis]도 가능
print(X.shape) # X는 특성 배열이 되었다.
(50,) (50, 1)
#추정기 인스턴스 생성
from sklearn.linear_model import LinearRegression
model = LinearRegression(fit_intercept=True)
#기존 데이터로 훈련
model.fit(X, y)
#훈련 결과 확인
print("회귀 계수:", model.coef_)
print("절편:", model.intercept_)
#8:2로 분할 - 데이터를 하나의 데이터로 제공하면 2개로 리턴하고
#특성 배열과 타겟 배열 2개를 대입하면 4개로 리턴
train_set, test_set = sklearn.model_selection.train_test_split(housing,
test_size=0.2,
random_state=42)
#분할된 데이터의 차원 확인
print(train_set.shape)
print(test_set.shape)
(16512, 10) (4128, 10)
X = housing.drop('median_house_value', axis=1)
y = housing['median_house_value']
result = sklearn.model_selection.train_test_split(X, y, test_size=0.2, random_state=42)
print(type(result))
<class 'list'>
- 층화 추출: 계층적 샘플링 (데이터를 일정한 비율로 샘플링)
회귀나 분류를 할 때 타겟의 데이터 분포가 일정하지 않은 경우 왜곡된 결과를 만들 수 있음
분류의 경우 1과 0의 비율을 10대 1 정도 되는 상황에서 훈련 데이터에 0으로 분류되는 데이터가 하나도 없다면 이 경우 모델은 테스트 데이터에 결과가 좋지 않을 것
0으로 분류되는 모든 데이터가 훈련 데이터에 포함되어 버리면 잘못하면 테스트 데이터에 완전하게 맞는 결과가 나와버릴 수 있다.
회귀의 경우 타겟이 연속형이라면 범주형으로 변환해서 수행해야 한다.
이때 사용할 수 있는 함수는 pandas의 cut 함수
데이터와 구간의 리스트, 레이블의 리스트 대입
API 함수는 StratifiedShuffleSplit
이 함수의 리턴되는 데이터는 데이터가 아니고 데이터의 인덱스
결과를 가지고 다시 데이터 추출을 해야 함
X = housing.drop('median_house_value', axis=1)
y = housing['median_house_value']
X_train, X_test, y_train, y_test = sklearn.model_selection.train_test_split(X, y, test_size=0.2, random_state=42)
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)
(16512, 9) (4128, 9) (16512,) (4128,)
- median_income의 비율을 이용한 층화 추출 (계층적 샘플링)
# 연속형 데이터를 범주형으로 변환
#pd.cut(데이터, bins =[경계값 나열] ,labels =[ 레이블 나열 ])
housing['income_cat'] = pd.cut(housing['median_income'],
bins=[0, 1.5 ,3.0 ,4.5, 6, np.inf],
labels=[1,2,3,4,5])
housing['income_cat'].value_counts()
방의 개수와 침실의 개수는 상관계수가 낮다. 그런데 bedrooms_per_room은 -0.25니까 회귀를 하려면 차라리 이걸 쓰는게 낫다.
그래서 도메인 지식이 중요하다.
- 피처와 타겟 분리
#훈련 데이터 복제 - 레이블을 제외한 데이터 복제
housing_feature = start_train_set.drop('median_house_value', axis=1)
#훈련 세트를 위해 레이블 삭제
#레이블에 변형을 적용하지 않기 위해서 레이블값도 복제
housing_leabels = start_train_set['median_house_value'].copy()
- 전처리 - 누락된 데이터 처리
#결측치 확인
#NaN 결측치 확인
sample_incomplete_rows = housing[housing.isnull().any(axis=1)].head()
sample_incomplete_rows
#sklearn 의 변환기들은 2차원 배열을 요구합니다.
from sklearn.preprocessing import OrdinalEncoder
oridinalEncoder = OrdinalEncoder()
result = oridinalEncoder.fit_transform(housing_features['ocean_proximity'])
print(result[:10])
ValueError: Expected 2D array, got 1D array instead: array=['INLAND' 'NEAR OCEAN' 'INLAND' ... '<1H OCEAN' '<1H OCEAN' 'INLAND']. Reshape your data either using array.reshape(-1, 1) if your data has a single feature or array.reshape(1, -1) if it contains a single sample.
#sklearn 의 변환기들은 2차원 배열을 요구합니다.
from sklearn.preprocessing import OrdinalEncoder
oridinalEncoder = OrdinalEncoder()
result = oridinalEncoder.fit_transform(housing_features[['ocean_proximity']])
print(result[:10])
#원 핫 인코딩 - 범주형의 개수 만큼 열을 만들어서 해당하는 열에만 1을 표시
from sklearn.preprocessing import OneHotEncoder
oneHotEncoder = OneHotEncoder()
#기본적으로 희소 행렬(sparse matrix)
result = oneHotEncoder.fit_transform(housing_features[['ocean_proximity']])
print(result[:10])
#밀집 행렬로 변환
print(result.toarray()[:10])