1
0

activations.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package nn
  2. import (
  3. "math"
  4. "makarna/pkg/backend/cpu"
  5. )
  6. // Sigmoid applies the sigmoid function: 1 / (1 + exp(-x))
  7. // Uses numerically stable computation for both positive and negative inputs.
  8. func Sigmoid(x float32) float32 {
  9. if x >= 0 {
  10. z := float32(math.Exp(float64(-x)))
  11. return 1 / (1 + z)
  12. }
  13. z := float32(math.Exp(float64(x)))
  14. return z / (1 + z)
  15. }
  16. // SigmoidInplace applies sigmoid to each element of the slice in-place.
  17. func SigmoidInplace(data []float32) {
  18. for i := range data {
  19. data[i] = Sigmoid(data[i])
  20. }
  21. }
  22. // SigmoidTensor applies sigmoid in-place to a tensor.
  23. func SigmoidTensor(x *cpu.Tensor) error {
  24. SigmoidInplace(x.DataFloat32())
  25. return nil
  26. }
  27. // Softplus computes log(1 + exp(x)), with numerical stability for large x.
  28. func Softplus(x float32) float32 {
  29. if x > 20 {
  30. return x
  31. }
  32. return float32(math.Log1p(math.Exp(float64(x))))
  33. }
  34. // SoftplusInplace applies softplus to each element of the slice in-place.
  35. func SoftplusInplace(data []float32) {
  36. for i := range data {
  37. data[i] = Softplus(data[i])
  38. }
  39. }
  40. // SoftplusTensor applies softplus in-place to a tensor.
  41. func SoftplusTensor(x *cpu.Tensor) error {
  42. SoftplusInplace(x.DataFloat32())
  43. return nil
  44. }
  45. // L2NormInplace normalizes a vector to unit length in-place.
  46. // eps is added to prevent division by zero.
  47. func L2NormInplace(x []float32, eps float32) {
  48. if len(x) == 0 {
  49. return
  50. }
  51. ss := float32(0)
  52. for _, v := range x {
  53. ss += v * v
  54. }
  55. inv := float32(1.0 / math.Sqrt(float64(ss+eps)))
  56. for i := range x {
  57. x[i] *= inv
  58. }
  59. }
  60. // L2NormHeads normalizes Q and K vectors per-head for multi-head attention.
  61. // Shape: [tokens, numHeads * headDim], normalizes each [headDim] segment.
  62. func L2NormHeads(q, k []float32, tokens, numHeads, headDim int, eps float32) {
  63. stride := numHeads * headDim
  64. for t := 0; t < tokens; t++ {
  65. base := t * stride
  66. for h := 0; h < numHeads; h++ {
  67. off := base + h*headDim
  68. L2NormInplace(q[off:off+headDim], eps)
  69. L2NormInplace(k[off:off+headDim], eps)
  70. }
  71. }
  72. }
  73. // Exp computes e^x
  74. func Exp(x float32) float32 {
  75. return float32(math.Exp(float64(x)))
  76. }
  77. // ExpInplace applies exp to each element of the slice in-place.
  78. func ExpInplace(data []float32) {
  79. for i := range data {
  80. data[i] = Exp(data[i])
  81. }
  82. }