머신러닝·딥러닝 캐글 생활백서 – 피처 요약표, 데이터 다운캐스팅, 디버깅 팁, 모델 저장 및 불러오기

캐글 도전을 위한 4가지 팁을 소개하는 ‘캐글 생활백서’입니다. 피처 요약표, 데이터 다운캐스팅, 디버깅 팁, 모델 저장 및 불러오기를 정리했습니다.

 

1. 피처 요약표

 

1.1 피처 요약표 만들기

피처 요약표는 피처별 데이터 타입, 결측값 개수, 고윳값 개수, 실제 입력값 등 피처에 관한 정보가 정리된 표입니다. 요약표를 만드는 코드와 요약표에 관한 설명이 조금 깁니다. 그래서 요약표를 먼저 보여드리고, 만드는 방법을 이어서 설명하겠습니다. 피처 요약표는 7장 범주형 데이터 이진분류 경진대회 데이터를 기준으로 만들었습니다.

 

▼ 피처 요약표

 

이 그림이 피처 요약표입니다. 피처 23개와 타깃값 1개에 관한 정보가 기재돼 있습니다.

앞의 요약표를 만든 코드를 살펴보겠습니다. 피처 요약표는 7장 범주형 데이터 이진분류 경진대회 데이터를 활용해 만들었습니다. 7장 데이터를 먼저 불러옵니다.

 

 

피처 요약표를 만드는 과정을 다음의 세 단계로 나눠 살펴보겠습니다.

  1. 피처별 데이터 타입 DataFrame 생성
  2. 인덱스재설정후열이름변경
  3. 결측값 개수, 고윳값 개수, 1~3행 입력값 추가

 

피처별 데이터 타입 DataFrame 생성

요약표 두 번째 열은 데이터 타입입니다. 따라서 피처별 데이터 타입부터 구해보겠습니다. DataFrame 객체에서 dtypes를 호출하면 피처별 데이터 타입을 반환해줍니다. 피처가 많아 여기서는 첫 5개만 출력해보겠습니다.

 

 

이 값을 입력으로 DataFrame을 새로 생성하면 피처별 데이터 타입이 입력된 DataFrame이 만들어집니다. 이때 다음과 같이 columns 파라미터로 원하는 열 이름을 설정할 수 있습니다.

 

 

인덱스 재설정 후 열 이름 변경

그런데 피처 이름들이 인덱스로 사용되었네요. reset_index( )로 인덱스를 재설정하겠습니다. reset_index( )를 호출하면 현재 인덱스를 열로 옮기고 새로운 인덱스를 만듭니다. 새로운 인덱스는 0부터 시작해 1씩 증가하는 정수이며, 옮겨진 열의 이름은 ‘index’가 됩니다.

 

 

현재 피처 이름이 포함된 열 이름이 index입니다. rename( ) 함수를 활용해 열 이름을 ‘피처’로 바꿔주겠습니다.

 

 

결측값 개수, 고윳값 개수, 1~3행 입력값 추가

DataFrame에 결측값 개수, 고윳값 개수, 첫 세 개 행에 입력된 값을 추가해보겠습니다.

 

# 피처별 결측값 개수 ❶
summary['결측값 개수'] = train.isnull().sum().values  
# 피처별 고윳값 개수 ❷
summary['고윳값 개수'] = train.nunique().values
# 1 ~ 3행에 입력되어 있는 값 ❸
summary['첫 번째 값'] = train.loc[0].values
summary['두 번째 값'] = train.loc[1].values
summary['세 번째 값'] = train.loc[2].values

summary.head()

 

❶ 은 피처별 결측값 개수를 DataFrame에 추가합니다. isnull( )은 결측값 포함 여부를 True, False로 반환하는 함수입니다. 파이썬에서 True는 1, False는 0으로 간주합니다. 따라서 isnull( )을 적용한 DataFrame에 sum( ) 함수를 호출하면 True의 개수, 즉 피처별 결측값 개수를 구해줍니다.

