Comments 27
-Неуместное использование макросов вместо constexpr-значания и constexpr-функции.Коротко: макросы хуже из-за того, что компилятор втыкает их в текст чисто механически, в отличие от constexpr, где он будет знать тип выражения и сможет ругаться в случае мест, подозрительных на ошибку и потерю точности.
-malloc и new в одной куче. Так на С++ писать нельзя — обязательно перепутаете пары malloc/free new/delete и засадите в программу неопределенное поведение, которое будет сложно отладить.
-отчасти, прошлый пункт компенсируется тем, что у вас деструкторов и очистки ресурсов просто нет. Да, программа при выходе все сама почистит, однако где гарантия, что ваш код не скопипастят в другую программу? Раз материал с намеком на обучение, не учите сразу плохому, мне после ваших обучалок, условно, джунов на работу брать.
-Использование сырых указателей — в ту же кучу камней — облом писать очистку руками? Заставь компилятор все почистить, благо это просто — RAII + смартпоинтеры называется.
-Куча магических констант по коду. Уж лучше юзать макросы, чем так поступать.
-Хранение индекса массива в переменной типа int — да, болваны-преподаватели любят так писать (благо слово всего из трех букв выходит), и в книжках тоже так пишут. Но это закладывает в программу страшную проблему под названием «несовместима с 64 битными архитектурами при обработке больших массивов». Просто юзайте size_t.
А по делу — вопрос автору статьи: где вы брали данные и как подключали к программе? Хотелось бы самому такое пощупать, но, насколько я знаю база данных NNIST имеет размеры 28х28, которые в свою очередь были преобразованы из матрицы 20х20.
Кстати, только сейчас заметил, что данные у вас генерируются, теперь разобрался.
matrix = (float**) malloc((in+1)*sizeof(float));
нужно указывать не sizeof(float), а sizeof(float*)
Возможно вы тестировали на 32 разрядной системе, там размер указателя совпадает с размером float и равен 4 байта.
На 64 разрядной системе размер указателя 8 байт, со всеми вытекающими последствиями — выделяется недостаточно памяти для массива указателей matrix.
Огромное спасибо, да действительно вы правы. На неделе подправлю, заодно в порядок приведу по советам gbg. В планах уйти от qt и перевести весь код на std. Ну а дальше будут фишки в отдельных ветках на git, типо свёртки и других приёмов. Главное сохранить максимальную простоту проекта
Если же речь идет о промышленной реализации нейросети, тогда не стоит делать велосипедной математики, а стоит взять Eigen, BLAS, ATLAS, Vienna CL и использовать их — они в любом случае окажутся быстрее и стабильнее.
Пока цель написание проекта с открытым и понятным исходным кодом, чтобы каждый мог хоть пошагово увидеть как сигнал проходит прямо, как обратно, как значения меняются, как функция активация влияет на результаты. Велосипед в разрезе. Версии 2.0 быть и скорее всего уже с разбором, на картинках с кусками кода отдельных функций
Чтобы с этим не возиться, возьмите генераторы и преобразователи псевдослучайных величин из C++11
А зачем? Как упражнение, навыки с++ потренировать — понятно. Но вы не используете векторизацию, то есть практического смысла мало.
Зачем вы реализовали этот проект. Однако ответ я уже увидел выше — вам не понравились сторонние библиотеки и вы реализовали свою. Ваше право.
Практический смысл — это когда и где использовать. Реализацию сети без векторных операций лично я бы не рекомендовал использовать нигде и никогда, поэтому вопрос и возник, зачем было сделано именно так. Сейчас я вижу, что сформулирован он был не очень ясно.
Желающие поучаствовать в развитии проекта с возможным преломлением на практике — писать сюда mamkin.itshnik@gmail.com
Посмотрите внимательный. Да немного, но есть, да можно переписать в С, но зачем мне искажать заголовок? Напишите решение где ++ больше или в ассемблер закатайте. Сообщество только выйграет от разнообразия материала. Каждый сам решит что ему ближе. По нейросетям много развелись статей, не хватает наверно DeepLearning на Assembler
С двумерной матрицей немного сложнее, т.к. в стандартной библиотеке нет подобного типа. Поскольку код, в целом, учебный, можно было бы обойтись кошмаром типа вектора векторов. Это как минимум решило бы проблемы с управлением памятью. Если же хочется написать красивее, то можно использовать Eigen или подобную библиотеку для операций над матрицами и векторами. Можно, конечно, свой класс двумерной матрицы придумать, но это выходит за рамка задачи создания нейронной сети.
Изначально vector<vector<....> был! Да удобней с памятью, но грамоздко показалось, вот и решил в С-образно сделать да и в struct загнать. Ну переоценил силы немного, бывает
using MyMatrix = std::vector< std::vector < float > >
Пишется один раз, после чего использование становится намного проще. Единственное что, возвращать по значению из функции не всегда может быть разумно. Но у Вас всё же структура, вполне можно обойтись без get/set методов — они ничего не прячут, да и для примера реализации не нужны.
Вообще, узнаю себя :)
Как только заинтересовался НС, сразу же кинулся их кодировать на C++. Сделал прямое распространение, потом обратное, градиентный спуск. Обучал, и они успешно обучались. Потом столкнулся с переобучением (до регуляризации и дропаута не дошел). По мере углубления в тему забросил свой велосипед, стал экспериментировать в матлабе, потом на питоне :)
С++ очень хороший язык, но что бы эффективно хотя бы перемножить матрицы, нужно, как минимум, знать алгоритмические методы оптимизации этой операции. Они есть, оказывается :) А еще нужно использовать векторные расширения процессоров, например, SSE. И оптимизация может быть разной на разных процессорах. А еще эта операция хорошо параллелится и можно задействовать несколько ядер процессора… А еще есть CUDA…
В общем, хорошие библиотеки считают НС на порядки (!) быстрее подобного кода.
Подобный код можно применить в продакшене только в одном случае — загрузить уже просчитанные другими средствами веса, и скорость не нужна. Но тогда весь код расчета НС поместится на одном-двух экранах, так как это будет простое прямое распространение, да свертки…
Но польза от велосипедов авторам есть! :)
Вы хотите сказать, что вот эта ваша сеть (та что в архиве) научилась распозновать набор MNIST за 3 секунды?
Сами то в это верите?
Для статьи можно было заменить QT функционал на стандартный и разобраться с чистотой кода. Смотреть и анализировать такое довольно трудно.
Пример простой нейросети на С/C++