Функция оптимизации для многих реализаций псевдоданных в TensorFlow 2 - PullRequest
2 голосов
/ 26 апреля 2019

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

import tensorflow as tf
import tensorflow_probability as tfp
from tensorflow_probability import distributions as tfd
import numpy as np

# Bunch of independent Poisson distributions that we want to combine
poises0 = [tfp.distributions.Poisson(rate = 10) for i in range(5)]

# Construct joint distributions
joint0 = tfd.JointDistributionSequential(poises0)

# Generate samples
N = int(1e3)
samples0 = joint0.sample(N)

# Now we need the same distributions but with floating parameters,
# and need to define the function to be minimised
mus = [tf.Variable(np.random.randn(), name='mu{0}'.format(i)) for i in range(5)]

#@tf.function
def loss():
    poises_free = [tfp.distributions.Poisson(rate = mus[i]) for i in range(5)]
    joint_free = tfd.JointDistributionSequential(poises_free)
    # Construct (half of) test statistic
    return -2*(joint_free.log_prob(samples0))

# Minimise (for all samples? Apparently not?)
opt = tf.optimizers.SGD(0.1).minimize(loss,var_list=mus)

print(mus)
print(loss())
print(opt)
quit()

Вывод:

[<tf.Variable 'mu0:0' shape=() dtype=float32, numpy=53387.016>, <tf.Variable 'mu1:0' shape=() dtype=float32, numpy=2540.568>, <tf.Variable 'mu2:0' shape=() dtype=float32, numpy=-5136.6226>, <tf.Variable 'mu3:0' shape=() dtype=float32, numpy=-3714.5227>, <tf.Variable 'mu4:0' shape=() dtype=float32, numpy=1062.9396>]
tf.Tensor(
[nan nan nan nan ... nan nan nan], shape=(1000,), dtype=float32)
<tf.Variable 'UnreadVariable' shape=() dtype=int64, numpy=1>

В конце я хочу вычислить статистику теста

q = -2*joint0.log_prob(samples0) - loss()

и покажите, что он имеет распределение хи-квадрат с 5 степенями свободы.

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

Редактировать:

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

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

poises0 = [tfp.distributions.Poisson(rate = 10) for i in range(5)]
joint0 = tfd.JointDistributionSequential(poises0)

N = int(5e2)
samples0 = joint0.sample(N)

mus = [tf.Variable(10., name='mu{0}'.format(i)) for i in range(5)]

#@tf.function
def loss(xi):
    def loss_inner():
        poises_free = [tfp.distributions.Poisson(rate = mus[i]) for i in range(5)]
        joint_free = tfd.JointDistributionSequential(poises_free)
        # Construct (half of) test statistic
        return -2*(joint_free.log_prob(xi))
    return loss_inner

# Minimise
# I think I have to loop over the samples... bit lame. Can perhaps parallelise though.
q = []
for i in range(N):
   xi = [x[i] for x in samples0]
   opt = tf.optimizers.SGD(0.1).minimize(loss=loss(xi),var_list=mus)
   q += [-2*joint0.log_prob(xi) - loss(xi)()]

fig = plt.figure()
ax = fig.add_subplot(111)
sns.distplot(q, kde=False, ax=ax, norm_hist=True)
qx = np.linspace(np.min(q),np.max(q),1000)
qy = np.exp(tfd.Chi2(df=5).log_prob(qx))
sns.lineplot(qx,qy)
plt.show()

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

Not a chi-squared distribution with DOF=5

Редактировать 2:

Вот попытка решения «монстр», где я минимизирую гигантскую сеть различных входных переменных для каждой реализации псевдоданных одновременно.Это больше похоже на то, что TensorFlow может хорошо сделать, хотя я чувствую, что у меня не хватит ОЗУ, когда я перейду к большим наборам псевдоданных.Тем не менее, я, вероятно, могу циклически перебирать пакеты псевдоданных.

poises0 = [tfp.distributions.Poisson(rate = 10) for i in range(5)]
joint0 = tfd.JointDistributionSequential(poises0)

N = int(5e3)
samples0 = joint0.sample(N)

mus = [tf.Variable(10*np.ones(N, dtype='float32'), name='mu{0}'.format(i)) for i in range(5)]

poises_free = [tfp.distributions.Poisson(rate = mus[i]) for i in range(5)]
joint_free = tfd.JointDistributionSequential(poises_free)
qM = -2*(joint_free.log_prob(samples0))

@tf.function
def loss():
    return tf.math.reduce_sum(qM,axis=0)

# Minimise
opt = tf.optimizers.SGD(0.1).minimize(loss,var_list=mus)
print("parameters:", mus)
print("loss:", loss())
q0 =-2*joint0.log_prob(samples0)
print("q0:", q0)
print("qM:", qM)
q = q0 - qM

fig = plt.figure()
ax = fig.add_subplot(111)
sns.distplot(q, kde=False, ax=ax, norm_hist=True)
qx = np.linspace(np.min(q),np.max(q),1000)
qy = np.exp(tfd.Chi2(df=5).log_prob(qx))
sns.lineplot(qx,qy)
plt.show()

К сожалению, теперь я получаю сообщение об ошибке:

Traceback (most recent call last):
  File "testing3.py", line 35, in <module>
    opt = tf.optimizers.SGD(0.1).minimize(loss,var_list=mus)   
  File "/home/farmer/anaconda3/envs/general/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py", line 298, in minimize
    return self.apply_gradients(grads_and_vars, name=name)
  File "/home/farmer/anaconda3/envs/general/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py", line 396, in apply_gradients
    grads_and_vars = _filter_grads(grads_and_vars)
  File "/home/farmer/anaconda3/envs/general/lib/python3.6/site-packages/tensorflow/python/keras/optimizer_v2/optimizer_v2.py", line 924, in _filter_grads
    ([v.name for _, v in grads_and_vars],))
ValueError: No gradients provided for any variable: ['mu0:0', 'mu1:0', 'mu2:0', 'mu3:0', 'mu4:0'].

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

1 Ответ

0 голосов
/ 30 апреля 2019

Хорошо, вот что я придумала.Я упустил следующие ключевые вещи:

  1. Определение входных переменных как гигантских тензоров, чтобы все минимизации могли происходить одновременно.
  2. Построение единой функции комбинированных потерь для всех минимизаций одновременно
  3. Создайте промежуточные переменные для вычисления потерь в определении функции потерь, чтобы TensorFlow мог отслеживать градиенты (я думаю, что функция minimize оборачивает функцию потерь в градиентную ленту или что-то подобное).
  4. Определите функцию потерь как часть класса, чтобы можно было сохранить промежуточные переменные.
  5. minimize выполняет только один шаг минимизации, поэтому нам нужно многократно повторять ее, пока она не сойдет в соответствии снекоторый критерий.
  6. Я столкнулся с некоторыми NaN из-за недействительности средних меньше нуля для распределений Пуассона.Поэтому мне нужно было добавить ограничение к входным переменным.

Теперь я могу сделать на своем ноутбуке эквивалент миллиона минимизаций за 10 секунд, что довольно приятно!

import tensorflow as tf
import tensorflow_probability as tfp
from tensorflow_probability import distributions as tfd
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt

# Bunch of independent Poisson distributions that we want to combine
poises0 = [tfd.Poisson(rate = 10) for i in range(5)]

# Construct joint distributions
joint0 = tfd.JointDistributionSequential(poises0)

N = int(1e6)
samples0 = joint0.sample(N)

class Model(object):
  def __init__(self):
     self.mus = [tf.Variable(10*np.ones(N, dtype='float32'), name='mu{0}'.format(i),
                    constraint=lambda x: tf.clip_by_value(x, 0.000001, np.infty)) for i in range(5)]

  def loss(self):
     poises_free = [tfd.Poisson(rate = self.mus[i]) for i in range(5)]
     joint_free = tfd.JointDistributionSequential(poises_free)
     # Construct (half of) test statistic
     self.qM = -2*(joint_free.log_prob(samples0))
     self.last_loss = tf.math.reduce_sum(self.qM,axis=0)
     return self.last_loss

model = Model()

# Minimise
tol = 0.01 * N
delta_loss = 1e99
prev_loss = 1e99
i = 0
print("tol:", tol)
while delta_loss > tol:
    opt = tf.optimizers.SGD(0.1).minimize(model.loss,var_list=model.mus)
    delta_loss = np.abs(prev_loss - model.last_loss)
    print("i:", i," delta_loss:", delta_loss)
    i+=1
    prev_loss = model.last_loss

q0 =-2*joint0.log_prob(samples0)
q = q0 - model.qM

print("parameters:", model.mus)
print("loss:", model.last_loss)
print("q0:", q0)
print("qM:", model.qM)

fig = plt.figure()
ax = fig.add_subplot(111)
sns.distplot(q, kde=False, ax=ax, norm_hist=True)
qx = np.linspace(np.min(q),np.max(q),1000)
qy = np.exp(tfd.Chi2(df=5).log_prob(qx))
sns.lineplot(qx,qy)
plt.show()

Вывод:

tol: 10000.0
i: 0  delta_loss: inf
i: 1  delta_loss: 197840.0
i: 2  delta_loss: 189366.0
i: 3  delta_loss: 181456.0
i: 4  delta_loss: 174040.0
i: 5  delta_loss: 167042.0
i: 6  delta_loss: 160448.0
i: 7  delta_loss: 154216.0
i: 8  delta_loss: 148310.0
i: 9  delta_loss: 142696.0
i: 10  delta_loss: 137352.0
i: 11  delta_loss: 132268.0
i: 12  delta_loss: 127404.0
...
i: 69  delta_loss: 11894.0
i: 70  delta_loss: 11344.0
i: 71  delta_loss: 10824.0
i: 72  delta_loss: 10318.0
i: 73  delta_loss: 9860.0
parameters: [<tf.Variable 'mu0:0' shape=(1000000,) dtype=float32, numpy=
array([ 6.5849004, 14.81182  ,  7.506216 , ..., 10.       , 11.491933 ,
       10.760278 ], dtype=float32)>, <tf.Variable 'mu1:0' shape=(1000000,) dtype=float32, numpy=
array([12.881036,  7.506216, 12.881036, ...,  7.506216, 14.186232,
       10.760278], dtype=float32)>, <tf.Variable 'mu2:0' shape=(1000000,) dtype=float32, numpy=
array([16.01586  ,  8.378036 , 12.198007 , ...,  6.5849004, 12.198007 ,
        8.378036 ], dtype=float32)>, <tf.Variable 'mu3:0' shape=(1000000,) dtype=float32, numpy=
array([10.      ,  7.506216, 12.198007, ...,  9.207426, 10.760278,
       11.491933], dtype=float32)>, <tf.Variable 'mu4:0' shape=(1000000,) dtype=float32, numpy=
array([ 8.378036 , 14.81182  , 10.       , ...,  6.5849004, 12.198007 ,
       10.760278 ], dtype=float32)>]
loss: tf.Tensor(20760090.0, shape=(), dtype=float32)
q0: tf.Tensor([31.144037 31.440613 25.355555 ... 24.183338 27.195362 22.123463], shape=(1000000,), dtype=float32)
qM: tf.Tensor([21.74377  21.64162  21.526024 ... 19.488544 22.40428  21.08519 ], shape=(1000000,), dtype=float32)

Результат теперь равен хи-квадрат DOF = 5!Или, по крайней мере, довольно близко.enter image description here

...