-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathassignment2-3.js
371 lines (291 loc) · 11.7 KB
/
assignment2-3.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
// This is the script for 6.s198 Assignments 2 and 3.
console.log('Starting script');
console.log('Importing and initializing the dataset');
const xhrDatasetConfigs = {
MNIST: {
data: [{
name: 'images',
path: 'https://storage.googleapis.com/learnjs-data/model-builder/mnist_images.png',
dataType: 'png',
shape: [28, 28, 1],
}, {
name: 'labels',
path: 'https://storage.googleapis.com/learnjs-data/model-builder/mnist_labels_uint8',
dataType: 'uint8',
shape: [10],
}],
},
Fashion_MNIST: {
data: [{
name: 'images',
path: 'https://storage.googleapis.com/learnjs-data/model-builder/fashion_mnist_images.png',
dataType: 'png',
shape: [28, 28, 1],
}, {
name: 'labels',
path: 'https://storage.googleapis.com/learnjs-data/model-builder/fashion_mnist_labels_uint8',
dataType: 'uint8',
shape: [10],
}],
},
CIFAR_10: {
"data": [{
"name": "images",
"path": "https://storage.googleapis.com/learnjs-data/model-builder/cifar10_images.png",
"dataType": "png",
"shape": [32, 32, 3]
}, {
"name": "labels",
"path": "https://storage.googleapis.com/learnjs-data/model-builder/cifar10_labels_uint8",
"dataType": "uint8",
"shape": [10]
}],
},
}
const dataSets = {};
function populateDatasets() {
for(const datasetName in xhrDatasetConfigs) {
if (xhrDatasetConfigs.hasOwnProperty(datasetName)) {
dataSets[datasetName] =
new dl.XhrDataset(xhrDatasetConfigs[datasetName]);
}
}
}
populateDatasets();
// To change between MNIST and CFAR, change the definitions
// of dataSet and showColor by commenting and uncommenting the
// lines below, and reload the page
// use these two lines for MNIST
// const dataSet = dataSets.MNIST;
// const showColor = false;
// use these two lines for Fashion MNIST
//const dataSet = dataSets.Fashion_MNIST;
//const showColor = false;
// use these two lines for CIFAR
const dataSet = dataSets.MNIST;
const showColor = true;
// functions for setting up the training data and the test data
const TRAIN_TEST_RATIO = 5 / 6;
function getTrainingData() {
const [images, labels] = dataSet.getData();
const end = Math.floor(TRAIN_TEST_RATIO * images.length);
// console.log(end);
return [images.slice(0, end), labels.slice(0, end)];
}
function getTestData() {
const data = dataSet.getData();
if (data == null) { return null; }
const [images, labels] = dataSet.getData();
const start = Math.floor(TRAIN_TEST_RATIO * images.length);
// console.log(images.length + " " + start);
return [images.slice(start), labels.slice(start)];
}
console.log('DataSet initialized. Please wait ...')
// Procedures to construct networks by adding layers to existing networks
// Add a flatten layer
function addFlattenLayer(graph, previousLayer) {
return graph.reshape(previousLayer, [dl.util.sizeFromShape(previousLayer.shape)]);
}
// Add a convolutional layer with specified field size, stride, zero padding, and output depth
// Index is used for naming the layer
function addConvLayer(graph, previousLayer, index, fieldSize, stride, zeroPad, outputDepth) {
inputShape = previousLayer.shape;
console.log("adding convLayer");
console.log('inputShape:', inputShape.toString());
const wShape = [fieldSize, fieldSize, inputShape[2], outputDepth];
console.log('layerspec:', wShape.toString());
w = dl.Array4D.randTruncatedNormal(wShape, 0, 0.1);
b = dl.Array1D.zeros([outputDepth]);
const wTensor = graph.variable(`conv2d-${index}-w`, w);
const bTensor = graph.variable(`conv2d-${index}-b`, b);
return graph.conv2d(previousLayer, wTensor, bTensor, fieldSize, outputDepth, stride, zeroPad);
}
// add a Rectified Linear Unit layer
function addReluLayer(graph, previousLayer){
return graph.relu(previousLayer);
}
// add a max pool layer with specified field size, stide, and zero padding
function addMaxPoolLayer(graph, previousLayer, fieldSize, stride, zeroPad){
return graph.maxPool(previousLayer, fieldSize, stride, zeroPad);
}
// Add a fully connected layer with a specified number of hidden units
// The index here is used for naming the layer
function addFcLayer(graph, previousLayer, index, hiddenUnits) {
const weightsInitializer = new dl.VarianceScalingInitializer();
const biasInitializer = new dl.ZerosInitializer();
const useBias = true;
return graph.layers.dense(
`fc-${index}`, previousLayer, hiddenUnits, null, useBias, weightsInitializer, biasInitializer);
}
// Hyperparameters to experiment with
const batchSize = 500;
const NUM_BATCHES = 500;
// print training results every batchInterval number of batches
const batchInterval = 250;
// flag to control whether or not to do testing
const DO_TESTING = true;
const NUM_IMAGES_TO_TEST = 10;
const CIFAR_10_LABELS = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck"]
// Glabal variable that can be examined using the Javascript console
var LABELS_TO_EXAMINE = [];
var IMAGES_TO_EXAMINE = [];
// Helper procedure for viewing the images
// These will appear on the HTML page
// The images have very low resolution, so this does not produce good views
function showimage(num) {
renderToCanvas(IMAGES_TO_EXAMINE[num], document.getElementById("imageHolder"));
}
//function renderToCanvas(a: Array3D, canvas: HTMLCanvasElement) {
function renderToCanvas(a, canvas) {
const [height, width, depth] = a.shape;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
const imageData = new ImageData(width, height);
const data = a.dataSync();
for (let i = 0; i < height * width; ++i) {
const j = i * 4;
const k = i * depth;
if (showColor) {
imageData.data[j + 0] = Math.round(255 * data[k + 0]);
imageData.data[j + 1] = Math.round(255 * data[k + 1]);
imageData.data[j + 2] = Math.round(255 * data[k + 2]);
imageData.data[j + 3] = 255;
} else {
const pixel = Math.round(255 * data[k]);
imageData.data[j+0] = pixel;
imageData.data[j+1] = pixel;
imageData.data[j+2] = pixel;
imageData.data[j+3] = 255;
}
ctx.putImageData(imageData, 0, 0);
}
}
// Helper procedures
function indexOfMax(a) {
return a.indexOf(a.reduce((max, value) => {return Math.max(max, value)}));
}
function labelTag(n) {
if (showColor) {
return CIFAR_10_LABELS[n];
} else {
return n;
}
}
function decimalToPercent(num) {
return Math.floor(num * 100).toString() + '%';
}
// Here is the function that builds the network and does the training and testing.
// Note that the variables defined in the body of runModel
// local to the function activation, so you cannot directly examine them in the console.
function runModel() {
console.log('Building the model');
// build the model
const graph = new dl.Graph();
const inputShape = dataSet.getDataShape(0);
const inputLayer = graph.placeholder('input', inputShape);
const labelShape = dataSet.getDataShape(1);
const labelTensor = graph.placeholder('label', labelShape);
const convLayer1 = addConvLayer(graph, inputLayer, 1, 10, 2, 2, 16);
const reLuLayer = addReluLayer(graph, convLayer1);
// const convLayer2 = addConvLayer(graph, reLuLayer, 2, 4, 2, 2, 8);
// const reLuLayer2 = addReluLayer(graph, convLayer2);
// const convLayer3 = addConvLayer(graph, reLuLayer2, 2, 2, 2, 2, 8);
// const reLuLayer4 = addReluLayer(graph, convLayer3);
const flattenLayer = addFlattenLayer(graph, reLuLayer);
// const flattenLayer2 = addFlattenLayer(graph, reLuLayer5);
const fcLayer1 = addFcLayer(graph, flattenLayer, 1, 10);
const reLuLayer3 = addReluLayer(graph, fcLayer1);
// const flattenLayer = addFlattenLayer(graph, inputLayer);
// const fcLayer1 = addFcLayer(graph, flattenLayer, 1, 10);
// const reLuLayer = addReluLayer(graph, fcLayer1);
// const fcLayer2 = addFcLayer(graph, reLuLayer,1, 10);
// const reLuLayer2 = addReluLayer(graph, fcLayer2);
/*
This is the code for the 80% accurate model that trains just over a minute
const convLayer1 = addConvLayer(graph, inputLayer, 1, 10, 2, 2, 16);
const reLuLayer = addReluLayer(graph, convLayer1);
const convLayer2 = addConvLayer(graph, reLuLayer, 2, 4, 2, 2, 8);
const reLuLayer2 = addReluLayer(graph, convLayer2);
const flattenLayer = addFlattenLayer(graph, reLuLayer2);
const fcLayer1 = addFcLayer(graph, flattenLayer, 1, 10);
const reLuLayer3 = addReluLayer(graph, fcLayer1);
*/
// This is a very simple network. Here's where you should
// include code to create new layers.
// Don't forget to change outputLayer and costTensor to account for the
// new layers
// const outputLayer = graph.softmax(fcLayer2);
const outputLayer = graph.softmax(reLuLayer3);
// use cross entropy to compute the overall error ("cost") to be minimized
// const costTensor = graph.softmaxCrossEntropyCost(fcLayer2, labelTensor);
const costTensor = graph.softmaxCrossEntropyCost(reLuLayer3, labelTensor);
const math = dl.ENV.math;
const session = new dl.Session(graph, math);
const learningRate = 0.05;
// The optimizer here is stochastic gradient descent (SGD)
const optimizer = new dl.SGDOptimizer(learningRate);
const trainingData = getTrainingData();
const trainingShuffledInputProviderGenerator =
new dl.InCPUMemoryShuffledInputProviderBuilder(trainingData);
const [trainInputProvider, trainLabelProvider] =
trainingShuffledInputProviderGenerator.getInputProviders();
const trainFeeds = [
{ tensor: inputLayer, data: trainInputProvider },
{ tensor: labelTensor, data: trainLabelProvider },
];
console.log('Model built');
math.scope(() => {
console.log('begin training ', NUM_BATCHES, ' batches, will print progress every ', batchInterval, ' batches');
trainStart = performance.now();
for (let i = 0; i < NUM_BATCHES; i += 1) {
const cost = session.train(
costTensor, trainFeeds, batchSize, optimizer, dl.CostReduction.MEAN);
// Compute the cost (by calling get), which requires transferring data
// from the GPU.
// save the cost for later examination
// print a message every 50 batches
if (!(i%batchInterval)){
console.log('batch', i, '---', 'Cost:', cost.get());
}
lastAverageCost = cost.get();
}
trainEnd = performance.now();
console.log('training complete');
console.log('training time:', Math.round(trainEnd - trainStart), 'milliseconds');
});
// do the testing
const [images, labels] = getTestData();
// save the images and labels in global variables so we can
// investigate them with console commands
IMAGES_TO_EXAMINE = images;
LABELS_TO_EXAMINE = labels;
if (DO_TESTING) {
var n = 0;
for (let i = 0; i < NUM_IMAGES_TO_TEST; i++) {
const testImage = images[i];
const testLabel = labels[i];
const testProbs = session.eval(outputLayer, [{tensor: inputLayer, data: testImage}]);
const probs = testProbs.dataSync();
const probs2 = testProbs.dataSync();
const maxIndex = indexOfMax(probs);
const topLabel = labelTag(maxIndex);
const trueLabelIndex = indexOfMax(testLabel.dataSync());
const trueLabel = labelTag(trueLabelIndex);
if (trueLabel == topLabel) {
n+=1;
}
const topProb = probs[maxIndex];
console.log('*** prediction', i);
console.log('true label:', trueLabel, 'predicted label:', topLabel, 'probability:', decimalToPercent(topProb));
console.log('probabilities:', probs);
console.log("fraction correct", (n/(i+1)));
}
} // end of testing
} //end of runModel
// run the model
// need to first noamalize the value to the range (-1, 1)
dataSet.fetchData().then(() => {
dataSet.normalizeWithinBounds(0 /* 0 means normalize only images, not labels */, -1, 1);
runModel();
});