❷ 는 피처별 고윳값 개수를 추가합니다. nunique( )는 피처별 고윳값 개수를 구하는 함수입니다.

❸ 은 훈련 데이터 1~3행에 입력된 값을 요약표 DataFrame에 추가하는 코드입니다. 각 피처에 실제 어떤 값이 들어있는지 확인하려고 추가했습니다. loc[0]은 첫 번째 행, loc[1]은 두 번째 행, loc[2]은 세 번째 행의 값을 의미합니다.

그런데 ❶, ❷, ❸에서 마지막에 values가 붙었네요. 이해를 돕기 위해 values 코드를 적용하기 전 객체가 어떻게 생겼는지 보겠습니다.

 

▼ 피처별 결측값, 고윳값, 첫 행 입력값

 

반환 타입인 Series는 인덱스(bin_0, bin_1 등)와 값values(0 등)의 쌍으로 이루어져 있습니다. Series 객체에서 값만 추출하려면 values를 호출하면 됩니다. ❶, ❷, ❸에서 모두 values 코드로 Series의 값을 불러와 피처 요약표(summary)에 추가했습니다.

드디어 피처 요약표가 완성됐습니다. summary를 출력하면 처음 보여드린 그림이 나타납니다.

 

전체 코드

 

1.2 피처 요약표 생성 함수

지금까지 피처 요약표를 만드는 방법을 배웠습니다. 간단하게 함수 형태로 만들어보겠습니다.

 

 

본문에서는 이 함수를 그대로 사용하거나 응용해서 피처 요약표를 만듭니다.

 

2. 메모리 절약을 위한 데이터 다운캐스팅

메모리 절약을 위한 데이터 다운캐스팅 방법을 알아보겠습니다. 향후 판매량 예측 경진대회 데이터를 기준으로 설명하겠습니다. 먼저 데이터를 불러옵니다.

 

 

전체 피처의 다운캐스팅 양상을 보기 위해 데이터를 모두 합치겠습니다. 참고로 9장에서는 데이터마다 따로따로 다운캐스팅했습니다. 데이터별로 다르게 작업할 사항이 있기 때문입니다.

 

 

합친 train의 피처별 데이터 타입을 살펴보시죠.

 

 

object, int64, float64로 다양합니다. 각 피처별 메모리 사용량을 알아보겠습니다. memory_usage( ) 메서드를 사용하면 피처별 메모리 사용량을 바이트 단위로 구할 수 있습니다.

 

 

피처당 메모리 23,486,792 바이트를 차지합니다. train의 메모리 총 사용량을 메가바이트(MB)단위로 구해보겠습니다.

 

 

246MB입니다. 각 피처마다 피처가 가지고 있는 모든 값을 포괄하는 데이터 타입으로 줄여볼까요. 원리는 다음과 같습니다.

object 타입이면 그대로 둡니다. 불리언 타입이면 int8로 바꿉니다. 정수형 타입이거나 실수형 타입 중 소수점 첫째 자리에서 반올림한 수가 원래 수와 같다면 정수형 타입 중 해당 데이터에 맞는 가장 작은 타입으로 바꿉니다. 그외 타입은 실수형 타입 중 해당 데이터에 맞는 가장 작은 타입으로 바꿉니다. 코드로 구현하면 다음과 같습니다.

💡Note: 실수형 타입 중 1.0, 10.0, 12.0과 같이 반올림해도 원래 수와 같은 경우엔 정수 형으로 바꾼다는 이야기입니다.

 

 

데이터 다운캐스팅을 했으니 다시 데이터 타입을 출력해보겠습니다.

 

 

기존에 object, int64, float64가 있었습니다. object는 그대로 object로 남아 있고, int64와 float64가 int8, int16, float32로 바뀌었습니다. 다운캐스팅 후 메모리 사용량도 알아봅시다.

 

 

많이 줄어들었습니다. 기존 데이터의 메모리 사용량은 246MB였습니다. 이 값이 얼마로 떨어졌을까요?

 

 

142MB입니다. 거의 100MB 가량 줄어들었습니다. 다음 코드를 실행하면 몇 퍼센트 압축됐는지 알 수 있습니다.

 

 

2.2 데이터 다운캐스팅 함수

지금까지 배운 데이터 다운캐스팅 코드를 함수로 표현해보겠습니다. verbose 파라미터를 추가했는데요, verbose가 True면 몇 퍼센트 압축됐는지를 출력합니다.

 

💡warning: object, bool, int, float 이외의 데이터 타입이 있으면 이 함수는 달라져야 합니다. 이 함수는 9장에서 제공한 데이터에 적합한 함수입니다.

 

3. 디버깅을 위한 간단한 팁

데이터가 크거나 모델이 복잡할수록 모델링하는 데 시간이 오래 걸립니다. 머신러닝, 딥러닝 모델을 돌리다보면 오랫동안 훈련하고 나서 단순 실수로 코드에 오류가 발생하는 경우가 있습니다. 허무하겠죠. 단순한 실수 하나로 시간을 낭비한 셈이니까요. 이런 상황을 방지하려면 코드 전체가 정상적으로 실행되는지 미리 확인할 필요가 있습니다.

12장 병든 잎사귀 식별에서 다룬 모델은 훈련 시간이 몇 시간이나 됩니다. 그렇기 때문에 빠르게 디버깅을 한 다음, 문제가 없으면 본격적으로 훈련하는 게 좋겠습니다. 디버깅을 위한 간단한 팁을 알려드리겠습니다. 12장 데이터를 기반으로 설명하겠습니다. 다음 코드를 보시죠.

 

import pandas as pd

# 데이터 경로
data_path = '/kaggle/input/plant-pathology-2020-fgvc7/'

DEBUG = True # 디버깅 결정 변수 ❶
#디버깅 여부에 따라 불러올 데이터 개수, 에폭수 지정 ❷
if DEBUG: # 디버깅하는 경우
		nrows=200
		epochs=1
else: # 디버깅하지 않는 경우
		nrows=None
		epochs=39

train = pd.read_csv(data_path + 'train.csv', nrows=nrows)
test = pd.read_csv(data_path + 'test.csv')
submission = pd.read_csv(data_path + 'sample_submission.csv')

 

❶은 디버깅 결정 변수를 설정합니다. True면 디버깅하겠다는 뜻이고, False면 디버깅이 아닌 일반적인 모델링을 하겠다는 뜻입니다. 디버깅을 하려면 True로 설정합니다.

❷는 데이터 개수와 에폭수를 지정합니다. 디버깅하는 경우에는 nrows를 200으로, epochs를 1로 지정합니다. 바로 아래 훈련 데이터를 불러오는 코드가 있습니다. 판다스의 read_csv( ) 메서드는 nrows라는 파라미터를 가지고 있습니다. nrows 파라미터는 csv 파일에서 행 몇 개를 불러올지를 결정합니다. 만약 디버깅 상태라면 csv 파일에서 첫 200개 행만 불러와 DataFrame으로 만들어 train 변수에 할당합니다. train.csv에는 총 1,821개 행이 있습니다. 모든 행을 다불러오면 훈련을 하는 데 시간이 오래 걸리겠죠. 그런데 약 10%인 200개만 불러오면 금방 훈련을 마칠 겁니다. 또한, 디버깅 상태면 에폭수도 1입니다. 에폭이 한 번이니 역시 훈련이 빨리 끝나겠죠.

이렇게 훈련 데이터 크기를 줄이고 에폭수도 줄이면 전체 모델링 코드를 금방 돌릴 수 있습니다. 그러면 오류를 더 쉽게 찾아낼 수 있겠죠. 디버깅 상태에서 전체 코드가 잘 실행되고, 제출 후 성능 점수까지 제대로 나오면( 물론 디버깅 상태로 훈련했으니 점수는 상당히 나쁘겠죠) 문제가 없는 겁니다. 디버깅을 마친 뒤, DEBUG = False로 바꾸고 본격적으로 모델을 훈련하면 됩니다.

 

