from torch import nn class CNNLSTM(nn.Module): def __init__(self, input_dim: int, output_dim: int, embed_dim: int, lstm_hidden_size=64, num_layers=1): super().__init__() self.conv = nn.Sequential( nn.Conv1d(in_channels=input_dim, out_channels=input_dim, kernel_size=9, stride=2), # 288 → ~140 nn.ReLU(), nn.AdaptiveAvgPool1d(output_size=128), # force to 128 nn.Conv1d(input_dim, input_dim, kernel_size=5, stride=2), # 128 → ~62 nn.ReLU(), nn.AdaptiveAvgPool1d(output_size=48), # final fixed length ) # linear projection to embed dim self.input_proj = nn.Linear(input_dim, embed_dim) self.lstm = nn.LSTM( input_size=embed_dim, hidden_size=lstm_hidden_size, num_layers=num_layers, batch_first=True, dropout=0.4, ) self.head = nn.Sequential( nn.Linear(lstm_hidden_size, output_dim) # Output is a scalar Δt ) def forward(self, x): # x: (batch_size, seq_len, input_size) x = x.permute(0, 2, 1) # x: (batch_size, input_size, seq_len) x = self.conv(x) # x: (batch_size, embed_dim, seq_len) x = x.permute(0, 2, 1) # x: (batch_size, seq_len, embed_dim) # project to embed dim x = self.input_proj(x) # x: (batch_size, seq_len, embed_dim) x, _ = self.lstm(x) # x: (batch_size, seq_len, lstm_hidden_size) x = x[:, -1, :] # Get the last time step # x: (batch_size, lstm_hidden_size) x = self.head(x) # x: (batch_size, output_size) return x