LSTM hidden_state PyTorch практически идентичен, что приводит к отрицательному KLDivLoss - PullRequest
2 голосов
/ 31 марта 2020

Я ломаю голову над этой проблемой и не могу понять, что я делаю неправильно ... Я обучил автокодеру (LSTM-LSTM) и сейчас пытаюсь использовать закодированные функции для другой задачи, используя KLDivLoss. Однако оказывается, что закодированные объекты почти всегда идентичны (см. Пример ниже при установке точности печати на 10):

# last hidden state of the encoder
tensor([[[ 0.1086065620, -0.0446619801, -0.0530930459,  ...,
          -0.0573375113,  0.1083261892,  0.0037083717],
         [ 0.1086065620, -0.0446619801, -0.0530930459,  ...,
          -0.0573375151,  0.1083261892,  0.0037083712],
         [ 0.1086065620, -0.0446619801, -0.0530930459,  ...,
          -0.0573375188,  0.1083262041,  0.0037083719],
         ...,
         [ 0.1086065620, -0.0446619801, -0.0530930422,  ...,
          -0.0573375151,  0.1083262041,  0.0037083724],
         [ 0.1086065620, -0.0446619801, -0.0530930385,  ...,
          -0.0573375151,  0.1083262041,  0.0037083712],
         [ 0.1086065620, -0.0446619801, -0.0530930385,  ...,
          -0.0573375188,  0.1083261892,  0.0037083707]]],
       grad_fn=<StackBackward>)

Как вы думаете, может быть объяснение такого поведения? Я работаю с многомерными временными рядами, и моя цель - реализовать метод кластеризации без контроля, черпая вдохновение из этой статьи . Если я подаю необработанный вывод кодера в KLDivLoss, я получаю отрицательные потери ... Однако, если я масштабирую выход кодера (используя sklearn.preprocessing.StandardScaler), я получаю желаемое поведение: положительное значение потерь.

Я уже гарантировал, что первый член в KLDivLoss является логарифмической вероятностью, а второй вероятности ...

Код для кодера (который содержит механизм внимания для идентификации соответствующие серии вождения):

class Encoder(nn.Module):
    def __init__(self, config, input_size: int):
        super(Encoder, self).__init__()
        self.input_size = input_size
        self.hidden_size = config['hidden_size_encoder']
        self.seq_len = config['seq_len']

        self.lstm = nn.LSTM(
            input_size=self.input_size,
            hidden_size=self.hidden_size,
            num_layers=1
        )
        self.attn = nn.Linear(
            in_features=2 * self.hidden_size + self.seq_len,
            out_features=1
        )
        self.dropout = nn.Dropout(p=0.5)
        self.softmax = nn.Softmax(dim=1)

    def forward(self, input_data):
        h_t, c_t = (init_hidden(input_data, self.hidden_size), init_hidden(input_data, self.hidden_size))
        input_weighted = Variable(torch.zeros(input_data.size(0), self.seq_len, self.input_size))

        for t in range(self.seq_len):
            x = torch.cat((h_t.repeat(self.input_size, 1, 1).permute(1, 0, 2),
                           c_t.repeat(self.input_size, 1, 1).permute(1, 0, 2),
                           input_data.permute(0, 2, 1).to(device)), dim=2).to(
                device) 

            e_t = self.attn(x.view(-1, self.hidden_size * 2 + self.seq_len)) 
            a_t = self.dropout(self.softmax(e_t.view(-1, self.input_size)))

            weighted_input = torch.mul(a_t, input_data[:, t, :].to(device))
            self.lstm.flatten_parameters()
            _, (h_t, c_t) = self.lstm(weighted_input.unsqueeze(0), (h_t, c_t))

            input_weighted[:, t, :] = weighted_input

        return input_weighted[:, -1:, :], h_t, c_t

Код для кластеризации

class Clusterizer(nn.Module):
    def __init__(self, n_clusters, hidden_dim, encode, alpha=1.0):
        super(Clusterizer, self).__init__()
        self.n_clusters = n_clusters
        self.hidden_dim = hidden_dim
        self.encoder = encoder
        self.alpha = alpha
        self.centroids = None

    def init_centroids(self, encoded_x):
        '''initialize clusters center using KMeans'''
        kmeans = KMeans(n_clusters=self.n_clusters, random_state=0, n_init=10).fit(encoded_x)
        centroids = torch.tensor(kmeans.cluster_centers_, dtype=torch.float)
        self.centroids = nn.Parameter(centroids, requires_grad=True)

    def target_distribution(self, q_):
        weight = (q_ ** 2) / torch.sum(q_, 0)
        return (weight.t() / torch.sum(weight, 1)).t()

    def forward(self, encoded_x):
        num = ((1 + torch.norm(encoded_x.unsqueeze(1) - self.centroids, dim=2)) / self.alpha) ** (-(self.alpha + 1) / 2)
        den = torch.sum(num, dim=1, keepdim=True)
        return num / den

Конечная часть кода (где возникает проблема), здесь encoded_data это сложенный вывод кодировщика формы (nb_observation, hidden_size).

criterion = nn.KLDivLoss(size_average=False)

...

clusterizer.init_centroids(encoded_data)
output = clusterizer(encoded_data.to(device))

target_distrib = clusterizer.target_distribution(output)
loss = criterion(output.log(), target_distrib)

Извините, кода много, но я надеюсь, что это поможет определить источник проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...