728x90
  • 강의 수강하면서 실습한 내용을 업로드합니다.
  • 필기 내용은 코드와 함께 주석으로 입력했습니다.
  • 한 실습 당 여러 방법이 섞여 있어서 주석처리를 예의주시 필요
  • 실습 날짜: 2021-02-17, OpenCV 첫시작

영상 기본 조작 

import cv2
import sys

#특정 키를 가지고 창을 끌때

print('Hellow, opencv', cv2.__version__)

# img= cv2.imread('cat.bmp') #img: image를 뜻함
img = cv2.imread('cat.bmp', cv2.IMREAD_GRAYSCALE) #BGR이미지를 gray로 열고 싶을때는 cv2.IMREAD_GRAYSCALE 삽입하기

#예외처리
if img is None: #영상 파일 불러오지 못했을때 실행되는 코드,
    print('Image load failed!')
    sys.exit() 
cv2.imwrite('cat_gray.png', img) #컬러 이미지를 gray로 저장하기 

cv2.namedWindow('image') #창을 하나 생성해주는 함수, 창 이름을 image 지정 
                        #띄울 이미지가 크다면 이 함수를 적고 flags를 normal로 지정해서 창사이즈 변경가능하도록 설정
                        #이미지가 작으면 생략해도 cv2.imread가 알아서 창을 만들어준다(auto창, 사이즈 변경 불가)

cv2.imshow('image',img) #창에 영상을 보여주는 함수, 첫번째 인자는 띄울 창 이름, 두번째 인자는 띄울 이미지 , 그리고 키보드 입력 있을때 까지 대기

#특정 키를 눌렀을때 종료하도록 하는 코드, 아스킷을 이용
# while True:
#     if cv2.waitKey()==27: #esc키의 아스킷 숫자 27
#         break


#아스킷이 아닌 키보드알파벳을 이용할때
while True:
    if cv2.waitKey()==ord('q'): #특정 알파벳을 눌러서 종료하고 싶을때는 ord() 안에 해당 알파벳 넣기
        break

# key=cv2.waitKey(2000) #영상이 실제로 나올수 있게끔 하는 함수, 2000-> 2초
# print(key)

cv2.destroyAllWindows() #기존에 띄운 창을 모두 닫은 함수

 

여러 이미지로 슬라이드 쇼 생성하기

#슬라이드 쇼 만들기

import sys, os
import glob
import cv2

#문자열의 리스트 형태로 저장
img_files= glob.glob('.\\images\\*.jpg') #images안에 .jpg파일을 문자열로 모두 가져온다

for f in img_files:
    print(f) #.\images\autumn_forest_park_128379_1920x1080.jpg  형태

#전체창 만들기setWindowProperty(), window normal로 만들어야 전체화면이 가능하다
cv2.namedWindow('image',cv2.WINDOW_NORMAL) #WINDOW_NORMAL속성의 창을 생성 후
cv2.setWindowProperty('image', cv2.WND_PROP_FULLSCREEN,  #setWindowProperty를 통해 전체화면 속성으로 변경
                                cv2.WINDOW_FULLSCREEN)

cnt= len(img_files)
idx=0

while True:
    img = cv2.imread(img_files[idx])

    if img is None:
        print('Image load failed!')
        break

    cv2.imshow('image',img)

    if cv2.waitKey(1000)==27: #ESC
        break

    # if cv2.waitKey(1000)==0: #키보드 아무 값을 쳐도 0보다는 큼 즉 아무키 눌렀을때 꺼진다
                              #아무것도 안누르면 -1을 리턴
    #     break

    idx +=1

    if idx>=cnt: 
        idx=0

cv2.destroyAllWindows()






 

Matplot으로 영상 load

import matplotlib.pyplot as plt
import cv2


# 컬러 영상 출력
imgBGR = cv2.imread('cat.bmp')

#matplotlib은 RGB순서를 기대하기 때문에 
#opencv BGR을 cvtColor통해서 RGB로 순서 변경 후 matplotlib에 적용 
imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB)

plt.axis('off') #눈금 제거
plt.imshow(imgRGB)
plt.show()

# 그레이스케일 영상 출력
#2차원 형태로 되어 있다고 보면된다. 밝기 값만 있다
imgGray = cv2.imread('cat.bmp', cv2.IMREAD_GRAYSCALE)

plt.axis('off')
plt.imshow(imgGray, cmap='gray')
plt.show()

# 두 개의 영상을 함께 출력
plt.subplot(121), plt.axis('off'), plt.imshow(imgRGB) #컬러버전 
plt.subplot(122), plt.axis('off'), plt.imshow(imgGray, cmap='gray') #gray버전
plt.show()

 

영상불러오기 & 픽셀 값 변경 

import sys
import cv2


