Skip to content

Instantly share code, notes, and snippets.

@johnkazer
Last active May 9, 2019 07:39
Show Gist options
  • Save johnkazer/ee7b608b41bee1f67c3db900bd0e1c0a to your computer and use it in GitHub Desktop.
Save johnkazer/ee7b608b41bee1f67c3db900bd0e1c0a to your computer and use it in GitHub Desktop.
exports.start = function (res) { // res being the Express response object, being the server package I used
const network = new NN.NeuralNetwork();
let dataTypes = []
let listOfTrainingData = []
let listOfTestData = []
let trainingDataRaw = []
let testDataRaw = []
const trainingOptions = { /* config options */ }
var parser = csv({ delimiter: ',' }, function (err, data) { // in general, I find processing of arbitrary data files hard to do with pure functions - so I took some short cuts...
if (err || data.length === 0) {
return null
} else {
dataTypes = data[0].slice(0) // not very pure
parseTrainingData(data.slice(1)) // this should normally return the completed data - see in-function comments below
}
})
try { // not really pure either, but I don't know a better way of handling contact with the file system. At least the event-driven code is all in one place
const stream = fs.createReadStream(path.join(__dirname, 'data.csv'))
stream.pipe(parser)
stream.on('close', () => {
network.train(definedTrainingDataset(trainingDataRaw), trainingOptions)
const nnConfig = network.toJSON()
const significantInputs = identifySignificantInputs(nnConfig)
fs.writeFileSync('savedNN.json', JSON.stringify(nnConfig, null, 2))
res.send({ msg: '', result: generatedTestResults(testData), keyInputData: significantInputs, testedData: listOfTestData })
})
stream.on('error', (error) => {
throw(error)
})
} catch (err) {
res.send({ msg: err })
}
function parseTrainingData (data) {
if (data[0][0] === 'TEST') {
return parseTestData(data.slice(1))
} else if (data.length === 0) {
return // normally a recursive function would return the completed data, but see next comment for why not in this case
} else {
listOfTrainingData.push(data[0][0]) // not very pure but I'm OK with it for now. Hard to return two new arrays and then two more test data arrays
trainingDataRaw.push(data[0].slice(1))
return parseTrainingData(data.slice(1))
}
}
function parseTestData (data) {
if (data.length === 0) {
return // see above regarding return values
} else {
listOfTestData.push(data[0][0]) // not very pure either!
testDataRaw.push(data[0].slice(1))
return parseTestData(data.slice(1))
}
}
function identifySignificantInputs (nnConfigData) {
const listOfHiddenNodes = Object.values(nnConfigData.layers[1])
const summedWeights = listOfHiddenNodes.map((node) => {
return Object.values(node.weights).reduce(function sumWeights (sum, weight) {
return sum + weight
})
})
const numWeights = summedWeights.length > 10 ? 10 : summedWeights.length
const topTen = summedWeights.sort().slice(summedWeights.length - numWeights)
return topTen.map((summedWeight) => { // what is actually wanted are the indexes of the highest 10 summedWeights values
return dataTypes[summedWeights.indexOf(summedWeight)] // then match these to the original data (column) names
})
}
function definedTrainingDataset(rawData) {
return rawData.map((data) => {
const input = data.slice(0, data.length - 1)
const output = data.slice(data.length - 1) // the CSV defines the desired neural network output as the last item in a row
return { input, output } // Brain.js specifies this type of object for the training set
})
}
function generatedTestResults(dataToTest) {
return dataToTest.map((dataPoint) => {
const input = dataPoint.slice(0, dataPoint.length - 1)
const output = dataPoint.slice(dataPoint.length - 1) // output is last item in a row
return { actual: network.run(input), desired: output } // not pure, as running the test function here - probably should segregate this better
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment