Window Dataset은 Tensorflow의 Dataset에서 sliding window기법을 적용한 것으로 시계열 데이터(Time series Data)를 처리하는데 적합하다.
이 문서를 작성하는데 참고자료는 다음과 같다.
참고1
참고2
참고3
참고4
window dataset을 이용하기 위한 전체코드는 다음과 같다. 참고로 입력 데이터는 1부터 10까지 정수값을 예제로 이용하였다.
series = np.arange(10)
series = tf.expand_dims(series, -1)
dataset = tf.data.Dataset.from_tensor_slices(series)
dataset = dataset.window(size=5, shift=1, stride=1, drop_remainder=True)
dataset = dataset.flat_map(lambda x: x.batch(5))
dataset = dataset.shuffle(5)
dataset = dataset.map(lambda x: (x[:-1], x[-1:]))
dataset = dataset.batch(batch_size=2).prefetch(1)
np.arange(10)은 0~9까지 10개의 정수를 생성한다.
series = tf.expand_dims(series, -1)
[1,2,...9]의 배열형태를 [[1], [2], [3],...,[9]]의 형태로 차원을 하나 더 늘려준다.이렇게 하는 이유는 나중에 LSTM, GRU같은 시계열 데이터 처리 layer에서 시간별로 데이터 하나씩 입력을 받는데, 이때의 입력 데이터 형태가 [1], [2],...이런 형태로 처리할 데이터를 배열 []로 감싸는 형태이기 때문이다.
dataset = tf.data.Dataset.from_tensor_slices(series)
tensor type을 tendor dataset type으로 변환시킨다. 꼭 tensor data가 아니라도 numpy array나 python list 모두 올 수 있다.
dataset을 출력하면 다음과 같이 나온다.
<TensorSliceDataset shapes: (1,), types: tf.int32>
dataset의 data 전체를 보고 싶으면 for문으로 돌려서 하나씩 확인하면 된다.
for data in dataset:
print(data)
tf.Tensor([0], shape=(1,), dtype=int32)
tf.Tensor([1], shape=(1,), dtype=int32)
tf.Tensor([2], shape=(1,), dtype=int32)
tf.Tensor([3], shape=(1,), dtype=int32)
tf.Tensor([4], shape=(1,), dtype=int32)
tf.Tensor([5], shape=(1,), dtype=int32)
tf.Tensor([6], shape=(1,), dtype=int32)
tf.Tensor([7], shape=(1,), dtype=int32)
tf.Tensor([8], shape=(1,), dtype=int32)
tf.Tensor([9], shape=(1,), dtype=int32)
dataset = dataset.window(size=5, shift=1, stride=1, drop_remainder=True)
여기서,
size : 그룹화할 window의 크기(갯수)
shift : 다음 window 시작할 때 얼마나 띄우고 시작할지 정한다.
stride : 하나의 wiondow 내에서 각각의 data 사이의 간격을 말한다.
drop_remainder=True는 window를 만들다 보면 window size보다 작은 window가 만들어질 수도 있는데 이때 이러한 window를 포함할지 말지를 정한다. default는 False이다.
일단 위의 조건에서 drop_remainder=False로 window를 만들면 다음과 같다.
[[0], [1], [2], [3], [4]]
[[1], [2], [3], [4], [5]]
[[2], [3], [4], [5], [6]]
[[3], [4], [5], [6], [7]]
[[4], [5], [6], [7], [8]]
[[5], [6], [7], [8], [9]]
[[6], [7], [8], [9]]
[[7], [8], [9]]
[[8], [9]]
[[9]]
drop_remainder가 False일 경우 모두 10개의 window가 만들어지는데, drop_remainder가 True일 경우에는 다음과 같이 6(10-5+1)개의 window가 만들어진다.
[[0], [1], [2], [3], [4]]
[[1], [2], [3], [4], [5]]
[[2], [3], [4], [5], [6]]
[[3], [4], [5], [6], [7]]
[[4], [5], [6], [7], [8]]
[[5], [6], [7], [8], [9]]
여기서 shift=2로 설정하면 다음과 같이 window의 갯수가 6개->3개로 절반으로 줄어든다. 다음 window의 시작값이 이전 window의 시작값에 2만큼(stride=2) 뛰우고 시작함을 알 수 있다.
[[0], [1], [2], [3], [4]]
[[2], [3], [4], [6], [5]]
[[4], [5], [6], [7], [8]]
다시 shift=1로 두고 sride만 2로 바꾸면 다음과 같다.
두번째 window가 shift=1이라 한칸 띄운 1로 시작되는 것을 볼 수 있고, stride=2라서 window내의 각각의 data가 2씩 차이나게 배치된 것을 볼 수 있다.
[[0], [2], [4], [6], [8]]
[[1], [3], [5], [7], [9]]
dataset = dataset.flat_map(lambda x: x.batch(5))
앞에서 window dataset은 for문을 이용해서 window의 데이터를 순차적으로 불러와야 만이 각각의 data에 접근이 가능했다. flat_map은 이를 한번에 읽을 수 있게 해준다.
즉 앞의 (4)에서 보는 data 각각은 실제로는 각 window에서 for문을 돌려서 순차적으로 읽은 것을 모아서 출력한 것이다. flat_map은 for문을 돌리지 않고 한번에 읽어올 수 있게 해준다. 이는 나중에 map처리에서 필요한 단계이다.
즉 다음과 같이 출력 결과는 같지만 각각의 window data는 한번에 읽어온 것이다.
x.batch에서 argument는 window size와 같은 값으로 한다.
[[0], [1], [2], [3], [4]]
[[1], [2], [3], [4], [5]]
[[2], [3], [4], [5], [6]]
[[3], [4], [5], [6], [7]]
[[4], [5], [6], [7], [8]]
[[5], [6], [7], [8], [9]]
dataset = dataset.shuffle(5)
shuffle은 dataset의 단위인 window끼리 무작위로 섞는 것을 말한다. argument가 5로 되어 있으면 전체 6개의 window 중에서 5개를 무작위로 선정하고 여기에 남은 한개를 포함한다.
예를 들면 다음과 같다.
[[3], [4], [5], [6], [7]]
[[2], [3], [4], [5], [6]]
[[4], [5], [6], [7], [8]]
[[1], [2], [3], [4], [5]]
[[5], [6], [7], [8], [9]]
[[0], [1], [2], [3], [4]]
window datasaet을 만들고 flat_map을 한 다음에 shuffle로 섞는데 이렇게 하는 이유는 적어도 window내의 data는 입력 순서를 유지해야 하기 때문에 window 단위로 섞는 것이다.
shuffle은 학습할 때에만 하고 예측할 때에는 shuffle을 사용하지 않도록 해서 모든 data들이 순서를 유지하도록 한다.
dataset = dataset.map(lambda x: (x[:-1], x[-1:]))
tensor dataset의 map은 pandas Series의 map과 유사하다. map함수에 lambda 함수가 argument로 들어갔는데 window data 각각에 대해서 처음부터 마지막 데이터 바로 앞까지는 train data이고 마지막 데이터는 label data이다. 이렇게 분리된 것은 반드시 tuple로 묶어 주어야 한다. 나중에 모델에서 학습할 때 dataset이 학습 데이터로 입력될 경우 train data와 label data가 tuple로 묶어져 있어야 에러없이 정상적으로 학습핧 수 있기 때문이다.
즉 window data에서 마지막 데이터 바로 앞까지 학습해서 마지막 데이터를 예측하는 것을 학습하는 것이다.
다음과 같이 data가 구성이 된다.
[[3], [4], [5], [6]], [[7]]
[[2], [3], [4], [5]], [[6]]
[[4], [5], [6], [7]], [[8]]
[[1], [2], [3], [4]], [[5]]
[[5], [6], [7], [8]], [[9]]
[[0], [1], [2], [3]], [[4]]
dataset = dataset.batch(batch_size=2).prefetch(1)
window 2개을 묶어서 하나의 batch로 만든다. 즉 한번에 window 2개씩 처리한다. batch size는 조절가능하다. prefetch(1)은 batch단위로 처리할 때 batch 처리가 끝나기 전에 다음 batch 1개를 미리 읽어서 준비하는 것을 말한다.
다음과 같이 데이터가 구성된다.
windiw 2개씩 묶어서 train data, label data를 구성한다.
# 첫번째 batch
[[[3], [4], [5], [6]], [[7],
[[2], [3], [4], [5]]], [6]]
# 두번째 batch
[[[4], [5], [6], [7]], [[8],
[[1], [2], [3], [4]]], [5]]
# 세번째 batch
[[[5], [6], [7], [8]] [[9],
[[0], [1], [2], [3]]], [4]]