Генеративное моделирование. Обзор

Что значит сделать выборку

Здравствуйте и вновь добро пожаловать на занятия по теме «Глубокое обучение без учителя, часть 2».

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

Мы уже видели, как делаются выборки в обычном смысле, например, как делается выборка из гауссового распределения, или выборка из распределения Бернулли, которое описывает нечто вроде броска монеты, так что результат всегда будет равен 0 или 1. В последующем мы сделаем ещё один шаг в этом направлении.

Наша цель состоит в том, чтобы создать байесовский классификатор. Напомним, что байесовский классификатор –генеративная модель. Это означает, что для каждого класса y мы изучаем распределение p(x|y), а не моделируем непосредственно p(y|x). В данном случае название «генеративная» весьма уместно, поскольку, как вы увидите далее, мы используем эту модель для генерации данных.

Есть несколько способов изучить подобное распределение – на самом деле, множество способов. Впрочем, стандартным является простая подгонка гауссового распределения к данным. Чтобы найти p(x|y), мы должны для каждого y сделать следующее: 1) найти все x, принадлежащие этому классу; и 2) найти и сохранить их среднее значение и ковариацию. Как вы, возможно, помните, мы можем принять решение о классификации на основе теоремы Байеса путём нахождения аргумента максимизации (argmax) по p(y|x):

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

Первый способ сделать выборку – выбрать класс. Предположим, мы выбрали класс y = 1. Мы знаем, что p(x|y = 1) имеет гауссово распределение, и умеем делать выборку из этого распределения. Тогда мы можем очень легко сделать выборку из этого гауссового распределения с помощью библиотеки Scipy.

Второй способ сделать выборку так же прост с механической точки зрения, однако концептуально несколько отличается. Мы можем изобразить нашу модель графически, где каждый узел представляет отдельное распределение. Конкретнее говоря, у нас есть распределения p(y) и p(x|y). Диаграмма (см. слайд) весьма наглядно показывает зависимость между x и y. Мы видим, что y влияет на вид x, что логично: если y = 1, то генерируемое нами изображение будет совершенно отличаться от того, которое было бы при y = 8. Ключевым здесь является то, что y имеет собственное распределение – категориальное, или дискретное распределение. Чтобы узнать p(y), мы просто берём количество раз, когда каждое изображение появляется в учебных данных, и делим его на количество учебных примеров. Но поскольку p(y) имеет собственное распределение, мы можем сделать из него выборку.

Иначе говоря, первый обсуждавшийся нами способ – это лишь взятие выборки из данного гауссового распределения, реализуемое простым вызовом функции библиотеки Scipy. Эта функция принимает в качестве аргумента один параметр y, так что мы можем выбрать, из какого гауссового распределения делать выборку. Второй метод выборки концептуально отличается. Тут мы делаем выборку из совместного распределения p(x, y). Поскольку x зависит от y, мы вначале делаем выборку из априорной вероятности p(y), которую можем найти из данных, а затем делаем выборку из p(x|y), что мы научились делать ранее.

Полагаю, всё рассказано достаточно подробно, чтобы вы смогли реализовать этот алгоритм самостоятельно. На данный момент предполагается, что вы уже загрузили данные, зная как это делать с помощью Numpy, так что следующим будет упражнение – написать байесовский классификатор, который соответствует данным и способный генерировать из них выборку при помощи двух только что обсуждавшихся методов – выборку из заданного y, каковое является, другими словами, p(x|y), и выборку из совместного распределения p(x,y).

Демонстрация выборки байесовского классификатора

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

Первое, что мы импортируем – это util. Обратите внимание, что оно ссылается на файл util.py в текущей папке и просто содержит полезный код для вещей вроде загрузки данных. Далее идут библиотеки Numpy, Matplotlib и, конечно же, multivariate_normal для осуществления выборки:

from __future__ import print_function, division

from builtins import range, input

# Note: you may need to update your version of future

# sudo pip install -U future

import util

import numpy as np

import matplotlib.pyplot as plt

from scipy.stats import multivariate_normal as mvn