4. 훈련된 모델 저장하고 불러오기

데이터가 크면 딥러닝 모델 훈련 시간이 오래 걸립니다. 모델을 저장해두지 않으면 그 모델을 다시 사용하고 싶을 때 처음부터 다시 훈련해야 합니다. 번거롭겠죠? 그렇기 때문에 훈련 시간이 오래 걸리는 모델은 저장해둘 필요가 있습니다. 오랜 훈련을 마친 모델을 저장해두면 나중에 사용할 때 다시 훈련할 필요 없이 저장한 모델을 바로 사용하면 됩니다. 훈련된 모델을 저장하고 불러오는 방법을 알아보겠습니다. 여기서는 12장을 기반으로 설명합니다.

 

4.1 모델 저장

모델 훈련을 마쳤다고 가정합시다. 훈련된 모델을 저장한 변수를 model, 옵티마이저를 저장한 변수를 optimizer라고 하겠습니다.

훈련된 모델을 저장하려면 모델의 가중치와 옵티마이저를 저장해야 합니다. 다음은 훈련된 모델의 가중치와 옵티마이저 상태를 저장하는 코드입니다.

 

path = './' # 모델 저장 경로 ❶
# 모델 가중치와 옵티마이저 상태 저장 ❷
torch.save({
		'model': model.state_dict(), # 모델 가중치
		'optimizer': optimizer.state_dict() # 옵티마이저 상태
		}, path + 'EfficientNet-B7.tar')

 

은 모델 저장 경로입니다. 캐글에서는 현재 디렉터리에 바로 저장하면 됩니다.

이어서 는 모델 가중치와 옵티마이저 상태를 저장합니다. torch.save( )의 첫 번째 인수로 딕셔너리를 전달했고, 두 번째 인수로 저장하려는 경로 및 파일명을 전달했습니다. 첫 번째 인수부터 보시죠. 딕셔너리 안에는 두 가지 값이 있습니다. model.state_dict( )는 모델 가중치를 뜻하고, optimizer.state_dict( )는 옵티마이저 상태를 뜻합니다. 이 둘을 딕셔너리로 감쌌죠. 두 번째 인수는 저장 경로입니다. 모델 가중치와 옵티마이저 상태가 저장된 딕셔너리를 EfficientNet-B7.tar로 저장합니다. 참고로 tar은 여러 파일의 묶음을 나타내는 확장자입니다.

모델 훈련을 모두 마치고 위 코드를 실행해야 합니다. 그러니 코드 마지막 부분에서 실행해야겠네요. 코드 실행 후 커밋까지 합니다. 그런 다음 결과 파일이 있는 페이지에서 EfficientNet-B7.tar를 다운받습니다.

💡모델 가중치가 아니라 모델 자체를 그대로 저장해도 됩니다. 하지만 파이토치 공식 문서에서는 가중치를 저장해 사용하기를 권장합니다. 여기서도 가중치를 저장하고 불러오는 방법을 설명했습니다. 더 자세한 내용이 궁금한 분은 다음 주소의 튜토리얼을 참고해주세요.

 

▼ EfficientNet-B7.tar 다운로드

 

4.2 모델 불러오기

우리는 앞서 모델과 옵티마이저를 저장했습니다. 다운로드까지 받아놨죠. 저장한 모델 가중치와 옵티마이저 상태를 불러오려면 우선 모델과 옵티마이저를 정의해야 합니다. 먼저 뼈대를 만든 뒤 가중치 혹은 파라미터를 덮어 써야 하기 때문입니다. 새로운 노트북에서 모델과 옵티마이저를 정의해봅시다. 12장에서 다룬 코드를 상기해보죠. 먼저 사전 훈련 모델을 설치합니다.

 

!pip install efficientnet-pytorch==0.7.1

 

이어서 사전 훈련 모델을 정의합니다.

 

