Реализация модели гауссовской смеси в MQL5
Введение
Модель смеси нормальных распределений (Gaussian Mixture Model, GMM) — один из самых ярких и классических примеров машинного обучения без учителя (unsupervised learning). Это параметрическая вероятностная модель, предполагающая, что наблюдаемые данные порождены взвешенной суммой нескольких гауссовых распределений.

где:
- πk — коэффициенты смешивания k-го компонента смеси (∑πk = 1),
- μk — вектор математического ожидания k-го компонента,
- Σk — ковариационная матрица k-го компонента,
- N — многомерная нормальная плотность.
В отличие от классических алгоритмов кластеризации (например, k-means), GMM не просто делит данные на группы, а моделирует вероятностную структуру данных. Благодаря этому каждая точка может в разной степени принадлежать сразу нескольким кластерам.
Реальные данные не всегда укладываются в одно нормальное распределение. Часто они образуют несколько групп (кластеров) с разными центрами, формами и ориентацией в пространстве. В качестве примера на рис. 1 схематически изображены три нормальные компоненты в двухмерном пространстве.

Рис. 1 Смесь трех нормальных распределений в двухмерном пространстве: a) изолинии постоянной плотности для каждой из компонент смеси (значения коэффициентов смешивания показаны под каждым компонентом), b) изолинии маргинальной плотности вероятности p(x) распределения смеси
Описать такую структуру данных с помощью одного распределения было бы сложно, тогда как гауссовская смесь справляется с этой задачей весьма успешно.
На сегодняшний день в открытом доступе нет реализации модели гауссовской смеси на языке MQL5. В данной статье мы попытаемся разобраться в самом устройстве модели, рассмотрим заложенные в нее предпосылки и напишем класс CGMM на языке MQL5. Это позволит решать задачи кластеризации, многомерного моделирования плотности вероятности и генерации данных непосредственно в MetaTrader 5, без необходимости постоянно возвращаться к Python.
Ключевые особенности реализации:
- Поддержка двух режимов обучения: MLE (максимальное правдоподобие) и MAP (максимальная апостериорная вероятность),
- Инициализация параметров с помощью K-Means++ (через интеграцию с библиотекой Alglib) или случайным способом,
- Работа с полными ковариационными матрицами, что позволяет улавливать любые линейные взаимосвязи между признаками,
- По аналогии с библиотекой scikit-learn реализованы методы жесткой (Predict) и мягкой кластеризации (PredictProba), генерации синтетических выборок (Sample), расчета логарифмического правдоподобия (ScoreSamples), а также информационных критериев (AIC/BIC) для подбора оптимального числа кластеров.
В следующих разделах мы разберем математическую основу модели, особенности поиска оптимальных параметров с помощью EM-алгоритма и сравним результаты вычислений нашей модели с эталонной реализацией GaussianMixture из библиотеки scikit-learn.
Смесь нормальных распределений в терминах латентных переменных
Предположим, у нас есть выборка из N независимых и одинаково распределенных наблюдений {х, …, xN}, которую можно представить в виде матрицы X размера N×D, где n-я строка — это вектор xn. Наша задача описать эти данные с помощью модели смеси нормальных распределений. Геометрическая форма и свойства такой смеси полностью определяются набором параметров θ = (π, μ, Σ):
- π = (π1, …, πk),
- μ = (μ1, …, μk),
- Σ = (Σ1, …, Σk).
Чтобы найти эти параметры, мы должны максимизировать логарифм функции правдоподобия:

Из-за суммы по компонентам внутри логарифма аналитически получить оценки параметров невозможно. Чтобы обойти эту трудность, модель GMM представляют в терминах латентных переменных. Это позволяет использовать для обучения ЕМ-алгоритм и находить удобные аналитические формулы для вычисления параметров. Для этого вводят К-мерную бинарную переменную z (вектор), имеющую представление 1 из К, где znk = 1, если n-е наблюдение порождено k-м компонентом, и 0 — в противном случае.
Такое представление модели удобно изобразить в виде вероятностного графа (рис.2)

Рис. 2 GMM, представленная в виде графа для набора из N данных x с соответствующими скрытыми переменными z
На графе:
- Светлый кружок - скрытые переменные zn,
- Черный кружок - наблюдаемые переменные xn,
- Параметры модели показаны без кружков (это не случайные величины, а константы, хоть и не известные).
Модель предполагает, что все наблюдения независимы (i.i.d.). Это хорошо видно на графе: каждое наблюдение xn зависит только от своей скрытой переменной zn и не зависит от других наблюдений.
Примечание:Если скрытая переменная zn зависит от своего предыдущего значения zn-1, то такая модель называется Скрытой Марковской Моделью (HMM). О том, что из себя представляет эта модель, вы можете прочитать в моей предыдущей статье, где рассматривается важный частный случай категориальной (дискретной) модели наблюдений.
EM – алгоритм для GMM
EM-алгоритм (Expectation-Maximization) — это итеративный метод нахождения оценок параметров (MLE/MAP) в вероятностных моделях со скрытыми переменными. Основная идея заключается в переходе от оптимизации сложного маргинального правдоподобия (прямое дифференцирование которого аналитически невозможно) к максимизации математического ожидания логарифма совместного распределения p(x, z), что существенно упрощает расчеты.
Рассмотрим ключевые распределения, необходимые для понимания работы EM-алгоритма в частном случае модели GMM.
Априорное распределение скрытой переменной z:

Условное распределение наблюдений х при известной скрытой переменной z:

Совместное распределение р(х, z) задается выражением р(х, z) = p(x|z)p(z), а маргинальное распределение х (то, что мы и называем GMM) получается суммированием совместного распределения по всем возможным состояниям z:

Таким образом мы пришли к эквивалентной формулировке гауссовской смеси, но уже в терминах скрытых переменных.
Еще одно важное распределение — это апостериорные вероятности принадлежности каждой точки к компонентам — так называемые ответственности (responsibilities) γ(znk). Их можно найти, используя теорему Байеса:

Обратите внимание, что это не аппроксимация: это абсолютно точно вычисленное распределение. Не во всех вероятностных моделях со скрытыми состояниями его можно так просто посчитать.
Теперь введем понятие полного множества данных — {X, Z}. Если бы мы знали истинные значения скрытых переменных, то логарифм правдоподобия полных данных (complete-data log-likelihood) имел бы простой вид:

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

