Skip to content

Commit 8728470

Browse files
committed
Sigmoid Activation function implementation with simplified batch calculation
1 parent 734a7a4 commit 8728470

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.thealgorithms.maths;
2+
3+
/**
4+
* Implementation of the Sigmoid Activation function.
5+
* Sigmoid function is used as an activation function in machine learning and neural networks
6+
* for modeling binary classification problems, smoothing outputs, and introducing non-linearity
7+
* into models.
8+
*
9+
* @author <a href="https://github.com/crapxxi">Alikhan Turugeldiyev</a>
10+
*/
11+
12+
public class SigmoidActivation {
13+
/**
14+
* @summary Maps any real-valued number into a value between 0 and 1
15+
* @param x In machine learning, x could be a weighted sum of inputs in a neural network neuron or a raw score in logistic regression.
16+
* @return The output (range) of the sigmoid function is always strictly between 0 and 1.
17+
*/
18+
public static double activate(double x) {
19+
// If the number x is NaN then, returning NaN to saving from unexpected output.
20+
if(Double.isNaN(x)) return Double.NaN;
21+
// Saving from unnecessary and heavy calculations.
22+
// lim x->-inf sigmoid(x) will return number very close to 0
23+
if(x < -745) return 0.0;
24+
// lim x->inf sigmoid(x) will return number very close to 1
25+
if(x > 745) return 1.0;
26+
// sigmoid function's formula
27+
return 1.0 / ( 1 + Math.exp((-1) * x));
28+
}
29+
30+
public static double[][] activate(double[][] x) {
31+
// apply calculation to every value in batch.
32+
double[][] activatedNumbers = new double[x.length][x[0].length];
33+
for(int i = 0; i < x.length; i++) {
34+
for (int j = 0; j < x[0].length; j++) activatedNumbers[i][j] = activate(x[i][j]);
35+
}
36+
return activatedNumbers;
37+
}
38+
39+
/**
40+
* @summary Calculates gradients for mapped values. By the chain rule, you can calculate error.
41+
* @param y Activated by sigmoid function value.
42+
* @return The output is a gradient of the activated value.
43+
*/
44+
public static double grad(double y) {
45+
// sigmoid function derivative is reducing to this value.
46+
// sigmoid'(x) = sigmoid(x) * (1-sigmoid(x))
47+
return y * (1 - y);
48+
}
49+
50+
public static double[][] grad(double[][] y) {
51+
// apply calculation to every value in batch.
52+
double[][] grads = new double[y.length][y[0].length];
53+
for(int i = 0; i < y.length; i++) {
54+
for (int j = 0; j < y[0].length; j++) grads[i][j] = grad(y[i][j]);
55+
}
56+
return grads;
57+
}
58+
59+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.thealgorithms.maths;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.Arrays;
6+
7+
import static org.junit.jupiter.api.Assertions.assertEquals;
8+
import static org.junit.jupiter.api.Assertions.assertTrue;
9+
10+
public class SigmoidActivationTest {
11+
12+
@Test
13+
public void calculationTest() {
14+
assertEquals(0.5,SigmoidActivation.activate(0), 0.01,"1 case correct" );
15+
assertEquals(0.73,SigmoidActivation.activate(1),0.01,"2 case correct");
16+
assertEquals(0.26,SigmoidActivation.activate(-1),0.01, "3 case correct");
17+
assertEquals(0.88,SigmoidActivation.activate(2),0.01,"4 case correct");
18+
assertEquals(0.11,SigmoidActivation.activate(-2),0.01,"5 case correct");
19+
20+
double[][] xBatch = new double[4][3];
21+
double[][] expectedX = new double[4][3];
22+
23+
for(int i = 0; i < 4; i++) {
24+
for(int j = 0; j < 3; j++) xBatch[i][j] = 0;
25+
}
26+
27+
for(int i = 0; i < 4; i++) {
28+
for(int j = 0; j < 3; j++) expectedX[i][j] = 0.5;
29+
}
30+
31+
assertTrue(Arrays.deepEquals(expectedX, SigmoidActivation.activate(xBatch)), "batch case correct");
32+
33+
assertEquals(0.25, SigmoidActivation.grad(0.5), 0.01, "grad calculation correct");
34+
35+
double[][] yBatch = new double[4][3];
36+
for(int i = 0; i < 4; i++) {
37+
for(int j = 0; j < 3; j++) yBatch[i][j] = 0.5;
38+
}
39+
40+
double[][] expectedY = new double[4][3];
41+
for(int i = 0; i < 4; i++) {
42+
for(int j = 0; j < 3; j++) expectedY[i][j] = 0.25;
43+
}
44+
assertTrue(Arrays.deepEquals(expectedY, SigmoidActivation.grad(yBatch)), "grad batch case correct");
45+
}
46+
47+
@Test
48+
public void willReturnNaN() {
49+
double x = Double.NaN;
50+
51+
assertTrue(Double.isNaN(SigmoidActivation.activate(x)), "returned NaN");
52+
}
53+
54+
@Test
55+
public void extremumNumbersOnActivate() {
56+
double x = 777;
57+
58+
assertEquals(1.0, SigmoidActivation.activate(x), 0.01, "big number case correct");
59+
assertEquals(0.0, SigmoidActivation.activate((-1) * x), 0.01, "small number case correct");
60+
}
61+
}

0 commit comments

Comments
 (0)