bbkdevops commited on
Commit
3a2687e
·
verified ·
1 Parent(s): 83559d3

Add model artifact tcn_attention_net.py

Browse files
Files changed (1) hide show
  1. tcn_attention_net.py +293 -0
tcn_attention_net.py ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # sca_core/tcn_attention_net.py
2
+ """
3
+ Sovereign TCNAttentionSCA - Advanced Neural Side-Channel Analysis Architecture.
4
+ Combines Dilated Temporal Convolutional Networks with Multi-Head Self-Attention,
5
+ Multi-Channel Waveform Support, Temporal Point-of-Interest (POI) Localization,
6
+ and Calibrated Cryptanalytic Posterior Estimation.
7
+ """
8
+ import math
9
+ import os
10
+ import warnings
11
+ import torch
12
+ import torch.nn as nn
13
+ import torch.nn.functional as F
14
+ from typing import Optional, List, Tuple, Union, Dict, Any
15
+
16
+ class Chomp1d(nn.Module):
17
+ """Maintains sequence length in causal convolutions by trimming future padding."""
18
+ def __init__(self, chomp_size: int):
19
+ super(Chomp1d, self).__init__()
20
+ self.chomp = chomp_size
21
+
22
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
23
+ if self.chomp == 0:
24
+ return x
25
+ return x[:, :, :-self.chomp].contiguous()
26
+
27
+ class TemporalBlock(nn.Module):
28
+ """
29
+ Dilated Residual Temporal Convolutional Block.
30
+ Supports both Causal (streaming) and Symmetric (offline forensic) receptive fields.
31
+ """
32
+ def __init__(
33
+ self,
34
+ n_inputs: int,
35
+ n_outputs: int,
36
+ kernel_size: int,
37
+ stride: int,
38
+ dilation: int,
39
+ padding: int,
40
+ dropout: float = 0.2,
41
+ causal: bool = True
42
+ ):
43
+ super(TemporalBlock, self).__init__()
44
+ self.causal = causal
45
+ self.conv1 = nn.Conv1d(n_inputs, n_outputs, kernel_size, stride=stride, padding=padding, dilation=dilation)
46
+ self.chomp1 = Chomp1d(padding) if causal else nn.Identity()
47
+ self.relu1 = nn.ReLU()
48
+ self.dropout1 = nn.Dropout(dropout)
49
+
50
+ self.conv2 = nn.Conv1d(n_outputs, n_outputs, kernel_size, stride=stride, padding=padding, dilation=dilation)
51
+ self.chomp2 = Chomp1d(padding) if causal else nn.Identity()
52
+ self.relu2 = nn.ReLU()
53
+ self.dropout2 = nn.Dropout(dropout)
54
+
55
+ self.net = nn.Sequential(
56
+ self.conv1, self.chomp1, self.relu1, self.dropout1,
57
+ self.conv2, self.chomp2, self.relu2, self.dropout2
58
+ )
59
+ self.downsample = nn.Conv1d(n_inputs, n_outputs, 1) if n_inputs != n_outputs else None
60
+ self.relu = nn.ReLU()
61
+ self.init_weights()
62
+
63
+ def init_weights(self):
64
+ self.conv1.weight.data.normal_(0, 0.01)
65
+ self.conv2.weight.data.normal_(0, 0.01)
66
+ if self.downsample is not None:
67
+ self.downsample.weight.data.normal_(0, 0.01)
68
+
69
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
70
+ out = self.net(x)
71
+ res = x if self.downsample is None else self.downsample(x)
72
+ return self.relu(out + res)
73
+
74
+ class TCNAttentionSCA(nn.Module):
75
+ """
76
+ Production-grade Sovereign Neural Side-Channel Analyzer (SOTA Enhanced).
77
+
78
+ Key Innovations & Enhancements:
79
+ 1. Multi-Channel Waveform Support (Power + Clock + PMU + EM sensors).
80
+ 2. Multi-Head Self-Attention for Point-of-Interest (POI) Leakage Extraction.
81
+ 3. Bidirectional/Symmetric or Causal Temporal Dilation Modes.
82
+ 4. Optional Residual Attention & Layer Normalization.
83
+ 5. POI Attention Heatmap Extraction for Explainable Cryptanalysis.
84
+ 6. Temperature-Calibrated Posterior & Entropy Estimation.
85
+ 7. 100% Backwards-Compatible with Legacy Checkpoints.
86
+ """
87
+ def __init__(
88
+ self,
89
+ trace_length: int,
90
+ num_classes: int = 256,
91
+ in_channels: int = 1,
92
+ num_channels: Optional[List[int]] = None,
93
+ kernel_size: int = 5,
94
+ num_heads: int = 4,
95
+ dropout: float = 0.2,
96
+ causal: bool = True,
97
+ residual_attention: bool = False,
98
+ use_layer_norm: bool = False
99
+ ):
100
+ super(TCNAttentionSCA, self).__init__()
101
+ self.trace_length = trace_length
102
+ self.num_classes = num_classes
103
+ self.in_channels = in_channels
104
+ self.causal = causal
105
+ self.residual_attention = residual_attention
106
+ self.use_layer_norm = use_layer_norm
107
+
108
+ if num_channels is None:
109
+ num_channels = [64, 128, 64]
110
+ self.num_channels = num_channels
111
+
112
+ layers = []
113
+ num_levels = len(num_channels)
114
+ for i in range(num_levels):
115
+ dilation_size = 2 ** i
116
+ in_ch = in_channels if i == 0 else num_channels[i-1]
117
+ out_ch = num_channels[i]
118
+
119
+ if causal:
120
+ pad = (kernel_size - 1) * dilation_size
121
+ else:
122
+ pad = ((kernel_size - 1) * dilation_size) // 2
123
+
124
+ layers += [TemporalBlock(
125
+ n_inputs=in_ch,
126
+ n_outputs=out_ch,
127
+ kernel_size=kernel_size,
128
+ stride=1,
129
+ dilation=dilation_size,
130
+ padding=pad,
131
+ dropout=dropout,
132
+ causal=causal
133
+ )]
134
+ self.network = nn.Sequential(*layers)
135
+
136
+ # Multi-Head Self Attention over the temporal dimension
137
+ self.attention = nn.MultiheadAttention(
138
+ embed_dim=num_channels[-1],
139
+ num_heads=num_heads,
140
+ batch_first=True
141
+ )
142
+
143
+ # Optional LayerNorm (only instantiated if requested to preserve legacy state_dict)
144
+ if use_layer_norm:
145
+ self.norm = nn.LayerNorm(num_channels[-1])
146
+ else:
147
+ self.norm = None
148
+
149
+ # Linear Classifier Head
150
+ self.fc = nn.Linear(num_channels[-1], num_classes)
151
+
152
+ def forward(
153
+ self,
154
+ x: torch.Tensor,
155
+ return_attention: bool = False,
156
+ return_features: bool = False
157
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, ...]]:
158
+ """
159
+ Forward propagation.
160
+
161
+ Args:
162
+ x: Input tensor. Accepts:
163
+ - 2D: (batch_size, trace_length)
164
+ - 3D: (batch_size, in_channels, trace_length)
165
+ return_attention: If True, returns attention weights matrix.
166
+ return_features: If True, returns pooled temporal embeddings.
167
+
168
+ Returns:
169
+ Logits (batch_size, num_classes) by default, or tuple if extra returns requested.
170
+ """
171
+ # Format input to (batch_size, in_channels, trace_length)
172
+ if not torch.jit.is_tracing() and not torch.jit.is_scripting():
173
+ if x.dim() == 2:
174
+ x = x.unsqueeze(1)
175
+ elif x.dim() == 3 and x.shape[1] != self.in_channels and x.shape[2] == self.in_channels:
176
+ x = x.permute(0, 2, 1)
177
+ else:
178
+ if x.dim() == 2:
179
+ x = x.unsqueeze(1)
180
+
181
+ # 1. TCN Hierarchical Feature Extraction: (batch_size, channels, seq_len)
182
+ tcn_out = self.network(x)
183
+
184
+ # 2. Permute for Temporal Multi-Head Attention: (batch_size, seq_len, channels)
185
+ tcn_perm = tcn_out.permute(0, 2, 1)
186
+
187
+ # 3. Multi-Head Self-Attention
188
+ attn_out, attn_weights = self.attention(
189
+ tcn_perm, tcn_perm, tcn_perm,
190
+ need_weights=return_attention,
191
+ average_attn_weights=True
192
+ )
193
+
194
+ # 4. Optional Residual Connection and LayerNorm
195
+ if self.residual_attention:
196
+ attended = tcn_perm + attn_out
197
+ else:
198
+ attended = attn_out
199
+
200
+ if self.norm is not None:
201
+ attended = self.norm(attended)
202
+
203
+ # 5. Global Temporal Pooling
204
+ pooled = torch.mean(attended, dim=1)
205
+
206
+ # 6. Classification Logits
207
+ logits = self.fc(pooled)
208
+
209
+ if return_attention and return_features:
210
+ return logits, pooled, attn_weights
211
+ elif return_attention:
212
+ return logits, attn_weights
213
+ elif return_features:
214
+ return logits, pooled
215
+ return logits
216
+
217
+ @torch.no_grad()
218
+ def predict_posteriors(self, x: torch.Tensor, temperature: float = 1.0) -> Dict[str, Any]:
219
+ """
220
+ Computes calibrated Bayesian class posteriors, top candidate, and Shannon entropy.
221
+ """
222
+ self.eval()
223
+ logits = self.forward(x)
224
+ scaled_logits = logits / max(temperature, 1e-4)
225
+ probs = F.softmax(scaled_logits, dim=-1)
226
+
227
+ top1_idx = torch.argmax(probs, dim=-1)
228
+ top1_prob = torch.gather(probs, 1, top1_idx.unsqueeze(-1)).squeeze(-1)
229
+
230
+ # Shannon Entropy H(P) = -sum(p * log2(p))
231
+ entropy = -torch.sum(probs * torch.log2(probs + 1e-12), dim=-1)
232
+
233
+ return {
234
+ "probabilities": probs.cpu().numpy(),
235
+ "predicted_classes": top1_idx.cpu().numpy(),
236
+ "confidence": top1_prob.cpu().numpy(),
237
+ "entropy_bits": entropy.cpu().numpy()
238
+ }
239
+
240
+ @torch.no_grad()
241
+ def extract_poi(self, x: torch.Tensor, top_k: int = 10) -> Dict[str, Any]:
242
+ """
243
+ Extracts Points-of-Interest (POI) temporal leakage heatmap from the attention matrix.
244
+ Returns top-k sample indices with highest attention energy.
245
+ """
246
+ self.eval()
247
+ _, attn_weights = self.forward(x, return_attention=True)
248
+ # attn_weights: (batch_size, seq_len, seq_len)
249
+ temporal_energy = torch.mean(attn_weights, dim=1) # (batch_size, seq_len)
250
+ mean_poi_curve = torch.mean(temporal_energy, dim=0).cpu().numpy()
251
+
252
+ top_indices = torch.topk(torch.tensor(mean_poi_curve), k=min(top_k, len(mean_poi_curve))).indices.tolist()
253
+
254
+ return {
255
+ "mean_temporal_energy": mean_poi_curve.tolist(),
256
+ "top_poi_indices": top_indices,
257
+ "peak_leakage_cycle": int(top_indices[0]) if top_indices else 0
258
+ }
259
+
260
+ def export_torchscript(self, filepath: str, example_input: Optional[torch.Tensor] = None, check_trace: bool = False):
261
+ """Exports the model to standalone TorchScript format."""
262
+ self.eval()
263
+ if example_input is None:
264
+ example_input = torch.randn(1, self.in_channels, self.trace_length)
265
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
266
+ with warnings.catch_warnings():
267
+ warnings.simplefilter("ignore", category=FutureWarning)
268
+ try:
269
+ warnings.simplefilter("ignore", category=torch.jit.TracerWarning)
270
+ except Exception:
271
+ pass
272
+ traced = torch.jit.trace(self, example_input, check_trace=check_trace)
273
+ traced.save(filepath)
274
+ return filepath
275
+
276
+ def export_onnx(self, filepath: str, example_input: Optional[torch.Tensor] = None):
277
+ """Exports the model to ONNX format for hardware accelerator compilation."""
278
+ self.eval()
279
+ if example_input is None:
280
+ example_input = torch.randn(1, self.in_channels, self.trace_length)
281
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
282
+ with warnings.catch_warnings():
283
+ warnings.simplefilter("ignore")
284
+ torch.onnx.export(
285
+ self,
286
+ example_input,
287
+ filepath,
288
+ input_names=["trace_waveform"],
289
+ output_names=["logits"],
290
+ dynamic_axes={"trace_waveform": {0: "batch_size"}, "logits": {0: "batch_size"}},
291
+ opset_version=18
292
+ )
293
+ return filepath