Yes its 20x14336.  My dimensions just aren’t correct each step of the way, I am doing something wrong.  here is a better flow:
My data is rows of 28 x 30495
Dataloader grabs batch_size 20, so 20x28x30495 and throws that to my lstm
And here is how my objects are changing
for data, target in trainX:
        data, target = data.to(device), target.to(device)
        # data torch.Size([20, 28, 30495])
        # target torch.Size([20, 30490])

        optimizer.zero_grad()
        lstm.init_hidden()
        outputs = lstm(data)

Inside my model
    def forward(self, x):                                             
        # x torch.Size([20, 28, 30495])
        # Propagate input through LSTM
        lstm_output, (h_n, c_n) = self.lstm(x, (self.h_0, self.c_0))  
        # lstm_output torch.Size([20, 28, 512])
        # h_n torch.Size([1, 20, 512])
        lstm_output = torch.flatten(lstm_output, start_dim=0, end_dim=1)
        # lstm_output  torch.Size([560, 512])
        out = self.fc(lstm_output)
        # out  torch.Size([560, 30490])
        out = self.dropout(out)
        # out torch.Size([560, 30490])        
        return out

The above fails with
RuntimeError: The size of tensor a (560) must match the size of tensor b (20) at non-singleton dimension 0
Because my loss function wants the output to be in 20x30490.  So I worry I am somehow getting 28 (the number of rows in a single batch) confused with 20 (the number of batches).  And I don’t want to just do reshape/flatten gymnastics to make something fit unless I am doing it the right way.  I fear I am doing something fundamentally wrong that is tripping everything up but I don’t see it.