Затем определяется класс BayesClassifier. Конструктора нет, так что мы сразу переходим к функции fit. Вначале мы получаем K – количество классов; после этого инициируем список, содержащий информацию по гауссовому распределению. Не забывайте, что гауссово распределение описывается двумя параметрами – средним значением и ковариацией, так что в цикле для каждого класса мы вначале получаем все x, принадлежащие этому классу, находим их среднее значение и ковариацию, а затем сохраняем их в словаре g, после чего добавляем g в список гауссовых распределений для позднейшего использования. Это всё, что касается функции fit. Обратите внимание, что поскольку классы нумеруются числами от 0 до K-1, каждый класс по умолчанию будет индексировать и соответствующее гауссового распределение в нашем списке распределений.

class BayesClassifier:

  def fit(self, X, Y):

    # assume classes are numbered 0…K-1

    self.K = len(set(Y))

    self.gaussians = []

    self.p_y = np.zeros(self.K)

    for k in range(self.K):

      Xk = X[Y == k]

      self.p_y[k] = len(Xk)

      mean = Xk.mean(axis=0)

      cov = np.cov(Xk.T)

      g = {‘m’: mean, ‘c’: cov}

      self.gaussians.append(g)

    # normalize p(y)

    self.p_y /= self.p_y.sum()

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

  def sample_given_y(self, y):

    g = self.gaussians[y]

    return mvn.rvs(mean=g[‘m’], cov=g[‘c’])

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

  def sample(self):

    y = np.random.choice(self.K, p=self.p_y)

    return self.sample_given_y(y)

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

if __name__ == ‘__main__’:

  X, Y = util.get_mnist()

  clf = BayesClassifier()

  clf.fit(X, Y)

  for k in range(clf.K):

    # show one sample for each class

    # also show the mean image learned

    sample = clf.sample_given_y(k).reshape(28, 28)

    mean = clf.gaussians[k][‘m’].reshape(28, 28)

    plt.subplot(1,2,1)

    plt.imshow(sample, cmap=’gray’)

    plt.title(“Sample”)

    plt.subplot(1,2,2)

    plt.imshow(mean, cmap=’gray’)

    plt.title(“Mean”)

    plt.show()

  # generate a random sample

  sample = clf.sample().reshape(28, 28)

  plt.imshow(sample, cmap=’gray’)

  plt.title(“Random Sample from Random Class”)

  plt.show()

Запустим программу и посмотрим, что у нас получится.

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

Гауссовы смеси распределений. Обзор

Напомним, что одним из основных выводов, сделанных из нашего предыдущего примера с единичным гауссовым распределением в коде, был тот, что результаты выглядели очень размытыми. Почему же так получилось? Установив, что каждое p(x|y) является единичным гауссовым распределением, нам пришлось «заставить» это единичное гауссово распределение соответствовать мультимодальному распределению. Что понимается под мультимодальным распределением? Напомним, что мода – это самое часто встречающееся значение случайной величины. В понятиях непрерывной случайной переменной, имеющей плотность распределения вероятностей, это любой локальный максимум функции плотности распределения вероятностей. Когда мы говорим о мультимодальных распределениях, то подразумеваем распределение, имеющее несколько пиков.

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

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

Поговорим о нескольких интересных фактах, касающихся гауссовых смесей распределения. Гауссова смесь распределений – это модель со скрытыми переменными, и данный конкретный факт представляет интерес для данного курса. Всё обучение без учителя посвящено скрытым переменным. В гауссовых смесях распределений скрытая переменная обозначается через z и показывает, какому кластеру принадлежат данные x. Любопытно, что окончательная плотность распределения вероятностей гауссовой смеси распределений очень похожа на байесовский классификатор. Пусть, например, есть модель с двумя кластерами:

Мы можем считать p(z) априорной вероятностью, что любое x принадлежит определённому кластеру. Это значит, что даже не рассматривая какое-либо x, это будет вероятностью того, что любая случайным образом выбранная точка принадлежит некоторому кластеру. При взгляде на данное уравнение оно кажется не слишком сложным, ведь мы знаем, что значит каждая его часть. Вновь-таки, p(z) имеет категориальное, или дискретное, распределение и представляет собой лишь вектор из чисел, сумма которых равна 1, а каждое p(x|z) является единичным гауссовым распределением.

Итак, p(z) является априорной вероятностью, показывающей, безотносительно к какому-либо x, наиболее вероятную принадлежность к некоторому кластеру. Приводя конкретный пример, это как опросить 1000 человек, болеют ли они чем-либо, и та часть людей, которые болеют, будет априорной вероятностью p(z = 1).

Для ясности, поскольку я ранее видел, как некоторые делали эту ошибку, поясним, что p(z) – это не то же самое, что p(z|x). Проводя аналогию, можно сказать, что p(z) – это лишь частота людей в популяции, имеющих заболевание, а p(z|x) – это когда человек пошёл к врачу и тот проверил, есть ли у того заболевание или нет, то есть вероятность основана на данных от пациента, которые мы и обозначаем через x, а не просто на доле заболевших в популяции, которая представлена вероятностью p(z).

Пусть у нас есть x и мы хотим узнать, к какому кластеру оно принадлежит. Тогда мы можем найти p(z|x), используя теорему Байеса:

Тут всё легко, поскольку мы уже знаем все величины в правой части уравнения: p(x|z) – это, как обсуждалось ранее, просто единичное гауссово распределение, p(z) – априорная вероятность, являющаяся лишь категориальным распределением, а p(x) – это «доказательство» и является, как было показано ранее, просто суммой.

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

Так вот, есть ещё кое-что интересное. Гауссовы смеси распределений обучаются при помощи так называемого алгоритма максимизации ожиданий или, для краткости, EM (expectation maximization). В данном курсе нас не интересуют подробности алгоритма, однако важным является то, почему нам необходимо его использовать и как он вписывается в общую схему. Мы используем максимизацию ожиданий, когда имеем модель скрытой переменной и не можем найти решение для нахождения максимума функции правдоподобия в аналитическом виде. Однако учитывайте – это важный момент, в котором многие часто путаются, – у нас нет какой-либо особой цели, мы всё так же просто хотим найти максимум функции правдоподобия. Простой пример: пусть мы хотим смоделировать рост всех слушателей курса по машинному обучению в виде гауссового распределения. Мы измеряем рост всех слушателей, берём среднее выборки и её стандартное отклонение – и получаем гауссово распределение. Как вы знаете, есть простые уравнения для нахождения среднего значения и стандартного отклонения выборки, а принцип максимума правдоподобия позволяет их определить. Конкретнее говоря, мы находим выражение для функции правдоподобия и максимизируем её при помощи дифференциального счисления относительно параметров – среднего значения и стандартного отклонения. О максимизации ожидания речь заходит тогда, когда решения нет, а цель всё та же.

Почему всё это важно?

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

Однако есть и ещё одна причина, по которой вариационный вывод может оказаться интересным. Как вы, возможно, помните, слабостью алгоритмов вроде кластеризации методом k-средних и гауссовых смесей распределений является то, что нам необходимо выбрать количество кластеров в алгоритме как гиперпараметр, и если мы выберем неправильно, модель будет плохо работать. Вот изящный приём: можно использовать версию гауссовых смесей распределений с вариационным выводом, которая на самом деле содержит бесконечное количество кластеров. Однако большинство этих кластеров окажутся пустыми, так что вариационная гауссова смесь распределений некоторым образом автоматически найдёт нужное количество кластеров. Будет ли это эффективно – другой вопрос, но будем надеяться, что будет. В этом курсе, поскольку он сосредоточен на глубоком обучении, а не на байесовском машинном, мы не будем делать каких-либо вариационных выводов или создавать гауссовы смеси распределений в коде. К счастью, в Sci-Kit Learn есть встроенная гауссова смесь распределений с вариационным выводом, которой мы и можем воспользоваться.

И наконец я хотел бы познакомить вас с простой графической моделью, которая поможет наглядно представить нашу модель в байесовском стиле. На самом деле это очень простая байесовская сеть. Как вы, возможно, помните из обсуждения нашего предыдущего байесовского классификатора, у нас была простая диаграмма, показывающая связь от y к x. В нашей же новой модели, где мы моделируем p(x|z) в виде гауссовой смеси распределений, она включает в себя скрытую переменную z, так что на рисунке мы показываем связь от y к z, а оттуда – к x (см. слайд). С точки зрения генерации данных или выборки, мы просто следуем стрелочкам. Это трёхэтапный процесс: вначале мы выбираем y (оно может быть и заданным, если мы хотим сгенерировать изображение конкретной цифры), затем выбираем один из кластеров z для этой данной цифры y – это показывает различные способы, которыми люди могут написать данную конкретную цифру, а после этого выбираем x|z, что является единичным гауссовым распределением и представляет собой изображение цифры.

