GCP ML Engineer

GCP ML Engineer 시험 노트개념 정리15 MIN

학습 데이터 구성과 파이프라인 적재

학습 데이터를 Cloud Storage와 BigQuery 중 어디에 둘지, 정형·비정형 데이터 적재, TFRecord와 샤딩, 학습·검증·테스트 분할과 데이터 누수, 관리형 데이터셋, 여러 출처를 학습 파이프라인으로 모으는 법을 봅니다.

11편에서 무엇을 어디서 만들지 정했다면, 이제 학습 작업이 읽을 데이터를 놓을 차례입니다. 같은 모델이라도 데이터를 어디에 어떤 모양으로 두느냐에 따라 학습 시간이 몇 배 달라지고, 나누는 방식 하나로 평가 점수가 거짓이 되기도 합니다. 시험은 이 자리에서 저장 위치·파일 형식·분할 방식을 고르는 문항과, 「평가 점수가 지나치게 높다」는 증상에서 누수를 찾는 문항을 냅니다.

저장 위치

Cloud Storage

Cloud Storage는 파일을 객체로 담는 저장소입니다. 이미지·음성·영상·문서 같은 비정형 데이터와, 학습 코드가 파일 단위로 읽는 CSV·TFRecord·Parquet가 여기 놓입니다. 학습 작업 안에서는 버킷이 파일 시스템 경로로 붙어 /gcs/버킷/경로처럼 일반 파일을 열듯 읽을 수 있어, 프레임워크 코드를 고치지 않고 그대로 씁니다.

BigQuery

BigQuery는 표 형태의 정형 데이터를 SQL로 다루는 데이터 웨어하우스입니다. 원천 데이터가 이미 여기 있으면 학습용 표도 SQL로 만들어 여기 두는 것이 자연스럽습니다. BigQuery ML과 AutoML 정형 모델은 표를 바로 읽고, 커스텀 학습 코드도 BigQuery Storage Read API로 표를 빠르게 끌어옵니다.

데이터 둘 곳 이유
거래·고객 같은 표 BigQuery SQL로 가공하고 그 자리에서 학습
이미지·음성·영상 파일 Cloud Storage 파일 단위로 읽는 데이터
이미지 + 메타데이터 표 파일은 Cloud Storage, 표는 BigQuery 표에 파일 경로 열을 두어 잇는다
커스텀 학습용 대량 레코드 Cloud Storage의 TFRecord·Parquet 여러 작업자가 병렬로 읽기

셋째 줄이 자주 나옵니다. 이미지를 BigQuery 셀에 바이트로 넣는 보기는 가능은 해도 비싸고 느립니다. 파일은 버킷에 두고 표는 그 경로를 가리키게 합니다.

정형과 비정형 적재

정형 데이터

정형 데이터는 행과 열이 정해진 데이터입니다. 운영 데이터베이스에서 BigQuery로 옮길 때는 한 번에 통째로 옮기는 일괄 적재와, 바뀐 행만 계속 흘려보내는 변경 데이터 캡처가 있습니다. 학습 데이터는 대개 하루 한 번 스냅샷이면 충분하므로 일괄 적재가 기본이고, 실시간 피처가 필요할 때만 스트리밍을 붙입니다.

비정형 데이터

비정형 데이터는 정해진 열이 없는 파일입니다. 이것을 학습에 쓰려면 파일마다 라벨을 붙여 줄 목록이 있어야 하고, 그 목록이 가져오기 파일입니다. 한 줄에 파일 경로와 라벨을 적은 CSV나 JSON Lines 파일이고, 관리형 데이터셋이 이것을 읽어 파일과 라벨을 잇습니다.

{"imageGcsUri": "gs://my-bucket/parts/0001.jpg", "classificationAnnotation": {"displayName": "defect"}}
{"imageGcsUri": "gs://my-bucket/parts/0002.jpg", "classificationAnnotation": {"displayName": "ok"}}

TFRecord와 샤딩

TFRecord

TFRecord는 레코드를 이진 형식으로 이어 붙인 TensorFlow의 파일 형식입니다. 작은 이미지 파일 백만 개를 하나씩 열면 파일을 여는 비용이 읽는 비용보다 커지는데, 레코드를 큰 파일 몇 개에 몰아 담으면 순차로 빠르게 읽힙니다. 학습이 데이터를 기다리느라 GPU가 노는 증상이 보이면 파일 형식부터 의심합니다.

import tensorflow as tf

def write_shards(examples, prefix, num_shards):
    writers = [tf.io.TFRecordWriter(f"{prefix}-{i:05d}-of-{num_shards:05d}.tfrecord")
               for i in range(num_shards)]
    for i, ex in enumerate(examples):
        writers[i % num_shards].write(ex.SerializeToString())
    for w in writers:
        w.close()

샤딩

한 파일에 다 담으면 이번에는 여러 작업자가 같은 파일을 나눠 읽기 어렵습니다. 그래서 데이터를 같은 크기의 여러 파일로 나누는데, 이것을 샤딩이라 하고 나뉜 파일 하나가 샤드입니다. 샤드 하나를 대략 100~200MB로 맞추는 것이 흔한 기준입니다. 60GB 데이터를 150MB 샤드로 나누면 60000150=400\frac{60000}{150}=400 개가 됩니다. 샤드 수는 작업자 수보다 넉넉히 많아야 모두가 쉬지 않고 읽고, 읽는 쪽에서는 tf.data.Dataset.list_files로 샤드 순서를 섞어 각 에폭이 같은 순서로 돌지 않게 합니다.

데이터 분할

세 덩어리

학습 데이터는 셋으로 나눕니다. 학습 세트로 가중치를 맞추고, 검증 세트로 하이퍼파라미터와 조기 종료 시점을 고르고, 테스트 세트는 마지막에 한 번만 열어 운영 성능을 추정합니다. 검증 세트로 설정을 여러 번 고르면 그 세트에 맞춰진 셈이라, 손대지 않은 테스트 세트가 따로 있어야 점수를 믿을 수 있습니다. 흔한 비율은 80·10·10입니다.

데이터 누수

데이터 누수는 예측 시점에는 알 수 없는 정보가 학습 데이터에 섞여 들어가는 것입니다. 증상은 언제나 같습니다 — 평가 점수가 지나치게 좋은데 운영에서는 무너집니다. 자주 나오는 원인이 셋입니다.

  • 시간 누수. 미래 행이 학습에, 과거 행이 테스트에 들어간 경우. 시계열과 이탈 예측은 무작위가 아니라 시간으로 자릅니다.
  • 개체 누수. 같은 고객의 행이 학습과 테스트에 갈라 들어간 경우. 고객 id로 묶어 한쪽에만 보냅니다.
  • 타깃 누수. 「해지 처리 일자」처럼 결과가 난 뒤에 생기는 열이 피처에 남은 경우. 그 열을 지웁니다.

개체로 묶는 분할은 SQL 한 줄로 됩니다. id의 해시값으로 나누면 같은 고객은 언제나 같은 쪽에 가고, 다시 돌려도 결과가 같습니다.

SELECT *,
  CASE WHEN MOD(ABS(FARM_FINGERPRINT(CAST(customer_id AS STRING))), 10) < 8 THEN 'TRAIN'
       WHEN MOD(ABS(FARM_FINGERPRINT(CAST(customer_id AS STRING))), 10) = 8 THEN 'VALIDATE'
       ELSE 'TEST' END AS split
FROM `proj.churn.features`;

RAND()로 나누면 돌릴 때마다 분할이 바뀌어 실험끼리 비교가 안 됩니다.

관리형 데이터셋

데이터셋 리소스

관리형 데이터셋은 플랫폼이 데이터의 위치·스키마·라벨·분할을 하나의 리소스로 들고 있는 것입니다. AutoML은 이것을 입력으로 받고, 커스텀 학습도 이것을 넘기면 분할된 경로를 환경 변수로 받아 씁니다. 같은 데이터셋을 여러 학습이 가리키므로 팀이 「어느 데이터로 학습했나」를 이름 하나로 말하게 되고, ML Metadata 계보에도 이 리소스가 남습니다.

from google.cloud import aiplatform

ds = aiplatform.TabularDataset.create(display_name="churn-v3", bq_source="bq://proj.churn.features")
job = aiplatform.AutoMLTabularTrainingJob(display_name="churn-automl", optimization_prediction_type="classification")
model = job.run(dataset=ds, target_column="churned", predefined_split_column_name="split")

분할 지정

분할은 넷 중 하나로 줍니다. 비율만 주면 플랫폼이 무작위로 나누고, 앞 절의 split 열처럼 미리 정한 열을 주면 그대로 따르며, 시각 열을 주면 시간 순서대로 앞쪽을 학습에 씁니다. 필터 식으로 나누는 방법도 있습니다. 시간 누수나 개체 누수가 걸린 시나리오라면 무작위 비율이 아니라 미리 정한 열이나 시각 열을 고르는 것이 답입니다.

