Skip to content

Commit b4a0d76

Browse files
authored
🌈 New Release: ml5.js v0.3.0 πŸŽ‰ (#386)
* add ImageData as valid image type * add CVAE * add latent dim * add random generate * fix p5Image support * fix CVAE parameter * Added a parameter to the save function so that it is possible to add a custom filename to the model that is saved. * Unet fix (#357) Add uNet model and additional fixes - adds uNet model from @zaidalyafeai ✨ - adds preload() for uNet - uses loadImage on window.loadImage vs. window.p5.loadImage * Added sentiment analysis (#339) * Added sentiment analysis * delete files * fixed issues for pull request * add p5 utils (#358) * fix charRNN tests (#349) * add tests to CharRNN * test(CharRNN): add tests to CharRNN added descriptive tests to ensure CharRNN behaves like its example * remove dist * Add tests to CharRNN (#307) * add tests to CharRNN * test(CharRNN): add tests to CharRNN added descriptive tests to ensure CharRNN behaves like its example * remove dist * check preload support for other nets and classifiers (#313) Adds specified nets to support preload // TODO: add examples showing appropriate use of preload * change CharRNN specs to meet time limit, add initial code for videoClassifier * videoClassifier functioning * charRNN functional * fix out of date file * add preload support for cvae (#360) * Update TensorFlow.js to 1.0.2 (#336) * upgrade to tfjs1.0.0 * fix loadModel * fix buffer * fix getLayer * Adds fixes to PR #332 for tfjs 1.0.2 updates (#366) * upgrade to tfjs1.0.0 * fix loadModel * fix buffer * fix getLayer * updated package lock * added @tensorflow/tfjs-core as dependency * add graphmodel for infer (#365) * Add DCGAN Model into ml5 (#351) * Create index.js * updated index.js and DCGAN/index.js * DCGAN updates and fixes (#362) * Create index.js * fixed DCGAN errors * updates p5Utils destructuring, fixes linting issues, and updates tfjs to 1.0.2 to match dcgan reqs * fixed cvae * use this.model instead of using model as param to this.compute() * Makes UNET compatible with tfjs 1.0.2 (#367) * added package-lock * updated UNET for use with tfjs 1.0.2 * Makes Sentiment compatible with tfjs 1.0.2 (#368) * added package-lock * rm sentiment-node * changed loadModel to loadLayersModel * Makes CVAE compatible with tfjs 1.0.2 (#369) * added package-lock * updates cvae to tfjs 1.0.2 api * update tfjs to 1.1.2 (#373) * featureExtractor: accept HTML canvas or p5 canvas when addImage(), classify() or predict() * fix: KNNClassifier accepts a number as class index when addExample(features, number) * added check for moz browser ref:https://stackoverflow.com/questions/48623376/typeerror-capturestream-is-not-a-function (#375) This addresses the video capture breaking in YOLO and potentially other video based functions that require the use of .captureStream(). As the .captureStream() function is still experimental, this adds the moz prefix and a browser check to see if we are using firefox or not. * rm todo * updated package-lock.json * Adds label number option to featureExtractor.classification() (#376) * changed numClasses to numLabels * added num label as option to classification() * updated FeatureExtractor Test with numLabels * adds object as param to .classificaiton() * moved options into this.config * fix feature extractor test - add .config * added pose:poseWithParts into .singlePose() (#381) * Adds jsdoc inline-documentation - work in progress (#378) * added jsdoc documentation for imageClassifier * adds dcgan documentation - needs checking * Add jsdoc (#382) * Add jsdocs for CharRNN * Add jsdocs for CVAE * Add jsdocs for FeatureExtractor * Add jsdocs for KNN * Add jsdocs for PitchDetection * Add jsdocs for Pix2pix * Add jsdocs for posenet * Add jsdocs for Sentiment * Add jsdocs for styletransfer * add linebreaks to long lines * added basic docs to sketchRnn * added basic docs to unet * added basic docs to word2vec * added basic yolo docs * Adds V0.3.0 to package.json and Readme for new release (#385) * changed package.json to v0.3.0 * added latest version reference in readme * added lib min - will remove after this release
1 parent 03bab68 commit b4a0d76

File tree

30 files changed

+1248
-9568
lines changed

30 files changed

+1248
-9568
lines changed

β€Ž.gitignoreβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.env
12
dev
23
examples/es6/node_modules
34
experiments/node_modules
@@ -19,4 +20,4 @@ website/node_modules
1920
website/i18n/*
2021
!website/i18n/en.json
2122

22-
yarn-error.log
23+
yarn-error.log

β€ŽREADME.mdβ€Ž

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,20 @@ ml5.js is heavily inspired by [Processing](https://processing.org/) and [p5.js](
1919

2020
There are several ways you can use the ml5.js library:
2121

22-
* You can use the latest version (0.2.3) by adding it to the head section of your HTML document:
22+
* You can use the latest version (0.3.0) by adding it to the head section of your HTML document:
2323

24-
**v0.2.3**
24+
**v0.3.0**
2525
```javascript
26-
<script src="https://unpkg.com/ml5@0.2.3/dist/ml5.min.js" type="text/javascript"></script>
26+
<script src="https://unpkg.com/ml5@0.3.0/dist/ml5.min.js" type="text/javascript"></script>
2727
```
2828

2929
* If you need to use an earlier version for any reason, you can change the version number.
3030

31+
**v0.2.3**
32+
```javascript
33+
<script src="https://unpkg.com/[email protected]/dist/ml5.min.js" type="text/javascript"></script>
34+
```
35+
3136
**v0.1.3**
3237

3338
```javascript

β€Ždist/ml5.min.jsβ€Ž

Lines changed: 10 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Ždist/ml5.min.js.mapβ€Ž

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žpackage-lock.jsonβ€Ž

Lines changed: 97 additions & 175 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žpackage.jsonβ€Ž

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ml5",
3-
"version": "0.2.3",
3+
"version": "0.3.0",
44
"description": "A friendly machine learning library for the web.",
55
"main": "dist/ml5.min.js",
66
"directories": {
@@ -88,11 +88,11 @@
8888
]
8989
},
9090
"dependencies": {
91-
"@magenta/sketch": "0.1.2",
92-
"@tensorflow-models/mobilenet": "0.2.2",
93-
"@tensorflow-models/posenet": "0.2.2",
94-
"@tensorflow-models/knn-classifier": "0.2.2",
95-
"@tensorflow/tfjs": "0.13.0",
91+
"@magenta/sketch": "0.2.0",
92+
"@tensorflow-models/knn-classifier": "1.0.0",
93+
"@tensorflow-models/mobilenet": "1.0.0",
94+
"@tensorflow-models/posenet": "1.0.0",
95+
"@tensorflow/tfjs": "1.1.2",
9696
"events": "^3.0.0"
9797
}
9898
}

β€Žsrc/CVAE/index.jsβ€Ž

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// Copyright (c) 2018 ml5
2+
//
3+
// This software is released under the MIT License.
4+
// https://opensource.org/licenses/MIT
5+
6+
/* eslint prefer-destructuring: ["error", {AssignmentExpression: {array: false}}] */
7+
/* eslint no-await-in-loop: "off" */
8+
/*
9+
* CVAE: Run conditional auto-encoder for pro-trained model
10+
*/
11+
12+
import * as tf from '@tensorflow/tfjs';
13+
import callCallback from '../utils/callcallback';
14+
15+
class Cvae {
16+
/**
17+
* Create a Conditional Variational Autoencoder (CVAE).
18+
* @param {String} modelPath - Required. The url path to your model.
19+
* @param {function} callback - Required. A function to run once the model has been loaded.
20+
*/
21+
constructor(modelPath, callback) {
22+
/**
23+
* Boolean value that specifies if the model has loaded.
24+
* @type {boolean}
25+
* @public
26+
*/
27+
this.ready = false;
28+
this.model = {};
29+
this.latentDim = tf.randomUniform([1, 16]);
30+
this.modelPath = modelPath;
31+
this.modelPathPrefix = '';
32+
33+
this.jsonLoader().then(val => {
34+
this.modelPathPrefix = this.modelPath.split('manifest.json')[0]
35+
this.ready = callCallback(this.loadCVAEModel(this.modelPathPrefix+val.model), callback);
36+
this.labels = val.labels;
37+
// get an array full of zero with the length of labels [0, 0, 0 ...]
38+
this.labelVector = Array(this.labels.length+1).fill(0);
39+
});
40+
}
41+
42+
// load tfjs model that is converted by tensorflowjs with graph and weights
43+
async loadCVAEModel(modelPath) {
44+
this.model = await tf.loadLayersModel(modelPath);
45+
return this;
46+
}
47+
48+
/**
49+
* Generate a random result.
50+
* @param {String} label - A label of the feature your want to generate
51+
* @param {function} callback - A function to handle the results of ".generate()". Likely a function to do something with the generated image data.
52+
* @return {raw: ImageData, src: Blob, image: p5.Image}
53+
*/
54+
async generate(label, callback) {
55+
return callCallback(this.generateInternal(label), callback);
56+
}
57+
58+
loadAsync(url){
59+
return new Promise((resolve, reject) => {
60+
if(!this.ready) reject();
61+
loadImage(url, (img) => {
62+
resolve(img);
63+
});
64+
});
65+
};
66+
67+
getBlob(inputCanvas) {
68+
return new Promise((resolve, reject) => {
69+
if (!this.ready) reject();
70+
71+
inputCanvas.toBlob((blob) => {
72+
resolve(blob);
73+
});
74+
});
75+
}
76+
77+
checkP5() {
78+
if (typeof window !== 'undefined' && window.p5 && this
79+
&& window.p5.Image && typeof window.p5.Image === 'function') return true;
80+
return false;
81+
}
82+
83+
async generateInternal(label) {
84+
const res = tf.tidy(() => {
85+
this.latentDim = tf.randomUniform([1, 16]);
86+
const cursor = this.labels.indexOf(label);
87+
if (cursor < 0) {
88+
console.log('Wrong input of the label!');
89+
return [undefined, undefined]; // invalid input just return;
90+
}
91+
92+
this.labelVector = this.labelVector.map(() => 0); // clear vector
93+
this.labelVector[cursor+1] = 1;
94+
95+
const input = tf.tensor([this.labelVector]);
96+
97+
const temp = this.model.predict([this.latentDim, input]);
98+
return temp.reshape([temp.shape[1], temp.shape[2], temp.shape[3]]);
99+
});
100+
101+
const raws = await tf.browser.toPixels(res);
102+
103+
const canvas = document.createElement('canvas'); // consider using offScreneCanvas
104+
const ctx = canvas.getContext('2d');
105+
const [x, y] = res.shape;
106+
canvas.width = x;
107+
canvas.height = y;
108+
const imgData = ctx.createImageData(x, y);
109+
const data = imgData.data;
110+
for (let i = 0; i < x * y * 4; i += 1) data[i] = raws[i];
111+
ctx.putImageData(imgData, 0, 0);
112+
113+
const src = URL.createObjectURL(await this.getBlob(canvas));
114+
let image;
115+
/* global loadImage */
116+
if (this.checkP5()) image = await this.loadAsync(src);
117+
return { src, raws, image };
118+
}
119+
120+
async jsonLoader() {
121+
return new Promise((resolve, reject) => {
122+
const xhr = new XMLHttpRequest();
123+
xhr.open('GET', this.modelPath);
124+
125+
xhr.onload = () => {
126+
const json = JSON.parse(xhr.responseText);
127+
resolve(json);
128+
};
129+
xhr.onerror = (error) => {
130+
reject(error);
131+
};
132+
xhr.send();
133+
});
134+
}
135+
}
136+
137+
const CVAE = (model, callback) => new Cvae(model, callback);
138+
139+
140+
export default CVAE;

β€Žsrc/CharRNN/index.jsβ€Ž

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,35 @@ const regexWeights = /weights|weight|kernel|kernels|w/gi;
1919
const regexFullyConnected = /softmax/gi;
2020

2121
class CharRNN {
22+
/**
23+
* Create a CharRNN.
24+
* @param {String} modelPath - The path to the trained charRNN model.
25+
* @param {function} callback - Optional. A callback to be called once
26+
* the model has loaded. If no callback is provided, it will return a
27+
* promise that will be resolved once the model has loaded.
28+
*/
2229
constructor(modelPath, callback) {
30+
/**
31+
* Boolean value that specifies if the model has loaded.
32+
* @type {boolean}
33+
* @public
34+
*/
2335
this.ready = false;
36+
37+
/**
38+
* The pre-trained charRNN model.
39+
* @type {model}
40+
* @public
41+
*/
2442
this.model = {};
2543
this.cellsAmount = 0;
2644
this.cells = [];
2745
this.zeroState = { c: [], h: [] };
46+
/**
47+
* The vocabulary size (or total number of possible characters).
48+
* @type {c: Array, h: Array}
49+
* @public
50+
*/
2851
this.state = { c: [], h: [] };
2952
this.vocab = {};
3053
this.vocabSize = 0;
@@ -128,7 +151,7 @@ class CharRNN {
128151
let probabilitiesNormalized = []; // will contain final probabilities (normalized)
129152

130153
for (let i = 0; i < userInput.length + length + -1; i += 1) {
131-
const onehotBuffer = tf.buffer([1, this.vocabSize]);
154+
const onehotBuffer = await tf.buffer([1, this.vocabSize]);
132155
onehotBuffer.set(1.0, 0, input);
133156
const onehot = onehotBuffer.toTensor();
134157
let output;
@@ -174,17 +197,45 @@ class CharRNN {
174197
};
175198
}
176199

200+
/**
201+
* Reset the model state.
202+
*/
177203
reset() {
178204
this.state = this.zeroState;
179205
}
180206

207+
/**
208+
* @typedef {Object} options
209+
* @property {String} seed
210+
* @property {number} length
211+
* @property {number} temperature
212+
*/
213+
181214
// stateless
215+
/**
216+
* Generates content in a stateless manner, based on some initial text
217+
* (known as a "seed"). Returns a string.
218+
* @param {options} options - An object specifying the input parameters of
219+
* seed, length and temperature. Default length is 20, temperature is 0.5
220+
* and seed is a random character from the model. The object should look like
221+
* this:
222+
* @param {function} callback - Optional. A function to be called when the model
223+
* has generated content. If no callback is provided, it will return a promise
224+
* that will be resolved once the model has generated new content.
225+
*/
182226
async generate(options, callback) {
183227
this.reset();
184228
return callCallback(this.generateInternal(options), callback);
185229
}
186230

187231
// stateful
232+
/**
233+
* Predict the next character based on the model's current state.
234+
* @param {number} temp
235+
* @param {function} callback - Optional. A function to be called when the
236+
* model finished adding the seed. If no callback is provided, it will
237+
* return a promise that will be resolved once the prediction has been generated.
238+
*/
188239
async predict(temp, callback) {
189240
let probabilitiesNormalized = [];
190241
const temperature = temp > 0 ? temp : 0.1;
@@ -212,6 +263,13 @@ class CharRNN {
212263
};
213264
}
214265

266+
/**
267+
* Feed a string of characters to the model state.
268+
* @param {String} inputSeed - A string to feed the charRNN model state.
269+
* @param {function} callback - Optional. A function to be called when
270+
* the model finished adding the seed. If no callback is provided, it
271+
* will return a promise that will be resolved once seed has been fed.
272+
*/
215273
async feed(inputSeed, callback) {
216274
await this.ready;
217275
const seed = Array.from(inputSeed);
@@ -223,7 +281,7 @@ class CharRNN {
223281

224282
let input = encodedInput[0];
225283
for (let i = 0; i < seed.length; i += 1) {
226-
const onehotBuffer = tf.buffer([1, this.vocabSize]);
284+
const onehotBuffer = await tf.buffer([1, this.vocabSize]);
227285
onehotBuffer.set(1.0, 0, input);
228286
const onehot = onehotBuffer.toTensor();
229287
let output;

β€Žsrc/CharRNN/index_test.jsβ€Ž

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const RNN_MODEL_URL = 'https://raw.githubusercontent.com/ml5js/ml5-data-and-mode
99

1010
const RNN_MODEL_DEFAULTS = {
1111
cellsAmount: 2,
12-
vocabSize: 64
12+
vocabSize: 223
1313
};
1414

1515
const RNN_DEFAULTS = {
@@ -21,15 +21,15 @@ const RNN_DEFAULTS = {
2121

2222
const RNN_OPTIONS = {
2323
seed: 'the meaning of pizza is: ',
24-
length: 100,
24+
length: 30,
2525
temperature: 0.7
2626
}
2727

2828
describe('charRnn', () => {
2929
let rnn;
3030

3131
beforeAll(async () => {
32-
jasmine.DEFAULT_TIMEOUT_INTERVAL = 120000; //set extra long interval due to issues with CharRNN generation time
32+
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000; //set extra long interval due to issues with CharRNN generation time
3333
rnn = await charRNN(RNN_MODEL_URL, undefined);
3434
});
3535

@@ -52,9 +52,9 @@ describe('charRnn', () => {
5252
expect(result.sample.length).toBe(20);
5353
});
5454

55-
// it('generates content that follows the set options', async() => {
56-
// const result = await rnn.generate(RNN_OPTIONS);
57-
// expect(result.sample.length).toBe(100);
58-
// });
55+
it('generates content that follows the set options', async() => {
56+
const result = await rnn.generate(RNN_OPTIONS);
57+
expect(result.sample.length).toBe(30);
58+
});
5959
});
6060
});

0 commit comments

Comments
Β (0)