# 1D CNN + LSTM

> Conv1D + LSTM baseline for ECG/PPG/IMU and other long-form physio signals

Pick for long-form 1D physiological signals (ECG, PPG, IMU) where local morphology + temporal context both matter. Solid baseline before reaching for transformers.

- Category: Time-series
- Layers: 14
- Parameters: 311.7K
- Input shape (batchless): 12 × 5000
- Output shape: 5
- Verifier verdict: pass
- Graph JSON: https://neurarch.com/templates/cnn-lstm-1d/model.json
- Open on the canvas: https://neurarch.com/?template=cnn-lstm-1d

## Structure

| # | Layer | Type | Parameters | Output shape |
| --- | --- | --- | --- | --- |
| 1 | ts_window | Input | shape=[12, 5000] | 12 × 5000 |
| 2 | conv1 | Conv1D | outChannels=64, kernelSize=7, stride=1 | 64 × 5000 |
| 3 | bn | BatchNorm | normalizedShape=64 | 64 × 5000 |
| 4 | act | ReLU |  | 64 × 5000 |
| 5 | pool | MaxPool1D | kernelSize=2, stride=2 | 64 × 2500 |
| 6 | conv2 | Conv1D | outChannels=128, kernelSize=5, stride=1 | 128 × 2500 |
| 7 | bn | BatchNorm | normalizedShape=128 | 128 × 2500 |
| 8 | act | ReLU |  | 128 × 2500 |
| 9 | pool | MaxPool1D | kernelSize=2, stride=2 | 128 × 1250 |
| 10 | to_timesteps | Permute |  | 1250 × 128 |
| 11 | lstm | LSTM | inFeatures=128, hiddenSize=128, numLayers=2 | 128 |
| 12 | drop | Dropout | p=0.3 | 128 |
| 13 | classifier | Linear | outFeatures=5 | 5 |
| 14 | logits | Output |  | 5 |

## Verifier findings

No finding. Shapes propagate end to end and no advisory rule fires.

## Exported PyTorch (first 46 lines)

```python
# Architecture designed with Neurarch: https://neurarch.com
# PyTorch: compatible with Python 3.8+ and torch>=1.12
# Colab: pip install torch torchvision  (usually pre-installed)
#
# WARNING: 1 layer(s) below are not yet supported by the PyTorch
# exporter and pass their input through UNCHANGED in forward():
#   - to_timesteps (permute)

import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple

class _1DCNNLSTM(nn.Module):
    def __init__(self):
        super().__init__()

        self.conv1d_1 = nn.Conv1d(12, 64, kernel_size=7, stride=1, padding=3)
        self.batchNorm_1 = nn.BatchNorm1d(64)
        self.maxpool1d_1 = nn.MaxPool1d(kernel_size=2, stride=2)
        self.conv1d_2 = nn.Conv1d(64, 128, kernel_size=5, stride=1, padding=2)
        self.batchNorm_2 = nn.BatchNorm1d(128)
        self.maxpool1d_2 = nn.MaxPool1d(kernel_size=2, stride=2)
        self.lstm_1 = nn.LSTM(128, 128, num_layers=2, batch_first=True)
        self.dropout_1 = nn.Dropout(p=0.3)
        self.linear_1 = nn.Linear(128, 5)

    def forward(self, x):
        # ts_window shape: [12,5000]
        conv1d_conv_1 = self.conv1d_1(x)
        batch_norm_bn_1 = self.batchNorm_1(conv1d_conv_1)
        relu_relu_1 = F.relu(batch_norm_bn_1)
        maxpool1d_pool_1 = self.maxpool1d_1(relu_relu_1)
        conv1d_conv_2 = self.conv1d_2(maxpool1d_pool_1)
        batch_norm_bn_2 = self.batchNorm_2(conv1d_conv_2)
        relu_relu_2 = F.relu(batch_norm_bn_2)
        maxpool1d_pool_2 = self.maxpool1d_2(relu_relu_2)
        # TODO: layer 'to_timesteps' (permute) is not yet supported by the exporter; passing through unchanged
        lstm_lstm_1 = self.lstm_1(maxpool1d_pool_2)[0][:, -1, :]
        dropout_drop_1 = self.dropout_1(lstm_lstm_1)
        linear_near_1 = self.linear_1(dropout_drop_1)
        # Output
        return linear_near_1


if __name__ == '__main__':
```

## Machine access

- Every architecture: https://neurarch.com/a/index.json
- Verify a graph of your own: `POST https://www.neurarch.com/api/v1/check` (see https://neurarch.com/developer.html)
- MCP server, so an agent edits the graph with the checks in the loop: https://neurarch.com/docs/mcp.md