# 영상 불러오기
img1 = cv2.imread('cat.bmp', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('cat.bmp', cv2.IMREAD_COLOR)

if img1 is None or img2 is None:
    print('Image load failed!')
    sys.exit()

#영상의 크기를 참조하는 방법

#그레이스케일
h,w = img1.shape
print('w x h= {} x {}'.format(w,h)) 

#컬러-> 에러뜸  왜? (w,h) 크기의 3개 이미지를 두개 변수로 받을려고 했기 때문에
# h,w = img2.shape
# print('w x h= {} x {}'.format(w,h))

#해결방법-> (w,h,c) 에서 w,h만 가져오도록 [:2] 적용
h,w = img2.shape[:2] #그레이스케일에 사용해도 무방
print('w x h= {} x {}'.format(w,h))


#영상의 픽셀 값을 참조하는 방법
#영상의 (20,10)좌표의 픽셀 값을 알고 싶을때
x=20
y=10 
p1= img1[y,x]
print(p1) #grayscale 픽셀값이 238(밝은 편)

p2= img2[y,x]
print(p2) # [237 242 232] 블루,그린,레드 순

#이를 이용해서 특정 픽셀값에 내가 원하는 값을 대입 할수있다
'''
for y in range(h):  #각 픽셀을 for문으로 돌아다니면서 아래 조건으로 픽셀값을 바꾸는 코드-> 느림
    for x in range(w):
        img1[y,x]=0 #검정색
        img2[y,x]=(0,255,255) #노란색
'''
#위코드는 사용 지양(느림),아래코드 추천
img1[:, :] = 0
img2[:, :] = (0,255,255)

cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
# cv2.waitKey()

cv2.destroyAllWindows()

 

부분영상 추출 & 복사

import numpy as np
import cv2

# 새 영상 생성하기
img1 = cv2.imread('HappyFish.jpg') #물고기 그림
img2=img1[50:160, 150:300]
img3=img1[50:160, 150:300].copy()

#(105,225) 좌표에, 반지름 20, 컬러는 (0,0,255), 두께2
                 #(x, y) 
#이미지를 짜른 순간 좌표는 짤려진 이미지 좌측 상단이 0,0으로 초기화                               
cv2.circle(img2, (50,50), 20, (0,0,255),2) 

cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
# cv2.imshow('img3', img3)
cv2.waitKey()
cv2.destroyAllWindows()

 

마스크 연산 & ROI

import sys
import cv2

# 마스크 영상을 이용한 영상 합성
# src = cv2.imread('airplane.bmp', cv2.IMREAD_COLOR)
# mask = cv2.imread('mask_plane.bmp', cv2.IMREAD_GRAYSCALE)
src =cv2.imread('opencv-logo-white.png', cv2.IMREAD_UNCHANGED)

#알파와 BGR 구성
mask =src[:, :, -1] #투명한 알파 채널
src= src[:, :, 0:3] #BGR만 따로
dst = cv2.imread('field.bmp', cv2.IMREAD_COLOR)

h,w = src.shape[:2]
crob= dst[0:h,0:w] #직접 dst 사진의 좌표를 지정해서 위치를 알려줘야 한다

#CV2를 이용한 방법
# cv2.copyTo(src, mask,dst) #crob을 넣지않고 왼쪽 코드처럼 실행하면 로고와 dst 크기가 맞지 않아서 합성이 안됨
cv2.copyTo(src, mask,crob)

#numpy로 이용한 방법
# dst[mask>0] = src[mask>0] #마스크가 0이상인 부분에 True값이 반환 될것이고 그 반환된 
                            #그 자리 그대로 src자리에 대입되면, 비행기가 싹뚝 뽑혀서 dst의 True위치에 비행기를 넣는다   

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.imshow('mask', mask)
cv2.waitKey()
cv2.destroyAllWindows()


그리기 함수

import numpy as np
import cv2

img = np.full((400, 400, 3), 255, np.uint8)

            #(가로,세로)(가로,세로)
cv2.line(img, (50, 50), (200, 50), (0, 0, 255), 5)
cv2.line(img, (50, 60), (150, 160), (0, 0, 128))

#직사각형은 두가지 방법으로 그릴수 있다

                #50,200 좌측상단 좌표, 150:가로, 100:세로
cv2.rectangle(img, (50,200,150,100), (0,0,225),2)

                #좌측상단좌표,우측하단좌표,color지정,두께
cv2.rectangle(img, (70,220),(180,280),(0,128,0),-1) #두께 음수는 내부를 채운다

#cv2.LINE_8로 원을 그리면 거친 느낌의 원을 만든다
#cv2.LINE_AA를 넣어주면 반듯한 원이 만들어진다 
#사각형은 cv2.LINE_AA 사용 안해도 되지만 원이나, 문자열 같은걸 이용할때는 애용하는게 좋다. 거친면이 사라진다                                               
cv2.circle(img, (300,100), 30,(255,255,0), -1, cv2.LINE_AA)
cv2.circle(img, (300,100),60,(255,0,0), 3, cv2.LINE_AA)

#다각형의 꼭지점을 넘파이 array로 만든다
pts = np.array([[250,200],[300,200],[350,300],[250,300]]) #이걸 넣을때 리스트 형태로 감싸서 넣어야한다

                        #True를 주면 시작점과 끝점을 이어준다(폐곡선)
cv2.polylines(img, [pts], True, (250,0,255),2)

#문자열 넣기
text= 'Hello? OpenCV'+cv2.__version__

                      #(x,y)인데 좌측하단 기준임
cv2.putText(img, text, (50,350), cv2.FONT_HERSHEY_SIMPLEX,0.8, #폰트설정
(0,0,255), 1, cv2.LINE_AA)

cv2.imshow("img", img)
cv2.waitKey()
cv2.destroyAllWindows()

 

카메라와 동영상 처리1

==================카메라 실습=========================
cap = cv2.VideoCapture()
# cap.open(0) #기본 카메라를 open하겠다는 의미
# cap = cv2.VideoCapture(0) #위와 같은 결과

# if not cap.isOpened(): #카메라 제대로 열렸는지 확인
#     print('camera open failed')
#     sys.exit()

# #카메라의 가로,세로를 가져오는 방법
# w= int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
# h= int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# print(w, h)

# # #카메라 크기를 변경하는 방법- 내 컴퓨터에는 지원 안되는듯
# # cap.set(cv2.CAP_PROP_FRAME_WIDTH),320)
# # cap.set(cv2.CAP_PROP_FRAME_HEIGHT),240)
# # print(w, h)


# #한 프레임씩 계속 받아오는 코드 생성
# while True:
#     #프레임을 받아오는 함수 는 cap.read()
#     #read()가 리턴하는 것이 두가지 : ret(불리언), image
#     #불리언의 True: 사진이 제대로 받은경우
#     #즉 read()는 불리언과 ndarray(이미지) 두가지를 준다
#     ret, frame = cap.read() #현재 열려 있는 카메라 디바이스에서 한 프레임씩 받아오는 작업을 한다

#     if not ret: #True가 아니면
#         break

#     #이 부분에는 가져온 정지 영상을 처리하는 코드를 작성 할수 있다
#     #윤곽선 추출
#     edge= cv2.Canny(frame,50,150)

#     cv2.imshow('edge',edge)
#     cv2.imshow('frame',frame) #'frame'의 창은 굳이 만들지 않아도 알아서 만든다(특이 경우는 예외)
#     if cv2.waitKey(20)==27: #20ms를 기다리고 꺼진다 하지만 여기에 ESC를 누르면 while문을 빠져나온다
#         break

# cap.release() #open했던 cap을 해체 시킨다
# cv2.destroyAllWindows()


# ======================동영상 실습==============================
import sys
import cv2


# 카메라 열기
cap = cv2.VideoCapture('video1.mp4')

if not cap.isOpened():
    print("video open failed!")
    sys.exit()

# 카메라 프레임 크기 출력
print('Frame width:', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
print('Frame height:', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))

# 카메라 프레임 처리
while True:
    ret, frame = cap.read()

    if not ret:
        break
    edge= cv2.Canny(frame,50,150)


    # inversed = ~frame  # 반전
    cv2.imshow('edge',edge)
    cv2.imshow('frame', frame)
    # cv2.imshow('inversed', inversed)

    if cv2.waitKey(20) == 27:
        break

cap.release()
cv2.destroyAllWindows()

 

카메라와 동영상 처리2

import sys
import cv2


cap = cv2.VideoCapture(0)

if not cap.isOpened():
    print("Camera open failed!")
    sys.exit()

w = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) #float형태로 나오기 때문에 정수로 만들기 위해서 
h = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS) 

#DIVX를 이용해서 정수값을 리턴해준다 fourcc : 압축 방식
fourcc = cv2.VideoWriter_fourcc(*'DIVX') # *'DIVX' == 'D', 'I', 'V', 'X'
delay = round(1000 / fps) #프레임간의 시간간격

out = cv2.VideoWriter('output.avi', fourcc, fps, (w, h)) #컬러영상 저장

if not out.isOpened():
    print('File open failed!')
    cap.release()
    sys.exit()

while True:
    ret, frame = cap.read()

    if not ret:
        break

    # inversed = ~frame
    edge= cv2.Canny(frame, 50,150) #edge는 그레이라서 컬러로 바꾼다음에 저장해야 19번 코드와 호환됨
    edge_color= cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)
    
    #out.write(frame) #소리는 x, 영상만 저장됨
    out.write(edge_color)

    cv2.imshow('frame', frame)
    cv2.imshow('edge', edge)
    cv2.imshow('edge_color', edge_color)
    # cv2.imshow('inversed', inversed)

    if cv2.waitKey(delay) == 27:
        break

cap.release()
out.release()
cv2.destroyAllWindows()

 

728x90
728x90

버스 승차인원예측을 오늘 모두 끝냈습니다.

이번에도 앙상블기법이 나왔고 익숙한 XGBoosst,LightGBM,RandomForest가 등장했습니다.

 

변수 선택 Part에서 A/B test를 실습했습니다. 기본 변수를 정하고 하나씩 교차검증을 진행하는 내용이였는데

변수의 수가 100개 넘는데 이걸 하나씩 검증했다는 것자체에 좀 놀랬습니다

 

이렇게 까지해야 일등을 하는구나 생각이 들었어요 이 부분도 분명히 일일이 넣었다 뺐다 하지 않고 

코딩으로 구현 하셨을텐데 개인적으로 어떻게 하셨을지 궁금하네요

A/B테스트를 말로만 들었지 실제로 실습과정에서 적용한 예시는 처음 본 것같습니다 

 

하이퍼 파라미터 튜닝에도 저는 항상 그리드만 사용했는데 임의 탐색이라는 또다른 방법이 있었더라구요

그리드는 시간이 오래 걸리는 단점이 있어서 이를 보완할 방법으로 임의탐색이 있다고 소개합니다

 

저도 진행하면서 그리드가 너무 오래걸려서 불편한 적이 여러번 있었는데 다음에 기회가 생긴다면 임의탐색을 사용해보고싶네요

 

몇달전에 앙상블 처음 배우면서 들었던 것이 완성된 모델을 앙상블하는 방법은 있을까 생각한 적이 있었는데

이거를 오늘 보게 되었습니다! 5개의 모델을 만들고 이를 앙상블해서 더욱 일반화 성능에 가까운 수치를 얻을수 있다고 합니다. 이 부분도 매력적으로 보였어요 정말 다양한 방법들이 많더라구요. 제가 들은 얘기로는 이렇게 고도의 앙상블은 실제로 사용되지 않는다고 알고 있어요 그러니까 완전 대회용으로 앙상블을 사용한다고 하더라구요 (제가 듣기로는..)

 

아무튼 데이콘 1등 코드를 1장과 3장을 실습해 봤어요 

제가 이번 실습 직전에 택시수요예측을 진행했는데 ... 하.. 부끄럽더라구요 .. 뭔가 다 틀린것같고 ㅋㅋ 자신이 없어졌습니다 ㅎㅎ; 이 실습을 진행 후 택시예측을 했다면 좀더 세련된 프로젝트가 되었을 텐데 좀 아쉽습니다 

 

