È questo overfitting o underfitting su una prova XOR?

voti
0

Ho il seguente codice di rete neurale, sto solo cercando di lavorare la mia strada fino a problemi di base, come ad esempio il problema XOR, mentre la costruzione di una base di codice. Si tratta di un progetto hobby.

#include <iostream>
#include <array>
#include <random>
#include <chrono>
#include <iomanip>
#include <fstream>
#include <algorithm>
#include <iomanip>

typedef float DataType;
typedef DataType (*ActivationFuncPtr)(const DataType&);

static DataType learningRate = 0.02;
static std::size_t numberEpochs = 1000000;

DataType sigmoid(const DataType& x)
{
    return DataType(1) / (DataType(1) + std::exp(-x));
}

template<typename T>
class Random
{
public:
    T operator()()
    {
        return m_dis(m_mt);
    }

protected:
    static std::mt19937 m_mt;
    static std::uniform_real_distribution<T> m_dis;
};

template<typename T> std::mt19937 Random<T>::m_mt(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
template<typename T> std::uniform_real_distribution<T> Random<T>::m_dis(0,1);

template<std::size_t NumInputs>
class Neuron
{
public:

    Neuron(ActivationFuncPtr activationFunction)
    :
        m_activationFunction(activationFunction)
    {
        Random<DataType> r;
        std::generate(m_weights.begin(),m_weights.end(),[&]()
        {
            return r();
        });
        m_biasWeight = r();
    }

    void FeedForward(const std::array<DataType,NumInputs>& inputValues)
    {
        DataType sum = m_biasWeight;
        for(std::size_t i = 0; i < inputValues.size(); ++i)
            sum += inputValues[i] * m_weights[i];
        m_output = m_activationFunction(sum);

        m_netInput = sum;
    }

    DataType GetOutput() const
    {
        return m_output;
    }

    DataType GetNetInput() const
    {
        return m_netInput;
    }

    std::array<DataType,NumInputs> Backpropagate(const DataType& error,
                           const std::array<DataType,NumInputs>& inputValues,
                           std::array<DataType,NumInputs+1>& weightAdjustments)
    {
        DataType errorOverOutput = error;
        DataType outputOverNetInput = m_output * (DataType(1) - m_output); // sigmoid derivative

        std::array<DataType,NumInputs> netInputOverWeight;
        for(std::size_t i = 0; i < NumInputs; ++i)
        {
            netInputOverWeight[i] = inputValues[i];
        }

        DataType netInputOverBias = DataType(1);

        std::array<DataType,NumInputs> errorOverWeight;
        for(std::size_t i = 0; i < NumInputs; ++i)
        {
            errorOverWeight[i] = errorOverOutput * outputOverNetInput * netInputOverWeight[i];
        }

        DataType errorOverBias = errorOverOutput * outputOverNetInput * netInputOverBias;

        for(std::size_t i = 0; i < NumInputs; ++i)
        {
            weightAdjustments[i] = errorOverWeight[i];
        }
        weightAdjustments[NumInputs] = errorOverBias;

        DataType errorOverNetInput = errorOverOutput * outputOverNetInput;

        std::array<DataType,NumInputs> errorWeights;
        for(std::size_t i = 0; i < NumInputs; ++i)
        {
            errorWeights[i] = errorOverNetInput * m_weights[i];
        }

        return errorWeights;
    }

    void AdjustWeights(const std::array<DataType,NumInputs+1>& adjustments)
    {
        for(std::size_t i = 0; i < NumInputs; ++i)
            m_weights[i] = m_weights[i] - learningRate * adjustments[i];
        m_biasWeight = m_biasWeight - learningRate * adjustments[NumInputs];
    }

    const std::array<DataType,NumInputs> GetWeights() const {return m_weights;}
    const DataType& GetBiasWeight() const { return m_biasWeight; }

protected:
    std::array<DataType,NumInputs> m_weights;
    DataType m_biasWeight;

    ActivationFuncPtr m_activationFunction;

    DataType m_output;
    DataType m_netInput;
};

main()
{

    std::array<std::array<DataType,2>,4> inputData = {{{0,0},{0,1},{1,0},{1,1}}};
    std::array<std::array<DataType,1>,4> desiredOutputs = {{{0},{1},{1},{0}}};
    std::array<Neuron<2>*,2> hiddenLayer1 = {{ new Neuron<2>(sigmoid), new Neuron<2>(sigmoid) }};
    std::array<Neuron<2>*,1> outputLayer = {{ new Neuron<2>(sigmoid) }};

    std::cout << std::fixed << std::setprecision(80);

    DataType minError = std::numeric_limits<DataType>::max();
    bool minErrorFound = false;

    std::size_t epochNumber = 0;
    while(epochNumber < numberEpochs && !minErrorFound)
    {
        DataType epochMSE = 0;

        for(std::size_t row = 0; row < inputData.size(); ++row)
        {
            const std::array<DataType,2>& dataRow = inputData[row];
            const std::array<DataType,1>& outputRow = desiredOutputs[row];

            // Feed the values through to the output layer

            hiddenLayer1[0]->FeedForward(dataRow);
            hiddenLayer1[1]->FeedForward(dataRow);

            DataType output0 = hiddenLayer1[0]->GetOutput();
            DataType output1 = hiddenLayer1[1]->GetOutput();

            outputLayer[0]->FeedForward({output0,output1});

            DataType finalOutput0 = outputLayer[0]->GetOutput();

            // if there was more than 1 output neuron these errors need to be summed together first to create total error
            DataType totalError = 0.5 * std::pow(outputRow[0] - finalOutput0,2.f);
            epochMSE += totalError * totalError;

            DataType propagateError = -(outputRow[0] - finalOutput0);

            std::array<DataType,3> weightAdjustmentsOutput;
            std::array<DataType,2> outputError = outputLayer[0]->Backpropagate(propagateError,
                                                                   {output0,output1},
                                                                   weightAdjustmentsOutput);

            std::array<DataType,3> weightAdjustmentsHidden1;
            hiddenLayer1[0]->Backpropagate(outputError[0],dataRow,weightAdjustmentsHidden1);

            std::array<DataType,3> weightAdjustmentsHidden2;
            hiddenLayer1[1]->Backpropagate(outputError[1],dataRow,weightAdjustmentsHidden2);

            outputLayer[0]->AdjustWeights(weightAdjustmentsOutput);
            hiddenLayer1[0]->AdjustWeights(weightAdjustmentsHidden1);
            hiddenLayer1[1]->AdjustWeights(weightAdjustmentsHidden2);
        }

        epochMSE *= DataType(1) / inputData.size();

        if(epochMSE >= minError + 0.00000001)
        {
            minErrorFound = true;
        }
        else
            minError = epochMSE;

        ++epochNumber;
    }

    std::cout << std::fixed << std::setprecision(80)
                << \n\n====================================\n
                <<    TRAINING COMPLETE
                << \n\n==================================== << std::endl;
    std::cout << Minimum error:  << minError << std::endl;
    std::cout << Number epochs:  << epochNumber << / << numberEpochs << std::endl;

    // output tests
    std::cout << std::fixed << std::setprecision(2)
                << \n\n====================================\n
                <<    FINAL TESTS
                << \n\n==================================== << std::endl;

    for(std::size_t row = 0; row < inputData.size(); ++row)
    {
        const std::array<DataType,2>& dataRow = inputData[row];
        const std::array<DataType,1>& outputRow = desiredOutputs[row];
        std::cout << dataRow[0] << , << dataRow[1] <<  ( << outputRow[0] << )  :  ;

        // Feed the values through to the output layer

        hiddenLayer1[0]->FeedForward(dataRow);
        hiddenLayer1[1]->FeedForward(dataRow);

        DataType output0 = hiddenLayer1[0]->GetOutput();
        DataType output1 = hiddenLayer1[1]->GetOutput();

        outputLayer[0]->FeedForward({output0,output1});

        DataType finalOutput0 = outputLayer[0]->GetOutput();

        std::cout << finalOutput0 << std::endl;
    }

    return 0;
}

La maggior parte del tempo, l'uscita assomiglia a questo, e penso grande! Successo!

====================================
   TRAINING COMPLETE

====================================
Minimum error: 0.00000000106923325748908837340422905981540679931640625000000000000000000000000000
Number epochs: 1000000/1000000


====================================
   FINAL TESTS

====================================
0.00,0.00 (0.00)  :  0.01
0.00,1.00 (1.00)  :  0.99
1.00,0.00 (1.00)  :  0.99
1.00,1.00 (0.00)  :  0.01

Process returned 0 (0x0)   execution time : 0.992 s
Press any key to continue.

Ma poi il seguente è l'output occasionalmente, che voglio capire, è questa sovra o underfitting, o ho fatto qualcosa di sbagliato da qualche parte? Come posso evitare questo?

====================================
   TRAINING COMPLETE

====================================
Minimum error: 0.00787912402302026748657226562500000000000000000000000000000000000000000000000000
Number epochs: 1000000/1000000


====================================
   FINAL TESTS

====================================
0.00,0.00 (0.00)  :  0.01
0.00,1.00 (1.00)  :  0.50
1.00,0.00 (1.00)  :  0.99
1.00,1.00 (0.00)  :  0.50

Process returned 0 (0x0)   execution time : 1.024 s
Press any key to continue.
È pubblicato 10/10/2019 alle 00:54
fonte dall'utente
In altre lingue...                            


1 risposte

voti
0

Hai fatto nulla di male. Si noti che si ottengono risultati diversi, anche dopo la formazione della rete con la stessa quantità di epoche e dati di allenamento. Overfitting sarebbe la causa, se si sarebbe usato più epoche e / o dati di allenamento nella rete che funziona male. Underfitting è l'opposto di quello. Il problema è che una rete neurale multistrato allenato con la regola di apprendimento backpropagation incontra un minimo locale diversa da quella minimi globale, riducendo al minimo la funzione di errore di formazione primaria. È per questo che si ottengono risultati diversi dopo diverse sessioni di allenamento, perché a volte si colpisce i minimi globale e funziona bene, ma quando si preme il minimi locali che non funziona come previsto dal momento che v'è una notevole errore in uscita. Non si dispone underfitting, e non si dispone di overfitting. Si potrebbe provare a ridurre il tasso di apprendimento da parte di un ordine di grandezza o di ar almeno per funzione metà o cambiamenti di formazione. E 'importante per voi sapere che le reti neurali è un processo molto empirico, se la rete addestrata passa la convalida, allora è ok, se non poi modificarlo un po' e riqualificare o semplicemente riqualificare. Non v'è alcuna forma chiusa formula, soluzione o ricetta per il loro design.

Risposto il 10/10/2019 a 01:40
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more