dayounglee commited on
Commit
e48cc47
Β·
verified Β·
1 Parent(s): 6a55622

Upload model.py

Browse files
Files changed (1) hide show
  1. model.py +122 -0
model.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Standalone LSTM models for EMG-to-pose prediction.
3
+
4
+ EMGPoseLSTM β€” processes full sequences at once (for training).
5
+ SequentialEMGPoseLSTM β€” steps through one timestep at a time (for streaming/inference).
6
+ """
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+
11
+
12
+ class EMGPoseLSTM(nn.Module):
13
+ """LSTM that maps EMG signals to joint angle predictions.
14
+
15
+ Processes the full sequence in one shot. Use this for training.
16
+
17
+ Input: (B, C_in=16, T) β€” EMG channels over time
18
+ Output: (B, C_out=20, T) β€” joint angle predictions over time
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ in_channels: int = 16,
24
+ out_channels: int = 20,
25
+ hidden_size: int = 256,
26
+ num_layers: int = 2,
27
+ dropout: float = 0.1,
28
+ ):
29
+ super().__init__()
30
+ self.lstm = nn.LSTM(
31
+ in_channels,
32
+ hidden_size,
33
+ num_layers,
34
+ batch_first=True,
35
+ dropout=dropout if num_layers > 1 else 0.0,
36
+ )
37
+ self.head = nn.Sequential(
38
+ nn.LeakyReLU(),
39
+ nn.Linear(hidden_size, out_channels),
40
+ )
41
+
42
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
43
+ # x: (B, C=16, T) from dataloader
44
+ x = x.permute(0, 2, 1) # β†’ (B, T, 16)
45
+ out, _ = self.lstm(x) # β†’ (B, T, hidden)
46
+ out = self.head(out) # β†’ (B, T, 20)
47
+ return out.permute(0, 2, 1) # β†’ (B, 20, T) matching target shape
48
+
49
+
50
+ class SequentialEMGPoseLSTM(nn.Module):
51
+ """LSTM that steps through one timestep at a time.
52
+
53
+ Maintains internal hidden state across calls to step().
54
+ Call reset_state() between sequences/trajectories.
55
+
56
+ Use this for streaming inference or autoregressive decoding.
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ in_channels: int = 16,
62
+ out_channels: int = 20,
63
+ hidden_size: int = 256,
64
+ num_layers: int = 2,
65
+ ):
66
+ super().__init__()
67
+ self.hidden_size = hidden_size
68
+ self.num_layers = num_layers
69
+ self.lstm = nn.LSTM(
70
+ in_channels,
71
+ hidden_size,
72
+ num_layers,
73
+ batch_first=True,
74
+ )
75
+ self.head = nn.Sequential(
76
+ nn.LeakyReLU(),
77
+ nn.Linear(hidden_size, out_channels),
78
+ )
79
+ self.hidden: tuple[torch.Tensor, torch.Tensor] | None = None
80
+
81
+ def reset_state(self):
82
+ """Reset hidden state. Call between sequences/trajectories."""
83
+ self.hidden = None
84
+
85
+ def step(self, x: torch.Tensor) -> torch.Tensor:
86
+ """Forward pass for a single timestep.
87
+
88
+ Args:
89
+ x: (B, C_in) β€” EMG channels for one timestep.
90
+
91
+ Returns:
92
+ (B, C_out) β€” joint angle predictions for this timestep.
93
+ """
94
+ if self.hidden is None:
95
+ batch_size = x.size(0)
96
+ device = x.device
97
+ size = (self.num_layers, batch_size, self.hidden_size)
98
+ self.hidden = (
99
+ torch.zeros(*size, device=device),
100
+ torch.zeros(*size, device=device),
101
+ )
102
+
103
+ out, self.hidden = self.lstm(x[:, None], self.hidden) # (B, 1, hidden)
104
+ return self.head(out[:, 0]) # (B, C_out)
105
+
106
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
107
+ """Process a full sequence by stepping through each timestep.
108
+
109
+ Args:
110
+ x: (B, C_in=16, T) β€” EMG channels over time.
111
+
112
+ Returns:
113
+ (B, C_out=20, T) β€” joint angle predictions over time.
114
+ """
115
+ x = x.permute(0, 2, 1) # β†’ (B, T, C_in)
116
+ self.reset_state()
117
+ outputs = []
118
+ for t in range(x.size(1)):
119
+ outputs.append(self.step(x[:, t])) # each is (B, C_out)
120
+ self.reset_state()
121
+ out = torch.stack(outputs, dim=2) # β†’ (B, C_out, T)
122
+ return out