From 2ddcad9053097c75bcdd97d234c1f8b605a88241 Mon Sep 17 00:00:00 2001 From: Yaw Joseph Etse Date: Wed, 18 May 2022 22:12:38 -0400 Subject: [PATCH 01/11] feat: custom modelfitargs for linear models --- .gitignore | 3 ++- src/linear_model/LinearRegression.ts | 8 ++++++-- src/linear_model/LogisticRegression.ts | 8 ++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4d06003..b57f5e7 100644 --- a/.gitignore +++ b/.gitignore @@ -107,4 +107,5 @@ dist # IDE Files .vscode/ -.idea/ \ No newline at end of file +.idea/ +.dccache \ No newline at end of file diff --git a/src/linear_model/LinearRegression.ts b/src/linear_model/LinearRegression.ts index 1913ed2..995f577 100644 --- a/src/linear_model/LinearRegression.ts +++ b/src/linear_model/LinearRegression.ts @@ -15,6 +15,7 @@ import { SGDRegressor } from './SgdRegressor' import { getBackend } from '../tf-singleton' +import { ModelFitArgs } from '../types' /** * LinearRegression implementation using gradient descent @@ -39,6 +40,8 @@ export interface LinearRegressionParams { * **default = true** */ fitIntercept?: boolean + modelFitOptions?: Partial + } /* @@ -66,7 +69,7 @@ Next steps: * ``` */ export class LinearRegression extends SGDRegressor { - constructor({ fitIntercept = true }: LinearRegressionParams = {}) { + constructor({ fitIntercept = true, modelFitOptions }: LinearRegressionParams = {}) { let tf = getBackend() super({ modelCompileArgs: { @@ -80,7 +83,8 @@ export class LinearRegression extends SGDRegressor { verbose: 0, callbacks: [ tf.callbacks.earlyStopping({ monitor: 'mse', patience: 30 }) - ] + ], + ...modelFitOptions }, denseLayerArgs: { units: 1, diff --git a/src/linear_model/LogisticRegression.ts b/src/linear_model/LogisticRegression.ts index 159cd36..b235bb3 100644 --- a/src/linear_model/LogisticRegression.ts +++ b/src/linear_model/LogisticRegression.ts @@ -15,6 +15,7 @@ import { SGDClassifier } from './SgdClassifier' import { getBackend } from '../tf-singleton' +import { ModelFitArgs } from '../types' // First pass at a LogisticRegression implementation using gradient descent // Trying to mimic the API of scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html @@ -35,6 +36,7 @@ export interface LogisticRegressionParams { C?: number /** Whether or not the intercept should be estimator not. **default = true** */ fitIntercept?: boolean + modelFitOptions?: Partial } /** Builds a linear classification model with associated penalty and regularization @@ -63,7 +65,8 @@ export class LogisticRegression extends SGDClassifier { constructor({ penalty = 'l2', C = 1, - fitIntercept = true + fitIntercept = true, + modelFitOptions }: LogisticRegressionParams = {}) { // Assume Binary classification // If we call fit, and it isn't binary then update args @@ -80,7 +83,8 @@ export class LogisticRegression extends SGDClassifier { verbose: 0, callbacks: [ tf.callbacks.earlyStopping({ monitor: 'loss', patience: 50 }) - ] + ], + ...modelFitOptions }, denseLayerArgs: { units: 1, From 7fa5c4259902d7dca0a925002cbfaf1937dc2b1b Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Wed, 18 May 2022 21:41:05 -0700 Subject: [PATCH 02/11] feat: added test case for custom callbacks. works great and somehow serializes. --- src/linear_model/LinearRegression.test.ts | 32 +++++++++++++++++++++++ src/linear_model/LinearRegression.ts | 10 ++++--- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/linear_model/LinearRegression.test.ts b/src/linear_model/LinearRegression.test.ts index 2e54a97..6681df6 100644 --- a/src/linear_model/LinearRegression.test.ts +++ b/src/linear_model/LinearRegression.test.ts @@ -17,6 +17,38 @@ describe('LinearRegression', function () { expect(roughlyEqual(lr.intercept as number, 0)).toBe(true) }, 30000) + it('Works on arrays (small example) with custom callbacks', async function () { + let trainingHasStarted = false + const onTrainBegin = async (logs: any) => { + trainingHasStarted = true + console.log('training begins') + } + const lr = new LinearRegression({ + modelFitOptions: { callbacks: [new tf.CustomCallback({ onTrainBegin })] } + }) + await lr.fit([[1], [2]], [2, 4]) + expect(tensorEqual(lr.coef, tf.tensor1d([2]), 0.1)).toBe(true) + expect(roughlyEqual(lr.intercept as number, 0)).toBe(true) + expect(trainingHasStarted).toBe(true) + }, 30000) + + it('Works on arrays (small example) with custom callbacks', async function () { + let trainingHasStarted = false + const onTrainBegin = async (logs: any) => { + trainingHasStarted = true + console.log('training begins') + } + const lr = new LinearRegression({ + modelFitOptions: { callbacks: [new tf.CustomCallback({ onTrainBegin })] } + }) + await lr.fit([[1], [2]], [2, 4]) + + const serialized = await lr.toJSON() + const newModel = await fromJSON(serialized) + expect(tensorEqual(newModel.coef, tf.tensor1d([2]), 0.1)).toBe(true) + expect(roughlyEqual(newModel.intercept as number, 0)).toBe(true) + }, 30000) + it('Works on small multi-output example (small example)', async function () { const lr = new LinearRegression() await lr.fit( diff --git a/src/linear_model/LinearRegression.ts b/src/linear_model/LinearRegression.ts index 995f577..c09a620 100644 --- a/src/linear_model/LinearRegression.ts +++ b/src/linear_model/LinearRegression.ts @@ -41,7 +41,6 @@ export interface LinearRegressionParams { */ fitIntercept?: boolean modelFitOptions?: Partial - } /* @@ -53,7 +52,7 @@ Next steps: /** Linear Least Squares * @example * ```js - * import {LinearRegression} from 'scikitjs' + * import { LinearRegression } from 'scikitjs' * * let X = [ * [1, 2], @@ -63,13 +62,16 @@ Next steps: * [10, 20] * ] * let y = [3, 5, 8, 8, 30] - * const lr = new LinearRegression({fitIntercept: false}) + * const lr = new LinearRegression({ fitIntercept: false }) await lr.fit(X, y) lr.coef.print() // probably around [1, 1] * ``` */ export class LinearRegression extends SGDRegressor { - constructor({ fitIntercept = true, modelFitOptions }: LinearRegressionParams = {}) { + constructor({ + fitIntercept = true, + modelFitOptions + }: LinearRegressionParams = {}) { let tf = getBackend() super({ modelCompileArgs: { From 3d7731cdcfefb6121a40b78f2000cedeb05a7d29 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 19 May 2022 05:00:22 +0000 Subject: [PATCH 03/11] chore(release): 1.23.0 [skip ci] # [1.23.0](https://github.com/javascriptdata/scikit.js/compare/v1.22.0...v1.23.0) (2022-05-19) ### Features * added test case for custom callbacks. works great and somehow serializes. ([7fa5c42](https://github.com/javascriptdata/scikit.js/commit/7fa5c4259902d7dca0a925002cbfaf1937dc2b1b)) * custom modelfitargs for linear models ([2ddcad9](https://github.com/javascriptdata/scikit.js/commit/2ddcad9053097c75bcdd97d234c1f8b605a88241)) --- CHANGELOG.md | 8 ++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 781ea72..a99ede5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# [1.23.0](https://github.com/javascriptdata/scikit.js/compare/v1.22.0...v1.23.0) (2022-05-19) + + +### Features + +* added test case for custom callbacks. works great and somehow serializes. ([7fa5c42](https://github.com/javascriptdata/scikit.js/commit/7fa5c4259902d7dca0a925002cbfaf1937dc2b1b)) +* custom modelfitargs for linear models ([2ddcad9](https://github.com/javascriptdata/scikit.js/commit/2ddcad9053097c75bcdd97d234c1f8b605a88241)) + # [1.22.0](https://github.com/javascriptdata/scikit.js/compare/v1.21.0...v1.22.0) (2022-05-18) diff --git a/package-lock.json b/package-lock.json index 2d1ffc2..04e3044 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "scikitjs", - "version": "1.22.0", + "version": "1.23.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "scikitjs", - "version": "1.22.0", + "version": "1.23.0", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 59bc332..040769c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scikitjs", - "version": "1.22.0", + "version": "1.23.0", "description": "Scikit-Learn for JS", "output": { "node": "dist/node/index.js", From 86a8f06390543a96d2e5fc3cc1a34a320dc3696f Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Wed, 18 May 2022 22:21:51 -0700 Subject: [PATCH 04/11] updated the docs --- README.md | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 3af37d6..fe35a3f 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,18 @@ For use with modern bundlers in a frontend application, simply npm i scikitjs ``` -Usage is similar to other js libraries. +Usage is similar to other js libraries. We depend on the tensorflow library in order to make our calculations fast, but we don't ship it in our bundle. We use it as a peer dependency. General usage is as follows. ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) ``` +This allows us to build a library that can be used in Deno, Node, and the browser with the same configuration. ### Backend Users -For Node.js users who wish to bind to the Tensorflow C++ library, simply +For Node.js users who wish to bind to the Tensorflow C++ library, simply import the tensorflow C++ version, and use that as the tf library ```bash npm i scikitjs @@ -41,14 +44,9 @@ npm i scikitjs But then import the node bindings ```js -import { LinearRegression } from 'scikitjs/node' -``` - -The `scikitjs/node` path uses the new "exports" feature of node (which is available in node v13.3+). -If you are using an older version of node, simply pass in the path to the cjs build - -```js -import { LinearRegression } from 'scikitjs/dist/cjs/index.js' +import * as tf from '@tensorflow/tfjs-node' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) ``` ### Script src @@ -57,16 +55,19 @@ For those that wish to use script src tags, simply ```html ``` ## Simple Example ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) const lr = new LinearRegression({ fitIntercept: false }) const X = [[1], [2]] // 2D Matrix with a single column vector @@ -124,7 +125,9 @@ Turns into #### JavaScript ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) let X = [[1], [2]] let y = [10, 20] @@ -154,7 +157,9 @@ Turns into #### JavaScript ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) let X = [[1], [2]] let y = [10, 20] @@ -189,7 +194,9 @@ Turns into #### JavaScript ```js -import { LogisticRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { LogisticRegression, setBackend } from 'scikitjs' +setBackend(tf) let X = [[1], [-1]] let y = [1, 0] From f66668d4160cb14a7d76cade8593d6290318218e Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Wed, 18 May 2022 22:37:40 -0700 Subject: [PATCH 05/11] updated readme --- README.md | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 3af37d6..d755753 100644 --- a/README.md +++ b/README.md @@ -21,34 +21,33 @@ Documentation site: [www.scikitjs.org](https://www.scikitjs.org) For use with modern bundlers in a frontend application, simply ```bash +npm i @tensorflow/tfjs npm i scikitjs ``` -Usage is similar to other js libraries. +We depend on the tensorflow library in order to make our calculations fast, but we don't ship it in our bundle. +We use it as a peer dependency. General usage is as follows. ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs' +import * as sk from 'scikitjs' +sk.setBackend(tf) ``` +This allows us to build a library that can be used in Deno, Node, and the browser with the same configuration. ### Backend Users -For Node.js users who wish to bind to the Tensorflow C++ library, simply +For Node.js users who wish to bind to the Tensorflow C++ library, simply import the tensorflow C++ version, and use that as the tf library ```bash +npm i @tensorflow/tfjs-node npm i scikitjs ``` -But then import the node bindings - -```js -import { LinearRegression } from 'scikitjs/node' -``` - -The `scikitjs/node` path uses the new "exports" feature of node (which is available in node v13.3+). -If you are using an older version of node, simply pass in the path to the cjs build - ```js -import { LinearRegression } from 'scikitjs/dist/cjs/index.js' +import * as tf from '@tensorflow/tfjs-node' +import * as sk from 'scikitjs' +sk.setBackend(tf) ``` ### Script src @@ -57,16 +56,21 @@ For those that wish to use script src tags, simply ```html ``` ## Simple Example ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { setBackend, LinearRegression } from 'scikitjs' +setBackend(tf) const lr = new LinearRegression({ fitIntercept: false }) const X = [[1], [2]] // 2D Matrix with a single column vector @@ -124,7 +128,9 @@ Turns into #### JavaScript ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { setBackend, LinearRegression } from 'scikitjs' +setBackend(tf) let X = [[1], [2]] let y = [10, 20] @@ -154,7 +160,9 @@ Turns into #### JavaScript ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { setBackend, LinearRegression } from 'scikitjs' +setBackend(tf) let X = [[1], [2]] let y = [10, 20] @@ -189,7 +197,9 @@ Turns into #### JavaScript ```js -import { LogisticRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs-node' +import { setBackend, LogisticRegression } from 'scikitjs' +setBackend(tf) let X = [[1], [-1]] let y = [1, 0] From f20e5c42e0be86a38429623b4c9df9c1edaf24a8 Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Sat, 21 May 2022 10:22:31 -0700 Subject: [PATCH 06/11] more updates to readme --- README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index d755753..07fd6a6 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,7 @@ Documentation site: [www.scikitjs.org](https://www.scikitjs.org) For use with modern bundlers in a frontend application, simply ```bash -npm i @tensorflow/tfjs -npm i scikitjs +npm i @tensorflow/tfjs scikitjs ``` We depend on the tensorflow library in order to make our calculations fast, but we don't ship it in our bundle. @@ -40,16 +39,24 @@ This allows us to build a library that can be used in Deno, Node, and the browse For Node.js users who wish to bind to the Tensorflow C++ library, simply import the tensorflow C++ version, and use that as the tf library ```bash -npm i @tensorflow/tfjs-node -npm i scikitjs +npm i @tensorflow/tfjs-node scikitjs ``` +```js +const tf = require('@tensorflow/tfjs-node') +const sk = require('scikitjs') +sk.setBackend(tf) +``` + +Note: If you have ESM enabled (by setting type="module" in your package.json), then you can consume this libary with import / export, like in the following code block. + ```js import * as tf from '@tensorflow/tfjs-node' import * as sk from 'scikitjs' sk.setBackend(tf) ``` + ### Script src For those that wish to use script src tags, simply @@ -68,7 +75,7 @@ For those that wish to use script src tags, simply ## Simple Example ```js -import * as tf from '@tensorflow/tfjs-node' +import * as tf from '@tensorflow/tfjs' import { setBackend, LinearRegression } from 'scikitjs' setBackend(tf) @@ -128,7 +135,7 @@ Turns into #### JavaScript ```js -import * as tf from '@tensorflow/tfjs-node' +import * as tf from '@tensorflow/tfjs' import { setBackend, LinearRegression } from 'scikitjs' setBackend(tf) @@ -160,7 +167,7 @@ Turns into #### JavaScript ```js -import * as tf from '@tensorflow/tfjs-node' +import * as tf from '@tensorflow/tfjs' import { setBackend, LinearRegression } from 'scikitjs' setBackend(tf) @@ -197,7 +204,7 @@ Turns into #### JavaScript ```js -import * as tf from '@tensorflow/tfjs-node' +import * as tf from '@tensorflow/tfjs' import { setBackend, LogisticRegression } from 'scikitjs' setBackend(tf) From 45c4305983c78f876457c35be602362a6935b6f8 Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Sat, 21 May 2022 12:20:13 -0700 Subject: [PATCH 07/11] updated docs --- README.md | 2 +- docs/docs/tutorial.md | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 07fd6a6..564093d 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ const sk = require('scikitjs') sk.setBackend(tf) ``` -Note: If you have ESM enabled (by setting type="module" in your package.json), then you can consume this libary with import / export, like in the following code block. +Note: If you have ESM enabled (by setting type="module" in your package.json), then you can consume this library with import / export, like in the following code block. ```js import * as tf from '@tensorflow/tfjs-node' diff --git a/docs/docs/tutorial.md b/docs/docs/tutorial.md index 75ca34a..d251735 100644 --- a/docs/docs/tutorial.md +++ b/docs/docs/tutorial.md @@ -11,13 +11,13 @@ Let's discover **Scikit.js in less than 5 minutes**. Get started by **installing the library**. ```shell -npm install scikitjs +npm install scikitjs @tensorflow/tfjs ``` or ```shell -yarn add scikitjs +yarn add scikitjs @tensorflow/tfjs ``` ## Build a model @@ -25,7 +25,9 @@ yarn add scikitjs Build a simple Linear Regression ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) let X = [ [2, 3], From 81634833bb6063468e8668aba5483e0b0e02eef4 Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Sat, 21 May 2022 12:27:50 -0700 Subject: [PATCH 08/11] better tutorial docs --- docs/docs/tutorial.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/docs/tutorial.md b/docs/docs/tutorial.md index d251735..8b1014b 100644 --- a/docs/docs/tutorial.md +++ b/docs/docs/tutorial.md @@ -8,7 +8,7 @@ Let's discover **Scikit.js in less than 5 minutes**. ## Getting Started -Get started by **installing the library**. +Get started by **installing the library as well as it's dependencies**. ```shell npm install scikitjs @tensorflow/tfjs @@ -25,10 +25,12 @@ yarn add scikitjs @tensorflow/tfjs Build a simple Linear Regression ```js +// import tensorflow and register it as the backend import * as tf from '@tensorflow/tfjs' import { LinearRegression, setBackend } from 'scikitjs' setBackend(tf) +// Perform a linear regression let X = [ [2, 3], [1, 4], From 63a2197f877be21399e0dee3206729ed424552d0 Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Sat, 21 May 2022 12:49:54 -0700 Subject: [PATCH 09/11] more updates --- docs/docs/python.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/docs/python.md b/docs/docs/python.md index deb4ee0..081808c 100644 --- a/docs/docs/python.md +++ b/docs/docs/python.md @@ -47,7 +47,9 @@ Turns into #### javascript ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) let X = [[1], [2]] let y = [10, 20] @@ -77,7 +79,9 @@ Turns into #### javascript ```js -import { LinearRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs' +import { LinearRegression, setBackend } from 'scikitjs' +setBackend(tf) let X = [[1], [2]] let y = [10, 20] @@ -112,7 +116,9 @@ Turns into #### javascript ```js -import { LogisticRegression } from 'scikitjs' +import * as tf from '@tensorflow/tfjs' +import { LogisticRegression, setBackend } from 'scikitjs' +setBackend(tf) let X = [[1], [-1]] let y = [1, 0] From 10141cd9213a2da7aa1d3122f3143d29abb574d8 Mon Sep 17 00:00:00 2001 From: Dan Crescimanno Date: Sun, 22 May 2022 01:50:29 -0700 Subject: [PATCH 10/11] feat: sgd classifier can not train on categorical variables, as well as one-hot encoded variables --- src/linear_model/LogisticRegression.test.ts | 62 +++++++++++++++++++++ src/linear_model/SgdClassifier.ts | 30 ++++++---- src/mixins.ts | 13 ++++- 3 files changed, 92 insertions(+), 13 deletions(-) diff --git a/src/linear_model/LogisticRegression.test.ts b/src/linear_model/LogisticRegression.test.ts index 1d38ba5..80b3964 100644 --- a/src/linear_model/LogisticRegression.test.ts +++ b/src/linear_model/LogisticRegression.test.ts @@ -47,6 +47,68 @@ describe('LogisticRegression', function () { expect(results.arraySync()).toEqual([0, 0, 0, 1, 1, 1]) expect(logreg.score(X, y) > 0.5).toBe(true) }, 30000) + it('Test of the function used with 2 classes (one hot)', async function () { + let X = [ + [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] + ] + let y = [ + [1, 0], + [1, 0], + [1, 0], + [1, 0], + [1, 0], + [1, 0], + [1, 0], + [1, 0], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1], + [0, 1] + ] + + let Xtest = [ + [0, -2], + [1, 0.5], + [1.5, -1], + [1, 4.5], + [2, 3.5], + [1.5, 5] + ] + + let logreg = new LogisticRegression({ penalty: 'none' }) + await logreg.fit(X, y) + let probabilities = logreg.predictProba(X) + expect(probabilities instanceof tf.Tensor).toBe(true) + let results = logreg.predict(Xtest) // compute results of the training set + expect(results.arraySync()).toEqual([ + [1, 0], + [1, 0], + [1, 0], + [0, 1], + [0, 1], + [0, 1] + ]) + expect(logreg.score(X, y) > 0.5).toBe(true) + }, 30000) it('Test of the prediction with 3 classes', async function () { let X = [ [0, -1], diff --git a/src/linear_model/SgdClassifier.ts b/src/linear_model/SgdClassifier.ts index 56a55e1..bc9ee15 100644 --- a/src/linear_model/SgdClassifier.ts +++ b/src/linear_model/SgdClassifier.ts @@ -13,7 +13,10 @@ * ========================================================================== */ -import { convertToNumericTensor1D, convertToNumericTensor2D } from '../utils' +import { + convertToNumericTensor1D_2D, + convertToNumericTensor2D +} from '../utils' import { Scikit2D, Scikit1D, @@ -23,8 +26,7 @@ import { Tensor2D, Tensor, ModelCompileArgs, - ModelFitArgs, - RecursiveArray + ModelFitArgs } from '../types' import { OneHotEncoder } from '../preprocessing/OneHotEncoder' import { assert } from '../typesUtils' @@ -103,6 +105,7 @@ export class SGDClassifier extends ClassifierMixin { lossType: LossTypes oneHot: OneHotEncoder tf: any + isMultiOutput: boolean constructor({ modelFitArgs, @@ -119,6 +122,7 @@ export class SGDClassifier extends ClassifierMixin { this.denseLayerArgs = denseLayerArgs this.optimizerType = optimizerType this.lossType = lossType + this.isMultiOutput = false // Next steps: Implement "drop" mechanics for OneHotEncoder // There is a possibility to do a drop => if_binary which would // squash down on the number of variables that we'd have to learn @@ -200,12 +204,17 @@ export class SGDClassifier extends ClassifierMixin { * // lr model weights have been updated */ - public async fit(X: Scikit2D, y: Scikit1D): Promise { + public async fit( + X: Scikit2D, + y: Scikit1D | Scikit2D + ): Promise { let XTwoD = convertToNumericTensor2D(X) - let yOneD = convertToNumericTensor1D(y) + let yOneD = convertToNumericTensor1D_2D(y) const yTwoD = this.initializeModelForClassification(yOneD) - + if (yOneD.shape.length > 1) { + this.isMultiOutput = true + } if (this.model.layers.length === 0) { this.initializeModel(XTwoD, yTwoD) } @@ -344,6 +353,9 @@ export class SGDClassifier extends ClassifierMixin { public predict(X: Scikit2D): Tensor1D { assert(this.model.layers.length > 0, 'Need to call "fit" before "predict"') const y2D = this.predictProba(X) + if (this.isMultiOutput) { + return this.tf.oneHot(y2D.argMax(1), y2D.shape[1]) + } return this.tf.tensor1d(this.oneHot.inverseTransform(y2D)) } @@ -418,10 +430,4 @@ export class SGDClassifier extends ClassifierMixin { return intercept } - - private getModelWeight(): Promise> { - return Promise.all( - this.model.getWeights().map((weight: any) => weight.array()) - ) - } } diff --git a/src/mixins.ts b/src/mixins.ts index 2d71b81..225f7f7 100644 --- a/src/mixins.ts +++ b/src/mixins.ts @@ -1,6 +1,8 @@ import { Scikit2D, Scikit1D, Tensor2D, Tensor1D } from './types' import { r2Score, accuracyScore } from './metrics/metrics' import { Serialize } from './simpleSerializer' +import { assert, isScikit2D } from './typesUtils' +import { convertToNumericTensor1D_2D } from './utils' export class TransformerMixin extends Serialize { // We assume that fit and transform exist [x: string]: any @@ -35,8 +37,17 @@ export class ClassifierMixin extends Serialize { [x: string]: any EstimatorType = 'classifier' - public score(X: Scikit2D, y: Scikit1D): number { + public score(X: Scikit2D, y: Scikit1D | Scikit2D): number { const yPred = this.predict(X) + const yTrue = convertToNumericTensor1D_2D(y) + assert( + yPred.shape.length === yTrue.shape.length, + "The shape of the model output doesn't match the shape of the actual y values" + ) + + if (isScikit2D(y)) { + return accuracyScore(yTrue.argMax(1) as Scikit1D, yPred.argMax(1)) + } return accuracyScore(y, yPred) } } From f388a67c0127a047b70971b790cfd29e7aa3f709 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Sun, 22 May 2022 09:20:17 +0000 Subject: [PATCH 11/11] chore(release): 1.24.0 [skip ci] # [1.24.0](https://github.com/javascriptdata/scikit.js/compare/v1.23.0...v1.24.0) (2022-05-22) ### Features * sgd classifier can not train on categorical variables, as well as one-hot encoded variables ([10141cd](https://github.com/javascriptdata/scikit.js/commit/10141cd9213a2da7aa1d3122f3143d29abb574d8)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a99ede5..6953079 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [1.24.0](https://github.com/javascriptdata/scikit.js/compare/v1.23.0...v1.24.0) (2022-05-22) + + +### Features + +* sgd classifier can not train on categorical variables, as well as one-hot encoded variables ([10141cd](https://github.com/javascriptdata/scikit.js/commit/10141cd9213a2da7aa1d3122f3143d29abb574d8)) + # [1.23.0](https://github.com/javascriptdata/scikit.js/compare/v1.22.0...v1.23.0) (2022-05-19) diff --git a/package-lock.json b/package-lock.json index 04e3044..8fddc13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "scikitjs", - "version": "1.23.0", + "version": "1.24.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "scikitjs", - "version": "1.23.0", + "version": "1.24.0", "hasInstallScript": true, "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 040769c..9537fb5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scikitjs", - "version": "1.23.0", + "version": "1.24.0", "description": "Scikit-Learn for JS", "output": { "node": "dist/node/index.js", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy