실습_2. 회귀 분석

Updated:

기울기와 절편

단순 선형회귀 분석 수식은 다음과 같습니다.

# 실습에 필요한 패키지입니다. 수정하지 마세요.
import elice_utils
import matplotlib as mpl
mpl.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
eu = elice_utils.EliceUtils()

# 실습에 필요한 데이터입니다. 수정하지마세요. 
X = [8.70153760, 3.90825773, 1.89362433, 3.28730045, 7.39333004, 2.98984649, 2.25757240, 9.84450732, 9.94589513, 5.48321616]
Y = [5.64413093, 3.75876583, 3.87233310, 4.40990425, 6.43845020, 4.02827829, 2.26105955, 7.15768995, 6.29097441, 5.19692852]

'''
beta_0과 beta_1 을 변경하면서 그래프에 표시되는 선을 확인해 봅니다.
기울기와 절편의 의미를 이해합니다.
'''

beta_0 = 0.5   # beta_0에 저장된 기울기 값을 조정해보세요. 
beta_1 = 2 # beta_1에 저장된 절편 값을 조정해보세요.

plt.scatter(X, Y) # (x, y) 점을 그립니다.
plt.plot([0, 10], [beta_1, 10 * beta_0 + beta_1], c='r') # y = beta_0 * x + beta_1 에 해당하는 선을 그립니다.

plt.xlim(0, 10) # 그래프의 X축을 설정합니다.
plt.ylim(0, 10) # 그래프의 Y축을 설정합니다.

plt.savefig("test.png")
eu.send_image("test.png")

Loss Function

앞서 배운 선형 회귀분석 모델에서 Loss Function을 구하는 방법을 알아보겠습니다.

import elice_utils
import matplotlib as mpl
mpl.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
eu = elice_utils.EliceUtils()

def loss(x, y, beta_0, beta_1):
    N = len(x)
    
    '''
    x, y, beta_0, beta_1 을 이용해 loss값을 계산한 뒤 리턴합니다.
    '''
#     Sum = 0
#     for i in range(N):
#         Sum+=(y[i] - (X[i]*beta_0 + beta_1))**2
    x = np.array(x)
    y = np.array(y)
    
    y_predicted = beta_0 * x + beta_1
    
    Sum = np.sum((y-y_predicted)**2)
    
    return Sum

X = [8.70153760, 3.90825773, 1.89362433, 3.28730045, 7.39333004, 2.98984649, 2.25757240, 9.84450732, 9.94589513, 5.48321616]
Y = [5.64413093, 3.75876583, 3.87233310, 4.40990425, 6.43845020, 4.02827829, 2.26105955, 7.15768995, 6.29097441, 5.19692852]

beta_0 = 1 # 기울기
beta_1 = 0.5 # 절편

print("Loss: %f" % loss(X, Y, beta_0, beta_1))

plt.scatter(X, Y) # (x, y) 점을 그립니다.
plt.plot([0, 10], [beta_1, 10 * beta_0 + beta_1], c='r') # y = beta_0 * x + beta_1 에 해당하는 선을 그립니다.

plt.xlim(0, 10) # 그래프의 X축을 설정합니다.
plt.ylim(0, 10) # 그래프의 Y축을 설정합니다.
plt.savefig("test.png") # 저장 후 엘리스에 이미지를 표시합니다.
eu.send_image("test.png")

Scikit-learn을 이용한 회귀분석

  • 기계학습 라이브러리 Scikit-learn을 사용하면 Loss Function을 최소값으로 만드는 B0, B1을 쉽게 구할 수 있습니다.

loss() 함수를 완성하세요. 앞서 구현한 함수를 그대로 사용할 수 있습니다.

sklearn에서는 벡터와 행렬을 나타내는 방법으로 numpy 배열을 표준으로 사용하고 있습니다. 따라서 X와 Y를 각각 np.array로 변환해야 합니다. 하나의 속성(feature)에 여러가지 값(sample)을 가지는 경우, reshape(-1, 1)을 적용하여 열벡터로 만들어야 합니다. X는 하나의 종속변수 Y에 대한 여러 값을 가지므로 reshape(-1, 1)을 적용합니다. np.array(X).reshape(-1, 1) 명령어를 이용해 길이 10인 1차원 리스트 X 를 10\times 110×1 형태의 np.array로 변경하세요.

종속변수 Y는 독립변수 X에 대한 값이므로 reshape(-1, 1)을 할 필요가 없습니다. 리스트 Y를 np.array 형식으로 변경하세요.

모델을 학습합니다.

lrmodel = LinearRegression()
lrmodel.fit(train_X, train_Y)

모델을 이용해 얻은 최적의 beta_0, beta_1 값과 Loss를 확인합니다.

import matplotlib as mpl
mpl.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from sklearn.linear_model import LinearRegression

import elice_utils
eu = elice_utils.EliceUtils()

def loss(x, y, beta_0, beta_1):
    N = len(x)
    
    x = np.array(x)
    y = np.array(y)
    
    y_predicted = beta_0 * x + beta_1
    
    Sum = np.sum((y-y_predicted)**2)
    
    return Sum
    
X = [8.70153760, 3.90825773, 1.89362433, 3.28730045, 7.39333004, 2.98984649, 2.25757240, 9.84450732, 9.94589513, 5.48321616]
Y = [5.64413093, 3.75876583, 3.87233310, 4.40990425, 6.43845020, 4.02827829, 2.26105955, 7.15768995, 6.29097441, 5.19692852]

# X는 하나의 행에 여러데이터가 들어가있다
# 여러개의 행에 하나의 데이터가 들어가도록 변경해 준다

train_X = np.array(X).reshape(-1,1)
train_Y = np.array(Y)
print(train_X)
print(train_Y)

'''
여기에서 모델을 트레이닝합니다.
'''
lrmodel = LinearRegression()
lrmodel.fit(train_X, train_Y)

'''
loss가 최소가 되는 직선의 기울기와 절편을 계산함
'''
beta_0 = lrmodel.coef_[0]   # lrmodel로 구한 직선의 기울기 .단순회귀분석이기 때문에 [0]번째만
beta_1 = lrmodel.intercept_ # lrmodel로 구한 직선의 y절편

print("beta_0: %f" % beta_0)
print("beta_1: %f" % beta_1)
print("Loss: %f" % loss(X, Y, beta_0, beta_1))

plt.scatter(X, Y) # (x, y) 점을 그립니다.
plt.plot([0, 10], [beta_1, 10 * beta_0 + beta_1], c='r') # y = beta_0 * x + beta_1 에 해당하는 선을 그립니다.

plt.xlim(0, 10) # 그래프의 X축을 설정합니다.
plt.ylim(0, 10) # 그래프의 Y축을 설정합니다.
plt.savefig("test.png") # 저장 후 엘리스에 이미지를 표시합니다.
eu.send_image("test.png")

다중선형회귀분석

다중 선형회귀 분석(Multiple Linear Regression)은 데이터의 여러 변수(features) XX를 이용해 결과 YY를 예측하는 모델입니다.

다음 모델을 직접 구현해보겠습니다.

Sales = B0X1 + B1X2 + B2*X3 + B3

./data/Advertising.csv 에 위치한 파일로부터 데이터를 읽어 X와 Y를 만듭니다.

X는 (200, 3) shape을 가진 2차원 np.array로 , TV, Newspaper Column에 해당하는 데이터를 저장합니다. Y는 (200,) 의 shape을 가진 1차원 np.array로 Sales Column에 해당하는 데이터를 저장합니다.

X = [[ 230.1   37.8   69.2]
     [  44.5   39.3   45.1]
     [  17.2   45.9   69.3]
     [ 151.5   41.3   58.5]
      ...
Y = [ 22.1  10.4   9.3  18.5 ...

Scikit-learn 라이브러리를 이용해 다중 선형회귀 분석을 진행하세요.

학습된 모델을 이용해 FB, TV, Newspaper의 값에 따라 예상되는 Sales 값을 expected_sales 에 작성하세요.

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score

'''
./data/Advertising.csv 에서 데이터를 읽어, X와 Y를 만듭니다.

X는 (200, 3) 의 shape을 가진 2차원 np.array,
Y는 (200,) 의 shape을 가진 1차원 np.array여야 합니다.

X는 FB, TV, Newspaper column 에 해당하는 데이터를 저장해야 합니다.
Y는 Sales column 에 해당하는 데이터를 저장해야 합니다.
'''

import csv
csvreader = csv.reader(open("data/Advertising.csv"))

x = []
y = []

next(csvreader)
for line in csvreader :
    x_i = [ float(line[1]), float(line[2]), float(line[3]) ]
    y_i = float(line[4])
    x.append(x_i)
    y.append(y_i)

X = np.array(x)
Y = np.array(y)
print(X)
print(Y)

lrmodel = LinearRegression()
lrmodel.fit(X, Y)

beta_0 = lrmodel.coef_[0] # 0번째 변수에 대한 계수 (페이스북)
beta_1 = lrmodel.coef_[1] # 1번째 변수에 대한 계수 (TV)
beta_2 = lrmodel.coef_[2] # 2번째 변수에 대한 계수 (신문)
beta_3 = lrmodel.intercept_ # y절편 (기본 판매량)

print("beta_0: %f" % beta_0)
print("beta_1: %f" % beta_1)
print("beta_2: %f" % beta_2)
print("beta_3: %f" % beta_3)

def expected_sales(fb, tv, newspaper, beta_0, beta_1, beta_2, beta_3):
    '''
    FB에 fb만큼, TV에 tv만큼, Newspaper에 newspaper 만큼의 광고비를 사용했고,
    트레이닝된 모델의 weight 들이 beta_0, beta_1, beta_2, beta_3 일 때
    예상되는 Sales 의 양을 출력합니다.
    '''
    Sum = fb*beta_0 + tv*beta_1 + newspaper*beta_2 + beta_3
    
    return Sum

print("예상 판매량: %f" % expected_sales(10, 12, 3, beta_0, beta_1, beta_2, beta_3))

숙제 - 다항식 회귀분석

다항식 회귀분석(Polynomial Linear Regression)은 다중 선형 회귀분석과 원리가 같습니다. 다만 데이터에 전처리를 함으로써 새로운 변수 간의 조합을 만들어낸 뒤 회귀분석을 진행하는 것이 차이입니다.

다항식 회귀분석을 통해 MSE(Mean Squared Error) 값을 원하는 수준까지 맞춰보겠습니다.

MSE란 평균제곱오차를 의미하며, 통계적 추정에 대한 정확성의 지표로 널리 이용됩니다.

Overfitting 데이터는 크게 트레이닝(training)과 테스트(Test) 세트로 나누어집니다. 트레이닝 데이터는 모델을 학습할 때, 테스트 데이터는 학습한 모델을 평가할 때 사용됩니다.

두 세트로 나누어서 모델을 각각 적용해보는 이유는, 만든 머신러닝 모델이 잘 예측하는지 검증하기 위함입니다. 이를 교차검증이라고 합니다.

모델을 복잡하게 만들면 트레이닝 데이터에서 정확도를 높힐 수 있지만 동일한 모델을 테스트 데이터에 적용하면 과적합(Overfitting) 현상이 일어나게 됩니다.

과적합 현상은 트레이닝 데이터에만 적합하게끔 모델을 작성하면 데이터 전체에 대해서는 모델이 잘 적용되지 않는 것입니다.

sklearn의 model_selection 에서 교차검증 기능을 제공합니다. 실습의 맨 위 부분에서 해당 모듈을 추가합니다.

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(X_poly, Y, test_size=0.2, random_state=0)

위 코드의 의미는 데이터의 80%를 모델 트레이닝에 사용하고, 나머지 20%를 모델 검증에 사용한다는 의미입니다.

random_state는 트레이닝과 테스트 데이터 그룹을 나눌 때 사용되는 난수 시드입니다. 이 값을 변경하여 트레이닝과 테스트 그룹을 임의의 다른 값으로 초기화할 수 있습니다.

  • 숙제 시작하기

주어진 모델에서 변수의 조합을 더하거나 빼면서 MSE의 값을 최대한 낮춰보세요.

테스트 데이터에서의 MSE를 1 미만으로 만들어보세요. 모델을 복잡하게 만들 경우 과적합이 일어나는 점을 주의하세요.

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import csv

'''
./data/Advertising.csv 에서 데이터를 읽어, X와 Y를 만듭니다.

X는 (200, 3) 의 shape을 가진 2차원 np.array,
Y는 (200,) 의 shape을 가진 1차원 np.array여야 합니다.

X는 FB, TV, Newspaper column 에 해당하는 데이터를 저장해야 합니다.
Y는 Sales column 에 해당하는 데이터를 저장해야 합니다.
'''
csvreader = csv.reader(open('./data/Advertising.csv'))
X = []
Y = []

next(csvreader)
for line in csvreader :
    x_i = [ float(line[1]), float(line[2]), float(line[3]) ]
    y_i = float(line[4])
    X.append(x_i)
    Y.append(y_i)


# 다항식 회귀분석을 진행하기 위해 변수들을 조합합니다.
X_poly = []
for x_i in X:
    X_poly.append([
        x_i[0], # X_1^2
        x_i[1], # X_2
        x_i[0] * x_i[1],
        x_i[0] * x_i[1] * x_i[2],
        x_i[0]**2
        #x_i[1]**2
        #x_i[2]
        #x_i[2] **2
        #x_i[1] * x_i[2], # X_2 * X_3
        #x_i[2] ** 2# X_3
    ])

# X, Y를 80:20으로 나눕니다. 80%는 트레이닝 데이터, 20%는 테스트 데이터입니다.
x_train, x_test, y_train, y_test = train_test_split(X_poly, Y, test_size=0.2, random_state=0)

# x_train, y_train에 대해 다항식 회귀분석을 진행합니다.
lrmodel = LinearRegression()
lrmodel.fit(x_train, y_train)

#x_train에 대해, 만든 회귀모델의 예측값을 구하고, 이 값과 y_train 의 차이를 이용해 MSE를 구합니다.
predicted_y_train = lrmodel.predict(x_train)
mse_train = mean_squared_error(y_train, predicted_y_train)
print("MSE on train data: {}".format(mse_train))

# x_test에 대해, 만든 회귀모델의 예측값을 구하고, 이 값과 y_test 의 차이를 이용해 MSE를 구합니다. 이 값이 1 미만이 되도록 모델을 구성해 봅니다.
predicted_y_test = lrmodel.predict(x_test)
mse_test = mean_squared_error(y_test, predicted_y_test)
print("MSE on test data: {}".format(mse_test))

영어단어의 특성과 단어의 사용 빈도수

두 번째 프로젝트에서는 약 1억 개의 영어 단어로 구성된 British National Corpus (BNC) 텍스트 코퍼스(단어모음)를 다루어봅니다.

words.txt 파일에 저장된 단어 10,000개의 빈도수를 그래프로 시각화한 후 선형회귀법을 적용해보겠습니다. 주석을 읽고 실행 결과가 출력되도록 do_linear_regression() 함수를 완성하세요.

  • 부가 설명

지프의 법칙에 따르면 자연어 코퍼스의 단어들을 순서대로 나열할 때 사용 빈도가 가장 높은 단어는 두 번째 단어 보다 빈도가 약 두 배 높으며, 세 번째 단어보다는 빈도가 세 배 높습니다.

즉, 거듭제곱 분포를 보이기 때문에 주석을 해제한 후 log(X), log(Y)를 적용하면 더욱 정확하게 선형회귀 분석을 할 수 있습니다.

import operator
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import elice_utils
import csv

def main():
    words = read_data()
    words = sorted(words, key = lambda x : x[1] , reverse = True) # words.txt 단어를 빈도수 순으로 정렬합니다.
    
    
    # 정수로 표현된 단어를 X축 리스트에, 각 단어의 빈도수를 Y축 리스트에 저장합니다.  
    X = list(range(1, len(words)+1))
    Y = [x[1] for x in words]
    
    
    
    # X, Y 리스트를 array로 변환한 후 각 원소 값에 log()를 적용합니다.
    X, Y = np.array(X), np.array(Y)  
    
    X, Y = np.log(X), np.log(Y)
    
    print(X)
    print(Y)
    
    # 기울기와 절편을 구한 후 그래프와 차트를 출력합니다. 
    slope, intercept = do_linear_regression(X, Y)
    draw_chart(X, Y, slope, intercept)
    
    return slope, intercept


# read_data() - words.txt에 저장된 단어와 해당 단어의 빈도수를 리스트형으로 변환합니다.
def read_data():
    # words.txt 에서 단어들를 읽어, 
    # [[단어1, 빈도수], [단어2, 빈도수] ... ]형으로 변환해 리턴합니다.
    words = []

    filename = 'words.txt'

    x = []
    y = []

    with open(filename) as data:
        lines = data.readlines()
        
    for line in lines:
        word = line.replace('\n','').split(',')
        word[1] = int(word[1])
        words.append(word)
    return words

# do_linear_regression() - 임포트한 sklearn 패키지의 함수를 이용해 그래프의 기울기와 절편을 구합니다.
def do_linear_regression(X, Y):
    # do_linear_regression() 함수를 작성하세요. 
    X = X.reshape(-1,1)
    li = LinearRegression()
    li.fit(X,Y)
    
    slope = li.coef_[0] # 2번째 변수에 대한 계수 (신문)
    intercept = li.intercept_ #
    

    return (slope, intercept)


# draw_chart() - matplotlib을 이용해 차트를 설정합니다.
def draw_chart(X, Y, slope, intercept):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    plt.scatter(X, Y)

    # 차트의 X, Y축 범위와 그래프를 설정합니다.
    min_X = min(X)
    max_X = max(X)
    min_Y = min_X * slope + intercept
    max_Y = max_X * slope + intercept
    plt.plot([min_X, max_X], [min_Y, max_Y], 
             color='red',
             linestyle='--',
             linewidth=3.0)
    
    # 기울과와 절편을 이용해 그래프를 차트에 입력합니다.
    ax.text(min_X, min_Y + 0.1, r'$y = %.2lfx + %.2lf$' % (slope, intercept), fontsize=15)
    
    plt.savefig('chart.png')
    elice_utils.send_image('chart.png')

if __name__ == "__main__":
    main()

Leave a comment