pytorch customdataset을 만들면서 __getitem__에서 idx를 2 dim으로 주고 싶습니다.

안녕하세요. Pytorch로 시계열 데이터 분석을 하고 있는 학생입니다.

현재 저는 시계열 데이터를 가지고 이상 탐지를 하기 위해서 class를 선언하면서 코드를 짜고있습니다.
custom dataset을 만들고자 현재 코딩을 하고 있는데 한가지 어려운 점을 발견했습니다.


이 사진에서 보이는 것처럼 시계열 데이터를 288데이터마다 끊어서 하나의 sequence를 만들려고 했습니다. 그러다 이 코드의 결과물이 전체 데이터를 거치는 것이 아닌 첫번째 데이터셋(처음 288개 데이터)를 가지고 학습을 하는 것을 발견했습니다.

이와 같이 dataloader를 이용해서 코드를 돌리면 첫번째 288개의 데이터만 가지고 해서 __getitem__에서 idx를 주는 것이 2차원 매트릭스 형태로 make_sequence를 하면서 만들어질텐데 idx가 하나만 주어져서 전체 데이터를 다 돌고 있지 않은 것 같았습니다.

이것을 어떻게 해결해야 할까요?

안녕하세요, @Learner 님.

먼저 코드는 이미지보다는 (```으로 감싸는) 코드 블럭으로 공유해주시면 다른 분들께서 재현해보시는데 좋을 것 같습니다.

질문을 정확히 이해했는지 자신이 없는데요, 대략 만드신 CustomDataset으로부터 만든 DataLoader에서 x_train 값이 제대로 불려오지 않는다는 것으로 이해했습니다. 제가 이해한게 맞을까요?

만드신 CustomDataset은 label이 없이, csv에서 읽어온 scalar 값으로부터 sequence를 만들어 반환하는 것으로 보입니다. Dataset에서 label없이 data만 반환하는 것도 가능하지만, 이렇게 반환하는 경우에는 Dataloader 또한 label없이 __getitem__()으로부터 반환된 data만 넘겨주게 됩니다.

즉, 위 코드, 두번째 이미지의 samplesdataloader로부터 2개(batch_size=2)의
(label이 없는) data(=CustomDatasetself.train_data_final)만 넘겨받고 있습니다.

따라서 samplesx_train_으로 언패킹(unpacking)할 때 에러가 발생해야 하는데, 마침 batch_size가 2이기 때문에 각각의 데이터가 x_train_으로 나뉘어 (에러 없이) 할당되면서 말씀하신 문제 상황이 발생하는 것으로 보입니다. (이해가 어려우시면 DataLoader에서 batch_size를 2가 아닌 다른 값으로 바꿔보시면 될 것 같습니다. :slight_smile: )

해결을 위해서는 (1) samples를 따로 unpack하지 않고 바로 x_train에 할당하는 방법과 (2) CustomDataset__getitem__()에서 label을 함께 반환하도록 한 뒤 위 코드 처럼 unpack하는 방법이 있을 것 같습니다.

추가로 참고하실만한 링크를 아래 첨부하오니 함께 확인하시면 좋을 것 같습니다.

https://pytorch.org/docs/stable/data.html

https://tutorials.pytorch.kr/beginner/basics/data_tutorial.html

더 궁금하신 내용이 있다면 알려주세요.

class CustomDataset(Dataset):

  def __init__(self, path):

    self.x_data = pd.read_csv(path).iloc[:, 1].values

    self.train_data = self.make_sequence(self.x_data, 288)

    self.train_data_tensor = torch.FloatTensor(self.train_data)

    self.train_data_final = torch.reshape(self.train_data_tensor, (self.train_data_tensor.shape[0], 1, self.train_data_tensor.shape[1]))

   

  # 총 데이터의 개수를 리턴

  def __len__(self):

    return len(self.train_data_final)

  # 인덱스를 입력받아 그에 맵핑되는 입출력 데이터를 파이토치의 Tensor 형태로 리턴

  def __getitem__(self, idx):

    x = torch.FloatTensor(self.train_data_final[idx])

    y = torch.FloatTensor(self.train_data_final[idx])

    return x, y

 

  def make_sequence(self, data, size):

      seq = []

      for i in range(0, len(data) - size):

        seq.append(data[i :i+size])

      return seq

이와 같이 변환했을 때 해결되는 것을 확인했습니다. 감사합니다!!

좋아요 1

해결되셨다니 다행입니다. :clap: :clap:


참고삼아 말씀드리면, 아래와 같이 특정 dim에 차원을 추가하기 위해 작성하신 코드는 unsqueeze() 함수를 사용하면 조금 더 직관적이고 간결하게 바꿀 수 있습니다. :slight_smile:

    self.train_data_final = self.train_data_tensor.unsqueeze(dim=1)

아래 샘플 코드와 실행 결과를 추가하였으니 참고하시면 좋을 것 같습니다.

>>> import torch
>>> data = torch.arange(10).reshape(2, 5)
>>> print(data.shape)
torch.Size([2, 5])
>>> print(data)
tensor([[0, 1, 2, 3, 4],
        [5, 6, 7, 8, 9]])
>>> another_data = data.unsqueeze(1)
>>> print(another_data.shape)
torch.Size([2, 1, 5])
>>> print(another_data)
tensor([[[0, 1, 2, 3, 4]],
        [[5, 6, 7, 8, 9]]])
>>>

추가로 아래 torch.unsqueeze() 문서 링크를 첨부하오니 함께 확인하시면 좋을 것 같습니다.
https://pytorch.org/docs/stable/generated/torch.unsqueeze.html

좋아요 1