• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[code review] C#. Sztuczne sieci neuronowe - wielowarstwowy perceptron.

Object Storage Arubacloud
0 głosów
2,120 wizyt
pytanie zadane 14 kwietnia 2017 w C# przez BlueSky Początkujący (330 p.)

Witam wszystkich. Samodzielnie napisałem kod realizujący wielowarstwowy perceptron przy okazji realizacji zadania na uczelnię. Chciałem prosić o ocenę jakości tego kodu i bardzo liczę na informację zwrotną. Szczególnie chciałbym abyście zwrócili uwagę czy prawidłowo stosuje zasady SOLID . Kod jest wrzucony na mój GitHub. Biblioteka klas znajduje się pod tym linkiem, zaś przykładowy projekt konsolowy znajduje się pod tym linkiem. W samej bibliotece wyszło mi 19 klas więc nie chce wszystkiego wrzucić tutaj bo zrobiłby się okropny bałagan.

Tutaj wrzucę tylko dwie klasy, które moim zdaniem szczególnie wzbudzają moje podejrzenia ale gorąco liczę, że ktoś przejrzy cały projekt na GitHubie i znajdzie dodatkowe fragmenty kodu warte uwagi : )


Pierwsza klasa to HiddenNeuron. Mam podejrzenia odnośnie złamania zasady Single responsibility principle ponieważ przy propagowaniu sygnałów w przód sieci HiddenNeuron doskonale sobie radził z mniejsza ilością pól ale po stworzeniu klasy BackPropagation musiałem dodać w klasie HiddenNeuorn takie pola jak previousDeltaWeights, inputs, reply, sum, error oraz odpowiednio ją zmodyfikować. Postąpiłem w ten sposób aby sobie uprościć sprawę i powiem szczerze nie miałem pomysłu gdzie przechowywać te pola poza klasą HiddenNeuron. Moje pytanie czy moje podejrzenia odnośnie złamania SRP są prawdziwe oraz jak rozwiązać ten problem?



namespace MLPLib
{
    public class HiddenNeuron
    {
        private IActivationFunction activationFunction;
        private IFillNumber fillNumber;
        private List<double> weights;
        private List<double> previousDeltaWeights;
        private List<double> inputs;
        private double reply;
        private double sum;
        private double error;

        public HiddenNeuron(IActivationFunction activationFunction, IFillNumber fillNumber, IBias bias, int numberOfInputs)
        {
            this.activationFunction = activationFunction;
            this.fillNumber = fillNumber;
            this.weights = new List<double>();
            this.previousDeltaWeights = new List<double>();
            this.inputs = new List<double>();
            
            for (int i = 0; i < numberOfInputs; i++)
                weights.Add(fillNumber.generateNumber());

            if (bias.isBias())
                weights.Add(fillNumber.generateNumber());

            
            for (int i = 0; i < weights.Count; i++)
                previousDeltaWeights.Add(0.0);
        }
        public double response(List<double> inputs)
        {
            this.inputs = inputs;
            this.sum = 0.0;

            for (int i = 0; i < weights.Count; i++)
                this.sum += weights[i] * inputs[i];

            reply = activationFunction.activationFunction(this.sum);
            return reply;
        }
        public double getInput(int index)
        {
            return inputs[index];
        }
        public double getWeight(int index)
        {
            return weights[index];
        }
        public void setWeight(int index, double value)
        {
            weights[index] = value;
        }
        public double getPreviousDeltaWeight(int index)
        {
            return previousDeltaWeights[index];
        }
        public void setPreviousDeltaWeight(int index, double value)
        {
            previousDeltaWeights[index] = value;
        }
        public IActivationFunction ActivationFunction
        {
            get { return activationFunction; }
        }
        public List<double> Weights
        {
            get { return weights; }
        }
        public double Reply
        {
            get { return reply; }
        }
        public double Sum
        {
            get { return sum; }
        }
        public double Error
        {
            get { return error; }
            set { this.error = value; }
        }
    }
}



using namespace MLPLib
{
    using TupleList = Tuple<List<double>, List<double>>;

    public class BackPropagation : ILearningAlgorithm
    {
        private MLP mlp;
        private List<TupleList> teachingSet;    //List of Tuple<List<inputs>, List<outputs>>
        private double learningRate;
        private double momentum;
        private double stopError;
        private int epochCounter;
        private double meanSquaredError;
        private RandomPaterns randomPaterns;

        public BackPropagation(List<TupleList> teachingSet, double learningRate, double momentum, double stopError)
        {
            this.teachingSet = teachingSet;
            this.learningRate = learningRate;
            this.momentum = momentum;
            this.stopError = stopError;
            this.randomPaterns = new RandomPaterns();
        }
        public void learning(MLP mlp)
        {
            this.mlp = mlp;

            do
            {
                learningEpoch();
                epochCounter++;

            } while (meanSquaredError > stopError);
        }
        private void learningEpoch()
        {
            meanSquaredError = 0.0;
            List<TupleList> patterns = randomPaterns.randomPaterns(teachingSet);
            foreach (var p in patterns)
            {
                mlp.response(p.Item1);
                learnSinglePattern(p.Item2);
            }

            meanSquaredError /= patterns.Count;
        }
        private void learnSinglePattern(List<double> target)
        {
            calculateErrorsLastLayer(target);
            if (mlp.HiddenLayers.Count > 1)
                calculateErrorsHiddenLayers();
            changeWeights();
            CalculateMeanSquaredError();
        }
        private void calculateErrorsLastLayer(List<double> target)
        {
            HiddenLayer outputLayer = mlp.HiddenLayers.Last();
            for (int i = 0; i < outputLayer.Neurons.Count; i++)
                calculateErrorLastLayerNeuron(outputLayer.Neurons[i], target[i]);
        }
        private void calculateErrorLastLayerNeuron(HiddenNeuron neuron, double target)
        {
            IActivationFunction function = neuron.ActivationFunction;
            neuron.Error = (target - neuron.Reply) * function.derivative(neuron.Sum);
        }
        private void calculateErrorsHiddenLayers()
        {
            for (int i = mlp.HiddenLayers.Count - 2; i >= 0; i--)
            {
                HiddenLayer hiddenLayer = mlp.HiddenLayers[i];
                HiddenLayer upperLayer = mlp.HiddenLayers[i + 1];
                List<HiddenNeuron> neurons = hiddenLayer.Neurons;
                List<double> errors = upperLayer.Errors;

                for (int j = 0; j < neurons.Count; j++)
                {
                    List<double> weights = upperLayer.WeightsByIndex(j);
                    calculateErrorHiddenLayerNeuron(neurons[j], errors, weights);
                }
            }
        }
        private void calculateErrorHiddenLayerNeuron(HiddenNeuron neuron, List<double> errors, List<double> weights)
        {
            double sumOfErrorsAndWeights = 0.0;
            for (int i = 0; i < errors.Count; i++)
                sumOfErrorsAndWeights += errors[i] * weights[i];

            IActivationFunction function = neuron.ActivationFunction;

            neuron.Error = sumOfErrorsAndWeights * function.derivative(neuron.Sum);
        }
        private void changeWeights()
        {
            foreach (var l in mlp.HiddenLayers)
                foreach (var n in l.Neurons)
                {
                    for (int i = 0; i < n.Weights.Count; i++)
                    {
                        double previousWeight = n.getWeight(i);
                        double deltaWeight = learningRate * n.Error *  n.getInput(i) + momentum * n.getPreviousDeltaWeight(i);
                        n.setPreviousDeltaWeight(i, deltaWeight);
                        n.setWeight(i, previousWeight + deltaWeight);
                    }
                }
        }
        private void CalculateMeanSquaredError()
        {
            foreach (var n in mlp.HiddenLayers.Last().Neurons)
                meanSquaredError += n.Error * n.Error;
        }
        public int getEpochCounter()
        {
            return epochCounter;
        }

    }
}

Jeszcze krótki opis co robi przykładowa aplikacja konsolowa. Zbudowana jest sieć o 3 warstwach. Warstwa wejściowa z 4 neuronami(sieć przyjmuje 4 wejścia), warstwa ukryta z 4 neuronami, oraz warstwa wyjściowa z 4 neuronami(którą w moim programie reprezentuje również klasa HiddenLayer). Każda warstwa ukryta lub wyjściowa może być ustawiona na inną funkcje aktywacji. U mnie dla warstwy ukrytej jest to funkcja sigmoidalna a dla warstwy wyjściowej funkcja identycznościowa. Sieć uczona jest algorytmem wstecznej propagacji błędów.

Zadanie jest bardzo proste. Należy odtworzyć na wyjściu sieci sygnały podawane na wejściach. W ciągu uczącym pojedyncze wzorce zajmują po dwie linijki.W pierwszej linijce sygnały wejściowe wzorca, oraz w następnej sygnały wyjściowe wzorca. W tym przypadku podanych jest 4 wzorców.

Nie chce tutaj opisywać jak działa wielowarstwowy perceptron oraz algorytm wstecznej propagacji, dla osób które tego nie wiedzą bo wyszedłby tutaj elaborat. Zakładam, że nikomu nie chciałoby się tego douczać bo to temat rzeka. Uznałem, że może mi tutaj pomóc ktoś kto wie coś na temat sieci neuronowych...

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
1 odpowiedź 359 wizyt
0 głosów
1 odpowiedź 807 wizyt
0 głosów
0 odpowiedzi 246 wizyt
pytanie zadane 9 marca 2022 w Inne języki przez Kobzdziej Nowicjusz (120 p.)

92,572 zapytań

141,422 odpowiedzi

319,643 komentarzy

61,959 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...