from efficientnet_pytorch import EfficientNet # EfficientNet 모델

# 사전 훈련된 efficientnet-b7 모델 불러오기
model = EfficientNet.from_pretrained('efficientnet-b7', num_classes=4)

 

다음과 같이 옵티마이저도 정의합니다.

 

import torch

optimizer = torch.optim.AdamW(model.parameters(), lr=0.00006, weight_decay=0.0001)

 

이상으로 모델과 옵티마이저를 정의했습니다. 이 모델과 옵티마이저의 가중치와 상태를 앞서 저장한 것으로 갱신해보겠습니다.

먼저 EfficientNet-B7.tar를 노트북 환경에 추가해줘야 합니다. 오른쪽 데이터 탭에서 ‘+ Add data’를 클릭합니다.

 

▼ 데이터 추가 절차 I

 

팝업창이 뜨면 ‘Upload a Dataset’을 클릭합니다.

 

▼ 데이터 추가 절차 II

 

이어서 다운받은 EfficientNet-B7.tar을 업로드합니다.

 

▼ 데이터 추가 절차 III

 

진행바가 다 채워지면 EfficientNet-B7.tar를 잘 업로드한 겁니다. 불러온 EfficientNet-B7.tar를 저장할 디렉터리명을 입력( )하고, 하단 [Create] 버튼( )을 클릭합니다.

 

▼ 데이터 추가 절차 IV

 

그러면 다음 그림과 같이 EfficientNet-B7.tar가 포함된 디렉터리가 추가된 것을 볼 수 있습니다.

 

 

▼ 데이터 추가 절차 V

 

왼쪽 화살표를 클릭해보시죠.

 

▼ 데이터 추가 절차 VI

 

 

pretrained-efficientnetb7 디렉터리 아래 EfficientNet-B7.tar 파일이 잘 추가됐네요.

EfficientNet-B7.tar를 추가했으니 불러오겠습니다.

 

 

 

pretrained_model_path = '/kaggle/input/pretrained-efficientnetb7/'

checkpoint = torch.load(pretrained_model_path + 'EfficientNet-B7.tar')

model.load_state_dict(checkpoint['model'])         # 모델 가중치 불러오기
optimizer.load_state_dict(checkpoint['optimizer']) # 옵티마이저 상태 불러오기

 

torch.load( )로 EfficientNet-B7.tar를 불러옵니다. 이를 checkpoint라는 변수에 할당합니다. EfficientNet-B7.tar를 저장할 때 딕셔너리 형태로 저장했죠? 그러니 파이썬 딕셔너리의 키를 호출해 값을 불러오는 것처럼 모델 가중치와 옵티마이저 상태를 불러오면 됩니다. 불러올 땐 load_state_dict( )를 사용합니다.

 

💡warning: 불러온 모델과 옵티마이저를 다시 훈련하려면 model.train( )을 호출하고, 예측에 사용하려면 model. eval( )를 호출해야 합니다. 잊지맙시다!


신백균
KAIST 산업및시스템공학과 졸업 후 한국생산성본부에서 직무교육 기획 및 운영을 담당하는 전문위원입니다. 세계 랭킹 0.18%의 캐글 노트북 엑스퍼트(Expert)이며, 월 평균 6만여 명이 방문하는 데이터 분석/머신러닝 관련 기술 블로그를 운영하고 있습니다. 참여자 1,200명 이상인 머신러닝 관련 오픈 채팅방의 운영진이기도 합니다.

Leave a Reply

©2020 GoldenRabbit. All rights reserved.
상호명 : 골든래빗 주식회사
(04051) 서울특별시 마포구 양화로 186, 5층 512호, 514호 (동교동, LC타워)
TEL : 0505-398-0505 / FAX : 0505-537-0505
대표이사 : 최현우
사업자등록번호 : 475-87-01581
통신판매업신고 : 2023-서울마포-2391호
master@goldenrabbit.co.kr
개인정보처리방침
배송/반품/환불/교환 안내