One afternoon while I was walking my two dogs, I was thinking about data anomaly detection using neural autoencoder reconstruction error. Briefly, the idea is to create a neural network model that predicts its own input. Then any data items that aren’t predicted well — reconstruction error — are anomalous according to the model.
I created a demo using raw JavaScript. For my demo program, I used one of my standard synthetic datasets. The 240-item tab-delimited raw data looks like:
F 24 michigan 29500.00 liberal M 39 oklahoma 51200.00 moderate F 63 nebraska 75800.00 conservative M 36 michigan 44500.00 moderate F 27 nebraska 28600.00 liberal . . .
To prepare the data for a neural network, I encoded the sex variable as M = 0, F = 1, and State as Michigan = 100, Nebraska = 010, Oklahoma = 001, and political leaning as conservative = 100, moderate = 010, liberal = 001. I normalized the numeric data. I divided age values by 100, and divided the target income values by 100,000. The resulting 240-item encoded and normalized comma-delimited data looks like:
1, 0.24, 1, 0, 0, 0.2950, 0, 0, 1 0, 0.39, 0, 0, 1, 0.5120, 0, 1, 0 1, 0.63, 0, 1, 0, 0.7580, 1, 0, 0 0, 0.36, 1, 0, 0, 0.4450, 0, 1, 0 1, 0.27, 0, 1, 0, 0.2860, 0, 0, 1 . . .
I created a 9-6-9 neural network with tanh() hidden node activation and identity() output activation. For training, I used a learning rate of 0.10, a batch size of 10, and 1,000 epochs. After training, I did a quick sanity check by computing the output for an input of:
M 46 Oklahoma $66,400 moderate = 0, 0.46, 0,0,1, 0.66400, 0,1,0
The rounded output was
-0.00 0.51 -0.00 -0.00 1.00 0.61 0.00 0.99 -0.00
and so the output matches the input very closely, as expected. Next the demo invokes an analyze() method that walks through each of the 240 data items, computing the error between input and output, where error is the square root of the sum of squared differences between fields. I could have used any of a number of other error functions.
The result was that the most anomalous item was at index [130] and had value (0.0000, 0.3600, 0.0000, 1.0000, 0.0000, 0.5300, 0.0000, 0.0000, 1.0000). That item’s reconstruction was (0.0009, 0.4047, -0.0066, 1.0019, 0.0046, 0.4971, 0.0193, -0.0114, 0.9921). The predicted sex, State, and political leaning values were very close but predicted age and income values were off by roughly 4 years and $3,000.00 — not too far but farther than the other 239 data items.

