1 /**
2  * A Library for AROW Linear Classification 
3  *
4  * Authors: Kazuya Gokita
5  */
6 
7 module arow;
8 
9 import std.math;
10 import std.random;
11 
12 /***
13  * Adaptive Regularization of Weight Vectors
14  *
15  * See_Also:
16  *   K. Crammer, A. Kulesza, and M. Dredze. "Adaptive regularization of weight vectors" NIPS 2009
17  */
18 class Arow {
19   private:
20     size_t dimension; // Size of feature vector
21     double[] mean;    // Average vector
22     double[] cov;     // Variance matrix (diagonal)
23 
24     double r;         // Hyper parameter ( r > 0 )
25 
26     invariant() {
27       assert(mean != null);
28       assert(cov != null);
29       assert(mean.length == dimension);
30       assert(cov.length == dimension);
31       assert(r > 0);
32     }
33 
34 
35     /**
36      * Calculate confidence
37      * Params:
38      *  f = feature
39      *
40      * Returns: confidence
41      */ 
42     double getConfidence(in double[int] f) @trusted
43     in {
44       assert(f != null);
45     }
46     out(confidence) {
47       assert(confidence != double.nan);
48       assert(confidence != double.nan && confidence != -double.infinity);
49     }
50     body {
51       double confidence = 0.0;
52       foreach(index; f.keys) {
53         confidence += cov[index] * f[index] * f[index];
54       }
55 
56       return confidence;
57     }
58 
59 
60   public:
61     this(size_t num_features, double param = 0.1) {
62       dimension = num_features;
63       mean = new double[dimension];
64       cov = new double[dimension];
65 
66       mean[] = 0.0;
67       cov[] = 1.0;
68       r = param;
69     }
70 
71 
72     @property {
73       auto dim() { return dimension; }
74     }
75 
76 
77     @property {
78       auto param() { return r; }
79       auto param(double val) { return r = val; }
80     }
81 
82 
83     /**
84      * Update weight vector
85      * Params:
86      *  fv    = feature
87      *  label = class label (+1 / -1)
88      *
89      * Returns: loss (0 / 1)
90      */
91     int update(in double[int] f, int label) @trusted
92     in {
93       assert(label == -1 || label == 1);
94       assert(f != null);
95     }
96     out(loss) {
97       assert(loss == 0 || loss == 1);
98     }
99     body {
100       immutable margin = getMargin(f);
101       if (margin * label >= 1) return 0;
102 
103       immutable confidence = getConfidence(f);
104       immutable beta = 1.0 / (confidence + r);
105       immutable alpha = (1.0 - label * margin) * beta;
106 
107       // Update mean
108       foreach(index; f.keys) {
109         mean[index] += alpha * cov[index] * label * f[index];
110       }
111 
112       // Update covariance
113       foreach(index; f.keys) {
114         cov[index] = 1.0 / ((1.0 / cov[index]) + f[index] * f[index] / r);
115       }
116 
117       return margin * label < 0 ? 1 : 0;
118     }
119 
120 
121     /**
122      * Calculate the distance between a vector and the hyperplane
123      * Params:
124      *  f = feature
125      *
126      * Returns: Margin(Euclidean distance)
127      */
128     double getMargin(in double[int] f) @trusted
129     in {
130       assert(f != null);
131     }
132     out(margin) {
133       assert(margin != double.nan);
134       assert(margin != double.nan && margin != -double.infinity);
135     }
136     body {
137       double margin = 0.0;
138       foreach(index; f.keys) {
139         margin += mean[index] * f[index];
140       }
141 
142       return margin;
143     }
144 
145 
146     /**
147      * Predict
148      * Params:
149      *  f = feature vector
150      * Returns: class label (-1, 1)
151      */
152     int predict(in double[int] f) @trusted
153     in {
154       assert(f != null);
155     }
156     out(label) {
157       assert(label == -1 || label == 1);
158     }
159     body {
160       double m = getMargin(f);
161       return m > 0 ? 1 : -1;
162     }
163 
164 }
165