FANN: утечка памяти при обучении ANN с использованием данных, прочитанных из нескольких файлов - PullRequest
0 голосов
/ 04 марта 2019

У меня есть следующий цикл:

for (int i = 1; i <= epochs; ++i) {
    for (std::vector<std::filesystem::path>::iterator it = batchFiles.begin(); it != batchFiles.end(); ++it) {
        struct fann_train_data *data = fann_read_train_from_file(it->string().c_str());
        fann_shuffle_train_data(data);
        float error = fann_train_epoch(ann, data);
    }
}

ann - это сеть.
batchFiles - это std::vector<std::filesystem::path>.

Этот код повторяет все обучениефайлы данных в папке и использует его для обучения ANN каждый раз, столько раз, сколько определено переменной epochs.

Следующая строка вызывает утечку памяти:

struct fann_train_data *data = fann_read_train_from_file(it->string().c_str());

Проблема в том, что я должен постоянно переключаться между учебными файлами, так как у меня недостаточно памяти, чтобы загрузить их все сразу, иначе я бы загрузил тренировочные данные только один раз.

Почему это происходит?Как я могу решить эту проблему?

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

Поскольку требуется вызвать fann_destroy_train_data, вы можете использовать C ++ и RAII, используя следующую оболочку:

struct fann_wrapper
{
   fann_train_data *td;
   fann_wrapper(fann_train_data* p) : td(p) {}
   ~fann_wrapper() { fann_destroy_train_data(td); }
};
//...
for (int i = 1; i <= epochs; ++i) {
    for (std::vector<std::filesystem::path>::iterator it = batchFiles.begin(); it != batchFiles.end(); ++it) {
        struct fann_train_data *data = fann_read_train_from_file(it->string().c_str());

        // the next line ensures that fann_destroy_train_data is called
        fann_wrapper fw(data);

        fann_shuffle_train_data(data);
        float error = fann_train_epoch(ann, data);
    }  // when this curly brace is encountered, the fann_destroy_train_data is always called
}  

fann_wrapper просто содержит указатель fain_train_data, и при уничтоженииfann_wrapper, fann_train_data уничтожено.

Причина, по которой этот метод намного безопаснее, чем необработанный метод C, заключается в том, что может быть выдано возможное исключение (по любой причине).Если выдается исключение, то fann_train_data будет всегда уничтожаться при использовании fann_wrapper.Эту гарантию нельзя сделать с помощью метода C, поскольку исключение будет полностью пропущено по любой строке, которая имеет fann_destroy_train_data.

Пример:

for (int i = 1; i <= epochs; ++i) {
    for (std::vector<std::filesystem::path>::iterator it = batchFiles.begin(); it != batchFiles.end(); ++it) {
        struct fann_train_data *data = fann_read_train_from_file(it->string().c_str());
        fann_shuffle_train_data(data);
        float error = fann_train_epoch(ann, data);

        fann_destroy_train_data(data); // this line is not executed if an exception is thrown above, thus a memory leak
    }
}  

Именно поэтому RAII являетсяважная концепция в C ++.Ресурсы будут очищаться автоматически, независимо от причины, по которой завершается исполняемый блок кода (генерируется исключение, выполняется return и т. Д.).

0 голосов
/ 04 марта 2019

В C ++ память автоматически освобождается, когда объект, управляющий ею, выходит из области видимости.(При условии, что класс был написан правильно.) Это называется RAII .

Но FANN представляет API C, а не C ++ API.В C вам нужно вручную освободить память, когда вы закончите с ней.Более того, когда библиотека C создает объект для вас, обычно требуется, чтобы вы сказали ему, когда вы закончите с объектом.У библиотеки нет хорошего способа самостоятельно определить, когда ресурсы объекта должны быть освобождены.

Соглашение состоит в том, что всякий раз, когда API C предоставляет вам функцию, подобную struct foo* create_foo(), вы должны искатьдля соответствующей функции, такой как void free_foo(struct foo* f).Это симметрично.

В вашем случае, как первоначально отмечал PaulMcKenzie, вам нужно void fann_destroy_train_data(struct fann_train_data * train_data).Из документации , выделено мое:

Уничтожает тренировочные данные и должным образом освобождает все связанные данные. Обязательно вызывайте эту функцию после завершения использования данных тренировки.

...