>

2014년 5월 28일 수요일

A C/C++ code for Softmax with OpenCV

I searched for a C/C++ code for Softmax written with OpenCV, and found one in
http://eric-yuan.me/softmax-regression-cv. I did not test the code but hopefully it should be good. I will write when a test is done.

The code below is a copy from Eric Yuan's blog http://eric-yuan.me/softmax-regression-cv.


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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
// ----------------------------------------------------------------------------------------- //
// Softmax regression
 
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <math.h>
#include <iostream>
using namespace cv;
using namespace std;
 
#define elif else if
#define AT at<double>
#define MAX_ITER 100000
 
double cost = 0.0;
Mat grad;
double lrate = 0.1;
double lambda = 0.0;
int nclasses = 2;
 
Mat vec2mat(vector<vector<double> >&vec){
    int cols = vec.size();
    int rows = vec[0].size();
    Mat result(rows, cols, CV_64FC1);
    double *pData;
    for(int i = 0; i<rows; i++){
        pData = result.ptr<double>(i);
        for(int j=0; j<cols; j++){
            pData[j] = vec[j][i];      
        }
    }
    return result;
}
 
Mat vec2colvec(vector<double>& vec){
    int length = vec.size();
    Mat A(length, 1, CV_64FC1);
    for(int i=0; i<length; i++){
        A.AT(i, 0) = vec[i];
    }
    return A;
}
 
Mat vec2rowvec(vector<double>& vec){
    Mat A = vec2colvec(vec);
    return A.t();
}
 
void update_CostFunction_and_Gradient(Mat x, Mat y, Mat weightsMatrix, double lambda){
 
    int nsamples = x.cols;
    int nfeatures = x.rows;
    //calculate cost function
    Mat theta(weightsMatrix);
    Mat M = theta * x;
    Mat temp, temp2;
    temp = Mat::ones(1, M.cols, CV_64FC1);
    reduce(M, temp, 0, CV_REDUCE_SUM);
    temp2 = repeat(temp, nclasses, 1);
    M -= temp2;
    exp(M, M);
    temp = Mat::ones(1, M.cols, CV_64FC1);
    reduce(M, temp, 0, CV_REDUCE_SUM);
    temp2 = repeat(temp, nclasses, 1);
    divide(M, temp2, M);
    Mat groundTruth = Mat::zeros(nclasses, nsamples, CV_64FC1);
    for(int i=0; i<nsamples; i++){
        groundTruth.AT(y.AT(0, i), i) = 1.0;
    }
    Mat logM;
    log(M, logM);
    temp = groundTruth.mul(logM);
    cost = - sum(temp)[0] / nsamples;
    Mat theta2;
    pow(theta, 2.0, theta2);
    cost += sum(theta2)[0] * lambda / 2;
    //calculate gradient
    temp = groundTruth - M;
    temp = temp * x.t();
    grad = - temp / nsamples;
    grad += lambda * theta;
 
}
 
Mat calculateY(Mat x, Mat weightsMatrix){
 
    int nsamples = x.cols;
    int nfeatures = x.rows;
    //calculate cost function
    Mat theta(weightsMatrix);
    Mat M = theta * x;
    Mat temp, temp2;
    temp = Mat::ones(1, M.cols, CV_64FC1);
    reduce(M, temp, 0, CV_REDUCE_SUM);
    temp2 = repeat(temp, nclasses, 1);
    M -= temp2;
    exp(M, M);
    temp = Mat::ones(1, M.cols, CV_64FC1);
    reduce(M, temp, 0, CV_REDUCE_SUM);
    temp2 = repeat(temp, nclasses, 1);
    divide(M, temp2, M);
    log(M, M);
 
    Mat result = Mat::ones(1, M.cols, CV_64FC1);
    for(int i=0; i<M.cols; i++){
        double maxele = M.AT(0, i);
        int which = 0;
        for(int j=1; j<M.rows; j++){
            if(M.AT(j, i) > maxele){
                maxele = M.AT(j, i);
                which = j;
            }
        }
        result.AT(0, i) = which;
    }
    return result;
}
 
void softmax(vector<vector<double> >&vecX, vector<double> &vecY, vector<vector<double> >& testX, vector<double>& testY){
 
    int nsamples = vecX.size();
    int nfeatures = vecX[0].size();
    //change vecX and vecY into matrix or vector.
    Mat y = vec2rowvec(vecY);
    Mat x = vec2mat(vecX);
 
    double init_epsilon = 0.12;
    Mat weightsMatrix = Mat::ones(nclasses, nfeatures, CV_64FC1);
    double *pData;
    for(int i = 0; i<nclasses; i++){
        pData = weightsMatrix.ptr<double>(i);
        for(int j=0; j<nfeatures; j++){
            pData[j] = randu<double>();      
        }
    }
    weightsMatrix = weightsMatrix * (2 * init_epsilon) - init_epsilon;
 
    grad = Mat::zeros(nclasses, nfeatures, CV_64FC1);
 
/*
    //Gradient Checking (remember to disable this part after you're sure the
    //cost function and dJ function are correct)
    update_CostFunction_and_Gradient(x, y, weightsMatrix, lambda);
    Mat dJ(grad);
//    grad.copyTo(dJ);
    cout<<"test!!!!"<<endl;
    double epsilon = 1e-4;
    for(int i=0; i<weightsMatrix.rows; i++){
        for(int j=0; j<weightsMatrix.cols; j++){
            double memo = weightsMatrix.AT(i, j);
            weightsMatrix.AT(i, j) = memo + epsilon;
            update_CostFunction_and_Gradient(x, y, weightsMatrix, lambda);
            double value1 = cost;
            weightsMatrix.AT(i, j) = memo - epsilon;
            update_CostFunction_and_Gradient(x, y, weightsMatrix, lambda);
            double value2 = cost;
            double tp = (value1 - value2) / (2 * epsilon);
            cout<<i<<", "<<j<<", "<<tp<<", "<<dJ.AT(i, j)<<", "<<dJ.AT(i, j) / tp<<endl;
            weightsMatrix.AT(i, j) = memo;
        }
    }
*/
 
    int converge = 0;
    double lastcost = 0.0;
    while(converge < MAX_ITER){
        update_CostFunction_and_Gradient(x, y, weightsMatrix, lambda);
        weightsMatrix -= lrate * grad;
 
        cout<<"learning step: "<<converge<<", Cost function value = "<<cost<<endl;
        if(fabs((cost - lastcost) ) <= 5e-6 && converge > 0) break;
        lastcost = cost;
        ++ converge;
    }
    cout<<"############result#############"<<endl;
 
    Mat yT = vec2rowvec(testY);
    Mat xT = vec2mat(testX);
    Mat result = calculateY(xT, weightsMatrix);
    Mat err(yT);
    err -= result;
    int correct = err.cols;
    for(int i=0; i<err.cols; i++){
        if(err.AT(0, i) != 0) --correct;
    }
    cout<<"correct: "<<correct<<", total: "<<err.cols<<", accuracy: "<<double(correct) / (double)(err.cols)<<endl;
}
 
int main(int argc, char** argv)
{
    long start, end;
    //read training X from .txt file
    FILE *streamX, *streamY;
    streamX = fopen("trainX.txt", "r");
    int numofX = 30;
    vector<vector<double> > vecX;
    double tpdouble;
    int counter = 0;
    while(1){
        if(fscanf(streamX, "%lf", &tpdouble)==EOF) break;
        if(counter / numofX >= vecX.size()){
            vector<double> tpvec;
            vecX.push_back(tpvec);
        }
        vecX[counter / numofX].push_back(tpdouble);
        ++ counter;
    }
    fclose(streamX);
 
    cout<<vecX.size()<<", "<<vecX[0].size()<<endl;
 
    //read training Y from .txt file
    streamY = fopen("trainY.txt", "r");
    vector<double> vecY;
    while(1){
        if(fscanf(streamY, "%lf", &tpdouble)==EOF) break;
        vecY.push_back(tpdouble);
    }
    fclose(streamY);
 
    for(int i = 1; i<vecX.size(); i++){
        if(vecX[i].size() != vecX[i - 1].size()) return 0;
    }
    if(vecX.size() != vecY.size()) return 0;
 
    streamX = fopen("testX.txt", "r");
    vector<vector<double> > vecTX;
    counter = 0;
    while(1){
        if(fscanf(streamX, "%lf", &tpdouble)==EOF) break;
        if(counter / numofX >= vecTX.size()){
            vector<double> tpvec;
            vecTX.push_back(tpvec);
        }
        vecTX[counter / numofX].push_back(tpdouble);
        ++ counter;
    }
    fclose(streamX);
 
    streamY = fopen("testY.txt", "r");
    vector<double> vecTY;
    while(1){
        if(fscanf(streamY, "%lf", &tpdouble)==EOF) break;
        vecTY.push_back(tpdouble);
    }
    fclose(streamY);
 
    start = clock();
    softmax(vecX, vecY, vecTX, vecTY);
    end = clock();
    cout<<"Training used time: "<<((double)(end - start)) / CLOCKS_PER_SEC<<" second"<<endl;
    return 0;
}
// EOF //


Read http://eric-yuan.me/softmax/ about Softmax. The final part of the page says as follows:

SOFTMAX VS MULTI-BINARY CLASSIFIERS

Now let’s retrieve back to the above question about softmax VS multi-binary classifiers. As Prof. Andrew Ng says, which algorithm to use depend on whether the classes are mutually exclusive, which means, whether the classes are mixed. For example:
  • Case 1. classes are [1, 2, 3, 4, 5, 6]
  • Case 2. classes are [1, 2, 3, odd, even, positive]
For case 1, Softmax regression classifier would be fine, in the second case, use multi-logistic regression.