вопрос реализации алгоритма обратного алгоритма нейронной сети - PullRequest
1 голос
/ 20 февраля 2020

Я писал нейронную сеть с нуля, чтобы учиться. но так как я все еще учусь - я хочу убедиться, что то, что я пишу, на самом деле правильно. У меня есть массив массивов (матрица), объектов ячеек. прикрепленный к объекту «мозг», который имеет следующие два метода:

train: function(data)
    {
    for (let b=0; b< data.length; b++)// for the length of the training data - I.E. we are going assume we are getting many relatively shortly indexed arrays
        {
        if(data[b].answers.length != data[b].questions.length)
            {
            console.log("bad data"); 
            return false;
        }   
        for(let c=0;c<data[b].questions.length;c++)
            {
            brain.attachInputLayer(data[b].questions[c]);
            brain.updateForward();
            let direction = brain.determinDirection(data[b].answers[c]); //return an array of updateObject with determined weights bias value adjustments, which each cell gets updated order should be from generation by column;
            brain.cellMatrix.forEach(cellArray=> cellArray.forEach(cell=> cell.adjust(direction.find(x=> x.ID ===cell.ID)))); 
            brain.updateForward();
            brain.displayBrain();
        }   
    }
    console.log("all training data done");
    alert("win?");
    console.log(brain.cellMatrix);
    console.log("brain");
}

and


determinDirection:function(answer)
    {
    // answer is the array of values of each answer cell we want as a result
    let arrayOfUpDateObjectsForCell = [];
    for(let e=0; e<answer.length; e++)
        {
        let answerCell = brain.cellMatrix[cellMatrix.length-1][e];
        let returnBucket = [];
        arrayOfUpDateObjectsForCell.push(answerCell.whatIwant(answer[e], returnBucket)); 
    }
    let list = Flat(arrayOfUpDateObjectsForCell);
    let realList = Clean(list); 
    return realList;
}

, поэтому каждая ячейка последнего поколения (вывод ответа) вызывает метод whatIwant в brain.train (), эта функция распространяется в обратном направлении по сети ... но мой вопрос на самом деле заключается в следующем :::

::: похоже, что я вычисляю ошибку / направление для правильного перемещения каждого значения? правильно ли усреднять изменения между дублированными объектами updateObjects? (требуемыйObjectchange для cell.gen = 3, order = 0 создается из каждой ячейки следующего слоя, вызывающей whatIwant. Изменения cell.gen = 4, order = 0 хочет cell.gen = 3, order = 0 для усреднения с изменениями cell.gen = 4, order = 1 хочет для cell.gen = 3, order = 0). усредняет правильную операцию здесь?

:::

whatIwant:function(answerValue, returnArray)
    {   
    let desiredWeights = this.weights;
    let desiredBias = this.bias;
    let desiredActivations = this.activations;
    let error = (1/2)*Math.pow(cell.value-answerValue,2);
    let desiredObjectChange = 
        {
        ID:this.ID,
        weights:this.weights,
        bias:this.bias,
        activations:this.activations,
        value:answerValue,
        combine:function(yourCloneFriend)
            {
            if(yourCloneFriend == false)
                {
                return;
            }
            this.bias = (1/2)*(this.bias+yourCloneFriend.bias); 
            let cWeight = yourCloneFriend.weights[Symbol.iterator]();
            let cActivations = yourCloneFriend.activations[Symbol.iterator]();
            this.weights.forEach(x=> (1/2)*(x+cWeight.next().value));
            this.activations.forEach(y=> (1/2)*(y+cActivations.next().value));
            this.recalculateValue();
            return this;
        },
        recalculateValue:function() 
            {
            this.value = Sigmoid(arrayMultiply(this.weights, this.activations)+this.bias);
        }   
    }
    for(let k = 0; k< this.weights.length; k++)
        {
        let lastValue = Sigmoid(arrayMultiply(desiredWeights, desiredActivations)+desiredBias);
        let lastError = (1/2)*Math.pow(lastValue-answerValue,2);
        for(let l=0;l<3;l++)
            {
            let currentValue = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
            let currentError = (1/2)*Math.pow(currentValue-answerValue,2);
            let positiveRange = false;
            if(desiredWeights[k] < 0){ positiveRange = true;}   
            let nudgedWeightArray = NudgeArray(desiredWeights, k, l, positiveRange);    //returns a copy array to test, with weight adjusted.
            let testWeightChange = Sigmoid(arrayMultiply(nudgedWeightArray,desiredActivations)+desiredBias);
            let testWeightError = (1/nudgedWeightArray.length)*Math.pow(testWeightChange - answerValue, 2);
            let testWeightResult = compareSmallnumbers('isSmaller', currentError, testWeightError);
            if(testWeightResult);
                {
                desiredWeights = nudgedWeightArray;
                currentError = testWeightError;
            }
            positiveRange=false;
            if(desiredBias < 0){positiveRange = true;}  
            let nudgedBiasVal = this.nudge(desiredBias,l,positiveRange);
            let testBiasChange = Sigmoid(nudgedBiasVal+desiredWeights[k]*desiredActivations[k]);
            let testBiasError = (1/1)*Math.pow(testBiasChange - answerValue, 2);
            let testBiastResult = ('isSmaller', currentError, testBiasError);
            if(testBiastResult);
                {
                desiredBias = nudgedBiasVal;
                currentError = testBiasError;
            }
            positiveRange=!!Math.random(0,1)>5;
            let nudgedAcitivationArray = NudgeArray(desiredActivations,k,l,positiveRange);
            let testActivationChange = Sigmoid(arrayMultiply(nudgedAcitivationArray,desiredWeights)+desiredBias);
            let testActivationError = (1/nudgedAcitivationArray.length)*Math.pow(testActivationChange - answerValue, 2);
            let testActivationResult = compareSmallnumbers('isSmaller', currentError, testActivationError);
            if(testActivationResult);
                {
                desiredActivations[k] = nudgedAcitivationArray[k];  
                currentError = testActivationError;
            }
            //and the end of the loop, update the error to the new value
            let errorResult = compareSmallnumbers('isSmaller',lastError, currentError);
            if(errorResult)
                {
                lastError = currentError;
            }
        }
        desiredObjectChange.weights[k] = desiredWeights[k];
        desiredObjectChange.bias = desiredBias;
        desiredObjectChange.activations[k] = desiredActivations[k];
        desiredObjectChange.value = Sigmoid(arrayMultiply(desiredObjectChange.weights, desiredObjectChange.activations)+desiredObjectChange.bias);
    }
    let combineObject = returnArray.find(x=>x.ID === desiredObjectChange.ID);
    if(!combineObject)
        {
        returnArray.push(desiredObjectChange);
    }       
    //that was this object - simple stuff, now we need to call this function
    if(Array.isArray(cell.lastGenerationTargetKeys) && cell.lastGenerationTargetKeys.length)
        {               
        let nextActivation = desiredObjectChange.activations[Symbol.iterator]();
        brain.cellMatrix[cell.generation-1].forEach(x=> x.whatIwant(nextActivation.next().value, returnArray));
        return returnArray;
    }
    else  
        {
        return;     
    }
},


clean, flat и NudgeArray - это: *

function Clean(array)
    {
    let rArray = [];    
    array.forEach((x)=>
        {
        let search = rArray.find(y=>y.ID ===x.ID);  
        if(search === undefined)
            {
            rArray.push(x);
        }
        else
            {
            rArray[rArray.indexOf(search)].combine(x);
        }
    });
    return rArray;

}
function Flat(array)
    {
    let holdBucket = [];
    let flatten = function(array)
        {
        for(let i = 0; i<array.length;i++)
            {
            if(Array.isArray(array[i]))
                {
                flatten(array[i]);
            }
            else
                {
                holdBucket.push(array[i]);
            }
        }
    }
    flatten(array);
    return holdBucket;

}
function NudgeArray(array ,arrayIndex, Nudgeindex, isPositive)
    {//nudge index is designed to act like a variable learning rate modifier, as it tests, jumps decrease in size
    let returnArray = [];
    array.forEach(x=>returnArray.push(x));  
    let value = returnArray[arrayIndex];
    if(isPositive)
        {
        value+=(Math.random(0,1)/(Nudgeindex+3));
        value = Sigmoid(value);
    }
    else
        {   
        value+=(Math.random(-1,1)/(Nudgeindex+3));
        value = Sigmoid(value);
    }
    returnArray.splice(arrayIndex,1,value);
    return returnArray;
}
...