시계열 시퀀스 데이터를 pytorch LSTM 입력으로 넣은 뒤 출력 텐서 형상에 대한 질문

안녕하세요. 현재 배치 사이즈가 28이고, 시퀀스 길이가 32인 데이터를 pytorch의 LSTM 모델의 입력에 넣으려고 합니다. 그런데, 아시다시피 LSTM 모델은 총 3개의 출력(이를 각각 hs, h, c 라고 하겠습니다)을 내뱉어주는데요?

이 때, hs는 시퀀스 길이만큼의 hidden_state 벡터를 출력하고, h는 LSTM의 순환하는 마지막 셀의 hidden_state 벡터를, c는 LSTM의 순환하는 마지막 셀의 memory_cell 벡터를 의미한다고 알고 있습니다.

그런데, 제가 예상했던 것과 달리, h 와 c 벡터의 형상이 이상해서요. 우선 코드는 아래와 같습니다.

import torch
import torch.nn as nn


class Encoder(nn.Module):
    def __init__(self,
                 notes_vocab_size,
                 durations_vocab_size,
                 embedding_size,
                 hidden_size):
        super(Encoder, self).__init__()

        self.notes_vocab_size = notes_vocab_size
        self.durations_vocab_size = durations_vocab_size
        self.embedding_size = embedding_size
        self.hidden_size = hidden_size

        self.notes_embed = nn.Embedding(num_embeddings=self.notes_vocab_size, embedding_dim=embedding_size)
        self.durations_embed = nn.Embedding(num_embeddings=self.durations_vocab_size, embedding_dim=embedding_size)

        self.lstm = nn.LSTM(input_size=self.embedding_size*2, hidden_size=self.hidden_size, num_layers=1)

    def forward(self, x_notes, x_durations):
        x_notes_embed = self.notes_embed(x_notes)
        x_durations_embed = self.durations_embed(x_durations)
        x = torch.cat((x_notes_embed, x_durations_embed), dim=2)
        hs, (h, c) = self.lstm(x)
        return hs, h, c

# params
notes_vocab_size = 32
durations_vocab_size = 5
embedding_size = 128
hidden_size = 256

# dataset
x_notes = torch.rand(28, 32).to(torch.int32)
x_durations = torch.rand(28, 32).to(torch.int32)


model = Encoder(notes_vocab_size, durations_vocab_size, embedding_size, hidden_size)
hs, h, c = model(x_notes, x_durations)

print(hs.shape, h.shape, c.shape)
# 출력: torch.Size([28, 32, 256]) torch.Size([1, 32, 256]) torch.Size([1, 32, 256])

위 출력 화면에서 hs 라는 텐서의 형상은 (28, 32, 256)으로 28은 배치 사이즈, 32는 시퀀스 길이, 256은 LSTM의 hidden_state unit 수인 것으로 이해가 되는데, h 와 c의 형상은 왜 (1, 32, 256)인 건가요? h 와 c 는 LSTM의 순환하는 마지막 셀의 hidden_state, memory_cell 벡터라고 한다면 형상이 (28, 1, 256) 이 되어야 하는 거 아닌가요?

초심자라 기본적인 질문 드립니다! 답변 해주시면 감사하겠습니다! :slight_smile:

1개의 좋아요
  • 아래 답변은 OpenAI의 GPT-4-Turbo 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요, young-hun-jo님. LSTM 모델의 출력 형상에 대해 혼란을 겪고 계신 부분에 대해 질문해주셨군요. 문제를 정확히 이해했습니다. PyTorch에서 LSTM의 출력 형상에 대해 궁금증을 해결해드리겠습니다.

PyTorch의 LSTM은 기본적으로 3개의 출력을 제공합니다: hs (hidden states for all timesteps), h (hidden state for the last timestep), 그리고 c (cell state for the last timestep). 여기서 hc의 형상이 예상과 다르게 나온 것에 대해 의문을 가지셨습니다.

hc의 형상이 (1, 32, 256)으로 나온 이유는 LSTM이 여러 레이어를 가질 수 있기 때문입니다. 여기서 첫 번째 차원은 레이어의 수를 나타냅니다. num_layers=1로 설정하셨기 때문에, 첫 번째 차원은 1이 됩니다. 두 번째 차원은 배치 사이즈, 세 번째 차원은 hidden state의 차원입니다.

그러나 여기서 주의해야 할 점은 PyTorch LSTM의 기본 배치 차원 순서입니다. 기본적으로, LSTM의 입력과 출력은 (seq_len, batch, feature) 형태를 가집니다. 따라서, hc의 형상이 (1, 32, 256)인 것은 레이어 수, 배치 사이즈, 히든 사이즈를 나타냅니다. 여기서 두 번째 차원이 배치 사이즈인 것이 아니라, 시퀀스 길이를 나타내는 것처럼 보이는 것은 입력 데이터의 차원 순서가 잘못되었기 때문입니다.

아래는 수정된 코드 예시입니다. x_notesx_durations의 차원을 (seq_len, batch)로 변경하고, LSTM의 출력을 확인하는 부분을 수정했습니다.

import torch
import torch.nn as nn

class Encoder(nn.Module):
    # ... (생략) ...

    def forward(self, x_notes, x_durations):
        # ... (생략) ...
        hs, (h, c) = self.lstm(x)
        return hs, h, c

# ... (생략) ...

# dataset
# 차원을 (seq_len, batch)로 변경
x_notes = torch.randint(high=notes_vocab_size, size=(32, 28))
x_durations = torch.randint(high=durations_vocab_size, size=(32, 28))

model = Encoder(notes_vocab_size, durations_vocab_size, embedding_size, hidden_size)
hs, h, c = model(x_notes, x_durations)

print(hs.shape)  # 시퀀스 길이, 배치 사이즈, 히든 사이즈
print(h.shape)   # 레이어 수, 배치 사이즈, 히든 사이즈
print(c.shape)   # 레이어 수, 배치 사이즈, 히든 사이즈

위 코드에서 x_notesx_durations의 차원을 (seq_len, batch)로 변경했습니다. 이제 hc의 형상은 (1, 28, 256)이 되어야 합니다. 여기서 첫 번째 차원은 레이어 수, 두 번째 차원은 배치 사이즈, 세 번째 차원은 히든 사이즈를 나타냅니다.

더 자세한 정보를 원하시면 PyTorch 공식 문서의 LSTM 섹션을 참조하시기 바랍니다:

제 답변이 정확하지 않을 수도 있으니, 실제 코드를 실행하시며 확인해보시는 것을 추천드립니다. 혹시 더 궁금한 점이 있으시면 언제든지 질문해주세요.


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
1개의 좋아요
  • 아래 답변은 Anthropic의 Claude-2 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요 young-hun-jo님,

LSTM 모델 출력 텐서 형상에 대한 질문 감사합니다.

저도 처음에는 LSTM 출력 텐서 형상이 이해가 안 갔었는데요. LSTM이 순환 구조로 시퀀스를 입력받아 가면서 hidden state와 cell state를 갱신하다가 마지막 시점의 hidden/cell state를 출력하는 방식인 것 같습니다.

즉, 순환 구조의 마지막 시점이 배치 크기(batch size) 28과 동일한 1개라서 h와 c 텐서 형상이 (1, 32, 256)이 나오는 것 같습니다.

한편 hs 텐서는 시퀀스 길이 32만큼의 hidden state가 배치 크기 28로 출력되기 때문에 (28, 32, 256) 형상인 것 같습니다.

다음은 LSTM 공식 문서와 유사한 경우의 간단한 예시 코드입니다.

import torch
import torch.nn as nn

lstm = nn.LSTM(3, 3) # input_size=3, hidden_size=3
inputs = [torch.randn(1, 3) for _ in range(5)] # make a sequence of length 5

# initialize the hidden state.
hidden = (torch.randn(1, 1, 3), 
          torch.randn(1, 1, 3))
for i in inputs:
    # Step through the sequence one element at a time.
    # after each step, hidden contains the hidden state.
    out, hidden = lstm(i.view(1, 1, -1), hidden)

# alternatively, we can do the entire sequence all at once.
# the first value returned by LSTM is all of the hidden states throughout
# the sequence. the second is just the most recent hidden state
# (compare the last slice of "out" with "hidden" below, they are the same)
# The reason for this is that:
# "out" will give you access to all hidden states in the sequence
# "hidden" will allow you to continue the sequence and backpropagate, 
# by passing it as an argument  to the lstm at a later time
# Add the extra 2nd dimension
inputs = torch.cat(inputs).view(len(inputs), 1, -1)
hidden = (torch.randn(1, 1, 3), torch.randn(1, 1, 3)) # clean out hidden state
out, hidden = lstm(inputs, hidden)
print(out)
print(hidden)

출력은 다음과 같습니다:

tensor([[[-0.5451, -0.5992,  0.1737],
         [-0.2030,  0.0936,  0.4481],
         [-0.4322, -0.1447,  0.2294],
         [-0.3816, -0.1106,  0.1991],
         [-0.3001, -0.0946,  0.1712]]], grad_fn=<StackBackward>)
(tensor([[[-0.3001, -0.0946,  0.1712]]], grad_fn=<SqueezeBackward1>), tensor([[[-0.0098, -0.0017,  0.0013]]],
       grad_fn=<SqueezeBackward1>))

out 텐서가 시퀀스 길이 5만큼의 hidden state를 출력하고,
hidden 텐서가 마지막 시점의 hidden state와 cell state를 출력하는 것을 확인할 수 있습니다.

제가 이해한 한계가 있을 수 있으니 더 자세한 내용이 필요하시다면 댓글 남겨주세요!


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!
  • 아래 답변은 OpenAI의 GPT-4 모델로 자동 생성한 것으로, 정확하지 않을 수 있습니다.
  • 잘못된 답변을 보셨다면 댓글로 첨삭 지도 부탁드립니다. :sweat_smile:

안녕하세요, young-hun-jo님! LSTM 모델을 사용하시면서 출력 텐서의 형상에 대해 혼란이 있으신 것 같네요. 질문을 정리해보면, PyTorch의 LSTM 모델을 사용할 때, hs, h, c의 출력 형상에 대해 이해하고 싶으시고, 특히 hc의 형상이 예상과 다른 이유를 알고 싶으신 것 같습니다.

LSTM의 출력 형상에 대해 설명드리겠습니다. LSTM에서 hs는 모든 시간 스텝에 대한 hidden states를 나타내며, h는 마지막 시간 스텝의 hidden state, c는 마지막 시간 스텝의 cell state를 나타냅니다. PyTorch에서 LSTM의 hc의 출력 형상은 (num_layers * num_directions, batch, hidden_size)입니다. 여기서 num_layers는 LSTM 층의 개수, num_directions는 LSTM이 양방향인 경우 2, 단방향인 경우 1입니다.

예제 코드에서 hc의 형상이 (1, 32, 256)으로 나온 이유는 LSTM이 단방향이고 층이 하나이기 때문입니다. 여기서 첫 번째 차원인 1은 num_layers * num_directions를 나타내고, 두 번째 차원인 32는 배치 사이즈, 세 번째 차원인 256은 hidden_size를 나타냅니다.

예상하신 (28, 1, 256) 형상으로 출력을 받고 싶으시다면, hc의 텐서를 조작하여 배치 차원과 시퀀스 차원의 위치를 바꿔주어야 합니다. 아래는 수정된 코드 예시입니다.

import torch
import torch.nn as nn

# ... [이전 코드와 동일] ...

model = Encoder(notes_vocab_size, durations_vocab_size, embedding_size, hidden_size)
hs, (h, c) = model(x_notes, x_durations)

# h와 c의 배치 차원과 시퀀스 차원의 위치를 바꿉니다.
h = h.transpose(0, 1)
c = c.transpose(0, 1)

print(hs.shape, h.shape, c.shape)
# 출력: torch.Size([28, 32, 256]) torch.Size([28, 1, 256]) torch.Size([28, 1, 256])

이렇게 하면 hc의 형상이 (28, 1, 256)으로 바뀌어 예상하신 형상으로 출력됩니다.

추가적으로 LSTM에 대한 더 자세한 정보는 PyTorch 공식 문서에서 확인하실 수 있습니다:

제 답변이 정확하지 않을 수도 있으니, 문서를 참고하시면서 실제 코드에 적용해보시길 권장드립니다. 혹시 더 궁금한 점이 있으시면 언제든지 질문해주세요!


  • 답변이 어떠셨나요? 괜찮았다고 생각하신다면 아래 :heart:를 한 번 눌러주시길 부탁드려요!

LSTM의 입력에 (sequence_length, batch_size) 형상으로 텐서를 넣어줘야 하는 것을 몰랐습니다. GPT의 첫번째 답변이 너무 도움이 되었습니다! 감사합니다!

위 처럼 넣어주어야 하는 이유가 정확히는 LSTM의 batch_first 인자가 False 라서 그랬다는 것도 추가로 확인했습니다! 제가 질문한대로 되려면 batch_first 가 True이도록 해야 하네요 ㅎㅎ

1개의 좋아요