I’m not a big fan of most movie remakes. But an anomaly for me is the story of “Treasure Island” based on the 1883 novel by Robert Louis Stevenson — I like almost all of the many remakes of the movie.
Left: The 1934 version starred Wallace Beery as pirate Long John Silver. This version established nearly all of what people associate with pirates, such as the parrot on the shoulder, the eye patch, the peg leg. This movie, with its tension between good and evil, holds up well, even by today’s standards.
Center: The 1950 version starred Robert Newton as Silver. It was Disney’s first live-action movie and remains one of the company’s best films. This movie was the direct inspiration for the Pirates of the Caribbean ride at Disneyland — I worked on that ride occasionally when I worked at Disneyland during my college days. Ask me about the “mylar table club” sometime.
Right: “Muppet Treasure Island” (1995) sounds a bit silly but it’s a very clever and funny movie. Actor Tim Curry plays Silver. This is one of my favorite Muppet movies.
Demo program. Replace “lt” (less than), “gt”, “lte”, “gte”, “and” with Boolean operator symbols.
// people_anomaly.js
// node.js ES6
// autoencoder reconstruction error
// tanh, identity activations, MSE loss
let U = require("..\\Utils\\utilities_lib.js")
let FS = require("fs")
// ----------------------------------------------------------
class NeuralNet
{
constructor(numInput, numHidden, numOutput, seed)
{
this.rnd = new U.Erratic(seed); // pseudo-random
this.ni = numInput;
this.nh = numHidden;
this.no = numOutput;
this.iNodes = U.vecMake(this.ni, 0.0);
this.hNodes = U.vecMake(this.nh, 0.0);
this.oNodes = U.vecMake(this.no, 0.0);
this.ihWeights = U.matMake(this.ni, this.nh, 0.0);
this.hoWeights = U.matMake(this.nh, this.no, 0.0);
this.hBiases = U.vecMake(this.nh, 0.0);
this.oBiases = U.vecMake(this.no, 0.0);
this.ihGrads = U.matMake(this.ni, this.nh, 0.0);
this.hbGrads = U.vecMake(this.nh, 0.0);
this.hoGrads = U.matMake(this.nh, this.no, 0.0);
this.obGrads = U.vecMake(this.no, 0.0);
this.initWeights();
}
initWeights()
{
let lo = -0.10;
let hi = 0.10;
for (let i = 0; i "lt" this.ni; ++i) {
for (let j = 0; j "lt" this.nh; ++j) {
this.ihWeights[i][j] = (hi - lo) * this.rnd.next() + lo;
}
}
for (let j = 0; j "lt" this.nh; ++j) {
for (let k = 0; k "lt" this.no; ++k) {
this.hoWeights[j][k] = (hi - lo) * this.rnd.next() + lo;
}
}
}
// --------------------------------------------------------
computeOutputs(X)
{
let hSums = U.vecMake(this.nh, 0.0);
let oSums = U.vecMake(this.no, 0.0);
this.iNodes = X;
for (let j = 0; j "lt" this.nh; ++j) {
for (let i = 0; i "lt" this.ni; ++i) {
hSums[j] += this.iNodes[i] * this.ihWeights[i][j];
}
hSums[j] += this.hBiases[j];
this.hNodes[j] = U.hyperTan(hSums[j]);
}
for (let k = 0; k "lt" this.no; ++k) {
for (let j = 0; j "lt" this.nh; ++j) {
oSums[k] += this.hNodes[j] * this.hoWeights[j][k];
}
oSums[k] += this.oBiases[k];
}
// this.oNodes = U.softmax(oSums);
for (let k = 0; k "lt" this.no; ++k)
this.oNodes[k] = oSums[k]; // Identity
let result = [];
for (let k = 0; k "lt" this.no; ++k) {
result[k] = this.oNodes[k];
}
return result;
} // eval()
// --------------------------------------------------------
setWeights(wts)
{
// order: ihWts, hBiases, hoWts, oBiases
let p = 0;
for (let i = 0; i "lt" this.ni; ++i) {
for (let j = 0; j "lt" this.nh; ++j) {
this.ihWeights[i][j] = wts[p++];
}
}
for (let j = 0; j "lt" this.nh; ++j) {
this.hBiases[j] = wts[p++];
}
for (let j = 0; j "lt" this.nh; ++j) {
for (let k = 0; k "lt" this.no; ++k) {
this.hoWeights[j][k] = wts[p++];
}
}
for (let k = 0; k "lt" this.no; ++k) {
this.oBiases[k] = wts[p++];
}
} // setWeights()
getWeights()
{
// order: ihWts, hBiases, hoWts, oBiases
let numWts = (this.ni * this.nh) + this.nh +
(this.nh * this.no) + this.no;
let result = U.vecMake(numWts, 0.0);
let p = 0;
for (let i = 0; i "lt" this.ni; ++i) {
for (let j = 0; j "lt" this.nh; ++j) {
result[p++] = this.ihWeights[i][j];
}
}
for (let j = 0; j "lt" this.nh; ++j) {
result[p++] = this.hBiases[j];
}
for (let j = 0; j "lt" this.nh; ++j) {
for (let k = 0; k "lt" this.no; ++k) {
result[p++] = this.hoWeights[j][k];
}
}
for (let k = 0; k "lt" this.no; ++k) {
result[p++] = this.oBiases[k];
}
return result;
} // getWeights()
shuffle(v)
{
// Fisher-Yates
let n = v.length;
for (let i = 0; i "lt" n; ++i) {
let r = this.rnd.nextInt(i, n);
let tmp = v[r];
v[r] = v[i];
v[i] = tmp;
}
}
// --------------------------------------------------------
// helpers for train(): zeroOutGrads(), accumGrads(y),
// updateWeights(lrnRate)
// --------------------------------------------------------
zeroOutGrads()
{
for (let i = 0; i "lt" this.ni; ++i)
for (let j = 0; j "lt" this.nh; ++j)
this.ihGrads[i][j] = 0.0;
for (let j = 0; j "lt" this.nh; ++j)
this.hbGrads[j] = 0.0;
for (let j = 0; j "lt" this.nh; ++j)
for (let k = 0; k "lt" this.no; ++k)
this.hoGrads[j][k] = 0.0;
for (let k = 0; k "lt" this.no; ++k)
this.obGrads[k] = 0.0;
}
accumGrads(y)
{
// y is target vector
let oSignals = U.vecMake(this.no, 0.0);
let hSignals = U.vecMake(this.nh, 0.0);
// 1. compute output node scratch signals
for (let k = 0; k "lt" this.no; ++k) {
let derivative = 1.0; // Identity activation
// let derivative =
// this.oNodes[k] * (1 - this.oNodes[k]); // MSE
oSignals[k] = derivative *
(this.oNodes[k] - y[k]); // CEE
}
// 2. accum hidden-to-output gradients
for (let j = 0; j "lt" this.nh; ++j)
for (let k = 0; k "lt" this.no; ++k)
this.hoGrads[j][k] += oSignals[k] * this.hNodes[j];
// 3. accum output node bias gradients
for (let k = 0; k "lt" this.no; ++k)
this.obGrads[k] += oSignals[k] * 1.0; // 1.0 dummy
// 4. compute hidden node signals
for (let j = 0; j "lt" this.nh; ++j) {
let sum = 0.0;
for (let k = 0; k "lt" this.no; ++k)
sum += oSignals[k] * this.hoWeights[j][k];
let derivative =
(1 - this.hNodes[j]) *
(1 + this.hNodes[j]); // assumes tanh
hSignals[j] = derivative * sum;
}
// 5. accum input-to-hidden gradients
for (let i = 0; i "lt" this.ni; ++i)
for (let j = 0; j "lt" this.nh; ++j)
this.ihGrads[i][j] += hSignals[j] * this.iNodes[i];
// 6. accum hidden node bias gradients
for (let j = 0; j "lt" this.nh; ++j)
this.hbGrads[j] += hSignals[j] * 1.0; // 1.0 dummy
} // accumGrads
updateWeights(lrnRate)
{
// assumes all gradients computed
// 1. update input-to-hidden weights
for (let i = 0; i "lt" this.ni; ++i) {
for (let j = 0; j "lt" this.nh; ++j) {
let delta = -1.0 * lrnRate * this.ihGrads[i][j];
this.ihWeights[i][j] += delta;
}
}
// 2. update hidden node biases
for (let j = 0; j "lt" this.nh; ++j) {
let delta = -1.0 * lrnRate * this.hbGrads[j];
this.hBiases[j] += delta;
}
// 3. update hidden-to-output weights
for (let j = 0; j "lt" this.nh; ++j) {
for (let k = 0; k "lt" this.no; ++k) {
let delta = -1.0 * lrnRate * this.hoGrads[j][k];
this.hoWeights[j][k] += delta;
}
}
// 4. update output node biases
for (let k = 0; k "lt" this.no; ++k) {
let delta = -1.0 * lrnRate * this.obGrads[k];
this.oBiases[k] += delta;
}
} // updateWeights()
// --------------------------------------------------------
train(trainX, trainY, lrnRate, batSize, maxEpochs)
{
let n = trainX.length; // 200
let batchesPerEpoch = Math.trunc(n / batSize); // 20
let freq = Math.trunc(maxEpochs / 10); // progress
let indices = U.arange(n);
// ----------------------------------------------------
//
// n = 200; bs = 10
// batches per epoch = 200 / 10 = 20
// for epoch = 0; epoch "lt" maxEpochs; ++epoch
// for batch = 0; batch "lt" bpe; ++batch
// for item = 0; item "lt" bs; ++item
// compute output
// accum grads
// end-item
// update weights
// zero-out grads
// end-batches
// shuffle indices
// end-epochs
//
// ----------------------------------------------------
for (let epoch = 0; epoch "lt" maxEpochs; ++epoch) {
this.shuffle(indices);
let ptr = 0; // points into indices
for (let batIdx = 0; batIdx "lt" batchesPerEpoch;
++batIdx) // 0, 1, . . 23
{
for (let i = 0; i "lt" batSize; ++i) { // 0 . . 9
let ii = indices[ptr++]; // compute output
let x = trainX[ii];
let y = trainY[ii];
this.computeOutputs(x); // into this.oNodes
this.accumGrads(y);
}
this.updateWeights(lrnRate);
this.zeroOutGrads(); // prep for next batch
} // batches
if (epoch % freq == 0) {
let mse =
this.meanSqErr(trainX, trainY).toFixed(4);
let s1 = "epoch: " +
epoch.toString().padStart(6, ' ');
let s2 = " MSE = " +
mse.toString().padStart(8, ' ');
console.log(s1 + s2);
}
} // epoch
} // train
// --------------------------------------------------------
// CEE not used
// --------------------------------------------------------
meanCrossEntErr(dataX, dataY)
{
let sumCEE = 0.0; // cross entropy errors
for (let i = 0; i "lt" dataX.length; ++i) {
let X = dataX[i];
let Y = dataY[i]; // target like (0, 1, 0)
let oupt = this.computeOutputs(X);
let idx = U.argmax(Y); // find loc of 1 in target
sumCEE += Math.log(oupt[idx]);
}
sumCEE *= -1;
return sumCEE / dataX.length;
}
meanSqErr(dataX, dataY)
{
let sumSE = 0.0;
for (let i = 0; i "lt" dataX.length; ++i) {
let X = dataX[i];
let Y = dataY[i]; // target output like (0, 1, 0)
let oupt = this.computeOutputs(X);
for (let k = 0; k "lt" this.no; ++k) {
let err = Y[k] - oupt[k] // target - computed
sumSE += err * err;
}
}
return sumSE / dataX.length; // consider Root MSE
}
// --------------------------------------------------------
analyze(dataX)
{
let n = dataX.length;
let largestError = 0.0;
let mostAnomalous = dataX[0];
let mostAnomalousIdx = 0;
for (let i = 0; i "lt" n; ++i) {
let predY = this.computeOutputs(dataX[i]);
let actualY = dataX[i];
let sumErr = 0.0;
for (let j = 0; j "lt" predY.length; ++j) {
sumErr += (predY[j] - actualY[j]) *
(predY[j] - actualY[j]);
}
let itemErr = Math.sqrt(sumErr);
if (itemErr "gt" largestError) {
largestError = itemErr;
mostAnomalous = dataX[i];
mostAnomalousIdx = i;
}
}
console.log("\nLargest reconstruction error = " +
largestError.toFixed(4).toString());
console.log("Most anomalous item = [ " +
mostAnomalousIdx.toString() + " ]");
U.vecShow(mostAnomalous, 4, 8, true);
console.log("\nThe reconstruction is: ");
let recon = this.computeOutputs(dataX[mostAnomalousIdx]);
U.vecShow(recon, 4, 8, true);
}
// --------------------------------------------------------
saveWeights(fn)
{
let wts = this.getWeights();
let n = wts.length;
let s = "";
for (let i = 0; i "lt" n-1; ++i) {
s += wts[i].toString() + ",";
}
s += wts[n-1];
FS.writeFileSync(fn, s);
}
loadWeights(fn)
{
let n = (this.ni * this.nh) + this.nh +
(this.nh * this.no) + this.no;
let wts = U.vecMake(n, 0.0);
let all = FS.readFileSync(fn, "utf8");
let strVals = all.split(",");
let nn = strVals.length;
if (n != nn) {
throw("Size error in NeuralNet.loadWeights()");
}
for (let i = 0; i "lt" n; ++i) {
wts[i] = parseFloat(strVals[i]);
}
this.setWeights(wts);
}
} // NeuralNet
// ----------------------------------------------------------
function main()
{
// process.stdout.write("\033[0m"); // reset
// process.stdout.write("\x1b[1m" + "\x1b[37m"); // white
console.log("\nBegin JavaScript NN anomaly demo ");
console.log("Autoencoder reconstruction error ");
// 1. load data
// -1 0.29 1 0 0 0.65400 0 0 1
// 1 0.36 0 0 1 0.58300 1 0 0
console.log("\nLoading data into memory ");
let dataX = U.loadTxt(".\\Data\\people_all.txt", ",",
[0,1,2,3,4,5,6,7,8], "#");
console.log("Done ");
console.log("\nFirst three data items: ");
for (let i = 0; i "lt" 3; ++i)
U.vecShow(dataX[i], 4, 8, true);
// 2. create network
console.log("\nCreating 9-6-9 tanh, identity NN ");
let seed = 0;
let nn = new NeuralNet(9, 6, 9, seed);
console.log("Done ");
// 3. train network
let lrnRate = 0.01;
let maxEpochs = 1000;
console.log("\nSetting learn rate = 0.01 ");
console.log("Setting bat size = 10 ");
nn.train(dataX, dataX, lrnRate, 10, maxEpochs);
console.log("Training complete ");
// 4. sanity check
console.log("\nPredict M 46 Oklahoma $66,400 moderate ");
let x = [0, 0.46, 0, 0, 1, 0.6640, 0, 1, 0];
let predicted = nn.computeOutputs(x);
console.log("Predicted: ");
U.vecShow(predicted, 4, 8, true);
// 5. analyze reconstruction error
console.log("\nAnalyzing reconstruction error ");
nn.analyze(dataX);
// 6. save trained model
fn = ".\\Models\\people_wts.txt";
console.log("\nSaving model weights and biases to: ");
console.log(fn);
nn.saveWeights(fn);
//process.stdout.write("\033[0m"); // reset
console.log("\n\nEnd demo");
}
main()
Utility functions file:
// utilities_lib.js
// ES6
let FS = require('fs');
// ----------------------------------------------------------
function loadTxt(fn, delimit, usecols, comment) {
// efficient but mildly complicated
let all = FS.readFileSync(fn, "utf8"); // giant string
all = all.trim(); // strip final crlf in file
let lines = all.split("\n"); // array of lines
// count number non-comment lines
let nRows = 0;
for (let i = 0; i "lt" lines.length; ++i) {
if (!lines[i].startsWith(comment))
++nRows;
}
let nCols = usecols.length;
let result = matMake(nRows, nCols, 0.0);
let r = 0; // into lines
let i = 0; // into result[][]
while (r "lt" lines.length) {
if (lines[r].startsWith(comment)) {
++r; // next row
}
else {
let tokens = lines[r].split(delimit);
for (let j = 0; j "lt" nCols; ++j) {
result[i][j] = parseFloat(tokens[usecols[j]]);
}
++r;
++i;
}
}
return result;
}
// ----------------------------------------------------------
function arange(n)
{
let result = [];
for (let i = 0; i "lt" n; ++i) {
result[i] = Math.trunc(i);
}
return result;
}
// ----------------------------------------------------------
class Erratic
{
constructor(seed)
{
this.seed = seed + 0.5; // avoid 0
}
next()
{
let x = Math.sin(this.seed) * 1000;
let result = x - Math.floor(x); // [0.0,1.0)
this.seed = result; // for next call
return result;
}
nextInt(lo, hi)
{
let x = this.next();
return Math.trunc((hi - lo) * x + lo);
}
}
// ----------------------------------------------------------
function vecMake(n, val)
{
let result = [];
for (let i = 0; i "lt" n; ++i) {
result[i] = val;
}
return result;
}
function matMake(rows, cols, val)
{
let result = [];
for (let i = 0; i "lt" rows; ++i) {
result[i] = [];
for (let j = 0; j "lt" cols; ++j) {
result[i][j] = val;
}
}
return result;
}
function matToOneHot(m, n)
{
// convert ordinal (0,1,2 . .) to one-hot
let rows = m.length;
let cols = m[0].length;
let result = matMake(rows, n);
for (let i = 0; i "lt" rows; ++i) {
let k = Math.trunc(m[i][0]); // 0,1,2 . .
result[i] = vecMake(n, 0.0); // [0.0 0.0 0.0]
result[i][k] = 1.0; // [ 0.0 1.0 0.0]
}
return result;
}
function matToVec(m)
{
let r = m.length;
let c = m[0].length;
let result = vecMake(r*c, 0.0);
let k = 0;
for (let i = 0; i "lt" r; ++i) {
for (let j = 0; j "lt" c; ++j) {
result[k++] = m[i][j];
}
}
return result;
}
function vecShow(v, dec, len)
{
for (let i = 0; i "lt" v.length; ++i) {
if (i != 0 "and" i % len == 0) {
process.stdout.write("\n");
}
if (v[i] "gte" 0.0) {
process.stdout.write(" "); // + or - space
}
process.stdout.write(v[i].toFixed(dec));
process.stdout.write(" ");
}
process.stdout.write("\n");
}
function vecShow(vec, dec, wid, nl)
{
for (let i = 0; i "lt" vec.length; ++i) {
let x = vec[i];
if (Math.abs(x) "lt" 0.000001) x = 0.0 // avoid -0.00
let xx = x.toFixed(dec);
let s = xx.toString().padStart(wid, ' ');
process.stdout.write(s);
process.stdout.write(" ");
}
if (nl == true)
process.stdout.write("\n");
}
function matShow(m, dec, wid)
{
let rows = m.length;
let cols = m[0].length;
for (let i = 0; i "lt" rows; ++i) {
for (let j = 0; j "lt" cols; ++j) {
if (m[i][j] "gte" 0.0) {
process.stdout.write(" "); // + or - space
}
process.stdout.write(m[i][j].toFixed(dec));
process.stdout.write(" ");
}
process.stdout.write("\n");
}
}
function argmax(v)
{
let result = 0;
let m = v[0];
for (let i = 0; i "lt" v.length; ++i) {
if (v[i] "gt" m) {
m = v[i];
result = i;
}
}
return result;
}
function hyperTan(x)
{
if (x "lt" -10.0) {
return -1.0;
}
else if (x "gt" 10.0) {
return 1.0;
}
else {
return Math.tanh(x);
}
}
function logSig(x)
{
if (x "lt" -10.0) {
return 0.0;
}
else if (x "gt" 10.0) {
return 1.0;
}
else {
return 1.0 / (1.0 + Math.exp(-x));
}
}
function vecMax(vec)
{
let mx = vec[0];
for (let i = 0; i "lt" vec.length; ++i) {
if (vec[i] "gt" mx) {
mx = vec[i];
}
}
return mx;
}
function softmax(vec)
{
//let m = Math.max(...vec); // or 'spread' operator
let m = vecMax(vec);
let result = [];
let sum = 0.0;
for (let i = 0; i "lt" vec.length; ++i) {
result[i] = Math.exp(vec[i] - m);
sum += result[i];
}
for (let i = 0; i "lt" result.length; ++i) {
result[i] = result[i] / sum;
}
return result;
}
module.exports = {
vecMake,
matMake,
matToOneHot,
matToVec,
vecShow,
matShow,
argmax,
loadTxt,
arange,
Erratic,
hyperTan,
logSig,
vecMax,
softmax
};
Data:
# people_all.txt # # sex (0 = male, 1 = female), age / 100, # state (michigan = 100, nebraska = 010, oklahoma = 001), # income / 100_000, # politics (conservative = 100, moderate = 010, liberal = 001) # 1, 0.24, 1, 0, 0, 0.2950, 0, 0, 1 0, 0.39, 0, 0, 1, 0.5120, 0, 1, 0 1, 0.63, 0, 1, 0, 0.7580, 1, 0, 0 0, 0.36, 1, 0, 0, 0.4450, 0, 1, 0 1, 0.27, 0, 1, 0, 0.2860, 0, 0, 1 1, 0.50, 0, 1, 0, 0.5650, 0, 1, 0 1, 0.50, 0, 0, 1, 0.5500, 0, 1, 0 0, 0.19, 0, 0, 1, 0.3270, 1, 0, 0 1, 0.22, 0, 1, 0, 0.2770, 0, 1, 0 0, 0.39, 0, 0, 1, 0.4710, 0, 0, 1 1, 0.34, 1, 0, 0, 0.3940, 0, 1, 0 0, 0.22, 1, 0, 0, 0.3350, 1, 0, 0 1, 0.35, 0, 0, 1, 0.3520, 0, 0, 1 0, 0.33, 0, 1, 0, 0.4640, 0, 1, 0 1, 0.45, 0, 1, 0, 0.5410, 0, 1, 0 1, 0.42, 0, 1, 0, 0.5070, 0, 1, 0 0, 0.33, 0, 1, 0, 0.4680, 0, 1, 0 1, 0.25, 0, 0, 1, 0.3000, 0, 1, 0 0, 0.31, 0, 1, 0, 0.4640, 1, 0, 0 1, 0.27, 1, 0, 0, 0.3250, 0, 0, 1 1, 0.48, 1, 0, 0, 0.5400, 0, 1, 0 0, 0.64, 0, 1, 0, 0.7130, 0, 0, 1 1, 0.61, 0, 1, 0, 0.7240, 1, 0, 0 1, 0.54, 0, 0, 1, 0.6100, 1, 0, 0 1, 0.29, 1, 0, 0, 0.3630, 1, 0, 0 1, 0.50, 0, 0, 1, 0.5500, 0, 1, 0 1, 0.55, 0, 0, 1, 0.6250, 1, 0, 0 1, 0.40, 1, 0, 0, 0.5240, 1, 0, 0 1, 0.22, 1, 0, 0, 0.2360, 0, 0, 1 1, 0.68, 0, 1, 0, 0.7840, 1, 0, 0 0, 0.60, 1, 0, 0, 0.7170, 0, 0, 1 0, 0.34, 0, 0, 1, 0.4650, 0, 1, 0 0, 0.25, 0, 0, 1, 0.3710, 1, 0, 0 0, 0.31, 0, 1, 0, 0.4890, 0, 1, 0 1, 0.43, 0, 0, 1, 0.4800, 0, 1, 0 1, 0.58, 0, 1, 0, 0.6540, 0, 0, 1 0, 0.55, 0, 1, 0, 0.6070, 0, 0, 1 0, 0.43, 0, 1, 0, 0.5110, 0, 1, 0 0, 0.43, 0, 0, 1, 0.5320, 0, 1, 0 0, 0.21, 1, 0, 0, 0.3720, 1, 0, 0 1, 0.55, 0, 0, 1, 0.6460, 1, 0, 0 1, 0.64, 0, 1, 0, 0.7480, 1, 0, 0 0, 0.41, 1, 0, 0, 0.5880, 0, 1, 0 1, 0.64, 0, 0, 1, 0.7270, 1, 0, 0 0, 0.56, 0, 0, 1, 0.6660, 0, 0, 1 1, 0.31, 0, 0, 1, 0.3600, 0, 1, 0 0, 0.65, 0, 0, 1, 0.7010, 0, 0, 1 1, 0.55, 0, 0, 1, 0.6430, 1, 0, 0 0, 0.25, 1, 0, 0, 0.4030, 1, 0, 0 1, 0.46, 0, 0, 1, 0.5100, 0, 1, 0 0, 0.36, 1, 0, 0, 0.5350, 1, 0, 0 1, 0.52, 0, 1, 0, 0.5810, 0, 1, 0 1, 0.61, 0, 0, 1, 0.6790, 1, 0, 0 1, 0.57, 0, 0, 1, 0.6570, 1, 0, 0 0, 0.46, 0, 1, 0, 0.5260, 0, 1, 0 0, 0.62, 1, 0, 0, 0.6680, 0, 0, 1 1, 0.55, 0, 0, 1, 0.6270, 1, 0, 0 0, 0.22, 0, 0, 1, 0.2770, 0, 1, 0 0, 0.50, 1, 0, 0, 0.6290, 1, 0, 0 0, 0.32, 0, 1, 0, 0.4180, 0, 1, 0 0, 0.21, 0, 0, 1, 0.3560, 1, 0, 0 1, 0.44, 0, 1, 0, 0.5200, 0, 1, 0 1, 0.46, 0, 1, 0, 0.5170, 0, 1, 0 1, 0.62, 0, 1, 0, 0.6970, 1, 0, 0 1, 0.57, 0, 1, 0, 0.6640, 1, 0, 0 0, 0.67, 0, 0, 1, 0.7580, 0, 0, 1 1, 0.29, 1, 0, 0, 0.3430, 0, 0, 1 1, 0.53, 1, 0, 0, 0.6010, 1, 0, 0 0, 0.44, 1, 0, 0, 0.5480, 0, 1, 0 1, 0.46, 0, 1, 0, 0.5230, 0, 1, 0 0, 0.20, 0, 1, 0, 0.3010, 0, 1, 0 0, 0.38, 1, 0, 0, 0.5350, 0, 1, 0 1, 0.50, 0, 1, 0, 0.5860, 0, 1, 0 1, 0.33, 0, 1, 0, 0.4250, 0, 1, 0 0, 0.33, 0, 1, 0, 0.3930, 0, 1, 0 1, 0.26, 0, 1, 0, 0.4040, 1, 0, 0 1, 0.58, 1, 0, 0, 0.7070, 1, 0, 0 1, 0.43, 0, 0, 1, 0.4800, 0, 1, 0 0, 0.46, 1, 0, 0, 0.6440, 1, 0, 0 1, 0.60, 1, 0, 0, 0.7170, 1, 0, 0 0, 0.42, 1, 0, 0, 0.4890, 0, 1, 0 0, 0.56, 0, 0, 1, 0.5640, 0, 0, 1 0, 0.62, 0, 1, 0, 0.6630, 0, 0, 1 0, 0.50, 1, 0, 0, 0.6480, 0, 1, 0 1, 0.47, 0, 0, 1, 0.5200, 0, 1, 0 0, 0.67, 0, 1, 0, 0.8040, 0, 0, 1 0, 0.40, 0, 0, 1, 0.5040, 0, 1, 0 1, 0.42, 0, 1, 0, 0.4840, 0, 1, 0 1, 0.64, 1, 0, 0, 0.7200, 1, 0, 0 0, 0.47, 1, 0, 0, 0.5870, 0, 0, 1 1, 0.45, 0, 1, 0, 0.5280, 0, 1, 0 0, 0.25, 0, 0, 1, 0.4090, 1, 0, 0 1, 0.38, 1, 0, 0, 0.4840, 1, 0, 0 1, 0.55, 0, 0, 1, 0.6000, 0, 1, 0 0, 0.44, 1, 0, 0, 0.6060, 0, 1, 0 1, 0.33, 1, 0, 0, 0.4100, 0, 1, 0 1, 0.34, 0, 0, 1, 0.3900, 0, 1, 0 1, 0.27, 0, 1, 0, 0.3370, 0, 0, 1 1, 0.32, 0, 1, 0, 0.4070, 0, 1, 0 1, 0.42, 0, 0, 1, 0.4700, 0, 1, 0 0, 0.24, 0, 0, 1, 0.4030, 1, 0, 0 1, 0.42, 0, 1, 0, 0.5030, 0, 1, 0 1, 0.25, 0, 0, 1, 0.2800, 0, 0, 1 1, 0.51, 0, 1, 0, 0.5800, 0, 1, 0 0, 0.55, 0, 1, 0, 0.6350, 0, 0, 1 1, 0.44, 1, 0, 0, 0.4780, 0, 0, 1 0, 0.18, 1, 0, 0, 0.3980, 1, 0, 0 0, 0.67, 0, 1, 0, 0.7160, 0, 0, 1 1, 0.45, 0, 0, 1, 0.5000, 0, 1, 0 1, 0.48, 1, 0, 0, 0.5580, 0, 1, 0 0, 0.25, 0, 1, 0, 0.3900, 0, 1, 0 0, 0.67, 1, 0, 0, 0.7830, 0, 1, 0 1, 0.37, 0, 0, 1, 0.4200, 0, 1, 0 0, 0.32, 1, 0, 0, 0.4270, 0, 1, 0 1, 0.48, 1, 0, 0, 0.5700, 0, 1, 0 0, 0.66, 0, 0, 1, 0.7500, 0, 0, 1 1, 0.61, 1, 0, 0, 0.7000, 1, 0, 0 0, 0.58, 0, 0, 1, 0.6890, 0, 1, 0 1, 0.19, 1, 0, 0, 0.2400, 0, 0, 1 1, 0.38, 0, 0, 1, 0.4300, 0, 1, 0 0, 0.27, 1, 0, 0, 0.3640, 0, 1, 0 1, 0.42, 1, 0, 0, 0.4800, 0, 1, 0 1, 0.60, 1, 0, 0, 0.7130, 1, 0, 0 0, 0.27, 0, 0, 1, 0.3480, 1, 0, 0 1, 0.29, 0, 1, 0, 0.3710, 1, 0, 0 0, 0.43, 1, 0, 0, 0.5670, 0, 1, 0 1, 0.48, 1, 0, 0, 0.5670, 0, 1, 0 1, 0.27, 0, 0, 1, 0.2940, 0, 0, 1 0, 0.44, 1, 0, 0, 0.5520, 1, 0, 0 1, 0.23, 0, 1, 0, 0.2630, 0, 0, 1 0, 0.36, 0, 1, 0, 0.5300, 0, 0, 1 1, 0.64, 0, 0, 1, 0.7250, 1, 0, 0 1, 0.29, 0, 0, 1, 0.3000, 0, 0, 1 0, 0.33, 1, 0, 0, 0.4930, 0, 1, 0 0, 0.66, 0, 1, 0, 0.7500, 0, 0, 1 0, 0.21, 0, 0, 1, 0.3430, 1, 0, 0 1, 0.27, 1, 0, 0, 0.3270, 0, 0, 1 1, 0.29, 1, 0, 0, 0.3180, 0, 0, 1 0, 0.31, 1, 0, 0, 0.4860, 0, 1, 0 1, 0.36, 0, 0, 1, 0.4100, 0, 1, 0 1, 0.49, 0, 1, 0, 0.5570, 0, 1, 0 0, 0.28, 1, 0, 0, 0.3840, 1, 0, 0 0, 0.43, 0, 0, 1, 0.5660, 0, 1, 0 0, 0.46, 0, 1, 0, 0.5880, 0, 1, 0 1, 0.57, 1, 0, 0, 0.6980, 1, 0, 0 0, 0.52, 0, 0, 1, 0.5940, 0, 1, 0 0, 0.31, 0, 0, 1, 0.4350, 0, 1, 0 0, 0.55, 1, 0, 0, 0.6200, 0, 0, 1 1, 0.50, 1, 0, 0, 0.5640, 0, 1, 0 1, 0.48, 0, 1, 0, 0.5590, 0, 1, 0 0, 0.22, 0, 0, 1, 0.3450, 1, 0, 0 1, 0.59, 0, 0, 1, 0.6670, 1, 0, 0 1, 0.34, 1, 0, 0, 0.4280, 0, 0, 1 0, 0.64, 1, 0, 0, 0.7720, 0, 0, 1 1, 0.29, 0, 0, 1, 0.3350, 0, 0, 1 0, 0.34, 0, 1, 0, 0.4320, 0, 1, 0 0, 0.61, 1, 0, 0, 0.7500, 0, 0, 1 1, 0.64, 0, 0, 1, 0.7110, 1, 0, 0 0, 0.29, 1, 0, 0, 0.4130, 1, 0, 0 1, 0.63, 0, 1, 0, 0.7060, 1, 0, 0 0, 0.29, 0, 1, 0, 0.4000, 1, 0, 0 0, 0.51, 1, 0, 0, 0.6270, 0, 1, 0 0, 0.24, 0, 0, 1, 0.3770, 1, 0, 0 1, 0.48, 0, 1, 0, 0.5750, 0, 1, 0 1, 0.18, 1, 0, 0, 0.2740, 1, 0, 0 1, 0.18, 1, 0, 0, 0.2030, 0, 0, 1 1, 0.33, 0, 1, 0, 0.3820, 0, 0, 1 0, 0.20, 0, 0, 1, 0.3480, 1, 0, 0 1, 0.29, 0, 0, 1, 0.3300, 0, 0, 1 0, 0.44, 0, 0, 1, 0.6300, 1, 0, 0 0, 0.65, 0, 0, 1, 0.8180, 1, 0, 0 0, 0.56, 1, 0, 0, 0.6370, 0, 0, 1 0, 0.52, 0, 0, 1, 0.5840, 0, 1, 0 0, 0.29, 0, 1, 0, 0.4860, 1, 0, 0 0, 0.47, 0, 1, 0, 0.5890, 0, 1, 0 1, 0.68, 1, 0, 0, 0.7260, 0, 0, 1 1, 0.31, 0, 0, 1, 0.3600, 0, 1, 0 1, 0.61, 0, 1, 0, 0.6250, 0, 0, 1 1, 0.19, 0, 1, 0, 0.2150, 0, 0, 1 1, 0.38, 0, 0, 1, 0.4300, 0, 1, 0 0, 0.26, 1, 0, 0, 0.4230, 1, 0, 0 1, 0.61, 0, 1, 0, 0.6740, 1, 0, 0 1, 0.40, 1, 0, 0, 0.4650, 0, 1, 0 0, 0.49, 1, 0, 0, 0.6520, 0, 1, 0 1, 0.56, 1, 0, 0, 0.6750, 1, 0, 0 0, 0.48, 0, 1, 0, 0.6600, 0, 1, 0 1, 0.52, 1, 0, 0, 0.5630, 0, 0, 1 0, 0.18, 1, 0, 0, 0.2980, 1, 0, 0 0, 0.56, 0, 0, 1, 0.5930, 0, 0, 1 0, 0.52, 0, 1, 0, 0.6440, 0, 1, 0 0, 0.18, 0, 1, 0, 0.2860, 0, 1, 0 0, 0.58, 1, 0, 0, 0.6620, 0, 0, 1 0, 0.39, 0, 1, 0, 0.5510, 0, 1, 0 0, 0.46, 1, 0, 0, 0.6290, 0, 1, 0 0, 0.40, 0, 1, 0, 0.4620, 0, 1, 0 0, 0.60, 1, 0, 0, 0.7270, 0, 0, 1 1, 0.36, 0, 1, 0, 0.4070, 0, 0, 1 1, 0.44, 1, 0, 0, 0.5230, 0, 1, 0 1, 0.28, 1, 0, 0, 0.3130, 0, 0, 1 1, 0.54, 0, 0, 1, 0.6260, 1, 0, 0 # 0, 0.51, 1, 0, 0, 0.6120, 0, 1, 0 0, 0.32, 0, 1, 0, 0.4610, 0, 1, 0 1, 0.55, 1, 0, 0, 0.6270, 1, 0, 0 1, 0.25, 0, 0, 1, 0.2620, 0, 0, 1 1, 0.33, 0, 0, 1, 0.3730, 0, 0, 1 0, 0.29, 0, 1, 0, 0.4620, 1, 0, 0 1, 0.65, 1, 0, 0, 0.7270, 1, 0, 0 0, 0.43, 0, 1, 0, 0.5140, 0, 1, 0 0, 0.54, 0, 1, 0, 0.6480, 0, 0, 1 1, 0.61, 0, 1, 0, 0.7270, 1, 0, 0 1, 0.52, 0, 1, 0, 0.6360, 1, 0, 0 1, 0.30, 0, 1, 0, 0.3350, 0, 0, 1 1, 0.29, 1, 0, 0, 0.3140, 0, 0, 1 0, 0.47, 0, 0, 1, 0.5940, 0, 1, 0 1, 0.39, 0, 1, 0, 0.4780, 0, 1, 0 1, 0.47, 0, 0, 1, 0.5200, 0, 1, 0 0, 0.49, 1, 0, 0, 0.5860, 0, 1, 0 0, 0.63, 0, 0, 1, 0.6740, 0, 0, 1 0, 0.30, 1, 0, 0, 0.3920, 1, 0, 0 0, 0.61, 0, 0, 1, 0.6960, 0, 0, 1 0, 0.47, 0, 0, 1, 0.5870, 0, 1, 0 1, 0.30, 0, 0, 1, 0.3450, 0, 0, 1 0, 0.51, 0, 0, 1, 0.5800, 0, 1, 0 0, 0.24, 1, 0, 0, 0.3880, 0, 1, 0 0, 0.49, 1, 0, 0, 0.6450, 0, 1, 0 1, 0.66, 0, 0, 1, 0.7450, 1, 0, 0 0, 0.65, 1, 0, 0, 0.7690, 1, 0, 0 0, 0.46, 0, 1, 0, 0.5800, 1, 0, 0 0, 0.45, 0, 0, 1, 0.5180, 0, 1, 0 0, 0.47, 1, 0, 0, 0.6360, 1, 0, 0 0, 0.29, 1, 0, 0, 0.4480, 1, 0, 0 0, 0.57, 0, 0, 1, 0.6930, 0, 0, 1 0, 0.20, 1, 0, 0, 0.2870, 0, 0, 1 0, 0.35, 1, 0, 0, 0.4340, 0, 1, 0 0, 0.61, 0, 0, 1, 0.6700, 0, 0, 1 0, 0.31, 0, 0, 1, 0.3730, 0, 1, 0 1, 0.18, 1, 0, 0, 0.2080, 0, 0, 1 1, 0.26, 0, 0, 1, 0.2920, 0, 0, 1 0, 0.28, 1, 0, 0, 0.3640, 0, 0, 1 0, 0.59, 0, 0, 1, 0.6940, 0, 0, 1

.NET Test Automation Recipes
Software Testing
SciPy Programming Succinctly
Keras Succinctly
R Programming
2026 Visual Studio Live
2025 Summer MLADS Conference
2026 DevIntersection Conference
2025 Machine Learning Week
2025 Ai4 Conference
2026 G2E Conference
2026 iSC West Conference
You must be logged in to post a comment.