Глава 4

Модели обучения на основе статистики и вероятности

В данной главе рассматриваются некоторые аспекты теории вероятностей и статистики, которые имеют отношение к моделям машинного обучения, а также методы их вычисления с использованием Python. Все коды представленные в этом учебнике и в этой главе в частности можно найти по ссылке https://sohoware.ru/SohoBook/. Создание модели машинного обучения, как правило, направлено на прогнозирование, классификацию или идентификацию на основе доступных данных и знаний об этих данных. Прогнозы могут быть детерминированными и...

80мин 15 018Слов 14Материалов

Ключевые идеи

  • Оценка и анализ вероятностей событий
  • Основы теории вероятностей
  • Распределения случайных чисел
  • Энтропия и теория вероятностей
  • 0.37113402 0.42268041 0.4742268 0.5257732 0.57731959
  • 0.68041237 0.73195876 0.78350515 0.83505155 0.88659794

Практическое задание

Возьмите небольшой список или словарь данных, преобразуйте его в NumPy-массив и посчитайте две простые статистики. Свяжите результат с главой "Модели обучения на основе статистики и вероятности".

Открыть лабораторию

Модели обучения на основе статистики и вероятности

Открыть лабораторию

В данной главе рассматриваются некоторые аспекты теории вероятностей и статистики, которые имеют отношение к моделям машинного обучения, а также методы их вычисления с использованием Python. Все коды представленные в этом учебнике и в этой главе в частности можно найти по ссылке https://sohoware.ru/SohoBook/.

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

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

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

4.1 Оценка и анализ вероятностей событий

4.1.1 Контролируемая случайная выборка: подходы и стратегии

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

Одним из основных инструментов для генерации случайных чисел в Python является функция random.randint(a, b), которая возвращает случайное целое число N такое, что a≤N≤b, обеспечивая равномерное распределение вероятностей в заданном диапазоне от a до b.

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

importrandom# Импортируем модуль для работы со случайными числами

Python
# Задаем параметры для генерации чисел
na, nb, n = 1, 100, 5# 'na' и 'nb' определяют диапазон, 'n' - количество чисел
# Генерируем и выводим n случайных чисел в диапазоне от 'na' до 'nb'
foriinrange(n):
print(random.randint(na, nb), ' ', end='') # Генерация и печать случайного числа
print('\n') # Переход на новую строку после вывода всех чисел
# Повторяем процесс для демонстрации независимости генераций
foriinrange(n):
print(random.randint(na, nb), ' ', end='')
# Выводим два набора случайных чисел для демонстрации их независимости

59 69 26 31 23

70 44 43 77 85

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

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

importrandom

Python
# Задаем параметры для генерации чисел
na, nb, n = 1, 100, 5# 'na' и 'nb' определяют диапазон, 'n' - количество чисел
random.seed(1) # Инициализируем генератор случайных чисел с начальным значением 1
# Генерируем и выводим n случайных чисел с фиксированным начальным значением
foriinrange(n):
print(random.randint(na, nb), ' ', end='')
print('\n') # Переход на новую строку
# Повторяем процесс с тем же начальным значением для демонстрации воспроизводимости
random.seed(1) # Используем то же начальное значение
foriinrange(n):
print(random.randint(na, nb), ' ', end='')
# В результате получаем два идентичных набора случайных чисел

18 73 98 9 33

18 73 98 9 33

Установка начального значения (seedvalue) позволяет достичь повторяемости результатов при каждом запуске кода, это важно для обеспечения воспроизводимости экспериментов в научных исследованиях и при разработке программного обеспечения.

Функция random.seed() инициализирует внутренний состояние генератора случайных чисел, что делает последовательность генерируемых чисел предсказуемой. Это необходимо при отладке программ и в сценариях, где требуется воспроизвести конкретные результаты для демонстрации или тестирования.

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

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

Text
Давайте разберем представленный код:

importrandom# Импорт модуля для работы со случайными числами

Text
# Установка начального значения для генератора случайных чисел обеспечивает воспроизводимость результатов

random.seed(1) # Можно изменить на любое другое число для получения другой последовательности

Python
n = 5# Количество генерируемых чисел
foriinrange(n): # Цикл для генерации n случайных вещественных чисел
print(random.random()) # Выводит случайное вещественное число от 0 до 1

0.13436424411240122

0.8474337369372327

0.763774618976614

0.2550690257394217

0.49543508709194095

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

4.2 Основы теории вероятностей

Вероятность — это числовая мера, отражающая вероятность наступления события или точность прогноза. Рассмотрим, например, случай, когда вероятность отказа конструкции составляет 0.1. Это можно математически выразить следующим образом:Prfailure="yes"=0,1

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

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

Для выполнения статистического анализа наборов данных с использованием Python, мы начинаем с импорта необходимых библиотек, включая TensorFlow:

importtensorflowastf# Импорт библиотеки TensorFlow для машинного обучения

importnumpyasnp# Импорт библиотеки NumPy для работы с массивами

Python
# Пример кода, демонстрирующий расчет вероятности с использованием TensorFlow
probability_of_failure = tf.constant(0.1) # Определение вероятности отказа как константы TensorFlow
probability_of_success = 1 - probability_of_failure#Вычислениевероятностиуспешнойработы
# Непосредственный расчет и вывод вероятностей без необходимости использования сессии
print(f"Вероятностьотказа: {probability_of_failure.numpy()}")
print(f"Вероятностьуспеха: {probability_of_success.numpy()}")
Вероятность отказа: 0.10000000149011612
Вероятность успеха: 0.8999999761581421

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

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

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

Text
Теперь давайте рассмотрим пример кода, который можно использовать для проведения таких экспериментов:

importtensorflowastf

Python
# Установка начального значения для воспроизводимости результатов
tf.random.set_seed(123)
# Создаем тензор, представляющий равномерное распределение вероятностей для чисел на верхней грани кубика
pr = tf.fill([6], 1/6) # Все значения равны 1/6, представляя равновероятные исходы
# Выводим вероятностное распределение
print('Вероятностное распределение для каждой грани:', pr.numpy())
# Выбираем одно значение из распределения, что соответствует броску кубика
n_top = tf.random.categorical(tf.math.log([pr]), 1) # Используем tf.random.categorical для выборки
# Выводим результат броска кубика
print('Число на верхней грани =', n_top.numpy()[0][0] + 1) # Добавляем 1, потому что индексы начинаются с 0
Вероятностное распределение для каждой грани: [0.16666667 0.16666667 0.16666667 0.16666667 0.16666667 0.16666667]
Число на верхней грани = 1

Каждый раз результат броска кубика был равен 1. Это может показаться удивительным, но это вполне возможно, особенно если начальное значение (seed) для генератора случайных чисел устанавливается в одно и то же значение перед каждым запуском кода. Установка одинакового начального значения гарантирует, что последовательность случайных чисел, генерируемых в каждом запуске, будет одинаковой, что может привести к тому, что при каждом запуске будет генерироваться одно и то же "случайное" число.

Text
Если вы хотите увидеть различные результаты при каждом запуске, вы можете убрать строку с tf.random.set_seed(123) или установить разные значения для начального значения в каждом запуске. Это приведет к тому, что генератор случайных чисел будет производить разные последовательности чисел при каждом запуске, что позволит получить различные результаты броска кубика.
В данной задаче мы предполагаем, что теоретическая или "истинная" вероятность выпадения конкретного числа на верхней грани игрального кубика равна 1/6, что приблизительно составляет 0.1667. Однократный бросок кубика приводит к созданию одномерного массива (тензора в TensorFlow) с одним элементом, который соответствует числу на верхней грани кубика. Для вычисления вероятности мы будем производить множество бросков, что позволит статистике дать более точные результаты. Это достигается путем указания размера тензора в функции tf.random.categorical():

importtensorflowastf

Python
n_surfaces = 6# количество возможных значений на гранях кубика
n_tosses = 18# количество бросков
# Установка seed для воспроизводимости результатов
tf.random.set_seed(1)
# Логарифмы вероятностей для каждой грани кубика
logits = tf.math.log([1.0/n_surfaces] * n_surfaces)
# Получениерезультатовбросков
toss_results = tf.random.categorical(logits[tf.newaxis,:], n_tosses)
print("Бросок", n_tosses, "раз.")
print("Результаты бросков:", tf.squeeze(toss_results).numpy()) # Удаление лишних измерений и преобразование в numpy массив

Бросок 18 раз.

Text
Результаты бросков: [1 0 2 4 1 0 0 1 3 0 1 3 0 4 0 1 5 1]
В этом примере мы произвели 18 бросков, что привело к созданию тензора с 18 элементами. Использование tf.random.set_seed(1) обеспечивает воспроизводимость результатов. При контролируемом эксперименте мы можем получить, например, 1 раз значение "5" из 18 бросков, что дает вероятность Pr(кубик="5") = 1/18. Если "3" выпадет три раза, то вероятность Pr(кубик="3") = 3/18 = 1/6, и так далее. Для более точной оценки вероятностей увеличим количество бросков.

importtensorflowastf

Python
n_surfaces = 6# количество возможных значений на гранях кубика
n_tosses = 18# количество бросков
# Установка seed для воспроизводимости результатов
tf.random.set_seed(1)
# Логарифмы вероятностей для каждой грани кубика
logits = tf.math.log([1.0/n_surfaces] * n_surfaces)
# Получениерезультатовбросков
toss_results = tf.random.categorical(logits[tf.newaxis,:], n_tosses)
print("Бросок", n_tosses, "раз.")
print("Результаты бросков:", tf.squeeze(toss_results).numpy()) # Удаление лишних измерений и преобразование в numpy массив
n_t = 20# Новое количество бросков
# Проведение новой серии бросков и вывод результатов
new_toss_results = tf.random.categorical(logits[tf.newaxis,:], n_t)
print("Новыерезультатыбросков:", tf.squeeze(new_toss_results).numpy())

Бросок 18 раз.

Text
Результаты бросков: [1 0 2 4 1 0 0 1 3 0 1 3 0 4 0 1 5 1]
Новые результаты бросков: [0 1 3 2 3 3 2 1 0 1 2 5 2 2 4 5 1 2 5 2]

importtensorflowastf

importnumpyasnp

Python
n_surfaces = 6# Количество возможных значений на гранях кубика
n_tosses = 2000# Количество бросков
# Установка seed для воспроизводимости результатов
tf.random.set_seed(1)
# Логарифмы вероятностей для каждой грани кубика
logits = tf.math.log([1.0/n_surfaces] * n_surfaces)
# Получениерезультатовбросков
toss_results = tf.random.categorical(logits[tf.newaxis,:], n_tosses)
toss_results = tf.squeeze(toss_results) # Удалениелишнихизмерений
# Подсчетколичествакаждойизцифр
counts = tf.math.bincount(toss_results, minlength=n_surfaces)
# Расчет вероятностей для каждой из граней
probabilities = counts / n_tosses
print('Всегобросков:', n_tosses)
print('Вероятность каждой из 6 граней:', probabilities.numpy())
print('Теоретические (истинные) вероятности:', [1.0/n_surfaces] * n_surfaces)
Всего бросков: 2000
Вероятность каждой из 6 граней: [0.1655 0.1495 0.1645 0.166 0.1675 0.187]

Теоретические (истинные) вероятности: [0.16666666666666666, 0.16666666666666666, 0.16666666666666666, 0.16666666666666666, 0.16666666666666666, 0.16666666666666666]

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

importtensorflowastf

importnumpyasnp

