동영상인 데이터셋을 사용하다보니까 메모리에 다 올라가지 않는 문제가 있었다... train_on_batch()사용으로 버텨보다가,,
텐서플로에서 제공하는 data API 사용을 도전!!!!
data API는 데이터셋 객체를 만들고 데이터를 읽어 올 위치와 변환 방법을 지정함으로서 대규모 데이터셋을 효율적으로 로드하고 전처리마저 구현할 수 있다고 한다!
멀티스레팅, 큐, 배치, 프리페치와 같은 사항도 대신 처리해주고 tf.keras와도 잘 동작한다고 한다~! (fit() 못잃어,,)
텍스트 파일(ex. CSV파일), 고정 길이의 레코드를 가진 이진 파일, 텐서 플로의 TFRecord 포맷을 사용하는 이진 파일에서 데이터를 읽을 수 있음(TFRecord 포맷은 길이가 다른 레코드를 지원함)
SQL 데이터베이스에서 읽는 기능도 지원하고 구글 빅쿼리와 같은 다양한 데이터 소스에서 읽을 수 있는 오픈 소스도 있다고한다~~
TF 변환 (tf.Transform)
: 훈련 전에 전체 훈련 세트에 대해 실행하는 전처리 함수 작성
데이터셋(dataset)
연속된 데이터 샘플(디스크에서 데이터를 점진적으로 읽는 데이터셋 사용)
tf.data.Dataset.from_tensor_slices(X)
from_tensor_slices() 함수는 텐서를 받아 X의 각 원소가 아이템으로 표현되는 tf.data.Dataset을 만듦
아이템 순회법
X = tf.range(10)으로 dataset을 만듦
for item in dataset:
print(item)
output:
tf.Tensor(0, shape=(), dtype=int32)
tf.Tensor(1, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32)
tf.Tensor(4, shape=(), dtype=int32)
tf.Tensor(5, shape=(), dtype=int32)
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(7, shape=(), dtype=int32)
tf.Tensor(8, shape=(), dtype=int32)
tf.Tensor(9, shape=(), dtype=int32)
연쇄 변환
repeat()
repeart()메소드를 호출하여 원본 데이터셋의 아이템을 반복
매개변수 없이 호출하면 끝없이 반복되므로 반복하는 코드가 중지하는 때를 결정해야함
dataset = dataset.repeat(3).batch(7)
for item in dataset:
print(item)
output:
tf.Tensor([0 1 2 3 4 5 6], shape=(7,), dtype=int32)
tf.Tensor([7 8 9 0 1 2 3], shape=(7,), dtype=int32)
tf.Tensor([4 5 6 7 8 9 0], shape=(7,), dtype=int32)
tf.Tensor([1 2 3 4 5 6 7], shape=(7,), dtype=int32)
tf.Tensor([8 9], shape=(2,), dtype=int32)
데이터셋을 3번 반복하고 아이템을 7개씩 묶어 출력함
마지막 아이템처럼 크기가 다른 아이템은 batch()메서드에 drop_remainder = True 로 호출하여 버릴 수 있음
map()
전처리 작업에도 적용할 수 있으나 복잡한 계산을 포함하는 경우엔 여러 스레드로 나누어 속도를 높이는 것이 좋음
dataset = dataset.map(lambda x: x * 2)
apply()
각 아이템에 변환을 적용하는 map()과 달리 데이터셋 전체에 변환을 적용
filter()
데이터셋 필터링
dataset = dataset.filter(lambda x: x < 10)
take()
데이터셋에 있는 몇 개의 아이템만 볼 때 사용
데이터 셔플링
경사 하강법은 train set에 있는 샘플이 독립적이고 동일한 분포일 때 최고의 성능을 발휘함
--> shuffle() 메소드를 사용하여 샘플을 섞음
shuffle()
데이터셋의 처음 아이템을 buffer_size 만큼 채운 후, 아이템이 요청되면 버퍼에서 랜덤하게 하나를 꺼내 반환한 후, 원본 데이터셋의 모든 아이템이 사용될 때까지 원본 데이터셋에서 새로운 아이템을 꺼내 버퍼를 채움
dataset = tf.data.Dataset.range(10).repeat(3)
dataset = dataset.shuffle(buffer_size=5, seed=42).batch(7)
for item in dataset:
print(item)
output:
tf.Tensor([0 2 3 6 7 9 4], shape=(7,), dtype=int64)
tf.Tensor([5 0 1 1 8 6 5], shape=(7,), dtype=int64)
tf.Tensor([4 8 7 1 2 3 0], shape=(7,), dtype=int64)
tf.Tensor([5 4 2 7 8 9 9], shape=(7,), dtype=int64)
tf.Tensor([3 6], shape=(2,), dtype=int64)
대규모 데이터셋은 버퍼가 데이터셋에 비해 작기 때문에 위와 같은 간단한 셔플링 버퍼 방식으로 충분하지 않음
-->원본 데이터 자체를 섞어야함
-->많이 사용하는 방법으론 원본 데이터를 여러 파일로 나눈 다음 훈련하는 동안 무작위로 읽음
data API를 사용하면 간단!
list_files()
파일 경로를 섞은 데이터셋을 반환
매개변수로 파일 경로를 담은 리스트를 줌
데이터 전처리
데이터 적재와 전처리 합치기
def csv_reader_dataset(filepaths, repeat = 1, n_readers = 5, n_read_threads = None, shuffle_buffer_size = 10000, n_parse_threads = 5, batch_size = 32):
dataset = tf.data.Dataset.list_files(filepaths).repeat(repeat)
dataset - dataset.interleave(
lambda filepath: tf.data.TextLineDataset(filepath).skip(1),
cycle_length = n_readers, num_parallel_calls = n_read_threads
)
dataset = dataset.shuffle(shuffle_buffer_size)
dataset = dataset.map(preprocess, num_parallel_calls= n_parse_threads)
return dataset.batch(batch_size).prefetch(1)
앞에 한 걸 다 정리해주는 듯한 함수
이제 내 코드를 짜러,,~!