하지만 이 경험 덕분에 프로젝트를 어떻게 하는지에 대한 방향을 잡을수 있었습니다 정~말 좋은 선생님에게 코치 받은 느낌이였어요 

 

내일부터는 컵퓨터 비전 강의를 들으면서 실습한 내용을 업로드 할 예정입니다. open CV를 해야지 해야지 했는데 

좋은 기회가 생겨서 결제하고 공부 할수 있게 되었습니다 

 

마지막으로 실습한 내용은 차근차근 올리도록 하겠습니다 

2021/02/20 - [실습 note] - 버스 승차인원 예측 실습(데이콘 경진대회 1등 솔루션)

728x90

'Data Diary' 카테고리의 다른 글

2021-02-18(OpenCV_2)  (0) 2021.02.18
2021-02-17(OpenCV_1)  (0) 2021.02.17
2021-02-15(데이콘_버스 승차인원예측 실습)  (0) 2021.02.15
2021-02-13 기록(데이콘_KBO 실습4)  (0) 2021.02.13
2021-02-12 기록(데이콘_KBO 실습3)  (0) 2021.02.12
728x90

뉴욕 택시수요예측

2015 5월의 yellow 뉴욕택시 데이트를 사용해서 수요량을 예측합니다. 시간은 30분 단위로 진행되었으며 주 평가척도는 mae입니다. 외부데이터는 날씨 데이터를 참고했으며 LSTM, Gru timestep은 메모리 크기에 맞게 유동적으로 변경하면서 진행했습니다.

목차

1. EDA

1. 1 EDA(Region)

1.1.1 퍼센트 별 Trip_cnt

1.1.2 zip_code별 Trip_cnt

1.2 EDA(Time)

1.2.1 시간

           1.2.2 요일

           1.2.3 주말

2. 데이터전처리

    2.1 target 분포 확인 / log 적용

    2.2 날씨변수 추가

    2.3 Scaling

    2.4 PCA 생성

    2.5 원핫인코딩

    Train/Test Dataset

3. 모델 구축과 검증

    3.1 XGBoost

           3.1.1 GridSearch

           3.1.2 최적 파라미터 적용

    3.2 LightGBM

            3.2.1 GridSearch

            3.2.2 최적 파라미터 적용

            3.2.3 LightGBM_PCA삭제 한 모델 검증

     3.3 스태킹 앙상블(zip_code 7개로 축소한 모델)

            3.3.1 개별 Model 학습&평가

            3.3.2 스태킹 최종 모델 결과

     3.4 CV기반의 스태킹

            3.4.1 CV 함수정의

            3.4.2 개별 Model 학습&평가

     3.4 딥러닝

            3.4.1 TimeStep 설정

            3.4.2 Scaling

            3.4.3 Train, Test Dataset

            3.4.4. Model 설정

                      3.4.4.1 기본모델

                      3.4.4.2 LSTM

                      3.4.4.3 LSTM(layter1)

                      3.4.4.3 LSTM(layter2+ DropOut)

                      3.4.4.4 LSTM(layter3+ DropOut)

                      3.4.4.5 Gru

                      3.4.4.6 Gru(layter1)

                      3.4.4.7 Gru (layter2+ DropOut)

 

Import

!pip install chart_studio
import chart_studio.plotly as py
import cufflinks as cf
import pandas as pd
import numpy as np
import seaborn as sns
import math
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
%%time
base_query="""
WITH base_data AS 
(
  SELECT nyc_taxi.*, gis.* EXCEPT (zip_code_geom)
  FROM (
    SELECT *
    FROM `bigquery-public-data.new_york_taxi_trips.tlc_yellow_trips_2015`
    WHERE 
        EXTRACT(MONTH from pickup_datetime) = 5
        and pickup_latitude  <= 90 and pickup_latitude >= -90
    ) AS nyc_taxi
  JOIN (
    SELECT zip_code, state_code, state_name, city, county, zip_code_geom
    FROM `bigquery-public-data.geo_us_boundaries.zip_codes`
    WHERE state_code='NY'
    ) AS gis 
  ON ST_CONTAINS(zip_code_geom, st_geogpoint(pickup_longitude, pickup_latitude))
)

SELECT 
    zip_code,
    DATETIME_TRUNC(pickup_datetime, hour) as pickup_hour,
    concat (format_datetime("%F %H:",pickup_datetime),
    case floor(extract(minute from pickup_datetime)/30) when 1.0 then 30 else 00 end) as minute, 
    EXTRACT(MONTH FROM pickup_datetime) AS month,
    EXTRACT(DAY FROM pickup_datetime) AS day,
    CAST(format_datetime('%u', pickup_datetime) AS INT64) -1 AS weekday,
    EXTRACT(HOUR FROM pickup_datetime) AS hour,
    CASE WHEN CAST(FORMAT_DATETIME('%u', pickup_datetime) AS INT64) IN (6, 7) THEN 1 ELSE 0 END AS is_weekend,
    round(sum(passenger_count)) AS passenger_cnt, #승객수
    round(sum(trip_distance)) AS trip_cnt,  #운행거리
    round(sum(total_amount)) AS total_amount_cnt, #승객에게 부과된 택시비
    COUNT(*) AS cnt	#콜 수
FROM base_data 
GROUP BY zip_code, pickup_hour, month, day, weekday, hour, is_weekend,minute
ORDER BY zip_code,pickup_hour

"""
base_df = pd.read_gbq(query=base_query, dialect='standard', project_id='abcd')
  • 30분 단위로 나눕니다 (minute 변수로 지정)

EDA(Region)_퍼센트 별 trip_cnt

#전체 콜 대비 각 zip_code 비율
zipcode_sum=base_df.groupby(['zip_code'])[['cnt']].sum()
zipcode_sum

temp=(zipcode_sum/base_df[['cnt']].sum()).rename(columns={'cnt':'percent'})*100
temp.reset_index(inplce=True)
temp

temp['percent'].plot()

 

#log 값으로 변환
temp['log_cnt']=np.log10(temp['percent'])
plt.figure(figsize=(16,6));
sns.barplot(x='zip_code', y='log_cnt', data=temp);
sns.scatterplot(x='zip_code', y='log_cnt', data=temp);
  • log10(percent value) -> 0퍼센트 미만 값은 음수
  • log10(percent value) -> 0보다 큰 값은 양수
  • 정리, 전체 비율에서 1%를 기준으로 분류

# 퍼센트 비율이 1%미만을 제거 하기 위한 필터
# 1%이상인 zip_code는 26개
filter_temp=temp[temp['log_cnt']>0]  
filter_temp.head()
# len(filter_temp)


zip_code	percent	log_cnt
0	10001	5.002529	0.699190
1	10002	1.962145	0.292731
2	10003	5.366801	0.729715
7	10009	2.003886	0.301873
8	10010	3.014356	0.479195
# 인덱스 초기화
#총 zip_code 383개
zipcode_sum.reset_index(inplace=True)
#zipcode_sum.head()
len(zipcode_sum)

383

 

# 1% 이상의 26개 지역 trip_cnt 보기
temp_cnt=[]
temp_zip_code=[]
for zip_code,cnt in np.array(zipcode_sum):
    for zip_code2 in np.array(filter_temp['zip_code']):
      if zip_code == zip_code2:
        temp_cnt.append(cnt)
        temp_zip_code.append(zip_code)
                              
temp_26=pd.concat([pd.DataFrame(temp_zip_code),pd.DataFrame(temp_cnt)], axis=1)
temp_26.columns=["zip_code",'cnt']
temp_26.tail()


zip_code	cnt
21	10075	224972
22	10119	157338
23	10128	371243
24	11371	324979
25	11430	282593
# 상위26개 지역 trip_cnt
plt.xticks(rotation =90)
sns.barplot(x="zip_code",y='cnt',data=temp_26)

EDA(Region)_zip_code 별 trip_cnt

#base_df에서 상위 26개 zip_code 정보 출력
filter_zip_df = base_df[base_df['zip_code'].isin(temp_zip_code)]
filter_zip_df.head()

#zip_code,date로 gropby 시킨 후 count 지표들을 비교
filter_zip_df['date']= filter_zip_df['pickup_hour'].dt.date
filter_zip_daily_df=filter_zip_df.groupby(['zip_code',"date"])[['cnt','passenger_cnt','trip_cnt','total_amount_cnt']].sum().reset_index()
filter_zip_daily_df

#히트맵
plt.figure(figsize=(16,10))
sns.heatmap(filter_zip_daily_df.pivot_table(values='cnt', index='date', columns='zip_code'), lw=.1, cmap='GnBu');

filter_zip_daily_df.pivot_table('cnt', index='date', columns='zip_code').iplot(kind='box', x='zip_code', y='cnt')

  • 상위 26개 zip_code 중 10019가 가장 색이 짙은 걸로 보아 Trip 수가 많았음을 알 수 있다.
  • 인덱스를 사용해서 수치로 보겠습니다
#콜수 최대값의 인덱스
filter_zip_daily_df['cnt'].idxmax()

63
filter_zip_daily_df.iloc[63,:]

zip_code                 10003
date                2015-05-02
cnt                      32042
passenger_cnt            55439
trip_cnt                 76000
total_amount_cnt        437920
Name: 63, dtype: object
  • 5월 2일은 토요일이며, 이 날이 5월 달 중 가장 많은 이동을 보였습니다.
  • 매 주말 토요일마다 이동 수가 급증하는지를 알아볼 필요가 있어 보입니다. 
  • 시간대 별 시각화를 통해 전반적인 흐름을 파악합니다.

EDA(Time)_시간 

#시간대별 시각화
base_df['minute']=pd.to_datetime(base_df['minute'])
minute_group=base_df.groupby(["minute"])[["passenger_cnt","trip_cnt","total_amount_cnt","cnt"]].sum().reset_index()
minute_group=minute_group.set_index('minute')
minute_group.tail(5)

chart_studio.tools.set_credentials_file(username='name', api_key='abcde')
minute_group.iplot()

#위 링크 걸어둔 corab에서는 그래프가 없습니다(그래프 수 제한 초과)

  • 크기가 맞지 않으므로 따로 분리해서 자세히 보겠습니다.
#운행거리
minute_group[['trip_cnt']].iplot()

  • 운행거리를 나타내는 시각화
  • 5월 2일(토요일)과 5월 31일(일요일), 5월 초, 말에 급증세를 보입니다.
#승객수, 콜 수
minute_group[['passenger_cnt',"cnt"]].iplot()

  • 급증했던 5월 2일과 31일의 콜 수를 볼 때 특이점은 없어 보입니다.
  • 경기 또는 축제처럼 이벤트가 발생한 날인지 알아보기 위해서 조사했지만 특별한 정보는 얻을 수 없었습니다.
  • 가까운 지역 간의 이동이 아니라 장거리 손님이 많았음을 추론이 가능합니다
  • 월 초 주말(2일)에 다른 지역으로 장거리 이동 후, 월 말(31일)에 다시 복귀하는 현상일 수도 있습니다
  • cnt 지표를 보면, 2일 대비 31일 콜수가 현저히 낮습니다
  • 아마도 2일에 떠나간 사람들이 특정 지역으로 집중되었다면, 그 지역의 31일 골수는 급증세를 보일 거라 예상됩니다.
minute_group.reset_index(inplace=True)
sns.pairplot(minute_group, 
             diag_kind='kde',
             palette='bright') # pastel, bright, deep, muted, colorblind, dark
plt.show()

plt.figure(figsize=(16,10))
sns.heatmap(minute_group.corr(),annot=True)
plt.show()

  • trip_cnt(운행거리)를 제외하면 모두 강한 상관관계를 보입니다
  • 상관관계를 갖는 변수는 삭제 대신 PCA를 활용해서 살려 보도록 했습니다

 

EDA(Time)_요일

#0:월요일 ~ 6:일요일
weekday_df=base_df.groupby(['weekday'])[['cnt','passenger_cnt','trip_cnt','total_amount_cnt']].sum()
weekday_df.reset_index(inplace=True)

                              #콜수, 손님 수
weekday_df.iplot(x='weekday',y=['cnt','passenger_cnt'],kind='line')

				# 운행 거리, 택시 비
weekday_df.iplot(x='weekday',y=['trip_cnt','total_amount_cnt'],kind='line')

weekday_df.iplot(x='weekday',kind='bar')

base_df.groupby(['hour','weekday']).mean()[['cnt']].unstack().iplot()

  • 일요일을 제외한 날들은 저녁 7시에 콜수가 가장 많았음을 알 수 있습니다.
  • 새벽 5시 기점으로 증가합니다. 
  • 주말만 별도로 자세히 살펴보겠습니다

EDA(Time)_주말

#0:평일,1:주말
weekend_df=base_df.groupby(['is_weekend'])[['cnt','passenger_cnt','trip_cnt','total_amount_cnt']].sum()
weekend_df.iplot(kind='bar')

base_df.groupby(['hour','is_weekend'])['cnt'].mean().unstack().iplot()

#시간대별 평일VS주말 비교
plt.figure(figsize=(16,10))
sns.heatmap(base_df.groupby(['hour','is_weekend'])['cnt'].mean().unstack().rename(columns={0:'weekday', 1:'weekend'}), lw=.5, annot=True, cmap='GnBu',
            fmt='g',annot_kws={'size':10});

왼쪽:평일, 오른쪽:주말

  • 평일은 러시아워 시간대에 콜 수가 집중되어 있습니다.
  • 반면 주말은 비교적 고르게 분포되어 있습니다.

EDA(Time)_뉴욕의 전반적인 콜 수 흐름 보기

base_df2=base_df
base_df2['year']=2015
base_df2['date_except_hour']=pd.to_datetime(base_df2[['year','month','day']])
base_df2.groupby('date_except_hour').sum()[['cnt']].iplot()

  • 5월 25일 월요일에 콜수가 급감을 보입니다. 
  • 구글링으로 공휴일, 이벤트 등 조사해봤지만 특이사항은 없었습니다.
  • 현재까지 어떤 이유로 급감을 보였는지는 알 수가 없었습니다.

Model 순서 

1. XGBoost

2. LightGBM

3. 스태킹 앙상블

4. LSTM

5. GRU

 

1. 데이터 전처리

Import

import numpy as np
from lightgbm import LGBMRegressor
from xgboost import XGBRegressor
from sklearn.svm import SVR
from sklearn.ensemble import RandomForestRegressor, AdaBoostRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import KFold
from sklearn.metrics import mean_absolute_error
import pandas as pd
import numpy as np
import seaborn as sns
import math, time
import matplotlib.pyplot as plt

 

1.1 target 분포 확인 및 log 적용

#target분포 확인
y_target= base_df['cnt']
y_target.hist()

# target log화(정규분포와 최대한 비슷하게 만들기 위함)
y_log=np.log1p(y_target)

#log_cnt 삽입
base_df['log_cnt']=y_log

y_log.hist()

1.2 날씨 변수 추가 

#날씨 정보를 추가
#bad: 비가 내린 날, normal: 비가 내리지 않은 날
base_df["wheather"] = base_df["day"].apply(lambda x: 'bad' if x==16 or x==27 else "normal")

 

1.3 Scaling

from sklearn.preprocessing import StandardScaler

#객체 생성
scaler=StandardScaler() #기존 변수의 범위를 정규 분포로 변환

#강한 상관관계를 보인 passenger_cnt, total_amount_cnt는 차원축소로 변환할 예정
scaled_df=scaler.fit_transform(base_df[['passenger_cnt','trip_cnt','total_amount_cnt','cnt']])

#PCA 할 변수와 target 변수 분리
scaled_df=pd.DataFrame(scaled_df).iloc[:,0:3]
#기존 passenger_cnt, total_amount_cnt 변수 삭제
base_df.drop(['passenger_cnt','total_amount_cnt'], axis=1, inplace=True)
del scaled_df[1]  #trip_cnt 삭제
scaled_df=pd.DataFrame(scaled_df).rename(columns={0:"passenger_cnt",2:'total_amount_cnt'})
scaled_df

1.4 PCA

# 2개의 변수를 하나로 저차원 축소
from sklearn.decomposition import PCA
pca= PCA(n_components=1)

pca.fit(scaled_df)
pca_result= pca.transform(scaled_df)
print(pca_result,pca_result.shape)

[[ 3.87046796]
 [ 3.2337383 ]
 [ 1.69928273]
 ...
 [-0.70746083]
 [-0.70026516]
 [-0.7066679 ]] (158514, 1)

 

1.5 원핫인코딩

#원핫인코딩

#방법1
# get_dummies 
ohe_df=pd.get_dummies(base_df, columns=["zip_code",'day','hour','wheather'])
ohe_df.shape

(158514, 447)

#방법2
# #객체 생성
from sklearn.preprocessing import OneHotEncoder

# ohe= OneHotEncoder()
# ohe.fit(base_df[["zip_code",'day','hour','wheather']])
# ohe_output= ohe.transform(base_df[["zip_code",'day','hour','wheather']]).toarray()
# ohe_df=pd.concat([base_df, pd.DataFrame(ohe_output, columns=np.concatenate([ohe.categories_[0],ohe.categories_[1],ohe.categories_[2],ohe.categories_[3]], axis=0))],axis=1)
# ohe_df
#PCA 값과 합치기
df=pd.concat([ohe_df, pd.DataFrame(pca_result)], axis =1)
ohe_df=df.rename(columns={df.columns[-1]:"pca_value"})
ohe_df.tail()

 

1.6 Train/Test Dataset

def train_test_split(df,date):
  train_df=df[df['minute']<date]
  test_df=df[df['minute']>=date]
  return train_df,test_df

train_df,test_df=train_test_split(ohe_df, '2015-05-24')
#변수 삭제
del train_df['minute']
del test_df['minute']


# train/test 나누기
y_train_log=train_df.pop('log_cnt')
y_train_raw=train_df.pop('cnt')
x_train=train_df.copy()

y_test_log=test_df.pop('log_cnt')
y_test_raw=test_df.pop('cnt')
x_test=test_df.copy()

#시간 데이터 삭제
del x_train['pickup_hour']
del x_test['pickup_hour']

print('x_train shape:',x_train.shape, "y_train_log:",y_train_log.shape)

x_train shape: (117883, 447) y_train_log: (117883,)

 

XGBoost

def evaluate(y_true, y_pred):
  from sklearn.metrics import mean_absolute_error
  from sklearn.metrics import mean_squared_error
  y_true, y_pred = np.array(y_true), np.array(y_pred)
  mape= np.mean(np.abs((y_true-y_pred)/y_true))*100
  mae= mean_absolute_error(y_true,y_pred)
  mse= mean_squared_error(y_true,y_pred)
  score = pd.DataFrame([mape, mae, mse], index=['mape','mae','mse'], columns=['score'])
  return score
from xgboost import XGBRegressor
from sklearn.model_selection import GridSearchCV

xgb_reg= XGBRegressor()

#GridSearch
params={
    "max_depth":[5,7,10],
    "n_estimators":[100,300,500],
    "learning_rate":[0.001,0.01]
}
gridcv= GridSearchCV(xgb_reg, cv=2, param_grid= params, n_jobs=-1, verbose=True)
gridcv.fit(x_train,y_train_log)

print("GridSearchCV 최적 파라미터:", gridcv.best_params_)
print("GridSearchCV 최적 성능:", gridcv.best_score_)

GridSearchCV 최적 파라미터: {'learning_rate': 0.01, 'max_depth': 10, 'n_estimators': 500}
GridSearchCV 최적 성능: 0.9458285690503948
xgb_reg= XGBRegressor(n_estimators=500, random_state= 150, learning_rate=0.01, max_depth=10, objective='reg:squarederror')
xgb_reg.fit(x_train,y_train_log)
pred=xgb_reg.predict(x_test)
evaluate(y_test_log,pred)

 

plt.figure(figsize=(12,10))
plt.plot(pred[:200],  color='blue', label='pred')
plt.plot(y_test_log[:200].reset_index(drop=True), color='red', label='y_test')
plt.title('y_test VS pred')
plt.xlabel('index')
plt.ylabel('value')
plt.show()

 

 

LightGBM

from lightgbm import LGBMRegressor
from sklearn.model_selection import GridSearchCV

lgbm_reg= LGBMRegressor()

params={
    "max_depth":[5,7,10],
    "n_estimators":[100,300,500],
    "learning_rate":[0.001,0.01]
}
gridcv= GridSearchCV(lgbm_reg, cv=2, param_grid= params, n_jobs=-1, verbose=True)
gridcv.fit(x_train,y_train_log)

print("GridSearchCV 최적 파라미터:", gridcv.best_params_)
print("GridSearchCV 최적 성능:", gridcv.best_score_)

GridSearchCV 최적 파라미터: {'learning_rate': 0.01, 'max_depth': 7, 'n_estimators': 500}
GridSearchCV 최적 성능: 0.939007168663416
from lightgbm import LGBMRegressor
lgbm_reg= LGBMRegressor(n_estimators=500, random_state= 150, learning_rate=0.01, max_depth=7)
lgbm_reg.fit(x_train,y_train_log)
pred=lgbm_reg.predict(x_test)
score=evaluate(y_test_log, pred)
score

plt.figure(figsize=(12,10))
plt.plot(pred[:200],  color='blue', label='pred')
plt.plot(y_test_log[:200].reset_index(drop=True), color='red', label='y_test')
plt.title('y_test VS pred')
plt.xlabel('index')
plt.ylabel('value')
plt.legend()
plt.show()

  • 두 모델 중 XGBoost가 좀 더 좋은 성능을 보입니다.
  • 하지만 학습시간은 131.8 min 소요되었습니다. 반면 LightGBM은 1.2 min이 걸렸습니다
  • PCA 변수가 성능에 악영향을 주는지 알아보기 위해 전 후 차이를 보겠습니다.

 

LightGBM_PCA 삭제

#pca 삭제
del x_train['pca_value']
del x_test['pca_value']

lgbm_reg= LGBMRegressor(n_estimators=500, random_state= 150, learning_rate=0.01, max_depth=7)
lgbm_reg.fit(x_train,y_train_log)
pred2=lgbm_reg.predict(x_test)
score2=evaluate(y_test_log, pred2)
score2

plt.figure(figsize=(12,10))
plt.plot(pred2[:100], color='blue',label='pred')
plt.plot(y_test_log[:100].reset_index(drop=True), color='red', label='y_test')
plt.title('y_test VS pred')
plt.xlabel('index')
plt.ylabel('value')
plt.legend()
plt.show()

pca 삭제 전 성능
pca 삭제 후 성능

  • pca 전후 차이 오류 값에 차이를 보입니다. 
  • pca 삭제 후 다른 모델들을 실행해 보겠습니다.

스태킹 앙상블(zip_code 7개 샘플링)

  • 전체 데이터로는 Corab에서 실행 불가능하므로 데이터 수가 많은 상위 7개의 zip_code를 대상으로 분석함

  • EDA 시각화 중에서 heatmap 참고하여 데이터가 많은 zip_code을 골랐습니다.

 

개별 Model 설정&평가

#개별 ML 모델 생성
svr_reg= SVR()
rf_reg= RandomForestRegressor(n_estimators=100,random_state=0)
dt_reg= DecisionTreeRegressor()
ada_reg= AdaBoostRegressor(n_estimators=100)

#스태킹으로 만들어진 데이터세트를 학습,예측할 최종모델
lgbm_reg= LGBMRegressor(n_estimators=300, random_state= 150, learning_rate=0.01, max_depth=5)
#개별 모델 학습
svr_reg.fit(x_train,y_train_log)
rf_reg.fit(x_train,y_train_log)
dt_reg.fit(x_train,y_train_log)
ada_reg.fit(x_train,y_train_log)
# 학습된 개별 모델들이 각자 반환하는 예측 데이터 세트를 생성하고 개별모델의 정확도 측정
svr_pred=svr_reg.predict(x_test)
rf_pred=rf_reg.predict(x_test)
dt_pred=dt_reg.predict(x_test)
ada_pred=ada_reg.predict(x_test)

#평가
svr_score=evaluate(y_test_log, svr_pred)
rf_score=evaluate(y_test_log, rf_pred)
dt_score=evaluate(y_test_log, dt_pred)
ada_score=evaluate(y_test_log, ada_pred)
print(pd.concat([svr_score,rf_score,dt_score,ada_score],axis=1))

#각 개별 분류기의 예측값은 1차원 형태이므로 이걸 행 형태로 붙인 뒤 transpose를 취하여 데이터 세트로 만든다

#4행 형태로 붙인다
pred= np.array([svr_pred, rf_pred,dt_pred,ada_pred])
print(pred.shape)

# transpose로 행과 열의 위치 교환하여 각 컬럼끼리 위치가 맞도록 해준다
final_pred=np.transpose(pred)
print(final_pred.shape)

(4, 2688)
(2688, 4)

 

 

스태킹 최종 모델 결과

# 최종 메타 모델인 LightGBM를 실시
lgbm_reg.fit(final_pred, y_test_log)
final=lgbm_reg.predict(final_pred)

