Нейронная сеть на практике: Пример реализации элемента XOR
Введение
В предыдущей статье «Нейронная сеть на практике: практика ведёт к совершенству» я объяснил, как можно ускорить вычисление или настройку значений внутри простого нейрона. И всё это — в рамках того, что я сейчас разрабатываю и позже покажу, как реализовать. Здесь я ни в коем случае не собираюсь показывать вам, как создать нейронную сеть для торговли на финансовом рынке. Цель состоит в том, чтобы как можно яснее и нагляднее объяснить, как обучается нейронная сеть и, главное, как она работает за кулисами.
В той же статье я привёл небольшую таблицу, которую вы можете увидеть ниже:

Эта небольшая таблица, также известная как таблица истинности, показывает тем, кто ищет информацию по этой теме или изучает электронику, как схема реагирует на данные, подаваемые на её входы. То есть, глядя на эту таблицу, мы можем определить, как будет работать электронное устройство. По сути, здесь показаны конфигурации основных логических элементов, которые можно реализовать. Слева направо у нас идут логические элементы AND, NAND, OR, NOR, XOR и NXOR.
Хорошо, благодаря некоторым факторам наш простой нейрон всё-таки способен понять, как работают первые четыре логических элемента. Однако он не может понять, как работают два последних. Если вы попытаетесь научить искусственный нейрон тому, как работают логический элемент XOR и его инверсия — NXOR, — он не сможет генерировать правильные значения для представления этих элементов. А почему так происходит? Дело в том, что логика, лежащая в основе логических элементов XOR и NXOR, значительно сложнее, чем у остальных логических элементов.
Многие люди ошибочно полагают, что нейрон может научиться представлять любой случай в пределах определённого пространства. Однако, как вы, мой уважаемый читатель, можете легко убедиться, это не так. Бывают случаи, когда нейронная сеть или, в данном контексте, нейрон не способны понять или, точнее говоря, чему-то научиться. Заметьте, что, хотя это и может показаться странным, такое действительно происходит. Именно поэтому я и показываю этот конкретный случай. Есть и другие случаи, когда более или менее сложно понять, почему нейронная сеть или нейрон не могут как следует научиться представлять данные. Но этот случай, в котором я использую логические элементы, объяснить проще всего. И его также проще всего понять всем, чтобы разобраться, что происходит.
Так что, думаю, мы все согласны с тем, что увидели и изложили. Таким образом, мы можем продолжить разбор. Итак, в предыдущей статье я предложил вам небольшой вызов: попытаться, мой уважаемый читатель, каким-нибудь образом заставить один-единственный нейрон научиться представлять логический элемент XOR или его инверсию — логический элемент NXOR. Чтобы как следует объяснить, как мы это сделаем, перейдём к новому разделу.
Немного о цифровой электронике
То, что я постараюсь здесь объяснить, входит в курсы по цифровой электронике. Но это также изучают на курсах по мехатронике, где мы учимся создавать машины и механизмы, сочетающие механические и электронные компоненты. Поскольку подготовка в области мехатроники — одна из самых продвинутых, ведь студентам часто приходится изучать ещё и программирование, операционные системы, приводы, датчики и многое другое, она даёт очень прочную основу для понимания концепций и знаний, необходимых для понимания интеллектуальных механизмов или создания человеко-машинных интерфейсов. Безусловно, это одна из тех областей, в которых вы никогда не перестаёте быть учеником и всегда продолжаете учиться. В общем, первое, что я хочу показать: решение, которое мы ищем, не всегда находится в программном обеспечении. Зачастую всё дело в знаниях из других областей.
Чтобы понять, как мы будем управлять нейроном или заставлять его представлять логический элемент XOR или его инверсию, сначала нужно понять, как строится логический элемент XOR. Или, точнее, как мы можем свести его к более базовому уровню. Вопреки распространённому мнению, логический элемент XOR — не простая функция. На самом деле это составная функция, состоящая из нескольких этапов. Тем не менее я не буду углубляться в ту часть, где схема реализуется с использованием дискретных компонентов, таких как транзисторы и другие элементы. Не буду вдаваться в подробности этой части. То есть я воспользуюсь другими логическими элементами, чтобы объяснить, как можно решить вопрос с логическим элементом XOR и его инверсией.
Дойдя до этого места, вы, возможно, думаете: «Но это же безумие. Почему кто-то стал бы так делать?» Что ж, мой уважаемый читатель, если вы действительно хотите понять, как всё устроено, вам нужно разобраться во множестве других тем. Недостаточно просто ждать, пока программа даст вам ответ. Нужно копнуть глубже. Здесь, однако, мы останемся лишь на поверхности. Тем не менее мы посмотрим, что стоит за тем, что позже будем делать внутри нейрона.
Хорошо, существует несколько способов представить логический элемент XOR и его инверсию с помощью других логических элементов. Главный вопрос заключается в следующем: как создать логический элемент XOR, используя элементы AND, NAND, OR и NOR? Помните, что в этой конфигурации мы также можем использовать инверторы для создания логического элемента XOR. Таким образом, мы можем свести всё к логическим элементам AND, OR и инверторам, поскольку NAND — это инвертированный элемент AND, а NOR — инвертированный элемент OR. Это видно на изображении в предыдущем разделе.
Поскольку цель состоит в том, чтобы создать логический элемент XOR или, точнее, корректно представить логический элемент такого типа, чтобы нейрон мог создать его представление, мы можем не доводить упрощение до крайности и использовать любой другой логический элемент, который нейрон способен представить. Вот что нас интересует: использовать логический элемент, который наш простой нейрон может представить без ошибок. Если вы это понимаете, то сможете понять, что будет делаться здесь, в этой статье.
Ну, это простая часть. Сложность в том, что, как я недавно говорил, логический элемент XOR можно создать несколькими разными способами, используя другие логические элементы. Но чтобы вы, мой уважаемый читатель, могли это понять, я не ограничусь тем, что просто скажу, какую конфигурацию буду использовать в этой статье. Я покажу вам, какая конфигурация будет использоваться. Это можно увидеть в следующей анимации.

