빅데이터 AI 학습: 효율적인 대용량 데이터 로드와 처리 방법

Posted by

현대의 인공지능(AI) 모델은 정확한 예측과 성능을 위해 대용량의 빅 데이터로 학습하는 것이 중요하다. 그러나 대용량 데이터를 불러오고 전처리하는 데는 상당한 시간이 소요될 수 있으며, 이는 매우 답답한 경험이 될 수 있다.

전 직장에서 머신러닝 모델을 훈련하기 위해 수천만 건의 데이터를 불러오고 전처리하는 데 하루가 꼬박 걸리는 문제를 겪었다.
회사에서는 모든 설비의 데이터를 클라우드 시스템에 자동으로 업로드하여 방화벽 문제만 해결된다면 어디서든 데이터에 접근할 수 있을 만큼 디지털 트랜스포메이션이 잘 구현된 회사였지만, 큰 문제점이 있었다.
데이터를 클라우드 시스템에서 csv 포맷으로만 내려받을 수 있다는 점이었다. 협업하는 모든 부서가 CSV 포맷의 데이터를 직접 다루고 있었다.

CSV 포맷은 파일을 열어 확인하기도 쉽고 처리 및 시각화가 편리한 점이 많다. 그러나 빅 데이터를 처리해야 하는 머신러닝 작업에는 적합하지 않은 포맷이다. 이러한 문제를 해결하기 위해서는 다양한 데이터 포맷을 활용하여 효율적으로 데이터를 처리하는 방법을 이해하는 것이 중요하다.

이번 포스트에서는 다양한 데이터 포맷을 설명하고, 빅 데이터를 로드하는 시간을 비교하는 Python 코드를 포함하여 설명하고자 한다.

빅데이터 처리를 위한 다양한 데이터 포맷

빅데이터를 처리하기 위해 다양한 데이터 포맷을 사용하고 있다. 하지만, 많은 사람들이 알고 있고 사용하고 있는 CSV 포맷은 전혀 빅데이터향으로 사용하기에는 무리가 있다. CSV 포맷 외에도 어떤 포맷들이 있으며 어떤 장단점이 있는지 알아보자.

CSV(Comma-Separated Values)

  • 가장 일반적인 텍스트 기반 데이터 포맷
  • 사람이 읽기 쉽고, 대부분의 분석 도구와 호환 가능
  • 빅 데이터에서는 읽고 쓰는 속도가 상대적으로 느림

Parquet

  • 컬럼형 저장 포맷으로, 저장 공간을 절약하고 읽기 속도가 빠름
  • Apache Arrow 기반의 인메모리 분석을 지원
  • 빅 데이터 처리에 적합

HDF5(Hierarchical Data Format)

  • 빅 데이터 저장을 위한 바이너리 포맷
  • 대규모 사이언스 데이터에 자주 사용
  • 데이터 계층 구조를 지원하여 효율적인 데이터 접근 가능

JSON(JavaScript Object Notation)

  • 데이터 교환을 위한 경량 포맷
  • 사람이 읽기 쉽고, 대부분의 프로그래밍 언어에서 파싱 가능
  • 중첩된 데이터 구조를 표현할 때 유용하지만, 빅 데이터 처리에는 비효율적

Python 데이터 로드 평가 코드

평가에서는 아래와 같이 3가지 종류의 랜덤 데이터를 포함한 1000만, 5000만, 1억개의 레코드를 각각 CSV, Parquet, HDF5, JSON의 포맷으로 저장한 뒤, 저장된 용량과 로드하는 시간을 측정하여 평가 하였다.

column1column2column3
150.032499886126697186C
540.3494937008068031C
230.531620365A
840.5575162159224915C
480.003971126B
840.20341102566009028C
220.8039966596242509B
320.20436228384007749C
90.062749613D
데이터 샘플
import pandas as pd
import numpy as np
import time
import os
import matplotlib.pyplot as plt

# 데이터 생성
def generate_data(num_rows):
    data = {
        'column1': np.random.randint(0, 100, num_rows),
        'column2': np.random.random(num_rows),
        'column3': np.random.choice(['A', 'B', 'C', 'D'], num_rows)
    }
    return pd.DataFrame(data)

# 데이터 저장
def save_data_formats(df, base_filename):
    df.to_csv(f"{base_filename}.csv", index=False)
    df.to_parquet(f"{base_filename}.parquet", index=False)
    df.to_hdf(f"{base_filename}.h5", key='df', mode='w', format='table')
    df.to_json(f"{base_filename}.json", orient='records', lines=True)

