نماذج المتغيرات الكامنة لعملية غاوس

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

تحاول النماذج المتغيرة الكامنة التقاط بنية مخفية في بيانات عالية الأبعاد. تشمل الأمثلة تحليل المكون الأساسي (PCA) وتحليل العوامل. العمليات الغاوسية هي نماذج "غير بارامترية" يمكنها التقاط بنية الارتباط المحلية وعدم اليقين بمرونة. عملية جاوس الكامنة نموذج متغير ( لورانس 2004 ) يجمع بين هذه المفاهيم.

الخلفية: العمليات الغاوسية

عملية غاوس هي أي مجموعة من المتغيرات العشوائية بحيث يكون التوزيع الهامشي على أي مجموعة فرعية محدودة توزيعًا طبيعيًا متعدد المتغيرات. لإلقاء نظرة مفصلة على الأطباء في سياق الانحدار، تحقق من التمويه الانحدار عملية في TensorFlow الاحتمالات .

نحن نستخدم ما يسمى مجموعة مؤشر لتسمية كل من المتغيرات العشوائية في جمع أن يتألف GP. في حالة مجموعة الفهرس المحدودة ، نحصل على عدد عادي متعدد المتغيرات. وGP هي الأكثر إثارة للاهتمام، على الرغم من أننا عندما تنظر مجموعات لا حصر لها. في حالة مجموعات مؤشر مثل \(\mathbb{R}^D\)، حيث لدينا متغير عشوائي على كل نقطة في \(D\)الفضاء الأبعاد، وGP يمكن التفكير فيه باعتباره التوزيع على وظائف عشوائية. التعادل واحد من هذا القبيل GP، إذا يمكن أن يتحقق ذلك، ستقوم بتخصيص (بالاشتراك توزع عادة) قيمة كل نقطة في \(\mathbb{R}^D\). في هذا colab، سنركز على لGP على بعض\(\mathbb{R}^D\).

يتم تحديد التوزيعات العادية تمامًا من خلال إحصائيات الرتبتين الأولى والثانية - في الواقع ، إحدى الطرق لتعريف التوزيع الطبيعي هي تلك التي تكون مجموعها التراكمية ذات الترتيب الأعلى صفرًا. هذا هو الحال بالنسبة لGP أيضا: نحدد تماما لGP بوصف نفسه والتغاير *. تذكر أنه بالنسبة للقواعد المعيارية متعددة المتغيرات ذات الأبعاد المحدودة ، يكون المتوسط ​​متجهًا والتغاير عبارة عن مصفوفة مربعة ومتناظرة موجبة التحديد. في GP-انهائية الأبعاد، هذه الهياكل تعمم إلى متوسط وظيفة \(m : \mathbb{R}^D \to \mathbb{R}\)، الذي يعرف في كل نقطة من المؤشر مجموعة، وظيفة التغاير "نواة"،\(k : \mathbb{R}^D \times \mathbb{R}^D \to \mathbb{R}\). مطلوب وظيفة نواة لتكون إيجابية، واضح ، وهو يقول أساسا أن تقتصر على مجموعة محدودة من النقاط، فإنه ينتج مصفوفة postiive-محددة.