Эта анимация создана в LOGISIM — программе с открытым исходным кодом, написанной на JAVA. С помощью этой программы мы можем моделировать электронные схемы в целом, чтобы понять, как они работают. Это довольно интересная, хотя и старая программа, которая позволяет нам собирать и тестировать разные схемы, не используя дорогие компоненты и не рискуя их сжечь. Нам важно, и вам, мой уважаемый читатель, нужно понять, что светло-зелёный цвет указывает на наличие напряжения. То есть у нас есть логический уровень 1. Или, для программистов, значение «истина».
Линии более тёмного зелёного цвета обозначают логический ноль, то есть значение «ложь». На входе схемы мы видим значения из таблицы истинности, показанной в предыдущем разделе. Выход — это кружок, который то красный, то серый; именно это мы и хотим получить в данном случае как выход логического элемента XOR. А теперь внимание: оба входа подключены к двум логическим элементам: верхний — NOR, а нижний — AND. Выходы обоих элементов подаются на другой логический элемент NOR, и конечный результат — это как раз выход этого второго элемента NOR.
Мы могли бы реализовать эту систему несколькими разными способами. Но здесь я решил использовать именно эту конфигурацию по очень простой причине: всё сводится к тому, чтобы понять, как работают логический элемент NOR и логический элемент AND. Наш простой нейрон может корректно представлять оба логических элемента. Так что, если использовать подходящую реализацию, мы можем применить хитрость и заставить нейрон представлять логический элемент, который сам по себе он представить не смог бы. То есть мы сможем реализовать логический элемент XOR с помощью всего одного нейрона.
Но подождите-ка. Можем ли мы делать такие вещи? Разве это не немного нечестно? Ну, нет никакого правила, которое бы запрещало нам это делать. Даже если это и хитрость и мы действуем несколько спорным образом, я не вижу причин, почему мы не можем показать, как делаются такие вещи. Итак, давайте теперь перейдем к части, посвященной программированию.
Глупый кусок кремния
Отлично, если вы поняли предыдущую тему, добро пожаловать к моменту, когда мы докажем, что машина будет такой, какой мы захотим её сделать. Она не выйдет за пределы того, что мы ей зададим, хотя многие думают иначе. Нейронная сеть — это не более чем очень хорошо выстроенное вычисление. А поскольку компьютер — это просто чуть более мощный калькулятор, мы можем заставить этот кусок кремния делать именно то, что нам нужно. И если он нас не слушается, мы заставим его выполнять наши команды, пока он не сдастся и не поймет, кто здесь главный.
В приведенном ниже коде мы можем увидеть наш исходный нейрон. Здесь мы будем использовать самую простую версию, в которой совершенно не оптимизируется количество входов. Так что вы можете добавить столько входов, сколько вам нужно. Но не забывайте о том, что мы рассмотрели в предыдущей статье.
//+------------------------------------------------------------------+ #property copyright "Daniel Jose" //+------------------------------------------------------------------+ #define macroRandom (rand() / (double)SHORT_MAX) #define macroSigmoid(a) (1.0 / (1 + MathExp(-a))) //+------------------------------------------------------------------+ #define def_Fast //+------------------------------------------------------------------+ double Train[][3] { {0, 0, 0}, {0, 1, 0}, {1, 0, 0}, {1, 1, 1}, }; //+------------------------------------------------------------------+ const uint nTrain = Train.Size() / 3; const double eps = 1e-3; //+------------------------------------------------------------------+ double Cost(const double w0, const double w1, const double b) { double err; err = 0; for (uint c = 0; c < nTrain; c++) err += MathPow((macroSigmoid((Train[c][0] * w0) + (Train[c][1] * w1) + b) - Train[c][2]), 2); return err / nTrain; } //+------------------------------------------------------------------+ void OnStart() { double w0, w1, err, ew0, ew1, eb, bias; ulong count, it0, it1; Print("The Neuron - Tutor..."); MathSrand(512); w0 = (double)macroRandom; w1 = (double)macroRandom; bias = (double)macroRandom; it0 = GetTickCount(); for (count = 0; (count < ULONG_MAX) && ((err = Cost(w0, w1, bias)) > eps); count++) { ew0 = (Cost(w0 + eps, w1, bias) - err) / eps; ew1 = (Cost(w0, w1 + eps, bias) - err) / eps; eb = (Cost(w0, w1, bias + eps) - err) / eps; w0 -= (ew0 * eps); w1 -= (ew1 * eps); bias -= (eb * eps); } it1 = GetTickCount(); Print("Time: ", (it1 - it0) / 1000.0, " seconds."); PrintFormat("%I64u > w0: %.4f %.4f || w1: %.4f %.4f || b: %.4f %.4f || %.4f", count, w0, ew0, w1, ew1, bias, eb, err); Print("w0 = ", w0, " || w1 = ", w1, " || Bias = ", bias); Print("Error Weight 0: ", ew0); Print("Error Weight 1: ", ew1); Print("Error Bias: ", eb); Print("Error: ", err); Print("************************************"); } //+------------------------------------------------------------------+
Итак, этот нейрон пока не может представлять логический элемент XOR или его инверсию. Но что произойдёт, если мы обучим его представлять логический элемент NOR и логический элемент AND, а также покажем, как соединить эти же элементы, чтобы представить логический элемент XOR? Сможет ли тогда этот глупый кусок кремния понять, как можно представить логический элемент XOR? Хорошо, давайте посмотрим, как это сделать. Помните, что то, что я собираюсь показать, — лишь один из многих возможных и работоспособных вариантов. Вы, мой уважаемый читатель, вольны попробовать и другие варианты.
Хорошо, прежде всего нам нужно понять, что мы собираемся построить небольшую нейронную сеть внутри одного простого нейрона. Для этого нам нужно заставить код вести себя так, будто мы делаем одно, хотя на самом деле будем делать другое. Поскольку нейрон должен научиться представлять каждый из логических элементов, у каждого элемента будет два веса (w0 и w1) и смещение (b). Это связано с тем, что у каждого логического элемента по два входа. На практике каждый логический элемент эквивалентен одному нейрону. По этой причине у нас будет два веса и одно смещение для каждого логического элемента, реализованного внутри. Поскольку мы используем три логических элемента, нам понадобятся четыре веса и два смещения. Но подождите-ка. Если для каждого логического элемента требуется по два веса и одно смещение, разве правильное количество не должно быть равно шести весам и трём смещениям? Нет, мой уважаемый читатель. Поскольку один из логических элементов, в данном случае NOR, будет дублироваться, нам не нужно учитывать его повторно. Это может показаться сложным, но на практике всё гораздо проще, чем кажется.
Очень важно, чтобы вы, мой уважаемый читатель, хорошо это поняли. Если вы всё ещё этого не поняли, перечитайте предыдущие статьи, чтобы понять, зачем нам нужны эти веса и смещения. Но прежде всего постарайтесь понять объяснение в предыдущем разделе, чтобы понять, как мы будем использовать логические элементы. Если у вас останутся какие-либо сомнения на этот счёт, вы не поймёте, что на самом деле происходит внутри нейрона.
Хорошо, тогда я буду исходить из того, что вы, мой уважаемый читатель, поняли, что нам нужно делать. Итак, приведённый выше код будет изменён и примет вид, показанный ниже.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define macroRandom (rand() / (double)SHORT_MAX) 005. #define macroSigmoid(a) (1.0 / (1 + MathExp(-a))) 006. //+------------------------------------------------------------------+ 007. #define def_Fast 008. //+------------------------------------------------------------------+ 009. double _NOR[][3] { 010. {0, 0, 1}, 011. {0, 1, 0}, 012. {1, 0, 0}, 013. {1, 1, 0}, 014. }; 015. double _AND[][3] { 016. {0, 0, 0}, 017. {0, 1, 0}, 018. {1, 0, 0}, 019. {1, 1, 1}, 020. }; 021. struct st_XOR 022. { 023. double Nor_w0, Nor_w1, Nor_bias, Nor_Err, 024. And_w0, And_w1, And_bias, And_Err; 025. }_XOR; 026. //+------------------------------------------------------------------+ 027. const uint nTrain = _NOR.Size() / 3; 028. const double eps = 1e-3; 029. //+------------------------------------------------------------------+ 030. double Cost(const double w0, const double w1, const double b, const double &Train[][]) 031. { 032. double err; 033. 034. err = 0; 035. for (uint c = 0; c < nTrain; c++) 036. err += MathPow((macroSigmoid((Train[c][0] * w0) + (Train[c][1] * w1) + b) - Train[c][2]), 2); 037. 038. return err / nTrain; 039. } 040. //+------------------------------------------------------------------+ 041. void OnStart() 042. { 043. double ew0, ew1, eb; 044. ulong count, it0, it1; 045. 046. Print("The Neuron - Tutor..."); 047. MathSrand(512); 048. 049. _XOR.Nor_w0 = (double)macroRandom; 050. _XOR.Nor_w1 = (double)macroRandom; 051. _XOR.Nor_bias = (double)macroRandom; 052. 053. _XOR.And_w0 = (double)macroRandom; 054. _XOR.And_w1 = (double)macroRandom; 055. _XOR.And_bias = (double)macroRandom; 056. 057. it0 = GetTickCount(); 058. 059. for (count = 0; count < ULONG_MAX; count++) 060. { 061. _XOR.Nor_Err = Cost(_XOR.Nor_w0, _XOR.Nor_w1, _XOR.Nor_bias, _NOR); 062. _XOR.And_Err = Cost(_XOR.And_w0, _XOR.And_w1, _XOR.And_bias, _AND); 063. 064. if ((_XOR.Nor_Err < eps) && (_XOR.And_Err < eps)) 065. break; 066. 067. ew0 = ((Cost(_XOR.Nor_w0 + eps, _XOR.Nor_w1, _XOR.Nor_bias, _NOR) - _XOR.Nor_Err) / eps); 068. ew1 = ((Cost(_XOR.Nor_w0, _XOR.Nor_w1 + eps, _XOR.Nor_bias, _NOR) - _XOR.Nor_Err) / eps); 069. eb = ((Cost(_XOR.Nor_w0, _XOR.Nor_w1, _XOR.Nor_bias + eps, _NOR) - _XOR.Nor_Err) / eps); 070. 071. _XOR.Nor_w0 -= (ew0 * eps); 072. _XOR.Nor_w1 -= (ew1 * eps); 073. _XOR.Nor_bias -= (eb * eps); 074. 075. ew0 = ((Cost(_XOR.And_w0 + eps, _XOR.And_w1, _XOR.And_bias, _AND) - _XOR.And_Err) / eps); 076. ew1 = ((Cost(_XOR.And_w0, _XOR.And_w1 + eps, _XOR.And_bias, _AND) - _XOR.And_Err) / eps); 077. eb = ((Cost(_XOR.And_w0, _XOR.And_w1, _XOR.And_bias + eps, _AND) - _XOR.And_Err) / eps); 078. 079. _XOR.And_w0 -= (ew0 * eps); 080. _XOR.And_w1 -= (ew1 * eps); 081. _XOR.And_bias -= (eb * eps); 082. } 083. 084. it1 = GetTickCount(); 085. Print("Time: ", (it1 - it0) / 1000.0, " seconds."); 086. PrintFormat("Interactions: %I64u", count); 087. Print("NOR port information:"); 088. Print("w0: ", _XOR.Nor_w0, " w1: ", _XOR.Nor_w1, " bias: ", _XOR.Nor_bias, " Error: ", _XOR.Nor_Err); 089. Print("AND port information:"); 090. Print("w0: ", _XOR.And_w0, " w1: ", _XOR.And_w1, " bias: ", _XOR.And_bias, " Error: ", _XOR.And_Err); 091. 092. Print("Testing the neuron..."); 093. for (uchar p0 = 0; p0 < 2; p0++) 094. for (uchar p1 = 0; p1 < 2; p1++) 095. PrintFormat("%d XOR %d IS %f", p0, p1, macroSigmoid((_XOR.Nor_w0 * macroSigmoid((p0 * _XOR.Nor_w0) + (p1 * _XOR.Nor_w1) + _XOR.Nor_bias)) + 096. (_XOR.Nor_w1 * macroSigmoid((p0 * _XOR.And_w0) + (p1 * _XOR.And_w1) + _XOR.And_bias)) + _XOR.Nor_bias)); 097. 098. Print("************************************"); 099. } 100. //+------------------------------------------------------------------+
И, запустив этот код в MetaTrader 5, вы увидите нечто очень похожее на следующее изображение.

Обратите внимание, что нейрон подтвердил правильность представления логического элемента XOR. То есть благодаря нашей помощи он смог понять, как можно представить логический элемент XOR. Однако, взглянув на приведённый выше код, вы, возможно, задаётесь вопросом, как именно это было сделано. Это может показаться чрезвычайно сложным и трудным для понимания. Однако, если вы внимательно изучите код, то сразу заметите, что он почти такой же, как код, который мы рассматривали в начале этой темы. Тогда почему в нём больше кода? И, кроме того, почему, в отличие от предыдущих случаев, мы не вводим в этот код таблицу истинности логического элемента XOR для обучения нейрона?
Хорошо, это вполне обоснованные вопросы. Что касается первого вопроса, ответ таков: нам нужно указать нейрону, чтобы он научился представлять логический элемент NOR и логический элемент AND. По этой причине здесь, по-видимому, у нас и больше элементов. Что касается второго вопроса, то правильно будет сказать, что нам не нужно обучать нейрон по таблице логического элемента XOR. Мы пойдём на хитрость и заставим его создать представление логического элемента на основе значений, которые сам нейрон сгенерирует для представления логического элемента NOR и логического элемента AND. Таким образом, мы создадим в коде эквивалент цифровой схемы, рассмотренной в предыдущем разделе.
Поскольку нам нужно немного разделить данные для каждого логического элемента, в строке 21 мы создаём структуру для хранения данных, необходимых для представления логического элемента XOR. Но обратите внимание, что там у нас есть только поля для хранения значений типа double. Нет никаких данных или сведений, которые позволяли бы утверждать, что действительно будет создан логический элемент XOR.
Функция потерь в строке 30 получила дополнительный аргумент. Его цель — указать функции потерь, какие данные будут использоваться во время обучения. Остальная часть функции потерь осталась без изменений. То есть нейрон остаётся в своей самой базовой форме. Таким образом, он по-прежнему способен делать то же самое, что и раньше, если вы захотите использовать его вне этого конкретного приложения, цель которого — получить представление логического элемента XOR.
В строке 41 мы начинаем часть, относящуюся к этому конкретному приложению. На данном этапе вся логика объединена в одной процедуре. Но, хотя это может показаться сложным, на самом деле всё довольно просто, и, поскольку я разбил код немного подробнее, думаю, вы, мой уважаемый читатель, сможете понять, что происходит.
Начиная со строк 49–51, мы инициализируем значения весов и смещения логического элемента NOR. То же самое происходит в строках с 53 по 55, только в этом случае мы инициализируем значения логического элемента AND. Обратите внимание, что мы инициализируем систему псевдослучайными значениями. Это уже происходило раньше, как вы можете видеть в коде в начале этой же темы. Разница заключается в том, что раньше мы инициализировали значения только для одного логического элемента, а теперь — для двух.
Хорошо, теперь в строке 59 мы входим в цикл for, цель которого — заставить нейрон найти способ представления логических элементов, используя данные из обучающих массивов. Опять же, это может показаться сложным. Но мы уже некоторое время делаем вещи именно такого рода. Отличие от предыдущих случаев заключается в том, что сейчас мы обучаем нейрон представлять не один, а два разных логических элемента. Итак, первое, что нам нужно сделать, — это определить начальную ошибку для каждого из логических элементов. Это делается в строке 61, где мы получаем ошибку логического элемента NOR, и в строке 62, где мы получаем ошибку логического элемента AND. Поскольку нам нужно проверить два значения, чтобы как можно скорее завершить цикл, в строке 64 мы проверяем, не ниже ли ошибка ожидаемого уровня. Если это так, строка 65 приведёт к завершению цикла. В противном случае мы перейдём к следующему шагу цикла.
Следующий шаг — использовать секущую прямую для поиска точки с наименьшей стоимостью. Сначала мы сделаем это для логического элемента NOR. Это можно увидеть в строках с 67 по 73. В этих строках мы выполняем точно те же вычисления, что и раньше, поэтому у тех, кто следит за нашими статьями, это не должно вызвать затруднений. После того как мы выполнили расчёты для логического элемента NOR, делаем то же самое для логического элемента AND. Это делается в строках с 75 по 81.
Таким образом, мы заставляем нейрон сформировать представление о том, что он способен понять, а именно о логических элементах AND и NOR. Обратите внимание, что, вопреки тому, что можно было предположить поначалу, нам не нужно обучать третий логический элемент. То есть сначала поймите, что нужно сделать, а потом сделайте то, что было задумано изначально.В строках с 84 по 90 мы выводим ряд данных, чтобы проверить полученные результаты. Помните, что идея, лежащая в основе нейронной сети, состоит в создании уравнения, которое представляет данные. То, что оно порождает нечто, что многие считают интеллектуальным, — всего лишь деталь. И, наконец, в строках с 92 по 96 мы выводим представление, которое нейрон генерирует для логического элемента XOR.
Понимание результатов
"Друг мой, я понял большую часть кода, так как довольно внимательно изучаю ваши программы. Я заметил, что мы действительно обучали логический элемент NOR и логический элемент AND, чтобы нейрон научился представлять эти элементы. Но что касается этой последней части, где мы выводим результат и получаем именно то, что нужно для представления логического элемента XOR, друг мой, я вообще не понял, что там делается. Не могли бы вы немного подробнее объяснить эту часть?"
Конечно. Конечно, я могу объяснить, что происходит в этой заключительной части кода. Я знаю, что на первый взгляд это может показаться довольно необычным и несколько сложным. Но, возможно, это связано с тем, что вы ожидали увидеть определённый код. И, рассматривая код, используемый для отображения логического элемента XOR, вы видите нечто совсем не такое, как ожидали. Чтобы понять это как следует, я хочу, чтобы вы следили за последовательностью изображений, приведённой ниже, и за тем, что происходит в фрагментах, показанных далее. Идея в том, чтобы ещё больше разбить итоговый код на части, чтобы нам было гораздо проще объяснить, что здесь происходит, мой уважаемый читатель.
Начнём со следующего: на изображении ниже показан логический элемент NOR, который изначально получает исходные данные и окрашен в СИНИЙ цвет.

