English 中文 Español Deutsch 日本語 Português
preview
Нейронная сеть на практике: Первый нейрон

Нейронная сеть на практике: Первый нейрон

MetaTrader 5Машинное обучение |
1 047 4
Daniel Jose
Daniel Jose

Введение

Всем привет и добро пожаловать в новую статью о нейронных сетях. В предыдущей статье Нейронная сеть на практике: Зарисовка нейрона, мы создавали базовую, начальную схему для построения простого нейрона. Однако тот простой нейрон, который мы получили, — это не совсем то, что нам нужно. Это связано с тем, что у него есть небольшой недостаток. Но я хочу, чтобы вы поняли, как строится нейронная сеть шаг за шагом. Данный недостаток не сильно влияет на работу этого простого нейрона.

Возможно, вы думаете: "Что это за недостаток, о котором он говорит? Я не заметил ничего плохого. Нейрон прекрасно работал в моих тестах". Вернемся немного назад в этой серии статей о нейронных сетях, чтобы понять, о чем я говорю.

В первых статьях о нейронных сетях мы рассмотрели, как можно заставить машину создавать уравнение прямой линии. Изначально уравнение ограничивалось началом координат на декартовых осях, то есть прямая должна была проходить через точку (0, 0). Это объясняется тем, что значение константы < b > в приведенном ниже уравнении равно нулю.

Хотя мы использовали метод наименьших квадратов для поиска подходящего уравнения, чтобы набор данных или предварительные знания, содержащиеся в базе данных, можно было адекватно представить в виде математического уравнения, такое моделирование не помогло найти действительно подходящее уравнение. Это связано с тем, что в зависимости от данных, находящихся в базе знаний, нам потребуется, чтобы значение константы < b > было ненулевым.

Если спокойно изучить предыдущие статьи, то можно заметить, что для определения наилучшего возможного значения как константы < a >, которая является угловым коэффициентом, так и константы < b >, которая является точкой пересечения, потребовалось некоторое математическое жонглирование. Данные манипуляции позволили найти наиболее подходящее уравнение прямой линии, причем было показано два способа сделать это: один - с помощью вычисления производных, другой - с помощью матричных вычислений.

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


Почему они так любят всё усложнять

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

Для простоты я остановлюсь на нескольких терминах, которые часто встречаются при разговоре о нейронных сетях. Вот первый: Вес. Этот термин обозначает именно угловой коэффициент уравнения прямой линии. Как бы вы его ни называли, термин "вес" просто обозначает угловой коэффициент. Еще один широко разрекламированный термин: Смещение. Об этом термине, также можно услышать такое: смещение - не такая уж большая проблема, и она не ограничивается только нейронными сетями или искусственным интеллектом. Но ничего подобного. Данный термин - не что иное, как точка пересечения. Помните, что мы имеем дело с секущей линией. Пожалуйста, не путайте понятия.

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

Я говорю так, потому что есть много людей, которые любят всё усложнять.

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

Итак, теперь перейдем к новой теме. Мы рассмотрим, как можно добавить значение константы < b >, которая является точкой пересечения, или, как некоторые предпочитают ее называть: смещение или предвзятость. Это необходимо для того, чтобы уравнение линии более точно отражало базу данных, которую мы используем для обучения нейрона.


Появление первого нейрона

Чтобы наш первый нейрон ожил, и как только он оживет, нам больше не понадобится его модифицировать, как можно будет увидеть ниже. Сначала нужно понять, с чем мы имеем дело. Наш текущий нейрон ведет себя так, как можно видеть на анимации ниже.



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

01. //+------------------------------------------------------------------+
02. double Cost(const double w, const double b)
03. {
04.     double err, fx, x;
05. 
06.     err = 0;
07.     for (uint c = 0; c < nTrain; c++)
08.     {
09.         x = Train[c][0];
10.         fx =  a * w + b;
11.         err += MathPow(fx - Train[c][1], 2);
12.     }
13. 
14.     return err / nTrain;
15. }
16. //+------------------------------------------------------------------+

Я стараюсь разбивать код на фрагменты, чтобы вы могли детально понять то, что делается. А потом скажите мне: сложно это или нет? Действительно ли нужны все эти сложности?

Будьте внимательны, потому что уровень сложности почти смешной. (СМЕХ). В девятой строке мы берем наше обучающее значение и помещаем его в переменную X. Затем, в десятой строке, мы выполняем факторизацию. Надо же! Какой сложный счет! Но подождите, разве это не то самое уравнение, которое было показано в начале? Знаменитое уравнение прямой линии. Вы шутите! Это не будет работать как нейрон, используемый в программах искусственного интеллекта.

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

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

01. //+------------------------------------------------------------------+
02. void OnStart()
03. {
04.     double weight, ew, eb, e1, bias;
05.     int f = FileOpen("Cost.csv", FILE_COMMON | FILE_WRITE | FILE_CSV);
06. 
07.     Print("The first neuron...");
08.     MathSrand(512);
09.     weight = (double)macroRandom;
10.     bias = (double)macroRandom;
11. 
12.     for(ulong c = 0; (c < ULONG_MAX) && ((e1 = Cost(weight, bias)) > eps); c++)
13.     {
14.         ew = (Cost(weight + eps, bias) - e1) / eps;
15.         eb = (Cost(weight, bias + eps) - e1) / eps;
16.         weight -= (ew * eps);
17.         bias -= (eb * eps);
18.         if (f != INVALID_HANDLE)
19.             FileWriteString(f, StringFormat("%I64u;%f;%f;%f;%f;%f\n", c, weight, ew, bias, eb, e1));
20.     }
21.     if (f != INVALID_HANDLE)
22.         FileClose(f);
23.     Print("Weight: ", weight, "  Bias: ", bias);
24.     Print("Error Weight: ", ew);
25.     Print("Error Bias: ", eb);
26.     Print("Error: ", e1);
27. }
28. //+------------------------------------------------------------------+

