본문 바로가기
카테고리 없음

파도!(12) - 무신 러닝? 머신러닝! - 리니어 리그레션 ( LinearRegression )

'파도'는 파이스크립트 도전기의 줄임말입니다.

지난 게시글까지는 파이스크립트에서 csv 로 된 데이터를 불러와
그래프로 표현하는 것이 중점적인 내용이었지요.

https://itadventure.tistory.com/552

 

파도!(11) - 꺾은선 그래프 - 주단위 매출

'파도'는 파이스크립트 도전기의 줄임말입니다. 지난 게시글에 연재되는 글입니다. https://itadventure.tistory.com/551 파도!(10) - 씨본으로 예쁜 그래프 꾸미기 '파도'는 파이스크립트 도전기의 줄임말

itadventure.tistory.com

 

이제 크레이가 그동안 학습하던 파이스크립트에서 머신러닝을 처리하는 부분을 다뤄볼텐데요.
이 방법이 제대로 된 접근방법인지는 100% 확신하지는 못합니다만,
파이썬이 아닌 파이스크립트에서 머신러닝을 다루는 방법은 아직 대부분 접근하지 않은 시도인만큼 
도전기에 초점을 맞춰주시면 감사하겠습니다 :)

 

무신 러닝? 머신 러닝!

 

머신 러닝은 기계(머신:Machine) 학습(러닝:Learning)이라는 의미입니다.
합쳐서 Machine Learning 이라고 부르지요.

앞에서 읽으면 머신 러닝이고, 뒤에서 읽으면 러닝 머신이군요?...
( 러닝머신은 그 헬스장에 있는 달리는 기계일까요..?
  아니야! 그건 Running Machine 이야! 그것도 콩글리쉬로! )

... 넘어갑시다...

머신러닝은 인공지능의 한 분야라고 하는데요.
아직 깊이 들어가지 않아 확실하다고는 말은 못하지만,
머신러닝은 통계학을 바탕으로 제공된 자료를 토대로 결과를 도출하는 것 뿐이지,
미래를 예지할 수 있는 어떤 능력을 가지고 있지는 않은 것으로 보입니다.

다만 매우 빠른 연산속도와 실수가 없는 계산 능력으로
사람이 몇년 걸려야 계산할 문제들을 단, 몇분만에 해결할 수 있는 것이지요.

 

싸이킷 러닝 라이브러리 선언하기

 

인공 지능 기술 자체를 개인이 개발하는 것은 상당히 어렵습니다.
우선 그 방대한 양과 높은 수학적 지식, 통계학 등이 바탕이 되어야 하는데요.
이제 시대가 흘러 파이썬에서 사용할 수 있는 여러 인공지능 관련 기술을 무료 공개하기에 이르렀습니다.
그 중 하나가 바로 싸이킷-런 라이브러리입니다.
이제 관련지식이 없어도 그냥 사용만 하면 되는 단계에 이르렀지요.

파이스크립트에서 싸이킷 러닝 라이브러리를 사용하려면
<py-env>  태그 내에 scikit-learn 을 사용 가능하도록 선언해 주어야 합니다 ( 빨간색 )


<py-env>
  - pandas
  - matplotlib
  - seaborn
  - scikit-learn
  - paths:
    - ./NANUMMYEONGJO.TTF
    - ./NANUMMYEONGJOBOLD.TTF
</py-env> 


처음에는 sklearn 으로 선언해보았는데 안되서 이리 저리 헤메다가 scikit-learn 으로 적어주어야 한다는 사실을 알게 되었지요 :)

 

리니어 리그레션(Linear Regression)

 

싸이킷 런 라이브러리에는 여러가지 머신 러닝 기술들이 있는데요.
그 중 기초중의 기초가 선형회귀 ( Linear regression : 리니어 리그레션 ) 기술입니다.
아래는 싸이킷 런 라이브러리에서 선형회귀 기술을 하나 꺼내와
학습할 '선형회귀모델'을 선언하는 코드입니다. 이제 선형회귀모델을 학습시키면 되는 것입니다.

from sklearn.linear_model import LinearRegression
선형회귀모델 = LinearRegression()

 

기계 학습 방법


모델을 학습시키는 방법은 이렇습니다.
먼저 머신러닝이 예측할 결과를 정합니다.
크레이는 머신러닝이 매출량을 예측하는 것을 목표로 하겠습니다.