evaluate(y_test_log,final)

스태킹 성능

  • 가장 낮은 에러 성능 수치를 보입니다.
  • 이번에는 CV를 적용하여 스태킹을 재 실행해 보겠습니다.

 

CV 기반의 스태킹

def get_stakong_base_datasets(model, x_train, y_train, x_test, n_folds):
  from sklearn.model_selection import KFold
  from sklearn.metrics import mean_absolute_error

  kf = KFold(n_splits=n_folds, shuffle=False, random_state=0)

  train_fold_pred=np.zeros((x_train.shape[0],1)) #x_train에서 n_folds수 만큼 학습,검증폴드 분리됨, 즉 검증세트=x_train.shape[0] 될때까지 진행
  test_pred= np.zeros((x_test.shape[0],n_folds)) #x_test의 예측값이 n_folds수 만큼 생성됨 
  print(model.__class__.__name__,'model start')

  for folder_counter, (train_idx, valid_idx) in enumerate(kf.split(x_train)):
    print('\n폴드세트:', folder_counter,'학습/검증폴드 생성')
    train_x= x_train[train_idx] #x_train의 학습폴드
    train_y= y_train[train_idx] #y_train의 학습폴드
    test_x= x_train[valid_idx]  #x_train의 검증폴드

    #학습폴드 인덱드에 해당하는 x_train, y_train를 가지고 fit 진행
    model.fit(train_x,train_y)

    #학습한 model를 검증폴드 예측(최종모델의 학습 데이터로 사용)
    train_fold_pred[valid_idx,:]=model.predict(test_x).reshape(-1,1)

    #학습한 model를 원본 테스트 데이터인 x_test 예측
    test_pred[:,folder_counter]=model.predict(x_test)
  
  #x_test를 예측한 값을 열 기준으로 평균내고 세로로 쌓는다(최종모델의 test 데이터로 사용)
  test_mean=np.mean(test_pred, axis=1).reshape(-1,1)

  return train_fold_pred,test_mean

 

 

개별 Model 설정&평가

 

#개별 ML 모델 생성
svr_reg= SVR()
rf_reg= RandomForestRegressor(n_estimators=100,random_state=0)
dt_reg= DecisionTreeRegressor()
ada_reg= AdaBoostRegressor(n_estimators=100)

#스태킹으로 만들어진 데이터세트를 학습,예측할 최종모델
lgbm_reg= LGBMRegressor(n_estimators=300, random_state= 150, learning_rate=0.01, max_depth=5)

#ndarray로 변경(안하면 get_stakong_base_datasets 적용 x)
x_train=np.array(x_train)
y_train_log=np.array(y_train_log)
x_test=np.array(x_test)
svr_train,svr_test=get_stakong_base_datasets(svr_reg,x_train,y_train_log,x_test,3)
rf_reg_train,rf_reg_test=get_stakong_base_datasets(rf_reg,x_train,y_train_log,x_test,3)
dt_reg_train,dt_reg_test=get_stakong_base_datasets(dt_reg,x_train,y_train_log,x_test,3)
ada_train,ada_test=get_stakong_base_datasets(ada_reg,x_train,y_train_log,x_test,3)
stack_final_x_train=np.concatenate((svr_train,rf_reg_train,dt_reg_train,ada_train),axis=1)
stack_final_x_test=np.concatenate((svr_test,rf_reg_test,dt_reg_test,ada_test), axis=1)
print('원본 학습 피처 데이터 shape:', x_train.shape, '원본 테스트 피처 shape:',x_test.shape)
print('스태킹 학습 피처 데이터 shape:', stack_final_x_train.shape, '스태킹 테스트 피처 데이터 shape:',stack_final_x_test.shape)

원본 학습 피처 데이터 shape: (7728, 68) 원본 테스트 피처 shape: (2688, 68)
스태킹 학습 피처 데이터 shape: (7728, 4) 스태킹 테스트 피처 데이터 shape: (2688, 4)
lgbm_reg.fit(stack_final_x_train,y_train_log) #원본 학습 label과 fit
stack_final=lgbm_reg.predict(stack_final_x_test)
evaluate(y_test_log,stack_final)

  • CV 적용하기 전에 비해 성능이 안 좋아졌습니다.
  • CV를 적용한다고 무조건의 성능개선은 이뤄지진 않습니다.
  • LSTM, GRU를 활용해서 딥러닝에서의 성능은 어떨지 실행해 보겠습니다. 

 

 

 

딥러닝

Import

import tensorflow as tf
from tensorflow.keras.layers import Input, LSTM, GRU, Dense,Dropout
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras import layers
from tensorflow.keras.optimizers import SGD, Adam, RMSprop
from tensorflow.keras import models
from tensorflow.keras.wrappers.scikit_learn import KerasRegressor
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import make_regression

import seaborn as sns
import numpy as np
from datetime import datetime 

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler

  • zip_code 7개 중 5개로 줄여서 진행했습니다(메모리 부족)

 

 

TimeStep 설정

# ex) 12시~12시30분의 minute 값은 0, 12시 30분~13시의 minute 값은 30 
ohe_df['minute']=pd.to_datetime(ohe_df['minute'])
ohe_df['minute2']=ohe_df['minute'].dt.minute

# 시간대별 정렬
ohe_df=ohe_df.sort_values(by=['hour','minute2'], axis=0)
# ohe_df.to_excel('imsi.xlsx')
ohe_df.head()

  • timestep은 시간대 별로 정렬했습니다. 4시간 단위로 묶었습니다.
  • ex) 12시~4시까지의 데이터로 4시~4시 30분의 값을 예측합니다.
  • 각 시간대별 몇 개의 데이터가 있는지 아래 코드를 통해 알아봅니다.
num=0
total_=[]
for idx in range(np.array(ohe_df).shape[0]):
  try:
    if ohe_df.iloc[idx]['minute2'] == ohe_df.iloc[idx+1]['minute2'] and ohe_df.iloc[idx]['hour'] == ohe_df.iloc[idx+1]['hour']:
      num+=1
    else:
      print('zip_code는{0} 이며 {1}의 총 갯수는 {2}입니다'.format(ohe_df.iloc[idx]['zip_code'],ohe_df.iloc[idx]['minute'], num+1))
      total_.append(num+1)  # 1를 더해준 이유: 처음 시작 인덱스는 0부터이다. 코드에서는 1부터 시작한다. 
      num=0  #카운트 초기화
  except:

    try:
      if ohe_df.iloc[idx]['minute2'] == 30 and ohe_df.iloc[idx]['hour'] == 23:  #23시30분~00시 처리부분
        print('zip_code는{0} 이며 {1}의 총 갯수는 {2}입니다'.format(ohe_df.iloc[idx]['zip_code'],ohe_df.iloc[idx]['minute'], num+1))
        total_.append(num)
      else:
        break
    except:
      break
      
      
      
zip_code는10019 이며 2015-05-31 00:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 00:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 01:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 01:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 02:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 02:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 03:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 03:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 04:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 04:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 05:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 05:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 06:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 06:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 07:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 07:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 08:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 08:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 09:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 09:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 10:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 10:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 11:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 11:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 12:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 12:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 13:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 13:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 14:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 14:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 15:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 15:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 16:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 16:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 17:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 17:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 18:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 18:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 19:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 19:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 20:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 20:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 21:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 21:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 22:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 22:30:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 23:00:00의 총 갯수는 155입니다
zip_code는10019 이며 2015-05-31 23:30:00의 총 갯수는 155입니다

엑셀표

  • 같은 시간대를 묶은 결과 각 시간대 별 155개가 있음을 확인했습니다.
  • 4시간을 기준으로 Timestep을 설정해야 하므로 155*8=1240 길이가 됩니다.
#변수 삭제
ohe_df.drop(['pickup_hour','month',"zip_code",'minute','minute2',"wheather",'hour','day'], axis=1, inplace=True)

#feature/label 정의
label_raw=ohe_df['cnt']
label_raw=np.array(label_raw).reshape(-1,1)

label_log=ohe_df['log_cnt']
label_log=np.array(label_log).reshape(-1,1)

input_data=ohe_df.drop(['cnt','log_cnt'],axis=1)
input_data=np.array(input_data)
print("input_data:",input_data.shape, 'label_data_y_log:',label_log.shape)

input_data: (7440, 65) label_data_y_log: (7440, 1)

 

 

 

Scaling

from sklearn.preprocessing import MinMaxScaler

# label_data scaling
label_scaler = MinMaxScaler()
label_raw = label_scaler.fit_transform(label_raw)
  • 다양한 시도를 위해 이번에는 cnt값을 MinMaxScaler를 사용하겠습니다.

 

Train, Test Dataset 

T=620 #2시간 과거 데이터
N=len(input_data)-T 
D=input_data.shape[1] 

#데이터 넣을 공간 만들기
x=np.zeros((N,T,D))
y_log=np.zeros(N)
y_raw=np.zeros(N)

for t in range(N):
  x[t,:,:]= input_data[t:t+T]
  y_log[t]= label_log[t+T]
  y_raw[t]= label_raw[t+T]

print(x.shape, y_log.shape)

(6789, 651, 65) (6789,)
  • 원래 계획은 4시간 길이였지만 메모리 부족으로 인해 2시간 데이터로 변경되었습니다.
from sklearn.model_selection import train_test_split

x_train,x_test,y_train,y_test= train_test_split(x,y_raw,test_size=0.2 ,shuffle=False ,random_state=5)
print("x_train:", x_train.shape, "y_train:",y_train.shape)

x_train: (5431, 651, 65) y_train: (5431,)

 

신경망 모델

신경망 모델에서는 LSTM과 Gru를 주로 사용했습니다. 성능 개선과 과적합 제거 목적으로 각 모델별로 조금씩 다르게 구성했습니다. 머신러닝과 직관적인 비교를 위해 성능지표는 mae로 통일해서 진행했습니다.

 

mae (Mean absolute error)-실제 값과 측정(예측) 값과의 차이

n = 오차의 개수

∑ = 합을 나타내는 기호

|xi−x| = 절대 오차

mae는 실제값에서 예측값을 빼준 절대 오차값을 n만큼 나눈 값이기 때문에 낮을수록 좋은 성능을 나타냅니다.

기본 모델 설정

  • 비교를 위해 기본 모델로 실행했습니다.
  • 머신러닝과의 직관적인 성능 차이를 보기 위해서 성능지표를 mae로 통일
model = Sequential()
model.add(layers.Flatten(input_shape=(T,input_data.shape[1])))  #(8,58)
model.add(layers.Dense(32, activation='relu'))
model.add(layers.Dense(1))
model.compile(optimizer=Adam(), loss = 'mse', metrics=['mae'])
 history= model.fit(x_train,y_train,
                    epochs=100,
                    batch_size=32,
                    validation_data=(x_test, y_test))

.

.

.

 

#그래프 시각화
plt.plot(history.history['mae'], label='mae')
plt.plot(history.history['val_mae'],label='val_mae')
plt.legend()
plt.show()

 

LSTM

model = Sequential()
model.add(layers.LSTM(32,input_shape=(T,input_data.shape[1])))
model.add(layers.Dense(1))
model.compile(optimizer=Adam(), loss = 'mse', metrics=['mae'])
earlystopper = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=15, verbose=1) 
history= model.fit(x_train,y_train,
                    epochs=100,
                    batch_size=32,
                    callbacks=[earlystopper],
                    validation_data=(x_test, y_test))

LSTM(층 추가)

model = Sequential()
model.add(layers.LSTM(32,return_sequences=True,input_shape=(T,input_data.shape[1])))
model.add(layers.LSTM(64))
model.add(layers.Dense(1))
model.compile(optimizer=Adam(), loss = 'mse', metrics=['mae'])
earlystopper = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=15, verbose=1) # loss감소가 10번 이상 미발생이면 stop
history= model.fit(x_train,y_train,
                    epochs=100,
                    batch_size=32,
                    callbacks=[earlystopper],
                    validation_data=(x_test, y_test))

LSTM(층 추가+Dropout)

model = Sequential()
model.add(layers.LSTM(32,return_sequences=True,input_shape=(T,input_data.shape[1])))
model.add(Dropout(0.3))
model.add(layers.LSTM(64))
model.add(Dropout(0.3))
model.add(layers.Dense(1))
model.compile(optimizer=Adam(), loss = 'mse', metrics=['mae'])
earlystopper = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=15, verbose=1) # loss감소가 10번 이상 미발생이면 stop
history= model.fit(x_train,y_train,
                    epochs=100,
                    batch_size=32,
                    callbacks=[earlystopper],
                    validation_data=(x_test, y_test))

  • 층을 추가한 LSTM 모델에서 val_mae:0.0771로 가장 좋은 성능을 보였고
  • 층을 추가한 상태에서 Dropout을 적용한 결과 같은 epoch대비 성능을 개선시키지 못했습니다
  • patience=15로 짧게 설정했기 때문에 개선 과정 중 멈췄을 가능성도 있습니다

GRU

# Gru
model = Sequential()
model.add(layers.GRU(32,input_shape=(T,input_data.shape[1])))
model.add(layers.Dense(1))
model.compile(optimizer=Adam(), loss = 'mse', metrics=['mae'])
earlystopper = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=15, verbose=1) # loss감소가 10번 이상 미발생이면 stop
history= model.fit(x_train,y_train,
                    epochs=100,
                    batch_size=32,
                    callbacks=[earlystopper],
                    validation_data=(x_test, y_test))

GRU(층 추가)

# Gru
model = Sequential()
model.add(layers.GRU(32,return_sequences=True,input_shape=(T,input_data.shape[1])))
model.add(layers.GRU(64))
model.add(layers.Dense(1))
model.compile(optimizer=Adam(), loss = 'mse', metrics=['mae'])
earlystopper = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=15, verbose=1) # loss감소가 10번 이상 미발생이면 stop
history= model.fit(x_train,y_train,
                    epochs=100,
                    batch_size=32,
                    callbacks=[earlystopper],
                    validation_data=(x_test, y_test))

GRU(층 추가+Dropout)

# Gru
from tensorflow.keras.layers import Dropout

model = Sequential()
model.add(layers.GRU(32,return_sequences=True,input_shape=(T,input_data.shape[1])))
model.add(Dropout(0.3))
model.add(layers.GRU(64))
model.add(Dropout(0.3))
model.add(layers.Dense(1))
model.compile(optimizer=Adam(), loss = 'mse', metrics=['mae'])
earlystopper = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=15, verbose=1) # loss감소가 10번 이상 미발생이면 stop
history= model.fit(x_train,y_train,
                    epochs=100,
                    batch_size=32,
                    callbacks=[earlystopper],
                    validation_data=(x_test, y_test))

 

 

  • Gru 모델도 층 추가한 모델이 val_loss:0.0863으로  가장 좋은 성능을 보였습니다.
  • 추가로 Dropout을 한 결과 오히려 과적합 현상이 의심되는 그래프를 보입니다.

 

 

스태킹 성능

  • Lightgbm 스태킹 mae : 0.095
  • LSTM의 val_mae: 0.0771

 

728x90
728x90

오늘은 제목처럼 버스 승차인원 예측 part를 시작했습니다

택시수요예측과 비슷한 부분이 있을거 같아서 선택했는데 

 

예상처럼 낯선 느낌은 들지 않았어요 

KBO는 야구의 야 자도 몰랐던 상태라 낯설었고 해서 시간이 많이 걸렸는데 

이번거는 코드가 복잡하게 이뤄지지 않았고 설명도 좀더 자세히 되어 있어서 좋았습니다

 

EDA를 끝냈고 데이터 전처리 끝부분쯤 진행 하고 있는데 

이번 실습에서 주목해야 할점은 파생변수인것 같아요

 

파생변수에 대해서 내부변수,외부변수 두 부분으로 나뉘어 있어요 

배울점이 많았습니다

더이상 없을것같았던 내부변수에서 또다른 파생변수를 파생시켰고

 

외부변수를 가져오기까지의 인사이트가 돋보였습니다. 깔끔하게 정리된 내용만 봐도 

이부분을 고심하기까지 얼마나 많은 시간을 들였을까 생각이 나더라구요 

 

하지만 이해가지 않은 코드는 있었습니다

f['interval']=interval_lst #interval_lst를 interval 변수로 생성

#interval 변수의 값을 문자형으로 변환 후 시:분:초 형태로 변환
f['interval']=f['interval'].astype(str).str[7:].replace("","00:00:00") #??
f['bus_route_id']=f['bus_route_id'].astype(object) #?

위에 이미지처럼 형태가 통일이 되지 않았습니다 이를 통일시키기 위해서 위 코드를 사용했는데

