Что такое сигмоида в машинном обучении
Перейти к содержимому

Что такое сигмоида в машинном обучении

  • автор:

Выбор слоя активации в нейронных сетях: как правильно выбрать для вашей задачи

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

Что такое слои активации?

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

Как работают слои активации?

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

Сигмойда

Код создания графика сигмойды на matplot

import numpy as np import matplotlib.pyplot as plt # Задаем параметры сигмоиды x = np.linspace(-100, 100, 1000) y = 1 / (1 + np.exp(-x)) # Строим график plt.plot(x, y) # Настраиваем оси координат и заголовок plt.xlabel('x') plt.ylabel('sigmoid(x)') plt.title('График сигмоиды') # Настраиваем значения осей координат plt.xlim(-10, 10) plt.ylim(0, 1) # Строим график, настраиваем ширину линии и добавляем сетку plt.plot(x, y, linewidth=5) plt.grid(True) # Отображаем график plt.show()

Сигмоидная функция активации — это нелинейная функция, которая преобразует входное значение в диапазоне от отрицательной бесконечности до положительной бесконечности в значение от 0 до 1. Эта функция активации часто используется в нейронных сетях для задач бинарной классификации.

Математически сигмоидная функция активации определяется следующим образом:

Графически сигмоидная функция активации выглядит как S-образная кривая, которая монотонно возрастает и имеет асимптоты на 0 и 1. В частности, если x > 0, то f(x) > 0.5, а если x < 0, то f(x) < 0.5. Значение 0.5 достигается при x = 0.

Сигмоидная функция активации используется для преобразования выходного значения нейрона в вероятность, т.е. вероятность того, что входное значение относится к классу 1, если мы работаем с задачей бинарной классификации. Если значение сигмоидной функции близко к 1, то вероятность того, что входное значение относится к классу 1, высока. Если значение близко к 0, то вероятность того, что входное значение относится к классу 1, низкая.

Однако сигмоидная функция активации имеет недостаток, который называется проблемой затухания градиента (vanishing gradient problem). Это означает, что при использовании сигмоидной функции активации в глубоких нейронных сетях градиенты могут становиться очень маленькими, что затрудняет обучение. В таких случаях часто используется другая функция активации, например, ReLU (Rectified Linear Unit).

ReLU

Код создания графика ReLU на matplot

import numpy as np import matplotlib.pyplot as plt # Задаем параметры сигмоиды x = np.linspace(-100, 100, 1000) y = np.maximum(x, 0) # Строим график plt.plot(x, y) # Настраиваем оси координат и заголовок plt.xlabel('x') plt.ylabel('ReLU(x)') plt.title('График функции ReLU') # Настраиваем значения осей координат plt.xlim(-5, 5) plt.ylim(-0.5, 5) # Строим график, настраиваем ширину линии и добавляем сетку plt.plot(x, y, linewidth=5) plt.grid(True) # Отображаем график plt.show()

ReLU (Rectified Linear Unit) — это нелинейная функция активации, которая широко используется в глубоком обучении. Она преобразует входное значение в значение от 0 до положительной бесконечности. Если входное значение меньше или равно нулю, то ReLU выдает ноль, в противном случае — входное значение.

Математически ReLU определяется следующим образом:

где max — функция, возвращающая максимальное значение из двух.

Графически ReLU выглядит как линейная функция с нулевым отсечением на оси абсцисс в точке 0. Это значит, что функция имеет постоянный наклон во всех точках, кроме точки 0, где происходит отсечение.

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

Однако, ReLU имеет некоторые недостатки. Во-первых, при использовании ReLU, некоторые нейроны могут «умереть» (dead neurons), т.е. они могут получить отрицательное значение и оставаться неактивными на всем протяжении обучения. Во-вторых, ReLU несимметрична относительно нуля, поэтому может возникнуть проблема «расслоения» (clustering), когда нейроны могут выдавать только положительные значения. Для решения этих проблем могут быть использованы другие функции активации, такие как Leaky ReLU или ELU.

Leaky ReLU

Код создания графика Leaky ReLU на matplot

import numpy as np import matplotlib.pyplot as plt # Задаем параметры функции Leaky ReLU x = np.linspace(-10, 10, 1000) alpha = 0.1 y = np.where(x > 0, x, alpha*x) # Строим график plt.plot(x, y) # Настраиваем оси координат и заголовок plt.xlabel('x') plt.ylabel('Leaky ReLU(x)') plt.title('График функции Leaky ReLU') # Настраиваем значения осей координат plt.xlim(-5, 5) plt.ylim(-0.5, 5) # Строим график, настраиваем ширину линии и добавляем сетку plt.plot(x, y, linewidth=5) plt.grid(True) # Отображаем график plt.show()

Leaky ReLU (Rectified Linear Unit) — это функция активации, которая используется в нейронных сетях для введения нелинейности в выходные данные каждого нейрона.

Обычный ReLU принимает входные значения и преобразует их, оставляя только положительные значения без изменения, а все отрицательные значения заменяет на 0. Однако у этого метода есть один недостаток, а именно «умирание ReLU». Это происходит в том случае, если входное значение отрицательное, то нейрон не будет активироваться и не будет вносить вклад в выходную функцию.

Для решения этой проблемы был разработан Leaky ReLU. В отличие от ReLU, Leaky ReLU возвращает само значение при положительном входном значении, а при отрицательных значениях возвращает линейную функцию от входа, умноженную на небольшой коэффициент, называемый отрицательным уклоном (leak). Таким образом, у нейрона всегда есть возможность вносить вклад в выходную функцию, даже если входные данные отрицательны.

Формула для Leaky ReLU выглядит следующим образом:

где a (alpha) — отрицательный уклон, который является маленьким положительным числом, например, 0,01.

Преимуществом Leaky ReLU является устойчивость к «умиранию» нейронов и лучшая сходимость в процессе обучения, что приводит к более быстрому и точному обучению нейронных сетей.

ELU

Код создания графика ELU на matplot

import numpy as np import matplotlib.pyplot as plt # Задаем параметры функции ELU x = np.linspace(-10, 10, 1000) alpha = 1.0 y = np.where(x > 0, x, alpha * (np.exp(x) - 1)) # Строим график plt.plot(x, y) # Настраиваем оси координат и заголовок plt.xlabel('x') plt.ylabel('ELU(x)') plt.title('График функции ELU') # Настраиваем значения осей координат plt.xlim(-5, 5) plt.ylim(-2, 5) # Строим график, настраиваем ширину линии и добавляем сетку plt.plot(x, y, linewidth=5) plt.grid(True) # Отображаем график plt.show()

ELU (Exponential Linear Unit) — это функция активации, которая была предложена в 2015 году в статье «Fast and Accurate Deep Network Learning by Exponential Linear Units (ELUs)». Она представляет собой измененную версию ReLU (Rectified Linear Unit), которая помогает ускорить обучение глубоких нейронных сетей и справляется с проблемой «мертвых нейронов» (dead neurons).

ELU определяется следующим образом:

где a (alpha) — это параметр, который может быть установлен в значение 1 по умолчанию.

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

Кроме того, ELU имеет свойство гладкости, которое так же помогает избежать проблемы «взрывающегося градиента» (exploding gradient), которая может возникать при использовании других функций активации, таких как ReLU. Это делает ELU более стабильной и более эффективной функцией активации для обучения глубоких нейронных сетей.

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

SiLU

Код создания графика SiLU на matplot

import numpy as np import matplotlib.pyplot as plt # Задаем параметры функции SiLU x = np.linspace(-10, 10, 1000) y = x * (1 / (1 + np.exp(-x))) # Строим график plt.plot(x, y) # Настраиваем оси координат и заголовок plt.xlabel('x') plt.ylabel('SiLU(x)') plt.title('График функции SiLU') # Настраиваем значения осей координат plt.xlim(-5, 5) plt.ylim(-1.5, 1.5) # Строим график, настраиваем ширину линии и добавляем сетку plt.plot(x, y, linewidth=2) plt.grid(True) # Отображаем график plt.show()

SiLU (Sigmoid-weighted Linear Unit) — это нелинейная функция активации, которая была предложена в 2017 году в статье «Sigmoid-Weighted Linear Units for Neural Network Function Approximation in Reinforcement Learning». SiLU сочетает в себе линейные и нелинейные свойства и имеет ряд преимуществ по сравнению с другими функциями активации.

Функция активации — это нелинейная функция, которая применяется к выходу каждого нейрона в нейронной сети. Она используется для добавления нелинейности в вычисления нейрона и позволяет модели учиться более сложным функциям. Различные функции активации могут влиять на скорость обучения модели, точность и стабильность её предсказаний.

Одной из самых распространенных функций активации является сигмоидная функция, которая представляет собой «сжимающую» функцию и применяется для преобразования значений в диапазон от 0 до 1. Однако, сигмоидная функция имеет некоторые недостатки, включая «затухание градиентов» и «эффект насыщения», что может затруднять обучение нейронных сетей.

SiLU — это функция активации, которая решает проблемы «затухание градиентов» и «эффект насыщения». Она является гладкой, монотонно возрастающей и не имеет «эффекта насыщения» как у сигмойдной функции, что позволяет модели обучаться более эффективно и быстро сходиться к оптимальному решению.

где: σ(x) — функция сигмойды, формула написанная ниже, и подробнее объяснена в пункте выше «Сигмойда».

В области компьютерного зрения SiLU часто используется в сверточных нейронных сетях (CNN), где она может помочь увеличить точность и скорость обучения моделей. Например, её используют в модели YOLOv8. Но, к сожалению, этой модели нет в библиотеки TensorFlow, и использовать её просто так, у вас не получиться.

Гиперболический тангенс

Код создания графика гиперболический тангенс на matplot

import numpy as np import matplotlib.pyplot as plt # Задаем параметры сигмоиды x = np.linspace(-100, 100, 1000) y = np.tanh(x) # Строим график plt.plot(x, y) # Настраиваем оси координат и заголовок plt.xlabel('x') plt.ylabel('tanh(x)') plt.title('График гиперболического тангенса') # Настраиваем значения осей координат plt.xlim(-10, 10) plt.ylim(-1, 1) # Строим график, настраиваем ширину линии и добавляем сетку plt.plot(x, y, linewidth=5) plt.grid(True) # Отображаем график plt.show()

Гиперболический тангенс (tanh) является одной из наиболее распространенных функций активации в нейронных сетях. Он используется как для классификации, так и для регрессии, а также для обработки изображений и других типов данных.

Это функция активации, которая преобразует входные значения в диапазоне от -1 до 1. Формула для вычисления гиперболического тангенса выглядит следующим образом

Гиперболический тангенс очень похож на сигмоидальную функцию, которая также используется в нейронных сетях. Он принимает входные значения и преобразует их в диапазон от -1 до 1, что может использоваться для задач регрессии. Значения, близкие к -1, интерпретируются как отрицательные значения, а значения, близкие к 1, как положительные значения. Значения, близкие к нулю, обрабатываются как нейтральные.

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

Softmax

К сожалению, функция Softmax визуально представляется в виде кривой, что затрудняет ее графическое отображение на графике. Поэтому будет демонстрация на примере заданного вектора.

Код создания функции Softmax с заданным вектором на matplot

import numpy as np import matplotlib.pyplot as plt def softmax(z): # Преобразуем вектор в массив, чтобы избежать ошибок типа "integer division or modulo by zero" z = np.array(z) # Вычисляем экспоненты каждого элемента вектора exp_z = np.exp(z) # Вычисляем сумму экспонент всех элементов вектора sum_exp_z = np.sum(exp_z) # Вычисляем вероятности для каждого элемента вектора softmax_z = exp_z / sum_exp_z return softmax_z # Задаем входной вектор z = [1, 2, 3, 4, 1, 2, 3] # Вычисляем значения Softmax softmax_z = softmax(z) # Выводим значения на экран print("Softmax(z) =", softmax_z) # Строим график вероятностного распределения plt.bar(range(len(z)), softmax_z) plt.title("Softmax Distribution") plt.xlabel("Class") plt.ylabel("Probability") plt.show()

В этом примере мы задаем входной вектор z , затем вычисляем значения с помощью функции softmax() . Затем мы выводим значения на экран и строим график вероятностного распределения. График отображает вероятности для каждого элемента входного вектора в виде столбчатой диаграммы. Более детальные значения, вы можете увидеть ниже:

Softmax(z) = [0.02364054 0.06426166 0.1746813 0.474833 0.02364054 0.06426166 0.1746813 ]

Функция Softmax используется для преобразования вектора значений в вероятностное распределение, которое суммируется до 1. Она особенно полезна в многоклассовой классификации, где необходимо определить вероятности для каждого класса.

Формула функции Softmax выглядит следующим образом:

где z_i — это элемент входного вектора, а k — это общее число элементов в векторе.

График функции Softmax представляет собой гладкую кривую, начинающуюся от 0 и заканчивающуюся на 1, что соответствует сумме вероятностей всех элементов вектора. Кривая функции Softmax имеет свойство, что вероятность любого элемента вектора увеличивается, если значения других элементов уменьшаются, что позволяет использовать эту функцию для многоклассовой классификации.

Хотя функция Softmax имеет множество применений в машинном обучении, она также может иметь недостатки, такие как чувствительность к выбросам и несбалансированным данным, что может приводить к неверным вероятностным оценкам.

Как выбрать подходящий слой активации?

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

  1. Для задач классификации, используйте Softmax, если вы хотите получить вероятности классов в качестве выходных данных. Используйте Sigmoid или Tanh, если вы хотите получить двоичный вывод.
  2. Для задач регрессии, используйте ReLU или его модификации, такие как LeakyReLU или ELU. Эти функции обычно дают лучшую производительность в задачах регрессии.
  3. Для моделей глубокого обучения, ReLU является общим выбором для скрытых слоев, так как она может ускорить обучение, но можно также использовать другие функции, например, PReLU или Swish.
  4. Для рекуррентных нейронных сетей, обычно используются функции активации Tanh.
  5. Если вы не уверены, какую функцию активации использовать, попробуйте использовать несколько функций активации и сравните их производительность на валидационном наборе данных.
  6. Кроме того, при выборе функции активации необходимо учитывать свойства функции, такие как производная, способность обеспечивать нелинейность и способность предотвращать затухание градиента.

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

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

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

Спасибо за прочтение!

  • искуственный интеллект
  • нейронные сети
  • функция активации
  • машинное обучение
  • градиентный спуск
  • затухание нейронов
  • мертвые нейроны
  • взрыв градиента

Практики реализации нейронных сетей

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

Определение:
Аугментация данных (англ. data augmentation) — это методика создания дополнительных данных из имеющихся данных.

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

Рис 1. a) Исходное изображение. b) Отображение по горизонтали. c) Вращение. d) Отступ.
e) Увеличение яркости и контрастности. f) Вырезание.
g) Добавление шума. h) Изменение RGB каналов.

  • Отображение по вертикали или горизонтали (англ. flipping).
  • Поворот изображения на определенный угол (англ. rotation).
  • Создание отступа (англ. padding).
  • Вырезание части изображения (англ. cropping).
  • Добавление шума (англ. adding noise).
  • Манипуляции с цветом (англ. color jittering).

Также, можно применять различные комбинации, к примеру, вырезать часть изображения, повернуть его и изменить цвет фона.

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

Дропаут

Одной из проблем глубокого обучения нейронных сетей является переобучение. И метод дропаут — популярный способ решения этой проблемы, благодаря простому алгоритму и хорошим практическим результатам.

Определение:
Дропаут (англ. dropout) — метод регуляризации нейронной сети для предотвращения переобучения.

В обычной нейронной сети явление переобучения появляется из-за так называемой совместной адаптации (англ. co-adaptation), то есть при обновлении весов нейрона, во время обучения методом обратного распространения ошибки, учитывается деятельность остальных нейронов с целью минимизировать функцию потерь. Поэтому веса нейронов могут меняться, исправляя при этом ошибки других нейронов. Метод дропаута как раз предотвращает эту адаптацию.

Рис 2. a) Стандартная нейронная сеть.
b) Нейронная сеть после применения дропаута.

Алгоритм дропаут

Рассмотрим слой нейронной сети состоящий из [math]H[/math] нейронов. Метод дропаут выключает нейрон с вероятностью [math]p[/math] , соответственно, оставляет включенным с вероятностью [math]q = 1 — p[/math] , причем вероятность выключения любого нейрона сети одинакова.

Пусть [math]a(x)[/math] — функция активации, тогда применение дропаута для [math]i[/math] -ого нейрона выглядит так: [math]U_ = \Theta_a(\sum\limits^_ w_x_ + b)[/math] ,

где вероятность [math]P(\Theta_[/math] [math]=[/math] [math]0)[/math] [math]= p[/math] .