그러면 아래와 같이 데이터를 나누어서 제공하는 것이지요.

훈련용데이터 : 연도, 월, 일, 주(1년중 몇번째 주인지)
훈련용목표 : 매출량

'훈련용데이터'는 되도록 많은 데이터가 주어지면 좋은데요.
그렇다고 매출량 자체를 훈련용데이터에 넣어서는 예측의 의미가 없겠지요?
여기서는 매출량을 빼고 연도, 월, 일, 주 정도로만 제공하는 것으로 하겠습니다.

중요한 것은 이러이러한 '훈련용 데이터'에는
이러이러한 '훈련용 목표'를 패턴으로 매칭하면 되는 것이지요.

훈련용 데이터는 파이썬에서 주로 활용되는 넘파이(numpy) 데이터 형태로 제공해야 하는데요.
준비되었다고 하고 아래와 같이 훈련을 해주면 됩니다.

선형회귀모델.fit(훈련용데이터, 훈련용목표)


그러면 선형회귀 모델은 훈련용 데이터를 바탕으로 훈련을 시작합니다.

 

모델의 예측

 

이렇게 학습한 선형회귀 모델은 다른 주어진 데이터를 바탕으로 결과를 예측하게 되는데요.
사실 연도, 월, 일, 주 정도의 데이터만 가지고 매출량을 예측한다는 것은 불가능합니다.
그래도 월별로 매출량이 들쑥날쑥한 부분은 어느정도 따라가 주기는 하더라구요.

연도, 월, 일, 주 단위의 데이터를 테스트데이터로 제공하면
매출량 결과를 예측하는 코드는 아래와 같습니다.

테스트목표예측 = 선형회귀모델.predict(테스트데이터)


그리고 위 결과를 그래프로 표시해주면 끝!
인공지능을 활용하는 코드가 이정도라니.. 너무 간단하지 않나요?
도리어 데이터를 준비하고, 그래프로 표시하는 부분의 코드가 더 복잡합니다 :)

# 선형회귀 기술을 꺼내와 모델을 선언
from sklearn.linear_model import LinearRegression
선형회귀모델 = LinearRegression()

# 훈련
선형회귀모델.fit(훈련용데이터, 훈련용목표)

# 얘축
테스트목표예측 = 선형회귀모델.predict(테스트데이터)

 

전체코드

 

위 과정을 머신러닝에 활용하는 전체 코드는 아래와 같습니다.
편의상 이해를 위해 변수명칭을 한글로 정의하였으나 웹 페이지에서 잘 작동되는 코드입니다.
참고로 이번에도 
common.py 라는 파일을 별도로 분리하였습니다.

common.py

def createElementDiv(document, Element, name):
    element = document.createElement('div')
    element.id = name
    document.body.append(element)
    return Element(name)


웹페이지 파일 - 파일명은 자유롭게

<html> 
    <head> 
      <link rel="stylesheet" 
        href="https://pyscript.net/alpha/pyscript.css" /> 
      <script defer 
        src="https://pyscript.net/alpha/pyscript.js"></script> 
<py-env>
  - pandas
  - matplotlib
  - seaborn
  - scikit-learn
  - paths:
    - ./NANUMMYEONGJO.TTF
    - ./NANUMMYEONGJOBOLD.TTF
    - ./common.py
</py-env>
    </head>
  <body> 
    <link rel="stylesheet" href="pytable.css"/>
<py-script>
import pandas as pd
from pyodide.http import open_url
from common import *
import numpy as np

from datetime import datetime

# 경고 문구 제거
import warnings
warnings.filterwarnings( 'ignore' )

# 판다스에서 csv 를 데이터 프레임으로 읽어옴
매출데이터 = pd.read_csv(open_url(
  "http://dreamplan7.cafe24.com/pyscript/csv/avocado.csv"
))      

# 2개 필드만 추려서 데이터 프레임을 다시 만듬
매출데이터 = 매출데이터[[
  'Date', 
  'Total Volume'
]]   

# 영문 제목을 한글로 변경
매출데이터.columns = [
  '날짜', 
  '매출량'
]

# 주간 매출량 그룹
주간매출데이터 = 매출데이터.fillna(0) \
  .groupby('날짜', as_index=False)[['매출량']] \
  .sum() \
  .sort_values(
    by='날짜', 
    ascending=True
  )

# 날짜(시간값) 추가
주간매출데이터.insert(1, '날짜(시간값)',
  '',
  True)
  
