From b5c283e86f18aa653e64d8c3894d8691d318bde7 Mon Sep 17 00:00:00 2001 From: Laurent Mazare Date: Sun, 3 Dec 2023 17:06:09 +0100 Subject: [PATCH] Add the prelu layer. (#1402) --- candle-nn/src/activation.rs | 50 +++++++++++++++++++++++++++++++++++-- candle-nn/src/lib.rs | 2 +- candle-nn/src/linear.rs | 3 ++- 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/candle-nn/src/activation.rs b/candle-nn/src/activation.rs index a2650634..8b9a8785 100644 --- a/candle-nn/src/activation.rs +++ b/candle-nn/src/activation.rs @@ -1,4 +1,4 @@ -use candle::Tensor; +use candle::{Result, Tensor}; use serde::Deserialize; #[derive(Debug, Clone, Copy, PartialEq, Deserialize, Default)] @@ -21,7 +21,7 @@ pub enum Activation { } impl super::Module for Activation { - fn forward(&self, xs: &Tensor) -> candle::Result { + fn forward(&self, xs: &Tensor) -> Result { match self { Self::Gelu => xs.gelu_erf(), // https://github.com/huggingface/transformers/blob/12f043eaeaabfef6f6efea411d98e6f6d3c094b7/src/transformers/activations.py#L49-L78 @@ -40,3 +40,49 @@ impl super::Module for Activation { } } } + +#[derive(Clone, Debug)] +pub struct PReLU { + weight: Tensor, + is_scalar: bool, +} + +impl PReLU { + pub fn new(weight: Tensor, is_scalar: bool) -> Self { + Self { weight, is_scalar } + } + + pub fn weight(&self) -> &Tensor { + &self.weight + } + + pub fn is_scalar(&self) -> bool { + self.is_scalar + } +} + +impl candle::Module for PReLU { + fn forward(&self, xs: &Tensor) -> Result { + let weight = if self.is_scalar { + self.weight.reshape(())? + } else { + self.weight.clone() + }; + let zeros = xs.zeros_like()?; + xs.maximum(&zeros)? + xs.minimum(&zeros)?.broadcast_mul(&weight)? + } +} + +/// Create or initialize a new PReLU layer. +/// +/// This uses some default name for weights, namely `"weight"`. +/// # Arguments +/// +/// * `num_parameters` - The number of parameters. Use `None` to have as single trainable value +/// and `Some` for a 1D vector with the appropriate number of features. +pub fn prelu(num_parameters: Option, vs: crate::VarBuilder) -> Result { + let init_ws = crate::init::Init::Const(0.25); + // When using a scalar weight, the PyTorch encoding is to use a 1d vector of length 1. + let ws = vs.get_with_hints((num_parameters.unwrap_or(1),), "weight", init_ws)?; + Ok(PReLU::new(ws, num_parameters.is_none())) +} diff --git a/candle-nn/src/lib.rs b/candle-nn/src/lib.rs index 52d8f0c5..8f00e54c 100644 --- a/candle-nn/src/lib.rs +++ b/candle-nn/src/lib.rs @@ -15,7 +15,7 @@ pub mod sequential; pub mod var_builder; pub mod var_map; -pub use activation::Activation; +pub use activation::{prelu, Activation, PReLU}; pub use batch_norm::{batch_norm, BatchNorm, BatchNormConfig}; pub use conv::{ conv1d, conv2d, conv2d_no_bias, conv_transpose2d, conv_transpose2d_no_bias, Conv1d, diff --git a/candle-nn/src/linear.rs b/candle-nn/src/linear.rs index 94632296..59a4db8a 100644 --- a/candle-nn/src/linear.rs +++ b/candle-nn/src/linear.rs @@ -56,7 +56,7 @@ impl super::Module for Linear { /// Create or initialize a new linear layer. /// -/// This uses some default names for weight and biases, namely `"weight"` and `"bias"`. +/// This uses some default names for weights and biases, namely `"weight"` and `"bias"`. pub fn linear(in_dim: usize, out_dim: usize, vs: crate::VarBuilder) -> Result { let init_ws = crate::init::DEFAULT_KAIMING_NORMAL; let ws = vs.get_with_hints((out_dim, in_dim), "weight", init_ws)?; @@ -69,6 +69,7 @@ pub fn linear(in_dim: usize, out_dim: usize, vs: crate::VarBuilder) -> Result
  • Result { let init_ws = crate::init::DEFAULT_KAIMING_NORMAL; let ws = vs.get_with_hints((out_dim, in_dim), "weight", init_ws)?;