반응형

컴퓨터 비전이나 텍스트 분석은 성능 좋은 오픈 API나 노하우가 축적되고 공유되어 빠르게 발전하고 있다. 하지만 투자 업계에서는 금융 데이터를 손쉽게 처리해주는 판다스 같은 라이브러리가 있어도, 성능 좋은 알고리즘이나 방법론이 공유되는 경우가 거의 없다.

세계 경제가 긴밀하게 연동하고 경제 주체들의 투자 패턴이 다양해짐에 따라 고려해야 할 변수가 기하급수적으로 증가한 반면 시계열 데이터를 기본으로 한 투자 데이터는 그 양이 한정적이다. 애널리스트 분석에 자주 사용되는 OECD 경기선행지수, 국가별 GDP, 금리 자료 등은 업데이트 주기가 길다. 이러한 데이터의 한계로 인해 좋은 모델을 만들기가 어렵다고 한다. 

금융은 자본주의 사회에서 우리의 삶에 깊숙이 침투하여 개인의 일상생활과 밀접한 관계를 맺고 있고 우리가 살아가며 생산하는 수많은 데이터를 통해 연결되어 있다. 많은 의사결정 프로세스가 데이터 기반으로 전환하는 시대에 투자라고 예외일 수 없다. 나날이 복잡다단해지는 투자 환경에서 사람의 '감'에 기반한 투자 의사결정은 점점 힘을 잃어갈 것이다.

투자 영역에서 활용하는 데이터('실전 금융 머신러닝 완벽분석'의 저자인 마르코스 로페즈가 분류)

재무제표 데이터 마켓 데이터 분석 데이터 대체 데이터
자산 가격/변동성 애널리스트 추천 위성/CCTV 이미지
부채 거래량 신용 등급 구글 검색어
판매량 배당 이익 예측 트윗/SNS
비용/이익 이자율 감성 분석 메타데이터
거시 변수 상장/폐지 ... ...

 

파이썬으로 만드는 투자전략과 주요 지표

1. 바이앤홀드(Buy & Hold) 전략

바이앤홀드는 주식을 매수한 후 장기 보유하는 투자 전략이다. 즉 매수하려는 종목이 충분히 저가라고 판단될 때 주기적으로 매수한 후 장기간 보유하는 식으로 투자하는 방법이다. 
주가는 예측이 불가하지만 경제가 성장함에 따라 장기적으로 우상향한다는 투자 철학의 관점에서 보면 합리적인 투자 방법이다.

주피터 노트북(Jupyter notebook) 환경에서 실행한다고 가정하고, 관련 라이브러리를 다음과 같이 설치해야 한다.

!pip install matplotlib
!pip install pandas
!pip install -U finance-datareader # 국내 금융데이터를 포함 pandas-datareader를 대체

준비가 되면, 관련 라이브러리를 불러온다

import pandas as pd
import numpy as np
import FinanceDataReader as fdr

본격적으로 바이앤홀드 전략을 구현해 보자. FinanceDataReader 라이브러리에서 직접 테슬라(TSLA) 데이터를 추출하여, 판다스의 head()함수를 사용해 데이터를 살펴본다.

df = fdr.DataReader('TSLA', '2013-01-01', '2023-08-31')
df.head()

데이터는 'Date'가 인덱스로 설정되고, 시작가(Open), 고가(High), 저가(Low), 종가(Close), 수정 종가(Adj Close), 거래량(Volume)으로 구성된다.

결측치(데이터에 값이 없는 것, NA)가 있는 지 살펴본다.

df.isin([np.nan, np.inf, -np.inf]).sum()

결측치가 없는 것을 확인했으니, 바이앤홀드 전략을 수정 종가(Adj Close)를 이용해 테스트한다.
보통 전략을 테스트할 때는 종가를 기준으로 처리한다.

수정 종가를 슬라이스해 별도의 데이터셋을 만든다.

price_df = df.loc[:,['Adj Close']].copy()
price_df.plot(figsize=(16,9))

수정 종가(Adj Close)로 DataFrame을 만들고 종가(Close) 모양이 어떻게 생겼는지 그래프를 통해 살펴본다.
전체 기간을 보면 주가가 많이 상승했다. 하지만 짧은 시점을 확인해 보면 다를 수 있다.
테슬라는 최근 2022~2023년에 주가 변동성이 매우 큰 것을 알 수 있다. 

from_date = '2022-01-01'
to_date = '2023-08-31'
price_df.loc[from_date:to_date].plot(figsize=(16,9))

2022년 400달러에 달하던 주가가 108달러까지 떨어지는 등 최고점 투자 대비 70% 가량까지 하락했다.
이렇게 최고점 대비 현재까지 하락한 비율 중 최대 하락률을 최대 낙폭(maximum draw down, MDD)이라고 한다.

그 다음으로 일별 수익률을 계산해 보자.

price_df['daily_rtn'] = price_df['Adj Close'].pct_change()
price_df.head(10)

바이앤홀드 전략에서 일별 수익률을 계산하는 이유는 매수한 시점부터 매도한 시점까지 보유하게 되는데 일별 수익률을 누적으로 곱하면 최종적으로 매수한 시점 대비 매도한 시점의 종가 수익률로 계산되기 때문이다.

다음에는 바이앤홀드 전략의 누적 곱을 계산해 보자. 판다스 cumprod() 함수를 사용하면 쉽게 계산된다.

price_df['st_rtn'] = (1+price_df['daily_trn']).cumprod()
price_head(10)
price_df['st_rtn'].plot(figsize=(16,9))

이번에는 매수 시점을 정해서 수익률을 계산해 본다.

base_date= '2020-01-02'
tmp_df = price_df.loc[base_date:,['st_rtn']] / price_df.loc[base_date,['st_rtn']]
last_date = tmp_df.index[-1]
print('누적 수익 : ',tmp_df.loc[last_date,'st_rtn'])
tmp_df.plot(figsize=(16,9))

base_date변수를 선언해 새로운 기준 일자를 정한다. 특정 일자를 기준으로 수익률을 계산하고 누적 수익을 보니 8.95까지 상승했다. 2020년 1월 2일에 처음 테슬라 주식을 매수했다면 8.95배의 수익률을 얻을 수 있었다는 뜻이다.

사실 지금 보여준 바이앤홀드 전략에는 복잡한 코딩은 없었다. 단지 주어진 기간의 수익률만 확인했을 뿐이다.
여기에 신호(매수/매도 신호)를 추가한다면 다양한 전략으로 응용할 수 있다.

반응형

- 연평균 복리 수익률(CAGR)

수익률 성과를 분석할 때 산술평균 수익률보다 기하평균 수익률을 더 선호한다. 기하평균 수익률이 복리 성질과 주가 변동이 심한 때에 따라 변동성을 표현해 주기 때문이다. 

이 CAGR 수식을 파이썬 코드로 구현하면 다음과 같다.

CAGR = price_df.loc['2023-08-30', 'st_rtn'] ** (252./len(price_df.index)) -1

우리가 가진 데이터셋의 마지막 날짜가 2023년 8월 30일이다. 해당일자의 최종 누적 수익률의 누적 연도 제곱근을 구하는 것이다. 또한 우리는 일(Daily) 데이터를 사용했으므로 전체년도를 구하기 위해 전체 데이터 기간을 252(금융공학에서 1년은 252 영업일로 계산)로 나눈 역수를 제곱(a**b) 연산한다. 그리고 -1을 하면 수익률이 나온다.

- 최대 낙폭(MDD)

최대 낙폭은 최대 낙폭 지수로, 투자 기간에 고점부터 저점까지 떨어진 낙폭 중 최댓값을 의미한다. 이는 투자자가 겪을 수 있는 최대 고통을 측정하는 지표로 사용되며, 낮을수록 좋다.

이 MDD 수식을 파이썬 코드로 구현하면 다음과 같다.

historical_max = price_df['Adj Close'].cummax()
daily_drawdown = price_df['Adj Close'] / historical_max - 1.0
historical_dd = daily_drawdown.cummin()
historical_dd.plot()

수정 종가에 cummax() 함수 값을 저장한다. cummax()는 누적 최댓값을 반환한다. 전체 최댓값이 아닌 행row별 차례로 진행함녀서 누적 값을 갱신한다. 현재 수정 종가에서 누적 최댓값 대비 낙폭률을 계산하고 cummin() 함수를 사용해 최대 하락률을 계산한다. 출력 그래프를 보면 최대 하락률의 추세를 확인할 수 있다.

- 변동성(Vol)

주가 변화 수익률 관점의 변동성을 알아보자. 변동성은 금융 자산의 방향성에 대한 불확실성과 가격 등락에 대한 위험 예상 지표로 해석하며, 수익률의 표준 편차를 변동성으로 계산한다.

만약 영업일 기준이 1년에 252일이고 일별 수익률의 표준 편차가 0.01이라면, 연율화된 변동성은 다음 수식과 같이 계산된다.

변동선 수익을 파이썬 코드로 구현하면 다음과 같다.

VOL = np.std(price_df['daily_rtn']) * np.sqrt(252.)

변동성은 수익률의 표준편차로 계산한다. 이는 일Daily 단위 변동성을 의미하는데, 루이 바슐리에의 '투기이론'에서 주가의 변동폭은 시간의 제곱근에 비례한다는 연구 결과에 따라 일 단위 변동성을 연율화할 때 252의 제곱근을 곱한다.

- 샤프 지수

샤프 지수는 위험 대비 수익성 지표라고 볼 수 있다. 

샤프 지수를 파이썬 코드로 구현하면 다음과 같다.

Sharpe = np.mean(price_df['daily_rtn']) / np.std(price_df['daily_rtn']) * np.sqrt(252.)

사후적 샤프 비율을 사용했다. 실현 수익률의 산술평균/실현 수익률의 변동성으로 계산하므로 넘파이의 평균을 구하는 함수와 연율화 변동성을 활용해 위험 대비 수익성 지표를 계산한다.

- 성과 분석 결과

최종 성과를 분석해 보자. 

CAGR = price_df.loc['2023-08-30', 'st_rtn'] ** (252./len(price_df.index)) -1
Sharpe = np.mean(price_df['daily_rtn']) / np.std(price_df['daily_rtn']) * np.sqrt(252.)
VOL = np.std(price_df['daily_rtn']) * np.sqrt(252.)
MDD = historical_dd.min()

print('CAGR : ',round(CAGR*100,2), '%')
print('Sharpe : ',round(Sharpe,2))
print('VOL : ',round(VOL*100,2), '%')
print('MDD : ',round(-1*MDD*100,2), '%')

CAGR : 55.34%
Sharpe : 1.06
VOL : 57.18%
MDD : 73.63%

테슬라는 2013년부터 현재(2023년 8월 30일 기준)까지 연평균 복리 수익률 CAGR로 55% 성장했다. 샤프 지수 Sharpe는 1 이상만 되어도 좋다고 판단한다.

다음으로 변동성 VOL을 보면 57%인 것을 확인할 수 있는데, 이는 주가 수익률이 꽤 많이 출렁인 것을 의미한다. 장기간의 시점에서 주가를 바라볼 때는 안정적으로 우상향했다고 생각할 수 있지만, 하루하루 주가 흐름은 그닥 안정적이지 않았다는 뜻이다.

마지막으로 최대 낙폭 MDD는 73%가 하락한 경우가 있음을 알 수 있다. 최고점에서 주식을 구입한 경우에는 견디가 어려운 낙폭임을 인지할 필요가 있다.

기업 성장성이 확실하다고 판단한 주식은 싸다고 생각되는 시장이 어려운 시기에 사서 장기적으로 보유하다가 고점이라고 생각되는 시점인 대중이 열광하는 시점에 처분한다는 자세를 잊지 말아야겠다.

반응형
반응형

보통 ML이 가능한 서버와 원격으로 접속하는 클라이언트 환경에서 주피터 노트북을 사용한다. 그리고, 서버는 리눅스(또는 우분투) 환경에서 사용하는 것이 일반적이라고 생각하기에 그런 환경을 염두에 두고 정리한다.

서버 주소를 알고 있다는 가정하에, SSH로 서버에 접속한다.

SSH 아이디@192.168.0.123
아이디@192.168.0.123's password:

패스워드를 입력하고, 접속하면 원격 서버에 접속하게 된다.

아이디@sss:~$ jupyter notebook --generate-config

콘솔 창에서 위 jupyter notebook --generate-config를 입력하면, jupyter_notebook_config.py 파일이 생성된다.
우분투의 ls -al 명령을 실행하면 현재 위치에 있는 폴더 목록(숨김 폴더까지 보여줌)을 보여준다. 
* 아래는 주피터 노트북 사이트에 나온 내용을 정리함

1. 단일 노트북 서버 실행

주피터 노트북 웹 애플리케이션은 서버-클라이언트 구조를 기반으로 한다. 노트북 서버는 HTTP 요청을 처리하기 위해 ZeroMQ와 Tornado를 기반으로 하는 2-프로세스 커널 아키텍처를 사용한다. 
※ 기본적으로 노트북 서버는 127.0.0.1:8888에서 로컬로 실행되며 localhost에서만 액세스할 수 있다. http://127.0.0.1:8888을 사용하여 브라우저에서 노트북 서버에 액세스할 수 있다.

2. 공개 노트북 서버 실행

웹 브라우저를 통해 원격으로 노트북 서버에 액세스하려면 공용 노트북 서버를 실행하면 된다. 공용 노트북 서버를 실행할 때 최적의 보안을 위해서는 먼저 노트북 서버 보안을 해야 한다.

비밀번호와 SSL/HTTPS로 서버를 보호해야 한다.

먼저 비밀번호를 사용해서 노트북 보안을 하려면 다음과 같은 명령어를 입력하면 된다.

jupyter notebook password

노트북 서버 보안을 위해 인정서 파일과 해시된 비밀번호 생성하는 것부터 시작한다.

아직 구성 파일이 없으면 다음 명령줄을 사용해 노트북용 구성 파일을 만든다.

jupyter notebook --generate-config

~/.jupyter 디렉토리에서, jupyter_notebook_config.py 구성 파일을 편집한다. 기본적으로 노트북용 구성파일은 모든 필드가 주석처리되어 있다. 사용하려는 명령어 옵션만 사용가능하게 주석 처리를 해제하고, 편집해야 한다. 최소한의 구성 옵션 세트는 다음과 같다.

# 인증서 파일, IP, 비밀번호에 대한 옵션을 설정하고 브라우저 자동 열기를 끕니다.
c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
c.NotebooApp.keyfile = u'/absolute/path/to/your/certificate/mykey.key'
# 공용 서버의 모든 인터페이스(ips)에 바인딩하려면 ip를 '*'로 설정합니다.
c.NotebookApp.ip = '*'
c.NotebookApp.password = u'sha1:bcd259ccf...<your hashed password here>'
c.NotebookApp.open_brower = False

# 서버 액세스를 위해 알려진 고정 포트를 설정하는 것이 좋습니다.
c.NotebookApp.port = 9999

그런 다음 jupyter notebook 명령어를 사용하여 노트북을 시작할 수 있다.

  • 'https를 사용하세요. SSL 지원을 활성화했을 땐,  일반 http://가 아닌 https://를 사용해 노트북 서버에 접속해야 한다는 것을 기억하세요. 콘솔의 서버 시작 메시지에 이 내용을 미리 알려주만 세부 사항을 간과하고 서버가 다른 이유로 응답하지 않는다고 생각하기 쉽습니다. 
  • SSL을 사용하는 경우 항상 'https://'로 노트북 서버에 접속!

이제 브라우저에서 공개 서버의 도메인 'https://your.host.com:9999'를 입력하여 당신의 공개 서버에 액세스할 수 있다.

반응형

3. 방화벽 설정

올바르게 작동하게 하려면, 클라이언트에서 연결할 수 있도록 주피터 노트북 서버가 돌아가는 컴퓨터 방화벽(공유기가 있는 경우 공유기 방화벽 설정도 필요)의 주피터 노트북 구성 파일(jupyter_notebook_config.py)의 액세스 포트(c.NotebooApp.port) 설정에 구성되어 있어야 한다. 방화벽은 49152에서 65535까지의 포트에서 127.0.0.1(localhost)의 연결을 허용해야 한다. 이러한 포트는 서버가 노트북 커널과 통신하는 데 사용된다. 커널 통신 포트는 ZeroMQ에 의해 무작위로 선택되며 커널당 여러 연결이 필요할 수 있으므로 광범위한 포트에 액세스할 수 있어야 한다.

좀 더 많은 명령어를 알아보려면, 주피터 노트북에서 다음과 같은 명령어를 사용하면 된다.

jupyter notebook --help

반응형
반응형

요즘 딥러닝 프로그램을 돌리는 컴퓨터가 있는 사무실에 계속 있기 어려운 환경에 있습니다. 그래서, 외부에서 컴퓨터를 온오프할 수 있도록 공유기 원격접속과 컴퓨터 WOL 기능을 켜두었습니다.

그리고 가장 많이 사용하고 있는 jupyter notebook을 원격지에서 사용할 수 있도록 세팅하였습니다.

1. 방화벽 해제 : 먼저 우분투 포트 방화벽 해제 및 공유기 포트포워드 설정을 합니다.
>> sudo ufw allow 8888(jupyter notebook의 기본포트로 원하시는 포트로 변경 가능)

2. jupyter notebook 암호 설정
로컬에서만 사용할 경우에는 보통 암호 설정 없이, 토큰을 이용해 접근했었습니다. 그렇지만 원격 접속은 보안을 위해 암호를 설정하는 것이 더 편하고 좋습니다. 
jupyter 서버 기본 설정을 위한 jupyter_notebook_config.py을 만들고, 패스워드를 설정합니다.
>> jupyter notebook --generate-config
Writing default config to: /{MyPath}/.jupyter/jupyter_notebook_config.py

>> jupyter notebook password
Enter password : ****
Verify password : ****
[NotebookPasswordApp] Wrote hashed password to /{Mypath}/.jupyter/jupyter_notebook_config.json

반응형

3. jupyter notebook 설정 파일 수정
nano ~/.jupyter/jupyter_notebook_config.py
수정할 코드를 ctrl+w(검색)를 사용해 찾은 후, 수정합니다.

c.NotebookApp.allow_origin = '*'  # 외부 접속을 허용합니다.
c.NotebookApp.notebook_dir = '작업 절대경로' # 작업경로를 설정합니다.
c.NotebookApp.ip = '*' # 외부 접속 포트 전체 오픈. 보안을 위해 특정 IP만 열어두셔도 됩니다.
c.NotebookApp.port = '8888' # 외부접속 사용 포트를 설정합니다.
c.NotebookApp.password = '<해시화된 패스워드 입력>' # 암호설정시 만들어진 jupyter_notebook_config.json을 nano등으로 열면 해시화된 패스워드를 확인할 수 있습니다.
c.NotebookApp.password_required = True # 비밀번호를 요구하도록 합니다.
c.NotebookApp.open_browser = False # 서버 pc에서 자동으로 브라우저가 열리지 않도록 합니다.

4. 서버 실행
>> jupyter notebook
이제 외부에서 접속하시면 됩니다. 공유기에서 ddns를 설정한 경우, ddns:포트번호(여기선 8888)을 주소창에 입력하면 접속할 수 있습니다. 
그 다음 설정한 비밀번호를 입력하면 로그인이 됩니다.

반응형
반응형

머신러닝 프로젝트 실행 1~2단계에 이어, 3단계를 정리하도록 하겠습니다.


3. 인사이트를 찾기 위해 데이터 탐색하기

1~2단계에서는 지금까지 처리하는 데이터의 종류에 대한 일반적인 이해를 얻기 위해 데이터를 훑어보았습니다. 

지금부터는 조금 더 깊이 들어가 보도록 하겠습니다.

먼저, 테스트 셋을 별도로 마련해 두었는지 확인하고, 훈련 셋을 탐색해 보도록 합시다. 만약 훈련 셋이 아주 크다면, 쉽고 빠르게 데이터를 다루기 위해 탐색하기 위한 셋을 샘플링하고 싶을지도 모릅니다. 우리 데이터의 경우에는, 훈련 셋이 매우 작기 때문에 전체 셋에서 직접 작업을 할 수 있습니다. 

카피본을 만들어, 훈련 셋에 영향을 주지 않고 작업을 시작해 봅시다.

>>> housing = strat_train_set.copy()


지리 데이터를 시각화하기

지리 정보(위도와 경도)가 있기 때문에, 데이터를 시각화하기 위해 모든 지구의 산포도를 만들어 보는 것은 좋은 아이디어입니다.

>>> housing.plot(kind="scatter", x="longitude", y="latitude")

이것은 캘리포니아처럼 보입니다. 하지만, 어떤 특정 패턴을 보기는 어렵습니다. alpha 옵션(투명도)을 0.1로 설정하면 데이터 포인트가 고밀도인 곳이 어느 지역인지 더 쉽게 시각화할 수 있습니다.

>>> housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.1)

이제 더 좋아 보입니다. 

즉 베이지역과 로스앤젤레스, 샌디에이고 같은 고밀도 지역을 분명하게 구분할 수 있습니다. 샌트럴 밸리 특히 새크래멘토와 프레스노 주변은 상당히 고밀도의 긴 선을 보여주고 있습니다.

일반적으로, 우리 두뇌는 그림에서 얼룩진 패턴을 인식하는데 매우 우수합니다. 하지만, 패턴을 눈에 띄게 만들기 위해 시각화 파라미터를 가지고 더 작업이 필요할 수도 있습니다. 


자, 집값을 보도록 합시다. 

각 원의 반경은 지구의 인구수를 나타냅니다. 그리고 색깔은 가격을 나타냅니다. 여기서는 jet(낮은 값은 푸른색, 높은 값은 붉은색으로 표시)라 불리는 미리 정의된 색깔 맵을 사용할 것입니다. 

>>> housing.plot(kind="scatter", x="longitude", y="latitude", alpha=0.4, s=housing["population"]/100, label="population", c="median_house_value", cmap=plt.get_cmap("jet"), colorbar=True)

>>> plt.legend()

이 이미지는 집값이 위치(예를 들자면, 해안과 가까운) 및 인구 밀도와 매우 밀접한 관계가 있다고 알려줍니다. 아마도 중심 클러스터를 탐지하기 위해 클러스터링 알고리즘을 사용하는 것이 유용할 수도 있습니다. 그리고, 클러스터 센터를 더 근접 측정하기 위해 새로운 피처들(features)을 추가할 수도 있습니다. 해안 근접 속성이 유용할 수 있지만, 북부 캘리포니아의 해안 지구 집값이 높지 않기 때문에, 규칙이 간단하지만은 않습니다.


상관관계 찾기

데이터셋이 아주 크지 않기 때문에, 모든 속성 쌍에서 corr() 메소드를 사용해 표준 상관 계수(Pearson's r이라 불림)를 쉽게 계산할 수 있습니다.

>>> corr_matrix = housing.corr()


중간 집 값과 각 속성들이 얼마나 상관관계가 있는 보도록 합시다.

>>> corr_matrix["median_house_value"].sort_values(ascending=False)

median_house_value        1.000000

median_income              0.687170

total_rooms                  0.135231

housing_median_age        0.114220

households                   0.064702

total_bedrooms              0.047865

population                  -0.026699

longitude                   -0.047279

latitude                     -0.142826

Name: median_house_value, dtype: float64


상관 계수의 범위는 -1에서 1사이입니다. 

1에 가깝다는 것은 강한 양의 상관관계가 있다는 것을 의미합니다: 예를 들어, 중간 소득이 올라갈 때, 중간 집 값이 올라가는 경향이 있습니다. 상관관계가 -1에 가깝다는 것은, 강한 음의 상관관계가 있다는 것을 의미합니다: 위도와 중간 집 값은 작은 음의 상관관계가 있다는 것(예, 북쪽으로 갈수록 집 가격이 약간 낮아지는 경향이 있습니다)을 알 수 있습니다. 마지막으로, 계수가 0에 가깝다는 것은 선형 상관관계가 없다는 것을 의미합니다. 

다음의 이미지들은 수평 및 수직 축 사이의 상관 계수에 대한 다양한 플롯을 보여줍니다.

(출처 : 위키피디아, public domain image)


속성들 사이의 상관관계를 점검하는 또 다른 방법은 판다스(Pandas)의 scatter_matrix 함수를 사용하는 것입니다. 이 함수는 모든 수치 속성을 모든 다른 수치 속성들에 대비한 플롯해 보여줍니다. 지금 11개의 수치 속성들이 있기 때문에, 121개의 플롯들을 얻게 될 것입니다. 여기서는 중간 집값과 가장 좋은 상관관계를 보여주는 몇가지 속성들에만 집중하도록 합니다.

>>> from pandas.tools.plotting import scatter_matrix

>>> attributes = ["median_house_value", "median_income", "total_rooms", "housing_median_age"]

>>> scatter_matrix(housing[attributes], figsize=(12, 8))

메인 대각선(왼쪽 위에서 오른쪽 아래)에는 각 속성에 대한 히스토그램을 보여줍니다. 이는 동일 속성에 대해 상관관계를 보여주는 것은 의미가 없기 때문입니다. 

중간 집값과 가장 상관관계가 높은 속성은 중간 소득입니다. 그 상관관계 산포도를 확대해 보도록 합시다.

>>> housing.plot(kind="scatter", x="median_income", y= "median_house_value", alpha=0.1)

이 플롯은 몇가지를 나타냅니다. 

첫째, 실제 상관관계가 매우 강하다는 것입니다; 상향 트렌드를 분명하게 볼 수 있고, 포인트들이 많이 분산되지도 않았습니다. 

둘째, 처음에 알고 있던 가격 상한선이 $500,000 수평선에 분명하게 보입니다. 하지만, 이 플롯은 또 다른 분명한 직선들을 보여줍니다: $450,000 주변 수평선, $350,000 주변 수평선, 아마도 $280,000 주변 수평선과 더 아래의 일부. 

이러한 데이터 단점을 재현하도록 알고리즘이 학습되는 것을 방지하기 위해 대응하는 지구를 제거해야 할지도 모릅니다.


속성 조합으로 시험하기

잘하면 이전 섹션의 몇 가지 방법을 통해 데이터를 탐색한 후 인사이트를 얻을 수 있습니다. 

머신러닝 알고리즘에 데이터를 피딩(feed)하기 전에 청소하고 싶어할 몇가지 데이터 단점을 알게 되었습니다. 그리고 타겟 속성과 다른 속성들 사이의 흥비로운 상관관계를 발견했습니다. 또한 몇몇 속성들은 편향된 분포도를 가지고 있다는 것을 알 수 있었고, 이것들을 변환(예, 그 속성들의 로그를 계산해서 표준 분포로)해야할지도 모릅니다. 물론, 각 프로젝트마다 수고해야 하는 노고는 각기 다릅니다만, 일반적인 아이디어는 유사합니다.


머신러닝 알고리즘을 위해 데이터를 실제 준비하기 전에 해야할 마지막 일은 다양하게 속성 조합을 시도해 보는 것입니다. 

예를 들어, 얼마나 많은 가정이 있는지 알지 못한다면, 각 지구의 전체 방수는 유용하지 않습니다. 정말 원하는 것은 각 가정의 방수입니다. 유사하게, 전체 침실수 자체로는 유용하지 않습니다. 아마도 침실수를 방숫자와 비교하고 싶어할 것입니다. 그리고 각 가정별 인구수 역시 흥미로운 속성 조합처럼 보입니다. 이들 새로운 속성들을 만들어 보도록 합시다.

>>> housing["rooms_per_household"] = housing["total_rooms"]/housing["househods"]

>>> housing["bedrooms_per_room"] = housing["total_bedrooms"]/housing["total_rooms"]

>>> housing["population_per_household"] = housing["population"]/housing["households"]


그런 다음, 다시 상관관계 매트릭스를 보도록 합시다:

>>> corr_matrix = housing.corr()

>>> corr_matrix["median_house_value"].sort_values(ascending=False)

median_house_value            1.000000

median_income                  0.687170

rooms_per_household           0.199343

total_rooms                      0.135231

housing_median_age            0.114220

households                       0.064702

total_bedrooms                  0.047865

population_per_household     -0.021984

population                      -0.026699

longitude                       -0.047279

latitude                         -0.142826

bedrooms_per_room            -0.260070

Name: median_house_value, dtype: float64


새로운 bedrooms_per_room 속성이 중간 집값과 가장 높은 상관관계를 보여줍니다. 분명히 더 적은 침실/전체방 비율을 가진 집들이 더 비싼 경향이 있습니다. 각 가정별 방수 또한 지구내 전체 방수보다 더 많은 정보를 제공합니다- 분명히 집들이 더 클수록 더 비쌉니다.

이 라운드의 탐색은 아주 철저하지 않아도 됩니다; 포인트는 올바른 출발점을 찾는 것이고 합리적으로 좋은 프로토타입을 얻기 위한 인사이트를 빠르게 얻는 것입니다. 하지만 이것은 반복적인 절차입니다: 한번 프로토타입을 얻어서 실행한 후, 더 많은 인사이트를 얻기 위해 그 결과물을 분석할 수 있습니다. 그런 다음 이 탐색 단계로 다시 돌아올 수 있습니다.


생각보다 공부한 것을 정리하는 것이 쉬운 작업이 아니네요. 조금식 꾸준히 하는 것이 한꺼번에 많이 하는 것보다 더 좋다고 생각해서 자주 한 단계씩 정리하도록 할 계획입니다.


"4. 기본 데이터 패턴을 머신러닝 알고리즘에 더 잘 노출할 수 있도록 데이터 준비하기"는 다음 글로 정리하겠습니다.


머신러닝 프로젝트 실행-1

(1~2단계: 1. 문제정의하고 전체 그림 바라보기/ 2. 데이터 얻기바로가기 


참고)'Hands-On Machine Learning with Scikit-Learn and TensorFlowchapter 2' 

주피터 노트북에서 볼 수 있는 전체 코드 얻기


반응형
반응형

요즘 읽고 있는 ML책 중, 예제를 통해 머신러닝 프로젝트 실행 프로세스를 처음부터 끝까지 배우는 부분이 있어, 정리해 봅니다.

머신러닝을 배우는 데 있어 실제 세상의 데이터를 가지고 프로젝트를 수행해 보는 것이 최상일 것입니다. 실제 데이터를 미국에서는 정말 많이 공짜로 제공하고 있습니다. 실 데이터를 가지고 머신러닝 실습을 하게 되면 무척 도움이 많이 될 것입니다.

우선 내용이 길어 글을 나눠서 올리도록 하겠습니다. 

전체 순서는

1. 문제를 정의하고 전체 그림 바라보기

2. 데이터 얻기

3. 인사이트를 찾기 위해 데이터 탐색하기

4. 기본 데이터 패턴을 머신러닝 알고리즘에 더 잘 노출할 수 있도록 데이터 준비하기

5. 다양한 모델을 탐색하고 그 중 가장 좋은 모델을 찾기

6. 모델을 알맞게 튜닝하고 멋진 솔루션으로 통합하기

7. 시스템 런칭, 모니터링과 유지하기

순입니다.


이번에는 1~2번까지 정리해서 올리도록 하겠습니다. 


참고로, 오픈 데이터셋 중에서 가장 인기있는 3곳의 링크는 아래와 같습니다. 

다른 곳도 있지만, 이곳을 가장 많이 애용한다고 하네요.

UC Irvine Machine Learning Repository (http://archive.ics.uci.edu/ml/)

Kaggle datasets (https://www.kaggle.com/datasets)

Amazon's AWS datasets (https://aws.amazon.com/fr/datasets/)


그럼, 지금부터  StatLib repository의 캘리포니아 집값 데이터셋을 가지고 머신러닝 프로젝트를 만들어 볼 것입니다. 

이 데이터셋은 1990년의 캘리포니아 인구조사 데이터를 기반으로 하고 있습니다. 


1. 문제를 정의하고 전체 그림 바라보기

첫번째 해야 할 일은 캘리포니아 인구조사 데이터를 사용해 캘리포니아 집값 모델을 만드는 것입니다. 이 데이터는 캘리포니아의 각 블록 그룹에 대한 인구, 중간 소득, 중간 집값 등 같은 메트릭스로 구성되어 있습니다. 블록 그룹은 미국 인구조사국이 제공하는 가장 작은 지리적 유닛(블록 그룹은 통상 600~3,000명의 사람들로 구성)입니다. 우리는 간단히 '지구(districts)'라고 부를 것입니다. 

그럼 이제 정확하게 이 데이터를 가지고 무엇을 하려고 하는 것인지를 분명히 하는 것으로 시작해야 합니다. 문제를 어떻게 정하느냐에 따라 문제를 정의하고 어떤 알고리즘을 선택해서, 성능을 측정할지가 정해지기 때문입니다. 

여기서는 주어진 데이터를 통해 지구별 가격을 예측해서, 어느 곳에 투자하는 것이 이익을 가장 많이 얻을 수 있는지 알아보는 것을 문제로 정했습니다.

그 다음 확인할 사항은 현재 실행되고 있는 해결 방안이 어떻게 이루어지고 있는지 파악하는 것입니다. 

여기서는 현재 지역별 집값을 전문가들이 직접 측정하고 있다고 합니다: 지구별 최신 정보를 팀이 모으고, 측정을 위해 복잡한 규칙을 적용합니다. 이것은 비용과 시간이 소모되고, 측정값이 훌륭하지도 않습니다; 통상 에러율이 약 15% 수준입니다.

이 모든 정보를 가지고 이제 시스템 디자인을 시작합니다. 

첫째, 문제에 대한 프레임이 필요합니다: 지도학습, 비지도학습, 또는 강화학습? 분류 업무, 회귀 업무, 또는 그 밖에 다른? 배치 학습 또는 온라인 러닝 기술을 사용해야 하는지? 등을 결정해야 합니다.

이것은 분명히 라벨링된 훈련 예제들(각 인스턴스들은 기대되는 아웃풋과 함께 제시, 예를 들어 각 지구의 중간 집값)이 주어진 통상적인 지도학습 업무입니다. 게다가, 값을 예측해야 하기 때문에 전형적인 회귀 업무입니다. 더 특별하게는, 이 시스템이 다양한 피처들(features)을 사용해서 예측을 만들기 때문에 다변량 회귀 문제입니다(지역별 인구, 중간 수익값 등을 사용할 것입니다). 마지막으로 빠르게 변화하는 데이터를 반영할 필요가 없고, 데이터도 메모리에서 처리가 적당할 정도로 적기 때문에, 배치 러닝을 사용하는 것으로도 충분할 것입니다. 

* 데이터가 크다면, 멀티 서버를 통해 배치 러닝 작업을 분할할 수도 있습니다(예를 들어 MapReduce 기술을 사용). 또는 온라인 러닝 기술을 사용할 수도 있습니다.

다음 단계는 성능 측정을 선택하는 것입니다. 회귀 문제에 대한 전형적인 성능 측정은 시스템이 만든 예측값들의 에러들에 대한 표준 편차를 측정하는 RMSE (Root Mean Square Error)를 사용합니다. 예를 들어, RMSE가 50,000과 같다는 것은 시스템이 예측한 68%가 실제 50,000에 포함된다는 의미입니다. 그리고 예측값의 95%가 실제 100,000 값들에 포함된다는 것입니다. 

RMSE가 일반적으로 회귀 업무에 있어 선호되는 성능 측정이긴 하지만, 몇몇 문맥에서 다른 기능을 사용하는 것을 선호할 수 도 있습니다. 예를 들어, 특이값을 갖는 지구가 많이 있다고 가정해 보자. 그러한 경우에, Mean Absolute Error(MAE)를 사용하는 것도 고려해 볼 수 있습니다. 

RMSE와 MAE 모두 두개의 벡터값 거리를 측정하는 방법들입니다: 예측 벡터와 타겟값 벡터. 다양한 거리 측정, 또는 규칙들이 가능합니다.

마지막으로, 지금까지 만들어진 가정들을 리스트하고 검증합니다. 이를 통해 심각한 이슈사항을 찾을 수 있습니다. 예를 들어, 시스템 아웃풋인 각 지구별 값을 정확하게 예측하는 것보다 카테고리(예, 싼, 중간, 비싼)로 가격을 변환해서 처리하는 것이 더 나을 수도 있습니다. 이럴 경우, 이 문제는 회귀 업무가 아니라, 분류 업무로 재정의되어야 할 것입니다. 몇 달 동안 회귀 시스템 구축을 추진한 후에 분류 업무로 처리해야 한다는 말을 듣고 싶지는 않을 것입니다. 그렇기 때문에, 초기에 피드백을 받는 것이 중요합니다.


2. 데이터 얻기

자, 이제 손을 사용해 봅시다. 

바로 랩탑을 열고 Jupyter 노트북에서 다음의 코드 예제를 열어봅시다.

먼저, 파이썬을 인스톨해야 합니다(이미 다 했겠죠?). 

다음으로는 머신러닝 코드와 데이터셋을 위한 작업 디렉토리가 필요합니다. 더 필요한 라이브러리들은 다음과 같습니다: Jupyter, Numpy, Pandas, Matplotlib, Scikit-Learn.  여기서는 이미 다 설치했다고 가정하겠습니다.

(Jupyter notebook 환경에서 실행하는 것을 가정했습니다. 다른 환경에서는 아래 코드가 제대로 실행되지 않을 수 있습니다)

>>> import os

>>> import tarfile

>>> from six.moves import urllib

>>> DOWNLOAD_ROOT = "https://raw.githubusercontent.com/ageron/handon-ml/master/"

>>> HOUSING_PATH = "datasets/housing"

>>> HOUSING_URL = DOWNLOAD_ROOT + HOUSING_PATH + "/housing.tgz"

>>> def fetch_housing_data(housing_url=HOUSING_URL, housing_path=HOUSING_PATH):

if not os.path.isdir(housing_path):

    os.makedirs(housing_path)

tgz_path = os.path.join(housing_path, "housing.tgz")

urllib.request.urlretrieve(housing_url, tgz_path)

housing_tgz = tarfile.open(tgz_path)

housing_tgz.extractall(path=housing_path)

housing_tgz.close()

함수 fetch_housing_data()를 불러오면, 작업 디렉토리에 datasets/housing 폴더를 만들고, housing.tgz파일을 다운로드 합니다. 그리고, 이 디렉토리에 housing.csv파일을 압축해제 합니다.

그 다음에 pandas를 사용해 데이타를 로드해 봅시다. 데이터를 불러오기 위해 다음 코드를 씁니다

>>> import pandas as pd

>>> def load_housing_data(housing_path=HOUSING_PATH):

csv_path = os.path.join(housing_path, "housing.csv")

return pd.read_csv(csv_path)

위 함수는 모든 데이터를 포함한 pandas 데이타프레임을 불러옵니다.

그 다음에 데이터 구조를 빠르게 살펴봅니다.

>>> housing = load_housing_data()

>>> housing.head()

각 열은 하나의 특정 속성을 나타냅니다. 여기에는 10개의 속성들이 있습니다 : longitude, latitude, housing_median_age, total_rooms, total_bedrooms, population, households, median_income, median_house_value, ocean_proximity.

>>> housing.info()

 이 데이터셋에는 총 20,640개의 레코드들이 있습니다. 머신러닝 표준에서는 매우 적은 수의 데이터들입니다. 그러나, 처음 시작하기에는 완벽합니다. 

info()로 확인한 바에 의하면, bedrooms 속성이 20,433개의 non-null 값을 가지고 있습니다. 이것은 207개의 레코드의 값이 없다는 것을 의미합니다. 그리고 모든 속성들이 숫자들입니다. ocean_proximity를 제외하구요. ocean_proximity의 type은 object로 파이썬의 어떤 object라도 담을 수 있습니다. 위 head()를 잘 살펴보았다면, ocena_proximity가 text속성을 가지고 잇다는 것을 알 수 있을 것입니다. 또한, 이 컬럼의 값이 반복된다는 것을 알아챘다면, value_counts() 메소드를 통해 각 카테고리에 얼마나 많은 값들이 있는지 알아낼 수 있을 것입니다.

>>> housing["ocean_proximity"].value_counts()

<1H OCEAN        9136

INLAND              6551

NEAR OCEAN     2658

NEAR BAY          2290

ISLAND                     5

Name: ocean_proximity, dtype: int64

다른 필드를 봅시다. 

describe() 메서드는 숫자 속성들의 요약을 보여줍니다.

>>> housing.describe()

count, mean, min과 max 열은 자명합니다. null 값들이 무시된 것을 주의하시기 바랍니다(예를 들어, total_bedrooms의 count는 20,640이 아니라 20,433입니다). 25%, 50%와 75% 열은 해당 백분위 수를 보여줍니다. 예를 들어, housing_median_age의 25%는 18보다 작다는 것을 알려줍니다. 50%는 29보다, 75%는 37보다 작습니다. 이것은 흔히 25th 백분위(또는 1  분위수), 중앙값, 그리고 75th 백분위(3 분위수)로 불립니다.

지금 다루고 있는 데이터 형태를 느끼기 위한 빠른 다른 방법은 각 숫자 속성에 대한 히스토그램을 그리는 것입니다. 

>>> %matplotlib inline # 주피터 노트북에서만 가능합니다.

>>> import matplotlib.pyplot as plt

>>> housing.hist(bins=50, figsize=(20,15))

>>> plt.show()

 (주피터 노트북에서 위의 명령어를 입력하고 실행해야 Matplotlib를 백그라운드에서 쉽게 이용할 수 있습니다) 

위 히스토그램에서, median_income 속성은 미국달러로 표현된 것처럼 보이지 않습니다. 머신러닝에서는 보통 전처리된 속성들로 작업합니다. 그래서, 문제가 되지는 않습니다만, 데이터가 어떻게 계산되었는지 파악하려는 노력을 해야만 합니다.

housing_median_age와 median_house_value값 모두 상한선이 있습니다. 이것이 문제가 될지 안될지 점검하는 것이 필요합니다. 

많은 히스토그램들의 꼬리가 깁니다. 중앙값보다 오른쪽으로 더 많이 깁니다. 이것은 몇몇 머신러닝 알고리즘에 있어 패턴을 인식하는데 더 어려움을 줄 수 있습니다. 나중에 이들 속성들이 종 모양의 분포를 가질 수 있도록 속성들에 변형을 주도록 할 것입니다.


테스트 셋 만들기

이론적으로 테스트 셋을 만드는 것은 매우 간단합니다 : 랜덤하게 몇몇 인스턴스를 선택합니다. 통상 데이터셋의 20%를 선택해서, 따로 설정해 둡니다.

>>> import numpy as np

>>> def split_train_test(data, test_ratio):

            shuffled_indices = np.random.permutation(len(data))

test_set_size = int(len(data) * test_ratio)

test_indices = shuffled_indices[:test_set_size]

train_indices = shuffled_indices[test_set_size:]

return data.iloc[train_indices], data.iloc[test_indices]


위 함수를 아래처럼 사용합니다:

>>> train_set, test_set = split_train_test(housing, 0.2)

>>> print (len(train_set), "train +", len(test_set), "test")

16512 train + 4128 test

이것이 동작하긴 하지만, 완벽하지는 않습니다. 만약 이 프로그램을 다시 작동시키면, 이것은 다른 테스트 셋을 생성할 것입니다! 하나의 해결책은 처음 작동시에 테스트 셋을 저장하는 것입니다. 다른 옵션은 np.random.permutation()을 호출하기 전에 랜덤 숫자 제너레이터 씨드를 설정(예를 들어, np.random.seed(42))하는 것입니다. 그러면 항상 동일한 수치들은 생성할 것입니다.

위 두개의 해결책은 다음 번 업데이트된 데이터셋을 패치할 때 깨질 것입니다. 일반적인 해결책은 테스트 셋에 가야할 지 말아야 할지를 결정하기 위해 각 인스턴스의 식별자(인스턴스들이 고유하고 불변의 식별자를 가지고 있다고 가정)를 사용하는 것입니다. 예를 들어, 각 인스턴스의 식별자의 해시를 계산하고, 그 해시의 마지막 바이트만 간직합니다. 그리고 그 값이 51(256의 20%)보다 작거나 같다면 테스트 셋에 그 인스턴스를 집어넣습니다. 이렇게 하면 여러번 실행되는 동안, 데이타셋을 리프레시할 때 조차도 테스트 셋이 일관성을 유지하게 됩니다. 새로운 테스트 셋은 새로운 인스턴스의 20%를 포함할 것입니다. 하지만, 이전 트레이닝 셋의 어떤 인스턴스도 포함하지 않습니다. 아래 실행 가능한 코드가 있습니다.

>>> import hashlib

>>> def test_set_check(identifier, test_ratio, hash):

return hash(np.int64(identifier)).digest()[-1] < 256 * test_ratio

>>> def split_train_test_by_id(data, test_ratio, id_column, hash=hashlib.md5):

ids = data[id_column]

in_test_set = ids.apply(lamda id_: test_set_check(id_, test_ratio, hash))

return data.loc[~in_test_set], data.loc[in_test_set] 

불행히도, housing 데이타 셋은 식별자 컬럼을 가지고 있지 않습니다. 간단한 해결책은 ID로써 열 인덱스를 사용하는 것입니다.

>>> housing_with_id = housing.reset_index() # '인덱스'컬럼 추가

>>> train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "index")

만약 고유한 식별자로 열 인덱스를 사용한다면, 새로운 데이터가 데이터 셋 마지막에 추가되는 것을 확인하는 것이 필요합니다. 그리고 어떤 열도 삭제되지 않아야 합니다. 이것이 불가능하다면, 고유한 식별자를 만들기 위해 가장 안정적인 피처를 사용하도록 해봐야 할 것입니다. 예를 들어, 지구(district)의 위도와 경도가 수백년 동안 안정된 것으로 보장될 것입니다. 그래서 이 둘을 ID로써 병합할 수 있습니다. 

>>> housing_with_id["id"] = housing["longitude"] * 1000 + housing["latitude"]

>>> train_set, test_set = split_train_test_by_id(housing_with_id, 0.2, "id")

사이킷런(Scikit-Learn)은 다양한 방법으로 데이터셋을 여러 개의 서브 셋으로 나누는 함수들을 제공합니다. 가장 간단한 함수가 train_test_split입니다. 앞에서 정의한 split_train_test 함수와 매우 많은 부분이 동일합니다. 

>>> from sklearn.model_selection import train_test_split

>>> train_set, test_set = train_test_split(housing, test_size=0.2, random_state=42)

지금까지 순수 랜덤 샘플링 메서드를 살펴보았습니다. 이것은 데이터셋이 충분히 크다면(특히 속성들의 수와 비례해서), 일반적으로 훌륭합니다. 그러나 그렇지 않은 경우에는 상당한 샘플링 편차가 발생할 수 있습니다. 설문조사 회사가 1,000명에게 설문을 돌릴 때, 단순히 전화번호부에서 무작위로 1,000명을 뽑지는 않습니다. 예를 들어, 미국 인구는 51.3%의 여성과 48.7%의 남성으로 구성되어 있습니다. 그래서 미국에서 잘 만든 설문조사는 샘플에서 이 비율(513명의 여성과 487명의 남성)을 유지하려고 할 것입니다. 이것을 층화(stratified) 샘플링이라 부릅니다: 인구는 지층이라 불리는 균질한 하위 집단으로 나뉘어 집니다 . 테스트 셋이 전체 모집단을 대표할 수 있도록 하기 위해 각 계층에서 올바른 숫자의 인스턴스들이 샘플링됩니다. 만약 순수 랜덤 샘플링 메서드를 사용했다면, 49% 여성보다 더 적거나 54% 여성보다 더 많은 편향된 테스트 셋을 샘플링할 확률이 12%나 됩니다. 어느 쪽이든, 설문조사 결과가 상당히 편향될 것입니다. 

전문가들이 중간(median) 집값을 예측하기 위해 중간(median) 소득이 매우 중요한 속성이라고 말했다고 가정해 봅시다. 테스트 셋이 전체 데이터셋의 다양한 카테고리의 소득들을 대표하는 것이 확실하기를 원할 것입니다. 중간(median) 소득이 연속된 숫자 속성이기 때문에, 최초에 수입 카테고리 속성을 만드는 것이 필요합니다. 중간(median) 소득 히스토그램을 더 자세히 들여다 봅시다.

대부분의 중간 소득 값은 2-5 사이입니다. 하지만 몇몇 중간 소득들은 6을 넘습니다. 데이터 집합에 각 계층별 충분한 수의 인스턴스들을 갖는 것이 중요합니다. 그렇지 않으면 지층별 중요성에 대한 추정치가 편향될 수도 있습니다. 

다음 코드는 1.5로 나눈 소득 카테고리 속성을 만듭니다. 그리고 ceil을 사용해 반올림(이산 카테고리를 갖게 됩니다)합니다. 그 다음에 5보다 큰 모든 카테고리를 카테고리 5로 합칩니다.

>>> housing["income_cat"] = np.ceil(housing["median_income"] / 1.5)

>>> housing["income_cat"].where(housing["income_cat"] < 5, 5.0, inplace=True)

이제 소득 카테고리에 기반한 층화(stratified) 샘플링을 할 준비가 되었습니다. 사이킷런(Scikit-Learn)의 StratifiedShuffleSplit 클래스를 사용할 수 있습니다.

>>> from sklearn.model_selection import StratifiedShuffleSplit

>>> split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=42)

>>> for train_index, test_index in split.split(housing, housing["income_cat"]):

strat_train_set = housing.loc[train_index]

strat_test_set = housing.loc[test_index]

기대한대로 동작하는지 봅시다. 먼저 전체 housing 데이터셋에서 소득 카테고리 비중을 봅시다.

>>> housing["income_cat"].value_counts() / len(housing)

3.0    0.350581

2.0    0.318847

4.0    0.176308

5.0    0.114438

1.0    0.039826

name: income_cat, dtype: float64

위와 유사한 코드로 테스트 셋의 소득 카테고리 비중을 측정할 수 있습니다. 아래 표에서 전체 데이터셋과 층화(stratified) 샘플링으로 생성된 테스트 셋, 순수 랜덤 샘플링으로 생성된 테스트 셋의 소득 카테고리 비중을 비교할 수 있습니다. 층화(stratified) 샘플링으로 생성된 테스트 셋의 비중이 전체 데이터셋의 비중과 거의 동일한 것을 알 수 있습니다.

이제 데이터를 원래 상태로 되돌리기 위해 income_cat 속성을 제거해야 합니다.

>>> for set in (strat_train_set, strat_test_set):

set.drop(["income_cat"], axis=1, inplace=True)

테스트 셋을 생성하는 것에 대해서 알아보았습니다. 이 부분은 머신러닝 프로젝트에서 흔히 간과되지만 중요한 부분입니다. 게다가 이들 많은 아이디어들은 앞으로 교차 검증을 논의할 때 유용할 것입니다. 


참고) 'Hands-On Machine Learning with Scikit-Learn and TensorFlow, chapter 2' 

주피터 노트북에서 볼 수 있는 전체 코드 얻기

반응형
반응형

Jupyter Notebook이란

Jupyter Notebook은 오픈 소스 웹 애플리케이션으로 라이브 코드, 등식, 시각화와 설명을 위한 텍스트 등을 포함한 문서를 만들고 공유하도록 할 수 있습니다. 

주로 데이터 클리닝과 변형, 수치 시뮬레이션, 통계 모델링, 머신 러닝 등에 사용할 수 있습니다.

Jupyter Notebook은 Python, R, Julia, Scala 등 데이터 과학 분야에서 인기있는 40종의 다양한 프로그래밍 언어를 지원합니다. 또한, 이메일, 드롭박스, 깃허브 등으로 공유할 수 있습니다. 가장 큰 장점은 실시간으로 인터렉티브하게 데이터를 조작하고 시각화할 수 있도록 해준다는 점을 것입니다.

신규 사용자는, Anaconda를 설치하는 것을 권장합니다. Anaconda는 과학적 컴퓨팅과 데이터 사이언스를 위해 필요한 라이브러리들을 대부분 포함하고 있기 때문입니다.

추가적으로 말씀드리자면, python 라이브러리들에 대한 관리를 pyenv와 virtualenv(python 가상 환경)을 사용해서 관리하는 것이 좋습니다.  

 

