25.12.2018       Выпуск 262 (24.12.2018 - 30.12.2018)       Статьи

Mixture Density Networks

Давайте поговорим о, как вы уже наверное смогли догадаться, нейронных сетях и машинном обучении. Из названия понятно, что будет рассказано о Mixture Density Networks, далее просто MDN, переводить название не хочу и оставлю как есть. Да, да, да… будет немного скучной математики и теории вероятности, но без неё, к сожалению, или к счастью, тут уж сами решайте, трудно представить мир машинного обучения. Но спешу вас успокоить, ее будет относительно мало и она будет не сильно сложная. Да и вообще ее можно будет пропустить, а просто посмотреть на небольшое количество кода на Python и PyTorch, все верно, сеть мы будем писать с помощью PyTorch, а так же на различные графики с результатами. Но самое главное то, что будет возможность немного разобраться и понять что же такое MD сети.

Читать>>




Экспериментальная функция:

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

Просим обратить внимание, что текст по ссылке и здесь может не совпадать.

Всем привет!

Давайте поговорим о, как вы уже наверное смогли догадаться, нейронных сетях и машинном обучении. Из названия понятно, что будет рассказано о Mixture Density Networks, далее просто MDN, переводить название не хочу и оставлю как есть. Да, да, да… будет немного скучной математики и теории вероятности, но без неё, к сожалению, или к счастью, тут уж сами решайте, трудно представить мир машинного обучения. Но спешу вас успокоить, ее будет относительно мало и она будет не сильно сложная. Да и вообще ее можно будет пропустить, а просто посмотреть на небольшое количество кода на Python и PyTorch, все верно, сеть мы будем писать с помощью PyTorch, а так же на различные графики с результатами. Но самое главное то, что будет возможность немного разобраться и понять что же такое MD сети.

Что ж начнем!


Регрессия

Давайте для начала немного освежим свои знания и вспомним, совсем вкратце, что такое

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

.

У нас есть вектор

$X = \{x_1, x_2,...,x_n\}$

, нам требуется предсказать значение

$Y$

, которое как то зависит от

$X$

, с помощью некой линейной модели:

$\hat{Y}=X^T\hat{\beta}$

В качестве функции ошибки будем использовать квадратичное отклонение (Squared Error):

$SE(\beta)=\sum_{i=1}^n(y_i-\hat{y}_i)^2=\sum_{i=1}^N(y_i-x_i^T\hat{\beta})^2$

Данную задачу можно решить напрямую, взяв производную от SE и приравняв ее значение к нулю:

$\frac{\delta SE(\beta)}{\delta\beta}=2X^T(\mathbf{y}-X\beta)=0$

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

$\beta$

:

$\hat\beta=(X^TX)^{-1}X^T\mathbf{y}$

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

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

$X^TX$

размерности

$n\times n$

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

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

Разбираемся с зоопарком, а именно: OLS, LS, SE, MSE, RSS

Все это одно и то же по сути, RSS — residual sum of squares, OLS — ordinary least squares, LS — least squares, MSE — mean squared error, SE — squared error. В разных источниках можно встретить разные названия. Суть у этого всего одна:

квадратичное отклонение

. Можно запутаться конечно, но к этому быстро привыкаешь.

Стоит отметить, что MSE это

средне

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

$MSE(\beta)=\frac{1}{N}\sum_{i=1}^n(y_i-\hat{y}_i)^2$

$N$

— размер датасета,

$\hat{y}_i$

— предсказание модели для

$y_i$

.

Стоп! Likelihood? Это ведь что то из теории вероятности. Все верно — это теория вероятности в чистом виде. Но как квадратичное отклонение может быть связано с функцией правдоподобия? А как оказывается связано. Связано с нахождением максимума правдоподобия (Maximum Likelihood) и с нормальным распределением, если быть более точным, то с его средним

$\mu$

.

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

$RSS(\beta)=\sum_{i=1}^n(y_i-\hat{y}_i)^2\qquad\qquad(1)$

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

$L(X)=p(X|\theta)=\prod^X\mathcal{N}(x_i; \mu, \sigma^2)$

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

Теперь, вспомнив формулу нормального распределения, получим:

$L(X; \mu, \sigma^2)=\prod^X\frac{1}{\sqrt{2\pi\sigma^2}}e^{-\frac{(x_i-\mu)^2}{2\sigma^2}}\qquad\qquad(2)$

А что если мы положим стандартное отклонение

$\sigma^2=1$

и уберем все константы в формуле (2), именно просто уберем, не сократим, ведь от них нахождение минимума функции не зависит. Тогда увидим это:

$L(X; \mu, \sigma^2)\sim \prod^Xe^{-(x_i-\mu)^2}$

Пока еще ничего не напоминает? Нет? Хорошо, а если взять логарифм от функции? От логарифма вообще одни плюсы: умножение превратит в сумму, степень в умножение, а

$\log{e} = 1$

— для данного свойства стоит уточнить, что речь идет о натуральном логарифме и строго говоря

$\ln{e}=1$

. Да и вообще логарифм от функции не меняет ее максимума, а это самая главная особенность для нас. О связи с Log-Likelihood и Likelihood и почему это полезно будет рассказано ниже, в небольшом отступлении. И так, что мы сделали: убрали все константы, и взяли логарифм от функции правдоподобия. Еще убрали знак минус, таким образом превратили Log-Likelihood в Negative Log-Likelihood (NLL), связь между ними тоже будет описана в качестве бонуса. В итоге получили функцию NLL:

$\log L(X; \mu, I^2)\sim \sum(X-\mu)^2$

Взгляните еще раз на функцию RSS (1). Да они же одинаковые! Именно! Так же видно, что

$\mu=\hat{y}$

.

Если использовать функцию среднеквадратичного отклонения MSE то из этого получим:

$\operatorname{argmin}MSE(\beta)\sim \operatorname{argmax}\mathbb{E}_{X\sim P_{data}}\log P_{model}(x; \beta)$

где

$\mathbb{E}$

— математическое ожидание,

$\beta$

— параметры модели, в дальнейшем будем обозначать их как:

$\theta$

.

Вывод:

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

$\hat{y}$

равно среднему в нормальном распределении. И теперь мы знаем как все это связано, как связана теория вероятности (с ее функцией правдоподобия и нормальным распределением) и методы среднеквадратичного отклонения или OLS. Более подробно об этом можно почитать в [2].

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

Связь между Cross-Entropy, Likelihood , Log-Likelihood и Negative Log-Likelihood

Предположим у нас есть данные

$X = \{x_1, x_2, x_3, x_4,...\}$

, каждая точка принадлежит определенному классу, например

$\{x_1\rightarrow1, x_2\rightarrow2, x_3\rightarrow n,...\}$

. Всего есть

$n$

классов, при этом класс 1 встречается

$c_1$

раз, класс 2 —

$c_2$

раз, а класс

$n$

$c_n$

раз. На этих данных мы обучили некоторую модель

$\theta$

. Функция правдоподобия (Likelihood) для нее будет выглядеть так:

$P(data|\theta)=P(0,1,...,n|\theta)=P(0|\theta)P(1|\theta)...P(n|\theta)$

$P(1|\theta)P(2|\theta)...P(n|\theta)=\prod^{c_1}\hat{y}_1\prod^{c_2}\hat{y}_2...\prod^{c_n}\hat{y}_n=\hat{y}_1^{c_1}\hat{y}_2^{c_2}...\hat{y}_n^{c_n}$

где

$P(n|\theta) = \hat{y}_n$

— предсказанная вероятность для класса

$n$

.

Берем логарифм от функции правдоподобия и получаем Log-Likelihood:

$\log{P(data|\theta)}=\log{(\hat{y}_1^{c_1}...\hat{y}_n^{c_n})}=c_1\log{\hat{y_1}}+...+c_n\log{\hat{y_n}}=\sum_i^n{c_i\log{\hat{y_i}}}$

Вероятность

$\hat{y} \in [0, 1]$

лежит в пределах от 0 до 1, исходя из определения вероятности. Следовательно логарифм будет иметь отрицательное значение. И если умножить Log-Likelihood на -1 мы получим функцию Negative Log-Likelihood (NLL):

