Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

155 lines
6.0KB

  1. import unittest
  2. import numpy as np
  3. from neural_net.activation_layers.relu_layer import ReluLayer
  4. # noinspection PyMethodMayBeStatic
  5. class ReluLayerTests(unittest.TestCase):
  6. def test_relu_layer_1x1(self):
  7. ##############
  8. # Arrange #
  9. ##############
  10. inputs = np.array([[1.0]])
  11. weights = np.array([[0.5]])
  12. biases = np.array([0.0])
  13. learning_rate = 0.001
  14. # Pre-activation value (z)
  15. # This is the intermediate value calculated as the weighted sum of inputs plus the bias.
  16. z = np.dot(inputs, weights) + biases
  17. # ReLU activation: f(z) = max(0, z)
  18. # The expected output after applying the ReLU activation function
  19. expected_output = np.maximum(0, z)
  20. # Loss gradient dL/dout
  21. # Represents how much the loss changes when the output changes.
  22. dL_dout = np.array([[1.0]])
  23. # Activation derivative dout/dz
  24. # For ReLU: If z > 0, dout/dz = 1; otherwise, dout/dz = 0
  25. dout_dz = np.where(z > 0, 1.0, 0.0)
  26. # Gradient of the loss with respect to weights (dL/dweights)
  27. # This represents how much the loss changes when the weights change.
  28. # Formula: dL/dweights = inputs × dL/dout × σ′(z)
  29. expected_dl_dweights = inputs * dL_dout * dout_dz
  30. # Gradient of the loss with respect to the bias (dL/dbias)
  31. expected_dL_dbias = np.sum(dL_dout * dout_dz)
  32. # Gradient of the loss with respect to inputs (dL/dinputs)
  33. # This is the gradient of the loss with respect to the input of the neuron or layer, often needed if you want to backpropagate further.
  34. # Formula: dL / dinputs = dL/dout × σ′(z) × weights
  35. expected_dl_dinputs = dL_dout * dout_dz * weights
  36. # Calculate expected new weights and biases
  37. expected_weights = weights - learning_rate * expected_dl_dweights
  38. expected_biases = biases - learning_rate * expected_dL_dbias
  39. # Initialize SigmoidLayer
  40. layer = ReluLayer(weights.shape[0], weights.shape[1], weights=weights, biases=biases)
  41. ##############
  42. # Act #
  43. ##############
  44. # Forward pass
  45. output = layer.forward(inputs)
  46. # Backward pass
  47. dl_dinputs = layer.backward(dL_dout, learning_rate)
  48. ##############
  49. # Assert #
  50. ##############
  51. ##############
  52. # Assert #
  53. ##############
  54. # Forward output correctness
  55. self.assertTrue(np.allclose(output, expected_output, atol=1e-6),
  56. f"Forward output incorrect: Actual: {output}, Expected: {expected_output}")
  57. # Backward pass correctness
  58. self.assertTrue(np.allclose(dl_dinputs, expected_dl_dinputs, atol=1e-6),
  59. f"Inputs derivative incorrect Actual: {dl_dinputs}, expected: {expected_dl_dinputs}")
  60. self.assertTrue(np.allclose(layer.weights, expected_weights, atol=1e-6),
  61. f"Weight update incorrect Actual: {layer.weights}, expected: {expected_weights}")
  62. self.assertTrue(np.allclose(layer.biases, expected_biases, atol=1e-6),
  63. f"Bias update incorrect Actual: {layer.biases}, expected: {expected_biases}")
  64. def test_relu_layer_2x2(self):
  65. ##############
  66. # Arrange #
  67. ##############
  68. inputs = np.array([[1.0, 2.0],
  69. [3.0, 4.0]]) # 2x2 input matrix
  70. weights = np.array([[0.5, 0.2],
  71. [0.3, 0.7]]) # 2x2 weight matrix
  72. biases = np.array([0.1, -0.1]) # 2 biases, one for each neuron
  73. learning_rate = 0.001 # Learning rate for weight updates
  74. # Pre-activation value (z)
  75. # z = inputs.dot(weights) + biases
  76. z = np.dot(inputs, weights) + biases
  77. # Expected output using the ReLU activation function
  78. expected_output = np.maximum(0, z) # Apply ReLU
  79. # Loss gradient dL/dout (assuming a gradient of 1 for simplicity)
  80. dL_dout = np.array([[1.0, 1.0],
  81. [1.0, 1.0]])
  82. # Activation derivative dout/dz
  83. # For ReLU: dout/dz = 1 where z > 0, and dout/dz = 0 where z <= 0
  84. dout_dz = np.where(z > 0, 1.0, 0.0)
  85. # Expected gradients (for backpropagation)
  86. # Expected gradients with respect to weights
  87. expected_dl_dweights = np.dot(inputs.T, dL_dout * dout_dz)
  88. # Expected gradients with respect to biases
  89. expected_dL_dbias = np.sum(dL_dout * dout_dz, axis=0)
  90. # Expected gradients with respect to inputs
  91. expected_dl_dinputs = np.dot(dL_dout * dout_dz, weights.T)
  92. # Expected updated weights and biases after backpropagation
  93. expected_weights = weights - learning_rate * expected_dl_dweights
  94. expected_biases = biases - learning_rate * expected_dL_dbias
  95. # Initialize the ReLU Layer
  96. layer = ReluLayer(weights.shape[0], weights.shape[1], weights=weights, biases=biases)
  97. ##############
  98. # Act #
  99. ##############
  100. # Forward pass
  101. output = layer.forward(inputs)
  102. # Backward pass
  103. dl_dinputs = layer.backward(dL_dout, learning_rate)
  104. ##############
  105. # Assert #
  106. ##############
  107. # Forward output correctness
  108. self.assertTrue(np.allclose(output, expected_output, atol=1e-6),
  109. f"Forward output incorrect: Actual: {output}, Expected: {expected_output}")
  110. # Backward pass correctness (for input gradients)
  111. self.assertTrue(np.allclose(dl_dinputs, expected_dl_dinputs, atol=1e-6),
  112. f"Inputs derivative incorrect Actual: {dl_dinputs}, Expected: {expected_dl_dinputs}")
  113. # Check weight updates
  114. self.assertTrue(np.allclose(layer.weights, expected_weights, atol=1e-6),
  115. f"Weight update incorrect Actual: {layer.weights}, Expected: {expected_weights}")
  116. # Check bias updates
  117. self.assertTrue(np.allclose(layer.biases, expected_biases, atol=1e-6),
  118. f"Bias update incorrect Actual: {layer.biases}, Expected: {expected_biases}")