for i in 주간매출데이터['날짜'].index:
  주간매출데이터['날짜(시간값)'].loc[i]=time.mktime(
    datetime.strptime(
      주간매출데이터['날짜'].loc[i], 
      '%Y-%m-%d'
    ).timetuple()
  )

# 10000으로 나눈 매출량 필드 추가
주간매출데이터.insert(2, '매출량(만)', 
  주간매출데이터['매출량']/10000, 
  True)

# 훈련학습용으로 날짜를 연도, 월, 일로 나눈다
주간매출데이터.insert(4, '연도', '', True)
주간매출데이터.insert(5, '월', '', True)
주간매출데이터.insert(6, '일', '', True)
주간매출데이터.insert(7, '주', '', True)

for i in 주간매출데이터['날짜'].index:
  임시=str(주간매출데이터['날짜'].loc[i]).split('-')
  연도=int(임시[0])
  월=int(임시[1])
  일=int(임시[2])
  주간매출데이터['연도'].loc[i]=연도
  주간매출데이터['월'].loc[i]=월
  주간매출데이터['일'].loc[i]=일
  주간매출데이터['주'].loc[i]=str(
    datetime(연도, 월, 일).isocalendar()[1]
  )

createElementDiv(
  document, 
  Element, 
  'output2'
).write(주간매출데이터)

주간매출데이터훈련_넘파이 = 주간매출데이터[['날짜(시간값)', '연도', '월', '일', '주']].to_numpy()
주간매출데이터목표_넘파이 = 주간매출데이터['매출량(만)'].to_numpy()

from sklearn.model_selection import train_test_split

훈련용데이터, 테스트데이터, 훈련용목표, 테스트목표 = \
  train_test_split(
    주간매출데이터훈련_넘파이, 
    주간매출데이터목표_넘파이,
    random_state=100,
    shuffle=False)

# 선형 회귀 알고리즘
# 훈련, 최적의 그래프를 찾아준다
from sklearn.linear_model import LinearRegression
선형회귀모델 = LinearRegression()
선형회귀모델.fit(훈련용데이터, 훈련용목표)

훈련용목표예측 = 선형회귀모델.predict(훈련용데이터)
테스트목표예측 = 선형회귀모델.predict(테스트데이터)

import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import matplotlib as mat

# 기준 폰트 변경 : legend 의 한글을 적용하려면 필수
fm.fontManager.addfont('./NANUMMYEONGJO.TTF') #  폰트명 : NanumMyeongjo
mat.rc('font', family='NanumMyeongjo')

# 개별 폰트 적용
NanumMyengjo = fm.FontProperties(
  fname='./NANUMMYEONGJO.TTF'
)
NanumMyengjoBold = fm.FontProperties(
  fname='./NANUMMYEONGJOBOLD.TTF'
)

# 그래프
fig = plt.figure(
  figsize=(15, 7)
)
plt.xticks(주간매출데이터['날짜(시간값)'].to_numpy(), 주간매출데이터[['날짜']].to_numpy()[:,0], rotation=90, fontsize=8)

plt.title('주간 아보카도 매출량',  
  fontproperties=NanumMyengjoBold, 
  fontsize=32
);

plt.plot(        
  훈련용데이터[:,0],
  훈련용목표,
  marker='o',
  color='#c14549',
  label='원본'
)
plt.plot(        
  훈련용데이터[:,0],
  훈련용목표예측,
  marker='d',
  color='blue',
  label='훈련패턴'
)

plt.plot(        
  테스트데이터[:, 0],
  테스트목표,
  marker='o',
  color='#c14549'
)

plt.plot(        
  테스트데이터[:, 0],
  테스트목표예측,
  marker='d',
  color='green',
  label='예측패턴'
)

plt.xlabel('날짜', fontsize=16)
plt.ylabel('매출량(단위:만)', fontsize=12)

plt.legend(
  shadow=True
)

ax = plt.gca()
# 축만 그리드
ax.xaxis.grid(True)

# 배경색, 마진 조정
ax.set_facecolor('#e8e7d2')
ax.margins(x=0.01, y=0.02)

# 주위 이상한 여백 없애기
fig.tight_layout() 
fig

</py-script> 
  </body> 
</html>

 

결과는 아래와 같고 크레이의 홈페이지 아래 URL에서도 확인하실 수 있습니다.
http://dreamplan7.cafe24.com/pyscript/py10-2.html

