1. 텐서(Tensor)

  • 일반적으로 텐서는 3차원 이상을 다룰 때 표현하는 방식이지만, 여기서는 어떠한 데이터를 표현할 때, 그 값 모두를 텐서라고 부르기로 함
  • 아래에서 a, b, c, d 모두 텐서라고 지칭할 수 있

  • 랭크(rank): 텐서의 축을 나타내고, 넘파이(numpy)의 ndim(number of dimension, 차원의 수)이라는 속성값으로 구할 수 있음
  • 대괄호의 개수가 곧 랭크(축)의 값

  • 크기(shape): 텐서의 각 축을 따라 얼마나 많은 차원이 있는지를 나타내며, 파이썬의 튜플 형태

 

  - 스칼라(0차원 텐서)

  • 하나의 숫자를 담고 있는 텐서
  • 형상은 없음
x = np.array(3)
print(x)
print(x.shape)
print(np.ndim(x))

# 출력 결과
3
()
0

 

  - 벡터(1차원 텐서)

  • 숫자의 배열을 나타내는 텐서
x = np.array([1, 2, 3, 4])
print(x)
print(x.shape)
print(np.ndim(x))

# 출력 결과
[1 2 3 4]
(4,)
1

 

  - 벡터의 합

  • 같은 형상(shape)일 때, 각 원소별로 계산
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
c = a + b
print(a)
print(b)
print(c)
print(c.shape)
print(np.ndim(c))

# 출력 결과
[1 2 3 4]
[5 6 7 8]
[ 6  8 10 12]
(4,)
1

 

  - 벡터의 곱

  • \(A=(x_{1}, x_{2}, x_{3}, \cdots, x_{n})\)
    \(B=(y_{1}, y_{2}, y_{3}, \cdots, y_{n})\) 일 때,
  • 원소곱
    • 같은 형상(shape)일 때, 각 원소별로 계산
      \(A\times B=(x_{1}, x_{2}, x_{3}, \cdots, x_{n})\times (y_{1}, y_{2}, y_{3}, \cdots, y_{n})\)
      \(b\;\qquad=(x_{1}y_{1}, x_{2}y_{2}, x_{3}y_{3}, \cdots, x_{n}y_{n})\)
  • 벡터곱(product, dot)
    • 두 1차원 벡터가 있을 때 각각의 성분끼리의 곱을 모두 더하는 계산

$$ A\bullet B\Rightarrow A \times B^{T}=(x_{1}, x_{2}, x_{3}, \cdots x_{n})\begin{pmatrix}
y_{1} \\
y_{2}  \\
y_{3}  \\
\vdots \\
y_{n}  \\
\end{pmatrix} \\=(x_{1}y_{1} + x_{2}y_{2} + x_{3}y_{3} + \cdots + x_{n}y_{n}$$

# 원소곱
a = np.array([1, 2, 3, 4])
b = np.array([5, 6, 7, 8])
c = a*b
print(c)
print(c.shape)
print(np.ndim(c))
print("-------------------------")
# 벡터곱
x = np.array([1, 2, 0])
y = np.array([0, 2, 1])
z = np.dot(x, y)
print(z)
print(z.shape)
print(np.ndim(z))

# 출력 결과
[ 5 12 21 32]
(4,)
1
-------------------------
4
()
0

 

  - 스칼라와 벡터의 곱

# 스칼라
a = np.array(10)
# 벡터
b = np.array([1, 2, 3])
print(a*b)

# 출력 결과
[10 20 30]

 

 

2. 2차원 텐서(행렬)

  • 2차원 텐서는 행렬로 생각할 수 있음
    • (m\(\times\)n)형상의 배열

matrix = np.array([[1, 2, 3],
                   [4, 5, 6]])
print(matrix)
print(matrix.shape)
print(np.ndim(matrix))
print("-------------------------")
matrix2 = np.array([[1, 2, 3, 4]])
print(matrix2)
print(matrix2.shape)
print(np.ndim(matrix2))

# 출력 결과
[[1 2 3]
 [4 5 6]]
(2, 3)
2
-------------------------
[[1 2 3 4]]
(1, 4)
2

 

  - 행렬 원소곱

  • 같은 형상(shape)일 때 덧셈, 곱셈과 같은 연산은 원소별로 진행
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)

 

 

3. 3차원 텐서

  • 보통 이미지를 나타낼 때 사용되는 텐서
    • (width, height, channels)
    • 일반적으로 Numpy Array로 표현

red channel, blue channel, green channel에 있는 값들이 모여 고양이 이미지가 됨

  • 시계열 데이터 또는 시퀀스(sequence) 데이터를 표현할 때도 사용
    • (samples, timesteps, features)
    • (예시) 주식 가격 데이터셋, 시간에 따른 질병 발병 건수
X = np.array([[[5, 3, 2, 1],
               [5, 5, 3, 1],
               [6, 1, 2, 3]],
              [[1, 1, 1, 1],
               [3, 4, 7, 5],
               [1, 8, 3, 4]],
               [[10, 9, 3, 9],
                [5, 4, 3, 2],
                [7, 6, 3, 4]]])

print("X\n", X, end = '\n\n')
print("X.shape:", X.shape)
print("X.ndim:", X.ndim)

# 출력 결과
X
 [[[ 5  3  2  1]
  [ 5  5  3  1]
  [ 6  1  2  3]]

 [[ 1  1  1  1]
  [ 3  4  7  5]
  [ 1  8  3  4]]

 [[10  9  3  9]
  [ 5  4  3  2]
  [ 7  6  3  4]]]

X.shape: (3, 3, 4)
X.ndim: 3
B = np.array([[[2, 3, 4],[2, 3, 4]],
              [[1, 1, 1], [1, 1, 1]]])

print("행렬 B\n", B, end = '\n\n')
print("B의 전치행렬\n", B.T)

# 출력 결과
행렬 B
 [[[2 3 4]
  [2 3 4]]

 [[1 1 1]
  [1 1 1]]]

B의 전치행렬
 [[[2 1]
  [2 1]]

 [[3 1]
  [3 1]]

 [[4 1]
  [4 1]]]

 

  - 3차원 텐서 활용 예시(이미지)

  • MNIST Dataset
  • 28\(\times\)28 사이즈의 gray scale 이미지들로 구성
  • gray scale: 0~255의 값을 통해 밝기를 포현, 0으로 갈수록 어두워지고, 255로 갈수록 밝아짐
# %pip install keras
# %pip install tensorflow
from keras.datasets import mnist

(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
print(train_images.ndim)
print(train_images.shape)
print(train_images.dtype)

# 출력 결과
3
(60000, 28, 28)
uint8
temp_image = train_images[3]
plt.imshow(temp_image, cmap = 'gray')
plt.show()

 

 

4. 브로드캐스팅(broadcasting)

  • 넘파이에서 다른 형상(shape)끼리 계산 가능

  • 더 작은 형상(shape)이 형상이 더 큰 배열에 확장이 가능해야함

  • (참고) 아래의 경우도 가능

# 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,)

 

 

5. 4, 5차원 텐서

  • Color Image Datasets(4차원)
    • (samples, heigth, width, channels) (Keras, Tensorflow)
    • (samples, channels, height, width) (Pytorch)
  • 동영상(5차원)
    • (samples, frames, heigth, width, channels) (Keras, Tensorflow)
    • (samples, frames, channels, height, width) (Pytorch)
    • 예시 1) (4, 300, 1920, 1080, 3) → 1920×1080 사이즈 3채널의 300프레임 수를 가진 배치가 4

 

 

6. 텐서 크기 변환

  • reshape으로 텐서의 크기 변환 가능
  • 변환 전의 원소의 개수와 변환 이후의 텐서의 개수가 같아야 함
A = np.array([[1, 2, 3], [4, 5, 6]])
print("행렬 A\n", A)
print("A.shape:", A.shape)
print("-------------------------")
# 그냥 1차원의 배열로 쭉 나열
A = A.reshape(6)
print("행렬 A\n", A)
print("A.shape:", A.shape)

# 출력 결과
행렬 A
 [[1 2 3]
 [4 5 6]]
A.shape: (2, 3)
-------------------------
행렬 A
 [1 2 3 4 5 6]
A.shape: (6,)
B = np.array([[[2, 3, 4], [2, 3, 4]],
              [[1, 1, 1], [1, 1, 1]]])
