데이터 분석가로 일하다보면 다양한 데이터를 활용하게 되는데, 이 때 마주치는 문제 중 하나는 큰 사이즈의 데이터를 처리하는 것이다. 이 문제는 사실 너무 당연하게 매번 마주하기 때문에 큰 문제가 아닌듯 하면서도, 항상 해결해야 할 숙제같은 것으로 볼 수 있다.
일반적으로 대용량 데이터를 활용하는 경우는 그만큼 트래픽 또는 유저 수가 많은 플랫폼에서의 ETL 파이프라인을 통한 DW(Data Warehouse) 또는 DM(Data Mart)까지 구축되어있는 환경에서 어렵지 않게 원하는 데이터를 추출할 수 있다.
인프라가 갖춰지지 않은 경우의 고민(소규모 기업, 데이터 팀 부재, 인턴, 취준생)
하지만 이런 인프라와 데이터 파이프라인이 잡혀있지 않거나 데이터 엔지니어가 없는 소규모의 기업에서 갑자기 큰 데이터를 활용해야 할 때에 어려움을 겪을 수 있다. 특히, 데이터 분석을 대행해주는 SI 회사에서 갑자기 큰 데이터를 전달 받거나, 인턴 또는 취업준비생 입장에서 커다란 데이터로 프로젝트를 진행할 경우에도 마찬가지이다.
사이즈가 큰 데이터를 통한 분석
이런 경우에도 어떻게든 커다란 데이터를 활용해서 결과를 도출해야만 하는 경우가 발생하는데, 이를 해결하기 위한 방법들을 아래와 같이 확인해본다.
0. 데이터 테이블 확인하기
데이터를 활용하기 전에 데이터가 어떻게 이뤄져있는지 난 샘플 1개라도 확인을 해 볼 필요가 있다.
대개 데이터가 너무 큰 경우, 데이터를 EDA 하는 자체도 쉽지 않기 때문에 테이블과 구조를 확인하여, 내가 필요한 Feature가 어떤 것들이 있을지, 해당 feature들로 세울 수 있는 가설들은 어떤 것들이 있는지 생각해볼 수 있다.
1. 데이터를 국소적으로 쪼개어 보기
데이터가 너무 클 경우엔 찾고자 하는 부분에 맞추어 국소적으로 관찰하는 방법이 있다.
특정 문제에 대한 가설을 바탕으로, 필요한 Feature들이 선정 되었다면 해당 Feature들 기준으로 추출하여 각각의 결과에 따른 인사이트를 얻는 방법이다.
예를 들어 의류 리테일 플랫폼 데이터를 가정할 때, 카테고리 별 매출에 대한 파악이 필요하다면, 반품과 중복 오류를 제외했을 때 '카테고리'와 '매출' feature가 주요 요소로 활용될 것이다.
아래에서 캐글 UCI 데이터셋을 활용해 예시를 들어본다.
from google.colab import drive
import pandas as pd
drive.mount('/content/drive/')
df = pd.read_csv('/content/drive/MyDrive/online_retail.csv')
print(df.info(memory_usage='deep'))
df.head()
위 결과로 보아, 전체 컬럼을 뽑았을 때의 메모리 사용량은 194.3MB임을 알 수 있다.
그러나 위와 같은 차트를 불러오는 것도 어려운 경우가 존재할 수 있다. 이 경우 필요한 columns만 불러와서 처리할 수 있다.
해당 데이터셋에서는 국가 별 평균 UnitPrice를 계산한다고 가정하고 두 개 columns(Country, UnitPrice)만 남긴다(본 구현에선 Quantity는 무시한다)
df1 = pd.read_csv('/content/drive/MyDrive/online_retail.csv', usecols = ['UnitPrice','Country'])
print(df1.info(memory_usage='deep'))
df1.head()
이렇게 필요한 column만 가져온 뒤, 필요한 연산을 하면 조금 더 수월해진다. 위 캡쳐와 같이 메모리 사용량이 40.5MB까지 줄어든 것을 볼 수 있다.
코드로 활용했던 데이터셋은 크기가 크지 않아 큰 차이를 못느끼겠지만, 더 많은 row와 columns를 갖는 데이터일 수록 유용하다.
python 코드로 입력했지만, DB를 통해 SQL로 추출을 할 때에도 필요 column만 조합해 조회하는 것이 좋다.
이와 같은 이치로, 필요한 column들의 조합과 결과로 테이블을 계속해서 나누어 가져가면 좀 더 수월하게 될 것이다.
2. 메모리의 최적화
Pycon 2019에서 오성우님의 발표를 듣고 많이 참고를 했다. 오성우님 파이콘2019 발표 링크
위에서 데이터를 나누고, 쪼개어 보는 방법으로 경량화를 했다면, 메모리의 최적화를 해볼 수도 있겠다.
데이터 파일을 효율적으로 활용하도록 메모리를 줄이는 방법으로는 아래와 같이 볼 수 있다.
1) 데이터 코드화: object -> int
string의 조합으로써 object로 나눠진 명목형 데이터들을 수치화하여 바꿔줌으로써 데이터가 갖는 용량 자체를 줄여준다.
from sklearn.preprocessing import LabelEncoder
import copy
df2 = df1.copy()
label = LabelEncoder()
df2['country'] = label.fit_transform(df['Country'])
df2 = df2.drop(columns = 'Country')
print(df2.info(memory_usage='deep'))
df2.head()
sklearn의 LabelEncoder를 활용하여 Country column을 모두 수치로 변경했다.
위에서 Country의 Dtype이 'object'였던 반면, 이번에는 Dtype이 'int64'로 변함과 함께 용량이 8.3MB까지 줄어든 것을 알 수 있다.
2) 데이터 형식 변환: object -> category, int64 -> int32
가능한 적은 byte로 줄이는 방법이다. object를 완전 수치화 할 수 없는 경우는 category로 바꿔주면 효율적이며, 데이터의 손실이 없는 상태에서 데이터 타입을 줄이는 방식이다. 이번 코드에선 수치형 데이터 타입을 적은 형식으로 줄여본다.
df3 = df2.copy()
df3['country'] = df3['country'].astype('int32')
df3['UnitPrice'] = df3['UnitPrice'].astype('float32')
print(df3.info(memory_usage='deep'))
df3.head()
위와 같이 int64를 int32로, float64를 float32로 줄여준 결과, 메모리 사용량이 절반에 가까운 4.1 MB가 되었다.
이처럼 데이터 형식을 조금씩 효율적으로 바꿔줌으로써 메모리 측면에서 이점이 발생할 수 있다.
3)파일 포맷의 변환: parquet, hdf5 등
데이터가 클 경우, 가장 드라마틱하게 로딩 및 연산 시간을 단축시켜주는 방식이다.
parquet(파케이)는 columna라는 열 기반 파일 포맷으로 하둡에서 사용하는 저장방식인데, 이러한 파일 변환을 통해 메모리 및 시간 효율을 가져올 수 있다. 열 기반 데이터 포맷에 대해서는 이번 포스팅에서 디테일하게 다루진 않겠다.
parquet의 드라마틱한 효과를 보기 위해 이번에는 정말 5GB짜리 4200만개의 데이터셋을 활용하며, 로딩타임도 체크해본다.
실제 빅데이터 축에 속하지도 않지만, 귀여운 코랩 pro는 이정도도 사실 버티기 힘들다.
%%time
# 데이터 불러오기
import pandas as pd
df4 = pd.read_csv('/content/drive/MyDrive/2019-Oct.csv',)
print(df4.info(memory_usage='deep'))
print(df4.shape)
df4.head()
데이터셋은 중요한 것이 아니므로 따로 캡쳐를 하지 않도록 한다.
기본 데이터를 불러보았을 때, 위와 같이 메모리 사용량은 15.4GB까지 나오며, wall time만 2분 40초가 걸린다.
이러한 데이터를 분석하는 것은 단연 시간 문제 뿐 아니라, 램이 터져 런타임 에러(노트북 환경에서)가 발생할 수 있다.
이것을 parquet로 변환시킨 뒤, 다시 불러보아 어떤 결과가 나오는지 보도록 한다.
%%time
# 파케이 파일로 변환 - gzip으로 저장
df4.to_parquet('oct1.parquet', compression = 'gzip')
[output]
CPU times: user 3min 42s, sys: 6.74 s, total: 3min 48s
Wall time: 3min 49s
%%time
df4_parquet = pd.read_parquet('/content/oct1.parquet')
print(df4_parquet.info(memory_usage='deep'))
print(df4_parquet.shape)
df4_parquet.head()
파일 포맷의 변경일 뿐이라 memory usage 측면에서 큰 차이는 없지만, 효율적인 포맷이기에 조회 자체에서 wall time 41.8s로 매우 드라마틱하게 단축된 것을 볼 수 있다. 이제 이러한 파일 포맷에서 데이터 경량화를 다양하게 진행해보아도 더 효율적으로 처리 가능하다.
3. 데이터 샘플링(표본 추출)
빅데이터 분야에서 기술의 발달에 따라 많이 사용되진 않지만, 주어진 환경에서 데이터를 다 줄여보더라도 안되는 경우는 샘플링을 해볼 수 있다.
실제 추천을 진행할 때에도 샘플링을 우선 진행하거나, 클러스터링을 통해 대표 샘플을 추출해서 활용하기도 한다.
단, 샘플링 시에는 진행하는 Task에 따라 필요 feature의 요소들이 제대로 들어가있는지 확인해보고, 요소의 비중을 잘 조절해서 진행해야 한다.
4. 빅데이터 처리 도구 활용 BigQuery & Spark
apache, hadoop, GCP 등 다양한 빅데이터 처리 플랫폼들이 서비스를 제공하고 있으며, 이러한 플랫폼의 서비스를 활용해서 좀 더 빠르고 간편하게 데이터 분석을 진행해볼 수도 있습니다. 직접 인프라를 갖춰야 하는 경우, 해당 서비스의 활용을 직접 학습해서 활용해볼 수도 있다. 가장 특징적인 몇가지만 소개 해보겠다.
1)GCP Bigquery
디테일한 설명은 사실 다른 블로그나 페이지에서도 많이 소개하고 있어서 넘어가고, 실제 사용해본 경험에 따라 이야기하면 가장 실용적인 플랫폼이다. 서버리스이기 때문에 account 생성만 하면 바로 쓸 수 있고, 속도도 매우 빠르기 때문에 실제 데이터 전처리 할때에도 많이 사용들 한다.
이 밖에 IAM 권한설정을 통해 팀단위 작업도 가능하고, 각종 API를 지원하기 때문에 호환성 역시도 좋아 매우 유용하게 사용했다.
사용방법이 아래와 같이 매우 쉽고 단순하여 한번 쯤 사용해보길 권장한다.
* GCP 가입
* 버킷에 데이터 적재
* Bigquery 콘솔에서 쿼리문 작성 (MySQL과 유사)
단, 무료크레딧은 90일이며, 이후 유료계정으로 설정하는데 이 땐 '슬롯'의 개념과 쿼리 효율에 따라 과금이 된다.
빅쿼리의 원리와 슬롯 개념들에 대해선 여기서 다루지 않지만, 쿼리를 효율적으로 짜는 것은 항상 중요하다고 생각하면 된다. -> 비효율적응로 슬롯을 낭비하면서 비용이 더 많이 나올 수 있기 때문이다.
2) Apache Spark
이 역시 결론부터 이야기 하면, 매우 실용적이고 없어선 안되는 도구이지만 인프라가 없는 상황에서 굳이 억지로 사용해봤자 큰 도움은 안된다고 생각한다.
Spark에 대한 실무적인 이야기로 보자면, 데이터를 다루는 회사에서는 꼭 활용해야 하는 도움이 되는 녀석이지만, 인프라가 갖춰지지 않은 상태에선 효과를 보기 힘들 수 있다.
스파크 활용이 어려운 점
스파크 분산처리를 통한 속도 향상은 클러스터라는 열심히 일할 컴퓨터들을 여럿 두고, 업무를 배분해 병렬적으로 처리하는 원리라고 간단히 이야기 할 수 있다. 다만 인프라가 없는 상황에서 AWS EC2와 같은 클라우드 상에 클러스터 여러개를 두고 진행하면 비용적 측면에서 부담이 될 수 있다. 그렇다고 무료 크레딧으로 클러스터 하나를 사용한다면 큰 효과를 보기 어렵기도 하다. 무엇보다 머신러닝과 학습과 같이 '반복적인 접근 및 처리'에 포커싱이 되어있어 전처리를 통한 결과 도출 등에 대한 연산에서도 크게 효과를 보긴 어렵다.
정리
이상 인프라가 없는 상태에서 빅데이터의 분석을 할 때에 도움이 될 수 있을 정보들을 정리해보았다.
실제 실무에서 많이 활용해봤던 것들이고, 그에 대한 개인적 의견을 적어보았다. 이후엔 디테일한 도구들의 작동 원리들도 다뤄보고자 한다.
댓글