Получение среднего и ковариационной матрицы для многомерного нормального распределения из модели Keras.

Вопрос или проблема

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

То есть для моего набора данных для любой строки из 6 входных признаков я хочу получить вектор средних значений из 5 значений и матрицу ковариации 5*5.

sample=pd.DataFrame({'X1':[1,2,3,4,5,6],
              'X2':[1,3,1,5,2,7],
              'X3':[3,0,0,7,5,0],
              'X4':[0,4,3,2,5,8],
              'X5':[9,7,0,2,4,5],
              'X6':[1,1,8,7,0,0],
              'Y1':[0.5,1.2,6.3,4.5,1.5,6.6],
              'Y2':[6.1,4.3,2.1,1.5,4.2,8.7],
              'Y3':[0,0,3.2,3.7,5.5,0.2],
              'Y4':[0.5,1.4,8.3,5.2,1.5,1.8],
              'Y5':[2.9,1.7,6.3,5.2,9.4,1.5]})
sample
    X1  X2  X3  X4  X5  X6  Y1  Y2  Y3  Y4  Y5
0   1   1   3   0   9   1   0.5 6.1 0.0 0.5 2.9
1   2   3   0   4   7   1   1.2 4.3 0.0 1.4 1.7
2   3   1   0   3   0   8   6.3 2.1 3.2 8.3 6.3
3   4   5   7   2   2   7   4.5 1.5 3.7 5.2 5.2
4   5   2   5   5   4   0   1.5 4.2 5.5 1.5 9.4
5   6   7   0   8   5   0   6.6 8.7 0.2 1.8 1.5

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

def lossF(y_true, mu, cov):

  dist = tfp.distributions.MultivariateNormalTriL(loc=mu, scale_tril=tf.linalg.cholesky(cov))
  return tf.reduce_mean(-dist.log_prob(y_true))

Я пробую сделать что-то вроде ниже, но запутался посередине.

#X_train имеет 6 значений в каждой строке
#y_train имеет 5 значений в каждой строке
#y_pred должно быть либо функцией распределения, либо mu & cov для каждой строки

opt = Adam(learning_rate=0.001)
inputs = Input(shape=(6,))
layer1 = Dense(24, activation='relu')(inputs)
layer2 = Dense(12, activation='relu')(layer1)
predictions = ???
model = Model(inputs=???, outputs=???)
model.compile(optimizer=opt, loss=loss_fn)
model.fit(X_train, y_train, epochs=100, batch_size=100)
y_pred=model.predict(X_test)

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

Учитывая, что матрица ковариации должна быть положительно определенной, разложение Холецкого является хорошим способом решения этой проблемы. Таким образом, выходом сети будет вектор средних значений mu и верхняя треугольная часть матрицы Холецкого (обозначенная здесь T). Диагональ этой матрицы должна содержать положительные элементы (диагональ матрицы ковариации — это стандартные отклонения):

p = y_train.shape[1] # размерность матрицы ковариации 
inputs = Input(shape=(6,))
layer1 = Dense(24, activation='relu')(inputs)
layer2 = Dense(12, activation='relu')(layer1)
mu = Dense(p, activation = "linear")(layer1)
T1 = Dense(p, activation="exponential")(layer1)# диагональ T
T2 = Dense((p*(p-1)/2), activation="linear")(layer1)
outputs = Concatenate()([mu, T1, T2]) 

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

def mu_sigma(output):
    mu = output[0][0:p]
    T1 = output[0][p:2*p]
    T2 = output[0][2*p:]
    ones = tf.ones((p,p), dtype=tf.float32) 
    mask_a = tf.linalg.band_part(ones, 0, -1)  
    mask_b = tf.linalg.band_part(ones, 0, 0)  
    mask = tf.subtract(mask_a, mask_b) 
    zero = tf.constant(0, dtype=tf.float32)
    non_zero = tf.not_equal(mask, zero)
    indices = tf.where(non_zero)
    T2 = tf.sparse.SparseTensor(indices,T2,dense_shape=tf.cast((p,p),
         dtype=tf.int64))
    T2 = tf.sparse.to_dense(T2)
    T1 = tf.linalg.diag(T1)
    sigma = T1 + T2
    return mu, sigma

Теперь для функции потерь:

