samplers.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package sample
  2. import (
  3. "errors"
  4. "math"
  5. "golang.org/x/exp/rand"
  6. "gonum.org/v1/gonum/stat/sampleuv"
  7. )
  8. type Sampler interface {
  9. Sample([]float32) (int32, error)
  10. }
  11. type weighted struct {
  12. src rand.Source
  13. transforms []Transform
  14. }
  15. // TODO(parthsareen): remove uv sample dependency https://github.com/ollama/ollama/issues/9279
  16. func Weighted(seed *uint64, transforms ...Transform) Sampler {
  17. var src rand.Source
  18. if seed != nil {
  19. src = rand.NewSource(*seed)
  20. }
  21. return weighted{src: src, transforms: transforms}
  22. }
  23. func (s weighted) Sample(logits []float32) (int32, error) {
  24. logits64 := make([]float64, len(logits))
  25. for i, v := range logits {
  26. logits64[i] = float64(v)
  27. }
  28. for _, t := range s.transforms {
  29. logits64 = t.Apply(logits64)
  30. }
  31. logitsCopy := make([]float64, 0, len(logits))
  32. indices := make([]int, 0, len(logits))
  33. for i, logit := range logits64 {
  34. if !math.IsInf(logit, -1) {
  35. logitsCopy = append(logitsCopy, logit)
  36. indices = append(indices, i)
  37. }
  38. }
  39. if len(logitsCopy) == 0 {
  40. return -1, errors.New("no valid logits found for weighed sampling")
  41. }
  42. probs := softmax(logitsCopy)
  43. w := sampleuv.NewWeighted(probs, s.src)
  44. if idx, ok := w.Take(); ok {
  45. return int32(indices[idx]), nil
  46. }
  47. return -1, errors.New("weighed sampler failed, no valid token found")
  48. }
  49. type greedy struct {
  50. transforms []Transform
  51. }
  52. func Greedy(transforms ...Transform) Sampler {
  53. return greedy{transforms: transforms}
  54. }
  55. func (s greedy) Sample(logits []float32) (int32, error) {
  56. logits64 := make([]float64, len(logits))
  57. for i, v := range logits {
  58. logits64[i] = float64(v)
  59. }
  60. for _, t := range s.transforms {
  61. logits64 = t.Apply(logits64)
  62. }
  63. var maxIdx int
  64. var maxLogit float64
  65. for i, logit := range logits64 {
  66. if logit > maxLogit {
  67. maxLogit = logit
  68. maxIdx = i
  69. }
  70. }
  71. if maxLogit == math.Inf(-1) {
  72. return -1, errors.New("no valid logits found for greedy sampling")
  73. }
  74. return int32(maxIdx), nil
  75. }
  76. // TODO(parthsareen): update sampler interface to use json unmarshal https://github.com/ollama/ollama/issues/9278
  77. func NewSampler(temperature float32, topK int, topP float32, minP float32, seed int) (Sampler, error) {
  78. transforms := []Transform{}
  79. if temperature < 0 || temperature > 2 {
  80. return nil, errors.New("temperature must be between 0 and 2")
  81. }
  82. if temperature != 0 {
  83. transforms = append(transforms, Temperature(temperature))
  84. }
  85. if topK != 0 {
  86. if topK <= 0 {
  87. return nil, errors.New("topK must be greater than 0")
  88. }
  89. transforms = append(transforms, TopK(topK))
  90. }
  91. if topP != 0 {
  92. if topP < 0 || topP >= 1 {
  93. return nil, errors.New("topP must be between 0 and 1")
  94. }
  95. transforms = append(transforms, TopP(topP))
  96. }
  97. if minP != 0 {
  98. if minP < 0 || minP >= 1 {
  99. return nil, errors.New("minP must be between 0 and 1")
  100. }
  101. transforms = append(transforms, MinP(minP))
  102. }
  103. if len(transforms) == 0 {
  104. return nil, errors.New("at least one transform is required")
  105. }
  106. if temperature == 0 {
  107. return Greedy(transforms...), nil
  108. }
  109. if seed != 0 {
  110. seed64 := uint64(seed)
  111. return Weighted(&seed64, transforms...), nil
  112. }
  113. return Weighted(nil, transforms...), nil
  114. }