1. Jupyter Notebook 시작

터미널에서 

>>> jupiter notebook

을 입력하세요.

그러면, 기본 웹 브라우저에서 다음과 같은 Notebook Dashboard가 구동되는 것을 볼 수 있을 것입니다.

위 화면에서 확장자가 .ipynb인 파일을 클릭해 이미 작성된 Notebook을 실행시킬 수 있고, 오른쪽 상단의 New버튼을 눌러 팝업 메뉴에서 python[defalut] 또는 python[conda root]를 선택해서 새로운 Notebook을 열 수도 있습니다.(참고로, 새로운 Notebook을 선택하는 Python[conda root, default, 2, 3]는 anaconda로 실행했을 때와 python에 별도로 jupyter notebook 관련 라이브러리를 설치하고 실행했을 때 조금 다르게 보일 수 있습니다)

반응형

2.  Jupyter Notebook 사용해 보기

새로운 Notebook을 실행하고, 각 셀에 파이썬 코드 및 등식, 설명을 입력합니다.설명 및 등식을 입력하려면, 아래 그림과 같이 상단 가운데 선택 메뉴에서 마크다운(Markdown)을 선택하고 입력하면 됩니다. 다 입력한 후에는 Shift+Enter(또는 Ctrl+Enter)로 실행 명령을 내립니다.

 

3. 단축키

<Command Mode>

- Enter : 에디트 모드(Edit Mode)로 진입- Y : 코드(Code) 셀로 전환- M : 마크다운(Markdown) 셀로 전환- R : Raw NB 셀로 전환- 1~6 : 마크다운 H1~H6- a : 위에 셀 삽입- b : 아래 셀 삽입

<Edit Mode>

- Tab : 코드 자동완성, 들여쓰기- Ctrl+] : 들여쓰기(intent)- Ctrl+[ : 내어쓰기(detent)- Ctrl+a : 전체 선택- Ctrl+z : 실행 취소(undo)- Esc : 커맨드 모드(Command mode)로 진입- Shift+Enter : 셀 실행하고 다음 셀 선택- Ctrl+Enter : 셀 실행- Alt+Enter : 셀 실행하고, 아래 셀 삽입


Jupyter Notebook도 제대로 사용하려면 생각보다 많은 것을 배워나가야 할 것 같네요. 그래도 위에 열거한 

기본적인 사항만 알면 실행하는데 크게 문제는 안되리라 생각합니다. 

 

반응형

+ Recent posts