MorganBrizon commited on
Commit
2c61639
·
verified ·
1 Parent(s): 2bda06c

Delete EpilepsyNet_model.py

Browse files
Files changed (1) hide show
  1. EpilepsyNet_model.py +0 -278
EpilepsyNet_model.py DELETED
@@ -1,278 +0,0 @@
1
- import numpy as np
2
- import torch
3
- import torch.nn as nn
4
- import torch.optim as optim
5
-
6
-
7
-
8
- def extract_upper_triangle(corr_matrices):
9
- """
10
- Extract upper triangles from correlation matrices
11
-
12
- Args:
13
- corr_matrices: numpy array of shape (n_segments, n_channels, n_channels)
14
-
15
- Returns:
16
- numpy array of shape (n_segments, n_features) where n_features = n_channels*(n_channels-1)/2
17
- """
18
- n_segments, n_channels, _ = corr_matrices.shape
19
- n_features = n_channels * (n_channels - 1) // 2
20
-
21
- flattened = np.zeros((n_segments, n_features))
22
-
23
- for i in range(n_segments):
24
- # Get upper triangle indices (excluding diagonal)
25
- upper_indices = np.triu_indices(n_channels, k=1)
26
- # Extract values
27
- flattened[i] = corr_matrices[i][upper_indices]
28
-
29
- return flattened
30
-
31
- class MultiHeadAttention(nn.Module):
32
- def __init__(self, embed_dim, num_heads, dropout=0.3):
33
- super(MultiHeadAttention, self).__init__()
34
- self.embed_dim = embed_dim
35
- self.num_heads = num_heads
36
- self.head_dim = embed_dim // num_heads
37
- assert self.head_dim * num_heads == embed_dim, "embed_dim must be divisible by num_heads"
38
-
39
- # Linear projections for Q, K, V
40
- self.q_proj = nn.Linear(embed_dim, embed_dim)
41
- self.k_proj = nn.Linear(embed_dim, embed_dim)
42
- self.v_proj = nn.Linear(embed_dim, embed_dim)
43
-
44
- # Final projection after concatenating heads
45
- self.out_proj = nn.Linear(embed_dim, embed_dim)
46
-
47
- # Dropout
48
- self.dropout = nn.Dropout(dropout)
49
-
50
- # Softmax for attention weights
51
- self.softmax = nn.Softmax(dim=-1)
52
-
53
- def forward(self, x, mask=None):
54
- batch_size = x.size(0)
55
-
56
- # Project Q, K, V
57
- Q = self.q_proj(x) # (batch_size, seq_len, embed_dim)
58
- K = self.k_proj(x) # (batch_size, seq_len, embed_dim)
59
- V = self.v_proj(x) # (batch_size, seq_len, embed_dim)
60
-
61
- # Split into multiple heads
62
- Q = Q.view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2) # (batch_size, num_heads, seq_len, head_dim)
63
- K = K.view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2) # (batch_size, num_heads, seq_len, head_dim)
64
- V = V.view(batch_size, -1, self.num_heads, self.head_dim).transpose(1, 2) # (batch_size, num_heads, seq_len, head_dim)
65
-
66
- # Calculate attention scores
67
- scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.head_dim ** 0.5) # (batch_size, num_heads, seq_len, seq_len)
68
-
69
- # Apply mask (if provided)
70
- if mask is not None:
71
- scores = scores.masked_fill(mask == 0, float('-inf'))
72
-
73
- # Apply softmax to get attention weights
74
- attn_weights = self.softmax(scores) # (batch_size, num_heads, seq_len, seq_len)
75
- attn_weights = self.dropout(attn_weights)
76
-
77
- # Calculate weighted output
78
- attn_output = torch.matmul(attn_weights, V) # (batch_size, num_heads, seq_len, head_dim)
79
-
80
- # Recompose heads
81
- attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, -1, self.embed_dim) # (batch_size, seq_len, embed_dim)
82
-
83
- # Pass through final projection
84
- output = self.out_proj(attn_output) # (batch_size, seq_len, embed_dim)
85
-
86
- return output, attn_weights
87
-
88
- class PositionalEncoding(nn.Module):
89
- def __init__(self, embed_dim, max_seq_length=100):
90
- super(PositionalEncoding, self).__init__()
91
-
92
- # Create positional encoding matrix
93
- pe = torch.zeros(max_seq_length, embed_dim)
94
- position = torch.arange(0, max_seq_length, dtype=torch.float).unsqueeze(1)
95
- div_term = torch.exp(torch.arange(0, embed_dim, 2).float() * (-np.log(10000.0) / embed_dim))
96
-
97
- pe[:, 0::2] = torch.sin(position * div_term)
98
- pe[:, 1::2] = torch.cos(position * div_term)
99
-
100
- # Register as buffer (not a parameter)
101
- self.register_buffer('pe', pe.unsqueeze(0))
102
-
103
- def forward(self, x):
104
- # Add positional encoding to input
105
- # x: [batch_size, seq_len, embed_dim]
106
- return x + self.pe[:, :x.size(1)]
107
-
108
- class TimeSeriesAttentionClassifier(nn.Module):
109
- def __init__(self, input_dim, embed_dim, num_heads, num_classes=2, dropout=0.2):
110
- super(TimeSeriesAttentionClassifier, self).__init__()
111
-
112
- # Project flattened correlation features to embedding space
113
- self.embedding = nn.Linear(input_dim, embed_dim)
114
-
115
- # Positional encoding
116
- self.pos_encoding = PositionalEncoding(embed_dim)
117
-
118
- # Multi-head attention
119
- self.attention = MultiHeadAttention(embed_dim, num_heads, dropout)
120
-
121
- # Layer normalization
122
- self.layer_norm1 = nn.LayerNorm(embed_dim)
123
- self.layer_norm2 = nn.LayerNorm(embed_dim)
124
-
125
- # Feed-forward network
126
- self.ffn = nn.Sequential(
127
- nn.Linear(embed_dim, embed_dim * 4),
128
- nn.GELU(),
129
- nn.Dropout(dropout),
130
- nn.Linear(embed_dim * 4, embed_dim)
131
- )
132
-
133
- # Output layer
134
- self.classifier = nn.Sequential(
135
- nn.Linear(embed_dim, embed_dim // 2),
136
- nn.GELU(),
137
- nn.Dropout(dropout),
138
- nn.Linear(embed_dim // 2, 1),
139
- nn.Sigmoid()
140
- )
141
-
142
- def forward(self, x):
143
- # batch_size, seq_len, input_dim = x.shape
144
-
145
- # Project to embedding space
146
- x = self.embedding(x)
147
-
148
- # Add positional encoding
149
- x = self.pos_encoding(x)
150
-
151
- # Self-attention (use x for query, key, and value)
152
- residual = x
153
- x, attention_weights = self.attention(x)
154
- x = self.layer_norm1(x + residual)
155
-
156
- # Feed-forward network with residual connection
157
- residual = x
158
- x = self.ffn(x)
159
- x = self.layer_norm2(x + residual)
160
-
161
- # Global average pooling over sequence dimension
162
- x = torch.mean(x, dim=1)
163
-
164
- # Classification
165
- logits = self.classifier(x)
166
-
167
- return logits, attention_weights
168
-
169
- def train_model(model, train_loader, val_loader, num_epochs=50, learning_rate=1e-4, weight_decay=1e-5, patience=10, scheduler_factor=0.5, min_lr=1e-6):
170
- device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
171
- model = model.to(device)
172
-
173
- # Changed from CrossEntropyLoss to BCELoss for binary classification with sigmoid
174
- criterion = nn.BCELoss()
175
- # optimizer = optim.Adam(model.parameters(), lr=learning_rate)
176
-
177
- # Add L2 regularization through weight_decay parameter in Adam
178
- optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=weight_decay)
179
-
180
- # Learning rate scheduler - reduce LR when validation loss plateaus
181
- scheduler = optim.lr_scheduler.ReduceLROnPlateau(
182
- optimizer,
183
- mode='min',
184
- factor=scheduler_factor,
185
- patience=patience,
186
- verbose=True,
187
- min_lr=min_lr
188
- )
189
-
190
- train_losses = []
191
- val_losses = []
192
- val_accuracies = []
193
-
194
-
195
- # Track best model and early stopping
196
- best_val_loss = float('inf')
197
- best_model_state = None
198
- early_stop_counter = 0
199
- early_stop_patience = patience * 2 # Stop after 2x the scheduler patience
200
-
201
- for epoch in range(num_epochs):
202
- # Training
203
- model.train()
204
- train_loss = 0.0
205
- for inputs, labels in train_loader:
206
- inputs, labels = inputs.to(device), labels.to(device)
207
-
208
- # Convert labels to float and reshape for BCE loss
209
- labels = labels.float().view(-1, 1)
210
-
211
- optimizer.zero_grad()
212
- outputs, _ = model(inputs)
213
- loss = criterion(outputs, labels)
214
- loss.backward()
215
- optimizer.step()
216
- train_loss += loss.item()
217
-
218
- train_loss /= len(train_loader)
219
- train_losses.append(train_loss)
220
-
221
- # Validation
222
- model.eval()
223
- val_loss = 0.0
224
- correct = 0
225
- total = 0
226
-
227
- with torch.no_grad():
228
- for inputs, labels in val_loader:
229
- inputs, labels = inputs.to(device), labels.to(device)
230
- # Convert labels to float and reshape for BCE loss
231
- labels = labels.float().view(-1, 1)
232
-
233
- outputs, _ = model(inputs)
234
- loss = criterion(outputs, labels)
235
- val_loss += loss.item()
236
-
237
- # For binary classification with sigmoid, prediction is 1 if output > 0.5
238
- predicted = (outputs > 0.5).float()
239
- total += labels.size(0)
240
- correct += (predicted == labels).sum().item()
241
-
242
- val_loss /= len(val_loader)
243
- val_losses.append(val_loss)
244
-
245
- accuracy = 100 * correct / total
246
- val_accuracies.append(accuracy)
247
-
248
- # Learning rate scheduler step based on validation loss
249
- scheduler.step(val_loss)
250
-
251
- # Print current learning rate
252
- current_lr = optimizer.param_groups[0]['lr']
253
-
254
-
255
- # Print epoch results
256
- print(f'Epoch {epoch+1}/{num_epochs}, LR: {current_lr:.6f}, Train Loss: {train_loss:.4f}, '
257
- f'Val Loss: {val_loss:.4f}, Val Accuracy: {accuracy:.2f}%')
258
-
259
- # Save best model
260
- if val_loss < best_val_loss:
261
- best_val_loss = val_loss
262
- best_model_state = model.state_dict().copy()
263
- early_stop_counter = 0
264
- else:
265
- early_stop_counter += 1
266
-
267
- # Early stopping
268
- if early_stop_counter >= early_stop_patience:
269
- print(f"Early stopping triggered after {epoch+1} epochs")
270
- break
271
-
272
- # Load best model weights
273
- if best_model_state is not None:
274
- model.load_state_dict(best_model_state)
275
- print(f"Loaded best model with validation loss: {best_val_loss:.4f}")
276
-
277
- return train_losses, val_losses, val_accuracies
278
-