В коде фрагмент, соответствующий этому же логическому элементу, показанному на предыдущем изображении, также выделен ниже.
PrintFormat("%d XOR %d IS %f", p0, p1, macroSigmoid((_XOR.Nor_w0 * macroSigmoid((p0 * _XOR.Nor_w0) + (p1 * _XOR.Nor_w1) + _XOR.Nor_bias)) + (_XOR.Nor_w1 * macroSigmoid((p0 * _XOR.And_w0) + (p1 * _XOR.And_w1) + _XOR.And_bias)) + _XOR.Nor_bias));
Отлично, обратите внимание, что это первый из изображаемых логических элементов. Теперь давайте рассмотрим следующий логический элемент — это логический элемент AND, на следующем изображении он обозначен зелёным цветом.

В коде этот же логический элемент AND, который на предыдущем изображении выделен зелёным цветом, в приведённом ниже фрагменте также выделен зелёным.
PrintFormat("%d XOR %d IS %f", p0, p1, macroSigmoid((_XOR.Nor_w0 * macroSigmoid((p0 * _XOR.Nor_w0) + (p1 * _XOR.Nor_w1) + _XOR.Nor_bias)) + (_XOR.Nor_w1 * macroSigmoid((p0 * _XOR.And_w0) + (p1 * _XOR.And_w1) + _XOR.And_bias)) + _XOR.Nor_bias));
Обратите внимание, что это один и тот же фрагмент. А теперь — грандиозный финал: выходной логический элемент NOR, выделенный красным цветом на следующем изображении.

