-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlr.cpp
264 lines (242 loc) · 6.92 KB
/
lr.cpp
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
257
258
259
260
261
262
263
264
/*
* lr.cpp is an simple ML algorithm to make predictions based on a linear regression model.
*
* Input data is stored as a 2d vector. Each row is vector<double> data, where the last element is the label.
* We have to delete data that is not numeric.
*
* There is timing of exectution enabled for testing purposes.
*/
#include <iostream>
#include <string>
#include <fstream>
#include <sstream> //istringstream
#include <fstream> // ifstream
#include <algorithm>
#include <math.h>
#include <chrono>
#include "lr.hpp"
#include <omp.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include <thrust/inner_product.h>
#include <thrust/execution_policy.h>
// Timing
using chrono::high_resolution_clock;
using chrono::duration_cast;
using chrono::duration;
using chrono::milliseconds;
vector<double> theta;
vector<double> gradient;
string label1;
string label2;
int CUDA_ENABLE = 0;
int SPARSE_ENABLE = 0;
int N_s = 600;
/*
* input_data parses a csv file data into a 2d vector. Each row has vector data followed by the label (0 or 1).
*
* No column of training data can be non-numeric (delete column)
*/
vector<vector<double> > input_data(string infile) {
vector<vector<double> > data;
ifstream input_file(infile);
int row = 0;
int num_cols = 0;
while (input_file) {
string row_string;
if (!getline(input_file, row_string)) break;
istringstream ss(row_string);
vector<double> record;
bool valid_row = true;
if(row == 0) {
num_cols = count(row_string.begin(), row_string.end(), ',') + 1;
}
if(row > 0) {
int col = 0;
double label;
// Loop through each col
while (ss) {
string cell;
if (!getline(ss, cell, ',')) break;
// Data case
if(col < num_cols-1) {
try {
size_t idx;
double cell_double = stof(cell, &idx);
if(idx == cell.length()) {
record.push_back(cell_double);
}
}
catch (const invalid_argument e) {
valid_row = false;
break;
}
catch (const out_of_range e) {
valid_row = false;
break;
}
}
// Label case
else {
// Use find instead of includes because of strange string escape sequences
if(cell.find(label1) != std::string::npos) {
label = 0;
}
else if(cell.find(label2) != std::string::npos) {
label = 1;
}
else {
valid_row = false;
break;
}
record.push_back(label);
}
col += 1;
}
if(valid_row) {
data.push_back(record);
}
}
row++;
}
return data;
}
/*
* init_theta allocates space for theta, our parameter vector
*/
void init_theta(int num_features) {
//theta.resize(num_features);
for(int i=0; i<num_features+1; i++) {
// We include an extra parameter for bias term
theta.push_back(0);
}
}
/*
* init_theta allocates space for gradient, our gradient vector
*/
void init_gradient(int num_features) {
//theta.resize(num_features);
for(int i=0; i<num_features+1; i++) {
// We include an extra parameter for bias term
gradient.push_back(0);
}
}
/*
* reset_gradient sets our gradient vector to 0
*/
void reset_gradient() {
fill(gradient.begin(), gradient.end(), 0);
}
/*
* dotprod takes the dotprod of v1 and v2,
* using the size of v2 (for functionality despite bias term)
*/
double ml_dotprod(vector<double> params, vector<double> data) {
double result = 0;
if(CUDA_ENABLE) {
auto start = params.begin();
auto end = params.begin() + N_s;
double result = thrust::inner_product(thrust::host, start, end, data.begin(), 0.0);
}
else {
// Sequential
//#pragma omp parallel for reduction(+:result) num_threads(2) // test?
for(int i=0; i<params.size()-1; i++) {
result += params[i]*data[i];
}
}
return result;
}
/*
* sigmoid bounds x to 0 or 1 (our prediction)
*/
double sigmoid(double x) {
if(x >= 0) {
double z = exp(-x);
return 1 / (1 + z);
}
else {
double z = exp(x);
return z / (1 + z);
}
}
/*
* SGD_step edits our theta parameter vector based on a single data row.
*
* Loss function: cross-entropy loss function
*/
void SGD_step(vector<double> data_row) {
/*
// Debugging: Print theta
cout << "Theta: ";
for(auto param : theta) {
cout << param << " ";
}
cout << endl;
*/
double bias = theta.back();
double label = data_row.back();
double theta_dot_x = ml_dotprod(theta, data_row);
//cout << "Dot Prod: " << theta_dot_x << endl;
double sig = sigmoid(theta_dot_x + bias);
//cout << "Sig: " << sig << " Label: " << label << endl;
//double cost = label*(log(sig)) + (1-label)*(log(1-sig));
double err = (sig - label);
/*
if(err < 0.1) {
cout << "Correct" << endl;
}
else {
cout << "Wrong" << endl;
}
*/
for(int i=0; i<gradient.size()-1; i++) {
gradient[i] += err * data_row[i];
}
gradient.back() += err;
}
/*
* train learns appropriate theta based on training data
*/
void train(vector<vector<double> > data, int num_epoch) {
// Debug: print the data vector
int rows = data.size();
double cost = 0;
for(int i=0; i<num_epoch; i++) {
for(int j=0; j<rows; j++) {
SGD_step(data[j]);
}
}
}
void print_usage() {
cout << "Usage: ./linear-regression [infile] [outfile] [label1] [label2] [num_epoch] [learning_rate] [-h for help] \n";
}
/*
* parse_flags parses options inputted from CMD
* returns 0 if we do not want to run, 1 otherwise
*/
int parse_flags(int argc, char **argv, int offset) {
int i = offset;
while(i < argc) {
if(argv[i][0] == '-') {
switch(argv[i][1])
{
case 'h':
print_usage();
return 0;
case 'c':
CUDA_ENABLE = 1;
//cout << "CUDA enabled!";
i+=1;
break;
case 's':
SPARSE_ENABLE = 1;
cout << "Sparse vectors not implemented yet";
return 0;
default:
cout << "Not a valid flag: -" << argv[i][1] << '\n';
}
}
}
return 1;
}