from tensorflow_probability import distributions as tfd
def gnll_loss(y, pred):
    mu, sigma = mu_sigma(pred)
    gm = tfd.MultivariateNormalTriL(loc=mu, scale_tril=sigma)
    log_likelihood = gm.log_prob(y)          
    return - tf.math.reduce_sum(log_likelihood)

Ответ или решение

Получение среднего значения и матрицы ковариации для многомерного нормального распределения с использованием Keras

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

Описание задачи

У вас есть набор данных, представлен в формате Pandas DataFrame:

import pandas as pd

sample = pd.DataFrame({
    'X1': [1, 2, 3, 4, 5, 6],
    'X2': [1, 3, 1, 5, 2, 7],
    'X3': [3, 0, 0, 7, 5, 0],
    'X4': [0, 4, 3, 2, 5, 8],
    'X5': [9, 7, 0, 2, 4, 5],
    'X6': [1, 1, 8, 7, 0, 0],
    'Y1': [0.5, 1.2, 6.3, 4.5, 1.5, 6.6],
    'Y2': [6.1, 4.3, 2.1, 1.5, 4.2, 8.7],
    'Y3': [0, 0, 3.2, 3.7, 5.5, 0.2],
    'Y4': [0.5, 1.4, 8.3, 5.2, 1.5, 1.8],
    'Y5': [2.9, 1.7, 6.3, 5.2, 9.4, 1.5]
})

Здесь X1X6 являются признаками входных данных, а Y1Y5 — это результаты, которые предполагаются как следствие многомерного нормального распределения.

Структура модели Keras

Модель будет составлена из следующих компонентов:

  1. Входные данные – 6 признаков.
  2. Скрытые слои – два полносвязных слоя с функцией активации ReLU.
  3. Выходные данные – вектор среднего значения и матрица ковариации, которая будет представлена через нижнюю треугольную матрицу, полученную с помощью разложения Холюса.

Пример кода для создания модели:

import tensorflow as tf
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Input, Dense, Concatenate
from tensorflow.keras.optimizers import Adam
import tensorflow_probability as tfp

# Определяем количество выходных параметров
p = 5  # Количество выходных параметров (Y1-Y5)

# Входные данные
inputs = Input(shape=(6,))
layer1 = Dense(24, activation='relu')(inputs)
layer2 = Dense(12, activation='relu')(layer1)

# Среднее значение
mu = Dense(p, activation='linear')(layer2)

# Параметры для ковариации
T1 = Dense(p, activation='exponential')(layer2)  # Диагональ
T2 = Dense((p*(p-1)//2), activation='linear')(layer2)  # Остальная часть

# Объединение выходных значений
outputs = Concatenate()([mu, T1, T2])

# Создание модели
model = Model(inputs=inputs, outputs=outputs)
model.compile(optimizer=Adam(learning_rate=0.001), loss='mean_squared_error')

Определение функции потерь

Функция потерь должна быть основана на максимизации логарифма вероятности. Мы можем определить функцию для извлечения параметров из выходных данных модели и вычислить логарифм вероятности распределения:

def mu_sigma(output):
    mu = output[0][:p]
    T1 = output[0][p:2*p]
    T2 = output[0][2*p:]

    # Построение матрицы ковариации
    T1_matrix = tf.linalg.diag(T1)  # Диагональ
    T2_indices = tf.reshape(tf.range(p)[:, tf.newaxis], (-1, 1)) + tf.range(1, p)  # Индексы
    T2_matrix = tf.scatter_nd(T2_indices, T2, (p, p))  # Остаток
    sigma = T1_matrix + T2_matrix

    return mu, sigma

def gnll_loss(y, pred):
    mu, sigma = mu_sigma(pred)
    gm = tfp.distributions.MultivariateNormalTriL(loc=mu, scale_tril=tf.linalg.cholesky(sigma))
    log_likelihood = gm.log_prob(y)
    return -tf.reduce_mean(log_likelihood)

Обучение модели

Модель должна быть обучена на ваших данных. Убедитесь, что ваши данные правильно разбиты на обучающие и тестовые наборы.

X_train = sample[['X1', 'X2', 'X3', 'X4', 'X5', 'X6']].values
y_train = sample[['Y1', 'Y2', 'Y3', 'Y4', 'Y5']].values

model.fit(X_train, y_train, epochs=100, batch_size=100)

Результаты предсказания

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

y_pred = model.predict(X_test)
mean, covariance = mu_sigma(y_pred)

Заключение

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

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

Капча загружается...