Я пытаюсь построить базовую c нейронную сеть с прямой связью с нуля, чтобы понять ее более глубоко.
Следуя моему другому вопросу , у него проблема с обучением партии. Что происходит, когда я передаю все входные данные, веса корректируются правильно, так как я вижу, что прогнозируемые значения становятся все ближе и ближе к ожидаемым значениям. Но тест заканчивается с точностью около 10%. Если я обучаю их пакетами, независимо от количества итераций, я вижу, что предсказанные значения в значительной степени совпадают.
Я проверил алгоритм обратного распространения ошибки, если все в порядке, но не могу найти проблем. Пробовал много вариантов слоев, нейронов внутри них, разную скорость обучения, разные размеры пакетов, нормализации, но ничего не работает. Однажды я запустил его на 10000 итераций, в течение 3 часов (я подумал, может быть, у него не хватит времени на обучение), но те же ужасные результаты. Провел исследование, и многие люди столкнулись с той же проблемой, и попробовали кучу решений для них, ничего не помогло. Я не даю слишком подробную информацию о проблеме, но, по крайней мере, вы можете увидеть, как я «заставил это работать» и какие значения это дает по времени:
const standardizeFeatures = (features, mean, variance) => {
const standardizedFeatures = features.sub(mean).div(variance.pow(0.5));
tf.dispose(features);
return standardizedFeatures;
}
const NN = ({
trainFeatures = [],
trainLabels = [],
layers = [],
learningRate = 0.1,
batchSize,
standardize = false
}) => {
const { mean, variance } = tf.tidy(() => {
const { mean, variance } = tf.moments(trainFeatures, 0);
const filler = variance
.cast('bool')
.logicalNot()
.cast('float32');
return { mean, variance: variance.add(filler) };
});
learningRate = tf.tensor(learningRate);
trainFeatures = standardize
? standardizeFeatures(trainFeatures, mean, variance)
: trainFeatures;
let weightsOfLayers = (() => {
const numberOfNeuronsOfOutput = trainLabels.shape[1];
const numberOfNeuronsOfLayers = [...layers, numberOfNeuronsOfOutput];
return numberOfNeuronsOfLayers.map(
(numberOfNeuronsOfCurrentLayer, i) => {
const numberOfNeuronsOfPrevLayer = i === 0
? trainFeatures.shape[1]
: numberOfNeuronsOfLayers[i - 1];
const currentLayerWeights = tf.stack(
[...Array(numberOfNeuronsOfCurrentLayer)].map(() => {
const bias = 1;
const neuronWeights = tf.truncatedNormal([bias + numberOfNeuronsOfPrevLayer]);
return neuronWeights;
}), 1
);
return currentLayerWeights;
}
);
})();
const getActivationsOfLayers = featureSet => tf.tidy(() => {
const layers = [featureSet];
weightsOfLayers.forEach(currentLayerWeights => {
const prevLayer = tf
.ones([layers[layers.length - 1].shape[0], 1])
.concat(layers[layers.length - 1], 1);
const currentLayer = prevLayer.matMul(currentLayerWeights);
layers.push(currentLayer.sigmoid());
});
return layers;
});
const getCost = (predictionLabelSet, labelSet) => (
predictionLabelSet
.sub(labelSet)
.pow(2)
.sum()
.div(labelSet.shape[0])
);
const updateLearningRate = (prevCost, cost) => tf.tidy(() => {
let newLearningRate = null;
if (prevCost) {
const modifier = tf.stack([cost, prevCost]).argMax().add(1.1).div(2);
newLearningRate = learningRate.mul(modifier);
tf.dispose(learningRate);
} else {
newLearningRate = learningRate;
}
return newLearningRate
})
const gradientDescent = (activationsOfLayers, labelSet) => tf.tidy(() => {
activationsOfLayers = activationsOfLayers.reverse();
let dCda = null;
const updatedWeightsOfLayers = weightsOfLayers.reverse().map((weights, i) => {
const prevActivations = activationsOfLayers[i - 1];
const activations = activationsOfLayers[i];
const nextActivations = activationsOfLayers[i + 1];
const prevWeights = weightsOfLayers[i - 1];
if (dCda) {
const dadn = prevActivations
.sub(1)
.mul(prevActivations)
.mul(-1);
dCda = prevWeights
.slice([1, 0], [-1, -1])
.matMul(dCda.mul(dadn.transpose()));
} else {
dCda = activations
.sub(labelSet)
.mul(2)
.transpose();
}
const dndw = tf
.ones([nextActivations.shape[0], 1])
.concat(nextActivations, 1)
.transpose();
const dadn = activations
.sub(1)
.mul(activations)
.mul(-1);
const dCdw = dndw
.matMul(
dCda.transpose().mul(dadn)
);
const updatedWeights = weights.sub(dCdw.mul(learningRate));
return updatedWeights;
}).reverse();
return updatedWeightsOfLayers;
});
const train = async (iterations = 100) => {
let prevCost = null;
const batchQuantity = Math.floor(trainFeatures.shape[0] / batchSize);
for (let i = 0; i < iterations; i++) {
for (let j = 0; j < batchQuantity; j++) {
const startRowIndex = j * batchSize;
const endRowIndex = batchSize;
const featureSet = trainFeatures.slice(
[startRowIndex, 0],
[endRowIndex, -1]
);
const labelSet = trainLabels.slice(
[startRowIndex, 0],
[endRowIndex, -1]
);
const activationsOfLayers = getActivationsOfLayers(featureSet);
const predictionLabels = activationsOfLayers[activationsOfLayers.length - 1];
const cost = getCost(predictionLabels, labelSet);
console.log('Expected:')
labelSet.print()
console.log('Prediction:')
predictionLabels.print()
weightsOfLayers = gradientDescent(activationsOfLayers, labelSet);
console.log('Cost:')
cost.print();
learningRate = updateLearningRate(prevCost, cost);
prevCost = cost;
tf.dispose([featureSet, labelSet, activationsOfLayers]);
}
}
};
const predict = featureSet => {
featureSet = standardize
? standardizeFeatures(featureSet, mean, variance)
: featureSet;
const activationsOfLayers = getActivationsOfLayers(featureSet);
const predictionLabels = activationsOfLayers[activationsOfLayers.length - 1].softmax();
return predictionLabels;
}
const test = ({ testFeatures, testLabels }) => tf.tidy(() => {
const numberOfObservations = tf.tensor(testLabels.shape[0]);
testLabels = testLabels.argMax(1);
const predictionLabels = predict(testFeatures).argMax(1);
const incorrect = predictionLabels
.notEqual(testLabels)
.sum();
return numberOfObservations.sub(incorrect).div(numberOfObservations);
});
return {
weightsOfLayers,
train,
test
}
}
const train = document.querySelector('#train');
const iterationsInput = document.querySelector('#iterations');
const batchInput = document.querySelector('#batch');
const trainSizeInput = document.querySelector('#trainSize');
const testSizeInput = document.querySelector('#testSize');
const learningRateInput = document.querySelector('#learningRate');
const layersInput = document.querySelector('#layers');
const standardizeInput = document.querySelector('#standardize');
const error = document.querySelector('#error')
let model = null;
train.onclick = () => {
const iterations = Number(iterationsInput.value);
const batchSize = Number(batchInput.value);
const trainSize = Number(trainSizeInput.value);
const testSize = Number(testSizeInput.value);
const learningRate = Number(learningRateInput.value);
const layers = layersInput.value.split(',').map(v => Number(v));
const standardize = standardizeInput.checked;
if (
iterations &&
batchSize &&
trainSize &&
testSize &&
learningRate &&
layers.every(v => v)
) {
error.innerHTML = '';
const { trainFeatures, trainLabels, testFeatures, testLabels } = (() => {
const { training, test } = mnist.set(trainSize, testSize);
const trainFeatures = tf.stack(training.map(({ input }) => tf.tensor(input)));
const trainLabels = tf.stack(training.map(({ output }) => tf.tensor(output)));
const testFeatures = tf.stack(test.map(({ input }) => tf.tensor(input)));
const testLabels = tf.stack(test.map(({ output }) => tf.tensor(output)));
return { trainFeatures, trainLabels, testFeatures, testLabels };
})();
model = NN({
batchSize,
learningRate,
trainFeatures,
trainLabels,
layers,
});
model.train(iterations);
console.log('Accuracy is:')
model.test({ testFeatures, testLabels }).print();
} else {
error.innerHTML = 'One of the properties are incorrect'
}
}
body {
margin: 0;
height: 100vh;
width: 100vw;
}
.inputs {
display: flex;
flex-direction: column;
padding: 10px
}
.buttons {
display: flex;
padding: 10px;
}
.as-console-wrapper {
display: none !important;
}
<body>
<div class='inputs'>
<label>Iterations:</label>
<input type='number' id='iterations' value='100'/>
<label>Batch size:</label>
<input type='number' id='batch' value='100'/>
<label>Train observations:</label>
<input type='number' id='trainSize' value='10000'/>
<label>Test observations:</label>
<input type='number' id='testSize' value='2000'/>
<label>Learning rate:</label>
<input type='number' id='learningRate' value='0.1'/>
<label>Hidden layers (number of neurons, layers separated with comma):</label>
<input type='text' id='layers' value='10, 10'/>
<label>Standardize:</label>
<input type='checkbox' id='standardize'/>
<span id='error'></span>
<div class='buttons'>
<button id='train'>
Start (results will be shown in the console)
</button>
</div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/tensorflow/2.0.1/tf.min.js' async></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/mnist/1.1.0/mnist.js' async></script>
</body>