Python
np.set_printoptions(suppress=True) # Предотвращаем использование научной нотации для больших чисел
n_surfaces = 6# Количество граней кубика
n_tosses = 2000# Количество бросков
# Установка seed для воспроизводимости результатов
tf.random.set_seed(1)
# Логарифмы вероятностей для каждой грани кубика
logits = tf.math.log([1.0/n_surfaces] * n_surfaces)
# Получениерезультатовбросков
toss_results = tf.random.categorical(logits[tf.newaxis,:], n_tosses)
toss_results = tf.squeeze(toss_results) # Удалениелишнихизмерений
# Инициализациязаписейрезультатов
record = tf.Variable(tf.zeros((n_surfaces, n_tosses), dtype=tf.float32))
# Подсчет и запись результатов для каждого броска
foriinrange(n_tosses):
# Подсчет количества каждой грани до текущего броска включительно
counts = tf.math.bincount(toss_results[:i+1], minlength=n_surfaces, maxlength=n_surfaces)
record[:,i].assign(tf.cast(counts, tf.float32))
# Нормализация результатов
x = tf.range(1, n_tosses + 1, dtype=tf.float32)
observations = record / x
# Вывод результатов
print("Результаты после первого броска:\n", observations[:, 0].numpy())
print("Результаты после первых 10 бросков:\n", observations[:, 10].numpy())
print("Результаты после первых 1000 бросков:\n", observations[:, 999].numpy())
Результаты после первого броска:
[0. 1. 0. 0. 0. 0.]
Результаты после первых 10 бросков:
[0.36363637 0.36363637 0.09090909 0.09090909 0.09090909 0.]
Результаты после первых 1000 бросков:
[0.157 0.155 0.163 0.17 0.18 0.175]

Этот простой эксперимент позволяет нам получить 1000 наблюдений для шести возможных значений равномерного распределения, когда каждая из шести граней игральной кости имеет одинаковую вероятность выпадения. После 1000 бросков кости и вычисления вероятности выпадения каждой из шести граней мы обычно получаем значения в диапазоне от 0,14 до 0,19. Эти вероятности будут немного изменяться с каждым новым экспериментом из-за случайной природы процесса. Если мы проведем 10 000 бросков в каждом эксперименте, то полученные вероятности будут очень близки к теоретическому значению 1/6 ≈ 0,1667. Читатели могут легко повторить это, используя предоставленный код на TensorFlow.

Теперь давайте визуализируем "численные" результаты эксперимента с использованием TensorFlow и библиотеки matplotlib для построения графиков.

importtensorflowastf

frommatplotlibimportpyplotasplt

importnumpyasnp

Python
np.set_printoptions(suppress=True) #Предотвращаемиспользованиенаучнойнотациидлябольшихчисел
n_surfaces = 6# Количество граней кубика
n_tosses = 2000# Количество бросков
# Установка seed для воспроизводимости результатов
tf.random.set_seed(1)
# Логарифмы вероятностей для каждой грани кубика
logits = tf.math.log([1.0/n_surfaces] * n_surfaces)
# Получениерезультатовбросков
toss_results = tf.random.categorical(logits[tf.newaxis,:], n_tosses)
toss_results = tf.squeeze(toss_results) # Удалениелишнихизмерений
# Инициализациязаписейрезультатов
record = tf.Variable(tf.zeros((n_surfaces, n_tosses), dtype=tf.float32))
# Подсчет и запись результатов для каждого броска
foriinrange(n_tosses):
# Подсчет количества каждой грани до текущего броска включительно
counts = tf.math.bincount(toss_results[:i+1], minlength=n_surfaces, maxlength=n_surfaces)
record[:,i].assign(tf.cast(counts, tf.float32))
# Нормализация результатов
x = tf.range(1, n_tosses + 1, dtype=tf.float32)
observations = record / x
# Вывод результатов
print("Результаты после первого броска:\n", observations[:, 0].numpy())
print("Результаты после первых 10 бросков:\n", observations[:, 10].numpy())
print("Результаты после первых 1000 бросков:\n", observations[:, 999].numpy())
# Предполагаем, что observations- это тензор с результатами эксперимента
# Например, observations = tf.random.uniform([6, 1000], minval=0, maxval=1)
# Отрисовываем графики наблюдаемых вероятностей для каждой из граней кости
foriinrange(6):
plt.plot(observations[i].numpy(), label=f"Наблюдаемаявероятность P(грань={i+1})")
# Добавляем горизонтальную линию для теоретической вероятности
plt.axhline(y=0.166667, color='black', linestyle='dashed', label="Теоретическая вероятность 1/6")
# Добавляем легенду и отображаем график
plt.legend()
plt.show()
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.2 Основы теории вероятностей

Чем больше экспериментов мы проводим, тем ближе получаемая вероятность приближается к теоретическому значению 1/6. Это является проявлением Закона больших чисел, согласно которому среднее значение результатов многократно повторенных экспериментов сходится к ожидаемому значению при увеличении числа попыток.

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

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

4.3 Распределения случайных чисел

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

4.3.1 Равномерное Распределение

Числа, сгенерированные на основе равномерного распределения, должны иметь равные шансы попасть в любое место в указанном диапазоне. Чтобы проверить равномерность сгенерированных чисел с помощью функции random.randint(), мы можем выполнить ее большое количество раз, скажем, 1 миллион, и посмотреть, как распределяются эти числа. Для этого используется следующий код:

importnumpyasnp# Импортируем библиотеку для работы с массивами

importmatplotlib.pyplotasplt# Импортируем библиотеку для визуализации данных

importrandom# Импортируем модуль для генерации случайных чисел

Python
na, nb, n = 0, 99, 100# Задаем начальное и конечное значения диапазона и количество чисел
counts = np.zeros(n) # Создаем массив для подсчета количества каждого сгенерированного числа
# Создаем фигуру для отображения гистограмм
fig, axes = plt.subplots(2, 3, figsize=(15, 8), sharex=True)
axes = axes.reshape(6) # Преобразуем массив осей для удобства обращения
n_samples = 1000001# Задаем количество выборок
# Генерируем случайные числа и подсчитываем их
foriinrange(1, n_samples):
counts[random.randint(na, nb)] += 1# Увеличиваем счетчик для сгенерированного числа
# Визуализируем распределение чисел на разных этапах генерации
ifiin [10, 100, 1000, 10000, 100000, 1000000]:
axes[int(np.log10(i)) - 1].bar(np.arange(na + 1, nb + 2), counts) # Строимгистограмму
plt.show() # Отображаем гистограммы
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.3.1 Равномерное Распределение

В этом коде используется функция random.randint(na, nb) для генерации случайных целых чисел в диапазоне от na до nb, включительно. Затем мы подсчитываем, сколько раз каждое число было сгенерировано, и визуализируем это с помощью гистограмм на различных этапах процесса (после 10, 100, 1000, 10000, 100000 и 1000000 сгенерированных чисел). Это позволяет нам наглядно оценить, насколько равномерно распределены числа.

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

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

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

4.3.2 Гауссово распределение

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

px= 1σ2πe-x-μ2σ2

где μ и σ соответственно среднее и стандартное отклонение распределения. Нормальное распределение часто обозначается как Nμ, σ2.В частности, когда μ =0 и σ=1, мы имеем дело со стандартным нормальным распределением, обозначаемым какN(0,1),и его функция плотности упрощается до:

px= 12πe-x22
Text
Функция gauss() из модуля random в библиотеке numpy позволяет удобно генерировать числа, следующие нормальному распределению.
Давайте визуализируем функцию плотности, определенную в уравнении:px= 1σ2πe-x-μ2σ2

Форма кривой, напоминающая колокол, вероятно, уже вам знакома.

importnumpyasnp

importmatplotlib.pyplotasplt

fromrandomimportgauss

Python
mu, sigma, n = 0., 0.1, 10# Среднее значение (mu), стандартное отклонение (sigma) и количество генерируемых чисел (n)
# Генерация n случайных чисел по нормальному распределению
foriinrange(n):
print(f'{gauss(mu, sigma):.4f} ', end='')
# Определение переменной x
x = np.arange(-0.5, 0.5, 0.001)
# Определение функции Гаусса
defgf(mu, sigma, x):
return1/(sigma * np.sqrt(2 * np.pi)) * np.exp(-0.5 * ((x - mu)/sigma)**2)
# Визуализация функции плотности
plt.figure(figsize=(6, 4))
plt.plot(x, gf(mu, sigma, x), label="Гауссова функция")
plt.title("Функция плотности нормального распределения")
plt.xlabel("Значение")
plt.ylabel("Плотность вероятности")
plt.legend()
plt.show()
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.3.2 Гауссово распределение
Python
Рисунок: Типичное нормальное распределение (Гауссово распределение).
Для генерации случайных выборок из гауссовского распределения и сравнения их с "истинным" гауссовским распределением, мы можем использовать функцию np.random.normal(). Эта функция позволяет нам генерировать выборки, следуя нормальному распределению с заданными параметрами среднего и стандартного отклонения. После генерации выборок мы можем визуализировать полученные данные в виде гистограммы и сравнить их с теоретической кривой нормального распределения.

importnumpyasnp

importmatplotlib.pyplotasplt

Python
# Задаем параметры нормального распределения
mu, sigma = 0, 0.1# среднее значение и стандартное отклонение
# Количество генерируемых случайных выборок
n = 500
# Генерация n случайных выборок из нормального распределения
samples = np.random.normal(mu, sigma, n)
# Гистограммавыборок
count, bins, ignored = plt.hist(samples, 80, density=True, alpha=0.6, color='g', label='Гистограммавыборок')
# Функция Гаусса для "истинного" распределения
defgf(mu, sigma, x):
return1/(sigma * np.sqrt(2 * np.pi)) * np.exp(-0.5 * ((x - mu)/sigma)**2)
# Отрисовкатеоретическойкривойнормальногораспределения
plt.plot(bins, gf(mu, sigma, bins), linewidth=2, color='r', label='Теоретическоераспределение')
plt.title("Сравнение гистограммы выборок с теоретическим нормальным распределением")
plt.xlabel("Значение")
plt.ylabel("Плотность вероятности")
plt.legend()
plt.show()
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.3.2 Гауссово распределение

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

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

4.4 Энтропия и теория вероятностей

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

Для заданных вероятностей случайных переменных статистического события можно оценить соответствующую энтропию. Это мера неопределенности вероятностного распределения для события. Это скалярное произведение вектора вероятности (который содержит значения вероятностей случайной переменной) на его отрицательный логарифм. Энтропия Hpдля события с вероятностью p выражается как

Hp=-ipilogpi=-p· logp

где piэто вероятность i-го возможного значения переменной и ipi=1. Вектор p это вектор, который содержит эти вероятности. Отрицательный знак необходим, поскольку энтропия положительна, а log(pi) всегда отрицательна для 0≤pi≤1. При вычислении мы часто нормализуем её, разделив на общее количество возможных значений.

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

Text
Поскольку логарифм часто используется, давайте подробнее рассмотрим его на примере функции log() из библиотеки NumPy.

importnumpyasnp

importmatplotlib.pyplotasplt

Python
p = np.arange(0.01, 1.0,.01) # Создаем массив вероятностей от 0.01 до 1 с шагом 0.01
logp = -np.log(p) # Вычисляем отрицательный логарифм каждого значения в массиве
# Рисуем график отрицательного логарифма
plt.plot(p, logp, color='blue')
plt.xlabel('Вероятность p') # Подписываем ось X
plt.ylabel('Отрицательный логарифм p') # Подписываем ось Y
plt.title('Значение отрицательного логарифма вероятности') # Заголовок графика
plt.show() # Отображаем график
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.4 Энтропия и теория вероятностей
Text
Рисунок: Логарифмическая функция вероятности.
Давайте упомянем несколько важных особенностей, почему логарифм так часто используется в машинном обучении:

Функция −log(p) монотонно изменяется с аргументом p, что является важным для оптимизационных алгоритмов, широко применяемых в машинном обучении. Монотонность важна, поскольку она не влияет на положение стационарных точек исходной функции при логарифмировании. Это обеспечивает надёжность работы алгоритмов оптимизации, поскольку гарантирует, что минимизация или максимизация функции потерь приведёт к однозначному оптимальному решению.

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

Функция −log(p) убывает с увеличением вероятности p, что инвертирует тенденцию изменения вероятности и делает её подходящим мерилом для измерения энтропии, особенно в диапазоне высоких вероятностей. При высокой вероятности события уровень неопределённости низок, так как мы почти уверены в его наступлении, что снижает значение энтропии. Аналогично, при низкой вероятности события уровень неопределённости также низок, так как мы почти уверены, что событие не произойдёт. В этом случае мы используем саму вероятность в уравнении энтропии.

Представь, что у тебя есть шкала от 0 до 100, где 100 — это полная уверенность в чём-то, а 0 — полная неуверенность. Если у тебя есть что-то с вероятностью 90 или выше, ты почти уверен в этом, и твоя "неуверенность" (или "сюрприз", если это случится) будет низкой. Если вероятность чего-то очень мала, скажем 10 или меньше, ты также почти уверен, что это не случится, и опять же, твоя "неуверенность" низка. Именно так работает функция −log(p) — она уменьшается, когда ты становишься всё более и более уверен в результате.

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

Это можно сравнить с измерением "интересности" или "неожиданности" информации. Если что-то очень вероятно (как день следует за ночью), то оно не очень "интересно" или "неожиданно", поэтому его "интересность" (энтропия) мала. Если что-то менее вероятно (как выпадение шестёрки при броске обычного игрального кубика), оно более "интересно" или "неожиданно", и его "интересность" выше. Используя вероятность и её отрицательный логарифм, мы можем создать формулу (энтропию), которая помогает измерить эту "интересность" или "неожиданность" в разных ситуациях, делая её очень полезной в машинном обучении для понимания, насколько хорошо мы можем предсказывать результаты различных событий.

Text
Следующие примеры демонстрируют, как работает функция энтропии:

4.4.1 Пример 1: Вероятность и её энтропия

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

В этом примере мы видим противоречивое поведение pи −log(p). Вектор q1 имеет либо низкие, либо высокие значения вероятности для двух своих переменных, что означает низкую неопределенность и, следовательно, низкую вычисленную энтропию. С другой стороны, q2 имеет две вероятности посередине для обеих переменных, что означает, что оно очень неопределенно. Вычисленная энтропия высока, как и ожидалось.

4.4.2 Пример 2: Вариация энтропии

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

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

importnumpyasnp

Python
# Функция для расчёта энтропии
defcalculate_entropy(probabilities):
# Применение отрицательного логарифма к каждому элементу вектора вероятностей
entropy = -np.sum(probabilities * np.log(probabilities))

returnentropy

Python
# Вектор вероятностей для первого наблюдения
q1 = np.array([0.5, 0.5]) # Пример вероятностей для двух исходов
# Вектор вероятностей для второго наблюдения
q2 = np.array([0.9, 0.1]) # Пример, показывающий различие в вероятностях
# Расчёт и вывод энтропии для каждого вектора вероятностей
entropy_q1 = calculate_entropy(q1)
entropy_q2 = calculate_entropy(q2)
print(f'Энтропия для q1: {entropy_q1:.4f}') # Вывод значения энтропии для q1 с 4 знаками после запятой
print(f'Энтропия для q2: {entropy_q2:.4f}') # Вывод значения энтропии для q2 с 4 знаками после запятой
Энтропия для q1: 0.6931
Энтропия для q2: 0.3251

importnumpyasnp

Python
# Вектор вероятностей для первого наблюдения с низкой неопределённостью:
# большая уверенность в наступлении события, поскольку переменная
# имеет либо очень высокую, либо очень низкую вероятность быть наблюдаемой.
q1 = np.array([0.999, 0.001])
# Вектор вероятностей для второго наблюдения с высокой неопределённостью:
# низкая уверенность в наступлении события, поскольку переменная
# не имеет ни высокой, ни низкой вероятности быть наблюдаемой.
q2 = np.array([0.5, 0.5])
# Вычисление и вывод отрицательных логарифмов вероятностей
# для демонстрации влияния на неопределённость
print('q1=', q1, ' -log(q1)=', -np.log(q1)) # Отрицательный логарифм увеличивает значение для малых p
print('q2=', q2, ' -log(q2)=', -np.log(q2))
# Вычисление энтропии как меры неопределённости системы
H_q1 = -np.dot(q1, np.log(q1)) / len(q1) # Энтропия для q1
H_q2 = -np.dot(q2, np.log(q2)) / len(q2) # Энтропия для q2
print('H_q1=', H_q1, 'H_q2=', H_q2)
q1= [0.999 0.001] -log(q1)= [1.00050033e-03 6.90775528e+00]
q2= [0.5 0.5] -log(q2)= [0.69314718 0.69314718]
H_q1= 0.003953627556116044 H_q2= 0.34657359027997264

В этом примере мы видим противоположное поведение p и −log(p). Вектор q1 содержит значения вероятностей, которые либо очень низкие, либо очень высокие, что указывает на низкую неопределённость и, следовательно, на низкую вычисленную энтропию. С другой стороны, вектор q2 содержит вероятности, близкие к среднему для обеих переменных, что говорит о высокой неопределённости. Вычисленная энтропия, как и ожидалось, высока.

Чтобы наглядно показать изменение энтропии, создадим искусственные события q1 с переменной, которая принимает два возможных значения. Пусть вероятности этих двух значений будут v1 и v2, изменяющиеся обратно пропорционально таким образом, что сумма вероятностей равна 1. Используем следующий код для расчета изменения энтропии в зависимости от изменений вероятностей v1и v2:

importnumpyasnp

importmatplotlib.pyplotasplt

Python
# Событие с переменной, которая принимает два значения
v1 = np.arange(0.01, 1.0,.05) # Инициализация v1 с шагом 0.05 начиная от 0.01 до 1.0
gap = (v1[1] - v1[0]) * len(v1) / 3. # Вычисление ширины промежутка между столбцами гистограммы
v1/= (v1[0] + v1[-1]) # Нормализация v1 для удержания суммы вероятностей равной 1
v2 = v1[::-1] # Создание v2 как зеркальное отображение v1
# Вывод значений v1 и v2 для проверки
print(v1, np.sum(v1))
print(v2, np.sum(v2))
# Проверка, что сумма v1 и v2 равна 1 для каждой пары значений
print(v1 + v2, np.sum(v1 + v2) / 2)
# Визуализация вероятностей с помощью гистограммы
xtick = range(len(v1)) # Метки для оси x
plt.bar(range(len(v1)), v1, width=gap * 1.2, alpha=.9, color='blue') #Гистограммадляv1
plt.bar(range(len(v2)), v2, width=gap, alpha=.9, color='red') #Гистограммадля v2
plt.xlabel('ID события, синий: v1, красный: v2')
plt.ylabel('Вероятность')
plt.xticks(xtick)
plt.show()
H_qf = np.array([]) # Инициализация массива для энтропии событий q1
# Расчет энтропии для каждой пары вероятностей
forq1inlist(zip(v1, v2)):
H_qf = np.append(H_qf, -(np.dot(q1, np.log(q1))) / 2) # Вычислениеидобавлениеэнтропии
# Визуализация изменения энтропии
plt.plot(v1, H_qf)
plt.xlabel('Вероятность, v1 (v2=1-v1)')
plt.ylabel('Энтропия событий')
plt.title('Энтропия событий')
plt.show()
[0.01030928 0.06185567 0.11340206 0.16494845 0.21649485 0.26804124

0.31958763 0.37113402 0.42268041 0.4742268 0.5257732 0.57731959

0.62886598 0.68041237 0.73195876 0.78350515 0.83505155 0.88659794

0.93814433 0.98969072] 10.0

Text
[0.98969072 0.93814433 0.88659794 0.83505155 0.78350515 0.73195876

0.68041237 0.62886598 0.57731959 0.5257732 0.4742268 0.42268041

0.37113402 0.31958763 0.26804124 0.21649485 0.16494845 0.11340206

0.06185567 0.01030928] 9.999999999999998

Text
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] 10.0
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 0.06185567 0.01030928] 9.999999999999998
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 0.06185567 0.01030928] 9.999999999999998

Как видно из графиков, энтропия достигает максимума, когда вероятности v1 и v2 равны 0.5, что соответствует наибольшей неопределенности исхода. Энтропия минимальна на краях диапазона, что соответствует случаям, когда исход событий почти определен.

4.4.3 Пример 3: Энтропия для событий с переменными, принимающими различное количество значений равномерного распределения

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

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

importmatplotlib.pyplotasplt

importnumpyasnp

Python
# Инициализация переменных
N = 0# Текущее количество возможных значений переменной
max_v = 100# Максимальное количество возможных значений
Ni = np.array([]) # Массив для хранения количества переменных
H_qf = np.array([]) # Массив для хранения значений энтропии
# Генерация равномерного распределения и вычисление энтропии для каждого N
whileN<max_v:
N += 1# Увеличиваем количество возможных значений переменной
Ni = np.append(Ni, N) # Записываем текущее количество переменных
qf = np.ones(N) /N# Генерация равномерного распределения
H_qf = np.append(H_qf, -np.dot(qf, np.log(qf)) / len(qf)) # Вычисление и запись энтропии
# Вывод результатов
print('Вероятностное распределение:', qf[0:max_v:10])
print('H_qf=', H_qf[0:max_v:10])
# Визуализация результатов
plt.plot(Ni, H_qf)
plt.xlabel('Количество переменных, все с одинаковой вероятностью')
plt.ylabel('Энтропия')
plt.title('События с переменными равномерного распределения')
plt.show()
Вероятностное распределение: [0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01]
H_qf= [-0. 0.21799048 0.14497726 0.11077378 0.09057493 0.07709462

0.06739137 0.06003774 0.05425246 0.04956988]

Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 0.06739137 0.06003774 0.05425246 0.04956988]

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

Text
Из результатов видно, что:

Энтропия равна нулю, когда N =1; это означает, что если существует только одно возможное значение, то неопределенности нет, и мы точно знаем исход.

Text
Энтропия достигает максимума при N=3; это указывает на наибольшую неопределенность при данном количестве возможных значений.

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

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

4.4.3 Кросс-энтропия

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

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

"Истинное распределение" - это то, что пользователь действительно нарисовал. Если пользователь рисовал кошку, то истинное распределение будет 100% кошка и 0% всё остальное.

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

Text
Давайте рассмотрим пример кода, который демонстрирует, как можно вычислить кросс-энтропию в Python:

importnumpyasnp

Python
# Истинное распределение: пользователь нарисовал кошку
true_distribution = np.array([1, 0, 0]) # Кошка, Дерево, Автомобиль
# Предсказанное распределение: программа думает, что это скорее всего кошка
predicted_distribution = np.array([0.7, 0.2, 0.1])
# Вычислениекросс-энтропии
cross_entropy = -np.sum(true_distribution*np.log(predicted_distribution))
print(f"Кросс-энтропия: {cross_entropy}")
Кросс-энтропия: 0.35667494393873245

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