Поскольку в этом фрагменте весь код относится к этому последнему логическому элементу, я лишь покажу, где находятся его параметры. То есть его входы — это как раз выходы остальных логических элементов. Однако ему нужны веса и смещение исходного логического элемента NOR. И именно это выделено в следующем фрагменте.
PrintFormat("%d XOR %d IS %f", p0, p1, macroSigmoid((_XOR.Nor_w0 * macroSigmoid((p0 * _XOR.Nor_w0) + (p1 * _XOR.Nor_w1) + _XOR.Nor_bias)) + (_XOR.Nor_w1 * macroSigmoid((p0 * _XOR.And_w0) + (p1 * _XOR.And_w1) + _XOR.And_bias)) + _XOR.Nor_bias));
На этом, думаю, объяснение завершено. Тем не менее, я хочу предложить вам испытание, мой уважаемый, чтобы вы сами убедились, что поняли изложенное в этой статье.
Испытание для вас, читатель
Давайте поступим так: в приложенном файле вы найдёте как код, который мы рассматривали в этой статье для представления логического элемента XOR, так и код с ответом на этот вызов. Тем не менее, я хочу, чтобы вы были честны и попытались справиться с этим вызовом, прежде чем смотреть ответ или возможный вариант решения. Хорошо, договорились?
Хорошо, если ответ «да», переходим к вызову. Это будет что-то простое и связанное с тем, что мы рассмотрели в этой статье. Я хочу, чтобы вы попробовали разработать обучение, при котором один нейрон сможет создать представление логического элемента XOR, но не то, которое мы рассматривали до сих пор. Я хочу, чтобы вы рассмотрели схему в следующей анимации и разработали решение, использующее логические элементы, задействованные в этой схеме. Поскольку многие из вас, вероятно, не разбираются в электронике, я покажу вам таблицу истинности каждого из логических элементов. В любом случае, сначала попробуйте посмотреть анимацию, так как она точно показывает, что происходит.

