federer
Version:
Experiments in asynchronous federated learning and decentralized learning
87 lines • 4.62 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TensorBoardOutput = void 0;
const tslib_1 = require("tslib");
const assert = require("assert");
const path = tslib_1.__importStar(require("path"));
const tf = tslib_1.__importStar(require("@tensorflow/tfjs-node"));
const root = tslib_1.__importStar(require("app-root-path"));
const common_1 = require("../../common");
/**
* Represents a TensorBoard output file to which we can write.
*/
class TensorBoardOutput {
constructor(runName, options, logger) {
this.runName = runName;
this.options = options;
this.logger = logger;
this.resultsPath = root.resolve(path.join("results", runName));
common_1.mkdirp(this.resultsPath);
this.summaryFileWriter = tf.node.summaryFileWriter(this.resultsPath);
}
/** Writes data distrbution to summary file*/
writeDataVisualization(dataDistibution) {
this.summaryFileWriter.histogram("Data Distribution: # of clients per # of samples", dataDistibution.sum(1), 0, undefined, "Buckets number of clients by number of samples that they hold.");
const unstacker = (data, boardName, description) => {
tf.unstack(data).forEach((perClient, i) => {
perClient.dataSync().forEach((perLabel, j) => {
this.summaryFileWriter.histogram(boardName, tf.ones([perLabel]).mul(j), i, undefined, description);
});
});
};
unstacker(dataDistibution, "Data Distribution: Per client(y) by label(x)", "Describes how many samples of each labels a client has.");
unstacker(dataDistibution.transpose(), "Data Distribution: Per label(y) by client(x)", "Describes how a label is distributed across clients.");
}
/** Write the graphs showing evaluation results. */
writeEvaluationResults(results) {
// Loss graphs:
this.summaryFileWriter.scalar("Global test loss (by round number)", results.loss, results.round.roundNumber);
// Accuracy graphs:
this.summaryFileWriter.scalar("Global test accuracy (by round number)", results.accuracy, results.round.roundNumber);
this.summaryFileWriter.scalar("Global test accuracy (by wall-clock time in ms since all clients connected)", results.accuracy, results.round.timeSinceStart);
this.summaryFileWriter.scalar("Global test accuracy (by number of epochs)", results.accuracy, results.round.numberEpochs);
this.summaryFileWriter.scalar("Global test accuracy (by number of uploads)", results.accuracy, results.round.numberUploads);
this.summaryFileWriter.flush();
this.logger.info(`wrote evaluation of round ${results.round.roundNumber}`);
}
/**
* Write the graphs that can be written from a round summary without
* evaluating the results.
*
* @see {@link Coordinator.evaluate} for the graphs that result from evaluation
*/
writeRoundSummary(summary) {
this.summaryFileWriter.scalar("Number of clients training at round end", summary.numberClientsTraining, summary.roundNumber);
// Instrumentation graphs:
const stalenessCounts = summary.instrumentation?.stalenessCounts;
if (stalenessCounts !== undefined) {
assert.strictEqual(this.options.instrument?.uploadStaleness, true);
this.writeStalenessGraph(stalenessCounts, summary.roundNumber);
}
else {
assert(!this.options.instrument?.uploadStaleness);
}
const memory = summary.instrumentation?.memory;
if (memory !== undefined) {
assert.strictEqual(this.options.instrument?.memoryUsage, true);
this.summaryFileWriter.scalar("RSS (in MB) by round number", memory.node.rss / 1024 / 1024, summary.roundNumber);
this.summaryFileWriter.scalar("Number of TensorFlow tensors by round number", memory.tf.numTensors, summary.roundNumber);
}
else {
assert(!this.options.instrument?.memoryUsage);
}
}
writeStalenessGraph(stalenessCounts, round) {
const entries = Object.entries(stalenessCounts);
if (entries.length === 0) {
return;
}
tf.tidy(() => {
const data = tf.concat(entries.map(([staleness, count]) => tf.ones([count]).mul(Number.parseInt(staleness))));
const name = `Staleness of ${this.runName}`;
this.summaryFileWriter.histogram(name, data, round, entries.length);
});
}
}
exports.TensorBoardOutput = TensorBoardOutput;
//# sourceMappingURL=TensorBoardOutput.js.map