print("행렬 B\n", B)
print("B.shape:", B.shape)
print("-------------------------")
# 2*2*3 배열에서 3*4 배열로 변환
B = B.reshape(3, 4)
print("행렬 B\n", B)
print("B.shape:", B.shape)

# 출력 결과
행렬 B
 [[[2 3 4]
  [2 3 4]]

 [[1 1 1]
  [1 1 1]]]
B.shape: (2, 2, 3)
-------------------------
행렬 B
 [[2 3 4 2]
 [3 4 1 1]
 [1 1 1 1]]
B.shape: (3, 4)
  • -1을 통해 자동으로 형상을 지정 가능
  • 원소의 개수에 맞게 넘파이가 자동으로 형상을 지정
    • (2, 2, 3) → (3, -1) (o)
                     → (2, 1, 6) (o)
                     → (2, -1, -1) (x)
                     → (2, 5, -1) (x)
B = np.array([[[2, 3, 4], [2, 3, 4]],
              [[1, 1, 1], [1, 1, 1]]])
print("행렬 B\n", B)
print("B.shape:", B.shape)
print("-------------------------")
# 행만 4로 지정하고 열은 넘파이가 자동으로 계산하여 4*3 배열로 변환
B = B.reshape(4, -1)
print("행렬 B\n", B)
print("B.shape:", B.shape)

# 출력 결과
행렬 B
 [[[2 3 4]
  [2 3 4]]

 [[1 1 1]
  [1 1 1]]]
B.shape: (2, 2, 3)
-------------------------
행렬 B
 [[2 3 4]
 [2 3 4]
 [1 1 1]
 [1 1 1]]
B.shape: (4, 3)
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

 

  - 역함수 관계

  • \(y\)\(=\)\(x\) 대칭
# 지수 함수
x1 = np.arange(-1, 5, 0.01)
y1 = np.exp(x1)

# 로그 함수
x2 = np.arange(0.000001, 5, 0.01)
y2 = np.log(x2)

# y=x의 직선
y3 = x1

plt.plot(x1, y1, 'r-', x2, y2, 'b-', x1, y3, 'k--')

plt.ylim(-2, 6)
plt.axvline(x = 0, color = 'k')
plt.axhline(y = 0, color = 'k')

plt.xlabel('x')
plt.ylabel('y')
plt.show()

 

7. 함수 조작

  • \(y=-log_{a}(x)\)와 \(y=-log_{a}(1-x)\)
    \(x=0.5\) 대칭
  • Logistic Regression을 위한 함수
x = np.arange(-10, 10, 0.01)
y1 = -np.log(x)
y2 = -np.log(1-x)

plt.axvline(x = 0, color = 'k')
plt.axhline(y = 0, color = 'k')

plt.grid()
plt.plot(x, y1, 'b-', x, y2, 'r-')
plt.text(0.9, 2.0, 'y=-log(1-x)', fontdict={'size': 15})
plt.text(0.1, 3, 'y=-log(x)', fontdict={'size': 15})
plt.xlim(-0.3, 1.4)
plt.ylim(-0.5, 4)
plt.scatter(0.5, -np.log(0.5))
plt.show()

 

8. 극한

  • x가 어떤 값 a에 가까이 다가갈 때 a에 '한없이 가까이 간다'일 뿐, a에 도달하지 않는다
  • 극한을 표현할 때, 엡실론(epsilon)이라는 아주 작은 값(ex, 0.00001)등으로 표현
from sympy import *
init_printing()

x, y, z = symbols('x y z')
a, b, c, t = symbols('a b c t')
  • \(\underset{x\to 1}{lim}(\frac{x^{3}-1}{x-1})=3\)
print("극한값:", limit((x**3-1) / (x-1), x, 1))

# 출력 결과
극한값: 3
plot( ((x**3-1) / (x-1)), xlim = (-5, 5), ylim = (-1, 10))

 

  • \(\underset{x\to \infty }{lim}(\frac{1+x}{x})=3\)
print("극한값:", limit((1+x) / (x), x, oo))

# 출력 결과
극한값: 1
plot( ((1+x) / (x)), xlim = (-10, 10), ylim = (-5, 5))

 

  • \(\underset{x\to 1 }{lim}(\frac{\sqrt{x+3}-2}{x-1})=\frac{1}{4}\)
print("극한값:", limit((sqrt(x+3)-2) / (x-1), x, 1))

# 출력 결과
극한값: 1/4
plot( ((sqrt(x+3)-2) / (x-1)), xlim = (-5, 12), ylim = (-0.5, 1))

 

  - 삼각함수의 극한

  • \(\underset{x\to \frac{\pi }{2}+0 }{lim} \tan x=-\infty\)
  • \(\underset{x\to \frac{\pi }{2}-0 }{lim} \tan x=\infty\)
print("극한값:", limit(tan(x), x, pi/2, '+'))
print("극한값:", limit(tan(x), x, pi/2, '-'))

# 출력 결과
극한값: -oo
극한값: oo
plot(tan(x), xlim = (-3.14, 3.14), ylim = (-6, 6))

 

  • \(\underset{x\to 0 }{lim}\begin{pmatrix}\frac{\sin x}{x}\end{pmatrix}\)
print("극한값:", limit(sin(x)/x, x, 0))

# 출력 결과
극한값: 1
plot(sin(x)/x, ylim = (-2, 2))

 

  • \(\underset{x\to 0 }{lim} x\sin\begin{pmatrix}\frac{1}{x}\end{pmatrix}\)
print("극한값:", limit(x * sin(1/x), x, 0))

# 출력 결과
극한값: 0
plot(x * sin(1/x), xlim = (-2, 2), ylim = (-1, 1.5))

 

  - 지수함수, 로그함수의 극한

  • \(\underset{x\to \infty }{lim} \begin{pmatrix}\frac{2^{x}-2^{-x}}{2^{x}+2^{-x}}\end{pmatrix}\)
print("극한값:", limit((2**x - 2**(-x)) / (2**x + 2**(-x)), x, oo))

# 출력 결과
극한값: 1
plot((2**x - 2**(-x)) / (2**x + 2**(-x)), xlim = (-10, 10), ylim = (-3, 3))

 

  • \(\underset{x\to \infty }{lim} (log_{2}(x+1)-log_{2}(x))=0\)
print("극한값:", limit(log(x+1, 2) - log(x, 2), x, oo))

# 출력 결과
극한값: 0
plot(log(x, 2), log(x+1, 2), xlim = (-4, 6), ylim = (-4, 4))

 

  - 자연로그(\(e\))의 밑

  • (1) \(\underset{x\to \infty }{lim} \begin{pmatrix}1+\frac{1}{x}\end{pmatrix}^{x}=e\)
  • (2) \(\underset{x\to \infty }{lim} \begin{pmatrix}1+\frac{2}{x}\end{pmatrix}^{x}=e^{2}\)
  • (3) \(\underset{x\to 0 }{lim} \frac{(e^{x}-1)}{x}=1\)
  • (4) \(\underset{x\to 0 }{lim} \frac{ln(1+x)}{x}=1\)
print('(1):', limit( (1+1/x)**x, x, oo ))
print('(2):', limit( (1+2/x)**x, x, oo ))
print('(3):', limit( (exp(x) -1) / x, x, 0))
print('(4):', limit( (ln(1+x) / x), x, 0))

# 출력 결과
(1): E
(2): exp(2)
(3): 1
(4): 1
# 그래프는 (4)만 출력
plot( ln(1+x) / x, xlim = (-4, 6), ylim = (-2, 8))

 

 

9. 미분

  • 어떤 한 순간의 변화량을 표시한 

 

  - 미분과 기울기

  • 어떤 함수를 나타내는 그래프에서 한 점의 미분값(미분계수)를 구하는 것은 해당 점에서의 접선을 의미
  • 기울기는 방향성을 가짐
    • 이용할 미분 식(수치 미분)
      \(\frac{df(x)}{dx}=\displaystyle \lim_{x \to \infty}\frac{f(x+h)-f(x-h))}{2h}\)
  • \(h\)는 아주 작은 수를 뜻하는데, 예를 들어 10\(e\)-50 정도의 수를 하면 파이썬은 이를 0.0으로 인식
  • 따라서, 딥러닝에서 아주 작은 수를 정할 때, 1\(e\)-4 정도로 설정해도 무방
