Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions src/main/java/com/thealgorithms/maths/SigmoidActivation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.thealgorithms.maths;

/**
* Implementation of the Sigmoid Activation function.
* Sigmoid function is used as an activation function in machine learning and neural networks
* for modeling binary classification problems, smoothing outputs, and introducing non-linearity
* into models.
*
* @author <a href="https://github.com/crapxxi">Alikhan Turugeldiyev</a>
*/

public final class SigmoidActivation {

private SigmoidActivation() {
}

/**
* @summary Maps any real-valued number into a value between 0 and 1
* @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.
* @return The output (range) of the sigmoid function is always strictly between 0 and 1.
*/
public static double activate(double x) {
// If the number x is NaN then, returning NaN to saving from unexpected output.
if (Double.isNaN(x)) {
return Double.NaN;
}
// Saving from unnecessary and heavy calculations.
// lim x->-inf sigmoid(x) will return number very close to 0
if (x < -745) {
return 0.0;
}
// lim x->inf sigmoid(x) will return number very close to 1
if (x > 745) {
return 1.0;
}
// sigmoid function's formula
return 1.0 / (1 + Math.exp((-1) * x));
}

public static double[][] activate(double[][] x) {
// apply calculation to every value in batch.
double[][] activatedNumbers = new double[x.length][];
for (int i = 0; i < x.length; i++) {
activatedNumbers[i] = new double[x[i].length];
for (int j = 0; j < x[i].length; j++) {
activatedNumbers[i][j] = activate(x[i][j]);
}
}
return activatedNumbers;
}

/**
* @summary Calculates gradients for mapped values. By the chain rule, you can calculate error.
* @param y Activated by sigmoid function value.
* @return The output is a gradient of the activated value.
*/
public static double grad(double y) {
// sigmoid function derivative is reducing to this value.
// sigmoid'(x) = sigmoid(x) * (1-sigmoid(x))
return y * (1 - y);
}

public static double[][] grad(double[][] y) {
// apply calculation to every value in batch.
double[][] grads = new double[y.length][];
for (int i = 0; i < y.length; i++) {
grads[i] = new double[y[i].length];
for (int j = 0; j < y[i].length; j++) {
grads[i][j] = grad(y[i][j]);
}
}
return grads;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.thealgorithms.maths;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Arrays;
import org.junit.jupiter.api.Test;

public class SigmoidActivationTest {

@Test
public void calculationTest() {
assertEquals(0.5, SigmoidActivation.activate(0), 0.01, "1 case correct");
assertEquals(0.73, SigmoidActivation.activate(1), 0.01, "2 case correct");
assertEquals(0.26, SigmoidActivation.activate(-1), 0.01, "3 case correct");
assertEquals(0.88, SigmoidActivation.activate(2), 0.01, "4 case correct");
assertEquals(0.11, SigmoidActivation.activate(-2), 0.01, "5 case correct");

double[][] xBatch = new double[4][3];
double[][] expectedX = new double[4][3];

for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
xBatch[i][j] = 0;
}
}

for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
expectedX[i][j] = 0.5;
}
}

assertTrue(Arrays.deepEquals(expectedX, SigmoidActivation.activate(xBatch)), "batch case correct");

assertEquals(0.25, SigmoidActivation.grad(0.5), 0.01, "grad calculation correct");

double[][] yBatch = new double[4][3];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
yBatch[i][j] = 0.5;
}
}

double[][] expectedY = new double[4][3];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
expectedY[i][j] = 0.25;
}
}
assertTrue(Arrays.deepEquals(expectedY, SigmoidActivation.grad(yBatch)), "grad batch case correct");
}

@Test
public void willReturnNaN() {
double x = Double.NaN;

assertTrue(Double.isNaN(SigmoidActivation.activate(x)), "returned NaN");
}

@Test
public void extremumNumbersOnActivate() {
double x = 777;

assertEquals(1.0, SigmoidActivation.activate(x), 0.01, "big number case correct");
assertEquals(0.0, SigmoidActivation.activate((-1) * x), 0.01, "small number case correct");
}
}
Loading