tac, в силу того, что переубедить вас невозможно, при всем моем желании, и времени на это у меня уже нет, а промышленного опыта разработки у вас явно нет и проблем ее вы не понимаете, я воспользуюсь вашим любимым аргументом — срочно идите читать первоисточники:
1. Про ООП — «Рефакторинг» и «Архитектуру корпоративных систем» Мартина Фаулера.
2. Так называемый SICP — про функциональное программирование.
Вы сейчас выглядите как те, вами ненавидимые люди, которые дают неправильное определение перцептрону, серьезно.
Потому что самый внешний цикл (в тесте) не параллельный и на каждой итерации старательно ждет, пока внутренние параллельные закончатся. А так как тут вы распараллелили очень атомарные операции, то получается, что издержки на параллелизацию больше, чем сами операции. Параллелить надо обучение и вычисление всей нейросети.
Еще раз — мне лень заниматься оптимизацией кода, написанного в чисто иллюстративных целях и суть моих претензий к вашему совсем не в производительности.
Во-первых, мой код не оптимизирован по производительности, а оптимизировать там очень много чего. В одном Activate можно три последовательных цикла заменить на один, например.
Во-вторых, не вижу каким образом вы сравниваете модель вычислений (которая у меня гибридная, а у вас чисто императивная) со способом структурирования кода(который в обоих вариантах — ООП). Это разные вещи.
В третьих, в наш современный век многоядерных процессоров… Предложите вариант быстро адаптировать ваш код для параллельных вычислений.
Напомню, что в моем это делается тупой заменой вызовов LINQ на вызовы PLINQ.
Я уже пилю реализацию на F#, не интересно возвращаться к этому коду. Тем более доказывать мне тут нечего, я знаю, что оно работает медленнее по факту ;)
1. Почему вы тестируете только функцию активации? Где остальное?
По хорошему надо отдельно померять скорость обучения, а затем отдельно скорость распознавания на обученном перцептроне.
2. Почему мою функцию вы запускаете 900 * 100 = 90000 раз, а свою — 10000 раз?
Я тут задумал статейку со сравнением перцептрона Розенблатта и перцептрона Румельхарта на реальной задаче. Код на F# (ну не так уж далеко от хаскелля) + OpenCV. Интересно?
«Допустим, мы хотим обучить перцептрон разделять два класса объектов так, чтобы при предъявлении объектов первого класса выход перцептрона был положителен (+1), а при предъявлении объектов второго класса — отрицательным (−1). Для этого выполним следующий алгоритм:[5]
Случайным образом выбираем пороги для A-элементов и устанавливаем связи S—A (далее они изменяться не будут).
Начальные коэффициенты полагаем равными нулю.
Предъявляем обучающую выборку: объекты (например, круги либо квадраты) с указанием класса, к которым они принадлежат.
Показываем перцептрону объект первого класса. При этом некоторые A-элементы возбудятся. Коэффициенты, соответствующие этим возбуждённым элементам, увеличиваем на 1.
Предъявляем объект второго класса и коэффициенты тех A-элементов, которые возбудятся при этом показе, уменьшаем на 1.
Обе части шага 3 выполним для всей обучающей выборки. В результате обучения сформируются значения весов связей .»
Про матрицы — согласен. Про тяжелую читаемость — имхо, дело привычки.
Собственно, еще одна претензия к коду в статье — он плохо параллелится ;)
А в моем достаточно заменить linq на plinq и переписать Train ;)
1. Про ООП — «Рефакторинг» и «Архитектуру корпоративных систем» Мартина Фаулера.
2. Так называемый SICP — про функциональное программирование.
Вы сейчас выглядите как те, вами ненавидимые люди, которые дают неправильное определение перцептрону, серьезно.
Еще раз — мне лень заниматься оптимизацией кода, написанного в чисто иллюстративных целях и суть моих претензий к вашему совсем не в производительности.
Во-вторых, не вижу каким образом вы сравниваете модель вычислений (которая у меня гибридная, а у вас чисто императивная) со способом структурирования кода(который в обоих вариантах — ООП). Это разные вещи.
В третьих, в наш современный век многоядерных процессоров… Предложите вариант быстро адаптировать ваш код для параллельных вычислений.
Напомню, что в моем это делается тупой заменой вызовов LINQ на вызовы PLINQ.
> Но менее понятен, в него сложнее вносить изменения если меняются процессы по активации.
А вот это уже неправда. Спорить не буду, не отвечайте ;) Пусть общественность рассудит.
Но вот если включить распараллеливание… Но это уже читерство, вы свой код так легко не распараллелите ;)
1. Почему вы тестируете только функцию активации? Где остальное?
По хорошему надо отдельно померять скорость обучения, а затем отдельно скорость распознавания на обученном перцептроне.
2. Почему мою функцию вы запускаете 900 * 100 = 90000 раз, а свою — 10000 раз?
Незачет ;)
Случайным образом выбираем пороги для A-элементов и устанавливаем связи S—A (далее они изменяться не будут).
Начальные коэффициенты полагаем равными нулю.
Предъявляем обучающую выборку: объекты (например, круги либо квадраты) с указанием класса, к которым они принадлежат.
Показываем перцептрону объект первого класса. При этом некоторые A-элементы возбудятся. Коэффициенты, соответствующие этим возбуждённым элементам, увеличиваем на 1.
Предъявляем объект второго класса и коэффициенты тех A-элементов, которые возбудятся при этом показе, уменьшаем на 1.
Обе части шага 3 выполним для всей обучающей выборки. В результате обучения сформируются значения весов связей .»
Собственно, еще одна претензия к коду в статье — он плохо параллелится ;)
А в моем достаточно заменить linq на plinq и переписать Train ;)