use crate::constants::{DEFAULT_DROPOUT, L2_REGULARIZATION};
use crate::minute::cnnlstm::step_2_cnn_lstm_cell::CNNLSTM;
use burn::module::Module;
use burn::nn::{Dropout, DropoutConfig, Linear, LinearConfig};
use burn::prelude::Backend;
use burn::tensor::{activation, Tensor};
#[derive(Module, Debug)]
pub struct TimeSeriesCnnLstm<B: Backend> {
input_size: usize,
hidden_size: usize,
output_size: usize,
dropout: Dropout,
output: Linear<B>,
cnn_lstm: CNNLSTM<B>,
regularization: f64,
}
impl<B: Backend> TimeSeriesCnnLstm<B> {
pub fn new(
input_size: usize,
hidden_size: usize,
output_size: usize,
num_layers: usize,
bidirectional: bool,
dropout_prob: f64,
device: &B::Device,
) -> Self {
let dropout_prob = if dropout_prob <= 0.0 {
DEFAULT_DROPOUT
} else {
dropout_prob
};
let lstm_output_size = if bidirectional {
2 * hidden_size
} else {
hidden_size
};
let dropout_config = DropoutConfig::new(dropout_prob);
let dropout = dropout_config.init();
let output_config = LinearConfig::new(lstm_output_size, output_size);
let output = output_config.init(device);
let cnn_lstm = CNNLSTM::new(input_size, hidden_size, num_layers, bidirectional, device);
Self {
input_size,
hidden_size,
output_size,
dropout,
output,
cnn_lstm,
regularization: L2_REGULARIZATION,
}
}
pub fn input_size(&self) -> usize {
self.input_size
}
pub fn regularization(&self) -> f64 {
self.regularization
}
pub fn forward(&self, x: Tensor<B, 3>) -> Tensor<B, 2> {
let cnn_lstm_out = self.cnn_lstm.forward(x);
let batch_size = cnn_lstm_out.dims()[0];
let last_step_idx = cnn_lstm_out.dims()[1] - 1;
let lstm_output_size = cnn_lstm_out.dims()[2];
let pooled = cnn_lstm_out
.narrow(1, last_step_idx, 1)
.reshape([batch_size, lstm_output_size]);
let dropped = self.dropout.forward(pooled);
let output = self.output.forward(dropped);
output.clamp(0.0, 1.0)
}
pub fn l2_penalty(&self) -> Tensor<B, 1> {
let device = &self.output.weight.device();
let mut squared_sum = Tensor::zeros([1], device);
let output_weights = self.output.weight.val().clone();
let weight_squared = output_weights.clone() * output_weights;
squared_sum = squared_sum + weight_squared.sum();
squared_sum * self.regularization
}
pub fn huber_loss(
&self,
pred: Tensor<B, 2>,
target: Tensor<B, 2>,
_delta: f64,
) -> Tensor<B, 1> {
let diff = pred - target;
let squared_diff = diff.clone() * diff;
let mse = squared_diff.mean().reshape([1]);
if self.regularization > 0.0 {
let weight_squared =
self.output.weight.val().clone() * self.output.weight.val().clone();
let l2_penalty = (weight_squared.sum() * self.regularization).reshape([1]);
mse + l2_penalty
} else {
mse
}
}
pub fn mse_loss(&self, pred: Tensor<B, 2>, target: Tensor<B, 2>) -> Tensor<B, 1> {
let diff = pred - target;
let squared_diff = diff.clone() * diff;
let mse = squared_diff.mean().reshape([1]);
if self.regularization > 0.0 {
let weight_squared =
self.output.weight.val().clone() * self.output.weight.val().clone();
let l2_penalty = (weight_squared.sum() * self.regularization).reshape([1]);
mse + l2_penalty
} else {
mse
}
}
}