Кросс-энтропия, часто используется в статистике. Перекрёстная энтропия распределения q относительно распределения p определяется следующим образом:

Hpq= -ipilogqi= -p· logq

В общем, кросс-энтропия является мерой сходства двух распределений (из одного и того же пространства). В машинном обучении нас интересует кросс-энтропияпредсказанной вероятности q по отношению к истинной p.

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

Text
Заметим следующие свойства:

Кросс-энтропия не является симметричной: Hpq≠ Hqp, если p≠q.

Будет выполняться H(p,q)≥H(p)Hpq≥ Hp, и Hqp≥ Hq. Разница будет представлять собой дивергенцию Кульбака-Лейблера, которая всегда положительна. Когда эти два распределения совпадают, кросс-энтропия становится энтропией, изученной в предыдущем разделе. Все три упомянутые неравенства в этом случае становятся равенствами.

Следовательно, в моделях машинного обучения, даже если предсказание идеально, кросс-энтропия все равно не будет равна нулю, поскольку истинное распределение само по себе может иметь энтропию. Если Hp- это энтропия истинного распределения, то кросс-энтропия Hpqограничена снизу значением Hp. Она может быть равна нулю только в случае, если истинное распределение не имеет никакой неопределенности (вероятности всех переменных равны нулю, кроме одной, которая равна 1).

Далее рассмотрим некоторые простые примеры.

4.4.4 Пример 4: Перекрёстная энтропия в прогнозировании качества

В разделе о перекрёстной энтропии в контексте прогнозирования, рассмотрим пример использования данного метода для оценки качества прогноза погоды. Предположим, что мы стремимся предсказать вероятность осадков на следующий день, исходя из текущих метеорологических данных. В качестве примера, наше предсказание состоит в том, что вероятность дождя составляет 90%, а вероятность ясной погоды - 10%. Это наше предположение или "прогноз".

Далее, представим, что у нас есть точные данные о погоде на следующий день, которые мы получили, к примеру, с помощью машины времени. Согласно этим данным, реальная вероятность дождя составляет 99%, а вероятность солнечной погоды - всего 1%. Эти значения являются "истинными" вероятностями.

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

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

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

Text
Случай хорошего прогноза:

importnumpyasnp

Python
# Предсказанная вероятность двух исходов
q_good = np.array([0.9, 0.1]) # Прогнозируемые вероятности для двух значений
y = np.array([0.99, 0.01]) # Истинные вероятности двух значений
p = y# Истинное распределение
q = q_good# Прогнозируемое распределение
# Выводим значения p и q, а также их логарифмы
print('p=', p, ' log(p)=', -np.log(p))
print('q=', q, ' log(q)=', -np.log(q))
# Расчёт и вывод энтропии для p и q
print(' Энтропия: Hp=', -np.dot(p, np.log(p)) / len(p),\
' Hq=', -np.dot(q, np.log(q)) / len(q))
# Расчётивыводперекрёстнойэнтропии
print('\nПерекрёстнаяэнтропия: Hpq=', -np.dot(p, np.log(q)) / len(p),\
' Hqp=', -np.dot(q, np.log(p)) / len(q))
p= [0.99 0.01] log(p)= [0.01005034 4.60517019]
q= [0.9 0.1] log(q)= [0.10536052 2.30258509]
Энтропия: Hp= 0.028000767177423672 Hq= 0.1625414866957241
Перекрёстнаяэнтропия: Hpq= 0.06366638071559423 Hqp= 0.2347811604334802
np.array([0.9, 0.1]) и np.array([0.99, 0.01]) задают прогнозируемые и истинные вероятности двух возможных исходов соответственно.
-np.log(p) и -np.log(q) используются для вычисления натурального логарифма (ln) от элементов массивов p и q, что является частью формулы для расчета энтропии и перекрёстной энтропии.
np.dot(p, np.log(p)) и np.dot(p, np.log(q)) вычисляют скалярное произведение между двумя массивами, что необходимо для расчета энтропии и перекрёстной энтропии. Деление на len(p) (количество элементов в p) позволяет нормализовать результат.

Вывод значений энтропии (Hp) и перекрёстной энтропии (Hpq и Hqp) дает представление о качестве прогноза. Низкая перекрёстная энтропия Hpq указывает на хорошее качество прогноза q по отношению к истинному распределению p.

Обратите внимание, что значения Hpq и Hqp различны, что демонстрирует асимметричность перекрёстной энтропии. При этом ожидается, что Hpq ≥ Hp и Hqp ≥ Hq, что подтверждает, что перекрёстная энтропия выше или равна соответствующей энтропии. Это свойство используется для оценки качества прогнозируемых распределений по отношению к истинному распределению.

Видно, что перекрёстная энтропия Hpq низкая, что указывает на то, что прогноз qхорош. Обратите внимание, что Hpq≠ Hqp, Hpq≥ Hp и Hqp≥ Hq.

4.4.5 Пример 5: Перекрёстная энтропия при некачественном прогнозе

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

Text
Прогноз: Допустим, мы предполагаем, что вероятность вытащить красную карту составляет 10% (0.1), а синюю - 90% (0.9).
Истина: На самом деле, красная карта появляется с вероятностью 99% (0.99), а синяя - всего с 1% (0.01).

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

importnumpyasnp

Python
# Задаём прогноз и истинные вероятности
q_bad = np.array([0.1, 0.9]) # Прогноз
y = np.array([0.99, 0.01]) # Истина
# Расчёт перекрёстной энтропии
cross_entropy = -np.dot(y, np.log(q_bad))
print(f"Перекрёстнаяэнтропия: {cross_entropy}")
Перекрёстная энтропия: 2.2806128472206835

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

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

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

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

Text
В случае совершенно ошибочного прогноза:

importnumpyasnp

Python
# Прогнозируемые вероятности двух значений (некачественный прогноз)
q_bad = np.array([0.1, 0.9])
# Истинные вероятности двух значений
y = np.array([0.99, 0.01])
p = y# Истина
q = q_bad# Прогноз
# Вывод истинных и прогнозируемых вероятностей с их логарифмами
print('p=', p, ' log(p)=', -np.log(p))
print('q=', q, ' log(q)=', -np.log(q))
# Расчёт энтропии для истинного распределения и прогноза
print(' Энтропия: Hp=', -np.dot(p, np.log(p)) / len(p), ' Hq=', -np.dot(q, np.log(q)) / len(p))
# Расчёт перекрёстной энтропии
print('\nПерекрёстная энтропия: Hpq=', -np.dot(p, np.log(q)) / len(p), ' Hqp=', -np.dot(q, np.log(p)) / len(q))
p= [0.99 0.01] log(p)= [0.01005034 4.60517019]
q= [0.1 0.9] log(q)= [2.30258509 0.10536052]
Энтропия: Hp= 0.028000767177423672 Hq= 0.1625414866957241
Перекрёстнаяэнтропия: Hpq= 1.1403064236103417 Hqp= 2.072829100487316
В этом коде:

q_bad представляет некачественный прогноз вероятностей событий.

y содержит истинные вероятности событий.

p и q используются для удобства и обозначают истинные и предсказанные вероятности соответственно.

Python
np.log() применяется для вычисления натурального логарифма вероятностей, что необходимо для расчёта энтропии и перекрёстной энтропии.

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

Из результатов видно, что значение перекрёстной энтропии Hpq высокое, что указывает на плохое качество прогноза q. Следует также отметить, что Hpq не равно Hqp, и как правило, Hpq ≥ Hp, а Hqp ≥ Hq, что демонстрирует асимметричность перекрёстной энтропии и её зависимость от того, какое распределение рассматривается как истинное, а какое - как предсказанное.

Видно, что кросс-энтропия Hpq высока, что указывает на плохое качество прогноза q. Обратите внимание, что Hpq≠ Hqp, Hpq≥ Hp и Hqp≥ Hq.

Text
Теперь мы готовы обсудить дивергенцию Кульбака-Лейблера.

4.5 Расхождение Кульбака-Лейблера

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

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

Расхождение Кульбака-Лейблера позволяет оценить, насколько сильно различаются эти два представления, то есть насколько один "мешочек" отличается от другого. Если "мешочки" идентичны, то есть представления наблюдателей о распределении событий полностью совпадают, расхождение Кульбака-Лейблера будет равно нулю. Однако, чем больше различий между "мешочками", тем больше будет значение KL-расхождения, что указывает на более значительное расхождение в представлениях наблюдателей о распределении событий или элементов.

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

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

В Python, мы можем представить вероятностное распределение как список вероятностей, где каждый элемент списка представляет вероятность определённого события.

Предположим, у нас есть два распределения, P и Q, представленные в Python как списки. Мы хотим вычислить, насколько Q отличается от P, используя KL-расхождение.

importnumpyasnp

Python
# Определяем два распределения P и Q
P = np.array([0.1, 0.2, 0.7]) # Распределение P
Q = np.array([0.1, 0.3, 0.6]) # Распределение Q
# Вычисляем KL-расхождение от Q к P
KL_divergence = np.sum(P*np.log(P/Q))
# Выводим результат
print(f"РасхождениеКульбака-Лейблера от Q к P: {KL_divergence}")
Расхождение Кульбака-Лейблера от Q к P: 0.026812454257447993

Расхождение Кульбака-Лейблера (KL-расхождение) является мерой относительной энтропии одного распределения по отношению к другому.Для двух данных распределений p и q, KL-дивергенция от q к p определяется как

DKLp∥q= ipi· logpi- logqi= p·logp-logq

Это также называется относительной энтропией q по отношению кp, которое может рассматриваться как истинное или референсное распределение. Используя определения для энтропии и кросс-энтропии, мы имеемDKLp∥q=Hpq-Hp

Обратите внимание, что KL-расхождение q относительно p отличается от расхождения pотносительно q. Мы также имеем DKLp∥q≥0, равенство выполняется только еслиp=q

Text
Ниже приведены два простых примера KL-расхождения.

4.5.1 Пример 1: KL-дивергенция распределения качественного прогноза

Представим, что у вас есть два знакомых, каждый из которых обожает мороженое, но отдает предпочтение различным вкусам. Один из них обычно выбирает шоколадный вкус (в 99% случаев), и лишь изредка предпочитает клубничный (1% случаев). Данное распределение предпочтений можно обозначить как PP.

Теперь, попробуем предсказать, какой вкус мороженого выберет ваш друг в следующий раз, основываясь на предыдущем опыте. Вы можете предположить: "Я считаю, что он выберет шоколадное с вероятностью 90% и клубничное с вероятностью 10%". Это ваше предположительное распределение вкусов, давайте назовем его QQ.

Расхождение Кульбака-Лейблера (KL-расхождение) представляет собой методику оценки того, насколько точно ваше предположительное распределение QQ согласуется с фактическим распределением PP. Это позволяет оценить, насколько хорошо вы знакомы с предпочтениями вашего друга.

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

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

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

Рассмотрим простое событие с переменной, которая может принимать два возможных значения. Предположим, что у нас есть прогноз качества распределения относительно истинного распределения. Каким образом это измеряется с помощью расхождения Кульбака-Лейблера (KL-расхождение)?

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

КЛ-расхождение не является симметричным, что означает, что Dpq не равно Dqp. Это важный аспект, указывающий на то, что различие между прогнозируемым и истинным распределениями неодинаково в обеих направлениях. Мы рассмотрим это на примере с конкретными значениями.

importnumpyasnp