# 미분 계산하는 함수 정의
def numerical_differential(f, x):
    h = 1e-4
    return (f(x+h)-f(x-h)) / (2*h)

 

  - 함수 위의 점 \((a, b)\)에서의 접선의 방정식

  • 예제: 점 (1, 7)에서의 기울기
# 직접 지정한 2차함수식
def my_func(x):
    return 2*x**2 + 3*x + 2

# 1차 선형식
# a: 직선의 x좌표
# b: 직선의 y좌표
# c: 직선의 기울기
def linear_func(a, b, c, x):
    return c*(x-a) + b

c = numerical_differential(my_func, 1)

x = np.arange(-5, 5, 0.01)
y = linear_func(1, my_func(1), c, x)

plt.xlabel('x')
plt.ylabel('y')
plt.scatter(1, my_func(1))
plt.plot(x, my_func(x), x, y, 'r-')
plt.title('f(x) & linear function')
plt.show()

 

  - 미분 공식

  • \(\frac{d}{dx}(c)=0\) (\(c\)는 상수)
  • \(\frac{d}{dx}[cf(x)]=cf'(x)\)
  • \(\frac{d}{dx}[f(x)+g(x)]=f'(x)+g'(x)\)
  • \(\frac{d}{dx}[f(x)-g(x)]=f'(x)-g'(x)\)
  • \(\frac{d}{dx}[f(x)g(x)]=f(x)g'(x)+f'(x)g(x)\) (곱셈 공식)
  • \(\frac{d}{dx}[\frac{f(x)}{g(x)}]=\frac{g(x)f'(x)-f(x)g'(x)}{[g(x)]^{2}}\)
  • \(\frac{d}{dx}[x^{n}]=nx^{n}-1\)


  - 편미분

  • 변수가 1개짜리인 위의 수치미분과 달리, 변수가 2개 이상일 때의 미분법을 편미분이라 함
  • 다변수 함수에서 특정 변수를 제외한 나머지 변수는 상수로 처리하여 미분을 하는 것
  • 각 변수에 대해 미분 표시를 \(\sigma \)를 통해 나타냄
  • ex) \(f(x_{0}, x_{1})=x\tfrac{2}{0}+x\tfrac{2}{1}\)
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()

거리가 멀어질수록 기울기가 커져 영향을 많이 받음

 

● 추천 시스템(Recommender Systems)

  • 추천 시스템은 크게 두 가지로 구분 가능
    • 컨텐츠 기반 필터링(content-based filtering): 지금까지 사용자의 이전 행동과 명시적 피드백을 통해 사용자가 좋아하는 것과 유사한 항목을 추천
    • 협업 필터링(collaborative filtering): 사용자와 항목간의 유사성을 동시에 사용해 추천
  • 두 가지를 조합한 hybrid 방식도 가능

 

1. Surprise

  • 추천 시스템 개발을 위한 라이브러리
  • 다양한 모델과 데이터 제공
  • scikit-learn과 유사한 사용방법
# %pip install scikit-surprise
from surprise import SVD
from surprise import Dataset
from surprise.model_selection import cross_validate

data = Dataset.load_builtin('ml-100k', prompt = False)
data.raw_ratings[:10]

# 출력 결과
# (사용자 id, 영화 id, 평점, 시간)
[('196', '242', 3.0, '881250949'),
 ('186', '302', 3.0, '891717742'),
 ('22', '377', 1.0, '878887116'),
 ('244', '51', 2.0, '880606923'),
 ('166', '346', 1.0, '886397596'),
 ('298', '474', 4.0, '884182806'),
 ('115', '265', 2.0, '881171488'),
 ('253', '465', 5.0, '891628467'),
 ('305', '451', 3.0, '886324817'),
 ('6', '86', 3.0, '883603013')]
# 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]

 

  - 유클리드 거리를 사용해 추천

  • 거리가 가까울수록(값이 작을수록) 나와 유사한 사용자

$$ euclidian = \sqrt{\sum_{d=1}^{D}(A_{i}-B_{i})^2} $$

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]

 

 

  - 코사인 유사도를 사용해 추천

  • 두 벡터가 이루고 있는 각을 계산

$$ cos\theta =\frac{A\cdot B}{\left\| A\right\|\times \left\| B\right\|} $$

# 코사인 유사도 계산식
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)

  • 컨텐츠 기반 필터링과 협업 필터링을 조합한 방식
  • 많은 하이브리드 방식이 존재
  • 실습에서는 협업 필터링으로 임베딩을 학습하고 컨텐츠 기반 필터링으로 유사도 기반 추천을 수행하는 추천 엔진 개발
import numpy as np
from sklearn.decomposition import randomized_svd, non_negative_factorization
from surprise import Dataset

data = Dataset.load_builtin('ml-100k', prompt = False)
raw_data = np.array(data.raw_ratings, dtype = int)
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, ratting, time in raw_data:
    adj_matrix[user_id][movie_id] = rating
adj_matrix

# 출력 결과
array([[3, 3, 3, ..., 0, 0, 0],
       [3, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [3, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 3, 0, ..., 0, 0, 0]])
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]

 

  - 비음수 행렬 분해를 사용한 하이브리드 추천

A, B, iter = non_negative_factorization(adj_matrix, n_components = 2)

np.matmul(A, B)

# 출력 결과
array([[2.65054793e+00, 1.43305723e+00, 7.12899311e-01, ...,
        4.99364929e-03, 1.61183931e-02, 1.52342501e-02],
       [1.46896452e+00, 3.82199135e-02, 4.63060557e-01, ...,
        7.41842083e-03, 0.00000000e+00, 0.00000000e+00],
       [1.05504090e+00, 2.74503376e-02, 3.32579733e-01, ...,
        5.32806429e-03, 0.00000000e+00, 0.00000000e+00],
       ...,
       [6.45969509e-01, 2.42500330e-02, 2.02959350e-01, ...,
        3.21642261e-03, 8.79481547e-05, 8.31239301e-05],
       [9.88950712e-01, 3.86580826e-01, 2.79306078e-01, ...,
        2.77435827e-03, 4.26387068e-03, 4.02998437e-03],
       [1.66732038e+00, 1.06422315e+00, 4.33815308e-01, ...,
        2.13993190e-03, 1.20624618e-02, 1.14007989e-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))

# 출력 결과
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]

● 분해(Decomposition)

  • 큰 하나의 행렬을 여러 개의 작은 행렬로 분해
  • 분해 과정에서 중요한 정보만 남게됨

 

  • 라이브러리, 데이터 불러오기
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)

일반 PCA로 변환한 것과 큰 차이는 없음

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]]
# 시각화
plt.figure(figsize = (20, 10))
plot_digits(decomposed_digits, digits.target)

 

  - KNN

# 분해 전
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)

 

# train 데이터로 NMF 분해 학습
nmf = NMF(n_components = 10)
nmf.fit(train_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)

n_components = 10일때 보다 더 정교하게 복원됨

nmf = NMF(n_components = 300)
nmf.fit(train_faces)

matrix1 = nmf.transform(damaged_faces)
matrix2 = nmf.components_
show_faces(matrix1 @ matrix2)

● 다양체 학습

  • 높은 차원의 데이터를 저차원으로 축소하는 방법

https://scikit-learn.org/0.23/auto_examples/manifold/plot_compare_methods.html

  • 고차원 데이터를 2차원 또는 3차원으로 축소해 시각화에 활용할 수 있음
  • 차원 축소 과정에서 중요하지 않은 정보는 버려지고 중요한 정보만 남기 대문에 데이터 정제에 활용 가능

 

  • 데이터 생성 및 시각화 함수
# 필요 라이브러리
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')

 

 

1. Locally Linear Embedding(LLE)

  • 국소 이웃 거리를 보존하는 저차원 임베딩을 찾음
# s_curve는 2차원으로
s_curve_lle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                              method = 'standard', random_state = 0).fit_transform(s_curve)

# 손글씨는 3차원으로
digits_lle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'standard', random_state = 0).fit_transform(s_curve)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_lle, color, 121, '2d')
plot_digits(digits_lle, labels, 122, '3d')

 

 

2. Local Tangent Space Alignment(LTSA)

  • 탄젠트 공간을 통해 각 이웃의 국소 성질을 특성화
  • 국소 탄젠트 공간을 정렬
# LLE에서 method만 'ltsa'로 바꾸면 ltsa 사용 가능

s_curve_ltsa = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                               method = 'ltsa', random_state = 0).fit_transform(s_curve)

digits_ltsa = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'ltsa', random_state = 0).fit_transform(digits)

 

# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_ltsa, color, 121, '2d')
plot_digits(digits_ltsa, labels, 122, '3d')

 

 

3. Hessian Eigenmapping

  • LLE의 문제를 해결한 다른 방법
    • LLE는 단순히 이웃간의 거리를 기반으로만 유지를 한다는 제한점이 있음
  • 국소 선형 구조를 복원하기 위해 각 이웃에서 hessian 기반의 이차 형태를 중심으로 회전
# LLE에서 method만 'hessian'으로 바꾸면 hessian 사용 가능

s_curve_hlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                               method = 'hessian', random_state = 0).fit_transform(s_curve)

digits_hlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'hessian', random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_hlle, color, 121, '2d')
plot_digits(digits_hlle, labels, 122, '3d')

s_curve데이터는 ltsa와 색깔이 반대로 나옴, 손글씨 데이터는 별 차이 없음

 

4. Modfied Locally Linear Embedding

  • 각 이웃에 여러 가중치 벡터를 사용
  • n_neighbors > n_components를 만족해야 함
# LLE에서 method만 'modified'로 바꾸면 modified 사용 가능

s_curve_mlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 2,
                                               method = 'modified', random_state = 0).fit_transform(s_curve)

digits_mlle = manifold.LocallyLinearEmbedding(n_neighbors = 30, n_components = 3,
                                              method = 'modified', random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_mlle, color, 121, '2d')
plot_digits(digits_mlle, labels, 122, '3d')

 

5. Isomap

  • 초기의 다양체 학습 알고리즘
  • MDS와 커널 PCA의 확장으로 볼 수 있음
  • 모든 점들 사이의 측지 거리를 유지하는 저차원 임베딩을 찾음
s_curve_isomap = manifold.Isomap(n_neighbors = 30, n_components = 2).fit_transform(s_curve)
digits_isomap = manifold.Isomap(n_neighbors = 30, n_components = 3).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_isomap, color, 121, '2d')
plot_digits(digits_isomap, labels, 122, '3d')

손글씨 데이터에서 위의 다른 분석에서는 한쪽에 몰려있던 것에 비해 측지거리를 유지한 모습을 확인할 수 있음

 

6. Multi Dimensional Scaling(MDS)

  • 고차원 공간에서의 거리를 고려하는 저차원 공간을 찾음
  • neighbors 개념이 없고 차원(components)만 지정
  • 거리를 각각 고려하며 동작하여 분석 속도가 느림
s_curve_mds = manifold.MDS(n_components = 2, random_state = 0).fit_transform(s_curve)
digits_mds = manifold.MDS(n_components = 3, random_state = 0).fit_transform(digits)
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_mds, color, 121, '2d')
plot_digits(digits_mds, labels, 122, '3d')

s_curve 데이터에서는 s모양 유지, 손글씨 데이터에서는 거리를 유지하면서 골고루 퍼진 형태로 embedding

 

7. Spectral Embedding(SE)

  • 스펙트럼 분해를 통해 데이터의 저차원 표현을 찾음
  • 데이터의 점이 저차원 공간에서도 서로 가깝게 유지되도록 함
s_curve_se = manifold.SpectralEmbedding(n_components = 2, random_state = 0).fit_transform(s_curve)
digits_se = manifold.SpectralEmbedding(n_components = 3, random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_se, color, 121, '2d')
plot_digits(digits_se, labels, 122, '3d')

모양이 펼쳐져 있지만 서로 가까이 유지되는 형태가 유지됨

 

 

8. t-distributed Stochastic Neighbor Embedding(t-SNE)

  • 데이터 포인트의 유사성을 확률로 변환
  • 국소(local) 구조에 민감
  • 국소 구조를 기반으로 샘플 그룹을 추출하는데 강함
  • 항상 KL발산의 국소 최소값에서 끝남
    • 쿨백-라이블러 발산(Kullback–Leibler divergence, KLD)은 두 확률분포의 차이를 계산하는 데에 사용하는 함수로, 어떤 이상적인 분포에 대해, 그 분포를 근사하는 다른 분포를 사용해 샘플링을 한다면 발생할 수 있는 정보 엔트로피 차이를 계산, 상대 엔트로피(relative entropy), 정보 획득량(information gain), 인포메이션 다이버전스(information divergence)라고도 한다.
  • 계산 비용이 많이 듦(시간이 오래 걸림)
  • 전역 구조를 보존하지 않음
s_curve_tsne = manifold.TSNE(n_components = 2, random_state = 0).fit_transform(s_curve)
digits_tsne = manifold.TSNE(n_components = 3, random_state = 0).fit_transform(digits)
# 시각화
fig = plt.figure(figsize = (20, 10))
plot_s_curve(s_curve_tsne, color, 121, '2d')
plot_digits(digits_tsne, labels, 122, '3d')

  • 단순히 s모양으로 된 데이터의 국소 구조를 유지한 채 임베딩이 된 모습
  • 포인트들 간의 유사성을 확률로 변환하는 알고리즘 동작 때문에 나온 분포
  • 손글끼는 데이터 포인트의 유사성을 확률로 변환하였기 때문에 잘 정리되어 임베딩된 모습

 

 

9. 정제된 표현을 이용한 학습

  • 다양체 학습의 결과를 정제된 데이터로 생각할 수 있음
  • 저차원 변환을 한 데이터를 다른 모델의 학습을 위한 데이터로 사용 가능
  • 정제된 표현이기 때문에 분석 비교적 용이함
  • 기계학습 모델의 입력으로 사용했을때 성능향상을 기대할 수 있음

 

  - 다른 모델에 활용 예시

# 다른 모델 라이브러리 불러오기
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

knn = KNeighborsClassifier()
svm = SVC()
decision_tree = DecisionTreeClassifier()
random_forest = RandomForestClassifier()


# 손글씨 데이터 새로 불러오기
raw_digits, target = datasets.load_digits(return_X_y = True)

 

  - 원본 데이터 사용 시

# KNN
score = cross_val_score(
    estimator = knn,
    X = raw_digits, y = 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)


# SVM
score = cross_val_score(
    estimator = svm,
    X = raw_digits, y = 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)


# Decision Tree
score = cross_val_score(
    estimator = decision_tree,
    X = raw_digits, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.77222222 0.73333333 0.79387187 0.82451253 0.78272981]
mean cross val score: 0.7813339523367379 (+/- 0.029700574831777498)


# Random Forest
score = cross_val_score(
    estimator = random_forest,
    X = raw_digits, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.94166667 0.90833333 0.96100279 0.96657382 0.92479109]
mean cross val score: 0.9404735376044568 (+/- 0.021831308367203855)

 

  - 정데된 데이터 사용 시

# KNN
score = cross_val_score(
    estimator = knn,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.96388889 0.96388889 0.98328691 0.99164345 0.96935933]
mean cross val score: 0.9744134942742185 (+/- 0.011159643225931395)


# SVM
score = cross_val_score(
    estimator = svm,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.95       0.93333333 0.98328691 0.98885794 0.96100279]
mean cross val score: 0.9632961931290621 (+/- 0.02065358544710109)


# Decision Tree
score = cross_val_score(
    estimator = decision_tree,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.96944444 0.91388889 0.98050139 0.9637883  0.93314763]
mean cross val score: 0.9521541318477251 (+/- 0.024752179064191634)


# Random Forest
score = cross_val_score(
    estimator = random_forest,
    X = digits_tsne, y = target,
    cv = 5
)
print(score)
print('mean cross val score: {} (+/- {})'.format(score.mean(), score.std()))

# 출력 결과
[0.95833333 0.91944444 0.99164345 0.99164345 0.97214485]
mean cross val score: 0.966641906530486 (+/- 0.02674722386771949)

 

  - 원본 데이터를 썼을 때보다 tsne로 정제된 데이터를 썼을 때 score가 0.1 내외의 차이로 상승되어 나옴

  - 특히 Decision Tree 알고리즘에서 tsne로 정제된 데이터를 썼을 때 큰 향상이 이루어짐

● 군집화

  • 대표적인 비지도학습 알고리즘
  • 레이블(정답)이 없는 데이터를 그룹화하는 알고리즘

https://scikit-learn.org/stable/auto_examples/cluster/plot_cluster_comparison.html#sphx-glr-auto-examples-cluster-plot-cluster-comparison-py

 

  • 필요 라이브러리
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
  • 예시 데이터 생성
# 그래프 그리는 함수 작성
def plot_data(datasets, position, title):
    X, y = datasets
    plt.subplot(position)
    plt.title(title)
    plt.scatter(X[:, 0], X[:, 1])
    
# 랜덤한 예시 데이터 생성을 위한 파라미터
np.random.seed(0)
n_samples = 1500
random_state = 0
noise = 0.05

# 여러 구조의 예시 데이터 작성
circles = datasets.make_circles(n_samples = n_samples, factor = 0.5, noise = noise, random_state = random_state)
moons = datasets.make_moons(n_samples = n_samples, noise = noise, random_state = random_state)
blobs = datasets.make_blobs(n_samples = n_samples, random_state = random_state)
no_structures = np.random.rand(n_samples, 2), None

# 그래프 그리는 함수로 예시 데이터 시각화
plt.figure(figsize = (12, 12))
plot_data(circles, 221, 'Circles')
plot_data(moons, 222, 'Moons')
plot_data(blobs, 223, 'Blobs')
plot_data(no_structures, 224, 'No structures')

 

  • 군집화를 위해 학습, 예측, 시각화하는 과정을 하나의 함수로 작성
# 학습하고, 예측하고, 시각화하는 함수
def fit_predict_plot(model, dataset, position, title):
    X, y = dataset
    model.fit(X)
    if hasattr(model, 'labels_'):
        labels = model.labels_.astype(np.int)
    else:
        labels = model.predict(X)
    
    colors = np.array(['#30A9DE', '#E53A40', '#090707', '#A593E0', '#F6B352', '##519D9E', '#D81159', '#8CD790', '#353866'])
    ax = plt.subplot(position)
    ax.set_title(title)
    ax.scatter(X[:, 0], X[:, 1], color = colors[labels])

 

 

1. K-Means

  • n개의 등분산 그룹으로 군집화
  • 제곱합 함수를 최소화
  • 군집화 개수를 지정해야 함
  • 각 군집 \(C\)의 평균 \(\mu_{j}\)을 중심점이라고 함
  • 다음을 만족하는 중심점을 찾는 것이 목표

$$ \sum_{i=0}^{n}\underset{\mu _{j}\in C}{min}(\left\| x_{i}-\mu_{j}\right\|^2) $$

  • 거리를 기반으로 계산

 

  - 만들어둔 예시 데이터와 함수를 기반으로 군집화 및 시각화

fig = plt.figure(figsize = (12, 12))
fig.suptitle('K-Means')
fit_predict_plot(cluster.KMeans(n_clusters = 2, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.KMeans(n_clusters = 2, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.KMeans(n_clusters = 2, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.KMeans(n_clusters = 2, random_state = random_state), no_structures, 224, 'No structures')

 

  - 군집 개수를 3개로

fig = plt.figure(figsize = (12, 12))
fig.suptitle('K-Means')
fit_predict_plot(cluster.KMeans(n_clusters = 3, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.KMeans(n_clusters = 3, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.KMeans(n_clusters = 3, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.KMeans(n_clusters = 3, random_state = random_state), no_structures, 224, 'No structures')

 

  - 군집 개수를 4개로

fig = plt.figure(figsize = (12, 12))
fig.suptitle('K-Means')
fit_predict_plot(cluster.KMeans(n_clusters = 4, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.KMeans(n_clusters = 4, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.KMeans(n_clusters = 4, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.KMeans(n_clusters = 4, random_state = random_state), no_structures, 224, 'No structures')

 

 

  - 붓꽃 데이터 군집화

from sklearn.datasets import load_iris
iris = load_iris()

model = cluster.KMeans(n_clusters = 3)
model.fit(iris.data)
predict = model.predict(iris.data)

# 예측결과가 0인 것에 인덱스 부여
idx = np.where(predict == 0)
iris.target[idx]

# 예측결과가 1인 것에 인덱스 부여
idx = np.where(predict == 1)
iris.target[idx]

# 예측결과가 2인 것에 인덱스 부여
idx = np.where(predict == 2)
iris.target[idx]

 

2. 미니 배치 K-Means

  • 배치 처리를 통해 계산 시간을 줄인 K-평균
  • K-평균과 다른 결과가 나올 수 있음

 

  - 위의 예제 데이터로 실습

  - 군집 2개로 군집화

fig = plt.figure(figsize = (12, 12))
fig.suptitle('MiniBatch K-Means')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 2, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 2, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 2, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 2, random_state = random_state), no_structures, 224, 'No structures')

  - 군집 3개로 군집화

fig = plt.figure(figsize = (12, 12))
fig.suptitle('MiniBatch K-Means')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 3, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 3, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 3, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 3, random_state = random_state), no_structures, 224, 'No structures')

  - 군집 4개로 군집화

fig = plt.figure(figsize = (12, 12))
fig.suptitle('MiniBatch K-Means')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 4, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 4, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 4, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.MiniBatchKMeans(n_clusters = 4, random_state = random_state), no_structures, 224, 'No structures')

 

 

3. Affinity Propagation

  • 샘플 쌍끼리 메시지를 보내 군집을 생성
  • 샘플을 대표하는 적절한 예를 찾을 때까지 반복
  • 군집의 개수를 자동으로 정함

https://datascienceschool.net/03%20machine%20learning/16.05%20Affinity%20Propagation.html

 

  - 위의 예제 데이터로 실습

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Affinity Propagation')

# 군집의 개수를 정할 필요 없고 대신, damping과 preference를 파라미터로 작성
# damping: 알고리즘의 매 반복마다 Responsiblity Matrix와 Availability Matrix를 업데이트할 때 Exponential weighted average를 적용
# preference: 각 data point들이 얼마다 exemplar로 선택될 가능성이 높은지를 지정
fit_predict_plot(cluster.AffinityPropagation(damping = .9, preference = -200), circles, 221, 'Circles')
fit_predict_plot(cluster.AffinityPropagation(damping = .9, preference = -200), moons, 222, 'Moons')
fit_predict_plot(cluster.AffinityPropagation(damping = .9, preference = -200), blobs, 223, 'Blobs')
fit_predict_plot(cluster.AffinityPropagation(damping = .9, preference = -200), no_structures, 224, 'No structures')

각 구조마다 적절한 군집 개수를 스스로 결정항 군집화한 모습

 

 

4. Mean Shift

  •  중심점 후보를 정해진 구역 내 평균으로 업데이트
fig = plt.figure(figsize = (12, 12))
fig.suptitle('Mean Shift')

# 파라미터는 따로 지정 안함
fit_predict_plot(cluster.MeanShift(), circles, 221, 'Circles')
fit_predict_plot(cluster.MeanShift(), moons, 222, 'Moons')
fit_predict_plot(cluster.MeanShift(), blobs, 223, 'Blobs')
fit_predict_plot(cluster.MeanShift(), no_structures, 224, 'No structures')

 

 

5. 스펙트럼 군집화

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Spectral Clustering')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 2, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 2, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 2, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 2, random_state = random_state), no_structures, 224, 'No structures')

  - 군집 3개

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Spectral Clustering')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 3, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 3, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 3, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 3, random_state = random_state), no_structures, 224, 'No structures')

  - 군집 4개

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Spectral Clustering')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 4, random_state = random_state), circles, 221, 'Circles')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 4, random_state = random_state), moons, 222, 'Moons')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 4, random_state = random_state), blobs, 223, 'Blobs')
fit_predict_plot(cluster.SpectralClustering(n_clusters = 4, random_state = random_state), no_structures, 224, 'No structures')

 

 

  - 유방암 데이터 군집화

from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()

model = cluster.SpectralClustering(n_clusters = 2, eigen_solver = 'arpack', affinity = 'nearest_neighbors')
model.fit(cancer.data)
predict = model.labels_
# 0으로 predict 한 target 표시
idx = np.where(predict == 0)
cancer.target[idx]

# 출력 결과
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1,
       1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
       0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
       1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1,
       1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0,
       0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1,
       1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1,
       1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0,
       1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0,
       0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0])
       
       

# 1로 predict 한 target 표시
idx = np.where(predict == 1)
cancer.target[idx]

# 출력 결과
array([0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1])

 

 

6. 계층 군집화

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Hierarchical Clustering')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 2, linkage = 'ward'), circles, 221, 'Circles')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 2, linkage = 'ward'), moons, 222, 'Moons')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 2, linkage = 'ward'), blobs, 223, 'Blobs')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 2, linkage = 'ward'), no_structures, 224, 'No structures')

  - 군집 3개

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Hierarchical Clustering')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 3, linkage = 'ward'), circles, 221, 'Circles')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 3, linkage = 'ward'), moons, 222, 'Moons')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 3, linkage = 'ward'), blobs, 223, 'Blobs')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 3, linkage = 'ward'), no_structures, 224, 'No structures')

  - 군집 4개

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Hierarchical Clustering')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 4, linkage = 'ward'), circles, 221, 'Circles')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 4, linkage = 'ward'), moons, 222, 'Moons')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 4, linkage = 'ward'), blobs, 223, 'Blobs')
fit_predict_plot(cluster.AgglomerativeClustering(n_clusters = 4, linkage = 'ward'), no_structures, 224, 'No structures')

 

  - 와인 데이터 군집화

from sklearn.datasets import load_wine
wine = load_wine()

model = cluster.AgglomerativeClustering(n_clusters = 3)
model.fit(wine.data)
predict = model.labels_

idx = np.where(predict == 0)
wine.target[idx]

# 출력 결과
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 1, 1])