여러 출처 모으기

파이프라인 입력

실제 학습 데이터는 한 곳에서 오지 않습니다. 주문은 운영 데이터베이스, 클릭은 스트리밍 로그, 상품 이미지는 버킷에 있습니다. 이것을 학습 직전에 손으로 합치면 다음 재학습 때 같은 결과를 못 만들므로, 모으는 단계도 파이프라인의 한 구성요소로 둡니다. 정형끼리는 BigQuery에서 SQL로 조인하고, 대량 변환이나 스트리밍 원천은 06편의 Dataflow가 맡아 BigQuery나 Cloud Storage에 내려놓습니다. 다음 구성요소가 그 결과로 관리형 데이터셋을 만들고 학습을 부릅니다.

시점 맞추기

출처마다 갱신 주기가 다르면 같은 행 안에서 시점이 어긋납니다. 3월 1일 주문에 3월 5일에야 집계된 클릭 수를 붙이면 그것이 곧 시간 누수입니다. 각 출처의 값을 「예측 시점 이전에 알 수 있었던 값」으로만 붙이는 시점 기준 조인이 필요하고, 이 일을 대신해 주는 것이 Feature Store의 과거 시점 조회입니다. 출처가 셋 넘게 얽힌 시나리오에서 「학습 데이터를 매번 같은 방식으로 다시 만든다」가 조건이면, 답은 손 작업이 아니라 파이프라인입니다.

연습 문제

  1. 제품 사진 50만 장과 각 사진의 라벨·촬영 공장이 담긴 표가 있습니다. 알맞은 배치는?
    ① 사진을 BigQuery에 바이트 열로 넣는다
    ② 사진은 Cloud Storage에, 표는 BigQuery에 두고 표에 사진 경로 열을 둔다
    ③ 사진과 표를 모두 노트북 디스크에 둔다
    ④ 표를 사진 파일 이름에 인코딩한다
    ②. 파일은 버킷에, 정형 메타데이터는 웨어하우스에 두고 경로로 잇습니다.
  2. 24GB 학습 데이터를 200MB 샤드로 나누면 샤드는 몇 개입니까?
    24000200=120\frac{24000}{200}=120 개입니다. 작업자가 8개라면 한 작업자가 15개씩 읽습니다.
  3. GPU 사용률이 30%를 넘지 못합니다. 학습 데이터는 작은 JPEG 파일 수백만 개로 버킷에 흩어져 있습니다. 먼저 할 조치는?
    ① GPU를 더 붙인다
    ② 레코드를 TFRecord 샤드로 묶어 순차로 읽게 한다
    ③ 학습률을 올린다
    ④ 테스트 세트를 줄인다
    ②. 파일 여는 비용 때문에 GPU가 데이터를 기다리고 있습니다. ①은 기다리는 GPU만 늘립니다.
  4. 고객 이탈 모델의 테스트 AUC가 0.99인데 운영에서는 0.70입니다. 피처 목록에 「해지 신청 상담 횟수」가 있습니다. 가장 유력한 원인은?
    ① 과소적합
    ② 타깃 누수
    ③ 샤드 수 부족
    ④ 검증 세트가 너무 큼
    ②. 해지 신청 상담은 해지를 결심한 뒤에 생기는 값이라 예측 시점에는 알 수 없습니다.
  5. 같은 고객의 거래가 여러 행입니다. 학습·테스트를 나눌 때 알맞은 방법은?
    ① RAND()로 행마다 무작위 분할
    ② 고객 id의 해시값으로 분할
    ③ 금액 순으로 정렬해 앞 80%를 학습에
    ④ 분할하지 않고 전체로 평가
    ②. 같은 고객은 한쪽에만 가고 다시 돌려도 분할이 같습니다. ①은 같은 고객이 양쪽에 갈라지고 실행마다 바뀝니다.
  6. 하루 단위 수요 예측 모델을 AutoML로 학습합니다. 관리형 데이터셋에 줄 분할 방식으로 알맞은 것은?
    ① 비율만 주는 무작위 분할
    ② 시각 열을 주는 시간순 분할
    ③ 라벨 값으로 정렬
    ④ 분할 없이 학습
    ②. 미래 행이 학습에 섞이지 않게 시간 순서로 앞쪽을 학습에 씁니다.
GCP ML Engineer 시험 노트 전체 보기