빨간 선원본데이터이고 파랑 선이 원본을 바탕으로 한 훈련결과, 녹색 선 예측결과입니다.

 

이제 소스 중 주요 변경점 부분을 소개해드리겠습니다.

 

공용 함수 파일

 

앞으로 선언될 공용 기능들을 별도의 파일로 나누어 놓기 위해 공용 파일을 하나 선언했는데요.
파이선 공용 파일이기 때문에 확장자는 .py 를 사용하는 것이 좋습니다.
createElementDiv() 함수를 별도로 common.py 파일로 빼놓았으며
이렇게 별도로 파일을 빼놓으면 다른 소스에서도 이 함수를 가져다 사용할 수 있습니다.

common.py

def createElementDiv(document, Element, name):
    element = document.createElement('div')
    element.id = name
    document.body.append(element)
    return Element(name)

 

이 파일을 가져다 쓰기 위해서는 2가지 부분이 선언되어야 하는데요.
먼저 <py-env> 태그에서 이 파일을 사용하겠다고 선언해야 하고 ( 빨간색 ),


<py-env>
  - pandas
  - matplotlib
  - seaborn
  - scikit-learn
  - paths:
    - ./NANUMMYEONGJO.TTF
    - ./NANUMMYEONGJOBOLD.TTF
    - ./common.py
</py-env>



<py-script>  태그 안에서 불러오는 부분이 추가되어야 합니다. ( 빨간색 )


<py-script>
import pandas as pd
from pyodide.http import open_url
from common import *


 

경고 제거

 

파이 스크립트에서 그래프에 행을 추가하면 뜨는 특유의 경고 메시지가 있는데요.
아래는 경고 메시지가 뜨지 않게 하는 코드입니다.
개발할 때는 이 코드가 필요없지만, 서비스 단계에서는 필요하지요.

# 경고 문구 제거
import warnings
warnings.filterwarnings( 'ignore' )

 

데이터 프레임에 연도, 월, 일, 주 칸을 추가

 

머신러닝은 오로지 숫자값만을 기반으로 훈련합니다.
그래서 2015-01-04 일과 같은 날짜로 된 단어를 훈련하지는 못하지요.
그렇기 때문에 연도, 월, 일을 각각 분리해야 합니다.
여기서는 일년중 몇번째 주(Week)인지까지의 정보까지 함께 추가해주었는데요.

# 훈련학습용으로 날짜를 연도, 월, 일로 나눈다
주간매출데이터.insert(4, '연도', '', True)
주간매출데이터.insert(5, '월', '', True)
주간매출데이터.insert(6, '일', '', True)
주간매출데이터.insert(7, '주', '', True)

for i in 주간매출데이터['날짜'].index:
  임시=str(주간매출데이터['날짜'].loc[i]).split('-')
  연도=int(임시[0])
  월=int(임시[1])
  일=int(임시[2])
  주간매출데이터['연도'].loc[i]=연도
  주간매출데이터['월'].loc[i]=월
  주간매출데이터['일'].loc[i]=일
  주간매출데이터['주'].loc[i]=str(
    datetime(연도, 월, 일).isocalendar()[1]
  )


그 결과는 아래와 같습니다.

 

머신러닝용 입력 데이터 정의

 

머신러닝에 훈련할 데이터는 파이썬에서 주로 사용하는 넘파이라는 형태로 제공이 되어야 하는데요.
아래는 데이터프레임에서 연도, 월, 일, 주 값을 뽑아 넘파이로 선언하는 과정입니다.
이 때 절대적으로 사용될 날짜(시간값)을 함께 넣어주어야 합니다.

주간매출데이터훈련_넘파이 = 주간매출데이터[['날짜(시간값)', '연도', '월', '일', '주']].to_numpy()


그리고 매출량에 해당하는 훈련목표 데이터도 넘파이로 선언합니다.

주간매출데이터목표_넘파이 = 주간매출데이터['매출량(만)'].to_numpy()

 

훈련/테스트 데이터 분리

 