Отсюда и название первого шага EM-алгоритма - expectation (математическое ожидание). Подставив апостериорные вероятности в эту формулу, получаем:

Вспомогательную функцию Q еще называют нижней границей маргинального правдоподобия (ELBO – evidence lower bound). Доказывается, что если максимизировать эту нижнюю границу, то тем самым мы максимизируем и маргинальное правдоподобие.
В частном случае модели смеси можно взять производные вспомогательной функции Q по параметрам модели и получить удобные аналитические формулы для обновления этих параметров на M-шаге EM-алгоритма. Такой простой M-шаг удается получить не всегда, что приводит к различным обобщениям EM-алгоритма.
Таким образом EM-алгоритм состоит из следующих повторяющихся шагов:
- Выбираем начальные значения параметров θ_old,
- E-шаг (Expectation) — вычисляем апостериор скрытых переменных,
- M-шаг (Maximization) — максимизируем функцию Q и получаем обновленные параметры θ_new,
- Проверяем сходимость логарифмической функции правдоподобия. Если критерий сходимости не выполняется, то θ_new → θ_old и возвращаемся к шагу 2.
Оценка параметров по методу максимального правдоподобия ( MLE )
Цель метода максимального правдоподобия — найти такие параметры модели θ = {π, μ, Σ}, при которых наблюдаемые данные X обладают наибольшей вероятностью. Математически эта задача сводится к максимизации логарифма функции правдоподобия.
После инициализации начальных параметров и выполнения Е-шага — расчета апостериорных вероятностей скрытых состояний γ(z), которые из-за дискретности латентных переменных удобно представлять в виде матрицы (таблицы чисел), — алгоритм переходит к М-шагу. На этом этапе оптимизация параметров существенно упрощается, так как мы работаем со вспомогательной функцией Q, чьи производные легко вычисляются в явном аналитическом виде.
Математическое ожидание μk:

Здесь Nk можно интерпретировать как фактическое количество точек, назначенных кластеру k. Математическое ожидание μk для k-го гауссовского компонента определяется путем вычисления взвешенного среднего значения по всем точкам данных. Весовой коэффициент для каждой точки xn определяется апостериорной вероятностью γ(znk) того, что за порождение xn несет ответственность компонент k.
Ковариация Σk:

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

Это просто отношение числа точек, распределенных в k-й кластер, к общему количеству наблюдений в выборке.
Оценка параметров по методу максимальной апостериорной вероятности (MAP)
Метод максимального правдоподобия часто сталкивается с численной нестабильностью, особенно при высокой размерности данных (D > 20) или небольших выборках. Основная проблема — появление вырожденных или плохо обусловленных ковариационных матриц.
Хорошим решением является переход от MLE к MAP-оценке. При этом мы остаёмся в рамках того же EM-алгоритма, но добавляем априорные распределения на параметры модели. Поскольку главная нестабильность возникает именно в ковариационных матрицах, изменения затрагивают только формулу их обновления на M-шаге. E-шаг остаётся полностью без изменений.
В качестве сопряженного априорного распределения для математического ожидания и ковариации рассматривают нормальное обратное распределение Вишарта (NIW). Благодаря свойству сопряженности априора, нет необходимости программировать эту сложную плотность распределения. Весь байесовский вывод на практике сводится к слегка модифицированной оценке MLE на M-шаге:

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

где, sd – эмпирическая дисперсия признака d, вычисленная по всей обучающей выборке.
- η (eta) — параметр информативности априорного распределения Вишарта. Самое слабое информативное распределение получается, если положить η = D + 2 (чем больше η, тем сильнее регуляризация).
- Nk - фактическое количество точек, назначенных кластеру k,
- D - размерность пространства признаков,
- K - количество компонентов смеси.
Класс CGMM
Вся описанная выше математика реализована в виде класса CGMM.
//+------------------------------------------------------------------+ //| GMM.mqh | //| Eugene | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Eugene" #property link "https://www.mql5.com" #include <Math\Stat\Math.mqh> #include <Math\Stat\Normal.mqh> #include <Math\Alglib\alglib.mqh> //--- тип оценки параметров модели enum ENUM_EM_MODE { MLE, MAP }; enum ENUM_INIT_MODE { KMEANS, // Инициализация центров с помощью K-Means++ (Alglib) RANDOM // Инициализация центров случайными числами }; //+------------------------------------------------------------------+ //| Класс Смеси Гауссовых Распределений (Gaussian Mixture Model) | //+------------------------------------------------------------------+ class CGMM { private: int m_k; // Количество кластеров (K) ulong m_dim; // Размерность признаков (D) bool m_is_fitted; // Флаг успешного обучения модели vector m_pi; // Веса кластеров (априоры) [K] matrix m_mu; // Средние значения кластеров [K][D] matrix m_sigma[]; // Матрицы ковариации [K][D x D] ENUM_EM_MODE m_estimate; // Режим оценки параметров (MLE или MAP) ENUM_INIT_MODE m_init_mode; // Режим инициализации средних (KMEANS или RANDOM) double m_threshold; // Порог схождения EM int m_max_iters; // Максимальное число итераций //--- Гиперпараметры для MAP оценки matrix m_S; // Масштабная матрица априорного распределения double m_eta; // Степени свободы априорного распределения void Initialize_parameters(const matrix &X); bool e_step(const matrix &X, matrix &responsibilities); // Е-шаг EM алгоритма bool m_step(const matrix &X, const matrix &responsibilities); // М-шаг EM алгоритма double log_likelihood(const matrix &X); // Функция правдоподобия данных X public: vector logliks; // история правдоподобия int n_iter_; // количество итераций выполненных EM алгоритмом CGMM(int components, ENUM_EM_MODE estimate=MLE, double tol=1e-5, int max_iter=100,ENUM_INIT_MODE mode = KMEANS); ~CGMM(void) {}; // Методы позволяющие установить стартовые значения параметров перед обучением модели bool SetInitialWeights(const vector &pi); bool SetInitialMeans(const matrix &means); bool SetInitialCovars(const matrix &covars[]); // Главные методы bool Fit(const matrix &X,bool verbose=false); // Обучение модели vector Predict(const matrix &X); // Вектор меток кластеров [N] matrix PredictProba(const matrix &X); // Матрица апостериорных вероятностей кластеров [N x K] bool Sample(const int n_samples, matrix &X, vector &labels); // Генерация выборок vector ScoreSamples(const matrix &X); // Вектор логарифма плотности вероятности [N] double AIC(const matrix &X); // Информационный критерий Акаике double BIC(const matrix &X); // Байесовский информационный критерий // Геттеры для доступа к параметрам модели vector GetWeights(void) { return m_pi; } matrix GetMeans(void) { return m_mu; } void GetCovars(matrix &out_sigma[]); };Основные методы класса:
- Fit(const matrix &X, bool verbose) — Обучает модель на данных X с помощью EM-алгоритма. Параметр verbose позволяет выводить ход обучения (значения логарифма правдоподобия),
- Predict(const matrix &X) — Выполняет жёсткую кластеризацию (Hard Clustering). Возвращает вектор размера N с индексами кластеров (от 0 до K-1) для каждой точки,
- PredictProba(const matrix &X) — Выполняет мягкую кластеризацию (Soft Clustering). Возвращает матрицу размером N×K с апостериорными вероятностями принадлежности каждой точки к каждому компоненту. Сумма по каждой строке равна 1,
- ScoreSamples(const matrix &X) — Возвращает вектор логарифма маргинального правдоподобия для каждого наблюдения,
- Sample(const int n_samples, matrix &X, vector &labels) — Генерирует синтетические данные вместе с истинными метками компонентов,
- AIC(const matrix &X) и BIC(const matrix &X) — информационные критерии Акаике и Байесовский. Используются для выбора оптимального количества кластеров.
Перед запуском EM-алгоритма необходимо задать начальные значения параметров. Инициализация параметров, если она не была задана пользователем вручную, происходит автоматически с помощью приватного метода Initialize_parameters(const matrix &X).
//+------------------------------------------------------------------+ //| Инициализация параметров | //+------------------------------------------------------------------+ void CGMM::Initialize_parameters(const matrix &X) { m_dim = X.Cols(); // --- 1. Инициализация весов (pi) --- //--- Если веса не заданы пользователем заранее, заполняем их равномерно if(m_pi.Size() != (ulong)m_k) { m_pi.Init(m_k); m_pi.Fill(1.0 / m_k); } // --- 2. Инициализация средних (mu) --- //--- Если центры кластеров не заданы, вычисляем их через K-Means++ или RANDOM if(m_mu.Rows() != (ulong)m_k || m_mu.Cols() != m_dim) { m_mu.Init((ulong)m_k, m_dim); if(m_init_mode == KMEANS) { CClusterizerState s; CKmeansReport rep; CMatrixDouble x; x = X; CAlglib::ClusterizerCreate(s); CAlglib::ClusterizerSetPoints(s, x, 2); // 2 - Евклидова метрика CAlglib::ClusterizerSetKMeansLimits(s, 1, 0); // 1 рестарт, без лимита итераций CAlglib::ClusterizerRunKMeans(s, m_k, rep); // Запуск на m_k кластеров if(rep.m_terminationtype > 0) m_mu = rep.m_c.ToMatrix(); // скопировали центры из K-Means++ else m_init_mode = RANDOM; // Если Alglib выдал ошибку, переключаемся на случайный старт } if(m_init_mode == RANDOM) { vector zero_mean = vector::Zeros(m_dim); matrix identity = matrix::Identity(m_dim, m_dim); CMVN::rnd(zero_mean, identity, m_k, m_mu); // Случайные центры } } // --- 3. Инициализация матриц ковариации (Sigma) --- // Если матрицы не заданы, берем дисперсию признаков всей выборки if(ArraySize(m_sigma) != m_k) { ArrayResize(m_sigma, m_k); vector var_ = X.Var(0); matrix diag_variance; diag_variance.Diag(var_); // Создаем диагональную матрицу дисперсий for(int i = 0; i < m_k; i++) m_sigma[i] = diag_variance; } }
- Коэффициенты смешивания - инициализируются равномерно,
- Центры кластеров
Это наиболее важный этап, так как качество работы EM-алгоритма сильно зависит от удачного выбора начальных центров. Класс поддерживает два режима инициализации:
- KMEANS (рекомендуемый) — центры вычисляются с помощью алгоритма K-Means++ (библиотека Alglib). Этот метод значительно снижает вероятность попадания в неудачный локальный максимум и ускоряет обучение модели.
- RANDOM — центры генерируются случайно из многомерного нормального распределения с нулевым математическим ожиданием и единичной ковариационной матрицей.
- Ковариационные матрицы - инициализируются одинаково для всех компонентов как диагональные матрицы, где на главной диагонали стоят эмпирические дисперсии каждого признака, рассчитанные по всей выборке X.
Метод обучения модели — Fit()
//+------------------------------------------------------------------+ //| Обучение модели | //+------------------------------------------------------------------+ bool CGMM::Fit(const matrix &X, bool verbose=false) { m_dim = X.Cols(); m_is_fitted = false; // --- расчет гиперпараметров S и eta при использовании оценки MAP --- if(m_estimate == MAP) { m_eta = (double)m_dim + 2.0; vector var = X.Var(0); m_S.Init(m_dim, m_dim); m_S.Fill(0.0); double k_correction = MathPow((double)m_k, 1.0 / (double)m_dim); for(ulong d = 0; d < m_dim; d++) { m_S[d, d] = var[d] / k_correction; } } // --- Инициализация параметров--- Initialize_parameters(X); // --- Основной цикл EM-алгоритма --- logliks.Init(m_max_iters); logliks.Fill(0.0); double LL = log_likelihood(X); double prev_LL = 0.0; matrix responsibilities; int iter = 0; if(verbose) Print(" Iteration LogLikelihood"); while(iter < m_max_iters) { prev_LL = LL; if(!e_step(X, responsibilities)) // E-шаг return false; if(!m_step(X, responsibilities)) // M-шаг return false; LL = log_likelihood(X); logliks[iter] = LL; if(verbose) PrintFormat(" %6d %15.5f", iter + 1, LL); iter++; n_iter_ = iter; if(MathAbs(LL / prev_LL - 1.0) < m_threshold) { if(verbose) PrintFormat("EM converged at %d iterations. Final Log-L value: %.5f", iter, LL); m_is_fitted = true; break; } } if(iter >= m_max_iters) { m_is_fitted = true; if(verbose) Print("Max number of EM iterations reached. Try different init parameters or increase max_iter."); } logliks.Resize(iter); return m_is_fitted; }
Если пользователем выбран режим оценки параметров MAP, автоматически рассчитываются гиперпараметры S и eta. После инициализации параметров запускается цикл EM-алгоритма. Проверяется условие остановки: если относительное изменение логарифма правдоподобия становится меньше порога m_threshold (по умолчанию 1e-5), обучение завершается. E и M шаги вынесены в отдельные функции.
//+------------------------------------------------------------------+ //| Е-Шаг EM-алгоритма (E-step) | //+------------------------------------------------------------------+ bool CGMM::e_step(const matrix &X, matrix &responsibilities) { ulong N = X.Rows(); responsibilities.Init(m_k, N); // Инициализируем матрицу апостериорных вероятностей [K x N] // Шаг 1: считаем числитель в формуле Байеса для всех K компонентов (pi_k * PDF_k) for(int k = 0; k < m_k; k++) { vector pdf_k; if(!CMVN::pdf(X, m_mu.Row(k), m_sigma[k], pdf_k)) return false; responsibilities.Row(pdf_k * m_pi[k], k); } // Шаг 2: суммируем по столбцам, получая знаменатель формулы Байеса для каждой точки vector likelihood_old = responsibilities.Sum(0); likelihood_old.Clip(1e-300, DBL_MAX); // Защита от деления на ноль для аномальных точек данных // Шаг 3: Нормировка for(int k = 0; k < m_k; k++) responsibilities.Row(responsibilities.Row(k) / likelihood_old, k); return true; }
Результатом E-шага является матрица ответственностей размером K × N.
//+------------------------------------------------------------------+ //| M-Шаг EM-алгоритма (M-step) | //+------------------------------------------------------------------+ bool CGMM::m_step(const matrix &X, const matrix &responsibilities) { ulong N = X.Rows(); for(int k = 0; k < m_k; k++) { vector resp_k = responsibilities.Row(k); double Nk = resp_k.Sum(); //--- 1. Обновляем средние m_mu.Row((resp_k @ X) / Nk, k); //--- 2. Считаем взвешенную Sigma_k matrix W_dk(N, m_dim); for(ulong n = 0; n < N; n++) W_dk.Row((X.Row(n) - m_mu.Row(k))* MathSqrt(resp_k[n]), n); matrix Sigma_k = W_dk.Transpose() @ W_dk; //--- 3. Обновляем матрицы ковариации if(m_estimate == MLE) { m_sigma[k] = Sigma_k / Nk; } else if(m_estimate == MAP) { m_sigma[k] = (m_S + Sigma_k) / (m_eta + Nk + m_dim + 2.0); } //--- 4. Обновляем веса m_pi[k] = Nk / (double)N; } return true; }
Результат M-шага – это обновленные параметры модели.
После успешного завершения обучения модель готова к использованию: можно выполнять оценку принадлежности точки данных к конкретному кластеру, генерировать синтетические данные или оценивать качество модели с помощью критериев AIC/BIC.
//+------------------------------------------------------------------+ //| Предсказание кластеров (Hard Clustering) | //+------------------------------------------------------------------+ vector CGMM::Predict(const matrix &X) { vector labels(X.Rows()); if(!m_is_fitted) { Print("Ошибка: Модель не обучена!"); return labels; } matrix resp = PredictProba(X); // матрица вероятностей размера [N x K] labels = resp.ArgMax(1); // ArgMax(1) находит индекс максимального значения в каждой строке return labels; // Возвращает вектор размера [N], содержащий номера кластеров (0, 1, ... K-1) } //+------------------------------------------------------------------+ //| Мягкое предсказание кластеров (Soft Clustering) | //| Возвращает матрицу размера [N x K], где: | //| N - количество точек данных (строк) | //| K - количество кластеров (вероятности для каждого компонента) | //+------------------------------------------------------------------+ matrix CGMM::PredictProba(const matrix &X) { matrix resp(X.Rows(),m_k); if(!m_is_fitted) { Print("Ошибка: Модель не обучена!"); return resp; } e_step(X, resp); // [K x N] return resp.Transpose(); // [N x K] } //+------------------------------------------------------------------+ //|Маргинальное правдоподобие | //+------------------------------------------------------------------+ double CGMM::log_likelihood(const matrix &X) { //--- Возвращает логарифм плотности вероятности для всех данных выборки X return ScoreSamples(X).Sum(); // суммируем вектор логарифмов } //+------------------------------------------------------------------+ //| Log-Likelihood для каждого наблюдения | //+------------------------------------------------------------------+ vector CGMM::ScoreSamples(const matrix &X) { ulong N = X.Rows(); matrix joint_density(m_k, N); // Матрица совместной плотности p(x,z) размера [K x N] //--- Шаг 1: Вычисляем совместную плотность p(x,z) = p(x|z) * p(z) for(int k = 0; k < m_k; k++) { vector pdf_k; if(!CMVN::pdf(X, m_mu.Row(k), m_sigma[k], pdf_k)) Print("Ошибка в CMVN::pdf при расчете компонента ", k); //--- Умножаем условную плотность p(x|z) на априорную вероятность скрытого состояния p(z) joint_density.Row(pdf_k * m_pi[k], k); } //--- Шаг 2: Маргинализация — суммируем по всем состояниям z, получая маргинальную плотность p(x) vector likelihood = joint_density.Sum(0); // p(xn) = p(xn,z=0) + p(xn,z=1) + ⋯ + p(xn,z=K−1) likelihood.Clip(1e-300, DBL_MAX);// Защита от получения логарифма бесконечности для аномальных точек //--- Шаг 3: Берем натуральный логарифм от плотности вероятности likelihood = MathLog(likelihood); return likelihood; // Возвращает вектор логарифмов правдоподобия размера [N] }
Функция маргинального правдоподобия вычисляется для контроля сходимости EM-алгоритма. Для удобства и лучшей совместимости с интерфейсом библиотеки scikit-learn вычисление правдоподобия разделено на два метода. ScoreSamples возвращает вектор логарифмических плотностей вероятности для каждого наблюдения, а метод log_likelihood — суммарное маргинальное логарифмическое правдоподобие по всей выборке.
Реализация многомерного нормального распределения (Класс CMVN)
Реализация модели смеси нормальных распределений была бы технически невозможна без методов для работы с многомерным нормальным распределением (Multivariate Normal Distribution, MVN). Поскольку встроенных средств для работы с многомерными распределениями в MQL5 пока нет, был написан класс CMVN.
//+------------------------------------------------------------------+ //| Многомерное нормальное распределение | //+------------------------------------------------------------------+ class CMVN { public: //+------------------------------------------------------------------+ //| Генерация случайных векторов (генерация выборки) | //| INPUT: | //| Mu - вектор математических ожиданий размера [d] | //| Sigma - ковариационная матрица размера [d x d] | //| N - количество генерируемых наблюдений | //| OUTPUT: | //| R - матрица сгенерированных данных [N x d] | //+------------------------------------------------------------------+ static bool rnd(const vector &Mu, const matrix &Sigma, const int N, matrix &R) { ulong d = Sigma.Rows(); if(N < 1 || d < 1) return false; if(Mu.Size() != d || Sigma.Cols() != d || !Sigma.IsSymmetric()) return false; //--- Разложение Холецкого matrix L; if(!Sigma.FactorizationCholesky(L)) { Print("Ошибка mvnrnd: Матрица ковариации не является положительно определенной"); return false; } int total_elements = (int)(N * d); double rand_array[]; if(!MathRandomNormal(0.0, 1.0, total_elements, rand_array)) return false; matrix Z(N, d); for(int n = 0; n < N; n++) { vector row_vec(d); for(ulong j = 0; j < d; j++) { row_vec[j] = rand_array[n * d + j]; } Z.Row(row_vec, n); } //--- Линейное преобразование и добавление математического ожидания R = Z @ L.Transpose(); for(int n = 0; n < N; n++) R.Row(R.Row(n) + Mu, n); return true; } //+------------------------------------------------------------------+ //| Расчет плотности вероятности (Probability Density Function) | //| INPUT: | //| X - матрица данных размера [N x D] | //| Mu - вектор математических ожиданий размера [D] | //| Sigma - ковариационная матрица размера [D x D] | //| OUTPUT: | //| y - вектор плотности вероятности размера [N] | //+------------------------------------------------------------------+ static bool pdf(const matrix &X, const vector &Mu, const matrix &Sigma, vector &y) { ulong N = X.Rows(); ulong D = X.Cols(); // --- Проверки размеров --- if(N < 1 || D < 1) { Print("Ошибка mvnpdf: Матрица X пуста"); return false; } if(Mu.Size() != D) { Print("Ошибка mvnpdf: Размер вектора средних должен соответствовать количеству столбцов X"); return false; } if(Sigma.Rows() != D || Sigma.Cols() != D) { Print("Ошибка mvnpdf: Матрица ковариации должна быть квадратной размера D x D"); return false; } // --- Проверка на симметричность --- if(!Sigma.IsSymmetric()) { Print("Ошибка mvnpdf: Матрица ковариации должна быть симметричной"); return false; } // --- Разложение Холецкого matrix L; // Нижнетреугольная матрица L (Sigma = L * L^T) if(!Sigma.FactorizationCholesky(L)) { Print("Ошибка mvnpdf: Матрица ковариации должна быть положительно определенной"); return false; } // --- Центрирование данных (X0 = X - Mu) matrix X0(N, D); for(ulong n = 0; n < N; n++) X0.Row(X.Row(n) - Mu, n); // --- Решение треугольной системы уравнений (LAPACK TRTRS) matrix B = X0.Transpose(); // Размер D x N matrix X_sol; // Решение размера D x N if(!L.LinearEquationsSolutionTriangular(EQUATIONSFORM_N, B, X_sol)) { Print("Ошибка mvnpdf: Не удалось решить треугольную систему уравнений"); return false; } // --- Вычисление логарифма квадратного корня из определителя vector diagL = L.Diag(); double logSqrtDetSigma = 0.0; logSqrtDetSigma = MathLog(diagL).Sum(); // --- Вычисление квадратичной формы vector quadform = (X_sol * X_sol).Sum(0); // --- Итоговый расчет плотности--- //--- Считаем вектор логарифмов плотностей vector logPdf = -0.5 * quadform - logSqrtDetSigma - (D * MathLog(2.0 * M_PI)) / 2.0; //--- Берем экспоненту от каждого элемента вектора logPdf y = MathExp(logPdf); return true; } };
Класс содержит две функции, необходимые для работы GMM:
- pdf — расчёт плотности вероятности многомерного нормального распределения,
- rnd — генерация случайных векторов из многомерного нормального распределения.
При разработке класса CMVN основной акцент был сделан на производительности, поскольку функция плотности вероятности вызывается многократно на каждой итерации EM-алгоритма. Для этого используются методы из высокопроизводительной библиотеки линейной алгебры OpenBLAS:
- FactorizationCholesky — разложение Холецкого матрицы ковариации и проверки ее на положительную определенность (аналог LAPACK POTRF),
- LinearEquationsSolutionTriangular — решение треугольных систем линейных уравнений(аналог LAPACK TRTRS) для ускоренного расчета квадратичной формы без явного вычисления обратной матрицы.
Сравнение результатов обучения с моделью scikit-learn
Для проверки корректности реализации класса CGMM проведено сравнение с моделью GaussianMixture из библиотеки scikit-learn (Python).
В качестве тестового набора использован классический датасет Old Faithful («Старый Служака») — данные об извержениях гейзера в Йеллоустонском национальном парке США. Набор содержит 272 наблюдения и две переменные:
- длительность извержения (в минутах),
- время до следующего извержения (в минутах).
На рис. 3 хорошо видно, что данные имеют два чётко выраженных кластера.

Рис. 3 Зависимость времени до следующего извержения от продолжительности текущего извержения (датасет Old Faithful)
Данные были загружены, стандартизированы и переданы в модель с числом компонентов K = 2. Обучение происходило в режиме MLE.
//+------------------------------------------------------------------+ //| Learning.mq5 | //| Eugene | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Eugene" #property link "https://www.mql5.com" #property version "1.00" #include <GMM\GMM.mqh> #include <Graphics\Graphic.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { // 1. Загрузка датасета Old Faithful (272 наблюдения, 2 признака) matrix X; CSVtoMatrix("GMM/OldFaithful.csv",X); ulong N = X.Rows(); ulong D = X.Cols(); // 2. Нормализация данных vector mean_ = X.Mean(0); vector std_ = X.Std(0); matrix X_norm(N, D); for(ulong i = 0; i < N; i++) X_norm.Row((X.Row(i) - mean_) / std_, i); // 3. Создаем модель int components = 2; CGMM gmm_model(components,MLE); // CGMM gmm_model(components,MAP, 1e-5, 100); // 4. Инициализация параметров через сеттеры // --- Начальные веса --- vector init_pi = {0.5, 0.5}; gmm_model.SetInitialWeights(init_pi); // --- Начальные центры средних --- matrix init_means = {{-1.5, 1.5}, {1.5, -1.5}}; gmm_model.SetInitialMeans(init_means); // --- Начальные матрицы ковариации (0.1 * Identity) --- matrix m = {{0.1, 0.0}, {0.0, 0.1}}; matrix init_covars[2]; init_covars[0] = m; init_covars[1] = m; gmm_model.SetInitialCovars(init_covars); // 5. Запуск обучения if(gmm_model.Fit(X_norm,true)) { Print("=== The model has been successfully trained! ==="); // 1. Веса после обучения vector final_pi = gmm_model.GetWeights(); Print("Final weights (pi): ", final_pi); // 2. Средние и матрицы ковариации matrix final_means = gmm_model.GetMeans(); matrix final_covars[]; gmm_model.GetCovars(final_covars); for(int k = 0; k < components; k++) { PrintFormat("--- Cluster %d ---", k); Print("Mean (mu): ", final_means.Row(k)); Print("Covariance (Sigma):\n", final_covars[k]); } Print("------------------------------------------------"); //--- Нарезаем подвыборку из первых 5 точек matrix first_five(5, D); for(ulong i=0; i<5; i++) first_five.Row(X_norm.Row(i), i); // --- Hard clustering vector predicted_labels = gmm_model.Predict(first_five); Print("Predicted clusters for the first 5 points:\n ", predicted_labels); // --- Soft clustering matrix predicted_probas = gmm_model.PredictProba(first_five); Print("Probability matrix [5 x 2] (rows - data, columns - cluster probabilities):\n", predicted_probas); vector score_samples = gmm_model.ScoreSamples(X_norm); //Print("Сумма лог-правдоподобия (Total Log-L): ", score_samples.Sum()); // --- Информационные критерии Print("================================================"); Print("=== INFORMATION CRITERIA OF THE MODEL ==="); double aic_value = gmm_model.AIC(X_norm); double bic_value = gmm_model.BIC(X_norm); PrintFormat("AIC (Akaike Information Criterion): %.5f", aic_value); PrintFormat("BIC (Bayesian Information Criterion): %.5f", bic_value); Print("================================================"); } else { Print("Error: Failed to train GMM model"); } VisualizeGMMTraining(gmm_model); } //+------------------------------------------------------------------+ //| Визуализация кривой обучения GMM | //+------------------------------------------------------------------+ void VisualizeGMMTraining(CGMM &gmm_model) { int plotLimit = (int)gmm_model.logliks.Size(); double x[], y_[]; ArrayResize(x, plotLimit); ArrayResize(y_, plotLimit); for(int i = 0; i < plotLimit; i++) { x[i] = i; y_[i] = gmm_model.logliks[i]; } ChartSetInteger(0, CHART_SHOW, false); CGraphic graphic; ulong width = ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); ulong height = ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); graphic.Create(0, "GMM_Training_Plot", 0, 0, 0, int(width), int(height)); CCurve *curve = graphic.CurveAdd(x, y_, ColorToARGB(clrDodgerBlue, 255), CURVE_LINES, "LogLikelihood"); graphic.BackgroundMainColor(ColorToARGB(clrBlack, 255)); graphic.BackgroundMainSize(24); graphic.BackgroundMain("GMM EM Training Progress"); graphic.XAxis().Name("Iteration"); graphic.XAxis().NameSize(18); graphic.YAxis().Name("Log Likelihood"); graphic.YAxis().NameSize(18); graphic.CurvePlotAll(); graphic.Update(); Sleep(10 * 1000); ChartSetInteger(0, CHART_SHOW, true); graphic.Destroy(); ChartRedraw(0); } //+------------------------------------------------------------------+
После установки начальных параметров (в данном примере они были заданы вручную) запускаем обучение с помощью метода Fit(). Для вывода в журнал значений маргинального правдоподобия в ходе обучения, установим аргумент verbose = true.
На рис. 4 представлены результаты обучения

Рис. 4 Кривая обучения модели GMM для данных Old Faithful
После обучения скрипт выводит в журнал:
- оптимизированные значения параметров,
- финальное значение функции правдоподобия,
- метки кластеров для первых 5 точек (Hard Clustering),
- матрицу вероятностей для первых 5 точек (Soft Clustering),
- значение информационных критериев AIC/BIC.
Для проверки корректности работы нашего класса, мы сравним полученные результаты с эталонной библиотекой scikit-learn. Для этого был создан скрипт scikit.py, в котором использовались те же данные, то же количество компонентов, аналогичная инициализация и те же настройки обучения.
import numpy as np from sklearn.mixture import GaussianMixture import matplotlib.pyplot as plt # 1. Читаем датасет Old Faithful file_path = r"C:\Program Files\MetaTrader 5\MQL5\Files\GMM\OldFaithful.csv" X = np.genfromtxt(file_path, delimiter=',') # 2. Нормализация данных X_mean = X.mean(axis=0) X_std = X.std(axis=0) X_norm = (X - X_mean) / X_std # 3. Стартовые параметры init_pi = np.array([0.5, 0.5]) init_mu = np.array([[-1.5, 1.5], [1.5, -1.5]]) init_Sigma = np.array([np.identity(2) * 0.1, np.identity(2) * 0.1]) # 4. Настройка и обучение Scikit-Learn GMM gmm = GaussianMixture( n_components=2, covariance_type="full", tol=1e-5, max_iter=100, weights_init=init_pi, means_init=init_mu, precisions_init=np.linalg.inv(init_Sigma), # Scikit требует матрицы точности verbose=0 ) gmm.fit(X_norm) # Переводим средний Log-L в суммарный total_log_likelihood = gmm.score(X_norm) * X_norm.shape[0] print(f"=== Scikit-Learn: EM сошелся за {gmm.n_iter_} итераций ===") print("Финальный Log-L:", total_log_likelihood) print("Финальные веса (pi):", gmm.weights_) for i in range(2): print(f"\n--- Кластер {i} ---") print("Среднее (mu):", gmm.means_[i]) print("Ковариация (Sigma):\n", gmm.covariances_[i]) print("\n" + "-"*48) # 5. Срез первых 5 точек для предсказания first_five = X_norm[:5] # Получаем жесткие метки (кластеры 0 или 1) predicted_labels = gmm.predict(first_five) print("Предсказанные кластеры для первых 5 точек:") print(predicted_labels) # Получаем вероятности (размер [5 x 2]) predicted_probas = gmm.predict_proba(first_five) print("\nМатрица вероятностей [5 x 2] (строки - данные, столбцы - кластеры):") print(np.round(predicted_probas, 10)) score_samples = gmm.score_samples(X_norm) #print("score_samples:") #print(score_samples) # --- Информационные критерии --- print("\n" + "="*48) print("=== ИНФОРМАЦИОННЫЕ КРИТЕРИИ МОДЕЛИ ===") aic_value = gmm.aic(X_norm) bic_value = gmm.bic(X_norm) print(f"AIC (Akaike Information Criterion): {aic_value:.5f}") print(f"BIC (Bayesian Information Criterion): {bic_value:.5f}") print("="*48) # ============================================================================== # ГРАФИК: Исходные данные # ============================================================================== plt.figure(figsize=(6, 4)) plt.scatter(X[:, 0], X[:, 1], c='blue', s=35, alpha=0.7, edgecolors='k', label='Data Old Faithful') plt.title("Old Faithful") plt.xlabel('Eruption Duration (minutes)') plt.ylabel('Waiting Time (minutes)') plt.grid(True, linestyle=':', alpha=0.6) plt.legend() plt.tight_layout() plt.show() # ============================================================================== # ГРАФИК: Мягкая кластеризация (PredictProba) # ============================================================================== # Получаем вероятности для всей выборки all_probas = gmm.predict_proba(X_norm) proba_cluster_1 = all_probas[:, 1] plt.figure(figsize=(6, 4)) scatter = plt.scatter(X[:, 0], X[:, 1], c=proba_cluster_1, cmap='bwr', s=35, alpha=0.8, edgecolors='k') plt.title("Soft Clustering of Old Faithful Dataset (PredictProba)") plt.xlabel('Eruption Duration (minutes)') plt.ylabel('Waiting Time (minutes)') plt.grid(True, linestyle=':', alpha=0.6) cbar = plt.colorbar(scatter) cbar.set_label('Probability of Cluster 1', rotation=270, labelpad=15) plt.tight_layout() plt.show()
Параметры моделей, правдоподобие, предсказания, информационные критерии полностью совпали с реализацией на MQL5, с погрешностью в пределах 5–6 знаков после запятой.
Результат мягкой кластеризации данных с помощью метода PredictProba представлен на рис. 5

Рис. 5 Мягкая кластеризация с помощью метода PredictProba
Генерация выборок
Модель GMM является порождающей. Поэтому ее можно использовать не только для кластеризации или оценки плотности, но и для генерации новых данных. Для этого в классе CGMM реализован метод Sample().
//+------------------------------------------------------------------+ //| Генерация случайных выборок (траекторий) из обученной модели GMM | //| INPUT: | //| n_samples - количество данных, которые нужно сгенерировать | //| OUTPUT: | //| sample_X - матрица сгенерированных данных [n_samples x D] | //| labels - вектор меток (индексов кластеров) [n_samples] | //+------------------------------------------------------------------+ bool CGMM::Sample(const int n_samples, matrix &sample_X, vector &labels) { if(n_samples <= 0) { Print("Ошибка: Количество выборок должно быть больше 0"); return false; } int K = m_k; int D = (int)m_mu.Cols(); // --- Шаг 1: выбор кластеров для всех N точек --- int cluster_indices[]; ArrayResize(cluster_indices, K); for(int k = 0; k < K; k++) cluster_indices[k] = k; double pi[]; VectorToArray(m_pi,pi); int z[]; ArrayResize(z, n_samples); if(!MathSample(cluster_indices, pi, n_samples, true, z)) { Print("Ошибка : Сбой при вызове MathSample"); return false; } sample_X.Init(n_samples, D); labels.Init(n_samples); for(int s = 0; s < n_samples; s++) labels[s] = (double)z[s]; //--- переносим метки в выходной вектор // --- Шаг 2: Генерация данных пакетами для каждого кластера с помощью mvnrnd for(int k = 0; k < K; k++) { //--- Считаем, сколько точек данных выпало для текущего кластера k int samples_in_cluster = 0; for(int s = 0; s < n_samples; s++) { if(z[s] == k) samples_in_cluster++; } //--- Если для этого кластера есть точки, генерируем их одним пакетом if(samples_in_cluster > 0) { vector current_mu = m_mu.Row(k); matrix cluster_samples; if(!CMVN::rnd(current_mu, m_sigma[k], samples_in_cluster, cluster_samples)) { PrintFormat("Ошибка: Сбой при генерации mvnrnd для кластера %d.", k); return false; } //--- Распределяем сгенерированный пакет по исходным индексам ulong current_row_idx = 0; for(int s = 0; s < n_samples; s++) { if(z[s] == k) { sample_X.Row(cluster_samples.Row(current_row_idx), s); current_row_idx++; } } } } return true; }
Процесс сэмплирования состоит из двух последовательных этапов:
- Выбор скрытого компонента: Для каждого нового наблюдения случайным образом выбирается компонент смеси в соответствии с коэффициентами смешивания π. Для этого используется встроенная функция из стандартной библиотеки статистики MathSample.
- Пакетная генерация координат: Для каждого компонента подсчитывается количество точек, которые должны быть из него сгенерированы. Затем с помощью метода CMVN::rnd() генерируется пакет векторов из соответствующего многомерного нормального распределения. Полученные точки затем распределяются по своим индексам в итоговой матрице sample_X.
На рис. 6 показана выборка 1000 точек на основе параметров полученных после обучения модели GMM на данных Old Faithful (скрипт Sample.mq5).
//+------------------------------------------------------------------+ //| Sample.mq5 | //| Eugene | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Eugene" #property link "https://www.mql5.com" #property version "1.00" #include <GMM\GMM.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { // 1. Создаем объект GMM (2 компонента) CGMM gmm(2); // 2. Настраиваем параметры vector init_pi = {0.35592792679548113, 0.6440720732045188}; gmm.SetInitialWeights(init_pi); matrix init_means = { {-1.27384982, -1.20981836}, {0.70395651, 0.66857136} }; gmm.SetInitialMeans(init_means); matrix m0 = {{0.05337263, 0.02822063}, {0.02822063, 0.18303629}}; matrix m1 = {{0.13083678, 0.06071860}, {0.06071860, 0.19563396}}; matrix init_covars[2]; init_covars[0] = m0; init_covars[1] = m1; gmm.SetInitialCovars(init_covars); // 3. Вызываем генерацию matrix sample_X; vector sample_z; if(gmm.Sample(1000, sample_X, sample_z)) { Print("Выборка успешно сгенерирована"); MatrixToCSV("GMM_Samples.csv", sample_X); VectorToCSV("GMM_Labels.csv", sample_z); } }

Рис. 6 Синтетическая выборка из 1000 точек, сгенерированная моделью GMM
Как видно на рисунке, синтетические данные хорошо воспроизводят структуру исходного датасета Old Faithful (два выраженных кластера с характерной формой и ориентацией). Обратите внимание, что визуализация выполнена в стандартизированном масштабе.
Выбор оптимального количества компонентов смеси (Model Selection)
При работе с реальными данными исследователь, как правило, не знает заранее, сколько скрытых состояний или компонентов смеси присутствует в данных. Увеличение количества компонентов почти всегда приводит к росту значения функции правдоподобия, поскольку модель получает больше степеней свободы и может подстраиваться под шум. Это приводит к такому негативному последствию, такому как переобучение.
Чтобы найти разумный баланс между качеством подгонки модели и её сложностью, используются информационные критерии:
- AIC (Akaike Information Criterion) штрафует модель пропорционально количеству свободных параметров,
- BIC (Bayesian Information Criterion) накладывает более строгий штраф, который также учитывает объём выборки.
Оптимальным считается число компонентов, при котором значение соответствующего критерия минимально.
Скрипт ModelSelection.mq5 демонстрирует процесс выбора оптимального количества компонентов (от 1 до 5) на данных Old Faithful.

Рис. 7 Зависимость логарифма правдоподобия, AIC и BIC от количества компонентов модели
Как видно на рис. 7:
- Логарифм правдоподобия монотонно растёт с увеличением числа компонентов,
- Критерий BIC достигает минимума при двух компонентах,
- Критерий AIC минимален при трёх компонентах.
Такое поведение типично: BIC чаще выбирает более экономные модели, в то время как AIC может допускать чуть более сложные. Финальный выбор оптимального количества компонентов в GMM — это в значительной степени эвристический процесс, опирающийся на экспертную оценку, а не строгая автоматическая процедура. AIC и BIC — это полезные эвристики, а не абсолютные критерии истины. На практике рекомендуется рассматривать результаты обоих критериев одновременно и анализировать поведение модели при разном числе кластеров.
Чтобы запустить скрипт scikit.py, вам понадобится интерпретатор Python и стандартный стек статистических пакетов. О том, как их установить, рассказывается в статье Python + MetaTrader 5: быстрый исследовательский контур для данных, признаков и прототипов.
После установки пакетов поместите файл scikit.py в папку (например, Scripts/GMM).
Запускайте скрипт через MetaEditor:
- в MetaEditor нажмите Файл → Открыть и выберите файл scikit.py из папки GMM,
- после открытия файла нажмите клавишу F7 (или кнопку «Компилировать» на панели инструментов),
- результаты вычислений должны появиться во вкладке Журнал.
Заключение
В рамках данной статьи мы детально разобрали теорию и практическую реализацию модели смеси нормальных распределений на языке MQL5.
Основные преимущества модели GMM:
- Мягкая кластеризация — модель выдаёт не просто номер кластера, а вероятности принадлежности каждой точки ко всем компонентам смеси, что позволяет делать более обоснованные выводы,
- Гибкость — благодаря полным ковариационным матрицам модель способна описывать кластеры любой формы, размера и ориентации в пространстве,
- Качественная инициализация — по умолчанию используется алгоритм K-Means++ (Alglib), который существенно ускоряет сходимость EM-алгоритма, снижая вероятность попадания в неудачные локальные максимумы,
- Генеративность — обученную модель можно использовать для генерации новых синтетических данных, близких по статистическим свойствам к оригинальным,
- Статистическая обоснованность — встроенная поддержка информационных критериев AIC и BIC позволяет объективно выбирать оптимальное количество компонентов и бороться с переобучением.
- Численная устойчивость — поддержка MAP-оценки с априорным нормальным обратным распределением Вишарта значительно повышает стабильность обучения при малых выборках и высокой размерности данных.
Особенно важно, что класс CGMM показывает результаты, полностью сопоставимые с GaussianMixture из scikit-learn, при этом работая непосредственно внутри MetaTrader 5, без внешних зависимостей. Это позволяет переобучать модель тогда, когда вы посчитаете нужным, не фиксируя один и тот же набор параметров на все случаи жизни.
Модель смеси нормальных распределений — это важный шаг от простой кластеризации к настоящему вероятностному подходу и теперь этот инструмент доступен каждому разработчику на MQL5.
Программы, используемые в статье
| # | Имя | Тип | Описание |
|---|---|---|---|
| 1 | GMM.mqh | Включаемый файл | Содержит класс модели гауссовской смеси и класс многомерного нормального распределения |
| 2 | Learning.mq5 | Скрипт | Пример обучения модели |
| 3 | Sample.mq5 | Скрипт | Пример генерации выборок |
| 4 | ModelSelection.mq5 | Скрипт | Пример выбора количества компонентов модели |
| 5 | scikit.py | Скрипт | Пример обучения модели GaussianMixture из библиотеки sklearn |
| 6 | OldFaithful.csv | CSV | Данные в формате csv |
| 7 | MQL5.zip | Архив | Все вышеперечисленные файлы можно найти в архиве |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Создание пользовательских индикаторов в MQL5 (Часть 2): Разработка индикатора RSI в виде шкалы со стрелкой на Canvas
Создание пользовательских индикаторов в MQL5 (Часть 1): Построение трендового индикатора на основе пивотов с градиентной заливкой на Canvas
Встраивание торговой дисциплины в код (Часть 3): Принудительное ограничение торговли на уровне символов с помощью белого списка в MQL5
Моделирование рынка: Position View (VII)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования