본문 바로가기
  • 문과생의 백엔드 개발자 성장기
|Playdata_study/AI

210810_MachinLearning 5 (Gradient Descent, Logistic Regression, Collaborative Filtering)

by 케리's 2021. 8. 11.

Linear Regression

 

Gradient Descent (경사하강법)

 

Gradient Descent는 학습 알고리즘 중 하나

학습이라 하면 머신러닝 알고리즘의 결과가 좋아지도록 파라미터를 조정하는 것

가중치(weight), 편향(bias)이 파라미터에 포함된다.

 

 

즉, 1차 근삿값 발견용 최적화 알고리즘이며

함수의 기울기(경사)를 구하고 경사의 절대값이 낮은쪽으로 계속 이동시켜 극값에 이르기까지 반복시킨다.

 

 

곡선의 특성상 초반에는 빠르게 내려간다 (큰폭의 변화률)

보폭을 짧게 내려가면 학습 시간이 오래걸린다.

Cost 0 근처에 다가갔는데 큰 폭으로 내려가면 발산의 우려가 있다.

 

Cost가 크면 어떤값을 수정하면 될까? 기울기 = W(weith)값 가중치

 


실습

 

1  선형회귀(Linear Regression) - 심화

 

1.1  Linear Regression을 활용하여

섭씨온도(C, Celsius)를 화씨온도(F, Fahrenheit)로 변환 해주는 공식을 만들수 있다.

 

섭씨온도과 화씨온도의 관계는 앞에서 우리가 살펴보았던 선형회귀의 관계를 가지고 있다.

 

1.1.1   

 

H(x) = aX + b 에서처럼, F = C*1.8 +32 

 

이 때, 1.8과 32라는 값을 모르고 있다고 가정하고, 머신러닝 알고리즘을 이용해서 주어진 섭씨 온도와 화씨온도 데이타 만으로 이 값들을 찾아내는 실습을 진행해보도록 하겠다. Chain Rule 증명

 

선형회귀는 쉽게 접근하면 쉽고, 어렵게 접근하면 매우 어려운 모델
이 부분은 결국 2가지가 정의되어야 하는데

 

1) 가장 기본적인 수식 Hypothesis Function
2) Cost Function

3) learning rate

 

  • Cost Function

Cost는 비용인데 학습(running)을 언제 멈출지 결정하는 요인이 된다.
비용은 적게 들수록 좋음! 비용이 안 들면 best!

머신, 딥러닝에서도 학습을 하면서 모델이 멈춰야 하는 시점을 알아야 하는데, 비용이 없을 때(0일 때) 멈춰야 한다.
이 수식이 바로 MSE Cost Function

Cost 함수를 이용해서, 그래프 선상에서 어느 위치에 있는지 알 수 있음
그런데 그래프 선상에서 내려갈지/올라갈지 어느 쪽으로 이동할지는 알 수 없음 → 그래서 미분이 이용됨: 경사하강법

 

  • learning rate

학습 속도와 관련이 있는 하이퍼 파라미터

하이퍼 파라미터 튜닝할 때 관련있는 것이 learning rate 이다.
learning rate가 높으면 발산 → Overfitting
learning rate가 낮으면 학습 속도가 매우 느려짐
0.01~0.004 사이 값 권장한다.

 

 

1.2  Configuration (or prerequisite)

 

%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

 

1.3  Generate Dataset

 

1.3.1  선형회귀 문제를 다루기 위한 학습용 데이타셋을 생성

 

0도에서 100도 사이의 값을 갖는 섭씨온도 데이타를 100개 만들어보자.

이 데이타가 해당 머신러닝 알고리즘에서 사실상 Feature가 될 것이다.

 

 

'''
섭씨온도 데이터를 담고있는 C의 shape를 확인합니다. (100,)로 100개의 데이터가 있습니다.
C의 첫 10개 값을 확인합니다. 
'''

C = np.random.randint(0,100)
C
C = np.random.randint(0,100,size=100) #0~99 까지의 랜덤한 정수가 리턴
C
# print(C.shape)
# C[:10]
# ----------- 결과

array([40, 60, 33, 58, 23, 38, 93, 32,  5, 83, 65, 92, 89, 10,  5, 89, 72,
       19, 65, 94, 45,  3, 23, 67, 85, 50, 67, 46, 89, 51, 25, 12, 54, 48,
       61, 82, 42,  5, 88, 29, 51, 88,  1, 54, 13, 38, 27,  3, 56, 87, 92,
        7, 47, 89, 85, 95, 48, 37, 30,  5, 55, 37,  9, 91, 74, 12, 91, 85,
       20, 54, 67,  7, 16, 25, 86,  6,  2, 35, 22, 58, 40,  7, 54,  1, 38,
       17, 64, 30, 12, 84, 13, 98,  0, 17, 80, 14, 35, 39, 94, 59])

 

 

print(C.shape)
C[:10]
# ---- 결과

(100,)
array([40, 60, 33, 58, 23, 38, 93, 32,  5, 83])

 

 

1.4  섭씨온도 데이타에 상응하는 화씨온도를 생성

 

1.4.1  우리가 알고있던 기존의 섭씨 - 화씨변환 공식을 적용해서

       위 섭씨온도 데이타에 대응하는 화씨온도 데이타를 생성.

 

이 데이타가 오늘 다룰 학습 모델 알고리즘의 Label이 될 것이다.

 

 