replace의 00:00:00에서 막히더라구요 제가 알고 있는 replace는 값 자체를 바꿔주는 것으로 알고있습니다

만일 Timedelta('0 days 01:26:44') 라면 replace는 01:26:44게 아니고 00:00:00이아닌가싶은거죠

밑에 object도 왜 해야하는지 아직 모르겠습니다 구글링을 잠깐 해봤는데 원하는 답은 못구했습니다

 

내일 실습이 끝나면 여러가지 실험을 하면서 알아봐야 할것같아요 

728x90
728x90

오늘 실습을 끝냈습니다

연휴가 겹쳐서 4일정도가 소요가 되었습니다

마지막에 반발계수라는 개념이 나옵니다. 공인구의 반발계수를 낮추면 비거리가 감소하는 효과를 보입니다.

2019년도 상반기를 예측하는 이번 실습 내용에서는 2019년도부터 다운그레이드된 반발계수를 적용시켰더라구요 

 

이부분이 좀 참신한거 같아요

반발계수를 적용하자는 아이디어 보다는 이를 도출하는 과정이 참신했어요

 

이해가 가지 않는 부분도 있어요

2018시즌에 1726개의 홈런이 있었어요 이 수를 나누기 2를 하더라구요 1762/2=863홈런으로 줄어 들었을때 얼마나 OPS가 줄어 드는가? 제 생각엔 1762/2 이 부분이 반발계수의 감소로 인한 홈런의 수를 추론 한거 같네요 

2에 대한 따로 추가적인 설명은 없었어요 

제 생각엔 홈런이다 아니다 두가지 경우를 가지고 2로 나눈것같아요

 

여기서, 홈런이 되지 않아도 안타가 되면 OPS 하락에는 영향을 미칠 가능성이 떨어지니까 

아웃 혹은 안타 이 두가지 경우를 가지고 다시 2를 나눕니다 

863/2=431.5 이 숫자가 반발계수를 줄었을때의 홈런의 개수라는 얘기인거죠

 

그럼 총 타자의 수를 나누어서 431.5/176=2.45, 즉 흠런 개수가 평균적으로 2.45개 적은 시즌의 OPS 수치를 참고한다는 내용입니다. 

 

논리적인 사고력의 필요성을 깨닫게 된 실습이였어요 1등을 괜히 한게 아니구나 싶었고

이 정도로만 하면 경쟁력이 있다는 생각에 의욕이 샘솟습니다! 

 

일단 실습내용 업로드는 차근차근 시간 쪼개서 하도록 할거고

택시프로젝트도 임시저장으로 해놨어요 조금씩 수정만 하고 월요일에 업로드 예정입니다

 

2021/02/20 - [실습 note] - KBO 타자 OPS 예측 실습(데이콘 경진대회 1등 솔루션)

 

 

728x90
728x90
#시간변수를 생성하느 함수 정의
def lag_function(df,var_name, past):
    # df = 시간변수를 생성할 데이터 프레임
    # var_name= 시간변수 생성의 대상이 되는 변수 이름
    # past= 몇 년 전의 성적을 생성할지 결정(정수형)
    
    df.reset_index(drop=True, inplace=True)
    #시간변수 생성
    df['lag'+str(past)+'_'+var_name] = np.nan #결측치로 채워 넣어 놓는다
    df['lag'+str(past)+'_'+'AB'] = np.nan
    
    for col in ['AB',var_name]:
        for i in range(0, (max(df.index)+1)):
            val=df.loc[(df['batter_name']==df['batter_name'][i])& #이름이 가르시아 이면서
                       (df['year']==df['year'][i]-past),col]   #년도는 i년도
            #과거 기록이 결측치가 아니라면 값을 넣기
            if len(val)!=0:
                df.loc[i,'lag'+str(past)+'_'+col]=val.iloc[0] #i번째 행에 삽입
    
    #30타수 미만 결측치 처리
    df.loc[df['lag'+str(past)+'_'+'AB']<30,
          'lag'+str(past)+'_'+var_name]=np.nan   #var_name 행의 존재하는 30미만은 제거하고
    df.drop('lag'+str(past)+'_'+'AB', axis=1, inplace=True)  #AB열을 제거 하여 var_name만 남김
    return df

위 코드를 이해 하는데 1시간이상이 걸렸어요

교재에는 짧게 설명이 되어 있어서 이 함수를 왜 만들고 무엇을 위한 목적인지 파악하는데 조차 

이해가 안되었어요 

 

일정도 새로 넉넉하게 짜야 할것같구요 

이 실습에 대해서 어떤 방향으로 공부 할지 고민좀 해봐야 할것같아요

한번 실습했다고 제 것이 될것같진 않더라구요

 

코드도 공부가 되지만 일단 인사이트를 어떤 식으로 뽑는지 그런 견문을 넓히는게 우선인 것같네요

 

 

728x90
728x90

어제에 이어서 데이콘 KBO 실습의 데이터 전처리 부분을 진행 좀 했습니다

오늘은 어제 말씀드린 것처럼 오후에 실습을 진행했어요 

얼마 못했다는 얘기죠 ㅠ 

 

일단 오늘 작업한 내용중 이해안가는 코드는 없었어요 

하지만 새로웠던 것은 인사이트를 뽑는 방식이였습니다. 

제가 이 책을 산 이유 중 하나죠 

아직 진도도 얼마 안나갔는데 

그에 비해 꽤 많이 깨우친 것 같아요

 

데이터 핸들링과 인사이트의 견문이 좀 넓어진 느낌?

아 그리고 제가 방금 강의를 결제 했습니다 :) 

총 다섯가지 패키지로 묶였는데 설 기념으로 세일을 하더라구요

 

그 패키지 안에 좀 비싸서 구매 망설였던 강의 2개나 포함되어 있었서 

고민 하다가 어차피 필요한 내용이고 언젠가는 살거같다는 생각에 세일할때 구매 했습니다

 

gan,컴퓨터비전,자연어처리, 이 세가가 가장 기대가 됩니다 

특히 컴퓨터비전이 제가 가장 듣고 싶었던 과목이었고요 책도 신속하게 주문했습니다

 

정말 열심히 공부 할일만 남았네요 

매일 집에서 갇혀있는 느낌으로다가 공부만하면 상당히 힘들긴 하지만 .. ㅎㅎㅎㅎ 

제가 선택한 길이기 때문에 감내 해야 합니다! 

 

일단 내일도 아침일찍 일어나야 하기 때문에 오늘은 좀 일찍 자려고 합니다 

내일도 집에 오는대로 다시 실습 진행 하겠습니다 

728x90

'Data Diary' 카테고리의 다른 글

2021-02-13 기록(데이콘_KBO 실습4)  (0) 2021.02.13
2021-02-12 기록(데이콘_KBO 실습3)  (0) 2021.02.12
2021-02-10 기록(데이콘_KBO 실습)  (0) 2021.02.10
2021-02-09 기록  (0) 2021.02.09
2021-02-08 기록  (0) 2021.02.08
728x90

아침에 바로 일정을 정해 봤어요

어제 자소서는 끝났고 오늘부터 실습을 진행해서 다음 주 금요일까지 기간을 정했습니다

1개 단원 당 이틀을 생각하고 있는데 

 

직접 하룻동안 해보니까 아~마도 더 걸릴 거 같아요

새로운 방법들과 이해 안가는 것들이 더러 있어서 중간마다 시간이 잡아먹더라고요 

 

제 목적은 내 것으로 만드는 거지 그냥 기계적으로 실습 코드를 따라 치는게 아니기 때문이죠

내가 이해한 게 맞는지 확인받고 싶은데 그런 게 없으니까 살짝 아쉽긴 했습니다

 

첫 단원인 KBO 성적 예측을 시작했고 현재 EDA 파트는 끝났습니다.

확실히 택시수요예측을 한 덕인지 익숙하고 예전보다는 쉽게 느껴졌습니다

다음은 데이터 전처리를 할 예정인데 

내일은 만두 빚으러 가야 하기 때문에 오래는 못할 것 같습니다 

 

그럼 내일 경과보고 진도 나가게 되면 기록하겠습니다 

728x90

'Data Diary' 카테고리의 다른 글

2021-02-12 기록(데이콘_KBO 실습3)  (0) 2021.02.12
2021-02-11 기록(데이콘_KBO 실습2)  (0) 2021.02.11
2021-02-09 기록  (0) 2021.02.09
2021-02-08 기록  (0) 2021.02.08
2015-02-06 기록  (0) 2021.02.06

+ Recent posts