idx = np.where(predict == 1)
wine.target[idx]

# 출력 결과
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2])


idx = np.where(predict == 2)
wine.target[idx]

# 출력 결과
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

 

 

7. DBSCAN(Density-Based Spatial Clustering of Applications with Noise)

fig = plt.figure(figsize = (12, 12))
fig.suptitle('DBSCAN')
fit_predict_plot(cluster.DBSCAN(eps = .3), circles, 221, 'Circles')
fit_predict_plot(cluster.DBSCAN(eps = .3), moons, 222, 'Moons')
fit_predict_plot(cluster.DBSCAN(eps = .3), blobs, 223, 'Blobs')
fit_predict_plot(cluster.DBSCAN(eps = .3), no_structures, 224, 'No structures')

 

 

8. OPTICS(Ordering Points To Identify the Clustering Structure)

fig = plt.figure(figsize = (12, 12))
fig.suptitle('DOPTICS')
fit_predict_plot(cluster.OPTICS(min_samples = 20, xi = 0.05, min_cluster_size = 0.1), circles, 221, 'Circles')
fit_predict_plot(cluster.OPTICS(min_samples = 20, xi = 0.05, min_cluster_size = 0.1), moons, 222, 'Moons')
fit_predict_plot(cluster.OPTICS(min_samples = 20, xi = 0.05, min_cluster_size = 0.1), blobs, 223, 'Blobs')
fit_predict_plot(cluster.OPTICS(min_samples = 20, xi = 0.05, min_cluster_size = 0.1), no_structures, 224, 'No structures')

 

 

9. Birch(Balanced iterative redcing and clustering using hierarchies)

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Birch')
fit_predict_plot(cluster.Birch(n_clusters = 2, threshold = .3), circles, 221, 'Circles')
fit_predict_plot(cluster.Birch(n_clusters = 2, threshold = .3), moons, 222, 'Moons')
fit_predict_plot(cluster.Birch(n_clusters = 2, threshold = .3), blobs, 223, 'Blobs')
fit_predict_plot(cluster.Birch(n_clusters = 2, threshold = .3), no_structures, 224, 'No structures')

  - 군집 3개

fig = plt.figure(figsize = (12, 12))
fig.suptitle('Birch')
fit_predict_plot(cluster.Birch(n_clusters = 3, threshold = .3), circles, 221, 'Circles')
fit_predict_plot(cluster.Birch(n_clusters = 3, threshold = .3), moons, 222, 'Moons')
fit_predict_plot(cluster.Birch(n_clusters = 3, threshold = .3), blobs, 223, 'Blobs')
fit_predict_plot(cluster.Birch(n_clusters = 3, threshold = .3), no_structures, 224, 'No structures')

 

 

10. 손글씨 데이터 군집화

  - 데이터 확인

 
from sklearn.datasets import load_digits

digits = load_digits()

X = digits.data.reshape(-1, 8, 8)
y= digits.target

plt.figure(figsize = (16, 8))
for i in range(10):
    plt.subplot(2, 5, i+1)
    plt.imshow(X[i])

 

  - K-Means

kmeans = cluster.KMeans(n_clusters = 10)
kmeans.fit(digits.data)
predict = kmeans.predict(digits.data)

# 텍스트로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)
    real_class = digits.target[idx]
    print('Cluster {}: {}'.format(i+1, real_class))
    
# 이미지로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)[0]
    choice_idx = np.random.choice(idx, size = 5)
    choice_image = X[choice_idx]

    k = 1

    print('Cluster: {}'.format(i+1))
    for image in choice_image:
        plt.subplot(1, 5, k)
        plt.xticks([])
        plt.yticks([])
        plt.imshow(image)
        k += 1
    
    plt.show()

 

  - 스펙트럼 군집화

spectral = cluster.SpectralClustering(n_clusters = 10, eigen_solver = 'arpack', affinity = 'nearest_neighbors')
spectral.fit(digits.data)
predict = spectral.labels_

# 텍스트로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)
    real_class = digits.target[idx]
    print('Cluster {}: {}'.format(i+1, real_class))

# 이미지로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)[0]
    choice_idx = np.random.choice(idx, size = 5)
    choice_image = X[choice_idx]

    k = 1

    print('Cluster: {}'.format(i+1))
    for image in choice_image:
        plt.subplot(1, 5, k)
        plt.xticks([])
        plt.yticks([])
        plt.imshow(image)
        k += 1
    
    plt.show()

 

  - 계층 군집화

hierarchical = cluster.AgglomerativeClustering(n_clusters = 10, linkage = 'ward')
hierarchical.fit(digits.data)
predict = hierarchical.labels_

# 텍스트로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)
    real_class = digits.target[idx]
    print('Cluster {}: {}'.format(i+1, real_class))

# 이미지로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)[0]
    choice_idx = np.random.choice(idx, size = 5)
    choice_image = X[choice_idx]

    k = 1

    print('Cluster: {}'.format(i+1))
    for image in choice_image:
        plt.subplot(1, 5, k)
        plt.xticks([])
        plt.yticks([])
        plt.imshow(image)
        k += 1
    
    plt.show()

 

  - Birch

birch = cluster.Birch(n_clusters = 10, threshold = .3)
birch.fit(digits.data)
predict = birch.labels_

# 텍스트로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)
    real_class = digits.target[idx]
    print('Cluster {}: {}'.format(i+1, real_class))

# 이미지로 예측 결과 확인
for i in range(10):
    idx = np.where(predict == i)[0]
    choice_idx = np.random.choice(idx, size = 5)
    choice_image = X[choice_idx]

    k = 1

    print('Cluster: {}'.format(i+1))
    for image in choice_image:
        plt.subplot(1, 5, k)
        plt.xticks([])
        plt.yticks([])
        plt.imshow(image)
        k += 1
    
    plt.show()

● XGBoost

  • 트리 기반의 앙상블 기법
  • 분류에 있어서 다른 알고리즘보다 좋은 예측 성능을 보여줌
  • 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

 

  - 변수들의 중요도를 알아보기 위한 그래프 출력

fig, ax = plt.subplots(figsize = (10, 12))
plot_importance(xgb_model, ax = ax)

 

  - xgb 모델을 트리 형식으로 표현한 그래프 출력

dot_data = xgb.to_graphviz(xgb_model)
dot_data

 

2. XGBClassifier

  - 붓꽃 데이터

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size = 0.2, random_state = 42)

  - XGBClassifier 모델 생성

xgbc = XGBClassifier(n_estimators = 400, learning_rate = 0.1, max_depth = 3)
xgbc.fit(X_train, y_train)
preds = xgbc.predict(X_test)
preds_proba = xgbc.predict_proba(X_test)[:, 1]