تستمد معظم بنية GP من دالة نواة التغاير - تصف هذه الوظيفة كيف تختلف قيم وظائف sampeld عبر نقاط قريبة (أو ليست قريبة جدًا). تعمل وظائف التغاير المختلفة على تشجيع درجات مختلفة من النعومة. وظيفة واحدة النواة المستخدمة شيوعا هي "الأسي الدرجة الثانية" (ويعرف أيضا باسم "التمويه"، "مربع الأسية" أو "وظيفة أساس شعاعي")، \(k(x, x') = \sigma^2 e^{(x - x^2) / \lambda^2}\). وترد أمثلة أخرى على ديفيد Duvenaud في الصفحة نواة كتاب طبخ ، وكذلك في النص الكنسي عمليات جاوس لآلة التعلم .

* مع مجموعة الفهرس اللانهائية ، نحتاج أيضًا إلى شرط الاتساق. نظرًا لأن تعريف GP من حيث الهامش المحدود ، يجب أن نطلب أن تكون هذه الهامش متسقة بغض النظر عن الترتيب الذي يتم فيه أخذ الهامش. هذا موضوع متقدم إلى حد ما في نظرية العمليات العشوائية ، خارج نطاق هذا البرنامج التعليمي ؛ يكفي أن نقول إن الأمور تسير على ما يرام في النهاية!

تطبيق GPs: الانحدار والنماذج المتغيرة الكامنة

طريقة واحدة يمكننا استخدام نظام تحديد المواقع هو الانحدار: نظرا لمجموعة من البيانات المرصودة في شكل مدخلات \(\{x_i\}_{i=1}^N\) (عناصر المجموعة رقم قياسي) والملاحظات\(\{y_i\}_{i=1}^N\)، يمكننا استخدام هذه لتشكيل توزيع التنبؤي الخلفي في الجديدة مجموعة من النقاط \(\{x_j^*\}_{j=1}^M\). منذ التوزيعات كلها التمويه، وهذا يتلخص في بعض الجبر الخطي مباشرة (ولكن ملاحظة: الحسابية اللازمة لها مكعب وقت التشغيل في عدد من نقاط البيانات وتتطلب من الدرجة الثانية الفضاء في عدد من نقاط البيانات - وهذا هو العامل المحدد الرئيسي في استخدام الممارسين العامين والكثير من الأبحاث الحالية تركز على بدائل قابلة للتطبيق حسابيًا للاستدلال اللاحق الدقيق). نحن نغطي الانحدار GP بمزيد من التفصيل في الانحدار GP في colab TFP .

هناك طريقة أخرى يمكننا من خلالها استخدام الممارسين العامين وهي كنموذج متغير كامن: بالنظر إلى مجموعة من الملاحظات عالية الأبعاد (على سبيل المثال ، الصور) ، يمكننا أن نفترض بعض الهياكل الكامنة منخفضة الأبعاد. نفترض أنه ، وفقًا للبنية الكامنة ، يكون العدد الكبير من المخرجات (وحدات البكسل في الصورة) مستقلة عن بعضها البعض. يتكون التدريب في هذا النموذج من

  1. تحسين معلمات النموذج (معلمات وظيفة kernel وكذلك ، على سبيل المثال ، تباين ضوضاء الملاحظة) ، و
  2. إيجاد ، لكل ملاحظة تدريب (صورة) ، موقع نقطة مقابلة في مجموعة الفهرس. يمكن إجراء كل التحسين عن طريق تعظيم احتمالية السجل الهامشي للبيانات.

الواردات

import numpy as np
import tensorflow.compat.v2 as tf
tf.enable_v2_behavior()
import tensorflow_probability as tfp
tfd = tfp.distributions
tfk = tfp.math.psd_kernels
%pylab inline
Populating the interactive namespace from numpy and matplotlib

تحميل بيانات MNIST

# Load the MNIST data set and isolate a subset of it.
(x_train, y_train), (_, _) = tf.keras.datasets.mnist.load_data()
N = 1000
small_x_train = x_train[:N, ...].astype(np.float64) / 256.
small_y_train = y_train[:N]
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step
11501568/11490434 [==============================] - 0s 0us/step

تحضير المتغيرات القابلة للتدريب

سنقوم بشكل مشترك بتدريب 3 معلمات نموذجية بالإضافة إلى المدخلات الكامنة.

# Create some trainable model parameters. We will constrain them to be strictly
# positive when constructing the kernel and the GP.
unconstrained_amplitude = tf.Variable(np.float64(1.), name='amplitude')
unconstrained_length_scale = tf.Variable(np.float64(1.), name='length_scale')
unconstrained_observation_noise = tf.Variable(np.float64(1.), name='observation_noise')
# We need to flatten the images and, somewhat unintuitively, transpose from
# shape [100, 784] to [784, 100]. This is because the 784 pixels will be
# treated as *independent* conditioned on the latent inputs, meaning we really
# have a batch of 784 GP's with 100 index_points.
observations_ = small_x_train.reshape(N, -1).transpose()

# Create a collection of N 2-dimensional index points that will represent our
# latent embeddings of the data. (Lawrence, 2004) prescribes initializing these
# with PCA, but a random initialization actually gives not-too-bad results, so
# we use this for simplicity. For a fun exercise, try doing the
# PCA-initialization yourself!
init_ = np.random.normal(size=(N, 2))
latent_index_points = tf.Variable(init_, name='latent_index_points')

بناء النموذج وعمليات التدريب

# Create our kernel and GP distribution
EPS = np.finfo(np.float64).eps

def create_kernel():
  amplitude = tf.math.softplus(EPS + unconstrained_amplitude)
  length_scale = tf.math.softplus(EPS + unconstrained_length_scale)
  kernel = tfk.ExponentiatedQuadratic(amplitude, length_scale)
  return kernel

def loss_fn():
  observation_noise_variance = tf.math.softplus(
      EPS + unconstrained_observation_noise)
  gp = tfd.GaussianProcess(
      kernel=create_kernel(),
      index_points=latent_index_points,
      observation_noise_variance=observation_noise_variance)
  log_probs = gp.log_prob(observations_, name='log_prob')
  return -tf.reduce_mean(log_probs)

trainable_variables = [unconstrained_amplitude,
                       unconstrained_length_scale,
                       unconstrained_observation_noise,
                       latent_index_points]

optimizer = tf.optimizers.Adam(learning_rate=1.0)

@tf.function(autograph=False, jit_compile=True)
def train_model():
  with tf.GradientTape() as tape:
    loss_value = loss_fn()
  grads = tape.gradient(loss_value, trainable_variables)
  optimizer.apply_gradients(zip(grads, trainable_variables))
  return loss_value

تدريب ورسم حفلات الزفاف الكامنة الناتجة

# Initialize variables and train!
num_iters = 100
log_interval = 20
lips = np.zeros((num_iters, N, 2), np.float64)
for i in range(num_iters):
  loss = train_model()
  lips[i] = latent_index_points.numpy()
  if i % log_interval == 0 or i + 1 == num_iters:
    print("Loss at step %d: %f" % (i, loss))
Loss at step 0: 1108.121688
Loss at step 20: -159.633761
Loss at step 40: -263.014394
Loss at step 60: -283.713056
Loss at step 80: -288.709413
Loss at step 99: -289.662253

نتائج المؤامرة

# Plot the latent locations before and after training
plt.figure(figsize=(7, 7))
plt.title("Before training")
plt.grid(False)
plt.scatter(x=init_[:, 0], y=init_[:, 1],
           c=y_train[:N], cmap=plt.get_cmap('Paired'), s=50)
plt.show()

plt.figure(figsize=(7, 7))
plt.title("After training")
plt.grid(False)
plt.scatter(x=lips[-1, :, 0], y=lips[-1, :, 1],
           c=y_train[:N], cmap=plt.get_cmap('Paired'), s=50)
plt.show()

بي إن جي

بي إن جي

بناء نموذج تنبؤي وعمليات أخذ العينات

# We'll draw samples at evenly spaced points on a 10x10 grid in the latent
# input space. 
sample_grid_points = 10
grid_ = np.linspace(-4, 4, sample_grid_points).astype(np.float64)
# Create a 10x10 grid of 2-vectors, for a total shape [10, 10, 2]
grid_ = np.stack(np.meshgrid(grid_, grid_), axis=-1)

# This part's a bit subtle! What we defined above was a batch of 784 (=28x28)
# independent GP distributions over the input space. Each one corresponds to a
# single pixel of an MNIST image. Now what we'd like to do is draw 100 (=10x10)
# *independent* samples, each one separately conditioned on all the observations
# as well as the learned latent input locations above.
#
# The GP regression model below will define a batch of 784 independent
# posteriors. We'd like to get 100 independent samples each at a different
# latent index point. We could loop over the points in the grid, but that might
# be a bit slow. Instead, we can vectorize the computation by tacking on *even
# more* batch dimensions to our GaussianProcessRegressionModel distribution.
# In the below grid_ shape, we have concatentaed
#   1. batch shape: [sample_grid_points, sample_grid_points, 1]
#   2. number of examples: [1]
#   3. number of latent input dimensions: [2]
# The `1` in the batch shape will broadcast with 784. The final result will be
# samples of shape [10, 10, 784, 1]. The `1` comes from the "number of examples"
# and we can just `np.squeeze` it off.
grid_ = grid_.reshape(sample_grid_points, sample_grid_points, 1, 1, 2)

# Create the GPRegressionModel instance which represents the posterior
# predictive at the grid of new points.
gprm = tfd.GaussianProcessRegressionModel(
    kernel=create_kernel(),
    # Shape [10, 10, 1, 1, 2]
    index_points=grid_,
    # Shape [1000, 2]. 1000 2 dimensional vectors.
    observation_index_points=latent_index_points,
    # Shape [784, 1000]. A batch of 784 1000-dimensional observations.
    observations=observations_)

ارسم عينات مشروطة بالبيانات والأفراح الكامنة

نقوم بأخذ عينات من 100 نقطة على شبكة ثنائية الأبعاد في الفضاء الكامن.

samples = gprm.sample()

# Plot the grid of samples at new points. We do a bit of tweaking of the samples
# first, squeezing off extra 1-shapes and normalizing the values.
samples_ = np.squeeze(samples.numpy())
samples_ = ((samples_ -
             samples_.min(-1, keepdims=True)) /
            (samples_.max(-1, keepdims=True) -
             samples_.min(-1, keepdims=True)))
samples_ = samples_.reshape(sample_grid_points, sample_grid_points, 28, 28)
samples_ = samples_.transpose([0, 2, 1, 3])
samples_ = samples_.reshape(28 * sample_grid_points, 28 * sample_grid_points)
plt.figure(figsize=(7, 7))
ax = plt.subplot()
ax.grid(False)
ax.imshow(-samples_, interpolation='none', cmap='Greys')
plt.show()

بي إن جي

استنتاج

لقد قمنا بجولة موجزة في نموذج المتغير الكامن لعملية غاوسي ، وأظهرنا كيف يمكننا تنفيذه في بضعة أسطر فقط من كود TF و TF الاحتمالية.