บันทึกวันที่! Google I / O ส่งคืนวันที่ 18-20 พฤษภาคม ลงทะเบียนตอนนี้
หน้านี้ได้รับการแปลโดย Cloud Translation API
Switch to English

การติดตั้งแบบจำลองกระบวนการผสม Dirichlet โดยใช้พลศาสตร์ Langevin Stochastic Gradient ที่ปรับสภาพล่วงหน้า

ดูใน TensorFlow.org เรียกใช้ใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดสมุดบันทึก

ในสมุดบันทึกนี้เราจะสาธิตวิธีการรวมกลุ่มตัวอย่างจำนวนมากและสรุปจำนวนคลัสเตอร์พร้อมกันโดยการปรับส่วนผสมของกระบวนการ Dirichlet ของการแจกแจงแบบเกาส์เซียน เราใช้ Preonditioned Stochastic Gradient Langevin Dynamics (pSGLD) สำหรับการอนุมาน

สารบัญ

  1. ตัวอย่าง

  2. รุ่น

  3. การเพิ่มประสิทธิภาพ

  4. เห็นภาพผลลัพธ์

    4.1. ผลลัพธ์แบบคลัสเตอร์

    4.2. เห็นภาพความไม่แน่นอน

    4.3. ค่าเฉลี่ยและขนาดของส่วนประกอบของส่วนผสมที่เลือก

    4.4. น้ำหนักส่วนผสมของส่วนผสมแต่ละส่วน

    4.5. การบรรจบกันของ $ \ alpha $

    4.6. จำนวนคลัสเตอร์ที่อ้างอิงจากการทำซ้ำ

    4.7. การติดตั้งโมเดลโดยใช้ RMSProp

  5. สรุป


1. ตัวอย่าง

ขั้นแรกเราตั้งค่าชุดข้อมูลของเล่น เราสร้างตัวอย่างสุ่ม 50,000 ตัวอย่างจากการแจกแจงแบบเกาส์เซียนสามตัวแปร

import time
import numpy as np
import matplotlib.pyplot as plt
import tensorflow.compat.v1 as tf
import tensorflow_probability as tfp
plt.style.use('ggplot')
tfd = tfp.distributions
def session_options(enable_gpu_ram_resizing=True):
  """Convenience function which sets common `tf.Session` options."""
  config = tf.ConfigProto()
  config.log_device_placement = True
  if enable_gpu_ram_resizing:
    # `allow_growth=True` makes it possible to connect multiple colabs to your
    # GPU. Otherwise the colab malloc's all GPU ram.
    config.gpu_options.allow_growth = True
  return config

def reset_sess(config=None):
  """Convenience function to create the TF graph and session, or reset them."""
  if config is None:
    config = session_options()
  tf.reset_default_graph()
  global sess
  try:
    sess.close()
  except:
    pass
  sess = tf.InteractiveSession(config=config)
# For reproducibility
rng = np.random.RandomState(seed=45)
tf.set_random_seed(76)

# Precision
dtype = np.float64

# Number of training samples
num_samples = 50000

# Ground truth loc values which we will infer later on. The scale is 1.
true_loc = np.array([[-4, -4],
                     [0, 0],
                     [4, 4]], dtype)

true_components_num, dims = true_loc.shape

# Generate training samples from ground truth loc
true_hidden_component = rng.randint(0, true_components_num, num_samples)
observations = (true_loc[true_hidden_component]

                + rng.randn(num_samples, dims).astype(dtype))
# Visualize samples
plt.scatter(observations[:, 0], observations[:, 1], 1)
plt.axis([-10, 10, -10, 10])
plt.show()

png

2. รุ่น

ที่นี่เรากำหนดส่วนผสมของกระบวนการ Dirichlet ของการแจกแจงแบบเกาส์เซียนด้วย Symmetric Dirichlet Prior ตลอดทั้งสมุดบันทึกปริมาณเวกเตอร์จะเขียนเป็นตัวหนา มากกว่า $ i \ in {1, \ ldots, N} $ ตัวอย่างโมเดลที่มีส่วนผสมของ $ j \ in {1, \ ldots, K} $ Gaussian มีสูตรดังนี้:

$$\begin{align*} p(\boldsymbol{x}_1,\cdots, \boldsymbol{x}_N) &=\prod_{i=1}^N \text{GMM}(x_i), \\ &\,\quad \text{with}\;\text{GMM}(x_i)=\sum_{j=1}^K\pi_j\text{Normal}(x_i\,|\,\text{loc}=\boldsymbol{\mu_{j} },\,\text{scale}=\boldsymbol{\sigma_{j} })\\ \end{align*}$$

ที่ไหน:

$$\begin{align*} x_i&\sim \text{Normal}(\text{loc}=\boldsymbol{\mu}_{z_i},\,\text{scale}=\boldsymbol{\sigma}_{z_i}) \\ z_i &= \text{Categorical}(\text{prob}=\boldsymbol{\pi}),\\ &\,\quad \text{with}\;\boldsymbol{\pi}=\{\pi_1,\cdots,\pi_K\}\\ \boldsymbol{\pi}&\sim\text{Dirichlet}(\text{concentration}=\{\frac{\alpha}{K},\cdots,\frac{\alpha}{K}\})\\ \alpha&\sim \text{InverseGamma}(\text{concentration}=1,\,\text{rate}=1)\\ \boldsymbol{\mu_j} &\sim \text{Normal}(\text{loc}=\boldsymbol{0}, \,\text{scale}=\boldsymbol{1})\\ \boldsymbol{\sigma_j} &\sim \text{InverseGamma}(\text{concentration}=\boldsymbol{1},\,\text{rate}=\boldsymbol{1})\\ \end{align*}$$

เป้าหมายของเราคือกำหนด $ x_i $ ให้กับคลัสเตอร์ $ j $ th ผ่าน $ z_i $ ซึ่งแสดงถึงดัชนีที่สรุปของคลัสเตอร์

สำหรับ Dirichlet Mixture Model ในอุดมคติ $ K $ จะถูกตั้งค่าเป็น $ \ infty $ อย่างไรก็ตามเป็นที่ทราบกันดีว่าเราสามารถประมาณ Dirichlet Mixture Model ที่มีขนาดใหญ่พอสมควร $ K $ โปรดทราบว่าแม้ว่าเราจะตั้งค่าเริ่มต้นเป็น $ K $ โดยพลการ แต่จำนวนคลัสเตอร์ที่เหมาะสมจะถูกอนุมานผ่านการเพิ่มประสิทธิภาพซึ่งแตกต่างจาก Gaussian Mixture Model ทั่วไป

ในสมุดบันทึกนี้เราใช้การแจกแจงแบบ Gaussian แบบสองตัวแปรเป็นส่วนประกอบของส่วนผสมและตั้งค่า $ K $ เป็น 30

reset_sess()

# Upperbound on K
max_cluster_num = 30

# Define trainable variables.
mix_probs = tf.nn.softmax(
    tf.Variable(
        name='mix_probs',
        initial_value=np.ones([max_cluster_num], dtype) / max_cluster_num))

loc = tf.Variable(
    name='loc',
    initial_value=np.random.uniform(
        low=-9, #set around minimum value of sample value
        high=9, #set around maximum value of sample value
        size=[max_cluster_num, dims]))

precision = tf.nn.softplus(tf.Variable(
    name='precision',
    initial_value=
    np.ones([max_cluster_num, dims], dtype=dtype)))

alpha = tf.nn.softplus(tf.Variable(
    name='alpha',
    initial_value=
    np.ones([1], dtype=dtype)))

training_vals = [mix_probs, alpha, loc, precision]


# Prior distributions of the training variables

#Use symmetric Dirichlet prior as finite approximation of Dirichlet process.
rv_symmetric_dirichlet_process = tfd.Dirichlet(
    concentration=np.ones(max_cluster_num, dtype) * alpha / max_cluster_num,
    name='rv_sdp')

rv_loc = tfd.Independent(
    tfd.Normal(
        loc=tf.zeros([max_cluster_num, dims], dtype=dtype),
        scale=tf.ones([max_cluster_num, dims], dtype=dtype)),
    reinterpreted_batch_ndims=1,
    name='rv_loc')


rv_precision = tfd.Independent(
    tfd.InverseGamma(
        concentration=np.ones([max_cluster_num, dims], dtype),
        rate=np.ones([max_cluster_num, dims], dtype)),
    reinterpreted_batch_ndims=1,
    name='rv_precision')

rv_alpha = tfd.InverseGamma(
    concentration=np.ones([1], dtype=dtype),
    rate=np.ones([1]),
    name='rv_alpha')

# Define mixture model
rv_observations = tfd.MixtureSameFamily(
    mixture_distribution=tfd.Categorical(probs=mix_probs),
    components_distribution=tfd.MultivariateNormalDiag(
        loc=loc,
        scale_diag=precision))

3. การเพิ่มประสิทธิภาพ

เราปรับโมเดลให้เหมาะสมด้วยการปรับสภาพก่อนกำหนด Stochastic Gradient Langevin Dynamics (pSGLD) ซึ่งช่วยให้เราสามารถปรับโมเดลให้เหมาะสมกับตัวอย่างจำนวนมากในลักษณะการไล่ระดับสีแบบมินิแบทช์

ในการอัปเดตพารามิเตอร์ $ \ boldsymbol {\ theta} \ equiv {\ boldsymbol {\ pi}, \, \ alpha, \, \ boldsymbol {\ mu_j}, \, \ boldsymbol {\ sigma_j}} $ ใน $ t \, $ การทำซ้ำด้วยขนาดมินิแบทช์ $ M $ การอัปเดตจะถูกสุ่มตัวอย่างเป็น:

$$\begin{align*} \Delta \boldsymbol { \theta } _ { t } & \sim \frac { \epsilon _ { t } } { 2 } \bigl[ G \left( \boldsymbol { \theta } _ { t } \right) \bigl( \nabla _ { \boldsymbol { \theta } } \log p \left( \boldsymbol { \theta } _ { t } \right) + \frac { N } { M } \sum _ { k = 1 } ^ { M } \nabla _ \boldsymbol { \theta } \log \text{GMM}(x_{t_k})\bigr) + \sum_\boldsymbol{\theta}\nabla_\theta G \left( \boldsymbol { \theta } _ { t } \right) \bigr]\\ &+ G ^ { \frac { 1 } { 2 } } \left( \boldsymbol { \theta } _ { t } \right) \text { Normal } \left( \text{loc}=\boldsymbol{0} ,\, \text{scale}=\epsilon _ { t }\boldsymbol{1} \right)\\ \end{align*}$$

ในสมการข้างต้น $ \ epsilon _ {t} $ คืออัตราการเรียนรู้ที่ $ t \, $ th การวนซ้ำและ $ \ log p (\ theta_t) $ คือผลรวมของการแจกแจงบันทึกก่อนหน้าของ $ \ theta $ $ G (\ boldsymbol {\ theta} _ {t}) $ เป็นตัวกำหนดเงื่อนไขเบื้องต้นซึ่งจะปรับขนาดของการไล่ระดับสีของแต่ละพารามิเตอร์

# Learning rates and decay
starter_learning_rate = 1e-6
end_learning_rate = 1e-10
decay_steps = 1e4

# Number of training steps
training_steps = 10000

# Mini-batch size
batch_size = 20

# Sample size for parameter posteriors
sample_size = 100

เราจะใช้ความน่าจะเป็นในการบันทึกร่วมของโอกาส $ \ text {GMM} (x_ {t_k}) $ และความน่าจะเป็นก่อนหน้า $ p (\ theta_t) $ เป็นฟังก์ชันการสูญเสียสำหรับ pSGLD

โปรดทราบว่าตามที่ระบุไว้ใน API ของ pSGLD เราจำเป็นต้องหารผลรวมของความน่าจะเป็นก่อนหน้าด้วยขนาดตัวอย่าง $ N $

# Placeholder for mini-batch
observations_tensor = tf.compat.v1.placeholder(dtype, shape=[batch_size, dims])

# Define joint log probabilities
# Notice that each prior probability should be divided by num_samples and
# likelihood is divided by batch_size for pSGLD optimization.
log_prob_parts = [
    rv_loc.log_prob(loc) / num_samples,
    rv_precision.log_prob(precision) / num_samples,
    rv_alpha.log_prob(alpha) / num_samples,
    rv_symmetric_dirichlet_process.log_prob(mix_probs)[..., tf.newaxis]
    / num_samples,
    rv_observations.log_prob(observations_tensor) / batch_size
]
joint_log_prob = tf.reduce_sum(tf.concat(log_prob_parts, axis=-1), axis=-1)
# Make mini-batch generator
dx = tf.compat.v1.data.Dataset.from_tensor_slices(observations)\
  .shuffle(500).repeat().batch(batch_size)
iterator = tf.compat.v1.data.make_one_shot_iterator(dx)
next_batch = iterator.get_next()

# Define learning rate scheduling
global_step = tf.Variable(0, trainable=False)
learning_rate = tf.train.polynomial_decay(
    starter_learning_rate,
    global_step, decay_steps,
    end_learning_rate, power=1.)

# Set up the optimizer. Don't forget to set data_size=num_samples.
optimizer_kernel = tfp.optimizer.StochasticGradientLangevinDynamics(
    learning_rate=learning_rate,
    preconditioner_decay_rate=0.99,
    burnin=1500,
    data_size=num_samples)

train_op = optimizer_kernel.minimize(-joint_log_prob)

# Arrays to store samples
mean_mix_probs_mtx = np.zeros([training_steps, max_cluster_num])
mean_alpha_mtx = np.zeros([training_steps, 1])
mean_loc_mtx = np.zeros([training_steps, max_cluster_num, dims])
mean_precision_mtx = np.zeros([training_steps, max_cluster_num, dims])

init = tf.global_variables_initializer()
sess.run(init)

start = time.time()
for it in range(training_steps):
  [
      mean_mix_probs_mtx[it, :],
      mean_alpha_mtx[it, 0],
      mean_loc_mtx[it, :, :],
      mean_precision_mtx[it, :, :],
      _
  ] = sess.run([
      *training_vals,
      train_op
  ], feed_dict={
      observations_tensor: sess.run(next_batch)})

elapsed_time_psgld = time.time() - start
print("Elapsed time: {} seconds".format(elapsed_time_psgld))

# Take mean over the last sample_size iterations
mean_mix_probs_ = mean_mix_probs_mtx[-sample_size:, :].mean(axis=0)
mean_alpha_ = mean_alpha_mtx[-sample_size:, :].mean(axis=0)
mean_loc_ = mean_loc_mtx[-sample_size:, :].mean(axis=0)
mean_precision_ = mean_precision_mtx[-sample_size:, :].mean(axis=0)
Elapsed time: 309.8013095855713 seconds

4. เห็นภาพผลลัพธ์

4.1. ผลลัพธ์แบบคลัสเตอร์

อันดับแรกเราเห็นภาพผลลัพธ์ของการจัดกลุ่ม

สำหรับการกำหนด $ x_i $ แต่ละตัวอย่างให้กับคลัสเตอร์ $ j $ เราคำนวณด้านหลังของ $ z_i $ เป็น:

$$\begin{align*} j = \underset{z_i}{\arg\max}\,p(z_i\,|\,x_i,\,\boldsymbol{\theta}) \end{align*}$$
loc_for_posterior = tf.compat.v1.placeholder(
    dtype, [None, max_cluster_num, dims], name='loc_for_posterior')
precision_for_posterior = tf.compat.v1.placeholder(
    dtype, [None, max_cluster_num, dims], name='precision_for_posterior')
mix_probs_for_posterior = tf.compat.v1.placeholder(
    dtype, [None, max_cluster_num], name='mix_probs_for_posterior')

# Posterior of z (unnormalized)
unnomarlized_posterior = tfd.MultivariateNormalDiag(
    loc=loc_for_posterior, scale_diag=precision_for_posterior)\
   .log_prob(tf.expand_dims(tf.expand_dims(observations, axis=1), axis=1))\

   + tf.log(mix_probs_for_posterior[tf.newaxis, ...])

# Posterior of z (normarizad over latent states)
posterior = unnomarlized_posterior\

  - tf.reduce_logsumexp(unnomarlized_posterior, axis=-1)[..., tf.newaxis]

cluster_asgmt = sess.run(tf.argmax(
    tf.reduce_mean(posterior, axis=1), axis=1), feed_dict={
        loc_for_posterior: mean_loc_mtx[-sample_size:, :],
        precision_for_posterior: mean_precision_mtx[-sample_size:, :],
        mix_probs_for_posterior: mean_mix_probs_mtx[-sample_size:, :]})

idxs, count = np.unique(cluster_asgmt, return_counts=True)

print('Number of inferred clusters = {}\n'.format(len(count)))
np.set_printoptions(formatter={'float': '{: 0.3f}'.format})

print('Number of elements in each cluster = {}\n'.format(count))

def convert_int_elements_to_consecutive_numbers_in(array):
  unique_int_elements = np.unique(array)
  for consecutive_number, unique_int_element in enumerate(unique_int_elements):
    array[array == unique_int_element] = consecutive_number
  return array

cmap = plt.get_cmap('tab10')
plt.scatter(
    observations[:, 0], observations[:, 1],
    1,
    c=cmap(convert_int_elements_to_consecutive_numbers_in(cluster_asgmt)))
plt.axis([-10, 10, -10, 10])
plt.show()
Number of inferred clusters = 3

Number of elements in each cluster = [16911 16645 16444]

png

เราสามารถเห็นว่ามีการกำหนดตัวอย่างจำนวนเกือบเท่ากันให้กับคลัสเตอร์ที่เหมาะสมและโมเดลก็สรุปจำนวนคลัสเตอร์ที่ถูกต้องได้สำเร็จเช่นกัน

4.2. เห็นภาพความไม่แน่นอน

ที่นี่เราดูความไม่แน่นอนของผลการจัดกลุ่มโดยการแสดงภาพสำหรับแต่ละตัวอย่าง

เราคำนวณความไม่แน่นอนโดยใช้เอนโทรปี:

$$\begin{align*} \text{Uncertainty}_\text{entropy} = -\frac{1}{K}\sum^{K}_{z_i=1}\sum^{O}_{l=1}p(z_i\,|\,x_i,\,\boldsymbol{\theta}_l)\log p(z_i\,|\,x_i,\,\boldsymbol{\theta}_l) \end{align*}$$

ใน pSGLD เราถือว่าค่าของพารามิเตอร์การฝึกในการวนซ้ำแต่ละครั้งเป็นตัวอย่างจากการแจกแจงหลัง ดังนั้นเราจึงคำนวณเอนโทรปีมากกว่าค่าจากการวนซ้ำ $ O $ สำหรับแต่ละพารามิเตอร์ ค่าเอนโทรปีสุดท้ายคำนวณโดยการหาค่าเฉลี่ยเอนโทรปีของการกำหนดคลัสเตอร์ทั้งหมด

# Calculate entropy
posterior_in_exponential = tf.exp(posterior)
uncertainty_in_entropy = tf.reduce_mean(-tf.reduce_sum(
    posterior_in_exponential

    * posterior,
    axis=1), axis=1)

uncertainty_in_entropy_ = sess.run(uncertainty_in_entropy, feed_dict={
    loc_for_posterior: mean_loc_mtx[-sample_size:, :],
    precision_for_posterior: mean_precision_mtx[-sample_size:, :],
    mix_probs_for_posterior: mean_mix_probs_mtx[-sample_size:, :]
})
plt.title('Entropy')
sc = plt.scatter(observations[:, 0],
                 observations[:, 1],
                 1,
                 c=uncertainty_in_entropy_,
                 cmap=plt.cm.viridis_r)
cbar = plt.colorbar(sc,
                    fraction=0.046,
                    pad=0.04,
                    ticks=[uncertainty_in_entropy_.min(),
                           uncertainty_in_entropy_.max()])
cbar.ax.set_yticklabels(['low', 'high'])
cbar.set_label('Uncertainty', rotation=270)
plt.show()

png

ในกราฟด้านบนความสว่างที่น้อยลงแสดงถึงความไม่แน่นอนมากขึ้น เราสามารถเห็นตัวอย่างที่อยู่ใกล้ขอบเขตของคลัสเตอร์มีความไม่แน่นอนที่สูงขึ้นโดยเฉพาะ นี่เป็นความจริงโดยสังหรณ์ใจว่ากลุ่มตัวอย่างเหล่านั้นยากที่จะรวมกลุ่ม

4.3. ค่าเฉลี่ยและขนาดของส่วนประกอบของส่วนผสมที่เลือก

ต่อไปเราจะดูที่คลัสเตอร์ที่เลือก '$ \ mu_j $ และ $ \ sigma_j $

for idx, numbe_of_samples in zip(idxs, count):
  print(
      'Component id = {}, Number of elements = {}'
      .format(idx, numbe_of_samples))
  print(
      'Mean loc = {}, Mean scale = {}\n'
      .format(mean_loc_[idx, :], mean_precision_[idx, :]))
Component id = 0, Number of elements = 16911
Mean loc = [-4.030 -4.113], Mean scale = [ 0.994  0.972]

Component id = 4, Number of elements = 16645
Mean loc = [ 3.999  4.069], Mean scale = [ 1.038  1.046]

Component id = 5, Number of elements = 16444
Mean loc = [-0.005 -0.023], Mean scale = [ 0.967  1.025]

อีกครั้ง $ \ boldsymbol {\ mu_j} $ และ $ \ boldsymbol {\ sigma_j} $ ใกล้เคียงกับความจริงพื้นดิน

4.4 น้ำหนักส่วนผสมของส่วนผสมแต่ละส่วน

นอกจากนี้เรายังดูน้ำหนักส่วนผสมที่สรุปได้ด้วย

plt.ylabel('Mean posterior of mixture weight')
plt.xlabel('Component')
plt.bar(range(0, max_cluster_num), mean_mix_probs_)
plt.show()

png

เราเห็นส่วนประกอบของส่วนผสมเพียงไม่กี่ (สาม) ชิ้นเท่านั้นที่มีน้ำหนักที่สำคัญและน้ำหนักที่เหลือมีค่าใกล้เคียงกับศูนย์ นอกจากนี้ยังแสดงให้เห็นแบบจำลองที่สรุปจำนวนส่วนประกอบของส่วนผสมที่ถูกต้องซึ่งถือเป็นการกระจายตัวอย่างได้สำเร็จ

4.5. การบรรจบกันของ $ \ alpha $

เราดูการบรรจบกันของพารามิเตอร์ความเข้มข้นของการแจกแจง Dirichlet $ \ alpha $

print('Value of inferred alpha = {0:.3f}\n'.format(mean_alpha_[0]))
plt.ylabel('Sample value of alpha')
plt.xlabel('Iteration')
plt.plot(mean_alpha_mtx)
plt.show()
Value of inferred alpha = 0.679

png

เมื่อพิจารณาจากข้อเท็จจริงที่ว่า $ \ alpha $ ที่เล็กลงส่งผลให้จำนวนคลัสเตอร์ที่คาดหวังน้อยลงในแบบจำลองส่วนผสมของ Dirichlet แบบจำลองดูเหมือนจะเรียนรู้จำนวนคลัสเตอร์ที่เหมาะสมที่สุดผ่านการทำซ้ำ

4.6. จำนวนคลัสเตอร์ที่อ้างอิงจากการทำซ้ำ

เราเห็นภาพว่าจำนวนคลัสเตอร์ที่สรุปเปลี่ยนแปลงไปอย่างไรเมื่อเทียบกับการทำซ้ำ

ในการทำเช่นนั้นเราจะสรุปจำนวนคลัสเตอร์ในการทำซ้ำ

step = sample_size
num_of_iterations = 50
estimated_num_of_clusters = []
interval = (training_steps - step) // (num_of_iterations - 1)
iterations = np.asarray(range(step, training_steps+1, interval))
for iteration in iterations:
  start_position = iteration-step
  end_position = iteration

  result = sess.run(tf.argmax(
      tf.reduce_mean(posterior, axis=1), axis=1), feed_dict={
          loc_for_posterior:
              mean_loc_mtx[start_position:end_position, :],
          precision_for_posterior:
              mean_precision_mtx[start_position:end_position, :],
          mix_probs_for_posterior:
              mean_mix_probs_mtx[start_position:end_position, :]})

  idxs, count = np.unique(result, return_counts=True)
  estimated_num_of_clusters.append(len(count))
plt.ylabel('Number of inferred clusters')
plt.xlabel('Iteration')
plt.yticks(np.arange(1, max(estimated_num_of_clusters) + 1, 1))
plt.plot(iterations - 1, estimated_num_of_clusters)
plt.show()

png

ในการทำซ้ำจำนวนคลัสเตอร์จะเข้าใกล้สามมากขึ้น ด้วยผลลัพธ์ของการรวมกันของ $ \ alpha $ เป็นค่าที่น้อยกว่าในการวนซ้ำเราจะเห็นว่าโมเดลประสบความสำเร็จในการเรียนรู้พารามิเตอร์เพื่อสรุปจำนวนคลัสเตอร์ที่เหมาะสมที่สุด

ที่น่าสนใจคือเราสามารถเห็นว่าการอนุมานได้มาบรรจบกันเป็นจำนวนคลัสเตอร์ที่ถูกต้องแล้วในการทำซ้ำในช่วงต้นซึ่งแตกต่างจาก $ \ alpha $ ที่มาบรรจบกันในการทำซ้ำในภายหลัง

4.7. การติดตั้งโมเดลโดยใช้ RMSProp

ในส่วนนี้เพื่อดูประสิทธิภาพของรูปแบบการสุ่มตัวอย่างมอนติคาร์โลของ pSGLD เราใช้ RMSProp เพื่อให้พอดีกับโมเดล เราเลือก RMSProp เพื่อเปรียบเทียบเนื่องจากไม่มีรูปแบบการสุ่มตัวอย่างและ pSGLD ขึ้นอยู่กับ RMSProp

# Learning rates and decay
starter_learning_rate_rmsprop = 1e-2
end_learning_rate_rmsprop = 1e-4
decay_steps_rmsprop = 1e4

# Number of training steps
training_steps_rmsprop = 50000

# Mini-batch size
batch_size_rmsprop = 20
# Define trainable variables.
mix_probs_rmsprop = tf.nn.softmax(
    tf.Variable(
        name='mix_probs_rmsprop',
        initial_value=np.ones([max_cluster_num], dtype) / max_cluster_num))

loc_rmsprop = tf.Variable(
    name='loc_rmsprop',
    initial_value=np.zeros([max_cluster_num, dims], dtype)

    + np.random.uniform(
        low=-9, #set around minimum value of sample value
        high=9, #set around maximum value of sample value
        size=[max_cluster_num, dims]))

precision_rmsprop = tf.nn.softplus(tf.Variable(
    name='precision_rmsprop',
    initial_value=
    np.ones([max_cluster_num, dims], dtype=dtype)))

alpha_rmsprop = tf.nn.softplus(tf.Variable(
    name='alpha_rmsprop',
    initial_value=
    np.ones([1], dtype=dtype)))

training_vals_rmsprop =\
    [mix_probs_rmsprop, alpha_rmsprop, loc_rmsprop, precision_rmsprop]

# Prior distributions of the training variables

#Use symmetric Dirichlet prior as finite approximation of Dirichlet process.
rv_symmetric_dirichlet_process_rmsprop = tfd.Dirichlet(
    concentration=np.ones(max_cluster_num, dtype)

    * alpha_rmsprop / max_cluster_num,
    name='rv_sdp_rmsprop')

rv_loc_rmsprop = tfd.Independent(
    tfd.Normal(
        loc=tf.zeros([max_cluster_num, dims], dtype=dtype),
        scale=tf.ones([max_cluster_num, dims], dtype=dtype)),
    reinterpreted_batch_ndims=1,
    name='rv_loc_rmsprop')


rv_precision_rmsprop = tfd.Independent(
    tfd.InverseGamma(
        concentration=np.ones([max_cluster_num, dims], dtype),
        rate=np.ones([max_cluster_num, dims], dtype)),
    reinterpreted_batch_ndims=1,
    name='rv_precision_rmsprop')

rv_alpha_rmsprop = tfd.InverseGamma(
    concentration=np.ones([1], dtype=dtype),
    rate=np.ones([1]),
    name='rv_alpha_rmsprop')

# Define mixture model
rv_observations_rmsprop = tfd.MixtureSameFamily(
    mixture_distribution=tfd.Categorical(probs=mix_probs_rmsprop),
    components_distribution=tfd.MultivariateNormalDiag(
        loc=loc_rmsprop,
        scale_diag=precision_rmsprop))
og_prob_parts_rmsprop = [
    rv_loc_rmsprop.log_prob(loc_rmsprop),
    rv_precision_rmsprop.log_prob(precision_rmsprop),
    rv_alpha_rmsprop.log_prob(alpha_rmsprop),
    rv_symmetric_dirichlet_process_rmsprop
        .log_prob(mix_probs_rmsprop)[..., tf.newaxis],
    rv_observations_rmsprop.log_prob(observations_tensor)

    * num_samples / batch_size
]
joint_log_prob_rmsprop = tf.reduce_sum(
    tf.concat(log_prob_parts_rmsprop, axis=-1), axis=-1)
# Define learning rate scheduling
global_step_rmsprop = tf.Variable(0, trainable=False)
learning_rate = tf.train.polynomial_decay(
    starter_learning_rate_rmsprop,
    global_step_rmsprop, decay_steps_rmsprop,
    end_learning_rate_rmsprop, power=1.)

# Set up the optimizer. Don't forget to set data_size=num_samples.
optimizer_kernel_rmsprop = tf.train.RMSPropOptimizer(
    learning_rate=learning_rate,
    decay=0.99)

train_op_rmsprop = optimizer_kernel_rmsprop.minimize(-joint_log_prob_rmsprop)

init_rmsprop = tf.global_variables_initializer()
sess.run(init_rmsprop)

start = time.time()
for it in range(training_steps_rmsprop):
  [
      _
  ] = sess.run([
      train_op_rmsprop
  ], feed_dict={
      observations_tensor: sess.run(next_batch)})

elapsed_time_rmsprop = time.time() - start
print("RMSProp elapsed_time: {} seconds ({} iterations)"
      .format(elapsed_time_rmsprop, training_steps_rmsprop))
print("pSGLD elapsed_time: {} seconds ({} iterations)"
      .format(elapsed_time_psgld, training_steps))

mix_probs_rmsprop_, alpha_rmsprop_, loc_rmsprop_, precision_rmsprop_ =\
  sess.run(training_vals_rmsprop)
RMSProp elapsed_time: 53.7574200630188 seconds (50000 iterations)
pSGLD elapsed_time: 309.8013095855713 seconds (10000 iterations)

เปรียบเทียบกับ pSGLD แม้ว่าจำนวนการทำซ้ำสำหรับ RMSProp จะนานกว่า แต่การเพิ่มประสิทธิภาพโดย RMSProp นั้นเร็วกว่ามาก

ต่อไปเราจะดูผลการจัดกลุ่ม

cluster_asgmt_rmsprop = sess.run(tf.argmax(
    tf.reduce_mean(posterior, axis=1), axis=1), feed_dict={
        loc_for_posterior: loc_rmsprop_[tf.newaxis, :],
        precision_for_posterior: precision_rmsprop_[tf.newaxis, :],
        mix_probs_for_posterior: mix_probs_rmsprop_[tf.newaxis, :]})

idxs, count = np.unique(cluster_asgmt_rmsprop, return_counts=True)

print('Number of inferred clusters = {}\n'.format(len(count)))
np.set_printoptions(formatter={'float': '{: 0.3f}'.format})

print('Number of elements in each cluster = {}\n'.format(count))

cmap = plt.get_cmap('tab10')
plt.scatter(
    observations[:, 0], observations[:, 1],
    1,
    c=cmap(convert_int_elements_to_consecutive_numbers_in(
        cluster_asgmt_rmsprop)))
plt.axis([-10, 10, -10, 10])
plt.show()
Number of inferred clusters = 4

Number of elements in each cluster = [ 1644 15267 16647 16442]

png

จำนวนคลัสเตอร์ไม่ได้รับการอนุมานอย่างถูกต้องโดยการเพิ่มประสิทธิภาพ RMSProp ในการทดสอบของเรา เราดูน้ำหนักส่วนผสมด้วย

plt.ylabel('MAP inferece of mixture weight')
plt.xlabel('Component')
plt.bar(range(0, max_cluster_num), mix_probs_rmsprop_)
plt.show()

png

เราสามารถเห็นจำนวนส่วนประกอบที่ไม่ถูกต้องซึ่งมีน้ำหนักส่วนผสมที่สำคัญ

แม้ว่าการเพิ่มประสิทธิภาพจะใช้เวลานานกว่า แต่ pSGLD ซึ่งมีรูปแบบการสุ่มตัวอย่างมอนติคาร์โลจะทำงานได้ดีกว่าในการทดสอบของเรา

5. สรุป

ในสมุดบันทึกนี้เราได้อธิบายวิธีการจัดกลุ่มตัวอย่างจำนวนมากตลอดจนการอนุมานจำนวนคลัสเตอร์พร้อม ๆ กันโดยการปรับส่วนผสมของกระบวนการ Dirichlet ของการแจกแจงแบบเกาส์เซียนโดยใช้ pSGLD

การทดลองแสดงให้เห็นว่าโมเดลจัดกลุ่มตัวอย่างได้สำเร็จและสรุปจำนวนคลัสเตอร์ที่ถูกต้อง นอกจากนี้เราได้แสดงรูปแบบการสุ่มตัวอย่างของมอนติคาร์โลของ pSGLD ช่วยให้เราเห็นภาพความไม่แน่นอนในผลลัพธ์ ไม่เพียง แต่จัดกลุ่มตัวอย่างเท่านั้น แต่เรายังได้เห็นแบบจำลองที่สามารถสรุปพารามิเตอร์ที่ถูกต้องของส่วนประกอบของส่วนผสมได้ เกี่ยวกับความสัมพันธ์ระหว่างพารามิเตอร์และจำนวนคลัสเตอร์ที่สรุปได้เราได้ตรวจสอบวิธีที่โมเดลเรียนรู้พารามิเตอร์เพื่อควบคุมจำนวนคลัสเตอร์ที่มีประสิทธิผลโดยการแสดงภาพความสัมพันธ์ระหว่างการบรรจบกันของ 𝛼 และจำนวนคลัสเตอร์ที่สรุปได้ สุดท้ายนี้เราได้ดูผลลัพธ์ของการปรับโมเดลโดยใช้ RMSProp เราได้เห็น RMSProp ซึ่งเป็นเครื่องมือเพิ่มประสิทธิภาพที่ไม่มีรูปแบบการสุ่มตัวอย่างมอนติคาร์โลทำงานได้เร็วกว่า pSGLD มาก แต่ให้ความแม่นยำในการทำคลัสเตอร์น้อยกว่า

แม้ว่าชุดข้อมูลของเล่นจะมีเพียง 50,000 ตัวอย่างที่มีเพียงสองมิติ แต่การเพิ่มประสิทธิภาพแบบมินิแบทช์ที่ใช้ที่นี่สามารถปรับขนาดได้สำหรับชุดข้อมูลที่มีขนาดใหญ่กว่ามาก