cross_val = cross_validate(
    estimator = xgbc,
    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.38040671348571775 (+/- 0.12548339326469174)
avg score time: 0.0038000106811523437 (+/- 0.001165665213187403)
avg test score: 0.96 (+/- 0.024944382578492935)

  - 모델에서 데이터의 각 변수 중요도 출력

fig, ax = plt.subplots(figsize = (10, 12))
plot_importance(xgbc, ax = ax)

  - xgb 모델을 트리 형식으로 출력

dot_data = xgb.to_graphviz(xgbc)
dot_data

 

 

  - 와인 데이터

wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(wine.data, wine.target, test_size = 0.2, random_state = 42)

  - XGBClassifier 모델 생성

  - 파라미터의 종류

  • n_estimators : 학습 모델의 수, 많아질수록 성능 향상의 가능성이 있으나, 속도가 느려짐
  • learning_rate : 학습률, 너무 크면 gradient 발산의 가능성이 있으며, 너무 작으면 학습이 느림
  • max_depth : 최대 탐색 깊이, 너무 크면 과적합의 가능성, 너무 작으면 학습 성능 저하
  • min_samples_split : 분할 종료 최소 샘플 수, 큰 수면 과적합을 막지만 학습 성능 저하 가능성
  • min_samples_leaf : leaf node가 되기 위한 최소 샘플 수, min_samples_split과 비슷한 용도
xgbc = XGBClassifier(n_estimators = 400, learning_rate = 0.1, max_depth = 3)
xgbc.fit(X_train, y_train)
preds = xgbc.predict(X_test)
preds_proba = xgbc.predict_proba(X_test)[:, 1]

cross_val = cross_validate(
    estimator = xgbc,
    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.8099905490875244 (+/- 0.2795799385941016)
avg score time: 0.004575538635253906 (+/- 0.0016525575848722433)
avg test score: 0.96 (+/- 0.024944382578492935)

  - 모델에서 데이터의 각 변수 중요도 출력

fig, ax = plt.subplots(figsize = (10, 12))
plot_importance(xgbc, ax = ax)

  - xgb 모델을 트리 형식으로 출력

dot_data = xgb.to_graphviz(xgbc)
dot_data

 

 

3. XGBRegressor

  - XGB는 Classifier에 더 특화되어 있지만 Regressor도 할 수는 있음

  - 당뇨병 데이터

diabetes = load_diabetes()
X_train, X_test, y_train, y_test = train_test_split(diabetes.data, diabetes.target, test_size = 0.2, random_state = 42)

  - XGBRegressor 모델 생성

  - 파라미터의 종류

  • n_estimators : 학습 모델의 수, 많아질수록 성능 향상의 가능성이 있으나, 속도가 느려짐
  • learning_rate : 학습률, 너무 크면 gradient 발산의 가능성이 있으며, 너무 작으면 학습이 느림
  • max_depth : 최대 탐색 깊이, 너무 크면 과적합의 가능성, 너무 작으면 학습 성능 저하
  • min_samples_split : 분할 종료 최소 샘플 수, 큰 수면 과적합을 막지만 학습 성능 저하 가능성
  • min_samples_leaf : leaf node가 되기 위한 최소 샘플 수, min_samples_split과 비슷한 용도
  • objective : 목적함수, reg:linear(linear-regression), binary:logistic(binary-logistic-classification), count:poisson(count data poison regression) 등 다양

  - XGBClassifier보다 test score가 낮게 나

xgbr = XGBRegressor(n_estimators = 400, learning_rate = 0.1, max_depth = 3, objective = 'reg:squarederror')
xgbr.fit(X_train, y_train)
preds = xgbr.predict(X_test)

cross_val = cross_validate(
    estimator = xgbr,
    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.3746964454650879 (+/- 0.027832593554179098)
avg score time: 0.005658674240112305 (+/- 0.0017620359721342335)
avg test score: 0.30005291115066424 (+/- 0.07589309667544569)

  - 모델에서 데이터의 각 변수 중요도 출력

fig, ax = plt.subplots(figsize = (10, 12))
plot_importance(xgbr, ax = ax)

  - xgb 모델을 트리 형식으로 출력

dot_data = xgb.to_graphviz(xgbr)
dot_data

 

 

● LightGBM

  • XGBoost보다 2년쯤 뒤에 나와 더 개선됨
  • 빠른 학습과 예측 시간
  • 더 적은 메모리 사용
  • 범주형 특징의 자동 변환과 최적 분할

 

  • 필요 라이브러리
from lightgbm import LGBMClassifier, LGBMRegressor
from lightgbm import plot_importance, plot_metric, plot_tree

 

1. LGBMClassifier

  - 붓꽃 데이터

iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size = 0.2, random_state = 42)

  - LGBMClassifier 모델 생성

lgbmc = LGBMClassifier(n_estimators = 400)
evals = [(X_test, y_test)]
lgbmc.fit(X_train, y_train, early_stopping_rounds = 100, eval_metric = 'logloss', eval_set = evals, verbose = True)
preds = lgbmc.predict(X_test)

cross_val = cross_validate(
    estimator = lgbmc,
    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.19687752723693847 (+/- 0.08293570240505971)
avg score time: 0.001802968978881836 (+/- 0.0004004494069017428)
avg test score: 0.9600000000000002 (+/- 0.04898979485566355)

  - lgbm 모델이 실행되며 logloss의 변화를 관찰할 수 있는 그래프 출력

  - 성능이 어떻게 향상되고 있는지 볼 수 있음

plot_metric(lgbmc)

150번째 반복쯤에서 거의 최저값 나옴

  - 변수 중요도

  - LGBM 전용 plot_importance 사용

plot_importance(lgbmc, figsize = (10, 12))

  - 트리 형태로 출력

plot_tree(lgbmc, figsize = (28, 24))

 

  - 와인 데이터

wine = load_wine()
X_train, X_test, y_train, y_test = train_test_split(wine.data, wine.target, test_size = 0.2, random_state = 42)

  - LGBMClassifier 모델 생성

lgbmc = LGBMClassifier(n_estimators = 400)
evals = [(X_test, y_test)]
lgbmc.fit(X_train, y_train, early_stopping_rounds = 100, eval_metric = 'logloss', eval_set = evals, verbose = True)
preds = lgbmc.predict(X_test)

cross_val = cross_validate(
    estimator = lgbmc,
    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.6419896125793457 (+/- 0.21000223475863156)
avg score time: 0.003860330581665039 (+/- 0.002245084936532545)
avg test score: 0.9776190476190475 (+/- 0.01119469694127331)

  - lgbm 모델이 실행되며 logloss의 변화를 관찰할 수 있는 그래프 출력

plot_metric(lgbmc)

  - 변수 중요도

plot_importance(lgbmc, figsize = (10, 12))

  - 트리 형태로 출력

plot_tree(lgbmc, figsize = (28, 24))
 

 

  - 유방암 데이터

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)

  - LGBMClassifier 모델 생성

lgbmc = LGBMClassifier(n_estimators = 400)
evals = [(X_test, y_test)]
lgbmc.fit(X_train, y_train, early_stopping_rounds = 100, eval_metric = 'logloss', eval_set = evals, verbose = True)
preds = lgbmc.predict(X_test)

cross_val = cross_validate(
    estimator = lgbmc,
    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: 1.5169551372528076 (+/- 0.7215118218596442)
avg score time: 0.0062160491943359375 (+/- 0.002043592719798801)
avg test score: 0.9736531594472908 (+/- 0.015674460437800138)

  - lgbm 모델이 실행되며 logloss의 변화를 관찰할 수 있는 그래프 출력

plot_metric(lgbmc)

  - 변수 중요도

plot_importance(lgbmc, figsize = (10, 12))

  - 트리 형태로 출력

plot_tree(lgbmc, figsize = (28, 24))

 

 

2. LGBMRegressor

  - 당뇨병 데이터

diabetes = load_diabetes()
X_train, X_test, y_train, y_test = train_test_split(diabetes.data, diabetes.target, test_size = 0.2, random_state = 42)

  - LGBMRegressor 모델 생성

lgbmr = LGBMRegressor(n_estimators = 400)
evals = [(X_test, y_test)]
lgbmr.fit(X_train, y_train, early_stopping_rounds = 100, eval_metric = 'logloss', eval_set = evals, verbose = True)
preds = lgbmr.predict(X_test)

cross_val = cross_validate(
    estimator = lgbmr,
    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: 1.210813045501709 (+/- 0.6173186047501074)
avg score time: 0.010993432998657227 (+/- 0.009632501449539222)
avg test score: 0.30867643947179507 (+/- 0.07010708786960605)

  - lgbm 모델이 실행되며 logloss의 변화를 관찰할 수 있는 그래프 출력

plot_metric(lgbmr)

  - 변수 중요도

plot_importance(lgbmr, figsize = (10, 12))

  - 트리 형태로 출력

plot_tree(lgbmr, figsize = (28, 24))

● 앙상블(Ensamble)

  • 일반화와 강건성(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]

 

  - soft 보팅

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(probability = True)
model2 = GaussianNB()
model3 = RandomForestClassifier()
vote_model = VotingClassifier(
    estimators = [('svc', model1), ('naive', model2), ('forest', model3)],
    voting = 'soft',
    weights = [2, 1, 2]
)

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.97 (+/- 0.02) [RandomForestClassifier]
accurancy: 0.96 (+/- 0.02) [VotingClassifier]

 

  - 결정경계 시각화

from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import VotingClassifier
from itertools import product

# 총 4개의 feature 중 2개만 사용
X = iris.data[:, [0, 2]]
y = iris.target

model1 = DecisionTreeClassifier(max_depth = 4)
model2 = KNeighborsClassifier(n_neighbors = 7)
model3 = SVC(gamma = .1, kernel = 'rbf', probability = True)
vote_model = VotingClassifier(estimators = [('dt', model1), ('knn', model2), ('svc', model3)], voting = 'soft', weights = [2, 1, 2])

model1 = model1.fit(X, y)
model2 = model2.fit(X, y)
model3 = model3.fit(X, y)
vote_model = vote_model.fit(X, y)

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, 0.1),
                     np.arange(y_min, y_max, 0.1))

f, axarr = plt.subplots(2, 2, sharex = 'col', sharey = 'row', figsize = (12, 8))
for idx, model, tt in zip(product([0, 1], [0,1]), [model1, model2, model3, vote_model],
                          ['Decision Tree (depth=4)', 'KNN (k=7)', 'Kernel SVM', 'Soft Voting']):
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    axarr[idx[0], idx[1]].contourf(xx, yy, Z, alpha = 0.4)
    axarr[idx[0], idx[1]].scatter(X[:, 0], X[:, 1], c = y, s = 20, edgecolor = 'k')
    axarr[idx[0], idx[1]].set_title(tt)
    
plt.show()

 

 

6. 투표 기반 회귀

  • 서로 다른 모델의 예측값의 평균을 사용
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor, VotingRegressor

model1 = LinearRegression()
model2 = GradientBoostingRegressor()
model3 = RandomForestRegressor()
vote_model = VotingRegressor(
    estimators = [('Linear', model1), ('gbr', model2), ('rfr', model3)],
    weights = [1, 1, 1]
)

for model in (model1, model2, model3, vote_model):
    model_name = str(type(model)).split(".")[-1][:-2]
    scores = cross_val_score(model, diabetes.data, diabetes.target, cv = 5)
    print('R2: %0.2f (+/- %0.2f) [%s]' % (scores.mean(), scores.std(), model_name))

# 출력 결과
R2: 0.48 (+/- 0.05) [LinearRegression]
R2: 0.41 (+/- 0.07) [GradientBoostingRegressor]
R2: 0.42 (+/- 0.05) [RandomForestRegressor]
R2: 0.46 (+/- 0.05) [VotingRegressor]

 

  - 시각화

X = diabetes.data[:, 0].reshape(-1, 1)
y = diabetes.target

model1 = LinearRegression()
model2 = GradientBoostingRegressor()
model3 = RandomForestRegressor()
vote_model = VotingRegressor(
    estimators = [('Linear', model1), ('gbr', model2), ('rfr', model3)],
    weights = [1, 1, 1]
)

model1 = model1.fit(X, y)
model2 = model2.fit(X, y)
model3 = model3.fit(X, y)
vote_model = vote_model.fit(X, y)

x_min, x_max = X.min() - 1, X.max() + 1
xx = np.arange(x_min - 1, x_max + 1, 0.1)

f, axarr = plt.subplots(2, 2, sharex = 'col', sharey = 'row', figsize = (12,  8))

for idx, model, tt in zip(product([0, 1], [0, 1]),
                          [model1, model2, model3, vote_model],
                          ['Linear Regressor', 'Gradient Boosting', 'Random Forest', 'Voting']):
    Z = model.predict(xx.reshape(-1, 1))
    axarr[idx[0], idx[1]].plot(xx, Z, c = 'r')
    axarr[idx[0], idx[1]].scatter(X, y, s = 20, edgecolor = 'k')
    axarr[idx[0], idx[1]].set_title(tt)

plt.show()

 

 

7. 스택 일반화(Stacked Generalization)

  • 각 모델의 예측값을 최종 모델의 입력으로 사용(모델의 예측값을 최종 모델에 스택으로 쌓음)
  • 모델의 편향을 줄이는데 효과적

 

  - 스택 회귀

from sklearn.linear_model import Ridge, Lasso
from sklearn.svm import SVR
from sklearn.ensemble import GradientBoostingRegressor, StackingRegressor

estimators = [('ridge', Ridge()),
              ('lasso', Lasso()),
              ('svr', SVR())]

reg = make_pipeline(
    StandardScaler(),
    StackingRegressor(estimators = estimators,
                      final_estimator = GradientBoostingRegressor())
)

cross_val = cross_validate(
    estimator = reg,
    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.34942941665649413 (+/- 0.10878244133017631)
avg score time: 0.010065841674804687 (+/- 0.00266810792836556)
avg test score: 0.37135451119632856 (+/- 0.0688832914154432)

 

  - 회귀식 시각화

X = diabetes.data[:, 0].reshape(-1, 1)
y = diabetes.target

model1 = Ridge()
model2 = Lasso()
model3 = SVR()
reg = StackingRegressor(estimators = estimators,
                        final_estimator = GradientBoostingRegressor())


model1 = model1.fit(X, y)
model2 = model2.fit(X, y)
model3 = model3.fit(X, y)
reg = reg.fit(X, y)

x_min, x_max = X.min() - 1, X.max() + 1
xx = np.arange(x_min - 1, x_max + 1, 0.1)

f, axarr = plt.subplots(2, 2, sharex = 'col', sharey = 'row', figsize = (12,  8))

for idx, model, tt in zip(product([0, 1], [0, 1]),
                          [model1, model2, model3, vote_model],
                          ['Ridge', 'Lasso', 'SVR', 'Stack']):
    Z = model.predict(xx.reshape(-1, 1))
    axarr[idx[0], idx[1]].plot(xx, Z, c = 'r')
    axarr[idx[0], idx[1]].scatter(X, y, s = 20, edgecolor = 'k')
    axarr[idx[0], idx[1]].set_title(tt)

plt.show()

 

  - 스택 분류

from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import StackingClassifier

estimators = [('logistic', LogisticRegression(max_iter = 10000)),
              ('svc', SVC()),
              ('naive', GaussianNB())]

clf = StackingClassifier(
    estimators = estimators,
    final_estimator = RandomForestClassifier()
)

cross_val = cross_validate(
    estimator = clf,
    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: 1.7309207439422607 (+/- 0.5153520045243725)
avg score time: 0.08048839569091797 (+/- 0.06365540285519627)
avg test score: 0.9733333333333334 (+/- 0.02494438257849294)

 

  - 결정 경계 시각화

# 총 4개의 feature 중 2개만 사용
X = iris.data[:, [0, 2]]
y = iris.target

model1 = LogisticRegression(max_iter = 10000)
model2 = SVC()
model3 = GaussianNB()
stack = StackingClassifier(estimators = estimators, final_estimator = RandomForestClassifier())

model1 = model1.fit(X, y)
model2 = model2.fit(X, y)
model3 = model3.fit(X, y)
stack = stack.fit(X, y)

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, 0.1),
                     np.arange(y_min, y_max, 0.1))

f, axarr = plt.subplots(2, 2, sharex = 'col', sharey = 'row', figsize = (12, 8))
for idx, model, tt in zip(product([0, 1], [0,1]), [model1, model2, model3, stack],
                          ['Logistic Regression', 'SVC', 'GaussianNB', 'Stack']):
    Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    axarr[idx[0], idx[1]].contourf(xx, yy, Z, alpha = 0.4)
    axarr[idx[0], idx[1]].scatter(X[:, 0], X[:, 1], c = y, s = 20, edgecolor = 'k')
    axarr[idx[0], idx[1]].set_title(tt)
    
plt.show()

+ Recent posts