Sơn lót Copulas

Xem trên TensorFlow.org Chạy trong Google Colab Xem nguồn trên GitHub Tải xuống sổ ghi chép
import numpy as np
import matplotlib.pyplot as plt
import tensorflow.compat.v2 as tf
tf.enable_v2_behavior()

import tensorflow_probability as tfp
tfd = tfp.distributions
tfb = tfp.bijectors

A [copula] (https://en.wikipedia.org/wiki/Copula_ (probability_theory% 29) là một phương pháp cổ điển để chụp sự phụ thuộc giữa các biến ngẫu nhiên. Chính thức hơn, một copula là một phân phối đa biến \(C(U_1, U_2, ...., U_n)\) mà marginalizing cho \(U_i \sim \text{Uniform}(0, 1)\).

Copulas rất thú vị vì chúng ta có thể sử dụng chúng để tạo ra các phân phối đa biến với các biên tùy ý. Đây là công thức:

  • Sử dụng Integral Xác suất chuyển đổi lần lượt một tùy RV liên tục \(X\) vào một bộ đồng phục một \(F_X(X)\), nơi \(F_X\) là CDF của \(X\).
  • Cho một copula (nói hai biến) \(C(U, V)\), chúng tôi có mà \(U\) và \(V\) có phân bố biên thống nhất.
  • Ngay bây giờ cho RV của chúng tôi là quan tâm \(X, Y\), tạo ra một phân phối mới \(C'(X, Y) = C(F_X(X), F_Y(Y))\). Các marginals cho \(X\) và \(Y\) là những người chúng ta mong muốn.

Biên là đơn biến và do đó có thể dễ dàng hơn để đo lường và / hoặc mô hình hóa. Một copula cho phép bắt đầu từ các biên nhưng cũng đạt được sự tương quan tùy ý giữa các kích thước.

Gaussian Copula

Để minh họa cách các công thức đồng biến được xây dựng, hãy xem xét trường hợp nắm bắt sự phụ thuộc theo các tương quan Gaussian đa biến. Một Gaussian copula là một trong do \(C(u_1, u_2, ...u_n) = \Phi_\Sigma(\Phi^{-1}(u_1), \Phi^{-1}(u_2), ... \Phi^{-1}(u_n))\) nơi \(\Phi_\Sigma\) đại diện cho CDF của một MultivariateNormal, với hiệp phương sai \(\Sigma\) và trung bình 0 và \(\Phi^{-1}\) là CDF nghịch đảo cho các tiêu chuẩn bình thường.

Việc áp dụng CDF nghịch đảo của thông thường sẽ làm cong các kích thước đồng nhất được phân phối chuẩn. Việc áp dụng CDF của chuẩn tắc đa biến sau đó thu nhỏ phân phối để được đồng nhất một chút và với các tương quan Gauss.

Vì vậy, những gì chúng ta có được là Gaussian copula là một bản phân phối qua các đơn vị hypercube \([0, 1]^n\) với marginals thống nhất.

Định nghĩa như vậy, Gaussian copula có thể được thực hiện với tfd.TransformedDistribution và phù hợp Bijector . Nghĩa là, chúng ta đang chuyển đổi một MultivariateNormal, thông qua việc sử dụng các phân phối của Bình thường nghịch đảo CDF, thực hiện bởi tfb.NormalCDF bijector.

Dưới đây, chúng tôi thực hiện một Gaussian copula với một giả định đơn giản hóa: đó là các hiệp phương sai được tham số hóa bởi một yếu tố Cholesky (vì thế một hiệp phương sai cho MultivariateNormalTriL ). (Người ta có thể sử dụng khác tf.linalg.LinearOperators để mã hóa các giả định ma trận miễn phí khác.).

class GaussianCopulaTriL(tfd.TransformedDistribution):
  """Takes a location, and lower triangular matrix for the Cholesky factor."""
  def __init__(self, loc, scale_tril):
    super(GaussianCopulaTriL, self).__init__(
        distribution=tfd.MultivariateNormalTriL(
            loc=loc,
            scale_tril=scale_tril),
        bijector=tfb.NormalCDF(),
        validate_args=False,
        name="GaussianCopulaTriLUniform")


# Plot an example of this.
unit_interval = np.linspace(0.01, 0.99, num=200, dtype=np.float32)
x_grid, y_grid = np.meshgrid(unit_interval, unit_interval)
coordinates = np.concatenate(
    [x_grid[..., np.newaxis],
     y_grid[..., np.newaxis]], axis=-1)

pdf = GaussianCopulaTriL(
    loc=[0., 0.],
    scale_tril=[[1., 0.8], [0., 0.6]],
).prob(coordinates)

# Plot its density.

plt.contour(x_grid, y_grid, pdf, 100, cmap=plt.cm.jet);

png

Tuy nhiên, sức mạnh từ một mô hình như vậy đang sử dụng Phép biến đổi Tích phân Xác suất, để sử dụng bản sao trên các RV tùy ý. Bằng cách này, chúng ta có thể chỉ định các biên tùy ý và sử dụng bản sao để ghép chúng lại với nhau.

Chúng tôi bắt đầu với một mô hình:

\[\begin{align*} X &\sim \text{Kumaraswamy}(a, b) \\ Y &\sim \text{Gumbel}(\mu, \beta) \end{align*}\]

và sử dụng copula để có được một hai biến RV \(Z\), trong đó có marginals KumaraswamyGumbel .

Chúng tôi sẽ bắt đầu bằng cách vẽ biểu đồ phân phối sản phẩm được tạo ra bởi hai RV đó. Đây chỉ là để phục vụ như một điểm so sánh khi chúng tôi áp dụng Copula.

a = 2.0
b = 2.0
gloc = 0.
gscale = 1.

x = tfd.Kumaraswamy(a, b)
y = tfd.Gumbel(loc=gloc, scale=gscale)

# Plot the distributions, assuming independence
x_axis_interval = np.linspace(0.01, 0.99, num=200, dtype=np.float32)
y_axis_interval = np.linspace(-2., 3., num=200, dtype=np.float32)
x_grid, y_grid = np.meshgrid(x_axis_interval, y_axis_interval)

pdf = x.prob(x_grid) * y.prob(y_grid)

# Plot its density

plt.contour(x_grid, y_grid, pdf, 100, cmap=plt.cm.jet);

png

Phân phối chung với các biên khác nhau

Bây giờ chúng ta sử dụng một copula Gaussian để ghép các phân bố lại với nhau và vẽ biểu đồ đó. Một lần nữa công cụ của chúng tôi lựa chọn được TransformedDistribution áp dụng thích hợp Bijector để có được những marginals chọn.

Cụ thể, chúng tôi sử dụng một Blockwise bijector áp dụng bijectors khác nhau ở các phần khác nhau của vector (mà vẫn còn là một chuyển đổi song ánh).

Bây giờ chúng ta có thể xác định Copula mà chúng ta muốn. Đưa ra danh sách các biên mục tiêu (được mã hóa dưới dạng bijector), chúng ta có thể dễ dàng tạo một phân phối mới sử dụng copula và có các biên được chỉ định.

class WarpedGaussianCopula(tfd.TransformedDistribution):
  """Application of a Gaussian Copula on a list of target marginals.

  This implements an application of a Gaussian Copula. Given [x_0, ... x_n]
  which are distributed marginally (with CDF) [F_0, ... F_n],
  `GaussianCopula` represents an application of the Copula, such that the
  resulting multivariate distribution has the above specified marginals.

  The marginals are specified by `marginal_bijectors`: These are
  bijectors whose `inverse` encodes the CDF and `forward` the inverse CDF.

  block_sizes is a 1-D Tensor to determine splits for `marginal_bijectors`
  length should be same as length of `marginal_bijectors`.
  See tfb.Blockwise for details
  """
  def __init__(self, loc, scale_tril, marginal_bijectors, block_sizes=None):
    super(WarpedGaussianCopula, self).__init__(
        distribution=GaussianCopulaTriL(loc=loc, scale_tril=scale_tril),
        bijector=tfb.Blockwise(bijectors=marginal_bijectors,
                               block_sizes=block_sizes),
        validate_args=False,
        name="GaussianCopula")

Cuối cùng, hãy thực sự sử dụng Gaussian Copula này. Chúng tôi sẽ sử dụng một Cholesky của \(\begin{bmatrix}1 & 0\\\rho & \sqrt{(1-\rho^2)}\end{bmatrix}\), mà sẽ tương ứng với phương sai 1, và tương quan \(\rho\) cho việc bình thường đa biến.

Chúng ta sẽ xem xét một số trường hợp:

# Create our coordinates:
coordinates = np.concatenate(
    [x_grid[..., np.newaxis], y_grid[..., np.newaxis]], -1)


def create_gaussian_copula(correlation):
  # Use Gaussian Copula to add dependence.
  return WarpedGaussianCopula(
      loc=[0.,  0.],
      scale_tril=[[1., 0.], [correlation, tf.sqrt(1. - correlation ** 2)]],
      # These encode the marginals we want. In this case we want X_0 has
      # Kumaraswamy marginal, and X_1 has Gumbel marginal.

      marginal_bijectors=[
          tfb.Invert(tfb.KumaraswamyCDF(a, b)),
          tfb.Invert(tfb.GumbelCDF(loc=0., scale=1.))])


# Note that the zero case will correspond to independent marginals!
correlations = [0., -0.8, 0.8]
copulas = []
probs = []
for correlation in correlations:
  copula = create_gaussian_copula(correlation)
  copulas.append(copula)
  probs.append(copula.prob(coordinates))


# Plot it's density

for correlation, copula_prob in zip(correlations, probs):
  plt.figure()
  plt.contour(x_grid, y_grid, copula_prob, 100, cmap=plt.cm.jet)
  plt.title('Correlation {}'.format(correlation))

png

png

png

Cuối cùng, hãy xác minh rằng chúng tôi thực sự nhận được những lợi nhuận mà chúng tôi muốn.

def kumaraswamy_pdf(x):
    return tfd.Kumaraswamy(a, b).prob(np.float32(x))

def gumbel_pdf(x):
    return tfd.Gumbel(gloc, gscale).prob(np.float32(x))


copula_samples = []
for copula in copulas:
  copula_samples.append(copula.sample(10000))

plot_rows = len(correlations)
plot_cols = 2  # for 2  densities [kumarswamy, gumbel]
fig, axes = plt.subplots(plot_rows, plot_cols, sharex='col', figsize=(18,12))

# Let's marginalize out on each, and plot the samples.

for i, (correlation, copula_sample) in enumerate(zip(correlations, copula_samples)):
  k = copula_sample[..., 0].numpy()
  g = copula_sample[..., 1].numpy()


  _, bins, _ = axes[i, 0].hist(k, bins=100, density=True)
  axes[i, 0].plot(bins, kumaraswamy_pdf(bins), 'r--')
  axes[i, 0].set_title('Kumaraswamy from Copula with correlation {}'.format(correlation))

  _, bins, _ = axes[i, 1].hist(g, bins=100, density=True)
  axes[i, 1].plot(bins, gumbel_pdf(bins), 'r--')
  axes[i, 1].set_title('Gumbel from Copula with correlation {}'.format(correlation))

png

Sự kết luận

Và chúng ta bắt đầu! Chúng tôi đã chứng minh rằng chúng ta có thể xây dựng Gaussian Copulas sử dụng Bijector API.

Tổng quát hơn, viết bijectors sử dụng Bijector API và sáng tác chúng với một phân phối, có thể tạo ra những gia đình giàu có của các bản phân phối cho mô hình linh hoạt.