A = np.array([[1, 2], [3, 4]])
B = np.array([[10, 10], [10, 10]])
print("행렬 A\n", A)
print("행렬 B\n", B)
print("A * B\n", A*B)
# 출력 결과
행렬 A
[[1 2]
[3 4]]
행렬 B
[[10 10]
[10 10]]
A * B
[[10 20]
[30 40]]
- 행렬 점곱(내적, product)
1차원 벡터와 마찬가지로 앞 행렬과 뒤 행렬의 수가 같아야함
# 2*2 행렬
M = np.array([[1, 2], [3, 4]])
# 2*3 행렬
N = np.array([[2, 3, 4], [2, 3, 4]])
# 앞 행렬의 열과 뒤 행렬의 행이 같아 행렬 점곱이 가능
print("행렬 M\n", M)
print("행렬 N\n", N)
L = np.dot(M, N)
print("행렬 L\n", L)
print(L.shape)
print(np.ndim(L))
# 출력 결과
행렬 M
[[1 2]
[3 4]]
행렬 N
[[2 3 4]
[2 3 4]]
행렬 L
[[ 6 9 12]
[14 21 28]]
(2, 3)
2
# 3*1 행렬
m = np.array([[1], [2], [3]])
# 3*1행렬
n = np.array([[1], [2], [3]])
# 앞 행렬의 열과 뒤 행렬의 행이 달라 행렬 점곱이 불가능
l = np.dot(m, n)
print(l)
print(l.shape)
print(np.ndim(l))
# 출력 결과
ValueError: shapes (3,1) and (3,1) not aligned: 1 (dim 1) != 3 (dim 0)
- 역행렬
어떤 행렬 A가 있을 때, 곱해서 단위행렬(E)를 만드는 행렬 B가 존재한다면, 행렬 B는 A의 역행렬
A = np.array([[1, 2], [3, 4]])
B = np.linalg.inv(A)
print(A)
print(B)
print(np.dot(A, B))
# 출력 결과
[[1 2]
[3 4]]
[[-2. 1. ]
[ 1.5 -0.5]]
# A 행렬과 A 행렬의 역행렬을 곱하여 단위행렬이 나옴
[[1.00000000e+00 1.11022302e-16]
[0.00000000e+00 1.00000000e+00]]
- 전치행렬
행과 열을 바꾼 배열의 형태
A = np.array([[1, 2, 3], [4, 5, 6]])
print("A\n", A)
print("A.shape\n", A.shape)
print("-------------------------")
A_ = A.T
print("A의 전치행렬\n", A_)
print("(A.T).shape\n", A_.shape)
# 출력 결과
A
[[1 2 3]
[4 5 6]]
A.shape
(2, 3)
-------------------------
A의 전치행렬
[[1 4]
[2 5]
[3 6]]
(A.T).shape
(3, 2)
# 1차원 텐서
a = np.array(10)
b = np.array([10, 20, 30])
print(np.dot(a, b))
print(a*b)
# 서로 shape이 다르지만 브로드캐스팅을 통해 계산이 가능하도록 만들어짐
# 출력 결과
[100 200 300]
[100 200 300]
# 2차원 텐서
A = np.array([[1, 2], [3, 4]])
B = np.array([10, 20])
print("행렬 A\n", A)
print("행렬 B\n", B)
print("A * B\n", A*B)
# 출력 결과
행렬 A
[[1 2]
[3 4]]
행렬 B
[10 20]
A * B
[[10 40]
[30 80]]
# 3차원 텐서
A = np.array([[[1, 1, 1],
[2, 2, 2]],
[[3, 3, 3],
[4, 4, 4]]])
B = np.array([[10, 10, 10]])
print("행렬 A\n", A)
print("A.shape:", A.shape)
print("행렬 B\n", B)
print("B.shape:", B.shape)
print("A * B\n", A*B)
# 출력 결과
행렬 A
[[[1 1 1]
[2 2 2]]
[[3 3 3]
[4 4 4]]]
A.shape: (2, 2, 3)
행렬 B
[[10 10 10]]
B.shape: (1, 3)
A * B
[[[10 10 10]
[20 20 20]]
[[30 30 30]
[40 40 40]]]
# 브로드캐스팅 조건에 맞지 않는 경우
# 2*3 행렬
A = np.array([[1, 2, 3], [4, 5, 6]])
# 1*2 행렬
B = np.array([10, 10])
print(A*B)
# 출력 결과
ValueError: operands could not be broadcast together with shapes (2,3) (2,)
import math
import numpy as np
import matplotlib.pyplot as plt
1. 일차함수
\(y\)\(=\)\(ax\)\(+\)\(b\)
\(a\): 기울기, \(b\): y절편
그래프 상에서 직선인 그래프(linear)
def linear_function(x):
a = 0.5
b = 2
return a*x+b
print(linear_function(5))
# 0.5 * 5 + 2 = 4.5
# 출력 결과
4.5
# x에 -5부터 5까지 0.1단위로 증가시키며 리스트 생성
x = np.arange(-5, 5, 0.1)
# x의 모든 값을 linear_function에 넣어 각 값에 대한 y값을 생성시킴
y = linear_function(x)
# 각 x값에 대응하는 y값을 그린 그래프
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Linear Function')
2. 이차함수
\(y=ax^2+bx+c\)
일반적으로 두 개의 실근을 가짐
def quadratic_function(x):
a = 1
b = -1
c = -2
return a*x**2 + b*x + c
print(quadratic_function(2))
# 1*2**2 + -1*2 -2 = 4 -2 -2 = 0
# 출력 결과
0
x = np.arange(-5, 5, 0.1)
y = quadratic_function(x)
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Quadratic Function')
3. 삼차함수(다항함수)
\(y=ax^3+bx^2+cx+d\)
def cubic_function(x):
a = 4
b = 0
c = -1
d = -8
return a*x**3 + b*x**2 + c*x + d
print(cubic_function(3))
# 4*3**3 + 0*3**2 + -1*3 + -8 = 108 + 0 + -3 + -8 = 97
# 출력 결과
97
x = np.arange(-5, 5, 0.1)
y = cubic_function(x)
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Cubic Function')
4. 함수의 최소값 / 최대값
x = np.arange(-10, 10, 0.1)
y = my_func(x)
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.scatter(1.5, my_func(1.5))
plt.text(1.5-1.5, my_func(1.5)+10, 'min value of f(x)\n({}, {})'.format(1.5, my_func(1.5)), fontdict={'size': 10})
plt.title('my_func')
plt.show()
min_val = min(y)
print(min_val)
# 출력 결과
7.75
5. 특정 구간 내에서 최소값 구하기
# x1과 x2 사이에서 최소값 구하기
def get_minimum(x1, x2, f):
x = np.arange(x1, x2, 0.1)
y = f(x)
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('get_minimum')
plt.show()
return min(y)
print(get_minimum(1, 4, my_func))
# 출력 결과
7.75
6. 지수함수 / 로그함수
지수함수·로그함수는 역함수 관계(\(y\)\(=\)\(x\) 직선 대칭, 단, 밑이 같을 때)
파이썬으로 직접 구현 가능
- 지수 함수
\(y=a^{x} (a\ne0)\)(기본형)
\(y=e^{x} (e=2.71828)\)
def exponential_function(x):
a = 4
return a**x
print(exponential_function(4))
print(exponential_function(0))
# 출력 결과
256
1
x = np.arange(-3, 2, 0.1)
y = exponential_function(x)
plt.plot(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.ylim(-1, 15)
plt.xlim(-4, 3)
plt.title('exponential_function')
plt.show()
def exponential_fnction2(x):
a = 4
return math.pow(a, x)
print(exponential_function(4))
print(exponential_function(0))
# 출력 결과
256
1
- 밑이 \(e\)인 지수 함수 표현
# math 라이브러리나 numpy 라이브러리에 있는 exp 사용
print(math.exp(4))
print(np.exp(4))
# 출력 결과
54.598150033144236
54.598150033144236
- 로그 함수
\(y=log_{a}(x) (a\ne1)\) (기본형)
\(y=log_{10}(x)\) (상용로그)
\(y=ln(x)\) (밑이 \(e\)인 자연로그)
# 밑이 3, 진수가 2인 로그
print(math.log(2, 3))
# 밑이 2인 로그
print(np.log2(4))
# np.log에서 log 다음 숫자가 없다면 밑이 e인 자연로그
print(np.log(4))
# 출력 결과
0.6309297535714574
2.0
1.3862943611198906
from mpl_toolkits.mplot3d import Axes3D
x = np.arange(-4.0, 4.0, 0.4)
y = np.arange(-4.0, 4.0, 0.4)
X, Y = np.meshgrid(x, y)
Z = X**2 + Y**2
fig = plt.figure()
ax = Axes3D(fig)
ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1, cmap = 'coolwarm')
ax.set_title('f(x, y) = x**2 + y**2')
plt.show()
편미분 예제1: \(x_{0}\)에 대한 편미분, \(\frac{\sigma f}{\sigma x_{0}}\)
x = np.array([1, 2])
def f0_function(x0):
return (x0**2) + (2**2)
print(numerical_differential(f0_function, x[0]))
# 출력 결과
1.9999999999997797
편미분 예제2: \(x_{1}\)에 대한 편미분, \(\frac{\sigma f}{\sigma x_{1}}\)
x = np.array([1, 2])
def f1_function(x1):
return (1**2) + (x1**2)
print(numerical_differential(f1_function, x[1]))
# 출력 결과
4.000000000004
- 기울기(gradient)
방향성을 가짐
# 기울기를 나타내는 함수
def numerical_diff(f, x):
h = 1e-5
# x와 모양은 같지만 전부 0으로 채워진 배열
grad = np.zeros_like(x)
for i in range(x.size):
tmp = x[i]
x[i] = tmp + h
fxh1 = f(x)
x[i] = tmp - h
fxh2 = f(x)
grad[i] = (fxh1 - fxh2) / (2*h)
x[i] = tmp
return grad
print(numerical_diff(my_func2, np.array([3.0, 4.0])))
print(numerical_diff(my_func2, np.array([1.0, 2.0])))
# 출력 결과
[6. 0.]
[2. 0.]
- 기울기의 의미를 그래프로 확인
기울기가 가장 낮은 장소(가운데)로부터 거리가 멀어질수록 기울기가 커짐
기울기가 커진다는 것은 영향을 많이 받는다는 의미
기울기가 작다는 것은 영향을 적게 받는다는 의미
X = np.arange(-20, 20, 2)
Y = np.arange(-20, 20, 2)
U, V = np.meshgrid(X, Y)
fig, ax = plt.subplots()
# 기울기 표시할 때 화살표로 표시할 수 있는 함수
q = ax.quiver(X, Y, U, V)
ax.quiverkey(q, X = 0.4, Y = 1.0, U = 20, label = 'Quiver Key', labelpos = 'E')
plt.grid()
plt.show()
# SVD 알고리즘을 통해 각각의 measure를 측정
model = SVD()
cross_validate(model, data, measures = ['rmse', 'mae'], cv = 5, verbose = True)
# 출력 결과
Evaluating RMSE, MAE of algorithm SVD on 5 split(s).
Fold 1 Fold 2 Fold 3 Fold 4 Fold 5 Mean Std
RMSE (testset) 0.9273 0.9320 0.9400 0.9451 0.9362 0.9361 0.0062
MAE (testset) 0.7333 0.7357 0.7387 0.7444 0.7372 0.7379 0.0037
Fit time 2.58 3.23 2.98 1.96 2.03 2.56 0.50
Test time 0.38 0.45 0.31 0.41 0.25 0.36 0.07
{'test_rmse': array([0.92730437, 0.93195753, 0.93997153, 0.94506308, 0.93619742]),
'test_mae': array([0.73328536, 0.73566774, 0.73869817, 0.74442193, 0.73723008]),
'fit_time': (2.5789830684661865,
3.2331647872924805,
2.975646495819092,
1.9638335704803467,
2.027461051940918),
'test_time': (0.3779747486114502,
0.4478921890258789,
0.3068206310272217,
0.40511560440063477,
0.24795103073120117)}
2. 컨텐츠 기반 필터링(Content-based Filtering)
컨텐츠 기반 필터링은 이전의 행동과 명시적 피드백을 통해 좋아하는 것과 유사한 항목을 추천
ex) 내가 지금까지 시청한 영화 목록과 다른 사용자의 시청 목록을 비교해 나와 비슷한 취향의 사용자가 시청한 영화를 추천
유사도를 기반으로 추천
컨텐츠 기반 필터링의 장단점
장점
많은 수의 사용자를 대상으로 쉽게 확장 가능
사용자가 관심을 갖지 않던 상품 추천 가능
단점
입력 틍성을 직접 설계해야 하기 때문에 많은 도메인 지식이 필요
사용자의 기존 관심사항을 기반으로만 추천 가능
import numpy as np
from surprise import Dataset
data = Dataset.load_builtin('ml-100k', prompt = False)
raw_data = np.array(data.raw_ratings, dtype = int)
# raw_data가 0부터 시작하도록 조절
raw_data[:, 0] -= 1
raw_data[:, 1] -= 1
n_users = np.max(raw_data[:, 0])
n_movies = np.max(raw_data[:, 1])
# 행: 총 유저수, 열: 총 영화수
shape = (n_users + 1, n_movies + 1)
shape
# 출력 결과
(943, 1682)
adj_matrix = np.ndarray(shape, dtype = int)
for user_id, movie_id, rating, time in raw_data:
# 1이 있는 위치가 현재 데이터가 있는 위치
adj_matrix[user_id][movie_id] = 1.
adj_matrix
# 출력 결과
array([[1, 1, 1, ..., 0, 0, 0],
[1, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[1, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 1, 0, ..., 0, 0, 0]])
my_id, my_vector = 0, adj_matrix[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector in enumerate(adj_matrix):
# user_id와 my_id가 다르면 유사도(similarity) 계산
if my_id != user_id:
similarity = np.dot(my_vector, user_vector)
if similarity > best_match:
best_match = similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 183, Best Match ID: 275
recommend_list = []
for i, log in enumerate(zip(my_vector, best_match_vector)):
log1, log2 = log
# 내가 보지 않았지만(log1(my_vector)가 1보가 작음) 유사하게 나온 영화(log2(best_match_vector)가 0보다 큼)를 추천
if log1 < 1. and log2 > 0.:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[272, 273, 275, ..., 1480, 1481, 1482]
my_id, my_vector = 0, adj_matrix[0]
best_match, best_match_id, best_match_vector = 9999, -1, []
for user_id, user_vector in enumerate(adj_matrix):
# user_id와 my_id가 다르면 유클리드 거리 계산
if my_id != user_id:
# 유클리드 거리 계산식
euclidian_dist = np.sqrt(np.sum(np.square(my_vector - user_vector)))
if euclidian_dist < best_match:
best_match = euclidian_dist
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 14.832396974191326, Best Match ID: 737
recommend_list = []
for i, log in enumerate(zip(my_vector, best_match_vector)):
log1, log2 = log
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[297, 312, 317, ..., 968, 1015, 1046]
# 코사인 유사도 계산식
def compute_cos_similarity(v1, v2):
norm1 = np.sqrt(np.sum(np.square(v1)))
norm2 = np.sqrt(np.sum(np.square(v2)))
dot = np.dot(v1, v2)
return dot / (norm1 * norm2)
my_id, my_vector = 0, adj_matrix[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector in enumerate(adj_matrix):
# user_id와 my_id가 다르면 코사인 유사도 계산
if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 0.5278586163659506, Best Match ID: 915
recommend_list = []
for i, log in enumerate(zip(my_vector, best_match_vector)):
log1, log2 = log
# 내가 보지 않았지만(log1(my_vector)가 1보가 작음) 유사하게 나온 영화(log2(best_match_vector)가 0보다 큼)를 추천
if log1 < 1. and log2 > 0.:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[272, 275, 279, ..., 1427, 1596, 1681]
- 기존 방법에 명시적 피드백(사용자가 평가한 영화 점수)을 추가해 실험
adj_matrix = np.ndarray(shape, dtype = int)
for user_id, movie_id, rating, time in raw_data:
# 단순히 데이터가 존재하는지 안하는지에 따라 0, 1이 아니라 존재하는 곳에 rating값을 넣음
adj_matrix[user_id][movie_id] = rating
adj_matrix
# 출력 결과
array([[5, 3, 4, ..., 0, 0, 0],
[4, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[5, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 5, 0, ..., 0, 0, 0]])
# 유클리드 거리로 계산
my_id, my_vector = 0, adj_matrix[0]
best_match, best_match_id, best_match_vector = 9999, -1, []
for user_id, user_vector in enumerate(adj_matrix):
# user_id와 my_id가 다르면 유클리드 거리 계산
if my_id != user_id:
# 유클리드 거리 계산식
euclidian_dist = np.sqrt(np.sum(np.square(my_vector - user_vector)))
if euclidian_dist < best_match:
best_match = euclidian_dist
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 55.06359959174482, Best Match ID: 737
# 코사인 거리로 계산
my_id, my_vector = 0, adj_matrix[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector in enumerate(adj_matrix):
# user_id와 my_id가 다르면 코사인 유사도 계산
if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 0.569065731527988, Best Match ID: 915
3. 협업 필터링(Collaborative Filtering)
사용자와 항목의 유사성을 동시에 고려해 추천
기존에 내 관심사가 아닌 항목이라도 추천 가능
자동으로 임베딩 학습 가능
협업 필터링의 장단점
장점
자동으로 임베딩을 학습하기 때문에 도메인 지식이 필요없음
기존의 관심사가 아니더라도 추천 가능
단점
학습 과정에서 나오지 않은 항목은 임베딩을 만들 수 없음
추가 특성을 사용하기 어려움
from surprise import KNNBasic, SVD, SVDpp, NMF
from surprise import Dataset
from surprise.model_selection import cross_validate
data = Dataset.load_builtin('ml-100k', prompt = False)
- KNN을 사용한 협업 필터링
model = KNNBasic()
cross_validate(model, data, measures = ['rmse', 'mae'], cv = 5, n_jobs = 4, verbose = True)
# 출력 결과
Evaluating RMSE, MAE of algorithm KNNBasic on 5 split(s).
Fold 1 Fold 2 Fold 3 Fold 4 Fold 5 Mean Std
RMSE (testset) 0.9743 0.9842 0.9849 0.9823 0.9689 0.9789 0.0063
MAE (testset) 0.7689 0.7785 0.7786 0.7752 0.7643 0.7731 0.0056
Fit time 2.72 2.70 2.85 2.98 1.69 2.59 0.46
Test time 16.55 16.59 16.43 15.93 7.88 14.68 3.40
{'test_rmse': array([0.9742902 , 0.98423331, 0.98486835, 0.98230369, 0.96892197]),
'test_mae': array([0.76887025, 0.77850316, 0.77859337, 0.77524521, 0.76433142]),
'fit_time': (2.724426746368408,
2.6965529918670654,
2.8537588119506836,
2.977182388305664,
1.6942033767700195),
'test_time': (16.55093264579773,
16.586602687835693,
16.429330825805664,
15.92642879486084,
7.883346796035767)}
- SVD를 사용한 협업 필터링
model = SVD()
cross_validate(model, data, measures = ['rmse', 'mae'], cv = 5, n_jobs = 4, verbose = True)
# 출력 결과
Evaluating RMSE, MAE of algorithm SVD on 5 split(s).
Fold 1 Fold 2 Fold 3 Fold 4 Fold 5 Mean Std
RMSE (testset) 0.9364 0.9366 0.9345 0.9349 0.9355 0.9356 0.0008
MAE (testset) 0.7378 0.7368 0.7356 0.7365 0.7381 0.7370 0.0009
Fit time 3.69 3.54 4.03 4.02 4.02 3.86 0.20
Test time 1.18 1.36 1.51 1.55 0.45 1.21 0.40
{'test_rmse': array([0.93641775, 0.93660876, 0.934486 , 0.93493886, 0.93553188]),
'test_mae': array([0.73780807, 0.73676556, 0.73564193, 0.73649871, 0.73805738]),
'fit_time': (3.690873146057129,
3.541585922241211,
4.030731439590454,
4.015835523605347,
4.018853425979614),
'test_time': (1.1828830242156982,
1.3635668754577637,
1.5132882595062256,
1.5461750030517578,
0.4530982971191406)}
- NMF를 사용한 협업 필터링
model = NMF()
cross_validate(model, data, measures = ['rmse', 'mae'], cv = 5, n_jobs = 4, verbose = True)
# 출력 결과
Evaluating RMSE, MAE of algorithm NMF on 5 split(s).
Fold 1 Fold 2 Fold 3 Fold 4 Fold 5 Mean Std
RMSE (testset) 0.9603 0.9552 0.9631 0.9654 0.9632 0.9614 0.0035
MAE (testset) 0.7569 0.7511 0.7570 0.7578 0.7573 0.7560 0.0025
Fit time 4.99 5.05 4.98 4.89 2.28 4.44 1.08
Test time 0.62 0.64 0.53 0.46 0.22 0.49 0.15
{'test_rmse': array([0.96025745, 0.95521602, 0.96314659, 0.96535182, 0.96319395]),
'test_mae': array([0.75686291, 0.75106343, 0.75704307, 0.75776259, 0.75726996]),
'fit_time': (4.98656702041626,
5.046948671340942,
4.978093385696411,
4.887223243713379,
2.2761926651000977),
'test_time': (0.6195485591888428,
0.6439330577850342,
0.5302431583404541,
0.4606480598449707,
0.2169051170349121)}
- SVD++를 사용한 협업 필터링
model = SVDpp()
cross_validate(model, data, measures = ['rmse', 'mae'], cv = 5, n_jobs = 4, verbose = True)
# 출력 결과
Evaluating RMSE, MAE of algorithm SVDpp on 5 split(s).
Fold 1 Fold 2 Fold 3 Fold 4 Fold 5 Mean Std
RMSE (testset) 0.9180 0.9196 0.9126 0.9336 0.9101 0.9188 0.0082
MAE (testset) 0.7193 0.7196 0.7155 0.7324 0.7149 0.7204 0.0063
Fit time 64.45 65.24 65.04 65.49 25.32 57.11 15.90
Test time 9.41 9.28 8.90 9.10 4.87 8.31 1.73
{'test_rmse': array([0.91803553, 0.9196422 , 0.91259638, 0.9335687 , 0.91007489]),
'test_mae': array([0.71934988, 0.719645 , 0.71549358, 0.73242896, 0.71492586]),
'fit_time': (64.4547209739685,
65.24023914337158,
65.03504347801208,
65.49227690696716,
25.323737144470215),
'test_time': (9.414633989334106,
9.28382134437561,
8.901108026504517,
9.10077452659607,
4.8735032081604)}
4. 하이브리드(Hybrid)
컨텐츠 기반 필터링과 협업 필터링을 조합한 방식
많은 하이브리드 방식이 존재
실습에서는 협업 필터링으로 임베딩을 학습하고 컨텐츠 기반 필터링으로 유사도 기반 추천을 수행하는 추천 엔진 개발
U, S, V = randomized_svd(adj_matrix, n_components = 2)
S = np.diag(S)
print(U.shape)
print(S.shape)
print(V.shape)
# 출력 결과
(943, 2)
(2, 2)
(2, 1682)
# 분해 후 재조합
# (U * S) * V
np.matmul(np.matmul(U, S), V)
# 출력 결과
array([[ 2.75413997e+00, 1.40492906e+00, 7.42675835e-01, ...,
-8.18516364e-04, 1.56868753e-02, 1.47148277e-02],
[ 1.37986536e+00, 9.01276577e-02, 4.55027384e-01, ...,
1.09681871e-02, 4.52128919e-04, -1.05261346e-03],
[ 9.93032597e-01, 7.39687516e-03, 3.35229530e-01, ...,
8.95866035e-03, -3.68132502e-04, -1.54632039e-03],
...,
[ 6.28183642e-01, 6.62904209e-02, 2.03737923e-01, ...,
4.52498630e-03, 5.10681060e-04, -1.32467414e-04],
[ 9.59658161e-01, 4.03024146e-01, 2.70469585e-01, ...,
1.31860581e-03, 4.42188957e-03, 3.93973413e-03],
[ 1.80307089e+00, 1.01280345e+00, 4.73641731e-01, ...,
-2.26049256e-03, 1.13925590e-02, 1.09104420e-02]])
- 사용자 기반 추천
나와 비슷한 취향을 가진 다른 사용자의 행동을 추천
사용자 특징 벡터의 유사도 사용
my_id, my_vector = 0, U[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector in enumerate(U):
# user_id와 my_id가 다르면 코사인 유사도 계산
if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
# 무려 99% 유사
Best Match: 0.9999996213542565, Best Match ID: 639
recommend_list = []
for i, log in enumerate(zip(adj_matrix[my_id], adj_matrix[best_match_id])):
log1, log2 = log
# 내가 보지 않았지만(log1(my_vector)가 1보가 작음) 유사하게 나온 영화(log2(best_match_vector)가 0보다 큼)를 추천
if log1 < 1. and log2 > 0.:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[300, 301, 303, ..., 1227, 1243, 1257]
- 항목 기반 추천
내가 본 항목과 비슷한 항목을 추천
항목 특징 벡터의 유사도 사용
my_id, my_vector = 0, V.T[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector in enumerate(V.T):
# user_id와 my_id가 다르면 코사인 유사도 계산
if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
# 더 높은 유사도
Best Match: 0.9999999998965627, Best Match ID: 1425
recommend_list = []
for i, user_vector in enumerate(adj_matrix):
if adj_matrix[i][my_id] > 0.9:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[0, 1, 4, ..., 935, 937, 940]
my_id, my_vector = 0, U[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector in enumerate(U):
# user_id와 my_id가 다르면 코사인 유사도 계산
if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 0.9999996213542565, Best Match ID: 639
recommend_list = []
for i, log in enumerate(zip(adj_matrix[my_id], adj_matrix[best_match_id])):
log1, log2 = log
# 내가 보지 않았지만(log1(my_vector)가 1보가 작음) 유사하게 나온 영화(log2(best_match_vector)가 0보다 큼)를 추천
if log1 < 1. and log2 > 0.:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[300, 301, 303, ..., 1227, 1243, 1257]
항목 기반 추천
my_id, my_vector = 0, V.T[0]
best_match, best_match_id, best_match_vector = -1, -1, []
for user_id, user_vector in enumerate(U):
# user_id와 my_id가 다르면 코사인 유사도 계산
if my_id != user_id:
cos_similarity = compute_cos_similarity(my_vector, user_vector)
if cos_similarity > best_match:
best_match = cos_similarity
best_match_id = user_id
best_match_vector = user_vector
print('Best Match: {}, Best Match ID: {}'.format(best_match, best_match_id))
# 출력 결과
Best Match: 0.9999999998965627, Best Match ID: 1425
recommend_list = []
for i, user_vector in enumerate(adj_matrix):
if adj_matrix[i][my_id] > 0.9:
recommend_list.append(i)
print(recommend_list)
# 출력 결과
[0, 1, 4, ..., 935, 937, 940]
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris, fetch_olivetti_faces
from sklearn.decomposition import PCA, IncrementalPCA, KernelPCA, SparsePCA
from sklearn.decomposition import TruncatedSVD, DictionaryLearning, FactorAnalysis
from sklearn.decomposition import FastICA, NMF, LatentDirichletAllocation
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
iris, labels = load_iris(return_X_y = True)
faces, _ = fetch_olivetti_faces(return_X_y = True, shuffle = True)
iris 그래프 그리기 함수
def plot_iris(iris, labels):
plt.figure()
colors = ['navy', 'purple', 'red']
for xy, label in zip(iris, labels):
plt.scatter(xy[0], xy[1], color = colors[label])
faces 그리기 함수
def show_faces(faces):
plt.figure()
# 2*3의 배열로 표현
num_rows, num_cols = 2, 3
# 총 6개씩 뜨게 됨
for i in range(num_rows * num_cols):
plt.subplot(num_rows, num_cols, i+1)
plt.imshow(np.reshape(faces[i], (64, 64)), cmap = plt.cm.gray)
위의 함수 사용해서 그래프 그리기
plot_iris(iris[:, :2], labels)
show_faces(faces)
1. 주성분 분석(Principal Component Analysis, PCA)
PCA를 사용해 iris 데이터 변환
150×4 크기의 데이터를 150×2 크기의 행렬로 압축
# 기존 iris 데이터의 shape
iris.shape
# 출력 결과
(150, 4)
# PCA 변환 후 iris 데이터의 shape
model = PCA(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# PCA로 변환한 iris 데이터 시각화
plot_iris(transformed_iris, labels)
PCA를 통해 학습된 각 컴포넌트(6개)
각 컴포넌트는 얼굴의 주요 특징을 나타냄
# 기존 faces 데이터의 shape
faces.shape
# 출력 결과
(400, 4096)
# PCA 변환 후 faces 데이터의 shape
model = PCA(n_components = 6, random_state = 0)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# PCA로 변환한 faces 데이터 시각화
show_faces(faces_components)
2. Incremental PCA
PCA는 SVD 알고리즘 실행을 위해 전체 학습용 데이터 셋을 메모리에 올려야 함
Incremental PCA는 학습 데이터를 미니 배치 단위로 나누어 사용
학습 데이터가 크거나 온라인으로 PCA 적용이 필요할 때 유용
model = IncrementalPCA(n_components = 2)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = IncrementalPCA(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
3. Kernel PCA
비선형적인 형태가 Kernel로 표현될 수 있음
차원 축소를 위한 복잡한 비선형 투형
model = KernelPCA(n_components = 2, kernel = 'rbf', random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = KernelPCA(n_components = 6)
model.fit(faces)
# Kernel PCA는 components_를 출력할 수 없어 오류가 발생함
faces_components = model.components_
4. Sparse PCA
PCA의 주요 단점 중 하나는 주성분들이 보통 모든 입력 변수들의 선형 결합으로 나타난다는 점
희소 주성분 분석은 몇 개의 변수들만의 선형결합으로 주성분을 나타냄으로써 이러한 단점을 극복
model = SparsePCA(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = SparsePCA(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
5. Truncated Singular Value Decomposition(Truncated SVD)
PCA는 정방 행렬에 대해서만 행렬 분해 가능
SVDs는 정방 행렬 뿐만 아니라 행과 열이 다른 행렬도 분해 가능
PCA는 밀집 행렬(Dense Matrix)에 대한 변환만 가능하지만, SVD는 희소 행렬(Sparse Matrix)에 대한 변환도 가능
전체 행렬 크기에 대해 Full SVD를 사용하는 경우는 적음
특이값이 0인 부분을 모두 제거하고 차원을 줄인 Truncated SVD를 주로 사용
model = TruncatedSVD(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = TruncatedSVD(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
6. Dictionary Learning
Sparse code를 사용하여 데이터를 가장 잘 나타내는 사전 찾기
Sparse coding은 overcomplete 기저 벡터(basis vector)(기저보다 많은 수의 함수로 프레임 표현하는 )를 기반으로 데이터를 효율적으로 표현하기 위한 개발
기저 벡터는 벡터 공간에 속하는 벡터의 집합이 선형 독립이고, 다른 모든 벡터 공간의 벡터들이 그 벡터 집합의 선형 조합으로 나타남
이웃한 픽셀들의 가능한 모든 조합으로 이미지를 재정의
픽셀 정보를 넘어서 더 풍부한 표현력으로 이미지를 설명, 인식의 정확성 향상
model = DictionaryLearning(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = DictionaryLearning(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
7. Factor Analysis
요인 분석은 변수들 간의 상관관계를 고려하여 저변에 내재된 개념인 요인들을 추출해내는 분석방법
요인 분석은 변수들 간의 상관관계를 고려하여 서로 유사한 변수들끼지 묶어주는 방법
PCA에선느 오차(error)를 고려하지 않고, 요인 분석에서는 오차(error)를 고려
model = FactorAnalysis(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = FactorAnalysis(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
8. Independent Component Analysis(ICA)
독립 성분 분석은 다변량의 신호를 통계적으로 독립적인 하부 성분으로 분리하는 계산 방법
ICA는 주성분을 이용하는 점은 PCA와 유사하지만, 데이터를 가장 잘 설명하는 축을 찾는 PCA와 달리 가장 독립적인 축, 독립성이 최대가 되는 벡터를 찾음
model = FastICA(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = FastICA(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
9. Non-negative Matrix Factorization
음수 미포함 행렬 분해는 음수를 포함하지 않은 행렬 V를 음수를 포함하지 않은 행렬 W와 H의 곱으로 분해하는 알고리즘
숫자 5를 분해하면 2+3, 1+4 등으로 분리 가능 -> 이것과 마찬가지로 행렬을 어떤 두 개의 행렬의 곱으로 분해
model = NMF(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = NMF(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
10. Latent Dirichlet Allocation(LDA)
잠재 디리클레 할당은 이산 자료들에 대한 확률적 생성 모형
디리클레 분포에 따라 잠재적인 의미 구조를 파악
디리클레 분포(Dirichlet distribution)는연속 확률분포의 하나로, k차원의 실수 벡터 중 벡터의 요소가 양수이며 모든 요소를 더한 값이 1인 경우 (이를 k−1차원단체라고 한다)에 대해 확률값이 정의되는 분포이다.
디리클레 분포는베이즈 통계학에서다항 분포에 대한사전 켤레확률이다. 이 성질을 이용하기 위해, 디리클레 분포는 베이즈 통계학에서의사전 확률로 자주 사용된다.
model = LatentDirichletAllocation(n_components = 2, random_state = 0)
model.fit(iris)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
model = LatentDirichletAllocation(n_components = 6)
model.fit(faces)
faces_components = model.components_
faces_components.shape
# 출력 결과
(6, 4096)
# 시각화
show_faces(faces_components)
11. Linear Discriminant Analysis(LDA)
LDA는 PCA와 유사하게 입력 데이터 세트를 저차원 공간에 통해 차원을 축소
LDA는 지도 학습 분류에서 사용하기 쉽도록 개별 클래스를 분별할 수 있는 기준을 최대한 유지하면서 차원 축소
정답이 있는 지도학습에서 사용하기 때문에 faces 데이터에 대해서는 사용 불가능
model = LinearDiscriminantAnalysis(n_components = 2)
# 정답인 labels를 같이 모델에 넣어 학습시켜 줘야함
model.fit(iris, labels)
transformed_iris = model.transform(iris)
transformed_iris.shape
# 출력 결과
(150, 2)
# 시각화
plot_iris(transformed_iris, labels)
12. 압축된 표현을 사용한 학습
원래의 digits 데이터와 분해(decomposition)된 digits 데이터의 cross val score 비교
- digits 데이터 및 학습 모델 라이브러리 불러오기
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import cross_val_score
# min_max 스케일링 하는 함수
def min_max_scale(x):
min_value, max_value = np.min(x, 0), np.max(x, 0)
x = (x - min_value) / (max_value - min_value)
return x
# digits 데이터 그래프 그리는 함수
def plot_digits(digits, labels):
digits = min_max_scale(digits)
ax = plt.subplot(111, projection = '3d')
for i in range(digits.shape[0]):
ax.text(digits[i, 0], digits[i, 1], digits[i, 2],
str(labels[i]), color = plt.cm.Set1(labels[i] / 10.),
fontdict = {'weight': 'bold', 'size': 9})
ax.view_init(4, -72)
# digits 데이터 불러온 뒤 NMF로 분해
# 기존 데이터와 분해된 데이터 shape 비교
digits = load_digits()
nmf = NMF(n_components = 3)
nmf.fit(digits.data)
decomposed_digits = nmf.transform(digits.data)
print(digits.data.shape)
print(decomposed_digits.shape)
print(decomposed_digits)
# 출력 결과
(1797, 64)
(1797, 3)
[[0.48392621 0. 1.24523912]
[0.5829615 1.4676756 0.07150889]
[0.61515882 1.10963207 0.387782 ]
...
[0.55272665 1.26056519 0.72094739]
[0.7872562 0.2789873 1.04952028]
[0.78507412 0.67250884 0.92677982]]
# 분해 전
knn = KNeighborsClassifier()
score = cross_val_score(
estimator = knn,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.94722222 0.95555556 0.96657382 0.98050139 0.9637883 ]
mean cross val score: 0.9627282575054161 (+/- 0.011168537355954218)
# 분해 후
knn = KNeighborsClassifier()
score = cross_val_score(
estimator = knn,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.54722222 0.58055556 0.64066852 0.59610028 0.56267409]
mean cross val score: 0.5854441349427422 (+/- 0.03214521445075084)
- SVC
# 분해 전
svm = SVC()
score = cross_val_score(
estimator = svm,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.96111111 0.94444444 0.98328691 0.98885794 0.93871866]
mean cross val score: 0.9632838130609718 (+/- 0.02008605863225686)
# 분해 후
svm = SVC()
score = cross_val_score(
estimator = svm,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.61388889 0.62222222 0.66016713 0.60167131 0.59888579]
mean cross val score: 0.6193670690188796 (+/- 0.022070024720937543)
- Decision Tree
# 분해 전
decision_tree = DecisionTreeClassifier()
score = cross_val_score(
estimator = decision_tree,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.78333333 0.69722222 0.78830084 0.83286908 0.78830084]
mean cross val score: 0.7780052615289385 (+/- 0.04421837659784472)
# 분해 후
decision_tree = DecisionTreeClassifier()
score = cross_val_score(
estimator = decision_tree,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.57222222 0.50833333 0.57938719 0.5821727 0.52924791]
mean cross val score: 0.5542726709996905 (+/- 0.0298931375955385)
- Random Forest
# 분해 전
random_forest = RandomForestClassifier()
score = cross_val_score(
estimator = random_forest,
X = digits.data, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.93888889 0.90555556 0.97214485 0.96657382 0.91086351]
mean cross val score: 0.9388053234292789 (+/- 0.02745509790638632)
# 분해 후
random_forest = RandomForestClassifier()
score = cross_val_score(
estimator = random_forest,
X = decomposed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.58888889 0.60277778 0.64066852 0.59052925 0.54874652]
mean cross val score: 0.594322191272052 (+/- 0.029463634023029848)
- 전반적으로 분해한 데이터에서 성능이 떨어진 모습
13. 복원된 표현을 사용한 학습
분해 후 복원된 행렬을 사용해 학습
- 데이터 행렬 복원
# 분해된 행렬에 곱하기 연산을 통해 원래의 행렬로 복원
components = nmf.components_
reconstructed_digits = decomposed_digits @ components
print(digits.data.shape)
print(decomposed_digits.shape)
print(reconstructed_digits.shape)
# 출력 결과
(1797, 64)
(1797, 3)
(1797, 64)
# reconstructed digits 시각화
plt.figure(figsize = (16, 8))
plt.suptitle('Re-Constructed digits')
for i in range(10):
plt.subplot(2, 5, i+1)
plt.xticks([])
plt.yticks([])
plt.imshow(reconstructed_digits[i].reshape(8, 8))
- KNN
knn = KNeighborsClassifier()
score = cross_val_score(
estimator = knn,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.54166667 0.59444444 0.66295265 0.57660167 0.57381616]
mean cross val score: 0.5898963169297431 (+/- 0.04029722337499952)
- SVM
svm = SVC()
score = cross_val_score(
estimator = svm,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.62777778 0.60555556 0.66016713 0.61002786 0.5821727 ]
mean cross val score: 0.6171402042711235 (+/- 0.025969174809053776)
- Decision Tree
decision_tree = DecisionTreeClassifier()
score = cross_val_score(
estimator = decision_tree,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.57777778 0.51666667 0.53481894 0.56824513 0.55153203]
mean cross val score: 0.5498081089445992 (+/- 0.0221279380012718)
- Random Forest
random_forest = RandomForestClassifier()
score = cross_val_score(
estimator = random_forest,
X = reconstructed_digits, y = digits.target,
cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))
# 출력 결과
[0.58055556 0.55833333 0.65181058 0.59610028 0.57660167]
mean cross val score: 0.592680284741566 (+/- 0.031916555892366645)
- 새로 복원한 데이터에서도 큰 성능 향상점은 없음
14. 이미지 복원
# faces 데이터를 train와 test 데이터로 분리
from sklearn.model_selection import train_test_split
train_faces, test_faces = train_test_split(faces, test_size = 0.1)
show_faces(train_faces)
show_faces(test_faces)
# 테스트 데이터에는 랜덤한 점에 0값을 주어 검은색의 점으로 노이즈를 생성
damaged_faces = []
for face in test_faces:
idx = np.random.choice(range(64 * 64), size = 1024)
damaged_face = face.copy()
damaged_face[idx] = 0.
damaged_faces.append(damaged_face)
show_faces(damaged_faces)
# 노이즈를 준 test 데이터를 NMF 모델에 넣어 분해하고 다시 복원하는 과정으로 노이즈 제거
# damaged_faces의 형식을 float32로 변환하는 과정 필요
damaged_faces = np.asarray(damaged_faces, dtype = np.float32)
matrix1 = nmf.transform(damaged_faces)
matrix2 = nmf.components_
show_faces(matrix1 @ matrix2)
# 분해 components를 조절하여 정교함 조절 가능, 더 높은 수로 분해할수록 더 정교하게 복원할 수 있음
nmf = NMF(n_components = 100)
nmf.fit(train_faces)
matrix1 = nmf.transform(damaged_faces)
matrix2 = nmf.components_
show_faces(matrix1 @ matrix2)
차원 축소 과정에서 중요하지 않은 정보는 버려지고 중요한 정보만 남기 대문에 데이터 정제에 활용 가능
데이터 생성 및 시각화 함수
# 필요 라이브러리
import numpy as np
import matplotlib.pyplot as plt
from sklearn import manifold
from sklearn import random_projection
from sklearn import datasets
# s_curve 데이터셋 생성
s_curve, color = datasets.make_s_curve(1000, random_state = 0)
# 손글씨 데이터셋 생성
digits, labels = datasets.load_digits(return_X_y = True)
# projection으로 만들기
rand_proj = random_projection.SparseRandomProjection(n_components = 3, random_state = 0)
projected_digits =rand_proj.fit_transform(digits)
# min_max_scale 하는 함수
def min_max_scale(x):
min_value, max_value = np.min(x, 0), np.max(x, 0)
x = (x - min_value) / (max_value - min_value)
return x
# s_curve 시각화 함수
def plot_s_curve(s_curve, color, position, projection):
s_curve = min_max_scale(s_curve)
if projection == '3d':
ax = plt.subplot(position, projection = projection)
ax.scatter(s_curve[:, 0], s_curve[:, 1], s_curve[:, 2], c = color, cmap = plt.cm.Spectral)
ax.view_init(4, -72)
elif projection == '2d':
ax = plt.subplot(position)
ax.scatter(s_curve[:, 0], s_curve[:, 1], c = color, cmap = plt.cm.Spectral)
# 손글씨 시각화 함수
def plot_digits(digits, labels, position, projection):
digits = min_max_scale(digits)
if projection == '3d':
ax = plt.subplot(position, projection = projection)
for i in range(digits.shape[0]):
ax.text(digits[i, 0], digits[i, 1], digits[i, 2], str(labels[i]),
color = plt.cm.Set1(labels[i] / 10.), fontdict = {'weight': 'bold', 'size': 9})
ax.view_init(4, -72)
elif projection == '2d':
ax = plt.subplot(position)
for i in range(digits.shape[0]):
ax.text(digits[i, 0], digits[i, 1], str(labels[i]),
color = plt.cm.Set1(labels[i] / 10.), fontdict = {'weight': 'bold', 'size': 9})
- 데이터 시각화
# 작성한 함수를 사용하여 s_curve와 손글씨 데이터를 각각 시각화(3차원으로)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve, color, 121, '3d')
plot_digits(projected_digits, labels, 122, '3d')
# 작성한 함수를 사용하여 s_curve와 손글씨 데이터를 각각 시각화(2차원으로)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve, color, 121, '2d')
plot_digits(projected_digits, labels, 122, '2d')
쿨백-라이블러 발산(Kullback–Leibler divergence,KLD)은 두 확률분포의 차이를 계산하는 데에 사용하는 함수로, 어떤 이상적인 분포에 대해, 그 분포를 근사하는 다른 분포를 사용해 샘플링을 한다면 발생할 수 있는정보 엔트로피차이를 계산, 상대 엔트로피(relative entropy), 정보 획득량(information gain), 인포메이션 다이버전스(information divergence)라고도 한다.
import numpy as np
import matplotlib.pyplot as plt
from sklearn import cluster
from sklearn import mixture
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
XGBoost는 GBM 기반이지만, GBM의 단점인 느린 수행 시간과 과적합 규제 부재 등의 문제를 해결
병렬 CPU 환경에서 빠르게 학습 가능
필요 라이브러리
from sklearn.datasets import load_iris, load_breast_cancer, load_wine, load_diabetes
from sklearn.model_selection import train_test_split, cross_validate
from sklearn.metrics import accuracy_score, precision_score, recall_score
import xgboost as xgb
from xgboost import XGBClassifier, XGBRegressor
from xgboost import plot_importance, plot_tree
import graphviz
import matplotlib.pyplot as plt
1. 파이썬 기반 XGBoost
- 유방암 데이터로 연습
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, test_size = 0.2, random_state = 42)
# XGBoost는 XGBoost만의 데이터 형식인 DMatrix 형식을 사용하므로 다음과 같이 train과 test 데이터를 변환
dtrain = xgb.DMatrix(data = X_train, label = y_train)
dtest = xgb.DMatrix(data = X_test, label = y_test)
# 파라미터
params = {
'max_depth': 3,
'eta': 0.1,
'objective': 'binary:logistic',
'eval_metric': 'logloss',
'early_stopping': 100
}
num_rounds = 400
# 모델 생성
evals = [(dtrain, 'train'), (dtest, 'eval')]
xgb_model = xgb.train(params = params, dtrain = dtrain, num_boost_round = num_rounds, early_stopping_rounds = 100, evals = evals)
- xgb_model 생성 결과, logloss가 더 이상 감소되지 않을 때까지 모델 최적화
- 계속 감소한다면 사전에 정해둔 round 횟수(num_rounds = 400)만큼만 진행한 뒤 모델 생성 종료
- 가장 마지막 모델로 생성
- train 데이터로 생성한 모델에 test 데이터를 넣어 예측값 출력(상위 10개만)
import numpy as np
predicts = xgb_model.predict(dtest)
print(np.round(predicts[:10], 3))
- 정확도, 정밀도, 재현율 출력
# 데이터 생성 결과를 이원화(0 또는 1로)하여 2*2 테이블 상에서 정확도, 정밀도, 재현율을 계산할 수 있도록 변경
preds = [1 if x > 0.5 else 0 for x in predicts]
print(preds[:10])
print("정확도: {}".format(accuracy_score(y_test, preds)))
print("정밀도: {}".format(precision_score(y_test, preds)))
print("재현율: {}".format(recall_score(y_test, preds)))
# 출력 결과
정확도: 0.9736842105263158
정밀도: 0.9722222222222222
재현율: 0.9859154929577465
일반화와 강건성(Robustness)을 향상시키기 위해 여러 모델의 예측 값을 결합하는 방법
앙상블의 종류
평균 방법
여러 추정값을 독립적으로 구한 뒤 평균을 취함
결합 추정값은 분산이 줄어들어 단일 추정값보다 좋은 성능
부스팅 방법
순차적으로 모델 생성
결합된 모델의 편향을 감소시키기 위해 노력
부스팅 방법의 목표는 여러 개의 약한 모델들을 결합해 하나의 강력한 앙상블 모델을 구축하는 것
1. Bagging meta-estimator
bagging은 bootstrap arregatng의 줄임말
원래 훈련 데이터셋의 일부를 사용해 여러 모델을 훈련
각각의 결과를 결합해 최종 결과를 생성
분산을 줄이고 과적합을 막음
강력하고 복잡한 모델에서 잘 동작
- 사용 라이브러리 및 연습용 데이터 불러오기
from sklearn.datasets import load_iris, load_wine, load_breast_cancer, load_diabetes
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_validate
from sklearn.ensemble import BaggingClassifier, BaggingRegressor
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
from sklearn.svm import SVC, SVR
from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor
iris = load_iris()
wine = load_wine()
cancer = load_breast_cancer()
diabetes = load_diabetes()
- KNeighborsClassifier에서 일반 모델과 bagging 모델 비교
# 기본 모델 파이프라인 생성
base_model = make_pipeline(
StandardScaler(),
KNeighborsClassifier()
)
# bagging 모델 분류기 생성
bagging_model = BaggingClassifier(base_model, n_estimators = 10, max_samples = 0.5, max_features = 0.5)
# 일반 모델 파이프라인을 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = base_model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.0015646457672119141 (+/- 0.00046651220506644384)
avg score time: 0.0024621009826660155 (+/- 0.0007703127175682573)
avg test score: 0.9493650793650794 (+/- 0.037910929811115976)
# bagging 모델 분류기를 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = bagging_model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.01585836410522461 (+/- 0.001614824165009782)
avg score time: 0.004801464080810547 (+/- 0.0011524532393675253)
avg test score: 0.9496825396825397 (+/- 0.02695890511274157)
- 일반 모델 파이프라인과 bagging 모델 분류기 간의 avg test score의 차이가 없음
- SVC에서 일반 모델과 bagging 모델 비교
# 기본 모델 파이프라인 생성
base_model = make_pipeline(
StandardScaler(),
SVC()
)
# bagging 모델 분류기 생성
bagging_model = BaggingClassifier(base_model, n_estimators = 10, max_samples = 0.5, max_features = 0.5)
# 일반 모델 파이프라인을 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = base_model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.0019928932189941405 (+/- 0.0006299217280194511)
avg score time: 0.0007997512817382813 (+/- 0.0003998954564704184)
avg test score: 0.9833333333333334 (+/- 0.022222222222222233)
# bagging 모델 분류기를 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = bagging_model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.03082098960876465 (+/- 0.010328177445939042)
avg score time: 0.003788471221923828 (+/- 0.0007431931234207308)
avg test score: 0.9495238095238095 (+/- 0.03680897280161461)
- bagging 모델 분류기에서 avg test score가 일반 모델보다 더 감소함
- DecisionTreeClassifier에서 일반 모델과 bagging 모델 비교
# 기본 모델 파이프라인 생성
base_model = make_pipeline(
StandardScaler(),
DecisionTreeClassifier()
)
# bagging 모델 분류기 생성
bagging_model = BaggingClassifier(base_model, n_estimators = 10, max_samples = 0.5, max_features = 0.5)
# 일반 모델 파이프라인을 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = base_model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.0016411781311035157 (+/- 0.0005283725076633593)
avg score time: 0.0005584716796875 (+/- 0.00046257629871031806)
avg test score: 0.8709523809523809 (+/- 0.04130828490281938)
# bagging 모델 분류기를 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = bagging_model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.025203371047973634 (+/- 0.006259195949988035)
avg score time: 0.0019852161407470704 (+/- 0.0006092217277319406)
avg test score: 0.9665079365079364 (+/- 0.032368500562618134)
- bagging 모델 분류기에서 avg test score가 일반 모델보다 더 증가함
- KNeighborsRegressor에서 일반 모델과 bagging 모델 비교
# 기본 모델 파이프라인 생성
base_model = make_pipeline(
StandardScaler(),
KNeighborsRegressor()
)
# bagging 모델 분류기 생성
bagging_model = BaggingClassifier(base_model, n_estimators = 10, max_samples = 0.5, max_features = 0.5)
# 일반 모델 파이프라인을 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = base_model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))# 출력 결과
# 출력 결과
avg fit time: 0.0011986732482910157 (+/- 0.0003976469573187139)
avg score time: 0.0011948585510253907 (+/- 0.0004032221568088893)
avg test score: 0.3689720650295623 (+/- 0.044659049060165365)
# bagging 모델 분류기를 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = bagging_model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))# 출력 결과
# 출력 결과
avg fit time: 0.01416783332824707 (+/- 0.0021502472754025685)
avg score time: 0.005353689193725586 (+/- 0.0005163540544834088)
avg test score: 0.4116223973880059 (+/- 0.039771045284647706)
- bagging 모델 분류기에서 avg test score가 일반 모델보다 더 증가함
- SVR에서 일반 모델과 bagging 모델 비교
# 기본 모델 파이프라인 생성
base_model = make_pipeline(
StandardScaler(),
SVR()
)
# bagging 모델 분류기 생성
bagging_model = BaggingClassifier(base_model, n_estimators = 10, max_samples = 0.5, max_features = 0.5)
# 일반 모델 파이프라인을 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = base_model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.005189418792724609 (+/- 0.00039828050210851215)
avg score time: 0.0029881954193115234 (+/- 4.301525777064362e-05)
avg test score: 0.14659868748701582 (+/- 0.021908831719954277)
# bagging 모델 분류기를 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = bagging_model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))# 출력 결과
# 출력 결과
avg fit time: 0.030098438262939453 (+/- 0.004244103777765697)
avg score time: 0.014351558685302735 (+/- 0.002644062031057098)
avg test score: 0.06636804266664734 (+/- 0.026375606251683278)
- bagging 모델 분류기에서 avg test score가 일반 모델보다 더 감소함
- DecisionTreeRegressor에서 일반 모델과 bagging 모델 비교
# 기본 모델 파이프라인 생성
base_model = make_pipeline(
StandardScaler(),
DecisionTreeRegressor()
)
# bagging 모델 분류기 생성
bagging_model = BaggingClassifier(base_model, n_estimators = 10, max_samples = 0.5, max_features = 0.5)
# 일반 모델 파이프라인을 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = base_model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.0030806541442871095 (+/- 0.0005084520543164849)
avg score time: 0.0005962371826171875 (+/- 0.0004868346584328474)
avg test score: -0.10402518188546787 (+/- 0.09614122612623877)
# bagging 모델 분류기를 사용하여 분류한 결과 정확도 출력
cross_val = cross_validate(
estimator = bagging_model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.028301239013671875 (+/- 0.009722365434744872)
avg score time: 0.001835775375366211 (+/- 0.0003816480339082081)
avg test score: 0.3206163477240517 (+/- 0.06240931175651393)
- bagging 모델 분류기에서 avg test score가 일반 모델보다 더 증가함
- 데이터에 따라 다르지만 bagging 모델이 일반 모델보다 좋은 성능을 보임
2. Forests of randomized trees
sklean.ensemble 모듈에는 무작위 결정 트리를 기반으로 하는 두 개의 평균화 알고리즘이 존재
Random Forest
Extra-Trees
모델 구성에 임의성을 추가해 다양한 모델 집합 생성
앙상블 모델의 예측은 각 모델의 평균
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier
- Random Forests 분류
# 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
RandomForestClassifier()
)
# 붓꽃 데이터
cross_val = cross_validate(
estimator = model,
X = iris.data, y = iris.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.18105216026306153 (+/- 0.01685682915874488)
avg score time: 0.013797760009765625 (+/- 0.0025621248659456705)
avg test score: 0.96 (+/- 0.024944382578492935)
# 와인 데이터
cross_val = cross_validate(
estimator = model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.22976922988891602 (+/- 0.028256732778271565)
avg score time: 0.01587204933166504 (+/- 0.0023274044664123553)
avg test score: 0.9665079365079364 (+/- 0.032368500562618134)
# 유방암 데이터
cross_val = cross_validate(
estimator = model,
X = cancer.data, y = cancer.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.2917177200317383 (+/- 0.045315794078103835)
avg score time: 0.017597723007202148 (+/- 0.006803860063290489)
avg test score: 0.9578326346840551 (+/- 0.02028739541529243)
- Random Forests 회귀
# 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
RandomForestRegressor()
)
# 당뇨병 데이터
cross_val = cross_validate(
estimator = model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.38582072257995603 (+/- 0.05492288277511406)
avg score time: 0.01399979591369629 (+/- 0.0017906758222056265)
avg test score: 0.41577582207684943 (+/- 0.04029500816412448)
- Extremely Randomized Trees 분류
# 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
ExtraTreesClassifier()
)
# 붓꽃 데이터
cross_val = cross_validate(
estimator = model,
X = iris.data, y = iris.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.12993454933166504 (+/- 0.013365132836320116)
avg score time: 0.015802621841430664 (+/- 0.0026369354952103644)
avg test score: 0.9533333333333334 (+/- 0.03399346342395189)
# 와인 데이터
cross_val = cross_validate(
estimator = model,
X = cancer.data, y = cancer.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.14599318504333497 (+/- 0.027877641431797804)
avg score time: 0.013452291488647461 (+/- 0.0019263323264865461)
avg test score: 0.9776190476190475 (+/- 0.020831783767013237)
# 유방암 데이터
cross_val = cross_validate(
estimator = model,
X = cancer.data, y = cancer.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.15427541732788086 (+/- 0.01803722986834076)
avg score time: 0.014791202545166016 (+/- 0.0007367600639162756)
avg test score: 0.9683744760130415 (+/- 0.010503414750935476)
- Extremely Randomized Trees 회귀
# 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
ExtraTreesRegressor()
)
# 당뇨병 데이터
cross_val = cross_validate(
estimator = model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.21371040344238282 (+/- 0.029887789885253244)
avg score time: 0.011214303970336913 (+/- 0.0022113466437151)
avg test score: 0.4334859913753323 (+/- 0.037205109491594564)
- Random Forest, Extra Tree 시각화
결정 트리, Random Forest, Extra Tree의 결정 경계와 회귀식 시각화
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn.tree import DecisionTreeClassifier
# 기본 파라미터
n_classes = 3
n_estimators = 30
cmap = plt.cm.RdYlBu
plot_step = 0.02
plot_step_coarser = 0.5
RANDOM_SEED = 13
iris = load_iris()
plot_idx = 1
# 세 가지 모델을 for문에 넣어서 각 모델에 대한 예측 결과를 출력
models = [DecisionTreeClassifier(max_depth = None),
RandomForestClassifier(n_estimators = n_estimators),
ExtraTreesClassifier(n_estimators = n_estimators)]
plt.figure(figsize = (16, 8))
for pair in ([0, 1], [0, 2], [2, 3]):
# 위에서 지정한 세가지 모델에 대해 각각 예측
for model in models:
X = iris.data[:, pair]
y = iris.target
# 독립변수의 개수만큼 인덱스 배열 생성
idx = np.arange(X.shape[0])
# 랜덤시드 설정하고 랜덤으로 인덱스 셔플링(필요 x)
np.random.seed(RANDOM_SEED)
np.random.shuffle(idx)
X = X[idx]
y = y[idx]
# 평균과 표준편차를 계산하여 데이터 정규화
mean = X.mean(axis = 0)
std = X.std(axis = 0)
X = (X-mean) / std
# 위에서 정규화한 X와 y값을 모델에 피팅
model.fit(X, y)
# 모델 제목은 모델의 타입을 "."으로 분리하여
# ["<class 'sklearn", 'tree', '_classes', "DecisionTreeClassifier'>"]
# [-1]은 리스트의 가장 마지막 부분 추출("DecisionTreeClassifier'>")
# [:-2]는 위의 문자열의 끝에서 두번째 문자전까지만 추출("DecisionTreeClassifier")
# 마지막으로 해당 문자열에서 Classifier을 제외하기 위해 Classifier의 문자열 길이 전까지만 추출
model_title = str(type(model)).split(".")[-1][:-2][:-len("Classifier")]
# 3 * 3의 그래프 배열에서 plot_idx만큼(plot_idx는 모델 한 번 돌때마다 변경됨)
plt.subplot(3, 3, plot_idx)
if plot_idx <= len(models):
plt.title(model_title, fontsize = 9)
# meshgrid 생성
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),
np.meshgrid(np.arange(y_min, y_max, plot_step)))
# DecisionTreeClassifier 모델일 때와 나머지 모델일 때 구분
if isinstance(model, DecisionTreeClassifier):
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap = cmap)
# 나머지 모델일 때는 alpha값을 주어 투명도 추가
else:
estimator_alpha = 1.0 / len(model.estimators_)
for tree in model.estimators_:
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, alpha = estimator_alpha, cmap = cmap)
xx_coarser, yy_coarser = np.meshgrid(np.arange(x_min, x_max, plot_step_coarser),
np.arange(y_min, y_max, plot_step_coarser))
Z_points_coarser = model.predict(np.c_[xx_coarser.ravel(),
yy_coarser.ravel()]).reshape(xx_coarser.shape)
cs_points = plt.scatter(xx_coarser, yy_coarser, s = 15, c = Z_points_coarser, cmap = cmap, edgecolor = 'none')
plt.scatter(X[:, 0], X[:, 1], c = y, cmap = ListedColormap(['r','y','b']), edgecolor = 'k', s = 20)
plot_idx += 1
# 그래프 최상단 제목 설정
plt.suptitle("Classifiers", fontsize = 12)
plt.axis('tight')
plt.tight_layout(h_pad = 0.2, w_pad = 0.2, pad = 2.5)
plt.show()
plot_idx = 1
models = [DecisionTreeRegressor(max_depth = None),
RandomForestRegressor(n_estimators = n_estimators),
ExtraTreesRegressor(n_estimators = n_estimators)]
plt.figure(figsize = (16, 8))
for pair in (0, 1, 2):
for model in models:
X = diabetes.data[:, pair]
y = diabetes.target
idx = np.arange(X.shape[0])
np.random.seed(RANDOM_SEED)
np.random.shuffle(idx)
X = X[idx]
y = y[idx]
mean = X.mean(axis = 0)
std = X.std(axis = 0)
X = (X-mean) / std
model.fit(X.reshape(-1, 1), y)
model_title = str(type(model)).split(".")[-1][:-2][:-len('Regreffor')]
plt.subplot(3, 3, plot_idx)
if plot_idx <= len(models):
plt.title(model_title, fontsize = 9)
x_min, x_max = X.min() -1, X.max() + 1
y_min, y_max = y.min() -1, y.max() + 1
xx, yy = np.arange(x_min - 1, x_max + 1, plot_step), np.arange(y_min - 1, y_max + 1, plot_step)
if isinstance(model, DecisionTreeRegressor):
Z = model.predict(xx.reshape(-1, 1))
cs = plt.plot(xx, Z)
else:
estimator_alpha = 1.0 / len(model.estimators_)
for tree in model.estimators_:
Z = tree.predict(xx.reshape(-1, 1))
cs = plt.plot(xx, Z, alpha = estimator_alpha)
plt.scatter(X, y, edgecolors = 'k', s = 20)
plot_idx += 1
plt.suptitle("Regressor", fontsize = 12)
plt.axis('tight')
plt.tight_layout(h_pad = 0.2, w_pad = 0.2, pad = 2.5)
plt.show()
3. AdaBoost
대표적인 부스팅 알고리즘
일련의 약한 모델들을 학습
수정된 버전의 데이터를 (가중치가 적용된)반복 학습
가중치 투표(또는 합)을 통해 각 모델의 예측값을 결합
첫 단계에서는 원본 데이터를 학습하고 연속적인 반복마다 개별 샘플에 대한 가중치가 수정되고 다시 모델이 학습
잘못 예측된 샘플은 가중치가 증가, 올바르게 예측된 샘플은 가중치 감소
각각의 약한 모델들은 예측하기 어려운 샘플에 집중하게 됨
- AdaBoostClassifier
from sklearn.ensemble import AdaBoostClassifier, AdaBoostRegressor
# 기본 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
AdaBoostClassifier()
)
# 붓꽃 데이터
cross_val = cross_validate(
estimator = model,
X = iris.data, y = iris.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.10685276985168457 (+/- 0.0251679732383264)
avg score time: 0.012082815170288086 (+/- 0.0029404438972186714)
avg test score: 0.9466666666666667 (+/- 0.03399346342395189)
# 와인 데이터
cross_val = cross_validate(
estimator = model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.12471566200256348 (+/- 0.022184160168356563)
avg score time: 0.010206842422485351 (+/- 0.0014569535430837709)
avg test score: 0.8085714285714285 (+/- 0.16822356718459935)
# 유방암 데이터
cross_val = cross_validate(
estimator = model,
X = cancer.data, y = cancer.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.21773457527160645 (+/- 0.036282505551639664)
avg score time: 0.01280508041381836 (+/- 0.006666702833190784)
avg test score: 0.9701133364384411 (+/- 0.019709915473893072)
- AdaBoostRegressor
from sklearn.ensemble import AdaBoostClassifier, AdaBoostRegressor
# 기본 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
AdaBoostRegressor()
)
# 당뇨병 데이터
cross_val = cross_validate(
estimator = model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.0806793212890625 (+/- 0.019655713051304806)
avg score time: 0.004206609725952148 (+/- 0.0017171964676314028)
avg test score: 0.4325980577698525 (+/- 0.046753912177138576)
4. Gradient Tree Boosting
임의의 차별화 가능한 손실함수로 일반화한 부스팅 알고리즘
웹 검색, 분류 및 회귀 등 다양한 분야에서 모두 사용 가능
- GradientBoostingClassifier
from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor
# 기본 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
GradientBoostingClassifier()
)
# 붓꽃 데이터
cross_val = cross_validate(
estimator = model,
X = iris.data, y = iris.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.28792128562927244 (+/- 0.08891755551147741)
avg score time: 0.0009984493255615235 (+/- 1.4898700510940572e-05)
avg test score: 0.9666666666666668 (+/- 0.02108185106778919)
# 와인 데이터
cross_val = cross_validate(
estimator = model,
X = wine.data, y = wine.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.557232141494751 (+/- 0.11776815518280624)
avg score time: 0.001400327682495117 (+/- 0.0004905852751874076)
avg test score: 0.9385714285714286 (+/- 0.032068206474093704)
# 유방암 데이터
avg fit time: 0.5082685947418213 (+/- 0.05271622631546759)
avg score time: 0.0009903907775878906 (+/- 9.857966772333804e-06)
avg test score: 0.9596180717279925 (+/- 0.02453263202329889)
- GradientBoostingRegressor
from sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressor
# 기본 모델 파이프라인 생성
model = make_pipeline(
StandardScaler(),
GradientBoostingRegressor()
)
# 당뇨병 데이터
cross_val = cross_validate(
estimator = model,
X = diabetes.data, y = diabetes.target,
cv = 5
)
print("avg fit time: {} (+/- {})".format(cross_val['fit_time'].mean(), cross_val['fit_time'].std()))
print("avg score time: {} (+/- {})".format(cross_val['score_time'].mean(), cross_val['score_time'].std()))
print("avg test score: {} (+/- {})".format(cross_val['test_score'].mean(), cross_val['test_score'].std()))
# 출력 결과
avg fit time: 0.12577242851257325 (+/- 0.01002747346482568)
avg score time: 0.0013751983642578125 (+/- 0.00048391264033975266)
avg test score: 0.40743256738384626 (+/- 0.06957393690515927)
5. 투표 기반 분류(Voting Classifier)
서로 다른 모델들의 결과를 투표를 통해 결합
두가지 방법으로 투표 가능
가장 많이 예측된 클래스를 정답으로 채택(hard voting)
예측된 확률의 가중치 평균(soft voting)
- hard 보팅
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier
from sklearn.model_selection import cross_val_score
model1 = SVC()
model2 = GaussianNB()
model3 = RandomForestClassifier()
vote_model = VotingClassifier(
estimators = [('svc', model1), ('naive', model2), ('forest', model3)],
voting = 'hard'
)
for model in (model1, model2, model3, vote_model):
model_name = str(type(model)).split(".")[-1][:-2]
scores = cross_val_score(model, iris.data, iris.target, cv = 5)
print('accurancy: %0.2f (+/- %0.2f) [%s]' % (scores.mean(), scores.std(), model_name))
# 출력 결과
accurancy: 0.97 (+/- 0.02) [SVC]
accurancy: 0.95 (+/- 0.03) [GaussianNB]
accurancy: 0.96 (+/- 0.02) [RandomForestClassifier]
accurancy: 0.96 (+/- 0.02) [VotingClassifier]