실습 · 연습문제 2개
epoch별 batch 인덱스와 worker 배분 batch-indices
DataLoader는 데이터를 직접 읽기 전에 인덱스부터 정한다. sampler가 한 epoch 동안 방문할 표본 순서를 만들고, batch sampler가 그 순서를 batch 크기로 자르고, 각 batch를 worker 프로세스에 나눠 맡긴다. 이 흐름을 NumPy로 구현한다.
epoch_batches(n, batch_size, seed, epoch, shuffle=True, drop_last=False)- 표본
0, ..., n-1을 한 번씩 방문하는 batch 목록(정수 list의 list)을 반환한다. shuffle=True이면 순서를 섞는다. 섞는 순서는(seed, epoch)만으로 정해져야 한다. 같은(seed, epoch)이면 항상 같은 결과,epoch이 다르면 (거의 항상) 다른 순서다. 힌트:np.random.default_rng([seed, epoch]).shuffle=False이면0, 1, 2, ...순서다.- 마지막 batch가
batch_size보다 작으면drop_last=True일 때 버리고,False일 때 남긴다.
- 표본
assign_to_workers(batches, num_workers)- batch 목록을 worker들에게 차례로 돌아가며 배분한다. worker
k는 batchk, k + W, k + 2W, ...를 맡는다(W = num_workers). map-style Dataset에서 PyTorch DataLoader가 batch를 worker에 나누는 기본 방식이다. - 반환: 길이
num_workers인 list.k번째 원소는 workerk가 맡은 batch들의 list.
- batch 목록을 worker들에게 차례로 돌아가며 배분한다. worker
worker들이 batch를 동시에 만들어도 DataLoader는 batch를 원래 순서대로 돌려준다(집필 시점 PyTorch의 기본값 in_order=True). 그래서 worker 수를 바꿔도 학습에 들어가는 batch 순서는 sampler가 정한 순서와 같다. 다만 worker 안에서 쓰는 난수(데이터 증강)는 worker마다 시드를 따로 정해야 한다(본문 참고).
가변 길이 시계열을 padding과 mask로 batch화하기 pad-and-mask
길이가 다른 시계열 들을 하나의 배열로 묶으려면 가장 긴 길이 에 맞춰 뒤를 채워야(padding) 한다. 채운 값은 데이터가 아니므로, 어디가 실제 값인지 알려 주는 mask를 함께 만들고 손실과 metric은 mask가 True인 곳에서만 계산한다. NumPy로 두 함수를 완성한다.
pad_batch(seqs, pad_value=0.0)seqs: 길이가 다른 2차원 배열(T_i, C)의 list. 모든 배열의C는 같다.- 반환:
(x, mask, lengths)x:(B, T_max, C)float 배열. 앞쪽은 원래 값, 뒤쪽은pad_value.mask:(B, T_max)bool 배열. 실제 값이면True.lengths:(B,)정수 배열.
- 입력 배열을 바꾸지 않는다.
masked_mse(pred, target, mask)pred,target:(B, T, C),mask:(B, T).- mask가
True인 위치의 모든 원소에 대한 제곱오차의 평균을 반환한다(float). padding 위치의 값이 무엇이든 결과가 같아야 한다. - mask가 모두
False이면ValueError를 낸다(0으로 나누는 대신 문제를 드러낸다).
이렇게 만든 손실은 원소 단위 평균이다. 긴 시계열이 짧은 시계열보다 더 큰 비중을 가진다. 표본마다 같은 비중을 원하면 표본별 평균을 먼저 구한 뒤 평균한다(본문 참고). 어느 쪽을 쓸지는 설계 선택이므로 코드와 보고서에 밝힌다.