2017-01-02 3 views
0

Я внедряю искусственную нейронную сеть в C++ для моего курса машинного обучения. Вот мой код «Нейрон» -Обработка освобождения памяти между классами в C++

struct Neuron 
{ 
    float *inputs;   // pointer to inputs array 
    int inputCount;   // number of input 
    float *weights; 
    float sum;    // linear weight sum of inputs 
    float output = 0.0;  // output from activation function 

    float bias = 1.0; 

    float delta;   // error gradient 


    Neuron(){} 

    Neuron(int inputCount) 
    { 
     this->inputCount = inputCount; 
     this->weights = new float [inputCount]; 

     InitializeWeights(); 
    } 


    float ActivationFunction(float x) 
    { 
     return 1.0/(1.0 + exp(-x)); 
    } 

    void Calculate(float *inputs) 
    { 
     this->inputs = inputs; 

     float temp = 0.0; 

     for(int i=0; i<inputCount; i++) 
     { 
      temp += weights[i] * inputs[i]; 
     } 

     temp += bias; 
     sum = temp; 
     output = ActivationFunction(sum); 
    } 

    void InitializeWeights() 
    { 
     for(int i=0; i<inputCount; i++) 
     { 
      weights[i] = rand() % 101/100; 
     } 
    } 

    ~Neuron() 
    { 
     delete[] weights; 
    } 

}; 

Я также другая структура под названием «Слой», который представляет собой слой. Нейроны инициализируются там -

for(int i=0; i<neuronCount; i++) 
{ 
    neurons[i] = Neuron(inputCount); 
} 

, где «neuronCount» является количество нейронов в слое. Проблема в том, что весовой массив в нейронах немедленно освобождается из-за вызова деструктора. Что я должен сделать, чтобы предотвратить это? Что еще более важно, есть ли лучший способ разработать мою программу?

+0

Не могли бы вы предоставить источник класса «Слой»? – pSoLT

+1

Сделайте себе одолжение и используйте 'std :: vector ' вместо 'float *' и 'new []/delete []'. Все ваши проблемы с этим кодом будут решены, если вы это сделаете. В противном случае вам нужно реализовать [правило 3] (http://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) – PaulMcKenzie

+0

Выходит ли из сферы действия? почему деструкторы называются? И можете ли вы попробовать распределить на основе кучи? но не забудьте удалить их. –

ответ

1

В коде есть несколько ошибок.

Во-первых, вы не смогли инициализировать указатели input и weights в конструкторе по умолчанию. Таким образом, следующая простая программа может вызвать проблему:

int main() 
{ 
    Neuron n; 
} 

Поскольку деструктор n будет пытаться вызвать delete [] на неинициализированном указателе weights.

Другая проблема заключается в том, что ваш класс Neuron не может быть надежно скопирован или назначен. Следующая программа также покажут проблемы с утечкой памяти и двойными ошибками делеции:

int main() 
{ 
    Neuron n(10); 
    Neuron n2 = n; // copy constructor 
    Neuron n3(2); 
    n = n3;   // assignment 
} // memory leaks, double deletion issues once main() exits. 

третьим вопросом в том, что вы передаете указатель на float в вашей Calculate функции, и вы понятия не имеете, если указатель действителен внутри этой функции, или даже если это действительно так, количество элементов, отображаемых указателем inputs. Если число меньше, чем число weights, ваш код имеет переполнение памяти, пытающееся получить доступ к inputs[i], как только i выходит за пределы input.

Чтобы устранить эти проблемы, проще всего сделать, это использовать std::vector:

#include <vector> 
#include <algorithm> 
#include <cmath> 
#include <cstdlib> 

struct Neuron 
{ 
    std::vector<float> inputs;   // pointer to inputs array 
    std::vector<float> weights; 
    float sum = 0;    // linear weight sum of inputs 
    float output = 0.0;  // output from activation function 
    float bias = 1.0; 
    float delta = 0;   // error gradient 

    Neuron() {} 
    Neuron(int inputCount) : weights(inputCount) 
    { 
     InitializeWeights(); 
    } 

    float ActivationFunction(float x) 
    { 
     return 1.0/(1.0 + exp(-x)); 
    } 

    void Calculate(const std::vector<float>& inputs_) 
    { 
     inputs = inputs_; 

     // make sure vectors are the same size. If inputs is smaller, 
     // the new elements added will be 0 
     inputs.resize(weights.size()); 

     // use inner_product to get the sum of the products. 
     sum = std::inner_product(std::begin(weights), 
           std::end(weights), 
           std::begin(inputs), 0.0f) + bias; 

     output = ActivationFunction(sum); 
    } 

    void InitializeWeights() 
    { 
     std::generate(std::begin(weights), std::end(weights), rand() % 101/100); 
    } 
}; 

Примечание мы больше не использовать указатели, как std::vector заботится управления памятью. Нам также не нужно иметь переменные-члены, которые отслеживают счет (например, inputCount), поскольку вектор знает свой собственный размер, используя vector::size().

Кроме того, std::inner_product используются для генерации суммы в функции Calculate (после изменения размера input вектора такого же размера, как weights). inputs_ передается Calculate как std::vector, таким образом, вы можете вызвать его с помощью фигурных скобок инициализированного списка:

someInstance.Calculate({9.8, 5.6, 4.5}); // calls Calculate with a vector consisting of 3 items. 

Кроме того, функция InitializeWeights вызывает функцию std::generate алгоритма для установки weights вектора.


Если вы не использовать std::vector, то ваш класс будет требовать конструктор копирования и оператор присваивания, следуя Rule of 3.Я не буду обсуждать, как вы можете исправить свой класс, не используя vector, поскольку ответ будет более активным.


Последний пункт является то, что использование rand() в C++ следует заменить с использованием в C++11 random number facilities.

+0

Большое спасибо за ваш отличный и подробный ответ. Большое спасибо за ваши усилия по исправлению моих ошибок. – Seeker

Смежные вопросы