sample.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package sample
  2. import (
  3. "cmp"
  4. "errors"
  5. "math"
  6. "slices"
  7. pq "github.com/emirpasic/gods/v2/queues/priorityqueue"
  8. "golang.org/x/exp/rand"
  9. "gonum.org/v1/gonum/floats"
  10. "gonum.org/v1/gonum/stat/sampleuv"
  11. )
  12. type Transform interface {
  13. Apply([]float64) ([]float64, error)
  14. }
  15. type Sampler interface {
  16. Sample([]float32, ...Transform) (int, error)
  17. }
  18. // TODO(parthsareen): potentially cache softmax values
  19. func softmax(logits []float64) []float64 {
  20. var sum float64
  21. tt := make([]float64, len(logits))
  22. for i, v := range logits {
  23. tt[i] = math.Exp(v)
  24. sum += tt[i]
  25. }
  26. floats.Scale(1/sum, tt)
  27. return tt
  28. }
  29. type Temperature float64
  30. func (t Temperature) Apply(logits []float64) ([]float64, error) {
  31. if t == 0 {
  32. return nil, errors.New("use Greedy sampler instead of Temperature(0)")
  33. }
  34. if t < 0 || t > 2 {
  35. return nil, errors.New("temperature must be between 0 and 2")
  36. }
  37. temp := math.Max(float64(t), 1e-7)
  38. // subtracting max logit to avoid under/overflow
  39. maxLogit := slices.Max(logits)
  40. for i := range logits {
  41. logits[i] = (logits[i] - maxLogit) / temp
  42. }
  43. return logits, nil
  44. }
  45. type logitMap struct {
  46. index int
  47. logit float64
  48. }
  49. func logitMapComparator(a, b logitMap) int {
  50. return -cmp.Compare(a.logit, b.logit)
  51. }
  52. type TopK int
  53. // TODO(parthsareen): avoid having to check all logits after this transform
  54. func (k TopK) Apply(logits []float64) ([]float64, error) {
  55. if k <= 0 {
  56. return nil, errors.New("k must be greater than 0")
  57. }
  58. if int(k) >= len(logits) {
  59. return logits, nil
  60. }
  61. q := pq.NewWith(logitMapComparator)
  62. for i, logit := range logits {
  63. q.Enqueue(logitMap{index: i, logit: logit})
  64. }
  65. validLogits := make(map[int]float64)
  66. for range k {
  67. logitMap, _ := q.Dequeue()
  68. validLogits[logitMap.index] = logitMap.logit
  69. }
  70. for i := range logits {
  71. if _, ok := validLogits[i]; !ok {
  72. logits[i] = math.Inf(-1)
  73. }
  74. }
  75. return logits, nil
  76. }
  77. type TopP float64
  78. func (p TopP) Apply(logits []float64) ([]float64, error) {
  79. if p <= 0 || p >= 1 {
  80. return nil, errors.New("p must be between 0 and 1")
  81. }
  82. probs := softmax(logits)
  83. indices := make([]int, len(probs))
  84. for i := range indices {
  85. indices[i] = i
  86. }
  87. // sort in descending order
  88. slices.SortFunc(indices, func(i, j int) int {
  89. return cmp.Compare(probs[j], probs[i])
  90. })
  91. var cumSum float64
  92. for i, idx := range indices {
  93. cumSum += probs[idx]
  94. if cumSum > float64(p) {
  95. for _, idx := range indices[i+1:] {
  96. logits[idx] = math.Inf(-1)
  97. }
  98. break
  99. }
  100. }
  101. return logits, nil
  102. }
  103. type MinP float64
  104. func (p MinP) Apply(logits []float64) ([]float64, error) {
  105. if p <= 0 || p >= 1 {
  106. return nil, errors.New("p must be between 0 and 1")
  107. }
  108. probs := softmax(logits)
  109. threshold := slices.Max(probs) * float64(p)
  110. for i, prob := range probs {
  111. if prob < threshold {
  112. logits[i] = math.Inf(-1)
  113. }
  114. }
  115. return logits, nil
  116. }
  117. type weighted struct {
  118. src rand.Source
  119. }
  120. func Weighted(seed *int64) Sampler {
  121. var src rand.Source
  122. if seed != nil {
  123. src = rand.NewSource(uint64(*seed))
  124. }
  125. return weighted{src: src}
  126. }
  127. func (s weighted) Sample(logits []float32, transforms ...Transform) (int, error) {
  128. logits64 := make([]float64, len(logits))
  129. for i, v := range logits {
  130. logits64[i] = float64(v)
  131. }
  132. var err error
  133. for _, t := range transforms {
  134. logits64, err = t.Apply(logits64)
  135. if err != nil {
  136. return -1, err
  137. }
  138. }
  139. logitsCopy := make([]float64, 0, len(logits))
  140. indices := make([]int, 0, len(logits))
  141. for i, logit := range logits64 {
  142. if !math.IsInf(logit, -1) {
  143. logitsCopy = append(logitsCopy, logit)
  144. indices = append(indices, i)
  145. }
  146. }
  147. if len(logitsCopy) == 0 {
  148. return -1, errors.New("no valid logits found for weighed sampling")
  149. }
  150. probs := softmax(logitsCopy)
  151. w := sampleuv.NewWeighted(probs, s.src)
  152. if idx, ok := w.Take(); ok {
  153. return indices[idx], nil
  154. }
  155. return -1, errors.New("weighed sampler failed, no valid token found")
  156. }