-
Notifications
You must be signed in to change notification settings - Fork 22
/
logistic_regression.go
198 lines (172 loc) · 5.19 KB
/
logistic_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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package goscore
import (
"encoding/xml"
"errors"
"math"
)
type PMMLLR struct {
// struct xml:PMML
LogisticRegression LogisticRegression `xml:"RegressionModel"`
}
type LogisticRegression struct {
// struct xml:PMML>RegressionModel
NormalizationMethod string `xml:"normalizationMethod,attr"`
Fields []MiningField `xml:"MiningSchema>MiningField"`
RegressionTable []RegressionTable `xml:"RegressionTable"`
}
// type MiningField struct {
// // struct xml:PMML>RegressionModel>MiningSchema>MiningField
// Name string `xml:"name,attr"`
// }
type RegressionTable struct {
// struct xml:PMML>RegressionModel>RegressionTable
Intercept float64 `xml:"intercept,attr"`
TargetCategory string `xml:"targetCategory,attr"`
NumericPredictor []NumericPredictor `xml:"NumericPredictor"`
NumericPredictorMap *map[string]float64
}
type NumericPredictor struct {
// struct xml:PMML>RegressionModel>RegressionTable>NumbericPredictor
Name string `xml:"name,attr"`
Coefficient float64 `xml:"coefficient,attr"`
}
type NormalizationMethodMap func(map[string]float64) (map[string]float64, error)
var NormalizationMethodMaps map[string]NormalizationMethodMap
//var NormalizationMethodNotImplemented = errors.New("Normalization Method Not Implemented Yet")
func init() {
NormalizationMethodMaps = map[string]NormalizationMethodMap{}
NormalizationMethodMaps["softmax"] = SoftmaxNormalizationMethods
}
// function for compute confidence value
// into probability using softMax function
// input : map of confidence value with float64 type
// output : map of probability each class with float64 type
func SoftmaxNormalizationMethods(confidence map[string]float64) (map[string]float64, error) {
if confidence != nil {
result := map[string]float64{}
tempExp := []float64{}
for _, v := range confidence {
tempExp = append(tempExp, math.Exp(v))
}
sum := 0.0
for _, j := range tempExp {
sum += j
}
i := 0
for k, _ := range confidence {
result[k] = tempExp[i]
i += 1
}
return result, nil
}
return nil, errors.New("feature is empty")
}
func NewLogisticRegression(source []byte) (*LogisticRegression, error) {
pmml := PMMLLR{}
err := xml.Unmarshal(source, &pmml)
if err != nil {
return nil, err
}
return &pmml.LogisticRegression, nil
}
// func NewLogisticRegressionFromReader(source io.Reader) (*LogisticRegression, error) {
// pmml := PMMLLR{}
// err := xml.NewDecoder(source).Decode(&pmml)
// if err != nil {
// return nil, err
// }
// return &pmml.LogisticRegression, nil
// }
// method for score test data
// input : independent variable with map["var name"]value
// voting with boolean type
// true (default) -> using normalization
// false -> without normalization
// return : -label with string type
// -confident/prob with map type
// -errors
func (lr *LogisticRegression) Score(args ...interface{}) (string, map[string]float64, error) {
features := map[string]float64{}
voting := true
for _, arg := range args {
switch t := arg.(type) {
case map[string]float64:
features = t
case bool:
voting = t
default:
return "", nil, errors.New("Unknown argument")
}
}
// calculate confident value using log reg function
confident := lr.RegressionFunctionContinuous(features)
if !voting {
return getMaxMap(confident), confident, nil
}
// calculate confident value with normalization method
var normMethod NormalizationMethodMap
if lr.NormalizationMethod != "" {
if _, ok := NormalizationMethods[lr.NormalizationMethod]; !ok {
return "", nil, NormalizationMethodNotImplemented
} else {
normMethod = NormalizationMethodMaps[lr.NormalizationMethod]
}
}
prob, err := normMethod(confident)
if err != nil {
return "", nil, err
}
return getMaxMap(prob), prob, nil
}
// create map for containing numeric predictor
func (lr *LogisticRegression) SetupNumbericPredictorMap() {
for i, rt := range lr.RegressionTable {
m := make(map[string]float64)
for _, np := range rt.NumericPredictor {
m[np.Name] = np.Coefficient
}
lr.RegressionTable[i].NumericPredictorMap = &m
//fmt.Println(rt.NumericPredictorMap)
}
//fmt.Println(lr.RegressionTable[0].NumericPredictorMap)
}
// method for calculate feature using logistic regression
// function for countinous independent variable
func (lr *LogisticRegression) RegressionFunctionContinuous(features map[string]float64) map[string]float64 {
confidence := map[string]float64{}
for _, regressionTable := range lr.RegressionTable {
var intercept float64
if regressionTable.Intercept != 0.0 {
intercept = regressionTable.Intercept
}
//fmt.Println(regressionTable.NumericPredictorMap)
if regressionTable.NumericPredictorMap != nil {
m := *regressionTable.NumericPredictorMap
sum := 0.0
for k, v := range features {
if c, ok := m[k]; ok {
sum += v * c
}
}
confidence[regressionTable.TargetCategory] = intercept + sum
}
}
return confidence
}
// method for key with search max value in map
func getMaxMap(feature map[string]float64) string {
result := ""
max := -999.999
for k, v := range feature {
if result != "" {
if max < v {
result = k
max = v
}
} else {
result = k
max = v
}
}
return result
}