First of all thanks for reaching out, sorry if I am not being clear.
I am performing to perform a prediction on a series of datas, all belonging to the same time-series (which I generated thanks to a sinusoidal function, the frequency of which is time-dependent).
In order to do so I build a LSTM NN, implemented a simple training algorithm to which I feed my preprocessed datas, which are been formatted to tensors.
I appended the model and training algorithm, but if you manage to look at my code you will surely see the problem in an instant.
Thank you for the attention
class LSTM(nn.Module):

  def __init__(self, n_features, n_hidden, seq_len, n_layers):
    super(LSTM, self).__init__()
    self.n_hidden = n_hidden
    self.seq_len = seq_len
    self.n_layers = n_layers

#lstm_input.shape =(seq_len=numero sequenze di input, batch_size=numero elementi ciascuna sequenza, input_size=terza
#dimensione sequenza
#output.shape=(seq_len, batch, num_directions * hidden_size) con una dimensione

    self.lstm = nn.LSTM(
      input_size=n_features,
      hidden_size=n_hidden,
      num_layers=n_layers,
      
    )
    #pass output of lstm to the linear
#output of the lstm is passed down to the linear, which performs a linear transf with random weights and bias(until update)
    self.linear = nn.Linear(in_features=n_hidden, out_features=1)

  def reset_hidden_state(self):
#it generates a tensor which is made of 2 3-dimensional tensors side by side
    self.hidden = (torch.zeros(self.n_layers, self.seq_len, self.n_hidden), torch.zeros(self.n_layers, self.seq_len, self.n_hidden))

  def forward(self, sequences):
#we pick all the sequences and pass them to te LSTM at once
    #lstm_out, self.hidden = self.lstm(sequences.view(len(sequences), self.seq_len, -1), self.hidden)
    lstm_out, self.hidden = self.lstm(sequences.view(len(sequences), 1, -1), self.hidden)
    #print(lstm_out.shape)
#view keeps all original data while changing their shape into a 3-dimensional tensor
    last_time_step = lstm_out.view(self.seq_len, len(sequences), self.n_hidden)[-1] 
    #print(last_time_step.shape)
#we pass the output of the last time stem to the linear operator, to get the prediction
    #y_pred = self.linear(last_time_step)
    y_pred = self.linear(lstm_out.view(len(sequences),-1))
    return y_pred

def train_model(model, train_data, train_labels, test_data=None, test_labels=None):
#define loss unction as MSE and optimiser as adam
  loss_fn = torch.nn.MSELoss(reduction='sum')
  optimiser = torch.optim.Adam(model.parameters(), lr=1e-3)#learning rate
  num_epochs = 60
    
#initialize to 0 the test and train history
  train_hist = np.zeros(num_epochs)
  test_hist = np.zeros(num_epochs)
#at each epoch we reset the hidden state, run the whole set, compute the loss
  for t in range(num_epochs):
    model.reset_hidden_state()
    optimiser.zero_grad()
    y_pred = model(X_train)
    loss = loss_fn(y_pred.float(), y_train)

    if test_data is not None:
      with torch.no_grad(): #disables gradient calculation for subsequent lines of code
#predict the output thanks to the model, the compute the loss thanks to verify data
        y_test_pred = model(X_test)
        test_loss = loss_fn(y_test_pred.float(), y_test)
      test_hist[t] = test_loss.item()
#printing progress
      if t % 1 == 0:
        print(f'Epoch {t} train loss: {loss.item()} test loss: {test_loss.item()}')
    elif t % 1 == 0:
      print(f'Epoch {t} train loss: {loss.item()}')
#including the losses in the train history
    train_hist[t] = loss.item()
#we reset the previous gradient, compute the new one and give a forward step to the optimizer
    #optimiser.zero_grad()

    loss.backward()

    optimiser.step()

  return model.eval(), train_hist, test_hist