krupal02's picture
Deploy Multi-Hazard Warning System - MTL model for wildfire risk + AQI forecasting
d5b0af1
Raw
History Blame Contribute Delete
4.9 kB
"""
lstm_branch.py — Bidirectional LSTM temporal encoder for weather + AQI data.
Processes 7-day sequences of weather and air quality features into
a compact temporal representation. Uses a 2-layer bidirectional LSTM
to capture both forward and backward temporal dependencies.
"""
import logging
import torch
import torch.nn as nn
from src.training.config import (
TIMESERIES_FEATURES, TIMESERIES_WINDOW,
LSTM_HIDDEN_SIZE, LSTM_NUM_LAYERS, LSTM_BIDIRECTIONAL, LSTM_FEATURE_DIM,
)
logger = logging.getLogger(__name__)
class LSTMBranch(nn.Module):
"""
Bidirectional LSTM encoder for temporal weather/AQI sequences.
Architecture:
Input: (batch, 7, 6) — 7 days × 6 features
Output: (batch, 256) — temporal feature vector
Features: [temperature, humidity, wind_speed, wind_direction,
precipitation, PM2.5]
"""
def __init__(
self,
input_size: int = TIMESERIES_FEATURES,
hidden_size: int = LSTM_HIDDEN_SIZE,
num_layers: int = LSTM_NUM_LAYERS,
bidirectional: bool = LSTM_BIDIRECTIONAL,
dropout: float = 0.2,
):
"""
Args:
input_size: Number of features per timestep (6).
hidden_size: LSTM hidden state size per direction (128).
num_layers: Number of stacked LSTM layers (2).
bidirectional: Whether to use bidirectional LSTM.
dropout: Dropout between LSTM layers.
"""
super().__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
self.bidirectional = bidirectional
self.num_directions = 2 if bidirectional else 1
# Input projection layer for better feature learning
self.input_proj = nn.Sequential(
nn.Linear(input_size, hidden_size),
nn.LayerNorm(hidden_size),
nn.ReLU(),
nn.Dropout(dropout),
)
# Bidirectional LSTM
self.lstm = nn.LSTM(
input_size=hidden_size,
hidden_size=hidden_size,
num_layers=num_layers,
batch_first=True,
bidirectional=bidirectional,
dropout=dropout if num_layers > 1 else 0,
)
# Attention mechanism to weight timesteps
self.attention = nn.Sequential(
nn.Linear(hidden_size * self.num_directions, hidden_size),
nn.Tanh(),
nn.Linear(hidden_size, 1),
)
# Output feature dimension
self.out_features = hidden_size * self.num_directions # 256
# Final projection
self.output_proj = nn.Sequential(
nn.Linear(self.out_features, self.out_features),
nn.LayerNorm(self.out_features),
nn.ReLU(),
)
logger.info(
f"LSTMBranch initialized: {input_size}{self.out_features}d, "
f"{num_layers} layers, {'bi' if bidirectional else 'uni'}directional"
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Forward pass through LSTM encoder.
Args:
x: Input tensor of shape (batch, seq_len, features).
Returns:
Temporal feature vector of shape (batch, 256).
"""
batch_size = x.size(0)
# Project input features
x = self.input_proj(x) # (batch, seq_len, hidden_size)
# LSTM encoding
lstm_out, (h_n, c_n) = self.lstm(x)
# lstm_out: (batch, seq_len, hidden_size * num_directions)
# Attention-weighted pooling over timesteps
attn_weights = self.attention(lstm_out) # (batch, seq_len, 1)
attn_weights = torch.softmax(attn_weights, dim=1)
context = torch.sum(lstm_out * attn_weights, dim=1) # (batch, 256)
# Final projection
output = self.output_proj(context) # (batch, 256)
return output
def get_attention_weights(self, x: torch.Tensor) -> torch.Tensor:
"""
Get attention weights for interpretability.
Returns:
Attention weights of shape (batch, seq_len).
"""
x = self.input_proj(x)
lstm_out, _ = self.lstm(x)
attn_weights = self.attention(lstm_out).squeeze(-1)
attn_weights = torch.softmax(attn_weights, dim=1)
return attn_weights
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
model = LSTMBranch()
# Test forward pass
dummy = torch.randn(4, 7, 6) # 4 samples, 7 days, 6 features
output = model(dummy)
print(f"Input: {dummy.shape} → Output: {output.shape}")
# Test attention weights
attn = model.get_attention_weights(dummy)
print(f"Attention weights: {attn.shape}")
print(f"Attention values: {attn[0].detach().numpy()}")
# Parameter count
total = sum(p.numel() for p in model.parameters())
print(f"Parameters: {total:,}")