본문 바로가기

Machine Learning_모델설계_Python

선형회귀_2 단순선형회귀 실습

안녕하세요 배도리입니다. 이전 게시글을 작성하면서 느꼈던건 제 설명력이 좀 부족한 것 같습니다. 앞으로 로지스틱, 판별분석 등등 많은 모델을 설명해야하는데 과연 제가 잘 설명드릴 수 있을지 의문입니다. 특히 제가 과거 비전공자 시절 혼자 공부하면서 여러 블로그들을 방문했을때 "왜 설명이 세세하지 못하지? 이해가 안되잖아" 라고 생각했습니다. 지금 생각해보면 그때의 저는 수학,통계,코드에 대하여 기본적인것들도 몰랐기에 이해하기 어려웠던 것입니다. 그렇기에 수학적, 통계적 지식 정말 중요한것 같습니다. 이전 게시글에도 말했지만 머신러닝 모델설계 부분이 끝나면 꼭 수학과 통계 관련 게시글 올리겠습니다. I pomise you

 

 

 

오늘은 단순선형회귀를 코드로 실습해보겠습니다.  

import numpy as np
import random

import matplotlib.pyplot as plt

 

 

일단 선형 회귀 문제를 위한 가상데이터생성함수! 얍!

def generate_data(n_samples, bias, variance): # 단순선형회귀(독립변수 1개) 
    X = np.zeros(shape=(n_samples, 2))
#독립변수 X에 대한 2차원 배열을 생성하고 0으로 초기화합니다. 배열의 모양은(n_samples, 2) 입니다.
    y = np.zeros(shape=n_samples)
# 종속변수 y에 대한 1차원 배열을 생성하고 0으로 초기화합니다. 배열의 모양은 (n_samples,)입니다.

    for i in range(n_samples): #0부터 n_samples - 1까지의 범위로 for문을 실행합니다. 여기서 i는 반복 횟수
        X[i][0] = 1 #x_0은 상수 1로 셋팅 X의 첫 번째 컬럼(인덱스 0)에 상수 1을 채우기. 이는 상수항을 위한 것
        X[i][1] = i #x_1값은 1씩 증가시킴 X의 두 번째 컬럼(인덱스 1)에 i 값을 채우기. 이는 독립 변수값을 1씩 증가시킴
        y[i] = (i+bias) + random.uniform(0, 1) * variance #y에 bias 생성
        #종속 변수 y를 설정합니다. 이는 독립 변수 i에 편향 bias를 더하고, 0과 1 사이의 균일 분포에서 추출한 난수에 분산 variance를 곱한 것을 더하여 구합니다.
        # 이것은 잡음(noise)을 생성하여 데이터에 약간의 변동성을 추가합니다.
        
    return X, y

 

가상데이터생성함수에 값에 샘플은 100개 편향은 25 분산은 10을 넣습니다.

X, y = generate_data(100, 25, 10) #n_samples:100, bias:25, variance:10

 

 

이것에 상관관계 그래프를 그려보겠습니다.

plt.plot(X[:,1],y,"ko") #x_1과 y의 상관관계 그래프
plt.xlabel('$x_1$') #x축
plt.ylabel('$y$')   #y축
plt.show()

 

 

이제 경사하강법 알고리즘을 한번 적용해봐야겠죠!?

다음은 경사 하강법 알고리즘을 이용해 선형 회귀 모델의 회귀 계수(파라미터)를 학습하는 코드입니다.

def gradient_descent(X, y, theta, alpha, m, n_iter):
#theta: 초기 회귀 계수(파라미터) (n x 1 형태의 1차원 배열) alpha: 학습률 (스칼라)
#m: 샘플 수 (스칼라)
#n_iter: 경사 하강법 반복 횟수 (스칼라)
    theta_list = [] #회귀계수(파라미터)를 저장할 빈 리스트를 초기화합니다.
    cost_list = []  #비용함수 값
    iter_list = []  # 반복 과정 중 상태를 기록할 지점을 결정합니다. 전체 반복 횟수를 20으로 나눈 몫을 사용합니다
    check_points = int(n_iter/20)

    for i in range(n_iter):
        hypothesis = np.dot(X, theta) #가설함수(X:mxn, theta:nx1, hypothesis:mx1)
        #여기서 X는 m x n 행렬이고, theta는 길이가 n인 1차원 배열입니다. NumPy는 theta를 암시적으로 n x 1 형태의 벡터로 변환하여 연산이 수행됩니다. X와 theta를 내적함으로써 예측값이 됩니다. 
        # 결과적으로 가설 함수 hypothesis는 m x 1 형태의 벡터가 됩니다.
        loss = hypothesis - y #error 예측값과 실제값의 차이를 계산하여 손실(loss)을 구합니다.
        cost = np.sum(loss ** 2) / (2 * m) #비용함수: J(theta) loss제곱
       # 비록 경사 하강법의 기울기 계산에 직접적으로 사용되지는 않지만, 비용 함수의 값은 알고리즘의 진행 상황을 평가하고 이해하는 데 중요한 역할을 합니다.
        gradient = np.dot(X.T, loss) / m #비용 함수의 편미분을 계산합니다. X의 전치행렬과 손실의 내적 그리고  m으로 나눈 값입니다. x=m,2 x.t = 2,m * loss m,1 = 2,1
        theta = theta - alpha * gradient #theta update(nx1)theta:
        if i % check_points == 0:
            iter_list.append(i) #현재 반복 횟수 i를 iter_list에 추가합니다.
            theta_list.append(theta) #현재 파라미터 theta를 theta_list에 추가, 20회일 때마다 추하게 됩니다.
            cost_list.append(cost)
    return theta, np.array(theta_list), cost_list, iter_list #최종 업데이트된 theta, theta_list, cost_list, iter_list를 반환합니다.