Таблица истинности, которой вы можете воспользоваться, показана на следующем изображении.

Одна деталь: чтобы решить этот вызов, попробуйте сделать что-то похожее на то, что мы рассматривали в этой статье. Однако вам придётся настроить некоторые вещи немного иначе. А теперь, если вы действительно поняли, как это делать, и хотите вызов немного посложнее, попробуйте создать нейрон, который сможет делать то, что показано в следующей анимации.

В данном случае мы используем только логические элементы NAND, и всё же конечный результат — это представление логического элемента XOR. Вот это уже может стать хорошим вызовом, если вы захотите попробовать настроить выход нейрона. Но сделать это несложно. Вам нужно лишь немного терпения, чтобы получить правильный результат.
Заключительные замечания
В этой статье я попытался показать вам, мой уважаемый читатель, что не всё так, как кажется. Часто мы склонны думать, что всё устроено определённым образом, хотя на самом деле в итоге можем поверить в то, что не обязательно соответствует действительности. Нейронные сети, безусловно, — одна из самых интересных тем вообще. И с математической точки зрения, и с точки зрения электроники, и даже программного обеспечения. Вопреки тому, что многие думают или утверждают, нейронные сети вовсе не являются ни проблемой, ни окончательным решением. Это лишь одно из направлений исследований, в котором нам следует продолжать разбираться и изучать, что происходит за кулисами.
Когда речь идёт о нейронных сетях, не следует слепо верить никому. На самом деле за эти годы я заметил, что чем дальше продвигаются исследования в области нейронных сетей, тем больше появляется лжи и слухов. На самом деле нейронные сети изучают уже много лет, если не десятилетий. Тем не менее я замечаю, что многие просто игнорируют этот факт и считают, что тема уже достигла своего пика. И всё же предстоит ещё многое сделать. Как вы уже могли заметить, не существует единого универсального способа представить модель любого типа. Как бы просто это ни казалось, до сих пор не существует способа адекватно представить всё на свете.
В области нейронных сетей ещё остаётся большой простор для исследований, улучшений и дальнейшего изучения. Какими бы продвинутыми ни казались нейронные сети, способные генерировать изображения или даже тексты, им всё ещё не хватает чего-то важнейшего: критического мышления и здравого смысла. В любом случае нам ещё предстоит рассмотреть много других аспектов этой темы. Итак, до встречи в следующей статье.| Файл MQ5 | Описание |
|---|---|
| Приложение | Базовая демонстрация |
| Вызов 01 | Базовая демонстрация |
| Вызов 02 | Базовая демонстрация |
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/13782
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Реализация алгоритма усечённого метода Ньютона с сопряжёнными градиентами в MQL5
От начального до среднего уровня: Перегрузка операторов (VI)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования