scorch/activation.rs
1/// `ActivationFunction` is a type alias for a function that takes a slice of `f32` values
2/// (the input to the activation function) and returns a `Vec<f32>` (the output of the activation function).
3/// This type alias is used to define various activation functions in the neural network.
4pub type ActivationFunction = fn(&[f32]) -> Vec<f32>;
5
6/// `sigmoid` is an activation function that maps the input values to a range between 0 and 1,
7/// making it useful for binary classification tasks. The function outputs values close to 0 for negative inputs,
8/// and values close to 1 for positive inputs. It is commonly used in the output layer of neural networks for classification tasks.
9///
10/// # Arguments
11/// * `input` - A slice of `f32` values representing the input to the Sigmoid function.
12///
13/// # Returns
14/// * A `Vec<f32>` where each element is the result of applying the Sigmoid function to the corresponding input value.
15///
16/// # Example
17/// ```
18/// let input = vec![-1.0, 0.0, 1.0];
19/// let output = sigmoid(&input);
20/// assert_eq!(output, vec![0.26894142, 0.5, 0.7310586]);
21/// ```
22pub fn sigmoid(input: &[f32]) -> Vec<f32> {
23 input.iter().map(|&x| 1.0 / (1.0 + f32::exp(-x))).collect()
24}
25
26/// `sigmoid_derivative` computes the derivative of the Sigmoid function. It is often used
27/// during backpropagation in neural networks for optimization.
28///
29/// # Arguments
30/// * `input` - A slice of `f32` values representing the input to the Sigmoid function.
31///
32/// # Returns
33/// * A `Vec<f32>` where each element is the gradient (derivative) of the Sigmoid function with respect to the input value.
34///
35/// # Example
36/// ```
37/// let input = vec![-1.0, 0.0, 1.0];
38/// let output = sigmoid_derivative(&input);
39/// assert_eq!(output, vec![0.19661193, 0.25, 0.19661193]);
40/// ```
41pub fn sigmoid_derivative(input: &[f32]) -> Vec<f32> {
42 input
43 .iter()
44 .map(|&x| {
45 let sig = 1.0 / (1.0 + f32::exp(-x));
46 sig * (1.0 - sig)
47 })
48 .collect()
49}
50
51/// `tanh` is the hyperbolic tangent activation function, which maps input values to a range between -1 and 1.
52/// It is commonly used in the hidden layers of neural networks, as it helps mitigate the vanishing gradient problem.
53///
54/// # Arguments
55/// * `input` - A slice of `f32` values representing the input to the Tanh function.
56///
57/// # Returns
58/// * A `Vec<f32>` where each element is the result of applying the Tanh function to the corresponding input value.
59///
60/// # Example
61/// ```
62/// let input = vec![-1.0, 0.0, 1.0];
63/// let output = tanh(&input);
64/// assert_eq!(output, vec![-0.7615942, 0.0, 0.7615942]);
65/// ```
66pub fn tanh(input: &[f32]) -> Vec<f32> {
67 input.iter().map(|&x| f32::tanh(x)).collect()
68}
69
70/// `tanh_derivative` computes the derivative of the Tanh function. It is used during backpropagation to update the weights.
71///
72/// # Arguments
73/// * `input` - A slice of `f32` values representing the input to the Tanh function.
74///
75/// # Returns
76/// * A `Vec<f32>` where each element is the gradient (derivative) of the Tanh function with respect to the input value.
77///
78/// # Example
79/// ```
80/// let input = vec![-1.0, 0.0, 1.0];
81/// let output = tanh_derivative(&input);
82/// assert_eq!(output, vec![0.41997434, 1.0, 0.41997434]);
83/// ```
84pub fn tanh_derivative(input: &[f32]) -> Vec<f32> {
85 input
86 .iter()
87 .map(|&x| {
88 let tanh_val = f32::tanh(x);
89 1.0 - tanh_val.powi(2)
90 })
91 .collect()
92}
93
94/// `softmax` is an activation function that converts a vector of raw input values (often called logits)
95/// into a probability distribution, where the sum of the output values equals 1.0. It is commonly used
96/// in the output layer of neural networks for classification tasks.
97///
98/// # Arguments
99/// * `input` - A slice of `f32` values representing the input to the softmax function (often logits).
100///
101/// # Returns
102/// * A `Vec<f32>` where each element is a probability corresponding to each input value.
103/// The probabilities are scaled such that their sum equals 1.0.
104///
105/// # Example
106/// ```
107/// let input = vec![1.0, 2.0, 3.0];
108/// let output = softmax(&input);
109/// assert_eq!(output.len(), input.len());
110/// assert!(output.iter().sum::<f32>() - 1.0 < 1e-6); // The sum of probabilities should be 1.0.
111/// ```
112pub fn softmax(input: &[f32]) -> Vec<f32> {
113 let max = input.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
114 let exp_values: Vec<f32> = input.iter().map(|&x| f32::exp(x - max)).collect();
115 let sum: f32 = exp_values.iter().sum();
116
117 exp_values.iter().map(|&x| x / sum).collect()
118}
119
120/// `relu` is the Rectified Linear Unit (ReLU) activation function, which outputs the input value
121/// if it is positive and zero otherwise. ReLU is one of the most commonly used activation functions
122/// in neural networks, especially for hidden layers.
123///
124/// # Arguments
125/// * `input` - A slice of `f32` values representing the input to the ReLU function.
126///
127/// # Returns
128/// * A `Vec<f32>` where each element is either the input value (if it is greater than 0) or 0 (if the input is negative).
129///
130/// # Example
131/// ```
132/// let input = vec![-1.0, 0.0, 1.0];
133/// let output = relu(&input);
134/// assert_eq!(output, vec![0.0, 0.0, 1.0]);
135/// ```
136pub fn relu(input: &[f32]) -> Vec<f32> {
137 input
138 .iter()
139 .map(|&x| if x > 0.0 { x } else { 0.0 })
140 .collect()
141}
142
143/// `linear` is a linear activation function that simply returns the input values as they are.
144/// This function is commonly used in the output layer of a neural network, especially in regression tasks,
145/// where no transformation of the output values is needed.
146///
147/// # Arguments
148/// * `input` - A slice of `f32` values representing the input to the linear function.
149///
150/// # Returns
151/// * A `Vec<f32>` that is identical to the input vector, as no transformation is applied.
152///
153/// # Example
154/// ```
155/// let input = vec![1.0, 2.0, 3.0];
156/// let output = linear(&input);
157/// assert_eq!(output, input);
158/// ```
159pub fn linear(input: &[f32]) -> Vec<f32> {
160 input.to_vec()
161}