$NLL=-\log{P(data|\theta)}=-\sum_i^n{c_i\log{\hat{y_i}}}$

Если поделим NLL на количество точек в

$X$

,

$N=c_1+c_2+...+c_n$

, то получим:

$-\frac{1}{N}\log{P(data|\theta)}=-\sum_i^n{\frac{c_i}{N}\log{\hat{y_i}}}$

при этом можно заметить, что реальная вероятность для класса

$n$

равна:

$y_n=\frac{c_n}{N}$

. Отсюда получаем:

$NLL=-\sum_i^n{y_i\log{\hat{y_i}}}$

теперь если посмотреть на определение кросс-энтропии

$H(p,q)=-\sum{p\log{q}}$

то получим:

$NLL=H(y_i,\hat{y_i})$

В случае когда у нас всего два класса

$n = 2$

(бинарная классификация) получим формулу для binary cross entropy (так же можно встретить всем известное название Log-Loss):

$H(y,\hat{y})=-(y\log{\hat{y}}+(1-y)\log{(1-\hat{y})})$

Из ходя из всего этого можно понять, что в некоторых случаях минимизация Cross-Entropy эквивалентна минимизации NLL или нахождению максимума функции правдоподобия (Likelihood) или Log-Likelihood.

Пример.

Рассмотрим бинарную классификацию. У нас есть значения классов:

y = np.array([0, 1, 1, 1, 1, 0, 1, 1]).astype(np.float32)

Реальная вероятность

$y$

для класса 0 равна

$2/8=0.25$

, для класса 1 равна

$6/8=0.75$

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

$\hat{y}$

для каждого примера, соответственно для класса 1 вероятность равна

$(1-\hat{y})$

. Построим график значений функции Log-Loss для разных предсказаний

$\hat{y}$

:

На графике можно увидеть что минимум функции Log-Loss соответствует точке 0.75, т.е. если бы наша модель полностью «выучила» распределение исходных данных,$\hat{y}=y$.

Регрессия с использованием нейронных сетей

Вот мы и подошли к более интересному, к практике. Посмотрим как можно решить задачу регрессии с помощью нейронных сетей (neural networks). Реализовывать все будем на языке программирования Python, для создания сети используем библиотеку глубокого обучения PyTorch.

Генерация исходных данных

Входные данные

$\mathbf{X}\in \mathbb{R}^N$

сгенерируем используя равномерное распределение (uniform distribution), интервал возьмем от -15 до 15,

$\mathbf{X} \in U[-15, 15]$

. Точки

$\mathbf{Y}$

получим с помощью уравнения:

$\mathbf{Y} = 0.5\mathbf{X} + 8\sin(0.3\mathbf{X}) + noise\qquad\qquad(3)$

где

$noise$

— вектор шума размерности

$N$

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

$\mu=0, \sigma^2=1$

.

Генерация данных
N = 3000 # размер данных
IN_DIM = 1
OUT_DIM = IN_DIM
x = np.random.uniform(-15., 15., (IN_DIM, N)).T.astype(np.float32)
noise = np.random.normal(size=(N, 1)).astype(np.float32)
y = 0.5*x+ 8.*np.sin(0.3*x) + noise # формула 3
x_train, x_test, y_train, y_test = train_test_split(x, y) #разобьем на тренировочные и тестовые данные
График полученных данных.

Построение сети

Создадим обычную сеть прямого распространения (feed forward neural network или FFNN).

Построение FFNN
class Net(nn.Module):
    def __init__(self, input_dim=IN_DIM, out_dim=OUT_DIM, layer_size=40):
        super(Net, self).__init__()
        self.fc = nn.Linear(input_dim, layer_size)
        self.logit = nn.Linear(layer_size, out_dim)

    def forward(self, x):
        x = F.tanh(self.fc(x)) # формула 4
        x = self.logit(x)
        return x

Наша сеть состоит из одного скрытого слоя размерностью 40 нейронов и с функцией активации — гиперболический тангенс:

$\tanh x=\frac{e^x-e^{-x}}{e^x+e^{-x}}\qquad\qquad(4)$

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

Обучение и получение результатов

В качестве оптимизатора будем использовать AdamOptimizer. Количество эпох обучения = 2000, скорость обучения (learning rate или lr) = 0.1.

Обучение FFNN
def train(net, x_train, y_train, x_test, y_test, epoches=2000, lr=0.1):
    criterion = nn.MSELoss()
    optimizer = optim.Adam(net.parameters(), lr=lr)
    N_EPOCHES = epoches
    BS = 1500
    n_batches = int(np.ceil(x_train.shape[0] / BS))
    train_losses = []
    test_losses = []
    for i in range(N_EPOCHES):
        for bi in range(n_batches):
            x_batch, y_batch = fetch_batch(x_train, y_train, bi, BS)
            x_train_var = Variable(torch.from_numpy(x_batch))
            y_train_var = Variable(torch.from_numpy(y_batch))
            optimizer.zero_grad()
            outputs = net(x_train_var)
            loss = criterion(outputs, y_train_var)
            loss.backward()
            optimizer.step()
        with torch.no_grad():
            x_test_var = Variable(torch.from_numpy(x_test))
            y_test_var = Variable(torch.from_numpy(y_test))
            outputs = net(x_test_var)
            test_loss = criterion(outputs, y_test_var)
            test_losses.append(test_loss.item())
            train_losses.append(loss.item())
            if i%100 == 0:
                sys.stdout.write('\r Iter: %d, test loss: %.5f, train loss: %.5f'
                                 %(i, test_loss.item(), loss.item()))
                sys.stdout.flush()
    return train_losses, test_losses
net = Net()
train_losses, test_losses = train(net, x_train, y_train, x_test, y_test)

Теперь посмотрим на результаты обучения.

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

Инвертированные данные

Усложним задачу и инвертируем данные.

Инвертирование данных
x_train_inv = y_train
y_train_inv = x_train
x_test_inv = y_train
y_test_inv = x_train
График Инвертированных данных.

Для предсказания

$\mathbf{\hat Y}$

давайте используем сеть прямого распространения из предыдущего раздела и посмотрим как она справится с этим.

inv_train_losses, inv_test_losses = train(net, x_train_inv, y_train_inv, x_test_inv, y_test_inv)
График значений MSE функции в зависимости от итерации обучения, на графике значения для тренировочных данных и тестовых данных. Реальные и предсказанные результаты на тестовых данных.

Как можно видеть из графиков выше наша сеть

вообще никак

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

$x$

может соответствовать несколько точек

$y$

. Вы спросите а как же шум? Он ведь тоже создавал ситуацию при которой для одного

$x$

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

$y$

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

$p(y|x)$

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

$x$

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

Mixture Density Network

Начинается самое интересное! Что же такое Mixture Density Network (далее MDN или MD сеть)? В общем эта некая модель, которая способна моделировать несколько распределений сразу:

$p(\mathbf{y}|\mathbf{x}; \theta)=\sum_k^K\pi_k(\mathbf{x})\mathcal{N}(\mathbf{y}; \mu_k(\mathbf{x}), \sigma^2(\mathbf{x}))\qquad\qquad(5)$

Какая то непонятная формула, скажите вы. Давайте разберемся. Наша MD сеть учится моделировать среднее

$\mu$

и дисперсию (variance)

$\sigma^2$

для

нескольких

распределений. В формуле (5)

$\pi_k(\mathbf{x})$

— так называемые коэффициенты значимости отдельного распределения для каждой точки

$x_i \in\mathbf{x}$

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

$K$

распределений.

Еще пару слов о

$\pi_k(\mathbf{x})$

— по сути, это тоже распределение и представляет собой вероятность того, что для точки

$x_i \in\mathbf{x}$

будет состояние

$k$

.

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

$K=30$

.

self.fc = nn.Linear(input_dim, layer_size)
self.fc2 = nn.Linear(layer_size, 50)
self.pi = nn.Linear(layer_size, coefs)
self.mu = nn.Linear(layer_size, out_dim*coefs) # mean
self.sigma_sq = nn.Linear(layer_size, coefs) # variance

Определим выходные слои для нашей сети:

x = F.relu(self.fc(x))
x = F.relu(self.fc2(x))
pi = F.softmax(self.pi(x), dim=1)
sigma_sq = torch.exp(self.sigma_sq(x))
mu = self.mu(x)

Напишем функцию ошибки или loss function, формула (5):

def gaussian_pdf(x, mu, sigma_sq):
    return (1/torch.sqrt(2*np.pi*sigma_sq)) * torch.exp((-1/(2*sigma_sq)) * torch.norm((x-mu), 2, 1)**2)

losses = Variable(torch.zeros(y.shape[0]))  # p(y|x)
for i in range(COEFS):
    likelihood = gaussian_pdf(y, mu[:, i*OUT_DIM:(i+1)*OUT_DIM], sigma_sq[:, i])
    prior = pi[:, i]
    losses += prior * likelihood
loss = torch.mean(-torch.log(losses))
Полный код построения MDN
COEFS = 30
class MDN(nn.Module):
    def __init__(self, input_dim=IN_DIM, out_dim=OUT_DIM, layer_size=50, coefs=COEFS):
        super(MDN, self).__init__()
        self.fc = nn.Linear(input_dim, layer_size)
        self.fc2 = nn.Linear(layer_size, 50)
        self.pi = nn.Linear(layer_size, coefs)
        self.mu = nn.Linear(layer_size, out_dim*coefs) # mean
        self.sigma_sq = nn.Linear(layer_size, coefs) # variance
        self.out_dim = out_dim
        self.coefs = coefs

    def forward(self, x):
        x = F.relu(self.fc(x))
        x = F.relu(self.fc2(x))
        pi = F.softmax(self.pi(x), dim=1)
        sigma_sq = torch.exp(self.sigma_sq(x))
        mu = self.mu(x)
        return pi, mu, sigma_sq

# функция плотности вероятности для нормального распределения
def gaussian_pdf(x, mu, sigma_sq):
    return (1/torch.sqrt(2*np.pi*sigma_sq)) * torch.exp((-1/(2*sigma_sq)) * torch.norm((x-mu), 2, 1)**2)

# функция ошибки
def loss_fn(y, pi, mu, sigma_sq):
    losses = Variable(torch.zeros(y.shape[0]))  # p(y|x)
    for i in range(COEFS):
        likelihood = gaussian_pdf(y,
                                  mu[:, i*OUT_DIM:(i+1)*OUT_DIM],
                                  sigma_sq[:, i])
        prior = pi[:, i]
        losses += prior * likelihood
    loss = torch.mean(-torch.log(losses))
    return loss

Наша MD сеть готова к работе. Почти готова. Осталось ее обучить и посмотреть на результаты.

Обучение MDN
def train_mdn(net, x_train, y_train, x_test, y_test, epoches=1000):
    optimizer = optim.Adam(net.parameters(), lr=0.01)
    N_EPOCHES = epoches
    BS = 1500
    n_batches = int(np.ceil(x_train.shape[0] / BS))
    train_losses = []
    test_losses = []
    for i in range(N_EPOCHES):
        for bi in range(n_batches):
            x_batch, y_batch = fetch_batch(x_train, y_train, bi, BS)
            x_train_var = Variable(torch.from_numpy(x_batch))
            y_train_var = Variable(torch.from_numpy(y_batch))
            optimizer.zero_grad()
            pi, mu, sigma_sq = net(x_train_var)
            loss = loss_fn(y_train_var, pi, mu, sigma_sq)
            loss.backward()
            optimizer.step()
        with torch.no_grad():
            if i%10 == 0:
                x_test_var = Variable(torch.from_numpy(x_test))
                y_test_var = Variable(torch.from_numpy(y_test))
                pi, mu, sigma_sq = net(x_test_var)
                test_loss = loss_fn(y_test_var, pi, mu, sigma_sq)
                train_losses.append(loss.item())
                test_losses.append(test_loss.item())
                sys.stdout.write('\r Iter: %d, test loss: %.5f, train loss: %.5f'
                                 %(i, test_loss.item(), loss.item()))
                sys.stdout.flush()
    return train_losses, test_losses
mdn_net = MDN()
mdn_train_losses, mdn_test_losses = train_mdn(mdn_net, x_train_inv, y_train_inv, x_test_inv, y_test_inv)
График значений loss функции в зависимости от итерации обучения, на графике значения для тренировочных данных и тестовых данных.

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

pi, mu, sigma_sq = mdn_net(Variable(torch.from_numpy(x_test_inv)))
График для двух наиболее вероятных значений среднего для каждой точки (слева). График для 4 наиболее вероятных значений среднего для каждой точки (справа). График для всех значений среднего для каждой точки.

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

$\mu$

и

$\sigma^2$

исходя из значения

$\pi_k(\mathbf{x})$

. А потом на их основе генерировать целевые данные

$\hat{y}$

с помощью нормального распределения.

Предсказание результата
def rand_n_sample_cumulative(pi, mu, sigmasq, samples=10):
    n = pi.shape[0]
    out = Variable(torch.zeros(n, samples, OUT_DIM))
    for i in range(n):
        for j in range(samples):
            u = np.random.uniform()
            prob_sum = 0
            for k in range(COEFS):
                prob_sum += pi.data[i, k]
                if u < prob_sum:
                    for od in range(OUT_DIM):
                        sample = np.random.normal(mu.data[i, k*OUT_DIM+od], np.sqrt(sigmasq.data[i, k]))
                        out[i, j, od] = sample
                    break
    return out
pi, mu, sigma_sq = mdn_net(Variable(torch.from_numpy(x_test_inv)))
preds = rand_n_sample_cumulative(pi, mu, sigma_sq, samples=10)
Предсказанные данные для 10 случайно выбранных значений$\mu$и$\sigma^2$(слева) и для двух (справа).

Из рисунков видно, что MDN отлично справилась с «обратной» задачей.

Использование более сложных данных

Посмотрим как наша MD сеть справится с более сложными данными, например спиральными данными. Уравнение гиперболической спирали в декартовых координатах:

$x = \rho\cos\phi\\\qquad\qquad\qquad\qquad\qquad\qquad(6)\\y = \rho\sin\phi\\$

Генерация спиралевидных данных
N = 2000
x_train_compl = []
y_train_compl = []
x_test_compl = []
y_test_compl = []
noise_train = np.random.uniform(-1, 1, (N, IN_DIM)).astype(np.float32)
noise_test = np.random.uniform(-1, 1, (N, IN_DIM)).astype(np.float32)
for i, theta in enumerate(np.linspace(0, 5*np.pi, N).astype(np.float32)):
    # формула 6
    r = ((theta))
    x_train_compl.append(r*np.cos(theta) + noise_train[i])
    y_train_compl.append(r*np.sin(theta))
    x_test_compl.append(r*np.cos(theta) + noise_test[i])
    y_test_compl.append(r*np.sin(theta))

x_train_compl = np.array(x_train_compl).reshape((-1, 1))
y_train_compl = np.array(y_train_compl).reshape((-1, 1))
x_test_compl = np.array(x_test_compl).reshape((-1, 1))
y_test_compl = np.array(y_test_compl).reshape((-1, 1))
График полученных спиралевидных данных.

Ради интереса посмотрим как обычная Feed-Forward сеть справится с такой задачей.

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

Используем, ранее описанную и созданную, MD сеть для обучения на спиралевидных данных.

Mixture Density Network и в данной ситуации отлично справилась.

Заключение

В начале данной статьи мы вспомнили основы линейной регрессии. Увидели что общего между нахождением среднего для нормального распределения и MSE. Разобрали как связаны NLL и cross entropy. И самое главное мы разобрались с моделью MDN, которая способна обучаться на данных, полученных из смешанного распределения. Надеюсь статья получилась понятной и интересной, несмотря на то, что было немного математики.

Полный код можно посмотреть на

GitHub

.


Литература

  1. Mixture Density Networks (Christopher M. Bishop, Neural Computing Research Group, Dept. of Computer Science and Applied Mathematics, Aston University, Birmingham) — в статье полностью описана теория MD сетей.
  2. Least squares and maximum likelihood (M.R.Osborne)


Лучшая Python рассылка




Разместим вашу рекламу

Пиши: mail@pythondigest.ru

Нашли опечатку?

Выделите фрагмент и отправьте нажатием Ctrl+Enter.

Система Orphus