Подведём итог. Большая часть этой лекции представляла собой лишь понятия и идеи, но их реализация должна быть очень простой. Конкретнее говоря, мы берём наш предыдущий байесовский классификатор и вместо единичного гауссового распределения используем вариационную гауссову смесь распределений из библиотеки Sci-Kit Learn. Разумеется, она сразу идёт с функциями fit и sample, так что работы совсем немного, хотя мы должны соответствовать API, то есть должны проверить возвращаемые типы, что узнать, что они из себя представляют, и использовать соответствующим образом. Так что сначала попробуйте сделать всё самостоятельно, а затем, в следующей лекции, мы рассмотрим код.

Демонстрация выборки байесовского классификатора с гауссовой смесью распределений

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

Первое, что можно заметить в этой программе, – что она почти идентична предыдущей с единичным гауссовым распределением, с тем лишь исключением, что теперь мы используем вариационную гауссову смесь распределений библиотеки Sci-Kit Learn.

Что же изменилось? У нас по-прежнему есть класс BayesClassifier, и он по-прежнему содержит список гауссовых распределений, но теперь каждое из них будет объектом байесовской гауссовой смеси распределений.

Обратите внимание, что конструктор принимает один аргумент. Что это за аргумент? Если вы глянете в документацию, то узнаете, что это целое число, определяющее максимальное количество кластеров. Таким образом, хотя формально вариационная гауссова смесь распределений имеет бесконечное количество кластеров, в действительности она должна в какой-то момент остановиться. Это и есть верхний предел, жёстко ограничивающий количество кластеров. Я произвольным образом выбрал 10, однако естественных кластеров может быть больше или меньше – я не всматривался.

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

from __future__ import print_function, division

from builtins import range, input

# Note: you may need to update your version of future

# sudo pip install -U future

import util

import numpy as np

import matplotlib.pyplot as plt

from sklearn.mixture import BayesianGaussianMixture

class BayesClassifier:

  def fit(self, X, Y):

    # assume classes are numbered 0…K-1

    self.K = len(set(Y))

    self.gaussians = []

    self.p_y = np.zeros(self.K)

    for k in range(self.K):

      print(“Fitting gmm”, k)

      Xk = X[Y == k]

      self.p_y[k] = len(Xk)

      gmm = BayesianGaussianMixture(10)

      gmm.fit(Xk)

      self.gaussians.append(gmm)

    # normalize p(y)

    self.p_y /= self.p_y.sum()

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

Обратите внимание, что кое-что, что здесь использовано, не отображено в официальной документации по Sci-Kit Learn, так что нужно просмотреть их код на Github, чтобы выяснить, какие атрибуты надо рассматривать.

  def sample_given_y(self, y):

    gmm = self.gaussians[y]

    sample = gmm.sample()

    # note: sample returns a tuple containing 2 things:

    # 1) the sample

    # 2) which cluster it came from

    # we’ll use (2) to obtain the means so we can plot

    # them like we did in the previous script

    # we cheat by looking at “non-public” params in

    # the sklearn source code

    mean = gmm.means_[sample[1]]

    return sample[0].reshape(28, 28), mean.reshape(28, 28)

  def sample(self):

    y = np.random.choice(self.K, p=self.p_y)

    return self.sample_given_y(y)

Далее идёт раздел main, почти такой же, что и ранее. Мы выводим на экран образец из каждого класса, а затем один образец из случайным образом выбранного класса.

if __name__ == ‘__main__’:

  X, Y = util.get_mnist()

  clf = BayesClassifier()

  clf.fit(X, Y)

  for k in range(clf.K):

    # show one sample for each class

    # also show the mean image learned

    sample, mean = clf.sample_given_y(k)

    plt.subplot(1,2,1)

    plt.imshow(sample, cmap=’gray’)

    plt.title(“Sample”)

    plt.subplot(1,2,2)

    plt.imshow(mean, cmap=’gray’)

    plt.title(“Mean”)

    plt.show()

  # generate a random sample

  sample, mean = clf.sample()

  plt.subplot(1,2,1)

  plt.imshow(sample, cmap=’gray’)

  plt.title(“Random Sample from Random Class”)

  plt.subplot(1,2,2)

  plt.imshow(mean, cmap=’gray’)

  plt.title(“Corresponding Cluster Mean”)

  plt.show()

Запустим программу и посмотрим, что у нас получится. Да, и не забудьте приготовиться к тому, что ждать придётся намного дольше, чем в прошлой программе.

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

Тут всё легко, поскольку мы уже знаем все величины в правой части уравнения: p(x|z) – это, как обсуждалось ранее, просто единичное гауссово распределение, p(z) – априорная вероятность, являющаяся лишь категориальным распределением, а p(x) – это «доказательство» и является, как было показано ранее, просто суммой.

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

Так вот, есть ещё кое-что интересное. Гауссовы смеси распределений обучаются при помощи так называемого алгоритма максимизации ожиданий или, для краткости, EM (expectation maximization). В данном курсе нас не интересуют подробности алгоритма, однако важным является то, почему нам необходимо его использовать и как он вписывается в общую схему. Мы используем максимизацию ожиданий, когда имеем модель скрытой переменной и не можем найти решение для нахождения максимума функции правдоподобия в аналитическом виде. Однако учитывайте – это важный момент, в котором многие часто путаются, – у нас нет какой-либо особой цели, мы всё так же просто хотим найти максимум функции правдоподобия. Простой пример: пусть мы хотим смоделировать рост всех слушателей курса по машинному обучению в виде гауссового распределения. Мы измеряем рост всех слушателей, берём среднее выборки и её стандартное отклонение – и получаем гауссово распределение. Как вы знаете, есть простые уравнения для нахождения среднего значения и стандартного отклонения выборки, а принцип максимума правдоподобия позволяет их определить. Конкретнее говоря, мы находим выражение для функции правдоподобия и максимизируем её при помощи дифференциального счисления относительно параметров – среднего значения и стандартного отклонения. О максимизации ожидания речь заходит тогда, когда решения нет, а цель всё та же.

Почему всё это важно?

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

Однако есть и ещё одна причина, по которой вариационный вывод может оказаться интересным. Как вы, возможно, помните, слабостью алгоритмов вроде кластеризации методом k-средних и гауссовых смесей распределений является то, что нам необходимо выбрать количество кластеров в алгоритме как гиперпараметр, и если мы выберем неправильно, модель будет плохо работать. Вот изящный приём: можно использовать версию гауссовых смесей распределений с вариационным выводом, которая на самом деле содержит бесконечное количество кластеров. Однако большинство этих кластеров окажутся пустыми, так что вариационная гауссова смесь распределений некоторым образом автоматически найдёт нужное количество кластеров. Будет ли это эффективно – другой вопрос, но будем надеяться, что будет. В этом курсе, поскольку он сосредоточен на глубоком обучении, а не на байесовском машинном, мы не будем делать каких-либо вариационных выводов или создавать гауссовы смеси распределений в коде. К счастью, в Sci-Kit Learn есть встроенная гауссова смесь распределений с вариационным выводом, которой мы и можем воспользоваться.

И наконец я хотел бы познакомить вас с простой графической моделью, которая поможет наглядно представить нашу модель в байесовском стиле. На самом деле это очень простая байесовская сеть. Как вы, возможно, помните из обсуждения нашего предыдущего байесовского классификатора, у нас была простая диаграмма, показывающая связь от y к x. В нашей же новой модели, где мы моделируем p(x|z) в виде гауссовой смеси распределений, она включает в себя скрытую переменную z, так что на рисунке мы показываем связь от y к z, а оттуда – к x (см. слайд). С точки зрения генерации данных или выборки, мы просто следуем стрелочкам. Это трёхэтапный процесс: вначале мы выбираем y (оно может быть и заданным, если мы хотим сгенерировать изображение конкретной цифры), затем выбираем один из кластеров z для этой данной цифры y – это показывает различные способы, которыми люди могут написать данную конкретную цифру, а после этого выбираем x|z, что является единичным гауссовым распределением и представляет собой изображение цифры.

Подведём итог. Большая часть этой лекции представляла собой лишь понятия и идеи, но их реализация должна быть очень простой. Конкретнее говоря, мы берём наш предыдущий байесовский классификатор и вместо единичного гауссового распределения используем вариационную гауссову смесь распределений из библиотеки Sci-Kit Learn. Разумеется, она сразу идёт с функциями fit и sample, так что работы совсем немного, хотя мы должны соответствовать API, то есть должны проверить возвращаемые типы, что узнать, что они из себя представляют, и использовать соответствующим образом. Так что сначала попробуйте сделать всё самостоятельно, а затем, в следующей лекции, мы рассмотрим код.

Демонстрация выборки байесовского классификатора с гауссовой смесью распределений

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

Первое, что можно заметить в этой программе, – что она почти идентична предыдущей с единичным гауссовым распределением, с тем лишь исключением, что теперь мы используем вариационную гауссову смесь распределений библиотеки Sci-Kit Learn.

Что же изменилось? У нас по-прежнему есть класс BayesClassifier, и он по-прежнему содержит список гауссовых распределений, но теперь каждое из них будет объектом байесовской гауссовой смеси распределений.

Обратите внимание, что конструктор принимает один аргумент. Что это за аргумент? Если вы глянете в документацию, то узнаете, что это целое число, определяющее максимальное количество кластеров. Таким образом, хотя формально вариационная гауссова смесь распределений имеет бесконечное количество кластеров, в действительности она должна в какой-то момент остановиться. Это и есть верхний предел, жёстко ограничивающий количество кластеров. Я произвольным образом выбрал 10, однако естественных кластеров может быть больше или меньше – я не всматривался.

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

from __future__ import print_function, division

from builtins import range, input

# Note: you may need to update your version of future

# sudo pip install -U future

import util

import numpy as np

import matplotlib.pyplot as plt

from sklearn.mixture import BayesianGaussianMixture

class BayesClassifier:

  def fit(self, X, Y):

    # assume classes are numbered 0…K-1

    self.K = len(set(Y))

    self.gaussians = []

    self.p_y = np.zeros(self.K)

    for k in range(self.K):

      print(“Fitting gmm”, k)

      Xk = X[Y == k]

      self.p_y[k] = len(Xk)

      gmm = BayesianGaussianMixture(10)

      gmm.fit(Xk)

      self.gaussians.append(gmm)

    # normalize p(y)

    self.p_y /= self.p_y.sum()

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

Обратите внимание, что кое-что, что здесь использовано, не отображено в официальной документации по Sci-Kit Learn, так что нужно просмотреть их код на Github, чтобы выяснить, какие атрибуты надо рассматривать.

  def sample_given_y(self, y):

    gmm = self.gaussians[y]

    sample = gmm.sample()

    # note: sample returns a tuple containing 2 things:

    # 1) the sample

    # 2) which cluster it came from

    # we’ll use (2) to obtain the means so we can plot

    # them like we did in the previous script

    # we cheat by looking at “non-public” params in

    # the sklearn source code

    mean = gmm.means_[sample[1]]

    return sample[0].reshape(28, 28), mean.reshape(28, 28)

  def sample(self):

    y = np.random.choice(self.K, p=self.p_y)

    return self.sample_given_y(y)

Далее идёт раздел main, почти такой же, что и ранее. Мы выводим на экран образец из каждого класса, а затем один образец из случайным образом выбранного класса.

if __name__ == ‘__main__’:

  X, Y = util.get_mnist()

  clf = BayesClassifier()

  clf.fit(X, Y)

  for k in range(clf.K):

    # show one sample for each class

    # also show the mean image learned

    sample, mean = clf.sample_given_y(k)

    plt.subplot(1,2,1)

    plt.imshow(sample, cmap=’gray’)

    plt.title(“Sample”)

    plt.subplot(1,2,2)

    plt.imshow(mean, cmap=’gray’)

    plt.title(“Mean”)

    plt.show()

  # generate a random sample

  sample, mean = clf.sample()

  plt.subplot(1,2,1)

  plt.imshow(sample, cmap=’gray’)

  plt.title(“Random Sample from Random Class”)

  plt.subplot(1,2,2)

  plt.imshow(mean, cmap=’gray’)

  plt.title(“Corresponding Cluster Mean”)

  plt.show()

Запустим программу и посмотрим, что у нас получится. Да, и не забудьте приготовиться к тому, что ждать придётся намного дольше, чем в прошлой программе.

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

Андрей Никитенко
Андрей Никитенко
Задать вопрос эксперту
Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: