|
1 | | -import * as tf from '@tensorflow/tfjs' |
| 1 | +import * as tf from "@tensorflow/tfjs"; |
2 | 2 |
|
3 | 3 | interface DataPoint extends tf.TensorContainerObject { |
4 | | - xs: tf.Tensor2D, |
5 | | - ys: tf.Tensor3D, |
| 4 | + xs: tf.Tensor2D; |
| 5 | + ys: tf.Tensor3D; |
6 | 6 | } |
7 | 7 |
|
8 | | -export default async function evaluate ( |
| 8 | +export default async function evaluate( |
9 | 9 | model: tf.LayersModel, |
10 | 10 | dataset: tf.data.Dataset<DataPoint>, |
11 | | - maxEvalBatches: number |
12 | | -): Promise<Record<'val_acc' | 'val_loss' | 'val_perplexity', number>> { |
13 | | - let datasetSize = 0 |
14 | | - let totalLoss = 0 |
15 | | - const acc: [number, number] = [0, 0] |
| 11 | + maxEvalBatches: number, |
| 12 | +): Promise<Record<"val_acc" | "val_loss" | "val_perplexity", number>> { |
| 13 | + let datasetSize = 0; |
| 14 | + let totalLoss = 0; |
| 15 | + const acc: [number, number] = [0, 0]; |
16 | 16 |
|
17 | | - await dataset.take(maxEvalBatches).map(({ xs, ys }) => { |
18 | | - const logits = model.apply(xs) |
19 | | - if (Array.isArray(logits)) { |
20 | | - throw new Error('model output too many tensor') |
21 | | - } |
22 | | - if (logits instanceof tf.SymbolicTensor) { |
23 | | - throw new Error('model output symbolic tensor') |
24 | | - } |
25 | | - xs.dispose() |
| 17 | + await dataset |
| 18 | + .take(maxEvalBatches) |
| 19 | + .map(({ xs, ys }) => { |
| 20 | + const logits = model.apply(xs); |
| 21 | + if (Array.isArray(logits)) { |
| 22 | + throw new Error("model output too many tensor"); |
| 23 | + } |
| 24 | + if (logits instanceof tf.SymbolicTensor) { |
| 25 | + throw new Error("model output symbolic tensor"); |
| 26 | + } |
| 27 | + xs.dispose(); |
26 | 28 |
|
27 | | - return { logits, ys } |
28 | | - }).mapAsync(async ({ logits, ys }) => { |
29 | | - const lossTensor = tf.losses.softmaxCrossEntropy(ys, logits) |
30 | | - const loss = await lossTensor.array() |
31 | | - if (typeof loss !== 'number') { |
32 | | - throw new Error('got multiple loss') |
33 | | - } |
| 29 | + return { logits, ys }; |
| 30 | + }) |
| 31 | + .mapAsync(async ({ logits, ys }) => { |
| 32 | + const lossTensor = tf.losses.softmaxCrossEntropy(ys, logits); |
| 33 | + const loss = await lossTensor.array(); |
| 34 | + if (typeof loss !== "number") { |
| 35 | + throw new Error("got multiple loss"); |
| 36 | + } |
34 | 37 |
|
35 | | - const accTensor = tf.metrics.categoricalAccuracy(ys, logits) |
36 | | - const accSize = accTensor.shape.reduce((l, r) => l * r, 1) |
37 | | - const accSum = accTensor.sum() |
38 | | - const accSummed = await accSum.array() |
39 | | - if (typeof accSummed !== 'number') { |
40 | | - throw new Error('got multiple accuracy sum') |
41 | | - } |
| 38 | + const accTensor = tf.metrics.categoricalAccuracy(ys, logits); |
| 39 | + const accSize = accTensor.shape.reduce((l, r) => l * r, 1); |
| 40 | + const accSum = accTensor.sum(); |
| 41 | + const accSummed = await accSum.array(); |
| 42 | + if (typeof accSummed !== "number") { |
| 43 | + throw new Error("got multiple accuracy sum"); |
| 44 | + } |
42 | 45 |
|
43 | | - tf.dispose([ys, logits, accTensor, accSum, lossTensor]) |
44 | | - return { loss, accSummed, accSize } |
45 | | - }).forEachAsync(({ loss, accSummed, accSize }) => { |
46 | | - datasetSize += 1 |
47 | | - totalLoss += loss |
48 | | - acc[0] += accSummed |
49 | | - acc[1] += accSize |
50 | | - }) |
| 46 | + tf.dispose([ys, logits, accTensor, accSum, lossTensor]); |
| 47 | + return { loss, accSummed, accSize }; |
| 48 | + }) |
| 49 | + .forEachAsync(({ loss, accSummed, accSize }) => { |
| 50 | + datasetSize += 1; |
| 51 | + totalLoss += loss; |
| 52 | + acc[0] += accSummed; |
| 53 | + acc[1] += accSize; |
| 54 | + }); |
51 | 55 |
|
52 | | - const loss = totalLoss / datasetSize |
| 56 | + const loss = totalLoss / datasetSize; |
53 | 57 | return { |
54 | 58 | val_loss: loss, |
55 | 59 | val_perplexity: Math.exp(loss), |
56 | | - val_acc: acc[0] / acc[1] |
57 | | - } |
| 60 | + val_acc: acc[0] / acc[1], |
| 61 | + }; |
58 | 62 | } |
0 commit comments