gradient_descent 함수 완성!

 

 

 

선형 회귀 모델 학습을 위해 필요한 변수들을 설정하고, 앞서 정의한 gradient_descent 함수를 호출하여 경사 하강법을 사용해 파라미터를 학습하는 과정을 실행하겠습니다.

m, n = np.shape(X) #m: n_sample, n: n_features
n_iter = 5000  #number of iterations 5000번 반복
alpha = 0.0005 #learning rate 학습률은 0.0005
theta = np.ones(n) #parameter vector(nx1) #파라미터 벡터 theta를 모든 원소가 1인 n차원 배열로 초기화합니다.

theta,theta_list,cost_list,iter_list = gradient_descent(X, y, theta, alpha, m, n_iter)
# 앞서 정의한 gradient_descent 함수를 호출하여 경사 하강법을 사용해 파라미터를 학습합니다.
# 이 과정에서 함수에 X, y, theta, alpha, m, n_iter를 인자로 전달합니다.
# 반환된 결과는 최종 업데이트된 theta, theta_list(회귀 계수의 기록), cost_list(비용 함수 값의 기록), iter_list(반복 횟수 기록)입니다.

학습 완료 ㅎㅎ!

 

 

 

훈련 데이터(X, y)와 학습 과정에서 각 단계에서의 예측값(y_predict_step)을 시각화를 해보겠습니다.

y_predict_step = np.dot(X, theta_list.T) #y_hat:100x20, X:100x2, theta:20x2
plt.plot(X[:,1], y, "ko") #여기서 "ko"는 'black circle'을 의미합니다.
for i in range (0,y_predict_step.shape[1],2): #range(0,20,2) y_predict_step의 두 번째 차원(즉, 열)에 대해 두 단계씩 건너뛰며 반복합니다.
    plt.plot(X[:,1],y_predict_step[:,i], label='Line %d'%i)  #각 단계에서의 예측값을 그래프에 추가합니다. 라벨은 각 단계를 나타냅니다.
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.) #범례를 그래프에 추가합니다. bbox_to_anchor와 loc은 범례의 위치를 정하는 파라미터입니다.
plt.show()

 

 

실제값와 최종 회귀직선을 시각화 비교해보겠습니다.

y_predict= np.dot(X, theta) #theta_hat
plt.plot(X[:,1],y,"ro") #  부분은 원본 데이터 포인트를 그래프에 빨간색 원형 마커로 표시합니다. X[:,1]은 독립 변수 값이고, y는 종속 변수 값(실제 값)입니다
plt.plot(X[:,1],y_predict, lw=3) #이 부분은 최종 회귀 직선을 그래프에 그립니다. 이때, X[:,1]은 독립 변수 값이고, y_predict는 계산된 예측값입니다. lw=3는 선의 두께(line width)를 3
plt.show()

 

 

 

최종 세타값을 구하겠습니다.

theta
#여기서 첫 번째 값 14.64808263은 상수항 (절편, bias)이며, 두 번째 값 1.23812655는 독립 변수에 대한 계수 (기울기, weight)입니다.
array([14.64808263, 1.23812655])

 

 

 

마지막으로 비용함수가 알고리즘이 진행되는동안 어떻게 변화되는지 살펴보겠습니다. 

plt.scatter(iter_list[:50], cost_list[:50]) 
plt.xlabel('Iterations')
plt.ylabel(r'$J(\mathbf{\theta})$')
plt.show()

 

 

다음게시글에는 다중선형회귀를 다루겠습니다!