Python
# Истинное или эталонное распределение
p = np.array([0.99, 0.01]) # Вероятности двух возможных исходов
# Прогнозируемое распределение
q_good = np.array([0.9, 0.1]) # Хороший прогноз распределения
# Расчет логарифмов для каждого элемента в распределениях
log_p = -np.log(p)
log_q_good = -np.log(q_good)
# Выводим значения для истинного и прогнозируемого распределений
print(f'p={p}, log(p)={log_p}')
print(f'q={q_good}, log(q)={log_q_good}')
# Расчет KL-расхождения от p к q и от q к p
Dpq = np.sum(p* (log_p-log_q_good)) /len(p)
Dqp = np.sum(q_good* (log_q_good-log_p)) /len(p)
# Выводимзначения KL-расхождений
print(f'Dpq={Dpq}, Dqp={Dqp}')
# Вывод: Наблюдаем, что оба KL-расхождения положительны и имеют низкие значения, что указывает на хорошее качество прогноза q.
p=[0.99 0.01], log(p)=[0.01005034 4.60517019]
q=[0.9 0.1], log(q)=[0.10536052 2.30258509]
Dpq=-0.035665613538170556, Dqp=-0.07223967373775611

В этом коде мы определяем истинное распределение p и прогнозируемое распределение q_good, вычисляем логарифмы этих распределений, и затем рассчитываем KL-расхождение в обоих направлениях (Dpq и Dqp).

Обратите внимание что Dpq≠Dqp.,

4.5.2 Пример 2: KL-расхождение для плохо предсказанного распределения

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

importnumpyasnp

Python
# Наше предположение о том, как часто монетка падает разными сторонами
# Настоящие шансы - 99% на "орла" и 1% на "решку"
p = np.array([0.99, 0.01])
# Наше неправильное предположение - 10% на "орла" и 90% на "решку"
q = np.array([0.1, 0.9])
# Рассчитываем и показываем, как часто мы думаем, что монетка падает каждой стороной
print('Думаем, что "орел" выпадает в', q[0]*100, '% случаев')
print('Думаем, что "решка" выпадает в', q[1]*100, '% случаев')
# Считаем наши ошибки в предположении
# Ошибка при предположении, что "орел" выпадает чаще, чем на самом деле
Dpq = np.sum(p* (np.log(p) -np.log(q)))
# Ошибка при предположении, что "решка" выпадает чаще, чем на самом деле
Dqp = np.sum(q* (np.log(q) -np.log(p)))
# Показываем, насколько сильно мы ошиблись
print('Наша ошибка, когда мы думаем, что "орел" падает чаще:', Dpq)
print('Наша ошибка, когда мы думаем, что "решка" падает чаще:', Dqp)

Думаем, что "орел" выпадает в 10.0 % случаев

Думаем, что "решка" выпадает в 90.0 % случаев

Text
Наша ошибка, когда мы думаем, что "орел" падает чаще: 2.224611312865836
Наша ошибка, когда мы думаем, что "решка" падает чаще: 3.8205752275831846

Предположение о том, что монетка выпадает "орлом" всего в 10% случаев и "решкой" в 90%, значительно отличается от реальности, где "орел" выпадает в 99% случаев и "решка" в 1%. Это означает, что наше предположение о распределении вероятностей очень неточно.

Полученные значения KL-расхождения (Dpq и Dqp) показывают, насколько большая ошибка была сделана в предположениях. Чем больше значение KL-расхождения, тем хуже предсказание распределения.

KL-расхождение не симметрично; это значит, что значение ошибки изменяется в зависимости от того, сравниваем ли мы предсказанное распределение с истинным (Dpq) или истинное распределение с предсказанным (Dqp). В нашем случае, ошибка, которая возникает, когда мы предполагаем, что "решка" падает чаще, чем "орел" (Dqp), больше, чем ошибка в обратном предположении (Dpq). Это показывает, что несоответствие между предсказанием и реальностью может иметь различные последствия в зависимости от направления сравнения.

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

Рассмотрим простое событие, связанное с переменной, которая может принимать два возможных значения. Предположим, что у нас есть неточное предсказание вероятностного распределения по сравнению с истинным или эталонным распределением. Мы исследуем, как это расхождение измеряется с помощью KL-расхождения на следующем примере кода:

importnumpyasnp

Python
# Плохойпрогноз
p = np.array([0.99, 0.01]) # Истинное распределение
q = np.array([0.1, 0.9]) # Предсказанное распределение
# Вывод значений распределений и их логарифмов
print('p=', p, ' log(p)=', -np.log(p))
print('q=', q, ' log(q)=', -np.log(q))
# Расчет KL-расхождения от p к q и от q к p
Dpq = np.dot(p, (np.log(p) -np.log(q))) / len(p)
Dqp = np.dot(q, (np.log(q) -np.log(p))) / len(p)
# Вывод значений KL-расхождений
print('KL-расхождение от p к q (Dpq)=', Dpq)
print('KL-расхождение от q к p (Dqp)=', Dqp)
p= [0.99 0.01] log(p)= [0.01005034 4.60517019]
q= [0.1 0.9] log(q)= [2.30258509 0.10536052]
KL-расхождение от p к q (Dpq)= 1.112305656432918
KL-расхождение от q к p (Dqp)= 1.910287613791592

Мы видим, что оба значения KL-расхождений Dpq и Dqpположительны. Они оба имеют высокие значения, что указывает на то, что предсказание q является неточным. Следует также отметить, что Dpq ≠ Dqp, что демонстрирует асимметричность KL-расхождения.

4.6 Основы бинарной кросс-энтропии в машинном обучении

Когда мы работаем с задачами классификации в Python, особенно когда наша задача — определить, принадлежит ли объект к одному из двух классов (например, "собака" или "кошка"), мы часто используем метод, называемый бинарной кросс-энтропией, для оценки эффективности наших алгоритмов.

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

В контексте Python, мы можем рассчитать бинарную кросс-энтропию, используя функцию, которая принимает два основных параметра: истинные метки (например, "1" для собак и "0" для кошек) и предсказанные вероятности того, что каждый объект принадлежит к классу "1". Функция затем вычисляет "штраф" для каждого предсказания, основываясь на разнице между предсказанием и истинной меткой, и возвращает общий "штраф" для всех предсказаний, который и есть бинарная кросс-энтропия.

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

importnumpyasnp

Text
defbinary_cross_entropy(true_labels, predicted_probs):

"""

Text
Вычисляет бинарную кросс-энтропию между истинными метками и предсказанными вероятностями.
Параметры:
true_labels (numpyarray): массив истинных меток (0 или 1).
predicted_probs (numpyarray): массив предсказанных вероятностей принадлежности к классу 1.
Возвращает:
float: значение бинарной кросс-энтропии.

"""

Python
# Минимальное значение, добавленное для предотвращения логарифма от нуля
epsilon = 1e-15
# Корректировка предсказанных вероятностей для предотвращения ошибок вычисления логарифма
predicted_probs = np.clip(predicted_probs, epsilon, 1 - epsilon)
# Вычислениебинарнойкросс-энтропии
bce = -np.mean(true_labels * np.log(predicted_probs) + (1 - true_labels) * np.log(1 - predicted_probs))

returnbce

Python
# Пример использования
true_labels = np.array([1, 0, 1, 1, 0]) # Истинные метки (1 - собака, 0 - кошка)
predicted_probs = np.array([0.9, 0.2, 0.8, 0.95, 0.1]) # Предсказанные вероятности того, что на изображении собака
bce = binary_cross_entropy(true_labels, predicted_probs)
print(f"Бинарнаякросс-энтропия: {bce}")
Бинарная кросс-энтропия: 0.14166028566632455
В этом коде функция binary_cross_entropy принимает два аргумента: true_labels, массив истинных меток, и predicted_probs, массив предсказанных вероятностей. Внутри функции используется операция np.clip для ограничения предсказанных вероятностей, предотвращая логарифмические ошибки из-за вероятностей, равных ровно 0 или 1. Затем вычисляется среднее значение взвешенных логарифмических потерь для каждого примера, что и является итоговой бинарной кросс-энтропией для набора данных.

Бинарная кросс-энтропия — это мера расхождения между двумя вероятностными распределениями для задачи бинарной классификации. Она используется в машинном обучении и статистике для измерения "расстояния" между истинным распределением p и предсказанным распределением q вероятностей. В контексте машинного обучения, где p представляет собой истинную метку класса (0 или 1), а q — предсказанную вероятность принадлежности к классу 1, бинарная кросс-энтропия определяется следующим образом:

HpqB= -ipi· logqi+ 1- pi· log1-qi

Для двух данных распределений p и q бинарная кросс-энтропия q относительно p определяется как

HpqB= -ipi· logqi+ 1- pi· log1-qi= -p·logq- 1-p· log1-q

В моделях машинного обучения мы обычно предполагаем, что p — это истинное распределение, которое может принимать значения 0 или 1, и поэтому не может быть подвергнуто логарифмированию. Бинарная кросс-энтропия может рассматриваться как мера энтропии предсказанной вероятности относительно истинной. Она учитывает как вероятность pи q, так и обратную вероятность 1-p и 1-qи вычисляет энтропию обеих. По сути, это удваивает кросс-энтропию, немного усиленную меру несоответствия предсказанного распределения истинному. Часто используется для измерения производительности модели и используется как один из типов функции потерь.

Рассмотрим несколько примеров.

4.6.1 Пример 1 Бинарная кросс-энтропия для распределения прогноза качества

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

Представьте, что вы пытаетесь предсказать, будет ли завтра дождь. Истинная вероятность дождя завтра составляет 80% (0.8). Ваша модель предсказания погоды дает вероятность дождя в 85% (0.85). Поскольку ваше предсказание очень близко к истинной вероятности, разница между этими двумя вероятностями невелика, и, следовательно, бинарная перекрестная энтропия будет низкой. Это указывает на то, что ваш прогноз достаточно хорош.

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

importnumpyasnp

Python
# Истинное распределение вероятностей
p = np.array([1.0, 0.0, 0.0, 0.0]) # истина
# Предсказанное распределение вероятностей
q = np.array([0.9, 0.04, 0.03, 0.03]) # прогноз
# Обратные вероятности для истинного и предсказанного распределений
p_conv = 1.0 - p# обратная сторона истины
q_conv = 1.0 - q# обратная сторона прогноза
# Вывод истинных и предсказанных вероятностей и их обратных значений
print(p, q, ' обратные значения:', p_conv, q_conv)
# Вычисление кросс-энтропии
cHpq = -np.sum(np.dot(p, np.log(q))) / len(p)
# Вычисление бинарной кросс-энтропии
bcHpq = -np.sum(np.dot(p, np.log(q)) + np.dot(p_conv, np.log(q_conv))) / len(p)
# Вывод значений кросс-энтропии и бинарной кросс-энтропии
print('Кросс-энтропия cHpq:', cHpq)
print('Бинарная кросс-энтропия bcHpq:', bcHpq)
[1. 0. 0. 0.] [0.9 0.04 0.03 0.03] обратные значения: [0. 1. 1. 1.] [0.1 0.96 0.97 0.97]
Кросс-энтропия cHpq: 0.02634012891445657
Бинарная кросс-энтропия bcHpq: 0.05177523128687465

В этом примере мы имеем дело с ситуацией, где переменная события может принимать одно из четырех значений. Предполагается, что распределение q является нашим предсказанием для истинного распределения p. Мы используем кросс-энтропию для оценки качества нашего предсказания относительно истинного распределения. Кросс-энтропия cHpq измеряет разницу между двумя распределениями, в то время как бинарная кросс-энтропия bcHpq учитывает как вероятности событий, так и их обратные значения, предоставляя более полную меру различий.

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

4.6.2 Пример 2: Бинарная перекрёстная энтропия для плохо предсказанного распределения

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

Используя тот же сценарий с прогнозом погоды, предположим, что истинная вероятность дождя завтра составляет 80% (0.8), но ваша модель предсказывает вероятность дождя всего в 30% (0.3). Здесь разница между предсказанием модели и истинной вероятностью значительна, что приводит к высокой бинарной перекрестной энтропии. Это говорит о том, что ваш прогноз далек от истины и не является качественным.

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

importnumpyasnp

Python
# Инициализация плохого предсказания и истинного распределения
q = np.array([0.4, 0.2, 0.3, 0.1]) # предсказание
p = np.array([1.0, 0.0, 0.0, 0.0]) # истинное распределение
# Расчёт обратных сторон предсказания и истинного распределения
p_conv = 1.0 - p# обратная сторона истинного распределения
q_conv = 1.0 - q# обратная сторона предсказания
# Вывод истинного распределения, предсказания и их обратных сторон
print(f"Истина: {p}, Предсказание: {q}, Обратная истина: {p_conv}, Обратное предсказание: {q_conv}")
# Расчёт перекрёстной энтропии
cHpq = -np.sum(p*np.log(q)) / len(p)
# Расчёт бинарной перекрёстной энтропии
bcHpq = -np.sum(p*np.log(q) +p_conv*np.log(q_conv)) /len(p)
# Вывод результатов
print(f"Перекрёстная энтропия cHpq: {cHpq}")
print(f"Бинарная перекрёстная энтропия bcHpq: {bcHpq}")
Истина: [1. 0. 0. 0.], Предсказание: [0.4 0.2 0.3 0.1], Обратная истина: [0. 1. 1. 1.], Обратное предсказание: [0.6 0.8 0.7 0.9]
Перекрёстная энтропия cHpq: 0.22907268296853875
Бинарная перекрёстная энтропия bcHpq: 0.4003674356962309

В данном примере, бинарная перекрёстная энтропия (bcHpq) показывает величину расхождения между предсказанным и истинным распределениями. Как видно из результатов, бинарная перекрёстная энтропия примерно в два раза больше перекрёстной энтропии (cHpq), что указывает на усиленную меру расхождения.

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

4.6.3 Пример 3: Бинарная кросс-энтропия для более равномерного истинного распределения: Прогноз качества

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

Допустим, мы пытаемся предсказать, выпадет ли орел или решка при подбрасывании идеальной монеты. Истинная вероятность каждого исхода составляет 50% (0.5). Если ваша модель предсказывает вероятность выпадения орла в 52% (0.52) и решки в 48% (0.48), то ваше предсказание достаточно равномерное и близко к истинному распределению. Бинарная перекрестная энтропия в этом случае будет относительно низкой, что указывает на качественный прогноз.

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

Text
Рассмотрим прогноз и истинное распределение с более равномерными вероятностями:

importnumpyasnp

Python
# Прогнозируемое распределение
q = np.array([0.4, 0.2, 0.3, 0.1]) # Прогноз
# Истинное распределение
p = np.array([0.3, 0.3, 0.2, 0.2]) # Истина, довольно равномерное распределение
# Расчет обратной стороны прогноза и истины для двоичной кросс-энтропии
p_conv = 1.0 - p# Обратная сторона истины
q_conv = 1.0 - q# Обратная сторона прогноза
# Вывод истинного и прогнозируемого распределений, а также их обратных сторон
print("Истина:", p, "Прогноз:", q, ' Обратные стороны: Истина:', p_conv, 'Прогноз:', q_conv)
# Расчет обычной кросс-энтропии
cHpq = -np.sum(p*np.log(q)) / len(p)
# Расчет двоичной кросс-энтропии
bcHpq = -np.sum(p*np.log(q) +p_conv*np.log(q_conv)) /len(p)
# Вывод результатов кросс-энтропии
print('Кросс-энтропия cHpq:', cHpq)
print('Двоичная кросс-энтропия bcHpq:', bcHpq)
Истина: [0.3 0.3 0.2 0.2] Прогноз: [0.4 0.2 0.3 0.1] Обратные стороны: Истина: [0.7 0.7 0.8 0.8] Прогноз: [0.6 0.8 0.7 0.9]
Кросс-энтропия cHpq: 0.36475754318911824
Двоичная кросс-энтропия bcHpq: 0.585609240747465

В этом коде мы сначала определяем прогнозируемое (q) и истинное (p) распределения вероятностей. Затем вычисляем обратные стороны этих распределений (p_conv и q_conv), которые необходимы для расчета двоичной кросс-энтропии. Две метрики энтропии рассчитываются и выводятся: обычная кросс-энтропия (cHpq) и двоичная кросс-энтропия (bcHpq).

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

4.6.4 Пример 4: Бинарная кросс-энтропия ля более равномерного истинного распределения: Прогноз качества

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

Возвращаясь к примеру с монетой, предположим, что ваша модель предсказывает вероятность выпадения орла в 80% (0.8) и решки в 20% (0.2). Здесь ваше предсказание значительно отличается от равномерного истинного распределения вероятностей (50% на орла и 50% на решку). В результате бинарная перекрестная энтропия будет высокой, что свидетельствует о плохом качестве предсказания, поскольку оно не соответствует ожидаемому равномерному распределению вероятностей.

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

В этом примере мы рассматриваем случай, когда прогнозное распределение (q) и истинное распределение (p) более равномерны, что позволяет нам более детально исследовать эффективность бинарной перекрестной энтропии как меры точности прогноза.

importnumpyasnp

Python
# Прогнозируемое распределение
q = np.array([0.4, 0.2, 0.3, 0.1]) # Прогноз
# Истинное распределение
p = np.array([0.3, 0.3, 0.2, 0.2]) # Истина, довольно равномерное распределение
# Расчет обратных сторон прогноза и истины
p_conv = 1.0 - p# Обратная сторона истины
q_conv = 1.0 - q# Обратная сторона прогноза
# Вывод значений для наглядности
print("Истина:", p, "Прогноз:", q, ' Обратные стороны:', p_conv, q_conv)
# Расчет перекрестной энтропии
cHpq = -np.sum(p*np.log(q)) / len(p)
# Расчет бинарной перекрестной энтропии
bcHpq = -np.sum(p*np.log(q) +p_conv*np.log(q_conv)) /len(p)
# Вывод результатов
print('Перекрестная энтропия cHpq:', cHpq)
print('Бинарная перекрестная энтропия bcHpq:', bcHpq)
Перекрестная энтропия cHpq: 0.36475754318911824
Бинарная перекрестная энтропия bcHpq: 0.585609240747465

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

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

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

4.7 Байесовская статистика

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

Давай проверим нашу теорию с помощью Python. Мы создадим простую программу, которая "достаёт" конфеты из "коробки" и помогает нам обновлять наше предположение о количестве конфет каждого цвета.

importrandom

Text
# Начальное количество конфет каждого цвета
красные = 50
синие = 50
# Функция для имитации выбора конфеты
defвыбрать_конфету():

globalкрасные, синие# Исправляем на 'global'

Python
ifrandom.random() <красные / (красные + синие):
красные -= 1
return'красная'
else: # Исправляем на 'else'
синие -= 1
return'синяя'
# Имитация выбора 10 конфет
for_inrange(10): # Исправляемна 'range'
print(выбрать_конфету())
# Посмотрим, как изменилось количество конфет
print(f"Красных конфет осталось: {красные}")
print(f"Синих конфет осталось: {синие}")

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

Рассмотрим статистическое событие, включающее в себя более одной случайной переменной, которые возникают совместно. Когда мы имеем дело с такими множественными случайными переменными, нас часто интересует совместная вероятностьPrA,B: вероятность того, что A=a и B=bпроизойдут одновременно для заданных элементов a и b.Очевидно, что для любых значенийaиb, PrA,B≤Pr(A=a), посколькуPr(A=a),измеряется независимо от того, что происходит сB. Для того чтобы события Aи Bпроизошли совместно, необходимо, чтобы произошло событие Aи событие B(и наоборот). Таким образом, вероятность совместного наступления Aи Bне может быть выше, чем вероятность наступления каждого из них по отдельности.

Совместная вероятность PrA,B при известном AA называется условной вероятностью и обозначается как PrBA, что представляет собой вероятность наступления B, при условии, что Aуже произошло. Это приводит нас к важной теореме Байеса.

По определению, мы имеем:PrA,B=PrBAPrA.

По симметрии, это также верно: равенствоPrA,B=PrABPrB.

Text
Следовательно, мы получаем формулу Байеса:
PrAB=PrBAPrA/PrB.
Python
# Инициализация вероятностей
p_A = 0.3# Вероятность события A
p_B_given_A = 0.6# Вероятность события B при условии A
p_B = 0.2# Вероятность события B
# Расчет условной вероятности A при условии B по теореме Байеса
p_A_given_B = (p_B_given_A * p_A) / p_B
# Вывод результата
print(f"Условная вероятность A при условии B: {p_A_given_B:.2f}")
Условная вероятность A при условии B: 0.90
p_A представляет собой априорную вероятность наступления события AA, то есть вероятность до получения каких-либо данных.
p_B_given_A- это вероятность наступления события BB, если известно, что событие AA уже произошло.
p_B - априорная вероятность события BB.

p_A_given_B вычисляется как условная вероятность наступления AA при условии, что BB уже произошло, используя теорему Байеса.

4.7.1 Классификация с использованием Наивного Байесовского подхода

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

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

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

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

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

Text
Установим библиотеку scikit-learn: pip install scikit-learn.
Эти библиотеки используются в Python для задач машинного обучения и обработки текста, каждая из них имеет своё назначение:

fromsklearn.model_selectionimporttrain_test_split: Эта функция из библиотеки scikit-learn используется для разделения набора данных на обучающую и тестовую выборки. Это позволяет оценить производительность модели на данных, которые она ранее не видела, и таким образом проверить её обобщающую способность.

fromsklearn.feature_extraction.textimportCountVectorizer: CountVectorizer применяется для преобразования текстовых данных в числовой формат, создавая так называемый "мешок слов" (bagofwords). Он подсчитывает количество вхождений каждого слова из обучающего набора в каждом документе и преобразует тексты в векторы признаков, которые можно использовать в машинном обучении.

fromsklearn.naive_bayesimportMultinomialNB: MultinomialNB представляет собой алгоритм наивного Байеса для многомерных признаков, который часто используется в классификации текстов. Этот алгоритм основан на теореме Байеса и предполагает независимость признаков внутри каждого класса. Он хорошо подходит для работы с векторизованными текстовыми данными, например, с теми, которые были преобразованы с помощью CountVectorizer.

fromsklearn.metricsimportaccuracy_score: Эта функция используется для оценки качества работы моделей машинного обучения. accuracy_score вычисляет точность классификации, которая является долей правильно предсказанных меток относительно всех предсказаний. Точность является одним из самых простых и наиболее часто используемых показателей эффективности моделей классификации.

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

Text
# Импортируем необходимые библиотеки

fromsklearn.model_selectionimporttrain_test_split# Для разделения данных на обучающую и тестовую выборки

fromsklearn.feature_extraction.textimportCountVectorizer# Для преобразования текста в числовой формат через векторизацию

fromsklearn.naive_bayesimportMultinomialNB# Для создания и использования наивного байесовского классификатора

fromsklearn.metricsimportaccuracy_score# Для расчета точности предсказаний модели

Text
# Примеры текстовых сообщений и соответствующие метки (1 - спам, 0 - не спам)
messages = ["бесплатные билеты отправьте СМС", "встретимся на обеде", "специальное предложение купите сейчас", "привет как дела", "не упустите шанс получить скидку"]
labels = [1, 0, 1, 0, 1] # Создаем список меток, где 1 обозначает спам, а 0 - не спам
# Разделяем данные на обучающую и тестовую выборки

messages_train, messages_test, labels_train, labels_test = train_test_split(messages, labels, test_size=0.4, random_state=42) # Разделениеданныхнаобучающую (60%) итестовую(40%) выборки, random_state обеспечивает воспроизводимость разделения

Python
# Векторизация текстовых данных
vectorizer = CountVectorizer() # Инициализация объекта векторизатора
messages_train_vectorized = vectorizer.fit_transform(messages_train) # Обучение векторизатора на обучающей выборке и преобразование текстов в векторы
messages_test_vectorized = vectorizer.transform(messages_test) # Преобразование текстов тестовой выборки в векторы с использованием ранее обученного векторизатора
# Обучение наивного байесовского классификатора
classifier = MultinomialNB() # Инициализация объекта классификатора
classifier.fit(messages_train_vectorized, labels_train) # Обучение классификатора на векторизованных сообщениях и их метках
# Предсказание на тестовых данных
predictions = classifier.predict(messages_test_vectorized) # Применение обученного классификатора для предсказания меток тестовых данных
# Оценка точности модели
accuracy = accuracy_score(labels_test, predictions) # Расчет точности модели путем сравнения предсказанных меток с истинными
print(f"Точность модели: {accuracy}") # Вывод точности классификации на экран
Точность модели: 0.5

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

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

4.7.2 Формулировка задачи

На основе байесовской статистики был разработан популярный алгоритм, известный как классификатор Наивного Байеса. Рассмотрим событие с p переменными x=x1,x2,...,xp∈Xp. Мы предполагаем, чтолюбая переменная xi независима от другой. Для данной метки yусловная вероятность для x выражается как

pxy=ipxiy
Text
На основе теоремы Байеса у нас есть следующая формула:
pyx=pxypypx=ipxiypypx

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

pyx∝ pxypy=ipxiypy

4.7.3 Анализ методов машинного обучения в распознавании рукописных чисел

Теперь мы можем рассмотреть, как кодируется классификатор Наивного Байеса для идентификации рукописных цифр. Мы будем использовать хорошо известный набор данных MNIST для обучения этого классификатора. Набор данных MNIST содержит в общей сложности 70,000 изображений (60,000 для обучения и 10,000 для тестирования) рукописных цифр от 0 до 9, и все эти изображения размечены. Эти изображения были взяты у сотрудников Бюро переписи населения США и студентов американских средних школ.

Задача классификации цифр затем сводится к вычислению вероятности данного изображения x быть цифрой y: p(y|x). Любое изображение x содержит pпикселей xii = 1, 2,..., p, и каждый пиксель xi может принимать значение 1 (когда он включен) или 0 (когда он выключен), и, следовательно, является бинарной переменной.

Затем может быть использовано уравнение (4.12), в котором нам нужно оценить p(y) и p(xi|y). Оба могут быть вычислены с использованием обучающего набора данных MNIST для каждой цифры. Например, в общей сумме 60,000 изображений набора данных MNIST, цифра 4 встречается 5,800 раз, и поэтому вероятность py = 4=580060000. Чтобы оценить p(xi|y), каждый пиксель xi является бинарным и p(xi = 0|y) = 1 − p(xi = 1|y). Оценка pxi=1yможет быть выполнена путём подсчёта, сколько раз пиксель iвключен для метки y, и затем деления этого числа на общее количество изображений с меткой y. В этом простом алгоритме, все, что нам нужно сделать, это подсчитать количество обучающих изображений MNIST для каждой метки y.

Text
Перед началом работы с MNISTубедитесь что установлены сертификаты python, чтобы не получить ошибку типа SSL: CERTIFICATE_VERIFY_FAILED.

Чтобы решить проблемуCERTIFICATE_VERIFY_FAILED, вам необходимо установить SSL-сертификаты для используемой вами среды Python. Если вы используете Python, входящий в состав macOS, или установили его с python.org, обычно эта проблема решается путём запуска скрипта InstallCertificates.command, который идёт в комплекте с вашей установкой Python.

1.Откройте Finder и перейдите в директорию установки Python. Если вы установили Python с python.org, он может быть в директории вроде /Applications/Python 3.x (где 3.x соответствует версии Python, которую вы установили).

Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.7.3 Анализ методов машинного обучения в распознавании рукописных чисел

2. Внутри директории Python найдите файл с названием InstallCertificates.command.

3.Дважды кликните по файлу InstallCertificates.command, чтобы запустить скрипт. Это откроет окно терминала и установит необходимые SSL-сертификаты для вашей среды Python.

Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.7.3 Анализ методов машинного обучения в распознавании рукописных чисел

После запуска этого скрипта попробуйте снова выполнить свой код. Теперь он должен быть способен загрузить набор данных MNIST без ошибок верификации SSL-сертификата.

Если вы используете виртуальную среду или установку Python из источника, отличного от python.org, вам может потребоваться убедиться, что SSL-сертификаты доступны для вашей среды Python. Иногда это можно достичь, установив пакет certifi через pip (pipinstallcertifi), который включает в себя коллекцию корневых сертификатов для проверки надёжности SSL-сертификатов при верификации идентичности хостов TLS. Однако эффективность этого решения может зависеть от того, как настроена ваша среда Python и как она настроена на работу с SSL-сертификатами.

Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 4.7.3 Анализ методов машинного обучения в распознавании рукописных чисел

Чтобы исправить ошибку SSL-сертификатаCERTIFICATE_VERIFY_FAILED на Windows, вам необходимо убедиться, что ваша среда Python имеет доступ к необходимым SSL-сертификатам. Ниже приведена инструкция по исправлению ошибки SSL: CERTIFICATE_VERIFY_FAILED для пользователей Windows:

1. Откройте командную строку (Command Prompt) или PowerShell с правами администратора. Для этого вы можете нажать на кнопку "Пуск", начать вводить "cmd" или "PowerShell", затем кликнуть правой кнопкой мыши на соответствующем приложении и выбрать "Запустить от имени администратора".

2. Найдите директорию, в которой установлен Python. Если вы установили Python с официального сайта python.org, директория обычно будет выглядеть примерно так: C:\Users\ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ\AppData\Local\Programs\Python\Python3X, где Python3X - это версия Python, которую вы установили. Если вы не уверены в расположении установки Python, вы можете выполнить команду wherepython в командной строке, чтобы найти его.

3. Перейдите в директорию, где установлен Python, используя команду cd с правильным путем к директории. Например: cd C:\Users\ВАШЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ\AppData\Local\Programs\Python\Python3X.

4. Внутри этой директории запустите скрипт для установки сертификатов. Вы можете сделать это, выполнив следующую команду:.\Scripts\pip.exe install --upgradecertifi. Эта команда установит или обновит пакет certifi, который содержит набор корневых сертификатов.

5. После установки или обновления certifi попробуйте снова выполнить свой код.

Text
Для решения проблемы с SSL сертификатомCERTIFICATE_VERIFY_FAILED в среде Python на Ubuntu, следуйте этим шагам:
1. Откройте терминал. Вы можете сделать это, нажав Ctrl + Alt + T на клавиатуре.
2. Обновите список пакетов и установите обновления, чтобы убедиться, что все пакеты системы актуальны. Выполните следующие команды:

sudoaptupdate

sudoaptupgrade

Text
3. Установите пакеты ca-certificates, которые содержат набор общепринятых сертификатов, и openssl, если они ещё не установлены:
sudo apt install ca-certificates openssl

4. Если вы используете Python, который был установлен через менеджер пакетов apt, сертификаты должны уже быть настроены. В случае если Python был установлен другим способом, вы можете установить сертификаты вручную.

Text
5. Чтобы установить или обновить сертификаты для среды Python, выполните следующую команду:
sudo /usr/local/bin/python3 -m pip install --upgrade certifi
Замените /usr/local/bin/python3 на путь к интерпретатору Python, который вы используете, если он отличается.

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

Если у вас есть установленная среда Python через pyenv или virtualenv, убедитесь, что сертификаты доступны и для этих сред, поскольку они могут использовать свои собственные наборы сертификатов. В таком случае, установите certifi внутри каждой виртуальной среды:

Text
pipinstall --upgradecertifi
Весь процесс обучения сводится к подсчету:

importtensorflowastf

importnumpyasnp

Python
# Загрузка данных MNIST с использованием TensorFlow
mnist = tf.keras.datasets.mnist
(training_images, training_labels), _ = mnist.load_data()
# Нормализацияизображений
training_images = training_images / 255.0
# Инициализация счетчиков для вероятностей
num_labels = 10# Количество меток (цифр от 0 до 9)
pixel_count = 28 * 28# Размер изображений MNIST 28x28
p_y = np.zeros(num_labels) # Вероятность каждой цифры
p_xi_given_y = np.zeros((num_labels, pixel_count)) # Вероятность каждого пикселя для каждой цифры
# Подсчет для оценки вероятностей
forlabelintraining_labels:
p_y[label] +=1# Считаем вхождения каждой цифры
fori, imageinenumerate(training_images):
label = training_labels[i]
# Преобразуем изображение в бинарный вектор
binary_image = np.round(image).flatten()
p_xi_given_y[label] +=binary_image# Считаем вхождения активных пикселей для каждой цифры
# Нормализация для получения вероятностей
p_y/=len(training_labels)
p_xi_given_y/=np.sum(p_xi_given_y, axis=1, keepdims=True)
# Добавление малой константы epsilon к вероятностям перед логарифмированием
epsilon = 1e-9
p_y+=epsilon
p_xi_given_y += epsilon
# Функция для классификации нового изображения
defclassify(image):
binary_image = np.round(image).flatten()
# Вычисление логарифмической вероятности каждой цифры для данного изображения
log_prob_y = np.log(p_y) +np.dot(binary_image, np.log(p_xi_given_y.T))
# Возврат цифры с наибольшей вероятностью
returnnp.argmax(log_prob_y)
# Тестирование классификатора с новым изображением (пример)
test_image = training_images[0] # Пример изображения для тестирования
predicted_digit = classify(test_image)
print("Предсказаннаяцифра:", predicted_digit)
Предсказанная цифра: 5

4.7.4 Алгоритм классификации на основе Наивного Байеса

Text
# Импорт необходимых библиотек

importnumpyasnp

importtensorflowastf

importmatplotlib.pyplotasplt

Python
# Загрузка набора данных MNIST с помощью TensorFlow Keras
(mnist_train_images, mnist_train_labels), (mnist_test_images, mnist_test_labels) = tf.keras.datasets.mnist.load_data()
# Функция для преобразования изображений
deftransform(images, labels):
# Преобразование данных: пиксели считаются "включенными" при значении >= 128
transformed_images = np.floor(images / 128).astype(np.float32)
returntransformed_images, labels.astype(np.float32)
# Преобразование тренировочного и тестового наборов данных
train_images, train_labels = transform(mnist_train_images, mnist_train_labels)
test_images, test_labels = transform(mnist_test_images, mnist_test_labels)
# Отображение примера изображения из набора данных
image_index = 8888#Произвольныйиндексизображения
plt.imshow(train_images[image_index], cmap='Greys')
plt.title(f'Label: {train_labels[image_index]}')
plt.show()
# Инициализация массивов для подсчета вероятностей
ycount = np.ones(10) # Для каждой из 10 цифр
xcount = np.ones((784, 10)) # Для каждого из 784 пикселей в каждой цифре
# Обработкатренировочногонабораданных
forimage, labelinzip(train_images, train_labels):
x = image.reshape((784,))
y = int(label)
ycount[y] += 1# Подсчет количества каждой цифры
xcount[:,y] +=x# Подсчет "включенных" пикселей для каждой цифры
# Вычисление вероятностей p(xi|y)
foriinrange(10):
xcount[:,i] = xcount[:, i] /ycount[i]
# Вычислениевероятности p(y)
py = ycount/np.sum(ycount)
# Отображение "обученной" модели
fig, figarr = plt.subplots(1, 10, figsize=(15, 15))
foriinrange(10):
figarr[i].imshow(xcount[:, i].reshape((28, 28)), cmap='hot')
figarr[i].axes.get_xaxis().set_visible(False)
figarr[i].axes.get_yaxis().set_visible(False)
plt.show()
# Вывод вероятностей для каждой цифры
print(py)
[0.09871688 0.11236461 0.09930012 0.10218297 0.09736711 0.09035161

0.09863356 0.10441593 0.09751708 0.09915014]

Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 0.09863356 0.10441593 0.09751708 0.09915014]
Text
Рисунок: Один образец изображения рукописной цифры из набора данных MNIST.
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 0.09863356 0.10441593 0.09751708 0.09915014]
Text
Рисунок:Средний вид рукописных цифр.

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

4.7.5 Тестирование модели Наивного Байеса

Мы теперь исследуем производительность этой статистически базируемой модели, используя тестовый набор данных MNIST. Обучение, которое представляет собой просто подсчет поверх обучающего набора данных, завершено выше, дает нам pxi=1yи p(y). Для данного изображения x из тестового набора данных, мы вычисляем вероятность соответствующую метке y, которая вычисляется p(y|x) используя уравнение (4.12), где logpxyв свою очередь вычисляется с использованием обученной модели. Чтобы избежать цепочки умножения малых вероятностных чисел, мы вычисляем следующие логарифмы вместо этого (известные как "лог-вероятность"):

log p(y|x) ≈logpxy + log p(y) =ilog p(xi|y) + log p(y)

Для данного изображения x, признак xi бинарен и принимает значения либо 1, либо 0. Поскольку мы используем модель, обученную для вычисления вероятностей, у нас будет

pxi=1y=pxi=1y ибо предсказание xi включеноpxi=0y=1-pxi=1y ибо предсказание xi выключено
Text
Уравнение может быть записано в одном виде, используя математический трюк:
pxiy=pxi=1yxi1-pxi=1y1-xi

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

ilog pxiy=ixilog pxi=1y+1-xilog1-pxi=1y

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

Чтобы избежать повторного вычисления логарифмов, мы предварительно вычисляем logp(y) для всех y, а также logp(xi|y) и log (1 − p(xi|y)) для всех пикселей.

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

importnumpyasnp

importtensorflowastf

importmatplotlib.pyplotasplt

Text
# Загрузка набора данных MNIST, который содержит рукописные цифры для обучения моделей машинного обучения
(mnist_train_images, mnist_train_labels), (mnist_test_images, mnist_test_labels) = tf.keras.datasets.mnist.load_data()
# Функция для предварительной обработки изображений
deftransform(images, labels):

"""

Text
Преобразует исходные изображения, делая пиксели бинарными (0 или 1).
Пиксели считаются "включенными" (1), если их значение >= 128, в противном случае - "выключенными" (0).

Принимает на вход изображения и метки, возвращает преобразованные изображения и метки.

"""

Python
transformed_images = np.floor(images / 128).astype(np.float32) # Деление на 128 и округление вниз преобразует градации серого в бинарный формат
returntransformed_images, labels.astype(np.float32)
# Применение функции преобразования к тренировочному и тестовому наборам данных
train_images, train_labels = transform(mnist_train_images, mnist_train_labels)
test_images, test_labels = transform(mnist_test_images, mnist_test_labels)
# Визуализация примера изображения после преобразования
image_index = 8888# Выбор случайного индекса для демонстрации
plt.imshow(train_images[image_index], cmap='Greys') # Использование оттенков серого для визуализации
plt.title(f'Метка: {int(train_labels[image_index])}') # Отображение метки изображения
plt.show()
# Инициализация массивов для подсчета вероятностей
ycount = np.ones(10) # Начальное значение 1 предотвращает деление на ноль (сглаживание Лапласа)
xcount = np.ones((784, 10)) # Аналогично, для избежания деления на ноль
# Обработка тренировочного набора данных для обучения наивного байесовского классификатора
forimage, labelinzip(train_images, train_labels):
x = image.reshape((784,)) # Преобразование изображения в вектор
y = int(label)
ycount[y] += 1# Учет встречаемости каждой цифры
xcount[:,y] +=x# Суммирование "включенных" пикселей для каждой цифры
# Нормализация для получения вероятностей p(xi|y)
foriinrange(10):
xcount[:,i] /=ycount[i] # Деление количества "включенных" пикселей на общее количество цифр каждого класса
# Вычисление априорной вероятности каждой цифры p(y)
py = ycount/np.sum(ycount)
# Визуализация "обученной" модели: отображение вероятности "включения" каждого пикселя для каждой цифры
fig, figarr = plt.subplots(1, 10, figsize=(15, 15))
foriinrange(10):

figarr[i].imshow(xcount[:, i].reshape((28, 28)), cmap='hot') # Использованиетепловойкартыдлявизуализации

Python
figarr[i].axis('off') # Скрытие осей для чистоты визуализации
plt.show()
# Вычисление и визуализация вероятностей для каждой цифры на тестовых изображениях
logxcount = np.log(xcount) # Логарифмирование для устранения проблем с плавающей точкой
logxcountneg = np.log(1 - xcount) # Логарифм вероятности "выключенного" состояния пикселя
logpy = np.log(py) # Логарифм априорной вероятности
fig, figarr = plt.subplots(2, 10, figsize=(15, 3))
ctr = 0# Счетчик для отслеживания индекса субплота
y_true = [] # Список для хранения истинных меток
pxm = np.array([]) # Массив для хранения максимальных вероятностей
xi_pred = [] # Список для хранения предсказанных меток
# Оценкамоделинатестовыхизображениях
forimage, labelinzip(test_images, test_labels):
x = image.reshape((784,))
y_true.append(int(label))
logpx = logpy.copy() # Копирование логарифмов априорных вероятностей
foriinrange(10):
# Вычисление логарифма совместной вероятности P(x|y)P(y)
logpx[i] += np.dot(logxcount[:, i], x) + np.dot(logxcountneg[:, i], 1 - x)
logpx-=np.max(logpx) # Нормализация для предотвращения числового переполнения
px = np.exp(logpx) # Возвращение к вероятностям из логарифмов
px *= py# Умножение на априорную вероятность
px /= np.sum(px) # Нормализация для получения условных вероятностей P(y|x)
pxm = np.append(pxm, np.max(px)) # Запись максимальной вероятности
xi_pred.append(np.argmax(px)) # Запись предсказанной цифры
figarr[1, ctr].bar(range(10), px, color='blue') # Визуализация вероятностей для каждой цифры
figarr[1, ctr].axis('off') # Скрытие осей
figarr[0, ctr].imshow(x.reshape((28, 28)), cmap='hot') # Отображение тестового изображения
figarr[0, ctr].axis('off') # Скрытие осей
ctr += 1
ifctr == 10: # Ограничение на количество отображаемых примеров

break

Python
plt.show()
# Вывод результатов классификации
y_true = np.array(y_true)
xi_pred = np.array(xi_pred)
print('Истинные метки: ', y_true)
print('Предсказанные цифры:', xi_pred)
print('Правильно?', np.equal(y_true, xi_pred))
print('Максимальная вероятность:', pxm)
Истинные метки: [7 2 1 0 4 1 4 9 5 9]
Предсказанные цифры: [7 2 1 0 4 1 4 9 4 9]
Правильно? [True TrueTrueTrueTrueTrueTrueTrue False True]
Максимальная вероятность: [1. 1. 1. 1. 1. 1.

0.99999999 0.9999996 0.99999758 0.99999899]

Иллюстрация из учебника: Модели обучения на основе статистики и вероятности
Иллюстрация из учебника: Модели обучения на основе статистики и вероятности — 0.99999999 0.9999996 0.99999758 0.99999899]

Этот код представляет собой реализацию наивного байесовского классификатора для распознавания рукописных цифр из набора данных MNIST, используя библиотеки NumPy, TensorFlow и Matplotlib для обработки данных и визуализации. Он загружает MNIST, преобразует изображения в бинарный формат (пиксели "включены" или "выключены" в зависимости от их интенсивности), а затем обучает модель, подсчитывая вероятности появления "включенных" пикселей для каждой цифры и вероятности каждой цифры в обучающем наборе. После обучения модель тестируется на тестовом наборе данных, где для каждого изображения вычисляется вероятность принадлежности к каждому классу (цифре), выбирается класс с наибольшей вероятностью, и результаты предсказаний визуализируются вместе с их вероятностями и сравнением с истинными метками.

4.9 Заключение

Результаты тестирования указывают на то, что представленный классификатор допустил ошибку при классификации одной из первых десяти цифр в тестовом наборе данных. Конкретно, девятая цифра, которая должна была быть распознана как "5", была ошибочно классифицирована как "4". Важно отметить, что уровень уверенности классификатора в этом неверном предсказании был высок и приближался к 1. Это указывает на возможные недостатки в предположениях, лежащих в основе модели классификации. Одно из таких предположений заключается в том, что каждый пиксель изображения генерируется независимо от других, исходя исключительно из его метки класса. Однако реальность значительно сложнее, поскольку изображение цифры представляет собой сложную функцию, в которой учитываются взаимосвязи между пикселями. Ограниченность одиночной статистической информации в этом контексте подчеркивает необходимость более продвинутых методов для задач классификации изображений.

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

Альтернативный подход заключается в использовании кросс-энтропии или бинарной кросс-энтропии для оценки качества предсказаний. Используя обучающий набор данных, мы можем вычислить вероятностьp(xiyi)для каждого изображенияxiи метки классаyi, гдеi=1,2,...,9. Затем, рассчитав кросс-энтропию между вероятностямимежду pxiytestдля тестового изображения иpxiyj,для каждой возможной метки классаyjможно определитьyjс наименьшей кросс-энтропией как наиболее вероятную метку класса для тестового изображения.

Text
Этот пример иллюстрирует ценность статистического анализа в задачах классификации и в машинном обучении в целом. Также демонстрируется, как можно вычислить статистические показатели для конкретных наборов данных. Несмотря на некоторые ограничения, наивные классификаторы продолжают быть мощным инструментом в арсенале машинного обучения, особенно в задачах, где не применяются строгие физические законы. Они находят широкое применение в медицинских приложениях, системах рекомендаций, классификации текстов и в создании прогнозов, основанных на реальных данных. Для обучения наивных Байесовских классификаторов можно использовать библиотеку Scikit-learn, которая предоставляет удобный и гибкий модуль sklearn.naive_bayes. Кроме того, для разработки более сложных моделей машинного обучения, включая глубокие нейронные сети, широко используется библиотека TensorFlow. TensorFlow предлагает обширный набор инструментов и API для проектирования, тренировки и развертывания как простых, так и сложных моделей глубокого обучения, что делает его ценным дополнением к Scikit-learn для решения более сложных задач в области машинного обучения.

Проверь себя

Какая идея лучше всего описывает фокус главы "Модели обучения на основе статистики и вероятности"?

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

Какие действия помогают закрепить материал главы?

Пройти тест