-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathregression.go
163 lines (135 loc) · 3.22 KB
/
regression.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package mlmetrics
import (
"math"
"sync"
)
// Regression is a basic regression evaluator
type Regression struct {
weight float64 // total weight observed
sum float64 // sum of all values
resSum float64 // residual sum
resSum2 float64 // residual sum of squares
logSum2 float64 // logarithmic residual sum of squares
totSum2 float64 // total sum of squares
maxDelta float64 // maximum error delta
mu sync.RWMutex
}
// NewRegression inits a new metric.
func NewRegression() *Regression {
return &Regression{}
}
// Reset resets state.
func (m *Regression) Reset() {
m.mu.Lock()
m.weight = 0
m.sum = 0
m.resSum = 0
m.resSum2 = 0
m.logSum2 = 0
m.totSum2 = 0
m.maxDelta = 0
m.mu.Unlock()
}
// Observe records an observation of the actual vs the predicted value.
func (m *Regression) Observe(actual, predicted float64) {
m.ObserveWeight(actual, predicted, 1.0)
}
// ObserveWeight records an observation of the actual vs the predicted value with a given weight.
func (m *Regression) ObserveWeight(actual, predicted, weight float64) {
if !isValidNumeric(actual) || !isValidNumeric(predicted) || !isValidWeight(weight) {
return
}
residual := math.Abs(actual - predicted)
logres := math.Abs(math.Log1p(actual) - math.Log1p(predicted))
m.mu.Lock()
defer m.mu.Unlock()
if residual > m.maxDelta {
m.maxDelta = residual
}
if m.weight != 0 {
delta := actual - m.sum/m.weight
m.totSum2 += delta * delta * weight
}
m.resSum += residual * weight
m.resSum2 += residual * residual * weight
m.logSum2 += logres * logres * weight
m.sum += actual * weight
m.weight += weight
}
// TotalWeight returns the total weight observed.
func (m *Regression) TotalWeight() float64 {
m.mu.RLock()
weight := m.weight
m.mu.RUnlock()
return weight
}
// MaxError returns the maximum observed error delta.
func (m *Regression) MaxError() float64 {
m.mu.RLock()
maxDelta := m.maxDelta
m.mu.RUnlock()
return maxDelta
}
// Mean returns the mean actual value observed.
func (m *Regression) Mean() float64 {
m.mu.RLock()
weight := m.weight
sum := m.sum
m.mu.RUnlock()
if weight > 0 {
return sum / weight
}
return 0.0
}
// MAE calculates the mean absolute error.
func (m *Regression) MAE() float64 {
m.mu.RLock()
weight := m.weight
resSum := m.resSum
m.mu.RUnlock()
if weight > 0 {
return resSum / weight
}
return 0.0
}
// MSE calculates the mean squared error.
func (m *Regression) MSE() float64 {
m.mu.RLock()
weight := m.weight
resSum2 := m.resSum2
m.mu.RUnlock()
if weight > 0 {
return resSum2 / weight
}
return 0.0
}
// MSLE calculates the mean squared logarithmic error loss.
func (m *Regression) MSLE() float64 {
m.mu.RLock()
weight := m.weight
logSum2 := m.logSum2
m.mu.RUnlock()
if weight > 0 {
return logSum2 / weight
}
return 0.0
}
// RMSE calculates the root mean squared error.
func (m *Regression) RMSE() float64 {
return math.Sqrt(m.MSE())
}
// RMSLE calculates the root mean squared logarithmic error loss.
func (m *Regression) RMSLE() float64 {
return math.Sqrt(m.MSLE())
}
// R2 calculates the R² coefficient of determination.
func (m *Regression) R2() float64 {
m.mu.RLock()
resSum2 := m.resSum2
totSum2 := m.totSum2
m.mu.RUnlock()
if totSum2 > 0 {
return 1 - resSum2/totSum2
}
return 0.0
}