# 데이터 로드 및 전처리 시간 측정
def measure_load_time_and_size(base_filename):
    times = {}
    sizes = {}
    
    # CSV
    start_time = time.time()
    df_csv = pd.read_csv(f"{base_filename}.csv")
    times['CSV'] = time.time() - start_time
    sizes['CSV'] = os.path.getsize(f"{base_filename}.csv")
    
    # Parquet
    start_time = time.time()
    df_parquet = pd.read_parquet(f"{base_filename}.parquet")
    times['Parquet'] = time.time() - start_time
    sizes['Parquet'] = os.path.getsize(f"{base_filename}.parquet")
    
    # HDF5
    start_time = time.time()
    df_hdf5 = pd.read_hdf(f"{base_filename}.h5")
    times['HDF5'] = time.time() - start_time
    sizes['HDF5'] = os.path.getsize(f"{base_filename}.h5")
    
    # JSON
    start_time = time.time()
    df_json = pd.read_json(f"{base_filename}.json", orient='records', lines=True)
    times['JSON'] = time.time() - start_time
    sizes['JSON'] = os.path.getsize(f"{base_filename}.json")
    
    return times, sizes

# 데이터 크기 설정
data_sizes = [10_000_000, 50_000_000, 100_000_000]

# 결과 저장
results = {}
size_results = {}

for size in data_sizes:
    print(f"Processing data size: {size}")
    df = generate_data(size)
    base_filename = f"data_{size}"
    save_data_formats(df, base_filename)
    times, sizes = measure_load_time_and_size(base_filename)
    os.remove(f"{base_filename}.csv")
    os.remove(f"{base_filename}.parquet")
    os.remove(f"{base_filename}.h5")
    os.remove(f"{base_filename}.json")
    
    results[size] = times
    size_results[size] = sizes

# 결과 출력
for size, times in results.items():
    print(f"\nData size: {size}")
    for format_name, time_taken in times.items():
        print(f"{format_name}: {time_taken:.2f} seconds")

for size, sizes in size_results.items():
    print(f"\nData size: {size}")
    for format_name, size_taken in sizes.items():
        print(f"{format_name}: {size_taken / (1024 * 1024):.2f} MB")

데이터 로드 시간 평가 결과

데이터 포맷별 성능 비교

  • CSV: CSV 포맷은 모든 데이터 크기에서 상대적으로 긴 시간이 걸린다. 이는 CSV가 단순 텍스트 포맷으로, 데이터 구조를 저장하는데 비효율적이기 때문이다.
  • Parquet: 모든 데이터 크기에서 가장 빠른 로드 시간(무려 1억개의 레코드에서는 무려 31배나 빠름을 보였다.) 을 보였다. Parquet는 컬럼 기반 저장 포맷으로, 효율적인 압축 및 스캔 성능을 제공하여 빅 데이터 처리에 적합함을 알 수 있다.
  • HDF5: CSV와 유사한 성능을 보였으며, 대체로 CSV보다 약간 빠른 경향이 있었다. HDF5는 계층적 데이터 포맷으로, 빅 데이터 저장에 강점을 가지지만, 읽기 성능은 상대적으로 떨어질 수 있다.
  • JSON: 모든 데이터 크기에서 가장 긴 로드 시간을 보였다. JSON은 가독성은 높으나, 구조적 복잡성으로 인해 빅 데이터를 다루는데 비효율적인 것을 알 수 있다.

데이터 크기별 성능 변화

  • CSV와 HDF5: 데이터 크기가 증가함에 따라 로드 시간이 선형적으로 증가하는 경향을 보였다. 이는 이러한 포맷들이 구조적 이점을 활용하지 못하고, 단순히 데이터 양에 비례하여 처리 시간이 늘어나기 때문이다.
  • Parquet: 데이터 크기가 증가해도 비교적 낮은 증가율을 보였다. 이는 Parquet의 컬럼 기반 저장 및 효율적인 압축 덕분에 데이터 크기가 커져도 상대적으로 적은 오버헤드를 발생시키기 때문이다.
  • JSON: 데이터 크기가 커질수록 로드 시간이 급격히 증가하는 경향을 보였다. JSON의 비효율적인 구조화 방식이 빅 데이터를 다루기에 적합하지 않음을 나타낸다.

데이터 저장 공간(용량) 평가 결과

  • CSV 포맷은 간단하고 널리 사용되지만, 대용량 데이터를 저장할 때 비효율적이다.
  • Parquet 포맷가장 높은 압축률을 제공하며, 대용량 데이터 저장에 가장 효율적이다.
  • HDF5 포맷은 빅 데이터 저장에 적합하지만, Parquet보다는 덜 효율적이다.
  • JSON 포맷가장 큰 파일 크기를 가지며, 빅 데이터 저장에 가장 비효율적이다.

결론 및 권장 사항

JSON 포맷은 가독성이 높고 범용성이 있지만, 빅 데이터 처리에는 부족함을 알 수 있다.

CSV 포맷은 간단하고 범용적이지만, 성능 측면에서 비효율적이다.

HDF5는 데이터 저장에 강점이 있지만, 데이터 로드에서 성능이 떨어짐으로 빅 데이터 처리에는 부족함이 있다.

따라서, 빅 데이터를 다룰 때에는 Parquet 포맷을 사용하는 것이 가장 효율적이다. 이는 로드 시간뿐만 아니라, 저장 공간에서도 이점을 제공한다.

Leave a Reply

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다