예측이 어느정도 일치하는지 확인하기 위해 일부 데이터는 훈련을 하지 않고 남겨두어야 하는데요.
이를테면 4분의 3은 훈련을 하고 4분의 1은 남겨두었다가 예측결과와 비교해 보는 방법이 있습니다.
그러기 위해서는 훈련용데이터와 훈련목표 각각 4분의 3과 4분의 1에 해당하는 데이터를 떼어 놓아야 합니다.
다행스럽게도 싸이킷 머신러닝에는 이렇게 나눠주는 기능이 이미 있는데요.
아래 코드는 각각의 데이터를 4개의 세트로 나누어 줍니다. 결과는 당연히 넘파이 형태이지요.

from sklearn.model_selection import train_test_split

훈련용데이터, 테스트데이터, 훈련용목표, 테스트목표 = \
  train_test_split(
    주간매출데이터훈련_넘파이, 
    주간매출데이터목표_넘파이,
    random_state=100,
    shuffle=False)

 

4분의 3으로 학습 시작.

 

이제 위 데이터를 바탕으로 선형회귀모델을 생성하고 이를 학습하는 코드는 아래와 같습니다.
입력되는 훈련용데이터, 훈련용 목표는 앞에서 분리한 4분의 3에 해당하는 데이터입니다.

from sklearn.linear_model import LinearRegression
선형회귀모델 = LinearRegression()
선형회귀모델.fit(훈련용데이터, 훈련용목표)

 

매출량 예측

 

아래는 학습한 데이터를 바탕으로 테스트데이터의 결과(매출량)를 예측하는 코드입니다.
여기서는 학습 데이터도 예측하는 코드를 작성했는데요.
학습한 데이터는 이미 학습했으니까 100% 맞추는게 아닌기요? 천만의 말씀입니다.

훈련용목표예측 = 선형회귀모델.predict(훈련용데이터)
테스트목표예측 = 선형회귀모델.predict(테스트데이터)

 

그래프 X축 대체 표시

 

위 데이터를 그대로 그래프에 반영하면 어떻게 될까요?
X축 데이터가 날짜 데이터인데로 아래와 같이 표시됩니다.

이는 날짜가 시간값으로 주어졌기 때문인데요.
넘파이는 숫자 데이터만 관리하기 때문에 어쩔수 없습니다.. 라고 말한다면 실망스럽겠지요?
다행스럽게도 이 X축의 표시결과를 바꿔치기 할 수 있습니다.
아래 코드는 주간매출데이터의 '날짜(시간값)' 배열을
주간매출데이터의 '날짜' 배열로 바꿔서 표시해주는 코드입니다.

plt.xticks(
  주간매출데이터['날짜(시간값)'].to_numpy(), 
  주간매출데이터[['날짜']].to_numpy()[:,0], 
  rotation=90, fontsize=8)

4개의 꺾은선을 하나의 그래프에 표시


그래프를 표시하는 부분은 4개의 코드가 사용되었습니다.

plt.plot(        
  훈련용데이터[:,0],
  훈련용목표,
  marker='o',
  color='#c14549',
  label='원본'
)

plt.plot(        
  훈련용데이터[:,0],
  훈련용목표예측,
  marker='d',
  color='blue',
  label='훈련패턴'
)

plt.plot(        
  테스트데이터[:, 0],
  테스트목표,
  marker='o',
  color='#c14549'
)

plt.plot(        
  테스트데이터[:, 0],
  테스트목표예측,
  marker='d',
  color='green',
  label='예측패턴'
)

 

이는 그래프에서 보실 때 각각 다음 순번에 해당합니다.

 

마무~리

 

여기까지가 소스의 주요  변경점인데요.
처음에는 일부 결과가 심하게 어긋나는 부분에 대해 다소 실망했지만,
전통 방식으로 프로그램 코드를 짠다고 해도 이런 결과를 도출하려면 상당한 시일이 걸릴것 같기 때문에
인공지능 기술을 높게 평가하기로 했습니다.

그렇게 마음 먹으니까 머신 러닝이 대견해 보이더라구요 :)

 

필요하신 분께 도움이 되셨을지요 :)
방문해주시는 모든 분들께 늘 감사드립니다.

유익하셨다면 공감  한방, 댓글은 굿잡!
감사합니다~


다음 게시글 : https://itadventure.tistory.com/554

 

파도!(13) - 음? 인공지능 적중율이?! - 평균가격 추가

'파도'는 파이스크립트 도전기의 줄임말입니다. 지난 게시글에서 이어지는 내용입니다 : https://itadventure.tistory.com/553 파도!(12) - 무신 러닝? 머신러닝! - 리니어 리그레션 ( LinearRegression ) '파도'..

itadventure.tistory.com