Skip to content

Commit 78c250c

Browse files
author
jajoe
committed
feat: Add the logistic regression
1 parent 76605b0 commit 78c250c

File tree

4 files changed

+159
-0
lines changed

4 files changed

+159
-0
lines changed

__tests__/test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
const {Matrix} = require('ml-matrix');
2+
const LogisticRegressionTwoClasses = require('../src/logreg_2classes');
3+
const LogisticRegression = require('../src/logreg');
4+
5+
// test for 2 classes
6+
7+
var X = new Matrix([[0,-1], [1,0], [1,1], [1,-1], [2,0], [2,1], [2,-1], [3,2], [0,4], [1,3], [1,4], [1,5], [2,3], [2,4], [2,5], [3,4]]);
8+
var Y = Matrix.columnVector([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]);
9+
10+
var Xtest = new Matrix([[0, -2], [1, 0.5], [1.5, -1], [1, 4.5], [2, 3.5], [1.5, 5]])
11+
var Ytest = Matrix.columnVector([0, 0, 0, 1, 1, 1]);
12+
13+
var logreg = new LogisticRegressionTwoClasses(num_steps = 500000, learning_rate = 5e-4);
14+
logreg.train(X, Y);
15+
// console.log(logreg.weights); // results should be [-12.07083366 9.94427349]
16+
var results = logreg.test(Xtest); // compute results of the training set
17+
console.log(results);
18+
19+
20+
21+
22+
23+
24+
// test for 3 classes
25+
26+
var X = new Matrix([[0,-1], [1,0], [1,1], [1,-1], [2,0], [2,1], [2,-1], [3,2], [0,4], [1,3], [1,4], [1,5], [2,3], [2,4], [2,5], [3,4], [1, 10], [1, 12], [2, 10], [2,11], [2, 14], [3, 11]]);
27+
var Y = Matrix.columnVector([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2]);
28+
29+
var Xtest = new Matrix([[0, -2], [1, 0.5], [1.5, -1], [1, 2.5], [2, 3.5], [1.5, 4], [1, 10.5], [2.5, 10.5], [2, 11.5]])
30+
var Ytest = Matrix.columnVector([0, 0, 0, 1, 1, 1, 2, 2, 2]);
31+
32+
var logreg = new LogisticRegression(num_steps = 10000000, learning_rate = 5e-5);
33+
logreg.train(X,Y);
34+
var finalResults = logreg.test(Xtest);
35+
console.log(finalResults);

package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "logreg",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "jajoe",
10+
"license": "MIT",
11+
"dependencies": {
12+
"ml-matrix": "^3.0.0"
13+
}
14+
}

src/logreg.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
const {Matrix} = require('ml-matrix');
2+
const LogisticRegressionTwoClasses = require('./logreg_2classes');
3+
4+
function transform_classes_for_one_vs_all(Y, oneClass){
5+
y = Y.to1DArray();
6+
for (var i = 0; i < y.length; i++){
7+
if (y[i] == oneClass) {
8+
y[i] = 0;
9+
} else {
10+
y[i] = 1;
11+
}
12+
}
13+
return Matrix.columnVector(y);
14+
}
15+
16+
class LogisticRegression {
17+
18+
constructor(options) {
19+
options = options || {};
20+
this.numSteps = options. numSteps || 500000;
21+
this.learningRate = options.learningRate || 5e-4;
22+
this.classifiers = [];
23+
this.numberClasses = 0;
24+
}
25+
26+
train(X, Y) {
27+
this.numberClasses = new Set(Y).size;
28+
this.classifiers = new Array(this.numberClasses);
29+
30+
// train the classifiers
31+
for (var i = 0; i < this.numberClasses; i++){
32+
this.classifiers[i] = new LogisticRegressionTwoClasses({numSteps: this.numSteps, learningRate: this.learningRate});
33+
var y = Y.clone();
34+
y = transform_classes_for_one_vs_all(y, i);
35+
this.classifiers[i].train(X,y);
36+
}
37+
}
38+
39+
test(Xtest) {
40+
var resultsOneClass = new Array(this.numberClasses).fill(0);
41+
for (var i = 0; i < this.numberClasses; i++){
42+
resultsOneClass[i]= this.classifiers[i].testScores(Xtest);
43+
}
44+
var finalResults = new Array(Xtest.rows).fill(0);
45+
for (var i = 0; i < Xtest.rows; i++){
46+
var minimum = 100000;
47+
for (var j = 0; j < this.numberClasses; j++){
48+
if (resultsOneClass[j][i] < minimum) {
49+
minimum = resultsOneClass[j][i];
50+
finalResults[i] = j;
51+
}
52+
}
53+
}
54+
return finalResults;
55+
}
56+
}
57+
58+
module.exports = LogisticRegression;

src/logreg_2classes.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const {Matrix} = require('ml-matrix');
2+
3+
class LogisticRegressionTwoClasses {
4+
5+
constructor(options) {
6+
options = options || {};
7+
this.numSteps = options.numSteps || 500000;
8+
this.learningRate = options.learningRate || 5e-4;
9+
this.weights = [];
10+
}
11+
12+
sigmoid(scores) {
13+
scores = scores.to1DArray();
14+
var result = [];
15+
for (var i = 0; i < scores.length; i++) {
16+
result.push(1 / (1 + Math.exp(-scores[i])));
17+
}
18+
return result;
19+
}
20+
21+
train(features, target) {
22+
var weights = Matrix.zeros(1, features.columns);
23+
24+
for (var step = 0; step < this.numSteps; step++) {
25+
var scores = features.mmul(weights.transpose());
26+
var predictions = this.sigmoid(scores);
27+
28+
// Update weights with gradient
29+
var output_error_signal = Matrix.columnVector(predictions).neg().add(target);
30+
var gradient = features.transpose().mmul(output_error_signal);
31+
weights = weights.add(gradient.mul(this.learningRate).transpose());
32+
}
33+
34+
this.weights = weights
35+
}
36+
37+
testScores(features) {
38+
var final_data = features.mmul(this.weights.transpose());
39+
var predictions = this.sigmoid(final_data);
40+
predictions = Matrix.columnVector(predictions);
41+
return predictions.to1DArray();
42+
}
43+
44+
test(features) {
45+
var final_data = features.mmul(this.weights.transpose());
46+
var predictions = this.sigmoid(final_data);
47+
predictions = Matrix.columnVector(predictions).round();
48+
return predictions.to1DArray();
49+
}
50+
}
51+
52+
module.exports = LogisticRegressionTwoClasses;

0 commit comments

Comments
 (0)