Когда мы запустим скрипт после этих изменений, мы увидим нечто похожее на изображение ниже.


Теперь давайте рассмотрим только второй фрагмент кода. В четвертой строке мы добавляем и изменяем некоторые переменные, что-то очень простое. В десятой строке мы указываем приложению определить случайное значение для смещения, или нашей константы пересечения. Теперь обратите внимание, что нам нужно также передать данное значение в функцию Cost. Это делается в строках 12, 14 и 15. Однако самое интересное заключается в том, что мы будем генерировать два вида накапливающихся ошибок: одну для значения веса и одну для значения смещения. Надо понять, что, хотя оба эти параметра являются частью одного уравнения, их нужно настраивать по-разному. Поэтому мы должны знать, какую ошибку представляет собой каждая из них в общей системе.

Зная это, в строках 16 и 17 мы сможем правильно задать значения для следующей итерации цикла for. Теперь (как и в предыдущей статье) мы также сбрасываем значения в CSV-файл. Это позволит нам построить график, чтобы изучить, как корректируются эти значения.

На данный момент наш первый нейрон полностью создан, но есть еще некоторые детали, которые вы сможете понять, если посмотрите на полный код этого нейрона. Полный код приведен ниже.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define macroRandom (rand() / (double)SHORT_MAX)
05. //+------------------------------------------------------------------+
06. double Train[][2] {
07.                     {0, 0},
08.                     {1, 2},
09.                     {2, 4},
10.                     {3, 6},
11.                     {4, 8},
12.                   };
13. //+------------------------------------------------------------------+
14. const uint nTrain = Train.Size() / 2;
15. const double eps = 1e-3;
16. //+------------------------------------------------------------------+
17. double Cost(const double w, const double b)
18. {
19.     double err, fx, a;
20. 
21.     err = 0;
22.     for (uint c = 0; c < nTrain; c++)
23.     {
24.         a = Train[c][0];
25.         fx =  a * w + b;
26.         err += MathPow(fx - Train[c][1], 2);
27.     }
28. 
29.     return err / nTrain;
30. }
31. //+------------------------------------------------------------------+
32. void OnStart()
33. {
34.     double weight, ew, eb, e1, bias;
35.     int f = FileOpen("Cost.csv", FILE_COMMON | FILE_WRITE | FILE_CSV);
36. 
37.     Print("The first neuron...");
38.     MathSrand(512);
39.     weight = (double)macroRandom;
40.     bias = (double)macroRandom;
41. 
42.     for(ulong c = 0; (c < ULONG_MAX) && ((e1 = Cost(weight, bias)) > eps); c++)
43.     {
44.         ew = (Cost(weight + eps, bias) - e1) / eps;
45.         eb = (Cost(weight, bias + eps) - e1) / eps;
46.         weight -= (ew * eps);
47.         bias -= (eb * eps);
48.         if (f != INVALID_HANDLE)
49.             FileWriteString(f, StringFormat("%I64u;%f;%f;%f;%f;%f\n", c, weight, ew, bias, eb, e1));
50.     }
51.     if (f != INVALID_HANDLE)
52.         FileClose(f);
53.     Print("Weight: ", weight, "  Bias: ", bias);
54.     Print("Error Weight: ", ew);
55.     Print("Error Bias: ", eb);
56.     Print("Error: ", e1);
57. }
58. //+------------------------------------------------------------------+

Наверняка вы заметили кое-что интересное как в коде, так и в результате, показанном на изображении выше. В шестой строке находятся значения, используемые для обучения нейрона. Очевидно, что при умножении используется значение два. Однако нейрон сообщает нам, что это значение: 1.9804357049081742. Также видно, что точка пересечения должна быть равна нулю, но нейрон говорит нам, что это так: 0.054422740828113325. Хорошо, учитывая, что в строке 15 мы допускаем, что ошибка может быть от: 0,001, не так плохо. Это связано с тем, что конечная ошибка, о которой сообщил нейрон, была: 0,0009994343288155726, то есть ниже предела погрешности, который мы считаем приемлемым.

Данные различия, которые вы можете отчетливо заметить, являются показателем вероятности того, что информация верна. Обычно это выражается в процентах. Однако мы никогда не увидим 100 %. Число может приближаться к 100%, но оно никогда не будет точным из-за этого небольшого отклонения от правильного результата.

Однако данный показатель вероятности не является индексом уверенности в той или иной информации. Мы пока не работаем над созданием этого второго индекса. Пока что мы просто проверяем, способен ли нейрон установить корреляцию между обучающими данными. Но, возможно, вы уже думаете: "Этот нейрон бесполезен, потому что в том виде, в котором вы его создали, он не годится ни для чего другого. Он служит только для поиска числа, которое мы и так уже знаем. На самом деле мне нужна система, которая могла бы рассказывать мне о чем-то, написать текст или код, даже программу, которая сможет торговать на финансовом рынке и дать какую-то прибыль.

Вы, безусловно, преследуете большие интересы, но если вы только смотрите на нейронные сети или на искусственный интеллект с намерением заработать деньги, боюсь, у вас ничего не получится. Единственные люди, которые действительно заработают на этом, - это те, кто продает нейросети или системы искусственного интеллекта. Это происходит, когда им удается убедить других в том, что искусственный интеллект или нейронные сети могут работать лучше, чем хороший профессионал. Кроме этих людей, которые собираются забрать все деньги у других путем продажи таких продуктов, никто больше на этом не заработает. Если бы это действительно было так, то зачем мне писать эти статьи с объяснениями? Или почему некоторые люди, не менее осведомленные в том, как работают данные механизмы, должны объяснять, как они работают? Это бессмысленно. Они могут просто молчать и зарабатывать деньги. Но на практике всё происходит иначе. По этой причине, вам лучше забыть о том, что вы создадите нейронную сеть и, не имея никаких знаний, заработайте деньги, просто собирая коде то тут, то там.

Однако ничто не мешает вам создать небольшую нейронную сеть, цель которой - помочь вам принимать решения, покупать, продавать или даже визуализировать некоторые аспекты рынка. Возможно ли это сделать? Да, конечно. На самом деле, изучив всё нужное, вы медленно и с большими усилиями и самоотдачей, но сможете обучить нейронную сеть этому. Но, как я только что сказал, вам придется потрудиться. Однако это вполне возможно.

У нас есть первый нейрон. Но прежде чем прийти в восторг, давайте лучше посмотрим, как он устроен. Для наглядности посмотрите на рисунок ниже.

На этом изображении видно, что реализовано в нашем нейроне. Обратите внимание: у него есть вход и выход. Этот единственный вход получает вес. Возможно, это не так уж и полезно, ведь какой смысл в присутствии входа и выхода? Хорошо, я понимаю ваше скептическое отношение к этому, но вы можете не знать (это зависит от того, насколько хорошо вы разбираетесь в различных темах), что в цифровой электронике есть схема, которая имеет вход и выход. На самом деле их два. Один из них - инвертор, а другой - буфер. Оба являются неотъемлемыми частями еще более сложных компонентов, и этот нейрон может узнать, как работают обе схемы. Для этого нужно просто его обучать, и для обучения, достаточно изменить матрицу обучения на одну из тех, что показаны ниже.

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define macroRandom (rand() / (double)SHORT_MAX)
//+------------------------------------------------------------------+
double Train[][2] {
                    {0, 1},
                    {1, 0},
                  };
//+------------------------------------------------------------------+
const uint nTrain = Train.Size() / 2;
const double eps = 1e-3;
//+------------------------------------------------------------------+

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


Обратите внимание, что значение веса отрицательно, и это означает, что мы будем инвестировать в входящее значение. Другими словами, у нас есть инвертор. Используя приведенный ниже фрагмент, мы получим другой выход.

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define macroRandom (rand() / (double)SHORT_MAX)
//+------------------------------------------------------------------+
double Train[][2] {
                    {0, 0},
                    {1, 1},
                  };
//+------------------------------------------------------------------+
const uint nTrain = Train.Size() / 2;
const double eps = 1e-3;
//+------------------------------------------------------------------+

Выходные данные в этом случае можно увидеть на рисунке ниже.


Обратите внимание, что значение веса теперь положительное. И это означает, что мы храним входы непосредственно на выходах. То есть, у нас есть буфер. Посмотрите, как интересно экспериментировать с ним.

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

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


Сигмоидная функция

С этого момента всё, что я вам покажу, будет лишь верхушкой айсберга. Каким бы крутым, веселым, сложным или захватывающим ни казалось программирование, Отныне абсолютно всё будет лишь малой толикой того, что мы можем сделать. Так что с этого момента вы должны становиться более самостоятельными. Я просто хочу направить вас на правильный путь. Не стесняйтесь экспериментировать с предложенным материалом. Ведь, как уже говорилось, единственным ограничивающим фактором будет ваше воображение.

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

Если говорить вкраце, то ниже приведено то же самое уравнение.

Значение < k > - это количество входов, которые могут быть у нашего нейрона. Таким образом, независимо от количества входов, нам нужно лишь добавить столько входов, сколько необходимо для того, чтобы нейрон научился справляться с каждой новой ситуацией. Однако, начиная со второго входа, функция уже не является уравнением прямой линии, а уравнением любой возможной формы. Это необходимо для того, чтобы нейрон мог найти наилучший способ обработки различных видов обучения.

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

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define macroRandom (rand() / (double)SHORT_MAX)
//+------------------------------------------------------------------+
double Train[][3] {
                    {0, 0, 0},
                    {0, 1, 1},
                    {1, 0, 1},
                    {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(((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;

    Print("The Mini Neuron...");
    MathSrand(512);
    w0 = (double)macroRandom;
    w1 = (double)macroRandom;
    bias = (double)macroRandom;

    for (ulong c = 0; (c < 3000) && ((err = Cost(w0, w1, bias)) > eps); c++)
    {
        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);
        PrintFormat("%I64u > w0: %.4f %.4f || w1: %.4f %.4f || b: %.4f %.4f || %.4f", c, 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);
}
//+------------------------------------------------------------------+

При выполнении данного кода вы получите результат, аналогичный изображенному ниже:


Что же здесь не так? Если вы заметили, мы просто добавили в код возможность новых записей, и это сделано хорошо. Но обратите внимание: примерно на десяти тысячах итераций стоимость просто перестает уменьшаться, а если и уменьшается, то очень медленно. Почему? Причина в том, что в нейроне чего-то не хватает. Это не было необходимым при работе с одним входом, но становится крайне важным, когда мы хотим добавить новые. Он также используется при работе со слоями нейронов, что характерно для глубокого обучения, но об этом мы поговорим позже. Пока что давайте сосредоточимся на главном. Нейрон достигает точки стагнации, когда он больше не может снижать расходы. Данная проблема решается добавлением триггерной функции прямо на выходе. Функции и порядок действий здесь во многом зависят от вида задачи, которую мы хотим выполнить. Единого способа решения этой части не существует, ведь можно использовать различные функции активации. Однако обычно используется сигмоид, и причина проста. Данная функция позволяет принимать значения от плюс бесконечности до минус бесконечности в диапазоне от 0 до 1. В некоторых случаях мы изменяем его таким образом, чтобы этот диапазон находился между 1 и -1, но здесь мы используем базовую версию. Сигмоидная функция представлена следующей формулой:

Но как применить это в нашем коде? Это может показаться очень сложным, но на самом деле, всё гораздо проще, чем кажется. В том же самом коде, показанном выше, нужно будет изменить совсем немногое:

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define macroRandom (rand() / (double)SHORT_MAX)
#define macroSigmoid(a) (1.0 / (1 + MathExp(-a)))
//+------------------------------------------------------------------+
double Train[][3] {
                    {0, 0, 0},
                    {0, 1, 1},
                    {1, 0, 1},
                    {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;

    Print("The Mini Neuron...");
    MathSrand(512);
    w0 = (double)macroRandom;
    w1 = (double)macroRandom;
    bias = (double)macroRandom;

    for (ulong c = 0; (c < ULONG_MAX) && ((err = Cost(w0, w1, bias)) > eps); c++)
    {
        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);
        PrintFormat("%I64u > w0: %.4f %.4f || w1: %.4f %.4f || b: %.4f %.4f || %.4f", c, 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);
}
//+------------------------------------------------------------------+

И когда мы запустим код, показанный выше, результат будет похож на это:


Интересно, что для достижения ожидаемого результата в пределах погрешности потребовалось 2 630 936 итераций, что совсем не плохо. У вас может сложиться впечатление, что программа начинает работать немного медленно, так как мы используем только центральный процессор. Но это впечатление связано с тем, что мы печатаем сообщение на каждой итерации кода. Мы можем сделать код немного быстрее, если изменим данный способ отображения на новый метод. В то же время мы добавим небольшой код для проверки возможностей нейрона. Итоговый код выглядит так:

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define macroRandom (rand() / (double)SHORT_MAX)
#define macroSigmoid(a) (1.0 / (1 + MathExp(-a)))
//+------------------------------------------------------------------+
double Train[][3] {
                    {0, 0, 0},
                    {0, 1, 1},
                    {1, 0, 1},
                    {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;

    Print("The Mini Neuron...");
    MathSrand(512);
    w0 = (double)macroRandom;
    w1 = (double)macroRandom;
    bias = (double)macroRandom;

    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);
    }
    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("Testing the neuron...");
    for (uchar p0 = 0; p0 < 2; p0++)
        for (uchar p1 = 0; p1 < 2; p1++)
            PrintFormat("%d OR %d IS %f", p0, p1, macroSigmoid((p0 * w0) + (p1 * w1) + bias));
}
//+------------------------------------------------------------------+

При запуске данного кода в терминале может появиться следующее:


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


Заключительные идеи

В сегодняшней статье мы начали создавать нечто, в некотором смысле, удивительное. Ведь этот простой и скромный нейрон, который мы смогли запрограммировать с помощью очень небольшого количества кода на MQL5, продемонстрировал свои возможности. Многие говорят, что для данной задачи требуется тысяча и один ресурс, но я надеюсь, что вы поняли, как развивается этот процесс. В всего лишь нескольких статьях я подвел итог длительной работы, проделанной несколькими исследователями. Несмотря на относительную простоту, разработка того, как должны реализовываться вещи, потребовала времени. Настолько, что даже сегодня ведутся исследования, направленные на то, чтобы сделать все эти вычисления более плавными и быстрыми. Здесь мы используем только один нейрон с двумя входами, пятью параметрами и одним выходом. И всё же обратите внимание, что системе требуется некоторое время, чтобы найти нужное уравнение.

Конечно, мы можем использовать OpenCL, чтобы ускорить работу с GPU, но, на мой взгляд, пока рано думать о таком решении. Мы можем пойти еще дальше, прежде чем возникнет необходимость в реальном использовании GPU для вычислений. Однако если есть желание углубиться в нейронные сети, я настоятельно рекомендую вам рассмотреть возможность приобретения графического процессора, поскольку он значительно ускорит работу некоторых видов нейронных сетей.

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

Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/13745

Прикрепленные файлы |
Anexo.mq5 (2.22 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (4)
Multi Dead
Multi Dead | 4 апр. 2025 в 17:50

да, правильно, я только что видел видео на YouTube, обучение ИИ играть flappy bird и другие игры с N.E.A.T метод, у меня появилась идея обучить ИИ для торговли с N.E.A.T, я только что изучил основы нейронной сети, и создал модель с помощью чата GPT, потому что я не знаю, чтобы код. У меня ушло две недели на нормализацию данных и два дня на создание модели, но на обучение ушел всего один час, 1300 генераций, 20 геномов на генерацию, мой 5-летний подержанный ноутбук горел, и когда я подключил модель к MT5, модель была такой агрессивной и так точно предсказывала следующие свечи, но не приносила прибыли. Потому что данные не были нормализованы должным образом, и я до сих пор не понимаю код модели. Но было весело учиться и видеть, как модель предсказывает, и именно поэтому я пришел сюда, чтобы узнать больше об искусственном интеллекте, и вот код NEAT AI


import os
import csv
import math
import random
import json
from datetime import datetime

# -----------------------------------------
# UTILS: Активация и простая передача вперед
# -----------------------------------------
def tanh(x):
    return math.tanh(x)

def forward_pass(genome, inputs):
    """
    genome = {
       'hidden_weights': 2D list,
       'hidden_bias': 1D list,
       'output_weights': list,
       'output_bias': float,
       'fitness': float
    }
    inputs: list of 5 floats
    Returns: single float in (-1, 1)
    """
    hidden_activations = []
    for h in range(len(genome['hidden_bias'])):
        z = sum(inputs[i] * genome['hidden_weights'][h][i] for i in range(len(inputs)))
        z += genome['hidden_bias'][h]
        hidden_activations.append(tanh(z))
    
    z_out = sum(hidden_activations[h] * genome['output_weights'][h] for h in range(len(hidden_activations)))
    z_out += genome['output_bias']
    return tanh(z_out)

def interpret_output(output):
    """
    Convert the Tanh output to discrete values:
      if output >= 0.5  =>  1  (Buy)
      if output <= -0.5 => -1  (Sell)
      otherwise         =>  0  (Hold)
    """
    if output >= 0.5:
        return 1
    elif output <= -0.5:
        return -1
    else:
        return 0

# ------------------------------------------------------
# ГЕНЕТИЧЕСКИЙ АЛГОРИТМ: инициализация, отбор, мутация
# ------------------------------------------------------
def create_random_genome(input_size=5, hidden_size=8):
    return {
        'hidden_weights': [[random.uniform(-1, 1) for _ in range(input_size)]
                           for _ in range(hidden_size)],
        'hidden_bias': [random.uniform(-1, 1) for _ in range(hidden_size)],
        'output_weights': [random.uniform(-1, 1) for _ in range(hidden_size)],
        'output_bias': random.uniform(-1, 1),
        'fitness': 0.0
    }

def mutate(genome, mutation_rate=0.1, mutation_strength=0.5):
    for h in range(len(genome['hidden_weights'])):
        for i in range(len(genome['hidden_weights'][h])):
            if random.random() < mutation_rate:
                genome['hidden_weights'][h][i] += random.uniform(-mutation_strength, mutation_strength)
    for h in range(len(genome['hidden_bias'])):
        if random.random() < mutation_rate:
            genome['hidden_bias'][h] += random.uniform(-mutation_strength, mutation_strength)
    for h in range(len(genome['output_weights'])):
        if random.random() < mutation_rate:
            genome['output_weights'][h] += random.uniform(-mutation_strength, mutation_strength)
    if random.random() < mutation_rate:
        genome['output_bias'] += random.uniform(-mutation_strength, mutation_strength)

def crossover(genome1, genome2):
    child = create_random_genome()  # случайная инициация, будет перезаписана
    for h in range(len(genome1['hidden_weights'])):
        for i in range(len(genome1['hidden_weights'][h])):
            child['hidden_weights'][h][i] = (genome1['hidden_weights'][h][i]
                                             if random.random() < 0.5
                                             else genome2['hidden_weights'][h][i])
    for h in range(len(genome1['hidden_bias'])):
        child['hidden_bias'][h] = (genome1['hidden_bias'][h]
                                   if random.random() < 0.5
                                   else genome2['hidden_bias'][h])
    for h in range(len(genome1['output_weights'])):
        child['output_weights'][h] = (genome1['output_weights'][h]
                                      if random.random() < 0.5
                                      else genome2['output_weights'][h])
    child['output_bias'] = (genome1['output_bias']
                            if random.random() < 0.5
                            else genome2['output_bias'])
    child['fitness'] = 0.0
    return child

def evolve_population(population, keep_best=5):
    population.sort(key=lambda g: g['fitness'], reverse=True)
    new_population = population[:keep_best]  # Сохраните лучшие геномы
    while len(new_population) < len(population):
        parent1 = random.choice(new_population)
        parent2 = random.choice(new_population)
        child = crossover(parent1, parent2)
        mutate(child)
        new_population.append(child)
    return new_population

# -------------------------------------------
# Сохранение и загрузка лучшей модели, чтобы избежать повторного обучения
# -------------------------------------------
def save_best_model(genome, filename="best_model.json"):
    data = {
        'hidden_weights': genome['hidden_weights'],
        'hidden_bias': genome['hidden_bias'],
        'output_weights': genome['output_weights'],
        'output_bias': genome['output_bias'],
        'fitness': genome['fitness']
    }
    with open(filename, 'w') as f:
        json.dump(data, f, indent=2)

def load_best_model(filename="best_model.json"):
    if not os.path.exists(filename):
        return None
    with open(filename, 'r') as f:
        data = json.load(f)
    return data

# ----------------
# ОСНОВНАЯ ЛОГИКА ОБУЧЕНИЯ
# ----------------
def train_neural_network_data(folder_path):
    # Гиперпараметры
    POP_SIZE = 20
    KEEP_BEST = 5
    input_size = 5
    hidden_size = 8

    # Создайте начальную популяцию или загрузите лучшую модель, если она доступна
    population = [create_random_genome(input_size, hidden_size) for _ in range(POP_SIZE)]
    best_saved = load_best_model()
    if best_saved is not None:
        population[0]['hidden_weights'] = best_saved['hidden_weights']
        population[0]['hidden_bias']    = best_saved['hidden_bias']
        population[0]['output_weights'] = best_saved['output_weights']
        population[0]['output_bias']    = best_saved['output_bias']
        population[0]['fitness']        = 0.0

    # Помощник для разбора строк даты/времени ISO8601 с информацией о часовом поясе
    def get_date_from_time_str(t_str):
        dt = datetime.strptime(t_str, "%Y-%m-%dT%H:%M:%S%z")
        return dt.date()

    csv_files = sorted([f for f in os.listdir(folder_path) if f.endswith('.csv')])

    # Для каждого генома сохраните последний сигнал и соответствующую цену (если есть).
    genome_states = [{'last_signal': None, 'last_price': None} for _ in range(POP_SIZE)]
    current_date = None
    generation_count = 0

    # Обрабатывайте каждый CSV-файл по очереди (поток строка за строкой)
    for csv_file in csv_files:
        file_path = os.path.join(folder_path, csv_file)
        with open(file_path, 'r', newline='') as f:
            reader = csv.DictReader(f)
            for row in reader:
                row_time_str = row['Time']
                row_date = get_date_from_time_str(row_time_str)

                # Проверьте, начался ли новый день (конец генерации)
                if current_date is None:
                    current_date = row_date
                elif row_date != current_date:
                    # Генерация завершена; выведите статистику для мониторинга
                    generation_count += 1
                    fitnesses = [g['fitness'] for g in population]
                    avg_fitness = sum(fitnesses) / len(fitnesses)
                    best_fitness = max(fitnesses)
                    print(f"Generation {generation_count} | Date: {current_date} | Avg Fitness: {avg_fitness:.2 f} | Best Fitness: {best_fitness:.2 f}")

                    # Эволюция населения для нового поколения
                    population = evolve_population(population, keep_best=KEEP_BEST)

                    # Сброс состояния генома для нового поколения
                    for state in genome_states:
                        state['last_signal'] = None
                        state['last_price'] = None

                    current_date = row_date

                # Подготовить входные данные на лету
                inputs = [
                    float(row['NTP']),
                    float(row['NCP']),
                    float(row['NT']),
                    float(row['NIP']),
                    float(row['N14IP'])
                ]
                bid_price = float(row['BidOpen'])
                ask_price = float(row['AskOpen'])

                # Обработать решение каждого генома для этого ряда
                for i, genome in enumerate(population):
                    raw_output = forward_pass(genome, inputs)
                    signal_val = interpret_output(raw_output)  # -1, 0 или 1
                    prev_signal = genome_states[i]['last_signal']
                    prev_price = genome_states[i]['last_price']

                    # Пропустить обработку, если сигнал не изменился
                    if signal_val == prev_signal:
                        continue

                    genome_states[i]['last_signal'] = signal_val

                    # Применяйте фитнес-логику на переходах сигналов
                    if signal_val == 1:  # Купить сигнал
                        genome_states[i]['last_price'] = ask_price
                        if prev_signal == -1 and prev_price is not None:
                            if ask_price < prev_price:
                                genome['fitness'] += 1
                            else:
                                genome['fitness'] -= 1
                    elif signal_val == -1:  # Сигнал на продажу
                        genome_states[i]['last_price'] = bid_price
                        if prev_signal == 1 and prev_price is not None:
                            if bid_price > prev_price:
                                genome['fitness'] += 1
                            else:
                                genome['fitness'] -= 1
                    else:  # Сигнал удержания
                        genome_states[i]['last_price'] = None

    # После обработки всех CSV-файлов завершите обучение
    population.sort(key=lambda g: g['fitness'], reverse=True)
    best_genome = population[0]
    print("Final Best genome fitness after all files:", best_genome['fitness'])
    save_best_model(best_genome, filename="best_model.json")

if __name__ == "__main__":
    folder_path = r""
    train_neural_network_data(folder_path)
и для MT5
import MetaTrader5 as mt5
import pandas as pd
import numpy as np
import json
import time

def tournament(values):
    """Tournament method to find maximum and minimum in a list."""
    max_val = values[0]
    min_val = values[0]
    for v in values[1:]:
        if v > max_val:
            max_val = v
        if v < min_val:
            min_val = v
    return max_val, min_val

# -------------------------------
# Загрузите лучшую модель из JSON
with open("best_model.json", "r") as f:
    best_model = json.load(f)

def forward_pass(inputs, model):
    """
    Perform a forward pass through a feedforward network with one hidden layer.
    Uses tanh for both hidden and output layers.
    The output is discretized:
       >0.5 --> 1, < -0.5 --> -1, else 0.
    """
    x = np.array(inputs)  # shape (5,)
    hidden_weights = np.array(model["hidden_weights"])  # shape (8, 5)
    hidden_bias = np.array(model["hidden_bias"])          # shape (8,)
    output_weights = np.array(model["output_weights"])    # shape (8,)
    output_bias = model["output_bias"]                    # скаляр

    hidden_input = np.dot(hidden_weights, x) + hidden_bias
    hidden_output = np.tanh(hidden_input)
    output_val = np.dot(output_weights, hidden_output) + output_bias
    output_activation = np.tanh(output_val)
    
    if output_activation > 0.5:
        return 1
    elif output_activation < -0.5:
        return -1
    else:
        return 0

# -------------------------------
# Функции управления заказами с помощью позиций
def get_open_position(symbol):
    """
    Returns information about an open position for the symbol as a dictionary:
    {"ticket": ticket, "type": "buy"/"sell", "volume": volume}.
    If no position is open, returns None.
    """
    positions = mt5.positions_get(symbol=symbol)
    if positions is None or len(positions) == 0:
        return None
    pos = positions[0]  # предполагать только одну открытую позицию на символ
    if pos.type == mt5.POSITION_TYPE_BUY:
        return {"ticket": pos.ticket, "type": "buy", "volume": pos.volume, "symbol": pos.symbol}
    elif pos.type == mt5.POSITION_TYPE_SELL:
        return {"ticket": pos.ticket, "type": "sell", "volume": pos.volume, "symbol": pos.symbol}
    return None

def close_position(position_info):
    """
    Closes the given position using its ticket.
    """
    symbol = position_info["symbol"]
    tick = mt5.symbol_info_tick(symbol)
    # Для закрытия: если открыта BUY, мы продаем по bid; если открыта SELL, мы покупаем по ask.
    if position_info["type"] == "buy":
        price = tick.bid
        order_type = mt5.ORDER_TYPE_SELL
    else:
        price = tick.ask
        order_type = mt5.ORDER_TYPE_BUY

    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": symbol,
        "volume": position_info["volume"],
        "type": order_type,
        "position": position_info["ticket"],
        "price": price,
        "deviation": 10,
        "magic": 123456,
        "comment": "Close position",
        "type_time": mt5.ORDER_TIME_GTC,
        "type_filling": mt5.ORDER_FILLING_IOC,
    }
    result = mt5.order_send(request)
    print("Close position result:", result)

def place_order(symbol, order_type, volume=0.01):
    """
    Place an order for the symbol.
    - order_type "buy": place BUY order.
    - order_type "sell": place SELL order.
    Before placing, if an open position exists with the opposite signal,
    it is closed using its order ticket.
    """
    # Проверьте текущую открытую позицию:
    current_position = get_open_position(symbol)
    if current_position:
        # Если существующая позиция противоположна новому сигналу, закройте ее.
        if (order_type == "buy" and current_position["type"] == "sell") or \
           (order_type == "sell" and current_position["type"] == "buy"):
            print(f"Opposite position ({current_position['type']}) detected. Closing it first.")
            close_position(current_position)
        # Если тип тот же, ничего не делайте.
        elif (order_type == current_position["type"]):
            print(f"{order_type.upper()} order already open. No new order will be placed.")
            return

    tick = mt5.symbol_info_tick(symbol)
    if order_type == "buy":
        price = tick.ask
        order_type_mt5 = mt5.ORDER_TYPE_BUY
    elif order_type == "sell":
        price = tick.bid
        order_type_mt5 = mt5.ORDER_TYPE_SELL
    else:
        print("Invalid order type:", order_type)
        return

    request = {
        "action": mt5.TRADE_ACTION_DEAL,
        "symbol": symbol,
        "volume": volume,
        "type": order_type_mt5,
        "price": price,
        "deviation": 10,
        "magic": 123456,
        "comment": "NEAT AI trade",
        "type_time": mt5.ORDER_TIME_GTC,
        "type_filling": mt5.ORDER_FILLING_IOC,
    }
    result = mt5.order_send(request)
    print(f"Placed {order_type.upper()} order result:", result)

# -------------------------------
# Инициализация подключения к MetaTrader 5
if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    quit()

symbol = "XAUUSDm"
timeframe = mt5.TIMEFRAME_M1  # 1-минутный таймфрейм

if not mt5.symbol_select(symbol, True):
    print("Failed to select symbol:", symbol)
    mt5.shutdown()
    quit()

# Непрерывный цикл мониторинга
try:
    while True:
        print("\n" + "="*80)
        print("Retrieving candle data...")
        num_candles = 15
        rates = mt5.copy_rates_from_pos(symbol, timeframe, 1, num_candles)
        if rates is None:
            print("Failed to get rates for", symbol)
        else:
            df = pd.DataFrame(rates)
            df['tick_count'] = df['tick_volume']
            df['9ma'] = df['close'].rolling(window=9).mean()
            df['14ma'] = df['close'].rolling(window=14).mean()
            df['TP'] = (df['high'] + df['low'] + df['close']) / 3
            df['NTP']   = (df['TP']    - df['low']) / (df['high'] - df['low'])
            df['NIP9']  = (df['9ma']   - df['low']) / (df['high'] - df['low'])
            df['NIP14'] = (df['14ma']  - df['low']) / (df['high'] - df['low'])
            close_prices = list(df['close'].tail(14))
            HC, LC = tournament(close_prices)
            tick_counts = list(df['tick_count'].tail(14))
            HT, LT = tournament(tick_counts)
            df['NCP'] = (df['close'] - LC) / (HC - LC)
            df['NT']  = (df['tick_count'] - LT) / (HT - LT)
            df = df.round(3)
            
            # Вывести нормализованные данные последней свечи
            display_cols = ['high', 'low', 'close', 'tick_count', '9ma', '14ma', 'TP', 'NTP', 'NCP', 'NT', 'NIP9', 'NIP14']
            print("\nNormalized Candle Data (Last Candle):")
            print(df[display_cols].tail(1).to_string(index=False))
            
            # Извлечение входных данных из последней свечи
            last_row = df.iloc[-1]
            input_vector = [
                last_row['NTP'],
                last_row['NCP'],
                last_row['NT'],
                last_row['NIP9'],
                last_row['NIP14']
            ]
            
            print("\nExtracted Inputs:")
            print(f"NTP = {last_row['NTP']}, NCP = {last_row['NCP']}, NT = {last_row['NT']}, NIP9 = {last_row['NIP9']}, NIP14 = {last_row['NIP14']}")
            print("Price Data - High:", last_row['high'], "Low:", last_row['low'], "Close:", last_row['close'])
            
            # Вычислить сигнал сети
            signal = forward_pass(input_vector, best_model)
            print("\nNetwork Signal (discretized):", signal)
            
            # Выполнение сделки на основе сигнала с использованием нашей логики управления ордерами
            if signal == 1:
                print("Received BUY signal.")
                place_order(symbol, "buy", volume=0.01)
            elif signal == -1:
                print("Received SELL signal.")
                place_order(symbol, "sell", volume=0.01)
            else:
                print("Signal is neutral. No action taken.")
        
        # Подождите до следующей минутной границы
        tick = mt5.symbol_info_tick(symbol)
        if tick is None:
            print("Failed to get tick info for", symbol)
            break
        server_time = tick.time
        next_minute = ((server_time // 60) + 1) * 60
        sleep_seconds = next_minute - server_time
        print(f"Sleeping for {sleep_seconds} seconds until next candle...")
        time.sleep(sleep_seconds)
except KeyboardInterrupt:
    print("Stopping continuous monitoring.")
finally:
    mt5.shutdown()
Irán Triay Hernández
Irán Triay Hernández | 6 апр. 2025 в 02:10
Очень хорошо.
CODE X
CODE X | 6 апр. 2025 в 13:57
Multi Dead нейронных сетей и создал модель с помощью чата GPT, потому что не умею программировать. У меня ушло две недели на нормализацию данных и два дня на создание модели, но на обучение ушел всего час, 1300 поколений, 20 геномов на поколение, мой 5-летний подержанный ноутбук горел, и, когда я подключил модель к MT5, модель была очень агрессивной и очень точно предсказывала следующие свечи, но она не приносила прибыли. Но было интересно учиться и видеть предсказания модели, поэтому я и пришел сюда, чтобы узнать больше об искусственном интеллекте, и вот код NEAT AI


и для МТ5

Нейронные сети - это действительно интересная и увлекательная тема. Однако я взял паузу в объяснении, так как решил сначала доработать реплейсер/симулятор. Однако как только я закончу публиковать статьи по симулятору, мы вернемся с новыми статьями о нейронных сетях. Цель всегда состоит в том, чтобы показать, как они работают под капотом. Большинство людей думают, что это волшебные коды, что не так. Но это все равно интересная и занимательная тема. Я даже подумываю смоделировать что-нибудь, чтобы все могли увидеть, как нейронная сеть обучается без наблюдения или предварительных данных. Это очень интересно и может помочь в понимании некоторых вещей. Деталь: весь мой код будет написан на MQL5. И раз уж вы сказали, что вы не программист. Как насчет того, чтобы выучить MQL5 и начать реализовывать свои собственные решения? Я пишу серию статей, ориентированных на таких людей, как вы. Последнюю из них можно посмотреть здесь: https: //www.mql5.com/pt/articles/15833. В этой серии я объясняю все с самых азов. Так что если вы совсем ничего не знаете о программировании, вернитесь к первой статье цикла. Ссылки на предыдущие статьи всегда будут находиться в начале статьи.

Andreas Alois Aigner
Andreas Alois Aigner | 9 апр. 2025 в 09:16
Большое спасибо за это представление!
Возможности Мастера MQL5, которые вам нужно знать (Часть 21): Тестирование с данными экономического календаря Возможности Мастера MQL5, которые вам нужно знать (Часть 21): Тестирование с данными экономического календаря
Данные экономического календаря по умолчанию недоступны для тестирования с помощью советников в тестере стратегий. Мы рассмотрим, как базы данных могут помочь обойти это ограничение. В частности, мы увидим, как можно использовать базы данных SQLite для архивирования новостей Экономического календаря, чтобы советники, собранные с помощью Мастера, могли использовать их для генерации торговых сигналов.
Нейросети в трейдинге: Анализ рыночной ситуации с использованием Трансформера паттернов Нейросети в трейдинге: Анализ рыночной ситуации с использованием Трансформера паттернов
В анализе рыночной ситуации нашими моделями ключевым элементом является свеча. Тем не менее давно известно, что свечные паттерны могут помочь в прогнозировании будущих ценовых движений. И в этой статье мы познакомимся с методом, который позволяет интегрировать оба этих подхода.
Разработка системы репликации (Часть 49): Все усложняется (I) Разработка системы репликации (Часть 49): Все усложняется (I)
В этой статье мы немного усложним ситуацию. Используя то, что было показано в предыдущих статьях, мы начнем открывать доступ к файлу шаблона, чтобы пользователь мог использовать свой собственный шаблон. Однако я буду вносить изменения постепенно, так как также буду дорабатывать индикатор, чтобы снизить нагрузку на MetaTrader 5.
Построение экономических прогнозов: потенциальные возможности Python Построение экономических прогнозов: потенциальные возможности Python
Как использовать экономические данные Всемирного банка для прогнозирования? Что будет если совместить модели ИИ и экономику?