@epfml/discojs 2.1.2-p20240507140056.0 → 2.1.2-p20240515132210.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/README.md +24 -0
- package/dist/dataset/dataset_builder.js +1 -1
- package/dist/default_tasks/cifar10/index.js +3 -4
- package/dist/default_tasks/index.d.ts +0 -2
- package/dist/default_tasks/index.js +0 -2
- package/dist/default_tasks/lus_covid.js +3 -3
- package/dist/default_tasks/mnist.js +0 -1
- package/dist/default_tasks/simple_face/index.js +0 -2
- package/dist/default_tasks/titanic.js +0 -1
- package/dist/default_tasks/wikitext.js +2 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/memory/base.d.ts +6 -19
- package/dist/memory/empty.d.ts +2 -2
- package/dist/memory/empty.js +2 -2
- package/dist/memory/index.d.ts +1 -1
- package/dist/memory/index.js +1 -1
- package/dist/memory/model_type.d.ts +1 -1
- package/dist/memory/model_type.js +5 -5
- package/dist/models/gpt/config.d.ts +32 -0
- package/dist/models/gpt/config.js +42 -0
- package/dist/models/gpt/evaluate.d.ts +7 -0
- package/dist/models/gpt/evaluate.js +44 -0
- package/dist/models/gpt/index.d.ts +37 -0
- package/dist/models/gpt/index.js +107 -0
- package/dist/models/gpt/layers.d.ts +13 -0
- package/dist/models/gpt/layers.js +272 -0
- package/dist/models/gpt/model.d.ts +43 -0
- package/dist/models/gpt/model.js +191 -0
- package/dist/models/gpt/optimizers.d.ts +4 -0
- package/dist/models/gpt/optimizers.js +95 -0
- package/dist/models/index.d.ts +5 -0
- package/dist/models/index.js +4 -0
- package/dist/models/model.d.ts +51 -0
- package/dist/models/model.js +8 -0
- package/dist/models/tfjs.d.ts +24 -0
- package/dist/models/tfjs.js +107 -0
- package/dist/models/tokenizer.d.ts +14 -0
- package/dist/models/tokenizer.js +23 -0
- package/dist/task/display_information.d.ts +3 -6
- package/dist/task/display_information.js +21 -10
- package/dist/task/index.d.ts +0 -1
- package/dist/task/index.js +0 -1
- package/dist/training/trainer/trainer_builder.js +2 -2
- package/package.json +1 -1
- package/dist/default_tasks/geotags/index.d.ts +0 -2
- package/dist/default_tasks/geotags/index.js +0 -65
- package/dist/default_tasks/geotags/model.d.ts +0 -593
- package/dist/default_tasks/geotags/model.js +0 -4715
- package/dist/default_tasks/skin_mnist.d.ts +0 -2
- package/dist/default_tasks/skin_mnist.js +0 -80
- package/dist/task/label_type.d.ts +0 -9
- package/dist/task/label_type.js +0 -28
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import { WeightsContainer } from '../index.js';
|
|
3
|
+
import { Model } from './index.js';
|
|
4
|
+
import type { EpochLogs, Prediction, Sample } from './model.js';
|
|
5
|
+
import type { Dataset } from '../dataset/index.js';
|
|
6
|
+
/** TensorFlow JavaScript model with standard training */
|
|
7
|
+
export declare class TFJS extends Model {
|
|
8
|
+
private readonly model;
|
|
9
|
+
/** Wrap the given trainable model */
|
|
10
|
+
constructor(model: tf.LayersModel);
|
|
11
|
+
get weights(): WeightsContainer;
|
|
12
|
+
set weights(ws: WeightsContainer);
|
|
13
|
+
train(trainingData: Dataset, validationData?: Dataset, epochs?: number): AsyncGenerator<EpochLogs>;
|
|
14
|
+
predict(input: Sample): Promise<Prediction>;
|
|
15
|
+
static deserialize(raw: tf.io.ModelArtifacts): Promise<Model>;
|
|
16
|
+
serialize(): Promise<tf.io.ModelArtifacts>;
|
|
17
|
+
[Symbol.dispose](): void;
|
|
18
|
+
/**
|
|
19
|
+
* extract wrapped model
|
|
20
|
+
*
|
|
21
|
+
* @deprecated use `Model` instead of relying on tf specifics
|
|
22
|
+
*/
|
|
23
|
+
extract(): tf.LayersModel;
|
|
24
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as tf from '@tensorflow/tfjs';
|
|
2
|
+
import { WeightsContainer } from '../index.js';
|
|
3
|
+
import { Model } from './index.js';
|
|
4
|
+
/** TensorFlow JavaScript model with standard training */
|
|
5
|
+
export class TFJS extends Model {
|
|
6
|
+
model;
|
|
7
|
+
/** Wrap the given trainable model */
|
|
8
|
+
constructor(model) {
|
|
9
|
+
super();
|
|
10
|
+
this.model = model;
|
|
11
|
+
if (model.loss === undefined) {
|
|
12
|
+
throw new Error('TFJS models need to be compiled to be used');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
get weights() {
|
|
16
|
+
return new WeightsContainer(this.model.weights.map((w) => w.read()));
|
|
17
|
+
}
|
|
18
|
+
set weights(ws) {
|
|
19
|
+
this.model.setWeights(ws.weights);
|
|
20
|
+
}
|
|
21
|
+
async *train(trainingData, validationData, epochs = 1) {
|
|
22
|
+
for (let epoch = 0; epoch < epochs; epoch++) {
|
|
23
|
+
let logs;
|
|
24
|
+
let peakMemory = 0;
|
|
25
|
+
await this.model.fitDataset(trainingData, {
|
|
26
|
+
epochs: 1,
|
|
27
|
+
validationData,
|
|
28
|
+
callbacks: {
|
|
29
|
+
onBatchEnd: (_) => {
|
|
30
|
+
const currentMemory = tf.memory().numBytes / 1024 / 1024 / 1024; // GB
|
|
31
|
+
if (currentMemory > peakMemory) {
|
|
32
|
+
peakMemory = currentMemory;
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
onEpochEnd: (_, cur) => { logs = cur; }
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
if (logs === undefined) {
|
|
39
|
+
throw new Error("Epoch didn't gave any logs");
|
|
40
|
+
}
|
|
41
|
+
const { loss, acc, val_acc, val_loss } = logs;
|
|
42
|
+
if (loss === undefined || isNaN(loss) || acc === undefined || isNaN(acc)) {
|
|
43
|
+
throw new Error("Training loss is undefined or nan");
|
|
44
|
+
}
|
|
45
|
+
const structuredLogs = {
|
|
46
|
+
epoch,
|
|
47
|
+
peakMemory,
|
|
48
|
+
training: {
|
|
49
|
+
loss: logs.loss,
|
|
50
|
+
accuracy: logs.acc,
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
if (validationData !== undefined) {
|
|
54
|
+
if (val_loss === undefined || isNaN(val_loss) ||
|
|
55
|
+
val_acc === undefined || isNaN(val_acc)) {
|
|
56
|
+
throw new Error("Invalid validation logs");
|
|
57
|
+
}
|
|
58
|
+
structuredLogs.validation = {
|
|
59
|
+
accuracy: logs.val_acc,
|
|
60
|
+
loss: logs.val_loss
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
yield structuredLogs;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
predict(input) {
|
|
67
|
+
const ret = this.model.predict(input);
|
|
68
|
+
if (Array.isArray(ret)) {
|
|
69
|
+
throw new Error('prediction yield many Tensors but should have only returned one');
|
|
70
|
+
}
|
|
71
|
+
return Promise.resolve(ret);
|
|
72
|
+
}
|
|
73
|
+
static async deserialize(raw) {
|
|
74
|
+
return new this(await tf.loadLayersModel({
|
|
75
|
+
load: () => Promise.resolve(raw)
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
async serialize() {
|
|
79
|
+
let resolveArtifacts;
|
|
80
|
+
const ret = new Promise((resolve) => { resolveArtifacts = resolve; });
|
|
81
|
+
await this.model.save({
|
|
82
|
+
save: (artifacts) => {
|
|
83
|
+
resolveArtifacts(artifacts);
|
|
84
|
+
return Promise.resolve({
|
|
85
|
+
modelArtifactsInfo: {
|
|
86
|
+
dateSaved: new Date(),
|
|
87
|
+
modelTopologyType: 'JSON'
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}, {
|
|
92
|
+
includeOptimizer: true // keep model compiled
|
|
93
|
+
});
|
|
94
|
+
return await ret;
|
|
95
|
+
}
|
|
96
|
+
[Symbol.dispose]() {
|
|
97
|
+
this.model.dispose();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* extract wrapped model
|
|
101
|
+
*
|
|
102
|
+
* @deprecated use `Model` instead of relying on tf specifics
|
|
103
|
+
*/
|
|
104
|
+
extract() {
|
|
105
|
+
return this.model;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Task } from '../index.js';
|
|
2
|
+
import { PreTrainedTokenizer } from '@xenova/transformers';
|
|
3
|
+
/**
|
|
4
|
+
* A task's tokenizer is initially specified as the tokenizer name, e.g., 'Xenova/gpt2'.
|
|
5
|
+
* The first time the tokenizer is needed, this function initializes the actual tokenizer object
|
|
6
|
+
* and saves it in the task' tokenizer field to be reused in subsequent calls.
|
|
7
|
+
*
|
|
8
|
+
* We are proceeding as such because the task object is sent from the server to the client. Rather than
|
|
9
|
+
* sending complex objects through the network, we simply send the tokenizer name, which is then initialized client-side the
|
|
10
|
+
* first time it is called.
|
|
11
|
+
* @param task the task object specifying which tokenizer to use
|
|
12
|
+
* @returns an initialized tokenizer object
|
|
13
|
+
*/
|
|
14
|
+
export declare function getTaskTokenizer(task: Task): Promise<PreTrainedTokenizer>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AutoTokenizer, env } from '@xenova/transformers';
|
|
2
|
+
/**
|
|
3
|
+
* A task's tokenizer is initially specified as the tokenizer name, e.g., 'Xenova/gpt2'.
|
|
4
|
+
* The first time the tokenizer is needed, this function initializes the actual tokenizer object
|
|
5
|
+
* and saves it in the task' tokenizer field to be reused in subsequent calls.
|
|
6
|
+
*
|
|
7
|
+
* We are proceeding as such because the task object is sent from the server to the client. Rather than
|
|
8
|
+
* sending complex objects through the network, we simply send the tokenizer name, which is then initialized client-side the
|
|
9
|
+
* first time it is called.
|
|
10
|
+
* @param task the task object specifying which tokenizer to use
|
|
11
|
+
* @returns an initialized tokenizer object
|
|
12
|
+
*/
|
|
13
|
+
export async function getTaskTokenizer(task) {
|
|
14
|
+
let tokenizer = task.trainingInformation.tokenizer;
|
|
15
|
+
if (tokenizer === undefined)
|
|
16
|
+
throw Error('No tokenizer specified in the task training information');
|
|
17
|
+
if (typeof tokenizer == 'string') {
|
|
18
|
+
env.allowLocalModels = false;
|
|
19
|
+
tokenizer = await AutoTokenizer.from_pretrained(tokenizer);
|
|
20
|
+
task.trainingInformation.tokenizer = tokenizer;
|
|
21
|
+
}
|
|
22
|
+
return tokenizer;
|
|
23
|
+
}
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { type Summary } from './summary.js';
|
|
2
2
|
import { type DataExample } from './data_example.js';
|
|
3
|
-
import { type LabelType } from './label_type.js';
|
|
4
3
|
export interface DisplayInformation {
|
|
5
|
-
taskTitle
|
|
6
|
-
summary
|
|
7
|
-
tradeoffs?: string;
|
|
4
|
+
taskTitle: string;
|
|
5
|
+
summary: Summary;
|
|
8
6
|
dataFormatInformation?: string;
|
|
9
7
|
dataExampleText?: string;
|
|
10
8
|
model?: string;
|
|
11
9
|
dataExample?: DataExample[];
|
|
12
10
|
headers?: string[];
|
|
13
11
|
dataExampleImage?: string;
|
|
14
|
-
|
|
15
|
-
labelDisplay?: LabelType;
|
|
12
|
+
sampleDatasetLink?: string;
|
|
16
13
|
}
|
|
17
14
|
export declare function isDisplayInformation(raw: unknown): raw is DisplayInformation;
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
import { isSummary } from './summary.js';
|
|
2
2
|
import { isDataExample } from './data_example.js';
|
|
3
|
-
import { isLabelType } from './label_type.js';
|
|
4
3
|
export function isDisplayInformation(raw) {
|
|
5
4
|
if (typeof raw !== 'object' || raw === null) {
|
|
6
5
|
return false;
|
|
7
6
|
}
|
|
8
|
-
const { dataExample, dataExampleImage, dataExampleText, dataFormatInformation,
|
|
7
|
+
const { dataExample, dataExampleImage, dataExampleText, dataFormatInformation, sampleDatasetLink, headers, model, summary, taskTitle, } = raw;
|
|
9
8
|
if (typeof taskTitle !== 'string' ||
|
|
10
9
|
(dataExampleText !== undefined && typeof dataExampleText !== 'string') ||
|
|
10
|
+
(sampleDatasetLink !== undefined && typeof sampleDatasetLink !== 'string') ||
|
|
11
11
|
(dataFormatInformation !== undefined && typeof dataFormatInformation !== 'string') ||
|
|
12
|
-
(tradeoffs !== undefined && typeof tradeoffs !== 'string') ||
|
|
13
12
|
(model !== undefined && typeof model !== 'string') ||
|
|
14
|
-
(dataExampleImage !== undefined && typeof dataExampleImage !== 'string')
|
|
15
|
-
(labelDisplay !== undefined && !isLabelType(labelDisplay)) ||
|
|
16
|
-
(limitations !== undefined && typeof limitations !== 'string')) {
|
|
13
|
+
(dataExampleImage !== undefined && typeof dataExampleImage !== 'string')) {
|
|
17
14
|
return false;
|
|
18
15
|
}
|
|
19
|
-
if (
|
|
16
|
+
if (!isSummary(summary)) {
|
|
20
17
|
return false;
|
|
21
18
|
}
|
|
19
|
+
if (sampleDatasetLink !== undefined) {
|
|
20
|
+
try {
|
|
21
|
+
new URL(sampleDatasetLink);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (dataExampleImage !== undefined) {
|
|
28
|
+
try {
|
|
29
|
+
new URL(dataExampleImage);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
22
35
|
if (dataExample !== undefined && !(Array.isArray(dataExample) &&
|
|
23
36
|
dataExample.every(isDataExample))) {
|
|
24
37
|
return false;
|
|
@@ -32,13 +45,11 @@ export function isDisplayInformation(raw) {
|
|
|
32
45
|
dataExampleImage,
|
|
33
46
|
dataExampleText,
|
|
34
47
|
dataFormatInformation,
|
|
48
|
+
sampleDatasetLink,
|
|
35
49
|
headers,
|
|
36
|
-
labelDisplay,
|
|
37
|
-
limitations,
|
|
38
50
|
model,
|
|
39
51
|
summary,
|
|
40
52
|
taskTitle,
|
|
41
|
-
tradeoffs,
|
|
42
53
|
};
|
|
43
54
|
const _correct = repack;
|
|
44
55
|
const _total = repack;
|
package/dist/task/index.d.ts
CHANGED
|
@@ -4,4 +4,3 @@ export { isDigest, type Digest } from './digest.js';
|
|
|
4
4
|
export { isDisplayInformation, type DisplayInformation } from './display_information.js';
|
|
5
5
|
export type { TrainingInformation } from './training_information.js';
|
|
6
6
|
export { pushTask, fetchTasks } from './task_handler.js';
|
|
7
|
-
export { LabelTypeEnum } from './label_type.js';
|
package/dist/task/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StoredModelType } from '../../index.js';
|
|
2
2
|
import { DistributedTrainer } from './distributed_trainer.js';
|
|
3
3
|
import { LocalTrainer } from './local_trainer.js';
|
|
4
4
|
/**
|
|
@@ -36,7 +36,7 @@ export class TrainerBuilder {
|
|
|
36
36
|
if (modelID === undefined) {
|
|
37
37
|
throw new TypeError('model ID is undefined');
|
|
38
38
|
}
|
|
39
|
-
const info = { type:
|
|
39
|
+
const info = { type: StoredModelType.WORKING, taskID: this.task.id, name: modelID };
|
|
40
40
|
const model = await (await this.memory.contains(info) ? this.memory.getModel(info) : client.getLatestModel());
|
|
41
41
|
return model;
|
|
42
42
|
}
|
package/package.json
CHANGED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { Range } from 'immutable';
|
|
2
|
-
import * as tf from '@tensorflow/tfjs';
|
|
3
|
-
import { data, models } from '../../index.js';
|
|
4
|
-
import { LabelTypeEnum } from '../../task/label_type.js';
|
|
5
|
-
import baseModel from './model.js';
|
|
6
|
-
export const geotags = {
|
|
7
|
-
getTask() {
|
|
8
|
-
return {
|
|
9
|
-
id: 'geotags',
|
|
10
|
-
displayInformation: {
|
|
11
|
-
taskTitle: 'GeoTags',
|
|
12
|
-
summary: {
|
|
13
|
-
preview: 'In this challenge, we predict the geo-location of a photo given its pixels in terms of a cell number of a grid built on top of Switzerland',
|
|
14
|
-
overview: 'The geotags dataset is a collection of images with geo-location information used to train a machine learning algorithm to predict the location of a photo given its pixels.'
|
|
15
|
-
},
|
|
16
|
-
limitations: 'The training data is limited to images of size 224x224.',
|
|
17
|
-
tradeoffs: 'Training success strongly depends on label distribution',
|
|
18
|
-
dataFormatInformation: 'Images should be of .png format and of size 224x224. <br> The label file should be .csv, where each row contains a file_name, class. The class is the cell number of a the given grid of Switzerland. ',
|
|
19
|
-
labelDisplay: {
|
|
20
|
-
labelType: LabelTypeEnum.POLYGON_MAP,
|
|
21
|
-
mapBaseUrl: 'https://disco-polygon.web.app/'
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
trainingInformation: {
|
|
25
|
-
modelID: 'geotags-model',
|
|
26
|
-
epochs: 10,
|
|
27
|
-
roundDuration: 10,
|
|
28
|
-
validationSplit: 0.2,
|
|
29
|
-
batchSize: 10,
|
|
30
|
-
dataType: 'image',
|
|
31
|
-
IMAGE_H: 224,
|
|
32
|
-
IMAGE_W: 224,
|
|
33
|
-
preprocessingFunctions: [data.ImagePreprocessing.Resize],
|
|
34
|
-
LABEL_LIST: Range(0, 127).map(String).toArray(),
|
|
35
|
-
scheme: 'federated',
|
|
36
|
-
noiseScale: undefined,
|
|
37
|
-
clippingRadius: 20,
|
|
38
|
-
decentralizedSecure: true,
|
|
39
|
-
minimumReadyPeers: 3,
|
|
40
|
-
maxShareValue: 100
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
},
|
|
44
|
-
async getModel() {
|
|
45
|
-
const pretrainedModel = await tf.loadLayersModel({
|
|
46
|
-
load: async () => Promise.resolve(baseModel),
|
|
47
|
-
});
|
|
48
|
-
const numLayers = pretrainedModel.layers.length;
|
|
49
|
-
pretrainedModel.layers.forEach(layer => { layer.trainable = false; });
|
|
50
|
-
pretrainedModel.layers[numLayers - 1].trainable = true;
|
|
51
|
-
const model = tf.sequential({
|
|
52
|
-
layers: [
|
|
53
|
-
tf.layers.inputLayer({ inputShape: [224, 224, 3] }),
|
|
54
|
-
tf.layers.rescaling({ scale: 1 / 127.5, offset: -1 }), // Rescaling input between -1 and 1
|
|
55
|
-
pretrainedModel
|
|
56
|
-
]
|
|
57
|
-
});
|
|
58
|
-
model.compile({
|
|
59
|
-
optimizer: 'adam',
|
|
60
|
-
loss: 'categoricalCrossentropy',
|
|
61
|
-
metrics: ['accuracy']
|
|
62
|
-
});
|
|
63
|
-
return new models.TFJS(model);
|
|
64
|
-
}
|
|
65
|
-
};
|