1.5  Bias (편향 찾기) -- Weight는 1.8

1.5.1  Weight는 1.8로 주고 Bias를 직관적으로 한번 찾아보겠다.

 

F = C * 1.8 + 32
F
# ---- 결과

array([104. , 140. ,  91.4, 136.4,  73.4, 100.4, 199.4,  89.6,  41. ,
       181.4, 149. , 197.6, 192.2,  50. ,  41. , 192.2, 161.6,  66.2,
       149. , 201.2, 113. ,  37.4,  73.4, 152.6, 185. , 122. , 152.6,
       114.8, 192.2, 123.8,  77. ,  53.6, 129.2, 118.4, 141.8, 179.6,
       107.6,  41. , 190.4,  84.2, 123.8, 190.4,  33.8, 129.2,  55.4,
       100.4,  80.6,  37.4, 132.8, 188.6, 197.6,  44.6, 116.6, 192.2,
       185. , 203. , 118.4,  98.6,  86. ,  41. , 131. ,  98.6,  48.2,
       195.8, 165.2,  53.6, 195.8, 185. ,  68. , 129.2, 152.6,  44.6,
        60.8,  77. , 186.8,  42.8,  35.6,  95. ,  71.6, 136.4, 104. ,
        44.6, 129.2,  33.8, 100.4,  62.6, 147.2,  86. ,  53.6, 183.2,
        55.4, 208.4,  32. ,  62.6, 176. ,  57.2,  95. , 102.2, 201.2,
       138.2])

 

 

# 화씨도 10개만 추려보자

print(F.shape)
F[:10]
# --- 결과

(100,)
array([104. , 140. ,  91.4, 136.4,  73.4, 100.4, 199.4,  89.6,  41. ,
       181.4])

 

1.6  Visualization

위에서 만든 섭씨, 화씨 온도를 Feature, Label이 되도록 그려본다

 

# 한글 안깨지게 함!
import platform

from matplotlib import font_manager, rc
plt.rcParams['axes.unicode_minus'] = False

if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    path = "c:/Windows/Fonts/malgun.ttf"
    font_name = font_manager.FontProperties(fname=path).get_name()
    rc('font', family=font_name)
else:
    print('Unknown system!!')

 

plt.scatter(C,F)
plt.xlabel('섭씨온도')
plt.ylabel('화씨온도')
plt.show()

 

Bias(편향찾기) -- Weight는 1.8로 지정

 

# 먼저 앞서 생성한 섭씨온도 X, 화씨온도 y 변수에 할당
X = C
y = F 

# 화씨온도가 라벨이기 때문에 1차원으로 밖에 나올 수 없다.
# w, b 둘다 찾으려면 처음에는 다소 어려울 수 있으니 bias 값만 찾아보자.

w = 1.8

# b 값은 랜덤하게 학습이 용이하게 될 수 있는 분포를 가진 값
b = np.random.uniform(low=-1.0, high =+ 1.0)

w,b
# ---- 결과

(1.8, 0.06362559903828613)

 

 

# (1.8, 0.8389629984504843) 값을 가지고 가설을 만들어 보자
# H(x) = wX + b
# 위에서 지정한 w(1.8), b(랜덤한 값)를 가지고 가설을 하나 세우자

y_predict = w * X + b
y_predict[:10]
# ---- 결과 

array([ 72.0636256, 108.0636256,  59.4636256, 104.4636256,  41.4636256,
        68.4636256, 167.4636256,  57.6636256,   9.0636256, 149.4636256])

 

 

1.7  실제값과 예측한 값 사이의 거리 Visualization'

 

plt.scatter(C,F)
plt.plot(C,y_predict, c='r')
plt.xlabel('섭씨온도')
plt.ylabel('화씨온도')
plt.show()

# y_predict - y = 선과 선 사이의 간격

 

# y_predict - y 에러 -> 다시말해서 오차
# 오차만큼 다시 bias에 보정을 해주면 된다.

b = b - (y_predict - y).mean() # 여러 값 보지 않으려면 통계함수 사용
b
# --- 결과

31.99999999999999

 

 

y_predict = w*X + b # b는 32로 보정된 값이 들어간다.
y_predict[:10]
# ---- 결과

array([104. , 140. ,  91.4, 136.4,  73.4, 100.4, 199.4,  89.6,  41. ,
       181.4])
plt.scatter(C,F)
plt.plot(C,y_predict, c='r')
plt.xlabel('섭씨온도')
plt.ylabel('화씨온도')
plt.show()

 

 

1.7.1  w, b 모두 랜덤한 값으로 지정하기

 

 

w = np.random.uniform(low=-1.0, high =+ 1.0)
b = np.random.uniform(low=-1.0, high =+ 1.0)

w,b
(0.44475890075141655, -0.13243747641251802)

 

 

# 예측한 값
y_predict = w * X + b
y_predict[:10]
# 결과 

array([17.65791855, 26.55309657, 14.54460625, 25.66357877, 10.09701724,
       16.76840075, 41.23014029, 14.09984735,  2.09135703, 36.78255129])

 

# 차이가 많이남! = cost값이 커졌다

plt.scatter(C,F)
plt.plot(C,y_predict, 'r')
plt.show()

 

 

1.8  Gradient Descent

가중치(Weight, W), 편향(Bias, B) 찾기

 

1.8.1  이제 경사하강법(Gradient Descent)을 사용하여 섭씨온도를 화씨온도로 변환해줄 주 있는 공식을 찾아보겠다.

 

공식의 세부내용은 모른다치고, 두 변수(X,y)가 선형(Linear)과 관계있음을 가정(y=X * w + b)하여

가중치(weight)와 편차(bias)를 정의해 둔다.

그리고 경사하강법을 이용해서 선형회귀를 학습시킨다.

학습이 완료되면, 얻어진 최적의 가중치와 편차로 섭씨온도를 화씨온도로 변환해주는 공식을 만들어 줄수 있다.

학습은 모든 epoch이 끝날 때까지 반복할 수도 있지만, 여기선 오차(error)가 0.1 이하가 되면 학습을 종료하도록 한다.

 

 

# epoch 은 학습을 몇 회 반복할지, 즉 몇 번의 for문을 돌릴 지 셋팅한다.
num_epoch = 100000 
learning_rate = 0.0003 #임의의 값

w = np.random.uniform(low=-1.0, high =+ 1.0)
b = np.random.uniform(low=-1.0, high =+ 1.0)

for epoch in range(num_epoch):
    y_predict = w * X + b # 가설세움 -> 오차가 어느정도 발생할지 알 수 있음
    
    # 현재 오차를 구하여 error에 저장 
    error = np.abs(y_predict - y).mean() #array니까 mean을 사용해야한다
    if error < 0.1:
        break
    # learing_rate사용하여 오차만큼 보정 
    w = w-learning_rate*((y_predict - y)*X).mean()    
    b = b-learning_rate*(y_predict - y).mean()
    
    # 10000회마다 epoch, b, w, error(cost)를 출력해서 확인
    if epoch % 10000 == 0:
        print(f"{epoch:5} w={w:.6f}, b={b:.6f}, error={error:.6f}")
print("*"*50)
print(f"{epoch:5} w={w:.6f}, b={b:.6f}, error={error:.6f}")
# ---- 결과

  0 w=2.084910, b=-0.732342, error=134.523073
10000 w=2.000811, b=18.694515, error=6.219410
20000 w=1.881636, b=26.590885, error=2.528394
30000 w=1.833188, b=29.801018, error=1.027875
40000 w=1.813492, b=31.106042, error=0.417865
50000 w=1.805485, b=31.636577, error=0.169876
**************************************************
55888 w=1.803229, b=31.786062, error=0.099992

 

 

1.10  Predict

 

y_predict = w*C + b
y_predict[:5]
# 결과 

array([103.91521522, 139.97979167,  91.29261347, 136.37333402,
        73.26032525])

 

1.10.1  

선형 회귀의 학습이 끝났으면, 이제 이 머신러닝 알고리즘을 활용하여 변환공식을 완성하고,

주어진 섭씨온도를 화씨온도로 변환(혹은 섭씨온도로 화씨온도를 예측)할 수 있다.

앞서 만들어준 데이터를 그대로 활용하여 주어진 섭씨온도(C)로 화씨온도를 예측해보겠다.

 

 

1.11  DataFrame Visualization

 

result = pd.DataFrame({"C":C, "F":F, "F(predict)":y_predict})
result.head(10)

 

 

plt.scatter(C,F)
plt.plot(C,y_predict, 'r')
plt.show()

 

 

Logistic Regression

 

Classification & Regression

 

맞춰야 하는 정답(Label, y)이 categorical(범주형) 데이터면 Classification(분류)

반면 맞춰야 하는 정답이 continuous(연속형) 데이터면 Regression(회귀) 

 

Linear Regression 알고리즘은 Regression (회귀)문제만 풀수있고, 분류(Classification) 문제는 풀 수 없다.

이유는 y의 예측값에 해당하는 h(x), 즉 hypothesis function의 범위가 연속적이기 때문

 

 

squashing funtion

 

연속적인 값 z(x)를 0-1 사이값으로 변환해주는 (값을 으깨주는) Squasing Function을 사용하면

Classification(분류) 문제에서도 퍼셉트론 알고리즘을 사용 할 수 있다.

x에 어떤 값이 들어갔을 때 그 값을 0에서 1사이로 으깨주는(squashing) 가장 심플한 방법은

Unit step 함수, 즉 x가 마이너스면 0으로, 플러스면 1로 변환시켜 주는것이다.

 

 

Sigmoid Funtion

 

가장 유명한 squashing funtion중 하나

공식은 복잡하지만 그림은 간결하다.

이 함수를 사용하면

 

1) x의 값을 0과 1사이로 squashing 시킬 수 있고

2) 동시에 미분이 가능하다.

 

 

 

선형구조 (Linear)

 

선형자료구조란 하나의 자료뒤에 하나의 자료가 존재

자료들 간의 앞뒤 관계가 1:1 선형관계

배열, 리스트, 스택, 큐 

 

 

 

비선형구조 (NonLinear)

 

하나의 자료 뒤에 여러개의 자료가 존재 할 수 있는 것

자료들 간의 앞뒤 관계가 1:n, 또는 n:n의 관계

트리, 그래프 , 계층적 구조 

 

 

 


실습 

 

11_ML_LogisticRegression

 

 

1  로지스틱 회귀(Logistic Regression) - 심화

 

선형회귀(Linear Regression) 경우,
공부한 시간(2, 3, 6, 9, 11시간)과 기말고사 성적(60, 65, 70, 80, 90점)과의 관계를 학습한 결과로
새로운 데이타(13시간)가 들어왔을때 성적을 예측하는 모델로 사용된다.

 

반면에 로지스틱회귀(Rogistic Regression) 경우,
Classification(분류) 문제를 다루는 모델이다.


맞다 / 틀리다, 살았다 /죽었다, 암이다 / 암이 아니다 의 경우처럼
Target의 카테고리가 2개인 이진 분류에서 주로 사용되는 회귀 모델이다.

 

즉, Linear Regression은 연속적인 숫자 선상에 있는 값을 예측할때 사용하는 모델이고
Rogistic Regression은 둘 중 하나를 선택할 때 사용하는 모델이라 할 수 있다.

 

1.1  Configuration

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

 

 

 

1.2  Generate Datasets

 

로지스틱 회귀를 가장 잘 이해할 수 있는 데이타 셋을 예시로 들어보자.
Feature 생성 → salaries(연봉), satisfactions(만족도)

 

S전자 신입사원 10명의 (2년후) 연봉 정보를 가져오자.
label = stay 0 (이직), 1 (남아있다)

 

이를 salaries라는 변수에 할당한 후, 행렬 연산을 편하게 하기 위해 Numpy의 배열로 변경한다.
단 여기서 편의를 위해서 단위는 1,000만원으로 함 (가령 5.0이면 연봉 5천만원)

 

 

1.2.1  salaries (연봉)

 

salaries = [5.0, 5.5, 5.3, 6.2, 5.25, 5.2, 5.5, 4.9, 5.35, 5.25] #연봉 임의수 지정
salaries = np.array(salaries)
salaries
# --- 결과

array([5.  , 5.5 , 5.3 , 6.2 , 5.25, 5.2 , 5.5 , 4.9 , 5.35, 5.25])

 

 

 

1.2.2  satisfactions (만족도)

 

S전자 신입사원 10명의 업무 만족도 정보를 가져오자.
점수는 1점부터 10점으로 구성되어 있다.
이를 satisfactions 라는 변수에 할당한 뒤, 행렬 연산을 편하게 하기 위해서

역시 Numpy의 array() 함수를 사용하겠다.

 

satisfactions = [3, 7, 4, 9, 8, 6, 5, 4, 6, 7]

# 행렬 연산을 하기위해 numpy배열로 만든다
satisfactions = np.array(satisfactions)
satisfactions
# 결과

array([3, 7, 4, 9, 8, 6, 5, 4, 6, 7])

 

 

1.2.3  Label생성 - stay

 

위에서 2개의 feature를 만들었다.
그리고 위 신입사원들의 이직 여부(회사 잔류여부)를 표현한 데이타를 생성한다.
이 데이타가 오늘 다룰 머신러닝 알고리즘의 Label이 될 것이다.

 

S전자 신입사원 10명의 이직 여부(회사 잔류여부)를 저장한다.
이직하지 않고 회사에 남아있는 경우 True 값을 넣으며, 반대로 다른 회사로 이직했을 경우 False라는 값을 넣는다.
이 값들을 stay라는 변수에 할당한 후 역시 행렬연산을 위해서 Numpy의 array()를 사용한다.

 

 

stay = [False, True, False, True, True, True, True, False, True, True]
stay = np.array(stay)
stay
# --- 결과

array([False,  True, False,  True,  True,  True,  True, False,  True,
        True])

 

1.3  Visualize - DataFrame

 

앞서 생성한 데이타를 하나로 묶어서 표(Table) 로 표현하자.
그리고 기 결과를 example 이라는 변수에 할당한다.

 

 

example = pd.DataFrame({"연봉" : salaries, "업무만족도" : satisfactions, "잔류" : stay})
example

 

1.4  Visualize - Matplotlib

 

주어진 데이터를 바탕으로 연봉과 업무만족도가 이직에 얼마나 영향을 미치는지 시각화 할 수 있다.
보라색은 퇴직한 사람.
c는 Color로 이직여부로 구분을 했다.

 

 

# c = stay -> label로 예측한다 (보라색 이직, 노란색 잔류)
plt.scatter(salaries, satisfactions, c=stay)
plt.show()

 

 

1.5  Define Sigmoid

 

RogisticRegression 모델에는 Sigmoid 함수를 사용한다.

이제 로지스틱 회귀(Logistic Regression)를 돌리기 위해 필요한 기능을 구현하자.

먼저 스쿼싱 함수(Squashing Function)로 사용할 시그모이드(Sigmpod)를 직접 구현해 보겠다.

시그모이드 함수의 공식은 다음과 같다.

 

 

이 공식을 그래프로 시각화 하면 다음과 같다.

 

 

 

이제 위에서 설명한 내용을 바탕으로 시그모이드 함수를 직접 구현해보자.

시그모이드 함수를 구현하기 위해서는 넘파이(Numpy)에서 지수 함수(exponential fucntion)를 계산하는 

np.exp를 사용해야 한다.

 

 

# custom 으로 정의해보기
# x 값은 무한대, y 값은 1,0 으로 나뉨
# y 값이 급격하게 변하는 곳은 기준치이다.


def sigmoid(x):
    return 1/(1+np.exp(-x))
# 균일한 간격을 가지는 1차원 배열 값을 생성 
# linspace(start, end, num) 

np.linspace(-10, 10, num=21) #21개의 값이 나오는데 균일하게 쪼개져서 나옴
np.linspace(-10, 10)
# ---- 결과

array([-10.        ,  -9.59183673,  -9.18367347,  -8.7755102 ,
        -8.36734694,  -7.95918367,  -7.55102041,  -7.14285714,
        -6.73469388,  -6.32653061,  -5.91836735,  -5.51020408,
        -5.10204082,  -4.69387755,  -4.28571429,  -3.87755102,
        -3.46938776,  -3.06122449,  -2.65306122,  -2.24489796,
        -1.83673469,  -1.42857143,  -1.02040816,  -0.6122449 ,
        -0.20408163,   0.20408163,   0.6122449 ,   1.02040816,
         1.42857143,   1.83673469,   2.24489796,   2.65306122,
         3.06122449,   3.46938776,   3.87755102,   4.28571429,
         4.69387755,   5.10204082,   5.51020408,   5.91836735,
         6.32653061,   6.73469388,   7.14285714,   7.55102041,
         7.95918367,   8.36734694,   8.7755102 ,   9.18367347,
         9.59183673,  10.        ])
xx = np.linspace(-10,10)
# 위 값을 스코징 시켜서 x, y 값 구함

yy = sigmoid(xx)
plt.plot(xx,yy)
plt.show()

 

1.6  Gradient Descent - 경사하강법

 

가설 ::  H(x) = W1 * X1 + W2 * X2 + b

 

이제 본격적으로 로지스틱 회귀 알고리즘을 학습시켜 보겠다.
먼저 앞서 생성한 두개의 Feature(salaries, satisfactions)를 편의상 각각 x1, x2 라는 변수에 할당한다.

 

x1 = salaries
x2 = satisfactions
x1, x2
# --- 결과

(array([5.  , 5.5 , 5.3 , 6.2 , 5.25, 5.2 , 5.5 , 4.9 , 5.35, 5.25]),
 array([3, 7, 4, 9, 8, 6, 5, 4, 6, 7]))

 

회사의 잔류 여부를 나타내는 stay를 Label로 간주하고 y변수에 할당

 

y = stay
y
# --- 결과

array([False,  True, False,  True,  True,  True,  True, False,  True,
        True])

 

이제 본격적으로 학습을 시작하자
두 개의 변수 x1과 x2를 가지고 있으니 총 세 개의 값
바로 가중치(weight) w1, w2과, bias인 b를 학습해야 한다.

세 개를 변수로 정의한 뒤, 경사 하강법(Gradient Descent)을 이용해 로지스틱 회귀를 학습시켜준다.

 

학습은 모든 epoch이 끝날 때까지 반복할 수도 있지만,
여기서는 굳이 그럴 필요없이 정확도(accuracy)가 1.0에 도달하면 학습을 종료하도록 하겠다.

 

num_epoch = 100
learning_rate = 1.0

w1 = np.random.uniform(low=-1.0, high =+1.0)
w2 = np.random.uniform(low=-1.0, high =+1.0)
b = np.random.uniform(low=-1.0, high =+1.0)


for epoch in range(num_epoch):  
    y_predict = w1 * x1 + w2 * x2 + b # 가설
    y_predict = sigmoid(y_predict) # 스코징 (0~1사이로 값이 만들어짐)
    
    predict = y_predict > 0.5 # 0.5 보다 크면 1로보자 = 임계치
    # 임계값이 해당 값을 넘어가면 1로보자! 
    accuracy = (predict == y).mean()
    print("### Accuracy", accuracy)
    
    if epoch % 10 == 0:
        print(f"{epoch}, accuracy={accuracy:.4f}")
    if accuracy == 1.0:
        break
    w1 = w1 - learning_rate * ((y_predict - y) * x1).mean()
    w2 = w2 - learning_rate * ((y_predict - y) * x2).mean()  
    b = b - learning_rate * (y_predict - y).mean()

print("===============================")
print(f"{epoch}, accuracy={accuracy:.4f}")
# --- 결과 

## Accuracy 0.7
0, accuracy=0.7000
### Accuracy 0.3
### Accuracy 0.7
### Accuracy 0.7
### Accuracy 0.7
### Accuracy 0.3
### Accuracy 0.7
### Accuracy 0.7
### Accuracy 0.7
### Accuracy 0.5
### Accuracy 0.7
10, accuracy=0.7000
### Accuracy 0.7
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.8
### Accuracy 0.6
### Accuracy 0.7
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.8
20, accuracy=0.8000
### Accuracy 0.8
### Accuracy 0.7
### Accuracy 0.8
### Accuracy 0.8
### Accuracy 0.9
### Accuracy 0.8
### Accuracy 0.9
### Accuracy 0.8
### Accuracy 0.9
### Accuracy 0.8
30, accuracy=0.8000
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
40, accuracy=0.9000
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
50, accuracy=0.9000
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 0.9
### Accuracy 1.0
===============================
54, accuracy=1.0000

 

1.7  Predict - 결과 예측하기

 

로지스틱 회귀의 학습이 끝났으면,
이 머신러닝 알고리즘을 활용하여 원하는 결과를 예측할 수 있다.
앞선 데이터를 그대로 활용하여,
S전자의 신입 데이터 사이언티스트들이 2년 후에 이직할 것인지 여부를 예측해 보도록 하겠다.

 

# 가설

w1 * x1 + w2 * x2 + b
# --- 결과

array([-5.3557064 , 12.50631208, -1.56475298, 19.70276108, 18.41728356,
        8.71535866,  2.61165827, -0.02292166,  8.13717192, 13.46995665])

 

y_predict = sigmoid(y_predict)
predict = y_predict>0.5

result = example.copy() # example 값 카피
result["잔류(예측)"] = predict
result["잔류(확률)"] = y_predict

result

 


Collaborative Filtering

 

협업 필터링은 다른 상품 (item)이나 사용자(user) 정보를 이용하여 평점(rating)이 없는 데이터의 평점을 예측한다.

예측 평점이 높은 상위 아이템을 추천함으로 써 작동하는 대표적인 추천 시스템 알고리즘 이다.

 

 

 

✔ 추천 시스템(Recommender System)에서 널리 사용되는 협업 필터링(이하 Collaborative Filtering) 이란?

 

추천 시스템은 사용자(이하 사용자)가 특정 물건이나 서비스(이하 상품)에 대한 선호 여부나 선호도를 예측하는 시스템을 의미한다.

추천 시스템은 아마존과 같은 이커머스부터 페이스북과 같은 SNS, 유튜브, 넷플릭스 등과 같은 동영상 플랫폼까지 다양한 분야에서 두루 활용되고 있다.

 

 

 

✔ Collaborative Filtering  추천 방법

 

 

1. 상품 기반

    : 사용자가 선호하는 상품과 유사한 다른 상품 을 추천

      아마존(Amazon)이 제안한 기법, 많은 기업에서 사용됨

 

2. 사용자 기반

    : 사용자와 유사한 다른 사용자가 선호하는 상품을 추천

     먼저 등장한 전통적인 알고리즘

   

   2.1 사용자 기반 방식 문제

 

     2.1.1) 계산 복잡성 문제

     2.1.2) 희소성 문제

 

 

아마존과 등 거대 이커머스 회사들은 수백만 명의 사용자와 수백만 개의 상품을 관리해야하는데

사용자 기반 방식을 사용하는 경우 사용자가 추가될 때마다 나머지 모든 사용자와의 유사도를 연산해야한다는 문제점이 있다.

 

상품 기반 방식을 사용하는 경우에 미리 구해 놓은 상품 간 유사도를 활용할 수 있기 때문에 이러한 문제점이 어느 정도 해결된다.

물론 상품 기반 방식도 상품과 사용자가 계속 추가되므로 일정 기간마다 새롭게 유사도를 구해야하지만 사용자 기반 방식보다는 훨씬 계산 복잡성이 작디.

그리고 계산 복잡성 문제가 해결되는 대신 이 거대한 행렬을 저장할 공간이 따로 확보되어야한다는 점을 굳이 단점으로 뽑을 수 있다.

데이터 희소성 문제는 협업 필터링 알고리즘의 본질적인 취약한 점이지만 사용자가 많은 상품을 평가한 경우는 보통 없어서 이런 경우 사용자간의 유사도를 연산하는 것 자체가 어렵기 때문에 보통 사용자 기반 방식이 더 취약하다.

 

 

✔ 정보의 활용 

 

1. 명시적 정보 (explicit ratings) 

   : 사용자가 상품에 내린 직접적인 평가 데이터

     사용자로부터 얻을 수 있는 가장 정확한 평점

     평점 간의 척도가 정확하지 않을 수 있고, 평점 수가 충분하지 않다는 한계가 있음

 

2. 암시적 정보 (implicit ratings)

   : 사용자 행동을 통해 (머무른시간, 클릭수) 추론한 상품에 대한 간접적인 평가 데이터

     평점을 쉽게 많이 수집할 수 있는 장점

     해당 정보를 무조건 사용자가 상품에 내린 긍정적 평가라고 결론 내릴 수 없다는 단점 

 

 

 


 

12_ML_CollaborativeFiltering

 

1  협업 필터링 (Collaborative Filtering) 구현하기

 

 

상품 / 사용자 기반 기법은 전반적으로 다음과 같은 흐름으로 동작한다.

 

  1. 우선 사용자 𝑢u가 내릴 상품 𝑖i에 대한 평점(rating)을 추정하고자 한다. 상품 𝑖i / 사용자 𝑢u와 나머지 모든 상품 / 사용자의 유사도를 연산한다.
  2. 유사도가 높은 k개 상품 / 사용자를 선택한다. 이를 이웃이라고 부르겠다.
  3. 상품 기반 혹은 사용자 기반 기법에 따라 아래 단계를 수행하며 평점을 예측한다.
    • 상품 기반 : 이웃 상품에 내린 사용자 𝑢u의 평점(rating)을 상품 𝑖i와의 유사도에 따라 가중 평균을 구한다.
    • 사용자 기반 : 이웃 사용자가 상품 𝑖i에 내린 평점(rating)을 사용자 𝑢u와의 유사도에 따라 가중 평균을 구한다.
  4. 아직 평점(rating)이 없는 항목에 대해 모든 평점(rating)을 예측한다. 평점(rating) 예측 값 상위 n개 상품을 추천한다.

 

이러한 머신러닝 알고리즘을 잘 이해하는 방법은, 알고리즘을 파이썬과 같은 프로그래밍 언어로 직접 구현해보는 것이다. 그러므로 이번 시간에는 주어진 데이터와 문제를 Collaborative Filtering을 활용하여 풀 되, surprise와 같은 추천 시스템 패키지를 사용하지 않고 파이썬으로 직접 구현해서 풀어보는 시간을 가질 것이다.

 

 

 

1.1  Configuration

 

import pandas as pd
import numpy as np
from pandas import DataFrame

 

 

1.2  데이터 생성

 

data = pd.read_csv("../data/ratings.csv")
data

 

data.shape
# --- 결과 


(23, 3)

 

'''
유사도를 계산할때 평점이 0인것은 유사성 값이 존재하는 반면에
NaN인 것인 평점 입력이 아예 안된 것으로 보기 때문에 
데이터(컬럼)가 아예 없는것으로 간주된다.

유사도 계산에 있어서 NaN값을 가지는 컬럼 값은 제외시키고 계산에 들어가야한다.
이것을 mask 씌운다. 라고 표현한다
'''

ratings = data.pivot_table(index='사람', columns='책', values='평점')
ratings

 

u = np.array([np.nan, 4, 3]) # n이라는사람이 평점을 nan, 4, 3입력
v = np.array([3,2,4])

mask = np.isfinite(u) & np.isfinite(v) # Nan 이 아닌값을 걸러냄 -> 그것을 마스크 씌움 
mask

# 결과 :: array([False, True, True])

u = u[mask]
v = v[mask]


print(u)
print(v)

# Nan columns 제외 후 출력 


# ========= Cosine Similarity (코사인 유사도) ===============

#분자
uvdot = (u * v).sum() 

# 분모
norm1 = (u ** 2).sum()
norm2 = (v ** 2).sum()

# squr = 루트씌우기(제곱근)
score = uvdot / np.sqrt(norm1 * norm2) # sqrt = 제곱급
print(score)
# --- 결과

[4. 3.] # Nan columns 제외 후 출력 
[2 4] # Nan columns 제외 후 출력 
0.8944271909999159

 

 

def get_cosine_similarity(u,v):
    mask = np.isfinite(u) & np.isfinite(v)

    u = u[mask]
    v = v[mask]


    #분자
    uvdot = (u * v).sum() 

    # 분모
    norm1 = (u ** 2).sum()
    norm2 = (v ** 2).sum()

    # squr = 루트씌우기(제곱근)
    score = uvdot / np.sqrt(norm1 * norm2)

    return score

u = np.array([np.nan, 4, 3]) # n이라는사람이 평점을 nan, 4, 3입력
v = np.array([3,2,4])


'''
cosine 유사도는 

-1 ~ 1까지의 값을 가진다.
1에 가까우면 유사도가 높음

0이면 2개가 서로 독립적
-1에 가까우면 유사도가 없음

'''
get_cosine_similarity(u,v)
# --- 결과

0.8944271909999159

 

 

### 민수와 민지의 유사도를 출력

u = ratings.loc['민수']
v = ratings.loc['민지']

get_cosine_similarity(u,v)
# ---- 결과

0.9398272507881658

 

ratings

 

ratings.index
# --- 결과

Index(['민수', '민지', '지민', '지연', '현우'], dtype='object', name='사람')

 

 

1.2.1  모든 사람의 유사도를 검색

 

 

itertools 해당 모듈을 이용해서 모든 사람의 유사도를 검색할 수 있다.
itertools 파이썬에서 조합, 순열을 만들 때 사용하는 아주 중요한 모듈
이번에는 조합을 사용해야 한다.

민수 - 민수, 민수 - 민지, 민수 - 지민 이런 식으로 조합을 만든다.
코드는 굉장히 직관적으로 작성 되어져 있다.

product 함수가 해당 모듈의 중요한 역할을 한다.

 

# ratings에 있는 사람들을 각각 2명씩 묶어보자

from itertools import product

def get_cosine_similarity_table(ratings):
    index_combinations = list(product(ratings.index, repeat=2))
    
    similarity_list = []

    for uname, vname in index_combinations:
        u,v = ratings.loc[uname], ratings.loc[vname]
        score = get_cosine_similarity(u,v)

        similarity = {
            'u':uname,
            'v':vname,
            'score':score
        }

        similarity_list.append(similarity)
        # 반복문이 끝나면 DataFrame으로 다시 만들어준다.

    similarity_list = pd.DataFrame(similarity_list)
    similarity_table = pd.pivot_table(similarity_list, index ='u', columns = 'v', values = 'score')
    
    return similarity_table

get_cosine_similarity_table(ratings)

 

similarity_table=get_cosine_similarity_table(ratings)
similarity_table

 

 

1.3  다른사람들의 평점과 유사도를 바탕으로 자신의 평점을 예측하기

 

위에서 민지의 노인과 바다에 대한 평점은 NaN값이다.
모든 사람들의 유사도를 파악한 결과를 바탕으로 민지의 노인과바다 책에 대한 평점을 예측할 수 있다.

 

1) 민지와 유사도가 가장 높은 사람은 현우
2) 현우의 노인과 바다 평점은 3.0
3) 민지의 노인과 바다 평점은 3.5 정도 나올 것이라고 예상할 수 있다.

 

# 본인을 제외한 다른 사람의 책 평점

neighbors_ratings = ratings['노인과바다']
neighbors_ratings
# --- 결과

사람
민수    3.0
민지    NaN
지민    4.0
지연    5.0
현우    3.0
Name: 노인과바다, dtype: float64

 

 

# 본인을 제외한 다른 사람의 책 평점
neighbors_ratings = ratings['노인과바다'].drop(index="민지")
# neighbors_ratings

# 본인과 관련된 다른사람의 유사도 (본인제외 -> 1이 나오기 때문에)
neighbors_similarity = similarity_table['민지'].drop(index="민지")
# neighbors_similarity

# 평점과 유사도를 가지고 계산 -> 각각 곱한다.
# (민수평점 * 민수 유사도) + (지민평점 * 지민 유사도) + (지연평점 * 지연유사도)
nominator = (neighbors_ratings * neighbors_similarity).sum()
denominator = neighbors_similarity.sum()

# 나를 제외한 유사도의 합
denominator = neighbors_similarity.sum()

# 특정한 책에 대한 본인의 평점을 예측
score = nominator/denominator
def predict_ratings(username, bookname):
    # 본인을 제외한 다른 사람의 책 평점
    neighbors_ratings = ratings[bookname].drop(index=username)
    # neighbors_ratings

    # 본인과 관련된 다른사람의 유사도 (본인제외 -> 1이 나오기 때문에)
    neighbors_similarity = similarity_table[username].drop(index=username)
    # neighbors_similarity

    # 평점과 유사도를 가지고 계산 -> 각각 곱한다.
    # (민수평점 * 민수 유사도) + (지민평점 * 지민 유사도) + (지연평점 * 지연유사도)
    nominator = (neighbors_ratings * neighbors_similarity).sum()
    denominator = neighbors_similarity.sum()

    # 나를 제외한 유사도의 합
    denominator = neighbors_similarity.sum()

    # 특정한 책에 대한 본인의 평점을 예측
    score = nominator/denominator
    return score

predict_ratings('민지', '노인과바다')
# 지민
predict_ratings('지민', '신데렐라')
# 민수
predict_ratings('민수', '흥부전')
# --- 결과

2.7543750620420546

 

 

1.4  모든 사용자와 상품에 대한 평점 검색

 

ratings_combinations = list(product(ratings.index, ratings.columns))
# ratings_combinations
rating_list=[]

for user_name, book_name in ratings_combinations:
    score = predict_ratings(user_name, book_name)
    
    rating_predict = {
        'user' : user_name,
        'book' : book_name,
        'score' : score
    }
    rating_list.append(rating_predict)

rating_list = pd.DataFrame(rating_list)

rating_table = pd.pivot_table(rating_list, index='user', columns='book', values='score')
rating_table

rating_list = pd.DataFrame(rating_list)

 

 

1.5  Case1. 지금 민지에게 가장 추천하고 싶은 책은?

 

def predict_book(user_name, k=1): # k는 추천하는 책의 수
    predict_list = rating_table.loc[user_name].sort_values(ascending=False)
    predict_list = predict_list.head(k).index
    return predict_list

predict_book('민지', k=2)
# 결과 

Index(['노인과바다', '어린왕자'], dtype='object', name='book')

 

 

1.6  Case2. 지금 백설공주 책에 가장 관심이 있을 것 같은 사용자는?

 

def predict_user(book_name, k=2):#k는 해당 책에 관심이 있을 것 같은 사용자 수
    predict_list = rating_table[book_name].sort_values(ascending=False)
    predict_list = predict_list.head(k).index
    return predict_list

predict_user('백설공주', k=2)
# 결과

Index(['현우', '지연'], dtype='object', name='user')

 

 

2  Notation

 

𝑟𝑢𝑖rui를 사용자 𝑢u가 상품 𝑖i에 내린 ratings, 𝐼𝑢𝑣Iuv 를 사용자 𝑢u와 사용자 𝑣v가 모두 평가한 상품 집합, 

𝑈𝑖𝑗Uij를 상품 𝑖i와 상품 𝑗j를 모두 평가한 사용자 집합이라고 표기하겠다.

 

 

3  Calculate Similarity

3.0.1  사용자 기반(User-based) 기법

  1. 사용자 "민지"와 나머지 모든 사용자의 유사도를 연산한다.  "민지"-"현우", "민지"-"민수", "민지"-"지민", "민지"-"지연"의 유사도를 연산한다.

 

3.0.2  상품 기반 (Item-based) 기법

  1. 상품 "노인과바다"와 나머지 모든 상품의 유사도를 연산한다. "노인과바다"-"백설공주", "노인과바다"-"신데렐라", "노인과바다"-"어린왕자", "노인과바다"-"콩쥐팥쥐", "노인과바다"-"흥부전"의 유사도를 연산한다.

 

 

그렇다면 임의의 두 사용자 혹은 임의의 두 상품, 즉 두 값이 얼마나 유사한지를 어떻게 판단할 수 있을까?

우리가 알고 있는 가장 대표적인 방법으로 두 데이터가 얼마나 가까운지를 유클리디안 거리(Euclidean Distance)를 활용하여 측정해볼 수도 있다.

 

이 외에도 다양한 유사도 메트릭을 사용하여 유사한 정도를 파악하는 것이 가능하다.

주로 유사도 측정을 위하여 피어슨 상관계수(Pearson Correlation Coefficient), 스피어만 순위 상관계수(Sprearman Rank Correlation Coefficient), 켄달의 타우(Kendall's Tau), 코사인 유사도(Cosine Similarity), 자카드 유사도(Jaccard Coefficient) 등을 활용한다.

 

이번 과제에서는 자주 쓰이는 피어슨 상관계수(Pearson Correlation Coefficient)에 대하여 더 자세하게 알아보고 이를 직접 구현해보겠다.

댓글