cifar10
Version:
CIFAR-10 data set for nodejs / browser
527 lines (427 loc) • 19.2 kB
JavaScript
const chaiAsPromised = require("chai-as-promised")
const fs = require("fs")
const chai = require("chai")
const assert = chai.assert
const expect = chai.expect
const sinonChai = require("sinon-chai")
const sinon = require("sinon")
chai.use(sinonChai)
chai.use(chaiAsPromised);
const {cifar10, testing} = require("../dist/cifar10.js")({dataPath: "./data", testing: true})
const {shuffle, get, getData, indeces} = testing
const categories = ["airplane", "automobile", "bird", "cat", "deer", "dog", "frog", "horse", "ship", "truck"]
describe("Loading cifar10", () => {
describe("Without testing flag", () => {
sinon.stub(fs, "statSync").callsFake(() => {return {size: 307100}})
const cifar10 = require("../dist/cifar10.js")()
fs.statSync.restore()
it("Loads just the CIFAR10 class", () => {
expect(cifar10).to.not.be.undefined
expect(cifar10.name).to.equal("CIFAR10")
})
it("Does not load any of the testing specific items", () => {
expect(cifar10.shuffle).to.be.undefined
// get is a separate thing that does get loaded
expect(cifar10.getData).to.be.undefined
})
})
describe("With testing flag", () => {
it("Loads a CIFAR10 class when required", () => {
expect(cifar10).to.not.be.undefined
expect(cifar10.name).to.equal("CIFAR10")
})
it("Loads a testing object", () => {
expect(testing).to.not.be.undefined
})
it("Loads a testing.shuffle function", () => {
expect(shuffle).to.not.be.undefined
})
it("Loads a testing.get function", () => {
expect(get).to.not.be.undefined
})
it("Loads a testing.getData function", () => {
expect(getData).to.not.be.undefined
})
it("Loads a testing.indeces object", () => {
expect(indeces).to.not.be.undefined
})
})
})
describe("CIFAR10 class", () => {
describe("Defaults", () => {
it("CIFAR10.categories includes all 10 categories", () => {
expect(cifar10.categories).to.deep.equal(categories)
})
it("CIFAR10.dataCount is set to 60000", () => {
expect(cifar10.dataCount).to.equal(60000)
})
it("CIFAR10.training.get is assigned the getData function", () => {
expect(cifar10.training).to.not.be.undefined
expect(typeof cifar10.training.get).to.equal("function")
expect(cifar10.training.get.name).to.equal(getData.bind(null, "training").name)
})
it("CIFAR10.test.get is assigned the getData function", () => {
expect(cifar10.test).to.not.be.undefined
expect(typeof cifar10.test.get).to.equal("function")
expect(cifar10.test.get.name).to.equal(getData.bind(null, "training").name)
})
it("Creates a Category class for each category and assigns it to CIFAR10", () => {
categories.forEach(category => {
expect(cifar10[category]).to.not.be.undefined
expect(cifar10[category].constructor.name).to.equal("Category")
})
})
it("Defaults the cifar10.training.length to 50000 and cifar10.test.length to 10000", () => {
expect(cifar10.training.length).to.equal(50000)
expect(cifar10.test.length).to.equal(10000)
})
it("Defaults the CIFAR10.dataPath to the __dirname of the file", () => {
sinon.stub(fs, "statSync").callsFake(() => {return {size: 30710}})
const cifar10 = require("../dist/cifar10.js")()
fs.statSync.restore()
expect(cifar10.dataPath).to.equal(__dirname.replace("test", "dist"))
})
})
describe("set", () => {
const reset = () => {
cifar10.training.length = 0
cifar10.test.length = 0
}
beforeEach(() => {
sinon.stub(cifar10, "reset")
reset()
})
afterEach(() => {
cifar10.reset.restore()
reset()
})
it("Sets the cifar10.training.length and cifar10.test.length to given values 80 and 20", () => {
cifar10.set(80, 20)
expect(cifar10.training.length).to.equal(80)
expect(cifar10.test.length).to.equal(20)
})
it("Defaults the training and test parameters to 50000 and 10000 respectively when left blank", () => {
cifar10.set()
expect(cifar10.training.length).to.equal(50000)
expect(cifar10.test.length).to.equal(10000)
})
it("Rounds down values to the nearest value divisible by 10", () => {
cifar10.set(11, 29)
expect(cifar10.training.length).to.equal(10)
expect(cifar10.test.length).to.equal(20)
})
it("Thresholds values to at least 10, for both parameters", () => {
cifar10.set(1, 9)
expect(cifar10.training.length).to.equal(10)
expect(cifar10.test.length).to.equal(10)
})
it("Warns the user when more data has been split than is available", () => {
sinon.spy(console, "warn")
cifar10.set(5000000, 1000000)
expect(console.warn).to.have.been.called
console.warn.restore()
})
it("Returns the total images count if more data has been split than is available", () => {
const response = cifar10.set(5000000, 1000000)
expect(response).to.equal(cifar10.dataCount)
})
it("Returns undefined if less data has been split than is available", () => {
const response = cifar10.set(50, 10)
expect(response).to.be.undefined
})
it("Scales down the training/test values if above data count", () => {
cifar10.set(5000000, 1000000)
expect(cifar10.training.length).to.equal(50000)
expect(cifar10.test.length).to.equal(10000)
})
it("Rounds the values down to nearest values divisible by 10", () => {
cifar10.set(34438907834, 4246982376)
expect(cifar10.training.length%10).to.equal(0)
expect(cifar10.test.length%10).to.equal(0)
})
})
describe("reset", () => {
before(() => {
cifar10.training.length = 80
cifar10.test.length = 20
cifar10.dataCount = 20
cifar10.reset()
})
it("For each category, it sets the indeces[category] to an array of the indeces of length category.totalLength", () => {
categories.forEach(category => {
expect(indeces.training[category]).to.not.be.undefined
expect(indeces.training[category].length).to.be.at.least(1)
})
})
it("Shuffles those values", () => {
categories.forEach(category => {
const temp = indeces.training[category].slice(0).sort((a,b) => a<b)
expect(indeces.training[category]).to.not.deep.equal(temp)
})
})
it("Splits those arrays into their training and testing parts", () => {
categories.forEach(category => {
expect(indeces.test[category]).to.not.be.undefined
expect(indeces.test[category].length).to.be.at.least(1)
})
})
it("Sets the category training.length to a tenth of the cifar10.trainingSize", () => {
categories.forEach(category => {
expect(cifar10[category].training.length).to.equal(8)
})
})
it("Sets the category test.length to a tenth of the cifar10.testSize", () => {
categories.forEach(category => {
expect(cifar10[category].test.length).to.equal(2)
})
})
})
})
describe("Category class", () => {
let cifar10, testing
before(() => {
const newCifar10 = require("../dist/cifar10.js")({dataPath: "./data", testing: true})
cifar10 = newCifar10.cifar10
})
describe("Defaults", () => {
it("Sets the label to the category", () => {
categories.forEach(category => {
expect(cifar10[category].label).to.equal(category)
})
})
it("Sets the category.output to an array of 0s for each category except for its own index", () => {
categories.forEach((category, ci) => {
const output = [0,0,0,0,0,0,0,0,0,0]
output[ci] = 1
expect(cifar10[category].output).to.deep.equal(output)
})
})
it("Sets the category.totalLength to the number of images there are in the file", () => {
categories.forEach((category, ci) => {
expect(cifar10[category].totalLength).to.equal(6000)
})
})
it("Sets the initial category.length to the category.totalLength", () => {
categories.forEach((category, ci) => {
expect(cifar10[category].length).to.equal(cifar10[category].totalLength)
})
})
it("Sets the category.training.get to the get function", () => {
categories.forEach((category, ci) => {
expect(cifar10[category].training.get.name).to.equal("get")
})
})
it("Sets the category.test.length to 0", () => {
categories.forEach((category, ci) => {
expect(cifar10[category].test.length).to.equal(0)
})
})
it("Sets the category.test.get to the get function", () => {
categories.forEach((category, ci) => {
expect(cifar10[category].test.get.name).to.equal("get")
})
})
it("Sets the category.get to the get function", () => {
categories.forEach((category, ci) => {
expect(cifar10[category].get).to.not.be.undefined
})
})
})
describe("range", () => {
it("Returns a promise", () => {
categories.forEach(category => {
expect(cifar10[category].range()).instanceof(Promise)
})
})
it("Resolves an array of items", () => {
return cifar10.cat.range().then(data => {
expect(data).instanceof(Array)
})
})
it("Resolves just one item when range is passed no parameters", () => {
return cifar10.cat.range().then(data => {
expect(data).to.have.lengthOf(1)
})
})
it("Resolves in the arrays objects with keys 'input' and 'output'", () => {
return cifar10.cat.range().then(data => {
expect(data[0]).to.have.keys("input","output")
})
})
describe("input", () => {
it("Is an array containing 3072 values", () => {
return cifar10.cat.range().then(data => {
const {input} = data[0]
expect(input).instanceof(Array)
expect(input).to.have.lengthOf(3072)
})
})
it("Returns values that are numbers", () => {
return cifar10.cat.range().then(data => {
const {input} = data[0]
expect(input.every(v => typeof v == "number")).to.be.true
})
})
it("Values are normalized", () => {
return cifar10.cat.range().then(data => {
const {input} = data[0]
expect(input.every(v => v >= 0)).to.be.true
expect(input.every(v => v <= 1)).to.be.true
})
})
})
describe("output", () => {
it("Returns an array containing 10 values", () => {
return cifar10.cat.range().then(data => {
const {output} = data[0]
expect(output).instanceof(Array)
expect(output).to.have.lengthOf(10)
})
})
it("Returns only numbers", () => {
return cifar10.cat.range().then(data => {
const {output} = data[0]
expect(output.every(v => typeof v == "number")).to.be.true
})
})
it("Returns values of all 0s except for one value, which is a 1", () => {
return cifar10.cat.range().then(data => {
const {output} = data[0]
expect(output.filter(v => v==0)).to.have.lengthOf(9)
expect(output.filter(v => v==1)).to.have.lengthOf(1)
})
})
})
describe("get", () => {
it("Returns one image when requesting from training with no parameters", () => {
return cifar10.cat.training.get().then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.keys("input","output")
})
})
it("Returns one image when requesting from test with no parameters", () => {
return cifar10.cat.test.get().then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.keys("input","output")
})
})
it("Returns one image when requesting from training with an index as parameter", () => {
return cifar10.cat.training.get(1).then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.keys("input","output")
})
})
it("Returns one image when requesting from test with an index as parameter", () => {
return cifar10.cat.test.get(1).then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.keys("input","output")
})
})
it("Returns an array of images when requesting from training with an array of indeces", () => {
return cifar10.cat.training.get([1,2,3]).then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.lengthOf(3)
expect(data[0]).to.have.keys("input","output")
})
})
it("Returns an array of images when requesting from test with an array of indeces", () => {
return cifar10.cat.test.get([1,2,3]).then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.lengthOf(3)
expect(data[0]).to.have.keys("input","output")
})
})
it("Returns one image when requesting from no specific split section", () => {
return cifar10.cat.get().then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.keys("input","output")
})
})
it("Returns one image when requesting from no specific split section with an index", () => {
return cifar10.cat.get(0).then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.keys("input","output")
})
})
it("Returns an array of images when requesting from no specific split section withan array of indeces", () => {
return cifar10.cat.get([1,2,3]).then(data => {
expect(data).to.not.be.undefined
expect(data).to.have.lengthOf(3)
expect(data[0]).to.have.keys("input","output")
})
})
})
})
})
describe("shuffle", () => {
const testArr = [1,2,3,4,5, "a", "b", "c"]
const original = testArr.slice(0)
shuffle(testArr)
it("Keeps the same number of elements", () => {
expect(testArr).to.have.lengthOf(8)
})
it("Changes the order of the elements", () => {
expect(testArr).to.not.deep.equal(original)
})
it("Does not include any new elements", () => {
expect(testArr.every(elem => original.includes(elem))).to.be.true
})
it("Still includes all original elements", () => {
expect(original.every(elem => testArr.includes(elem))).to.be.true
})
})
describe("get", () => {
const fakeThis = {range: x => [x, 2, 3]}
beforeEach(() => {
sinon.spy(fakeThis, "range")
})
afterEach(() => {
fakeThis.range.restore()
})
it("Returns a promise", () => {
expect(get.bind(fakeThis)()).instanceof(Promise)
})
it("Returns the first item in the array returned by the .range() function", () => {
const out = get.bind(fakeThis, 1)()
expect(fakeThis.range).to.have.been.called
return expect(out).to.eventually.equal(1)
})
it("When index parameter is an array, it returns 1D array of first item from .range() function calls' response", () => {
const out = get.bind(fakeThis, [1, 7, 10])()
return expect(out).to.eventually.deep.equal([1,7,10])
})
})
describe("getData", () => {
it("Returns a promise", () => {
expect(getData("training", 1)).instanceof(Promise)
})
it("Resolves an array", () => {
return getData("training", 10).then(data => {
expect(data).instanceof(Array)
})
})
it("Resolves 100 items when requesting 100 items", () => {
return getData("training", 100).then(data => {
expect(data).to.have.lengthOf(100)
})
})
it("Resolves 0 items when requesting less than 10 items (5)", () => {
return getData("training", 5).then(data => {
expect(data).to.have.lengthOf(0)
})
})
it("Rounds down number of values to nearest 10. AKA, returns 50 items when requesting 57 items", () => {
return getData("training", 55).then(data => {
expect(data).to.have.lengthOf(50)
})
})
it("Loops through the data again if requesting more than is available", () => {
const oldIndecesData = indeces.training.cat
indeces.training.cat = [1,2,3]
return getData("training", 50).then(data => {
indeces.training.cat = oldIndecesData
expect(data.filter(({output}) => output.indexOf(1) == 3)).to.have.lengthOf(5)
})
})
})