💬 왜 "데이터 적재와 전처리"를 따로 배우는 걸까?
딥러닝 모델은 데이터를 먹고 자랍니다.
데이터 없으면 아무것도 못 합니다.
문제:
초창기에는 사람들이 작은 데이터셋으로만 실험했음.
(예: MNIST 손글씨 6만장, CIFAR-10 이미지 5만장)
👉 데이터를 메모리에 "한 번에" 다 올려놓고 학습하면 됐었습니다.
하지만 현실에서는?
- 데이터가 너무 큼. (1억 장 사진, 10억 개 로그 기록)
- 메모리에 한 번에 못 담음. (RAM 16GB, 데이터 1TB...)
해결해야 할 문제:
- 메모리에 다 올릴 수 없는데 어떻게 모델에 먹일까?
- 디스크에서 읽을 때 속도가 느리면 학습이 멈추는데 어떻게 할까?
- 파일이 여러 개면 어떻게 효율적으로 읽을까?
- 데이터가 편향돼 있으면 학습이 이상해지는데 순서를 어떻게 섞을까?
📌 그래서 등장한 것
👉 TensorFlow tf.data.Dataset API
- 데이터를 조각조각 나눠서 메모리로 가져오고
- 한 줄씩 읽거나, 몇 개씩 묶어서 신경망에 보내고
- 미리 준비해서 신경망이 굶지 않게 하고
- 랜덤하게 섞어서 다양하게 학습하도록 도와주는
데이터 전용 파이프라인.
1️⃣ tf.data.Dataset.from_tensor_slices()
- 작은 데이터를 메모리에 다 넣을 수 있을 때 사용.
- 예를 들어, 이미지 1만 장, 레이블 1만 개 → 작은 텐서에 저장 가능.
왜 만들었을까?
- 딥러닝 라이브러리는 모든 걸 "Dataset" 객체로 통일해서 처리하고 싶음.
- 그래야 shuffle, batch, repeat 같은 작업을 일관되게 할 수 있음.
어떻게 동작해?
- 큰 텐서를 조각조각 나눠서 한 줄씩 꺼낼 수 있게 만듦.
2️⃣ repeat(), batch(), map()
왜 필요할까?
- repeat()
→ 데이터가 한 번만 있으면 훈련 다 못 함. 여러 번 반복해야 함. - batch()
→ 딥러닝은 여러 개 데이터를 한꺼번에 처리할 때 성능이 좋음. (GPU 병렬처리) - map()
→ 원본 데이터를 그대로 쓰면 안 되고, 전처리해야 함. (예: 정규화, augmentation)
3️⃣ shuffle(buffer_size)
왜 섞어야 할까?
- 학습할 때 같은 패턴만 계속 나오면 모델이 편향(bias)됨.
- 예를 들어, 집값 예측 데이터가 지역별로 정렬돼 있다면?
- 싼 동네만 계속 학습하다가
- 갑자기 비싼 동네 나오면 모델이 못 맞추지 못함.
shuffle()이 해결하는 것:
- 무작위로 데이터를 섞어서 모델이 고르게 다양한 데이터를 보게 해줌.
- buffer size로 메모리 크기 조절
4️⃣ 메모리가 부족할 때: 파일 여러 개로 쪼개기 + interleave()
왜 파일을 나눌까?
- 하나의 파일이 너무 크면
- 읽는데 시간이 오래 걸림.
- 디스크 입출력이 bottleneck(병목) 걸림.
그래서
- 데이터를 여러 파일로 쪼갠다.
- 동시에 여러 파일을 읽는다.
- **interleave()**를 쓰면 한 줄씩 번갈아 읽을 수 있다.
interleave() 핵심:
- 파일 A, 파일 B, 파일 C를
- 한 줄 A, 한 줄 B, 한 줄 C 식으로 번갈아 가져옴
- 학습이 훨씬 다양해지고 빨라짐.
5️⃣ prefetch(1)
왜 필요할까?
- GPU는 아주 빠름.
- CPU가 데이터를 준비 못하면 GPU가 놀게됨 (시간 낭비).
prefetch()가 하는 일:
- "현재 배치를 학습하는 동안"
- "다음 배치를 미리 준비해서 기다리게 함"
그래서
→ GPU를 최대한 쉬지 않고 100% 사용 가능.
전체 흐름 정리
- 데이터가 너무 커서 메모리에 다 못 올린다.
- Dataset API로 파일을 조각조각 읽는다.
- repeat를 해서 모델이 잘 학습할 수 있게 csv 파일을 중첩시킨다.
- interleave로 파일마다 한줄씩 읽는다.
- shuffle을 해서 데이터를 섞고 buffer size를 통해 메모리의 크기에 맞춰준다.
- map으로 전처리(정규화, 파싱 등)를 해준다.
- prefetch로 다음 배치를 미리 준비해서 GPU를 계속 굴린다.
- 만들어진 데이터셋을 model.fit() 에 바로 넣어서 훈련한다.
비유하자면 !
- Dataset API = "자동으로 데이터 요리해주는 주방"
- 모델 = "배고픈 손님"
- batch = "한 접시에 여러 음식 담기"
- shuffle = "음식 순서를 무작위로 섞기"
- map = "요리사가 음식에 소스 바르기"
- prefetch = "손님이 먹고 있는 동안 다음 음식을 준비"
주요 동작들
from_tensor_slices() | 텐서를 dataset으로 변환 |
map() | 각 데이터를 전처리하는 함수 적용 |
shuffle() | 데이터 순서 섞기 |
batch() | 여러 데이터를 묶기 |
repeat() | 데이터 반복 |
interleave() | 여러 파일에서 한 줄씩 번갈아 읽기 |
prefetch() | 다음 데이터를 미리 준비하기 |
# shuffle(buffer_size)
✅ 내부 동작:
- 데이터를 1000개까지 메모리 버퍼에 쌓음
- 그 중 랜덤하게 하나 꺼내서 반환
- 새 데이터를 하나 읽어와서 다시 버퍼에 채움
- 반복
이걸 통해 모델이 데이터 순서에 과적합하지 않게 도와줌.
✅ seed를 주면?
- 매번 같은 셔플 순서를 보장 → 디버깅할 때 유용
# interleave()
lambda filepath: tf.data.TextLineDataset(filepath).skip(1),
cycle_length=5
)
✅ 내부 동작:
- filepath_dataset에 여러 파일 경로가 있을 때
- 동시에 5개를 열고, 각 파일에서 한 줄씩 번갈아 읽음
✅ 중요한 옵션:
- cycle_length: 동시에 몇 개의 파일을 읽을지
- block_length: 한 파일에서 몇 줄씩 읽고 다른 파일로 넘어갈지
- num_parallel_calls: 병렬 읽기 개수
📌 왜 쓰냐?
- 데이터를 다양한 파일에서 균형 있게 읽고, IO 병목을 줄이기 위해
'전공 > 신경망딥러닝' 카테고리의 다른 글
신경망딥러닝(11)_딥러닝 학습 순서 전체 정리 (0) | 2025.04.10 |
---|---|
신경망딥러닝(10)_전이 학습(Transfer Learning) (1) | 2025.04.08 |
신경망딥러닝(1)_개요 (0) | 2025.04.03 |
신경망딥러닝(1)_batch와 epoke (0) | 2025.04.03 |