@epfml/discojs-web 2.1.2-p20240506085037.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/image_loader.d.ts +5 -0
- package/dist/data/image_loader.js +7 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/data/tabular_loader.d.ts +4 -0
- package/dist/data/tabular_loader.js +8 -0
- package/dist/data/text_loader.d.ts +4 -0
- package/dist/data/text_loader.js +9 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +1 -0
- package/dist/memory/memory.d.ts +31 -0
- package/dist/memory/memory.js +134 -0
- package/package.json +28 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import { data } from '@epfml/discojs';
|
|
3
|
+
export class TabularLoader extends data.TabularLoader {
|
|
4
|
+
async loadDatasetFrom(source, csvConfig) {
|
|
5
|
+
const file = new tf.data.FileDataSource(source);
|
|
6
|
+
return Promise.resolve(new tf.data.CSVDataset(file, csvConfig));
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import { data } from '@epfml/discojs';
|
|
3
|
+
export class TextLoader extends data.TextLoader {
|
|
4
|
+
loadDatasetFrom(source) {
|
|
5
|
+
const file = new tf.data.FileDataSource(source);
|
|
6
|
+
const dataset = new tf.data.TextLineDataset(file).filter(s => s != ' '); // newline creates empty strings
|
|
7
|
+
return Promise.resolve(dataset);
|
|
8
|
+
}
|
|
9
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IndexedDB } from './memory.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { IndexedDB } from './memory.js';
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import type { Path, Model, ModelInfo, ModelSource } from '@epfml/discojs';
|
|
3
|
+
import { Memory } from '@epfml/discojs';
|
|
4
|
+
export declare class IndexedDB extends Memory {
|
|
5
|
+
pathFor(source: ModelSource): Path;
|
|
6
|
+
infoFor(source: ModelSource): ModelInfo;
|
|
7
|
+
getModelMetadata(source: ModelSource): Promise<tf.io.ModelArtifactsInfo | undefined>;
|
|
8
|
+
contains(source: ModelSource): Promise<boolean>;
|
|
9
|
+
getModel(source: ModelSource): Promise<Model>;
|
|
10
|
+
deleteModel(source: ModelSource): Promise<void>;
|
|
11
|
+
loadModel(source: ModelSource): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Saves the working model to the source.
|
|
14
|
+
* @param source the destination
|
|
15
|
+
* @param model the model
|
|
16
|
+
*/
|
|
17
|
+
updateWorkingModel(source: ModelSource, model: Model): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a saved copy of the working model corresponding to the source.
|
|
20
|
+
* @param source the source
|
|
21
|
+
*/
|
|
22
|
+
saveWorkingModel(source: ModelSource): Promise<Path>;
|
|
23
|
+
saveModel(source: ModelSource, model: Model): Promise<Path>;
|
|
24
|
+
/**
|
|
25
|
+
* Downloads the model corresponding to the source.
|
|
26
|
+
* @param source the source
|
|
27
|
+
*/
|
|
28
|
+
downloadModel(source: ModelSource): Promise<void>;
|
|
29
|
+
latestDuplicate(source: ModelSource): Promise<number | undefined>;
|
|
30
|
+
duplicateSource(source: ModelSource): Promise<ModelInfo>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions used to load and save TFJS models from IndexedDB. The
|
|
3
|
+
* working model is the model currently being trained for a task. Saved models
|
|
4
|
+
* are models that were explicitly saved to IndexedDB. The two working/ and saved/
|
|
5
|
+
* folders are invisible to the user. The user only interacts with the saved/
|
|
6
|
+
* folder via the model library. The working/ folder is only used by the backend.
|
|
7
|
+
* The working model is loaded from IndexedDB for training (model.fit) only.
|
|
8
|
+
*/
|
|
9
|
+
import { Map } from 'immutable';
|
|
10
|
+
import * as tf from '@tensorflow/tfjs';
|
|
11
|
+
import { Memory, ModelType, models } from '@epfml/discojs';
|
|
12
|
+
export class IndexedDB extends Memory {
|
|
13
|
+
pathFor(source) {
|
|
14
|
+
if (typeof source === 'string') {
|
|
15
|
+
return source;
|
|
16
|
+
}
|
|
17
|
+
if (source.type === undefined || source.taskID === undefined || source.name === undefined) {
|
|
18
|
+
throw new TypeError('source incomplete');
|
|
19
|
+
}
|
|
20
|
+
const version = source.version ?? 0;
|
|
21
|
+
return `indexeddb://${source.type}/${source.taskID}/${source.name}@${version}`;
|
|
22
|
+
}
|
|
23
|
+
infoFor(source) {
|
|
24
|
+
if (typeof source !== 'string') {
|
|
25
|
+
return source;
|
|
26
|
+
}
|
|
27
|
+
const [stringType, taskID, fullName] = source.split('/').splice(2);
|
|
28
|
+
const type = stringType === 'working' ? ModelType.WORKING : ModelType.SAVED;
|
|
29
|
+
const [name, versionSuffix] = fullName.split('@');
|
|
30
|
+
const version = versionSuffix === undefined ? 0 : Number(versionSuffix);
|
|
31
|
+
return { type, taskID, name, version };
|
|
32
|
+
}
|
|
33
|
+
async getModelMetadata(source) {
|
|
34
|
+
const models = await tf.io.listModels();
|
|
35
|
+
return models[this.pathFor(source)];
|
|
36
|
+
}
|
|
37
|
+
async contains(source) {
|
|
38
|
+
return await this.getModelMetadata(source) !== undefined;
|
|
39
|
+
}
|
|
40
|
+
async getModel(source) {
|
|
41
|
+
return new models.TFJS(await tf.loadLayersModel(this.pathFor(source)));
|
|
42
|
+
}
|
|
43
|
+
async deleteModel(source) {
|
|
44
|
+
await tf.io.removeModel(this.pathFor(source));
|
|
45
|
+
}
|
|
46
|
+
async loadModel(source) {
|
|
47
|
+
const src = this.infoFor(source);
|
|
48
|
+
if (src.type === ModelType.WORKING) {
|
|
49
|
+
// Model is already loaded
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
await tf.io.copyModel(this.pathFor(src), this.pathFor({ ...src, type: ModelType.WORKING, version: 0 }));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Saves the working model to the source.
|
|
56
|
+
* @param source the destination
|
|
57
|
+
* @param model the model
|
|
58
|
+
*/
|
|
59
|
+
async updateWorkingModel(source, model) {
|
|
60
|
+
const src = this.infoFor(source);
|
|
61
|
+
if (src.type !== undefined && src.type !== ModelType.WORKING) {
|
|
62
|
+
throw new Error('expected working model');
|
|
63
|
+
}
|
|
64
|
+
if (model instanceof models.TFJS) {
|
|
65
|
+
await model.extract().save(this.pathFor({ ...src, type: ModelType.WORKING, version: 0 }), { includeOptimizer: true });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
throw new Error('unknown model type');
|
|
69
|
+
}
|
|
70
|
+
// Enforce version 0 to always keep a single working model at a time
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Creates a saved copy of the working model corresponding to the source.
|
|
74
|
+
* @param source the source
|
|
75
|
+
*/
|
|
76
|
+
async saveWorkingModel(source) {
|
|
77
|
+
const src = this.infoFor(source);
|
|
78
|
+
if (src.type !== undefined && src.type !== ModelType.WORKING) {
|
|
79
|
+
throw new Error('expected working model');
|
|
80
|
+
}
|
|
81
|
+
const dst = this.pathFor(await this.duplicateSource({ ...src, type: ModelType.SAVED }));
|
|
82
|
+
await tf.io.copyModel(this.pathFor({ ...src, type: ModelType.WORKING }), dst);
|
|
83
|
+
return dst;
|
|
84
|
+
}
|
|
85
|
+
async saveModel(source, model) {
|
|
86
|
+
const src = this.infoFor(source);
|
|
87
|
+
if (src.type !== undefined && src.type !== ModelType.SAVED) {
|
|
88
|
+
throw new Error('expected saved model');
|
|
89
|
+
}
|
|
90
|
+
const dst = this.pathFor(await this.duplicateSource({ ...src, type: ModelType.SAVED }));
|
|
91
|
+
if (model instanceof models.TFJS) {
|
|
92
|
+
await model.extract().save(dst, { includeOptimizer: true });
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
throw new Error('unknown model type');
|
|
96
|
+
}
|
|
97
|
+
return dst;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Downloads the model corresponding to the source.
|
|
101
|
+
* @param source the source
|
|
102
|
+
*/
|
|
103
|
+
async downloadModel(source) {
|
|
104
|
+
const src = this.infoFor(source);
|
|
105
|
+
await tf.io.copyModel(this.pathFor(source), `downloads://${src.taskID}_${src.name}`);
|
|
106
|
+
}
|
|
107
|
+
async latestDuplicate(source) {
|
|
108
|
+
if (typeof source !== 'string') {
|
|
109
|
+
source = this.pathFor({ ...source, version: 0 });
|
|
110
|
+
}
|
|
111
|
+
// perform a single memory read
|
|
112
|
+
const paths = Map(await tf.io.listModels());
|
|
113
|
+
if (!paths.has(source)) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const latest = Map(paths)
|
|
117
|
+
.keySeq()
|
|
118
|
+
.toList()
|
|
119
|
+
.map((p) => this.infoFor(p).version)
|
|
120
|
+
.max();
|
|
121
|
+
if (latest === undefined) {
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
return latest;
|
|
125
|
+
}
|
|
126
|
+
async duplicateSource(source) {
|
|
127
|
+
const latestDuplicate = await this.latestDuplicate(source);
|
|
128
|
+
source = this.infoFor(source);
|
|
129
|
+
if (latestDuplicate === undefined) {
|
|
130
|
+
return source;
|
|
131
|
+
}
|
|
132
|
+
return { ...source, version: latestDuplicate + 1 };
|
|
133
|
+
}
|
|
134
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@epfml/discojs-web",
|
|
3
|
+
"version": "2.1.2-p20240506085037.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"watch": "nodemon --ext ts --ignore dist --watch ../discojs/dist --watch . --exec npm run",
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"lint": "npx eslint .",
|
|
11
|
+
"test": ": nothing"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/epfml/disco.git"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/epfml/disco/issues"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/epfml/disco#readme",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@epfml/discojs": "*",
|
|
23
|
+
"@tensorflow/tfjs": "4"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"nodemon": "3"
|
|
27
|
+
}
|
|
28
|
+
}
|