Данная формула применяется на этапе обучения модели. Но так как на этом этапе нейрон остается в сети с вероятностью [math]q[/math] , на этапе тестирования необходимо эмулировать поведение нейронной сети, использованного при обучении. Для этого результат выходного значения функции активации умножается на коэффициент [math]q[/math] , то есть на этапе тестирования: [math]U_ = qa(\sum\limits^_ w_x_ + b)[/math] .

Обратный дропаут

Обратный дропаут (англ. inverted dropout) отличается от обычного тем, что умножение на коэффициент происходит на этапе обучения, причем этот коэффициент равен обратной вероятности того, что нейрон останется в сети: [math]\dfrac1[/math] . А на этапе тестирования выходное значение нейрона остается таким же, как и в методе обратного распространения ошибки.

Таким образом, выходное значение [math]i[/math] -ого нейрона на этапе обучения: [math]U_ = \dfrac1\Theta_a(\sum\limits^_ w_x_ + b)[/math] , на этапе тестирования: [math]U_ = a(\sum\limits^_ w_x_ + b)[/math] .

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

Функции активации

Одним из важнейших аспектов глубокой нейронной сети являются функции активации.

Определение:
Функция активации (англ. activation function) [math]a(x)[/math] определяет выходное значение нейрона в зависимости от результата взвешенной суммы входов и порогового значения.

Рассмотрим нейрон, у которого взвешенная сумма входов: [math]z = \sum\limits_ w_x_ + b[/math] , где [math]w_[/math] и [math]x_[/math] — вес и входное значение [math]i[/math] -ого входа, а [math]b[/math] — смещение. Полученный результат передается в функцию активации, которая решает рассматривать этот нейрон как активированный, или его можно игнорировать.

Рис 3. Ступенчатая функция

Ступенчатая функция

Ступенчатая функция (англ. binary step function) является пороговой функцией активации. То есть если [math]z[/math] больше или меньше некоторого значения, то нейрон становится активированным. Такая функция отлично работает для бинарной классификации. Но она не работает, когда для классификации требуется большее число нейронов и количество возможных классов больше двух.

Рис 4. Линейная функция

Линейная функция

Линейная функция (англ. linear function) представляет собой прямую линию, то есть [math]a(x) = \sum\limits_ c_x_[/math] , а это значит, что результат этой функции активации пропорционален переданному аргументу. В отличии от предыдущей функции, она позволяет получить диапазон значений на выходе, а не только бинарные 0 и 1, что решает проблему классификации с большим количеством классов. Но у линейной функции есть две основных проблемы:

  1. Невозможность использования метода обратного распространения ошибки. Так как в основе этого метода обучения лежит градиентный спуск, а для того чтобы его найти, нужно взять производную, которая для данной функции активации — константа и не зависит от входных значений. То есть при обновлении весов нельзя сказать улучшается ли эмпирический риск на текущем шаге или нет.
  2. Рассмотрим нейронную сеть с несколькими слоями с данной функцией активации. Так как для каждого слоя выходное значение линейно, то они образуют линейную комбинацию, результатом которой является линейная функция. То есть финальная функция активации на последнем слое зависит только от входных значений на первом слое. А это значит, что любое количество слоев может быть заменено всего одним слоем, и, следовательно, нет смысла создавать многослойную сеть.

Главное отличие линейной функции от остальных в том, что ее область значений не ограничена: [math](-\infty; +\infty)[/math] . Следовательно, ее нужно использовать, когда выходное значение нейрона должно [math]\in \mathbb R[/math] , а не ограниченному интервалу.

Рис 5. Сигмоидная функция

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

Сигмоидная функция (англ. sigmoid function), которую также называет логистической (англ. logistic function), является гладкой монотонно возрастающей нелинейной функцией: [math]\sigma(z) = \dfrac1>[/math] . И так как эта функция нелинейна, то ее можно использовать в нейронных сетях с множеством слоев, а также обучать эти сети методом обратного распространения ошибки. Сигмоида ограничена двумя горизонтальными асимптотами [math]y = 1[/math] и [math]y = 0[/math] , что дает нормализацию выходного значения каждого нейрона. Кроме того, для сигмоидной функции характерен гладкий градиент, который предотвращает «прыжки» при подсчете выходного значения. Помимо всего этого, у этой функции есть еще одно преимущество, для значений [math]x \gt 2[/math] и [math]x \lt -2[/math] , [math]y[/math] «прижимается» к одной из асимптот, что позволяет делать четкие предсказания классов.

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

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

Рис 6. Функция гиперболического тангенса

Функция гиперболического тангенса

Функция гиперболического тангенса (англ. hyperbolic tangent) имеет вид: [math]tanh(z) = \dfrac2> — 1[/math] . Эта функция является скорректированной сигмоидной функцей [math]tanh(z) = 2 \cdot sigma(2z) — 1[/math] , то есть она сохраняет те же преимущества и недостатки, но уже для диапазона значений [math](-1; 1)[/math] .

Обычно, [math]tanh[/math] является предпочтительнее сигмоиды в случаях, когда нет необходимости в нормализации. Это происходит из-за того, что область определения данной функции активации центрирована относительно нуля, что снимает ограничение при подсчете градиента для перемещения в определенном направлении. Кроме того, производная гиперболического тангенса значительно выше вблизи нуля, давая большую амплитуду градиентному спуску, а следовательно и более быструю сходимость.

Рис 7. Функция ReLU

Функция ReLU

Rectified Linear Unit — это наиболее часто используемая функция активации при глубоком обучении. Данная функция возвращает 0, если принимает отрицательный аргумент, в случае же положительного аргумента, функция возвращает само число. То есть она может быть записана как [math]f(z)=max(0, z)[/math] . На первый взгляд может показаться, что она линейна и имеет те же проблемы что и линейная функция, но это не так и ее можно использовать в нейронных сетях с множеством слоев. Функция ReLU обладает несколькими преимущества перед сигмоидой и гиперболическим тангенсом:

  1. Очень быстро и просто считается производная. Для отрицательных значений — 0, для положительных — 1.
  2. Разреженность активации. В сетях с очень большим количеством нейронов использование сигмоидной функции или гиперболического тангенса в качестве активационный функции влечет активацию почти всех нейронов, что может сказаться на производительности обучения модели. Если же использовать ReLU, то количество включаемых нейронов станет меньше, в силу характеристик функции, и сама сеть станет легче.

У данной функции есть один недостаток, называющийся проблемой умирающего ReLU [2] . Так как часть производной функции равна нулю, то и градиент для нее будет нулевым, а то это значит, что веса не будут изменяться во время спуска и нейронная сеть перестанет обучаться.

Функцию активации ReLU следует использовать, если нет особых требований для выходного значения нейрона, вроде неограниченной области определения. Но если после обучения модели результаты получились не оптимальные, то стоит перейти к другим функциям, которые могут дать лучший результат.

Рис 8. Функция Leaky ReLU

Функция Leaky ReLU

Одной из проблем стандартного ReLU является затухающий, а именно нулевой, градиент при отрицательных значениях. При использовании обычного ReLU некоторые нейроны умирают, а отследить умирание нейронов не просто. Чтобы решить эту проблему иногда используется подход ReLU с «утечкой» (leak) — график функции активации на отрицательных значениях образует не горизонтальную прямую, а наклонную, с маленьким угловым коэффициентом (порядка 0,01). То есть она может быть записана как [math]\begin f(x) = \begin 0.01x, & \text\ x \lt 0 \\ x, & \text \\ \end \end[/math] . Такое небольшое отрицательное значение помогает добиться ненулевого градиента при отрицательных значениях. Однако, функция Leaky ReLU имеет некоторые недостатки:

  1. Сложнее считать производную, по сравнению со стандартным подходом (так как значения уже не равны нулю), что замедляет работу каждой эпохи.
  2. Угловой коэффициент прямой также является гиперпараметром, который надо настраивать.
  3. На практике, результат не всегда сильно улучшается относительно ReLU.

Стоит отметить, что помимо проблемы умирающих нейронов, у ReLU есть и другая — проблема затухающего градиента [на 03.01.20 не создан] . При слишком большом количестве слоев градиент будет принимать очень маленькое значение, постепенно уменьшаясь до нуля. Из-за этого нейронная сеть работает нестабильно и неправильно. Leaky ReLU (LReLU) решает первую проблему, но в по-настоящему глубоких сетях проблема затухания градиента все еще встречается и при использовании этого подхода.

На практике LReLU используется не так часто. Практический результат использования LReLU вместо ReLU отличается не слишком сильно. Однако в случае использования Leaky требуется дополнительно настраивать гиперпараметр (уровень наклона при отрицательных значениях), что требует определенных усилий. Еще одной проблемой является то, что результат LReLU не всегда лучше чем при использовании обычного ReLU, поэтому чаще всего такой подход используют как альтернатива. Довольно часто на практике используется PReLU (Parametric ReLU), который позволяет добиться более значительных улучшений по сравнению с ReLU и LReLU. Также, в случае параметрической модификации ReLU, угол наклона не является гиперпараметром и настраивается нейросетью.

См. также

  • Нейронные сети, перцептрон
  • Глубокое обучение
  • Настройка глубокой сети

Примечания

  1. ↑Vanishing gradient problem, Wikipedia
  2. ↑Dying ReLU problem, Wikipedia

Источники информации

  • Аугментация данных
  • Аугментация данных с помощью Imgaug package
  • G. E. Hinton, N. Srivastava — Improving neural networks by preventing co-adaptation of feature detectors
  • Wikipedia — Функция активации

Machine Learning – не только нейронки

Нейронные сети и глубокое обучение (deep learning) у всех на слуху, но нейросети – это лишь подобласть такого обширного предмета, как машинное обучение (machine learning). Существует несколько сотен других алгоритмов, которые способны быстро и эффективно решать задачи искусственного интеллекта и в большинстве случаев являются более интерпретируемыми для человека. В этой статье рассмотрим алгоритмы классического машинного обучения, принцип работы нейросетей, подготовку данных для обучения моделей и задачи, которые решают с помощью искусственного интеллекта (ИИ).

Основные задачи машинного обучения

  • Восстановление регрессии (прогнозирования) – построение модели, способной предсказывать численную величину на основе набора признаков объекта.
  • Классификация – определение категории объекта на основе его признаков.
  • Кластеризация – распределение объектов.

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

  • Прогнозирование рейтинга приложения на основе признаков: размер, категория, возрастной рейтинг, жанр и цена – задача регрессии.
  • Определение категории приложения на основе набора признаков: размер, возрастной рейтинг, жанр и цена – задача классификации.
  • Разбиение приложений на группы на основании множества признаков (например, количество отзывов, скачиваний, рейтинга) таким образом, чтобы приложения внутри группы были более похожи друг на друга, чем приложения разных групп.

Нейронные сети (многослойный перцептрон)

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

Принцип работы

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

Нейронные связи в головном мозге.png

Нейронные связи в головном мозге («Создаем нейронную сеть», Тарик Рашид)

Пример искусственной трехслойной нейросети.png

Пример искусственной трехслойной нейросети («Создаем нейронную сеть», Тарик Рашид)

Каждый слой нейросети оперирует разными представлениями о данных. На рисунке ниже можно увидеть пример использованиям глубокого обучения (нейросети) для распознавания образа на картинке. На входной слой нам поступают пиксели изображений, далее после вычислений между входным и первым скрытым слоем мы получаем границы, на втором скрытом слое – контуры, на третьем – части объектов, на выходном – вероятности принадлежности изображения к каждому типу объектов.

pasted-image-0-_2_.png

Пример использования нейросети для распознавания образа ( «Глубокое обучение», Ян Гудфеллоу)

Как настраивать

Настраивается путем задания количества узлов, скрытых слоев и выбора функции активации. В искусственных нейронных сетях функция активации нейрона отвечает за выходной сигнал, который определяется входным сигналом или набором входных сигналов.

Какие задачи решают

Нейросети применяют для решения задач классификации, регрессии и кластеризации.

Классические алгоритмы машинного обучения (Machine Learning)

K-ближайших соседей

Метод K-ближайших соседей – простой и эффективный алгоритм, его можно описать известной поговоркой: «Скажи мне, кто твой друг, и я скажу, кто ты».

Принцип работы

Пусть имеется набор данных с заданными классами. Мы можем определить класс неизвестного объекта, если рассмотрим определенное количество ближайших объектов (k) и присвоим тот класс, который имеет большинство «соседей». Посмотрим на рисунок ниже.

Есть набор точек с двумя классами: синие крестики и красные кружки. Мы хотим определить, к какому классу относится неизвестная зеленая точка. Для этого мы берем k ближайших соседей, в данном случае 3, и смотрим, к каким классам они относятся. Из трех ближайших соседей больше оказалось синих крестиков, соответственно, мы можем предположить, что зеленая точка также, скорее всего, относится к этому классу.

k-bloku-K_blizhayshikh-sosedey.png

Как настраивать

Необходимо подобрать параметр k (количество ближайших соседей) и метрику для измерения расстояний между объектами.

Какие задачи решает

В основном – классификация, но может применяться и для задач регрессии.

Линейная регрессия

Линейная регрессия – простая и эффективная модель машинного обучения, способная решать задачи быстро и недорого.

Принцип работы

Модель линейной регрессии можно описать уравнением y =a0 + a1x1 + a2x2+. +anxn, где x – это значения признаков, y – целевая переменная, a – весовые коэффициенты признаков. При обучении модели весовые коэффициенты подбираются таким образом, чтобы как можно лучше описывалась линейная зависимость признаков от целевой переменной.

Пример: задача предсказания стоимости квартиры в зависимости от площади и удаленности от метро в минутах. Целевой переменной (y) будет являться стоимость, а признаками (x) – площадь и удаленность.

На рисунке ниже также представлен пример построения линейной регрессии. Красная прямая более точно описывает линейную зависимость x от y.

k-bloku-Lineynaya-regressiya.png

Как настраивать

Для многих моделей Machine Learning, в частности и для линейной регрессии, можно улучшить итоговое качество с помощью регуляризации.

Регуляризация в статистике, машинном обучении, теории обратных задач — метод добавления некоторых дополнительных ограничений к условию с целью решить некорректно поставленную задачу или предотвратить переобучение, то есть ситуацию, когда модель хорошо показывает себя на тренировочный данных, но перестаёт работать на новых.

Распространенные методы регуляризации для повышения качества модели линейной регрессии:

  • Ridge — один из методов понижения размерности. Применяется для борьбы с переизбыточностью данных, когда независимые переменные коррелируют друг с другом (мультиколлинеарность), вследствие чего проявляется неустойчивость оценок коэффициентов линейной регрессии.
  • LASSO — также как и Ridge, применяется для борьбы с переизбыточностью данных.
  • Elastic-Net — модель регрессии с двумя регуляризаторами L1, L2. Частными случаями являются модели LASSO L1 = 0 и Ridge регрессии L2 = 0.

Какие задачи решает

Логистическая регрессия

Логистическая регрессия – также простая и эффективная модель машинного обучения, способная решать задачи быстро и недорого.

Принцип работы

Алгоритм логистической регрессии очень похож на алгоритм линейной регрессии. Несмотря на свое название, решает задачу бинарной классификации (классы: 1 и -1). Сумма a1x1 + a2x2+. +anxn проходит через функцию сигмоиды, которая возвращает число от 0 до 1, характеризующее вероятность отнесения объекта к классу 1. Пример: логистическую регрессию часто применяют в задачах кредитного скоринга, когда по определенным данным о клиенте нужно определить, стоит ли выдавать ему кредит.

Illyustratsiya-algoritmov-lineynoy-i-logisticheskoy-regressii.png

Иллюстрация алгоритмов линейной и логистической регрессии (источник)

Как настраивать

Как и в случае с линейной регрессией, существуют реализации с коэффициентом регуляризации можно выбрать один из методов регуляризации: Ridge, LASSO, Elastic-Net.

Какие задачи решает

Метод опорных векторов (SVM)

Принцип работы

Чтобы лучше всего понять алгоритм метода опорных векторов, рассмотрим рисунок. На рисунке приведен пример двух линейно разделимых классов в двумерном пространстве. Идея алгоритма заключается в нахождении оптимальной разделяющей прямой (или гиперплоскости для более высоких пространств) для отделения объектов одного класса от другого. Пунктирные линии выделяют разделяющую полосу и проводятся через объекты, которые называют опорными. Чем шире разделяющая полоса, тем качественнее модель SVM. Чтобы определить класс объекта, достаточно определить, с какой стороны гиперплоскости он находится.

k-bloku-Metod-opornykh-vektorov-_SVM_.png

Как настраивать

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

Какие задачи решает

Применяется для решения задач классификации и регрессии.

Сравнение классических алгоритмов с нейросетью

Для примера был взят датасет со статистикой приложений в Play Market. Датасет содержит следующие данные: размер приложения, возрастной рейтинг, количество скачиваний, жанр, категория и др. На данном датасете были обучены модели: линейная регрессия, метод опорных векторов, нейронная сеть (многослойный перцептрон).

В ходе экспериментов были подобраны следующие параметры для моделей машинного обучения:

  • Линейная регрессия – модели линейной регрессии с регуляризацией не показали результат, превосходящий качество классической линейной регрессии.
  • Метод опорных векторов – модель метода опорных векторов с RBF-ядром показала лучший результат по сравнению с другими ядрами.
  • Многослойный перцептрон – оптимальный результат показала модель с 4 слоями, 300 нейронами и функций активацией ReLu. При попытках увеличить количество слоев и нейронов прирост качества не наблюдался.

Была решена задача прогнозирования потенциального рейтинга приложения в зависимости от его признаков.

Результаты ошибки среднего отклонения от истинного значения целевой переменной в процентах для каждой модели:

  • Линейная регрессия – 6.13 %
  • Метод опорных векторов – 6.01%
  • Нейронная сеть – 6.41%

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

На гистограмме ниже представлены итоговые весовые коэффициенты a, полученные при обучении модели линейной регрессии. Чем больше столбик, тем выше влияние признака на целевую переменную. Если столбик направлен вверх, то он оказывает положительное влияние на рост целевой переменной, если вниз – то отрицательное. Другими словами, если приложение имеет жанр “Other” или “Tools”, то, скорее всего, его рейтинг будет высоким, а если у него категория “FAMILY” или “GAME” – то, вероятно, низким. Данная интерпретация весовых коэффициентов линейной регрессии бывает очень полезной при анализе данных.

Gistogramma-znacheniy-koeffitsientov-lineynoy-regressii.png

Гистограмма значений коэффициентов линейной регрессии

Примеры из нашей практики

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

Познакомьтесь с нашими решениями Machine Learning и другими кейсами в портфолио.

Машинное обучение и Data Science (Часть 02): Логистическая регрессия

Машинное обучение и Data Science (Часть 02): Логистическая регрессия

В первой части мы говорили о линейной регрессии, сейчас же поговорим о логистической, которая представляет собой метод классификации, основанный на линейной регрессии.

Теория. Предположим, мы построили график вероятности зависимости ожирения человека от его веса.

Недостаток линейной регрессии

В этом случае нельзя использовать линейную модель. Будем использовать другую технику для преобразования этой линии в S-кривую, известную как сигмоид.

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

  • 0 или 1
  • Да или нет
  • Правда или ложь
  • Максимум или минимум
  • Покупка или продажа

В нашей библиотеке, которую будем создавать в этой статье, мы будем игнорировать другие дискретные значения. Сосредоточимся только на бинарных значениях (0,1).

Поскольку мы ожидаем значения y между 0 и 1, обрежем нашу линию, ограничив ее значениями 0 и 1. Это можно сделать с помощью следующей формулы:

Сигмовидная формула логистической регрессии

После этого получим такой график

Логистическая регрессия данных по ожирению

Линейная модель передается логистической функции (сигмоид/p) =1/1+е^t, где t является линейной моделью, результатом которой являются значения от 0 до 1. Так представлена вероятность того, что точка данных принадлежит классу.

Вместо использования y линейной модели в качестве зависимой, в качестве нее в функции используется p

p = 1/1+ e ^- (c+m1x1+m2x2+. +mnxn) при наличии нескольких значений

Как я писал ранее, сигмовидная кривая позволяет преобразовывать бесконечные значения в выходной двоичный формат (0 или 1). Но что, если есть точка данных, расположенная на 0,8? Как решить, что значение равно нулю или единице? Для этого будем использовать пороговые значения.

Сигмовидный порог логистической регрессии

Порог указывает вероятность выигрыша или проигрыша, он находится на уровне 0,5 (посередине между 0 и 1).

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

Линейная и логистическая регрессия

Линейная регрессия Логистическая регрессия
Непрерывная переменная Категориальная переменная
Для решения задач регрессии Для решения задач классификации
Модель имеет прямое уравнение Модель имеет логистическое уравнение

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

  1. Сбор и анализ данных
  2. Очистка данных
  3. Проверка точности

1. Сбор и анализ данных

В этом разделе мы будем писать много кода на Python для визуализации наших данных. Для начала нужно импортировать библиотеки, которые мы будем использовать для извлечения и визуализации данных в блокноте Jupyter.

Для построения нашей библиотеки мы будем использовать данные о Титанике, том самом, который затонул в Северной Атлантике 15 апреля 1912 года после столкновения с айсбергом (если вдруг вы не слышали, подробности в Википедии ). Все коды Python и наборы данных можно найти в моем GitHub по ссылке в конце статьи.

Импорт библиотек визуализации данных Python

Поясню, что означают столбцы

survival — выживание (0 = нет; 1 = да)
class — пассажирский класс (1 = Первый; 2 = Второй; 3 = Третий)
name — имя
sex — пол
age — возраст
sibsp — количество сестер и братьев/супругов на борту
parch — количество родителей/детей на борту
ticket — номер билета
fare — пассажирский тариф
cabin — каюта
embarked — порт посадки (C = Шербур; Q = Квинстаун; S = Саутгемптон)

После сбора данных и сохранения их в переменной titanic_data приступим к визуализации данные в столбцах, начиная со столбца выживания.

sns.countplot(x=»Survived», data = titanic_data)

График данных по выжившим на Титанике

Он показывает, что в катастрофе выжило меньшинство пассажиров из всех, кто был на корабле. Большая же часть погибла.

Далее визуализируем количество выживших в зависимости от пола

sns.countplot(x='Survived', hue='Sex', data=titanic_data)

Выжившие на Титанике в зависимости от пола

Я не знаю, что случилось с мужчинами в тот день, но количество выживших женщин вдвое больше, чем мужчин.

Давайте теперь визуализируем число выживших в соответствии с классами пассажиров

sns.countplot(x=’Survived’, hue=’Pclass’, data=titanic_data)

Выжившие на Титанике в зависимости от класса

На корабле было очень много пассажиров третьего класса, но только небольшой процент из них выжил.

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

titanic_data['Age'].plot.hist()

Гистограмма возрастных групп пассажиров Титаника

Ни у последнее — пассажирский тариф на корабле

titanic_data['Fare'].plot.hist(bins=30, figsize=(10,10))

 Гистограмма тарифов пассажиров Титаника

На этом закончим с визуализацией данных. Мы визуализировали только пять столбцов из 12 — самые важные данных. Теперь перейдем к очистке данных.

2. Очистка данных

Здесь очистим наши данные: удалим значения NaN (нет значений) и исключим ненужные столбцы из набора данных.

При использовании логистической регрессии нужно работать со значениями double и integer, а бессмысленные строковые значения надо исключить, поэтому в нашем случае мы игнорируем следующие столбцы:

  • Столбец name (в имени нет значимой информации)
  • Столбец ticket (не имеет никакого смысла для выживания в катастрофе)
  • Столбец cabin (в нем слишком много пропущенных значений, это видно даже в первых 5 строках)
  • Порт посадки (думаю, это не имеет значения)

Для этого откроем файл CSV в WPS office и вручную удалим столбцы. Вы можете использовать любую программу для работы с таблицами по выбору.

После удаления столбцов визуализируем новые данные.

new_data = pd.read_csv(r'C:\Users\Omega Joctan\AppData\Roaming\MetaQuotes\Terminal\892B47EBC091D6EF95E3961284A76097\MQL5\Files\titanic.csv') new_data.head(5)

Очищенные данные о Титанике

Мы очистили данные, хотя у нас все еще есть отсутствующие значения в столбце возраста, не говоря уже о том, что в столбце пола находятся строковые значения. Давайте исправим эти ошибки с помощью кода. Создадим label encoder для преобразования строковых значений male и female в 0 и 1, соответственно.

void CLogisticRegression::LabelEncoder(string &src[],int &EncodeTo[],string members="male,female") < string MembersArray[]; ushort separator = StringGetCharacter(m_delimiter,0); StringSplit(members,separator,MembersArray); //конвертируем список элементов в массив ArrayResize(EncodeTo,ArraySize(src)); //размер массива EncodeTo зададим равным размеру исходного массива int binary=0; for(int i=0;iArraySize(MembersArray);i++) // цикл по элементам массива < string val = MembersArray[i]; binary = i; //значения binary для элементов int label_counter = 0; for (int j=0; jArraySize(src); j++) < string source_val = src[j]; if (val == source_val) < EncodeTo[j] = binary; label_counter++; >> Print(MembersArray[binary]," total string">" Encoded To background-color:rgb(164, 192, 228);">scr[] я также написал функцию для получения данных из определенного столбца в файле CSV, а затем поместил их в массив строковых значений MembersArray[]. Вот как получилось:

void CLogisticRegression::GetDatatoArray(int from_column_number, string &toArr[]) < int handle = FileOpen(m_filename,FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI,m_delimiter); int counter=0; if (handle == INVALID_HANDLE) Print(__FUNCTION__," Invalid csv handle err functions">GetLastError()); else < int column = 0, rows=0; while (!FileIsEnding(handle)) < string data = FileReadString(handle); column++; //--- if (column==from_column_number) //если столбец в цикле совпадает с искомым столбцом < if (rows>=1) //Исключаем первый столбец, который содержит заголовок < counter++; ArrayResize(toArr,counter); toArr[counter-1]=data; > > //--- if (FileIsLineEnding(handle)) < rows++; column=0; > > > FileClose(handle); >

Ниже показано, как правильно вызывать функции и инициализировать библиотеку внутри нашей программы testscript.mq5 :

#include "LogisticRegressionLib.mqh"; CLogisticRegression Logreg; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() < //--- Logreg.Init("titanic.csv",","); string Sex[]; int SexEncoded[]; Logreg.GetDatatoArray(4,Sex); Logreg.LabelEncoder(Sex,SexEncoded,"male,female"); ArrayPrint(SexEncoded); >

После успешного выполнения скрипта будет выведена следующая информация

male total =577 Encoded To = 0

female total =314 Encoded To = 1

[ 0] 0 1 1 1 0 0 0 0 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 0 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 0 1 0 0 1 1 0 0 1 0 1 0 0 1 0 0 0 0 1 0 1 0 0 1 0 0 0

[ 75] 0 0 0 0 1 0 0 1 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 1 1 1 0 0 0 0 1 0 0

[750] 1 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 1 0 1 0 0 1 0 1 1 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 1 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 0 1 0 0 1 0

[825] 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 1 1 1 0 1 0 0 0 1 1 0 1 1 0 0 0 0 1 0 0 1 1 0 0 0 1 1 0 1 0 0 1 0 1 1 0 0

Перед тем как программировать значения обратите внимание на members="male,female" в аргументах функции. Первое значение в строке будет заменено на 0 — в данном случае первым идет столбец male, поэтому ему будет присвоен 0, а значению females будет присвоена 1. Однако функция не ограничивается двумя значениями, вы можете внести столько значений, сколько хотите.

Отсутствующие значения

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

The output will be:

Из 891 строки в 177 строках в нашем столбце Age отсутствуют значения (NAN).

Давайте заменим отсутствующие значения в нашем столбце, заменив значения средним от всех возрастов.

void CLogisticRegression::FixMissingValues(double &Arr[]) < int counter=0; double mean=0, total=0; for (int i=0; iArraySize(Arr); i++) //первый шаг - найти среднее ненулевых значений < if (Arr[i]!=0) < counter++; total += Arr[i]; >> mean = total/counter; //все значения, поделенные на их количество Print("mean ",MathRound(mean)," before Arr"); ArrayPrint(Arr); for (int i=0; iArraySize(Arr); i++) < if (Arr[i]==0) < Arr[i] = MathRound(mean); //замена нулевых значений в массиве > > Print("After Arr"); ArrayPrint(Arr); >

Эта функция находит среднее значение всех ненулевых значений, а затем заменяет все нулевые значения в массиве средним значением.

Вот что покажет запуск скрипта. Как видите, все нулевые значения были заменены на 30.0 — средний возраст всех пассажиров Титаника.

mean 30.0 before Arr

[ 0] 22.0 38.0 26.0 35.0 35.0 0.0 54.0 2.0 27.0 14.0 4.0 58.0 20.0 39.0 14.0 55.0 2.0 0.0 31.0 0.0 35.0 34.0 15.0 28.0 8.0 38.0 0.0 19.0 0.0 0.0

[840] 20.0 16.0 30.0 34.5 17.0 42.0 0.0 35.0 28.0 0.0 4.0 74.0 9.0 16.0 44.0 18.0 45.0 51.0 24.0 0.0 41.0 21.0 48.0 0.0 24.0 42.0 27.0 31.0 0.0 4.0

[870] 26.0 47.0 33.0 47.0 28.0 15.0 20.0 19.0 0.0 56.0 25.0 33.0 22.0 28.0 25.0 39.0 27.0 19.0 0.0 26.0 32.0

[ 0] 22.0 38.0 26.0 35.0 35.0 30.0 54.0 2.0 27.0 14.0 4.0 58.0 20.0 39.0 14.0 55.0 2.0 30.0 31.0 30.0 35.0 34.0 15.0 28.0 8.0 38.0 30.0 19.0 30.0 30.0

[840] 20.0 16.0 30.0 34.5 17.0 42.0 30.0 35.0 28.0 30.0 4.0 74.0 9.0 16.0 44.0 18.0 45.0 51.0 24.0 30.0 41.0 21.0 48.0 30.0 24.0 42.0 27.0 31.0 30.0 4.0

[870] 26.0 47.0 33.0 47.0 28.0 15.0 20.0 19.0 30.0 56.0 25.0 33.0 22.0 28.0 25.0 39.0 27.0 19.0 30.0 26.0 32.0

Построение модели логистической регрессии

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

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

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

Coefficient_of_X() и y_intercept() — эти функции не новые, мы работали с ними в первой статье из этой серии. Рекомендую прочитать ее, чтобы узнать больше об этих функциях и линейной регрессии в целом.

double CLogisticRegression::y_intercept() < // c = y - mx return (y_mean-coefficient_of_X()*x_mean); > //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CLogisticRegression::coefficient_of_X() < double m=0; //--- < double x__x=0, y__y=0; double numerator=0, denominator=0; for (int i=0; iArraySize(m_xvalues); i++) < x__x = m_xvalues[i] - x_mean; //правая часть числителя (сторона x) y__y = m_yvalues[i] - y_mean; //левая часть числителя (сторона y) numerator += x__x * y__y; //сумма произведений двух частей числителя denominator += MathPow(x__x,2); > m = numerator/denominator; > return (m); >

Теперь напишем код логистической модели по формуле.

Формула p

Обратите внимание, что z здесь является логарифмом шансов (log-odds) , потому что инверсия сигмоиды утверждает, что z можно определить как логарифм вероятности 1 (например, "выжил"), поделенной на вероятность 0 (например, "не выжил"):

Формула логарифмических шансов

Получается, y = mx+c (помним это из формулы линейной модели).

Переведя это в код, получим

double y_= (m*m_xvalues[i])+c; double z = log(y_)-log(1-y_); //log loss p_hat = 1.0/(MathPow(e,-z)+1);

Обратите внимание, что мы сделали со значением z. Наша формула log(y/1-y), а в коде записано log(y_)-log(1-y_). Вспоминаете законы логарифмов из курса математики? Деление логарифмов с одинаковым основанием приводит к вычитанию степеней (ссылка).

Это основа нашей модели с запрограммированной формулой. Но внутри функции LogisticRegression() происходит многое. Вот что находится внутри функции:

double CLogisticRegression::LogisticRegression(double &x[],double &y[],int& Predicted[],double train_size_split = 0.7) < int arrsize = ArraySize(x); //размер входного массива double p_hat =0; //сохраним вероятность //--- int train_size = (int)MathCeil(arrsize*train_size_split); int test_size = (int)MathFloor(arrsize*(1-train_size_split)); ArrayCopy(m_xvalues,x,0,0,train_size); ArrayCopy(m_yvalues,y,0,0,train_size); //--- y_mean = mean(m_yvalues); x_mean = mean(m_xvalues); // Обучаем модель в фоновом режиме double c = y_intercept(), m = coefficient_of_X(); //--- А вот и модель логистической регрессии int TrainPredicted[]; double sigmoid = 0; ArrayResize(TrainPredicted,train_size); //изменяем размер массива, чтобы он соответствовал размеру обучающего массива Print("Training starting. train size keyword">for (int i=0; idouble y_= (m*m_xvalues[i])+c; double z = log(y_)-log(1-y_); //log loss p_hat = 1.0/(MathPow(e,-z)+1); double odds_ratio = p_hat/(1-p_hat); TrainPredicted[i] = (int) round(p_hat); //округляем значения, чтобы получить фактические 0 или 1 if (m_debug) PrintFormat("%d Age =%.2f survival_Predicted =%d ",i,m_xvalues[i],TrainPredicted[i]); > //--- Тестируем модель if (train_size_split1.0) //если есть возможность тестирования < ArrayRemove(m_xvalues,0,train_size); //очищаем массив ArrayRemove(m_yvalues,0,train_size); //очищаем массив от обучающих данных ArrayCopy(m_xvalues,x,0,train_size,test_size); //новые значения x начинаются там, где заканчивается обучение ArrayCopy(m_yvalues,y,0,train_size,test_size); //новые значения y начинаются там, где заканчивается тестирование Print("start testing. test size functions">ArrayResize(Predicted,test_size); //изменяем размер массива, чтобы он соответствовал размеру тестового массива for (int i=0; idouble y_= (m*m_xvalues[i])+c; double z = log(y_)-log(1-y_); //log loss p_hat = 1.0/(MathPow(e,-z)+1); double odds_ratio = p_hat/(1-p_hat); TrainPredicted[i] = (int) round(p_hat); //округляем значения, чтобы получить фактические 0 или 1 if (m_debug) PrintFormat("%d Age =%.2f survival_Predicted =%d , Original survival=%.1f ",i,m_xvalues[i],Predicted[i],m_yvalues[i]); > >

Теперь давайте обучим и протестируем нашу модель в скрипте TestScript.mq5.

double Age[]; Logreg.GetDatatoArray(5,Age); Logreg.FixMissingValues(Age); double y_survival[]; int Predicted[]; Logreg.GetDatatoArray(2,y_survival); Logreg.LogisticRegression(Age,y_survival,Predicted);

The output of a successful script run will be:

Training starting. train size=624

0 Age =22.00 survival_Predicted =0

1 Age =38.00 survival_Predicted =0

622 Age =20.00 survival_Predicted =0

623 Age =21.00 survival_Predicted =0

start testing. test size=267

0 Age =21.00 survival_Predicted =0

1 Age =61.00 survival_Predicted =1

265 Age =26.00 survival_Predicted =0

266 Age =32.00 survival_Predicted =0

Great. Теперь наша модель работает, и мы можем как минимум получить от нее результаты. Но делает ли модель хорошие прогнозы?

Нужно проверить ее на точность.

Матрица путаницы

Матрица путаницы, определяющая точность логистической регрессии

Как мы знаем, каждая хорошая и плохая модель может делать предсказания. Я создал файл CSV с прогнозами, которая сделала наша модель, сопоставленными с оригинальными данными из тестирования выживаемости пассажиров. Все так же 1 означает, что пассажир выжил, а 0 означает, что не выжил.

Вот несколько столбцов:

Вычисляем матрицу путаницы, используя:

  • TP — истинно положительный
  • TN — истинно отрицательный
  • FP — ложноположительный
  • FN — ложноотрицательный

Что означают эти значения?

TP (истинно положительный)

Это когда исходное значение положительное (1), и модель также дает положительное предсказание (1)

TN (истинный отрицательный результат)

Это когда исходное значение отрицательное (0), и модель также дает отрицательное предсказание (0)

FP — ложноположительный

Это когда исходное значение отрицательное (0), а модель дает положительное предсказание (1)

FN — ложноотрицательный

Это когда исходное значение положительное (1), а модель дает отрицательное предсказание (0)

Теперь давайте рассчитаем матрицу путаницы для приведенного выше примера.

Матрицу путаницы можно использовать для расчета точности нашей модели с использованием этой формулы.

Из нашей таблицы:

Формула матрицы путаницы для определения точности

Точность = 1 + 5 / 4 + 1 + 2 + 3

В нашем точность составляет 50% ( 0,5 * 100%)

Мы разобрались, как работает матрица путаницы 1X1. Пришло время преобразовать ее в код и проанализировать точность нашей модели на всем наборе данных.

void CLogisticRegression::ConfusionMatrix(double &y[], int &Predicted_y[], double& accuracy) < int TP=0, TN=0, FP=0, FN=0; for (int i=0; iArraySize(y); i++) < if ((int)y[i]==Predicted_y[i] && Predicted_y[i]==1) TP++; if ((int)y[i]==Predicted_y[i] && Predicted_y[i]==0) TN++; if (Predicted_y[i]==1 && (int)y[i]==0) FP++; if (Predicted_y[i]==0 && (int)y[i]==1) FN++; > Print("Confusion Matrix \n ","[ ",TN," ",FP," ]","\n"," [ ",FN," ",TP," ] "); accuracy = (double)(TN+TP) / (double)(TP+TN+FP+FN); >

Вернемся к основной функции в нашем классе — LogisticRegression(). На этот раз мы превратим ее в двойную функцию, которая возвращает точность модели. Также я хочу уменьшить количество методов Print(), а вместо них использовать оператор if. По факту нам не нужно каждый раз выводить значения (если, конечно, это не отладка класса). Все изменения выделены синим цветом:

double CLogisticRegression::LogisticRegression(double &x[],double &y[],int& Predicted[],double train_size_split = 0.7) < double accuracy =0; //Точность обучения/тестовой модели int arrsize = ArraySize(x); //размер входного массива double p_hat =0; //сохраним вероятность //--- int train_size = (int)MathCeil(arrsize*train_size_split); int test_size = (int)MathFloor(arrsize*(1-train_size_split)); ArrayCopy(m_xvalues,x,0,0,train_size); ArrayCopy(m_yvalues,y,0,0,train_size); //--- y_mean = mean(m_yvalues); x_mean = mean(m_xvalues); // Обучаем модель в фоновом режиме double c = y_intercept(), m = coefficient_of_X(); //--- А вот и модель логистической регрессии int TrainPredicted[]; double sigmoid = 0; ArrayResize(TrainPredicted,train_size); //изменяем размер массива, чтобы он соответствовал размеру обучающего массива Print("Training starting. train size keyword">for (int i=0; idouble y_= (m*m_xvalues[i])+c; double z = log(y_)-log(1-y_); //log loss p_hat = 1.0/(MathPow(e,-z)+1); TrainPredicted[i] = (int) round(p_hat); //округляем значения, чтобы получить фактические 0 или 1 if (m_debug) PrintFormat("%d Age =%.2f survival_Predicted =%d ",i,m_xvalues[i],TrainPredicted[i]); > ConfusionMatrix(m_yvalues,TrainPredicted,accuracy); //будьте осторожны, чтобы не перепутать массивы значений обучения printf("Train Model Accuracy =%.5f",accuracy); //--- Тестируем модель if (train_size_split1.0) //если есть возможность тестирования < ArrayRemove(m_xvalues,0,train_size); //очищаем массив ArrayRemove(m_yvalues,0,train_size); //очищаем массив от обучающих данных ArrayCopy(m_xvalues,x,0,train_size,test_size); //новые значения x начинаются там, где заканчивается обучение ArrayCopy(m_yvalues,y,0,train_size,test_size); //новые значения y начинаются там, где заканчивается тестирование Print("start testing. test size functions">ArrayResize(Predicted,test_size); //изменяем размер массива, чтобы он соответствовал размеру тестового массива for (int i=0; idouble y_= (m*m_xvalues[i])+c; double z = log(y_)-log(1-y_); //log loss p_hat = 1.0/(MathPow(e,-z)+1); TrainPredicted[i] = (int) round(p_hat); //округляем значения, чтобы получить фактические 0 или 1 if (m_debug) PrintFormat("%d Age =%.2f survival_Predicted =%d , Original survival=%.1f ",i,m_xvalues[i],Predicted[i],m_yvalues[i]); > ConfusionMatrix(m_yvalues,Predicted,accuracy); printf("Testing Model Accuracy =%.5f",accuracy); > return (accuracy); //Наконец, вернем точность тестирования >

После исполнения скрипта получим такой результат:

Training starting. train size=624

Train Model Accuracy =0.60577

start testing. test size=267

Testing Model Accuracy =0.64045

Ура! Теперь мы можем определить, насколько хороша наша модель с помощью чисел. И хотя точность 64,045% на тестовых данных не такая уж хорошая, чтобы модель можно было использовать для прогнозирования (на мой взгляд), но у нас уже есть библиотека, которая может помочь нам классифицировать данные с помощью логистической регрессии.

Далее пояснения по основной функции:

double CLogisticRegression::LogisticRegression(double &x[],double &y[],int& Predicted[],double train_size_split = 0.7)

Входной параметр train_size_split позволяет разбить данные на учебную и тестовую выборку. По умолчанию набор делится так: 0,7 т.е. 70% данных будут использоваться для обучения, а оставшиеся 30% — для тестирования. Массив Predicted[] вернет прогнозируемые данные тестирования.

Двоичная перекрестная энтропия, или Функция потерь

Так же, как среднеквадратическая ошибка является функцией ошибки для линейной регрессии, двоичная перекрестная энтропия является функцией стоимости для логистической регрессии.

Давайте посмотрим, как это работает в двух случаях использования логистической регрессии, например: когда фактический результат равен 0 и 1.

1. Когда фактический результат равен 1

Рассмотрим модель для двух входных выборок p1 = 0,4 и p2 = 0,6. Ожидается, что штраф на p1 должен быть больше, чем p2, потому что он дальше от 1, чем p1.

С математической точки зрения отрицательный логарифм малого числа есть большое число и наоборот.

Чтобы назначить входным данным штрафы, будем использовать следующую формулу

В этих двух случаях

  • Штраф = -log(0.4)=0.4 т.е. штраф на p1 = 0.4
  • Штраф = -log(0.6)=0.2 т.е. штраф на p2 = 0.2

2. Когда фактический результат равен 0

Рассмотрим выходные данные модели для двух входных выборок, p1 = 0,4 и p2 = 0,6 (как и в предыдущем случае). Ожидается, что p2 должен быть оштрафован больше, чем p1, потому что он дальше от 0, но не забывайте, что выход логистической модели — это вероятность того, что выборка будет положительной. Чтобы оштрафовать входные вероятности, нам нужно найти вероятность того, что выборка будет отрицательной. Это несложно. Вот формула:

Вероятность того, что выборка будет отрицательной = 1-вероятность того, что выборка будет положительной

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

В этих двух случаях

  • Штраф = -log(1-p) = -log(1-0.4) =0.2, т.е. штраф равен 0.2
  • Штраф = -log(1-p) = -log(1-0.6) =0.4, т.е. штраф равен 0.4

Штраф на p2 больше, чем на p1 (работает как положено). Отлично!

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

if input sample is positive y=1:

Однострочное уравнение, эквивалентное приведенному выше оператору блока if-else, может быть записано как

penalty = -( y*log(p) + (1-y)*log(1-p) )

y = фактические значения в наборе данных

p = необработанная прогнозируемая вероятность модели (до округления)

Докажем, что это уравнение эквивалентно приведенному выше оператору if-else.

1. Когда выходные значения y = 1

штраф = -( 1*log(p) + (1-1)*log(1-p) ) = -log(p) следовательно, доказано

2. Когда выходные значения y = 0

штраф = -( 0*log(p) + (1-0)* log(1-p) ) = log(1-p) следовательно, доказано

Наконец, функция логарифмических потерь для N входных выборок выглядит так

Бинарная перекрестная энтропия/логарифмическая функция потерь

Log-loss показывает, насколько близка вероятность предсказания к соответствующему фактическому/истинному значению (0 или 1 в случае бинарной классификации). Чем больше прогнозируемая вероятность отличается от фактического значения, тем выше значение логарифмической потери.

Функции стоимости, такие как log-loss (логарифмическая потеря) и многие другие, могут использоваться в качестве метрики того, насколько хороша модель. Но наибольшая польза — это оптимизация модели для достижения наилучших параметров с использованием градиентного спуска или других алгоритмов оптимизации (мы поговорим об этом в последующих сериях, так что следите за публикациями).

Если что-то можно измерить, это можно улучшить. Это основная цель функций затрат.

Из нашего набора данных для тестирования и обучения видно, что логарифмическая потеря составляет от 0,64 до 0,68, что не идеально (мягко говоря).

Обучающий набор данных

Тестовый набор данных

Вот как мы можем преобразовать нашу функцию log-loss в код

double CLogisticRegression::LogLoss(double &rawpredicted[]) < double log_loss =0; double penalty=0; for (int i=0; iArraySize(rawpredicted); i++ ) < penalty += -((m_yvalues[i]*log(rawpredicted[i])) + (1-m_yvalues[i]) * log(1-rawpredicted[i])); //сумма всех штрафов if (m_debug) printf("penalty =%.5f",penalty); > log_loss = penalty/ArraySize(rawpredicted); //все штрафы поделенные на их количество Print("Logloss keyword">return(log_loss); >

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

Задача множественной динамической логистической регрессии

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

void MultipleRegressionMain(double& predicted_y[],double& Y[],double& A[],double& B[]); void MultipleRegressionMain(double& predicted_y[],double& Y[],double& A[],double& B[],double& C[],double& D[]);

Но этот метод неудобный, кроме того, он нарушает правила чистого кода и принцип DRY (не повторяться) из ООП.

В отличие от Python с гибкими функциями, которые могут принимать большое количество аргументов с помощью *args и **kwargs, в MQL5, насколько я знаю, то же самое можно сделать, только если использовать строки. Думаю, это наша отправная точка.

void CMultipleLogisticRegression::MLRInit(string x_columns="3,4,5,6,7,8")

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

Мы могли бы динамически создавать файлы CSV и использовать их как массивы, но это сделает программы более дорогими в плане использования ресурсов компьютера по сравнению с использованием массивов. Особенно это будет заметно при работе с множеством данных, не говоря уже о циклах while, которые мы будем часто использовать для открытия файлов. Все это будет замедлять весь процесс. Но я не уверен в этом на 100%, так что поправьте меня, если я ошибаюсь.

Все же упомянутый способ можно использовать.

И я нашел способ использовать массивы — будем хранить все данные из всех столбцов в одном массиве, а затем использовать необходимые данные из этого единственного массива.

 int start = 0; if (m_debug) //если режим отладки, выводим каждый массив против его строки for (int i=0; iArrayCopy(EachXDataArray,m_AllDataArray,0,start,rows_total); start += rows_total; Print("Array Number string">" From column number ",m_XColsArray[i]); ArrayPrint(EachXDataArray); >

Внутри цикла for проходим по данным и выполняем все необходимые вычисления для модели по всем столбцам. Я пробовал этот метод, но все еще не достиг успеха. Причина, по которой я рассказываю про эту гипотезу, — объяснить проблему читателям. Если у вас есть мнения насчет того, как можно написать код функции множественной динамической логистической регрессии, пожалуйста, делитесь ими в комментариях. Все, что я пытался сделать для создания этой библиотеки, можно посмотреть по этой ссылке: https://www.mql5.com/ru/code/38894.

Моя попытка не увенчалась успехом, но все не так безнадежно, и я считаю, что этим стоит поделиться.

Преимущества логистической регрессии

  1. Не предполагает распределения классов в пространстве признаков
  2. Легко расширяется до нескольких классов (полиномиальная регрессия)
  3. Естественно-вероятностный взгляд на предсказания классов
  4. Быстрое обучение
  5. Очень быстро классифицирует незнакомые данные
  6. Хорошая точность для многих простых наборов данных
  7. Устойчивость к подгонке
  8. Может интерпретировать коэффициенты модели как индикатор важности функции

Недостатки

  1. Строит линейные границы

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

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

Я понимаю, что нет прикладной цели в использовании алгоритмов логистической регрессии для классификации данных по Титанику, особенно в платформе MetaTrader 5, однако, как я уже говорил, набор данных использовался только ради построения библиотеки по сравнению с выводом, который был достигнут в python, ссылка: https://github.com/MegaJoctan/LogisticRegression-MQL5-and-python. В следующей статье мы увидим, как можно использовать логистические модели для прогнозирования падения фондового рынка.

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

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

Прикрепленные файлы |

Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.

Другие статьи автора
  • Машинное обучение и Data Science (Часть 14): Применение карт Кохонена на рынках
  • Машинное обучение и Data Science (Часть 13): Анализируем финансовый рынок с помощью метода главных компонент (PCA)
  • Машинное обучение и Data Science (Часть 12): Можно ли выигрывать на рынке с помощью самообучающихся нейронных сетей?
  • Машинное обучение и Data Science (Часть 11): Наивный байесовский классификатор и теория вероятностей в трейдинге
  • Работа с матрицами, расширение функционала Стандартной библиотеки матриц и векторов
  • Машинное обучение и Data Science (Часть 10): Гребневая регрессия
  • Машинное обучение и Data Science (Часть 9): Алгоритм k-ближайших соседей (KNN)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *