Hiểu các hình dạng phân phối TensorFlow

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 collections

import tensorflow as tf
tf.compat.v2.enable_v2_behavior()

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

Khái niệm cơ bản

Có ba khái niệm quan trọng liên quan đến hình dạng Phân phối TensorFlow:

  • Hình tổ chức sự kiện mô tả hình dạng của một trận hòa duy nhất từ phân phối; nó có thể phụ thuộc vào các kích thước. Đối với các bản phân phối vô hướng, hình dạng sự kiện là [] . Đối với một MultivariateNormal 5 chiều, hình dạng sự kiện là [5] .
  • Hình hàng loạt mô tả độc lập, không giống nhau phân phối thu hút, hay còn gọi là một "mẻ" của các bản phân phối.
  • Hình mẫu mô tả độc lập, phân phối hệt rút lô từ các gia đình phân phối.

Hình dạng sự kiện và hình dạng đợt này chỉ là thuộc tính của một Distribution đối tượng, trong khi hình dạng mẫu được liên kết với một cuộc gọi cụ thể để sample hoặc log_prob .

Mục đích của sổ tay này là minh họa những khái niệm này thông qua các ví dụ, vì vậy nếu điều này không rõ ràng ngay lập tức, đừng lo lắng!

Đối với một cái nhìn tổng quan về khái niệm của những khái niệm này, xem bài viết trên blog này .

Một ghi chú trên TensorFlow Eager.

Toàn bộ máy tính xách tay này được viết bằng TensorFlow Háo hức . Không ai trong số các khái niệm được trình bày dựa vào Háo hức, mặc dù với Háo hức, hàng loạt phân phối và sự kiện hình được đánh giá (và do đó biết) khi Distribution đối tượng được tạo ra trong Python, trong khi đồ thị (chế độ không háo hức), người ta có thể xác định sự phân bố có hình dạng sự kiện và lô không được xác định cho đến khi đồ thị được chạy.

Phân phối vô hướng

Như chúng ta đã nói ở trên, một Distribution đối tượng đã xác định sự kiện và hàng loạt các hình dạng. Chúng tôi sẽ bắt đầu với một tiện ích để mô tả các bản phân phối:

def describe_distributions(distributions):
  print('\n'.join([str(d) for d in distributions]))

Trong phần này chúng ta sẽ khám phá các bản phân phối vô hướng: phân phối với một hình dạng trường hợp [] . Một ví dụ điển hình là sự phân bố Poisson, xác định bởi một rate :

poisson_distributions = [
    tfd.Poisson(rate=1., name='One Poisson Scalar Batch'),
    tfd.Poisson(rate=[1., 10., 100.], name='Three Poissons'),
    tfd.Poisson(rate=[[1., 10., 100.,], [2., 20., 200.]],
                name='Two-by-Three Poissons'),
    tfd.Poisson(rate=[1.], name='One Poisson Vector Batch'),
    tfd.Poisson(rate=[[1.]], name='One Poisson Expanded Batch')
]

describe_distributions(poisson_distributions)
tfp.distributions.Poisson("One_Poisson_Scalar_Batch", batch_shape=[], event_shape=[], dtype=float32)
tfp.distributions.Poisson("Three_Poissons", batch_shape=[3], event_shape=[], dtype=float32)
tfp.distributions.Poisson("Two_by_Three_Poissons", batch_shape=[2, 3], event_shape=[], dtype=float32)
tfp.distributions.Poisson("One_Poisson_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32)
tfp.distributions.Poisson("One_Poisson_Expanded_Batch", batch_shape=[1, 1], event_shape=[], dtype=float32)

Sự phân bố Poisson là một phân phối vô hướng, vì vậy hình dạng sự kiện của nó luôn là [] . Nếu chúng tôi chỉ định nhiều tỷ lệ hơn, những tỷ lệ này sẽ hiển thị ở dạng lô. Cặp ví dụ cuối cùng rất thú vị: chỉ có một tỷ lệ duy nhất, nhưng vì tỷ lệ đó được nhúng trong một mảng numpy có hình dạng không trống, hình dạng đó trở thành hình dạng lô.

Tiêu chuẩn Phân phối chuẩn cũng là một đại lượng vô hướng. Đó là hình dạng sự kiện là [] , giống như cho Poisson, nhưng chúng tôi sẽ chơi với nó để xem ví dụ đầu tiên của chúng ta về phát sóng. Các bình thường được quy định sử dụng locscale các thông số:

normal_distributions = [
    tfd.Normal(loc=0., scale=1., name='Standard'),
    tfd.Normal(loc=[0.], scale=1., name='Standard Vector Batch'),
    tfd.Normal(loc=[0., 1., 2., 3.], scale=1., name='Different Locs'),
    tfd.Normal(loc=[0., 1., 2., 3.], scale=[[1.], [5.]],
               name='Broadcasting Scale')
]

describe_distributions(normal_distributions)
tfp.distributions.Normal("Standard", batch_shape=[], event_shape=[], dtype=float32)
tfp.distributions.Normal("Standard_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32)
tfp.distributions.Normal("Different_Locs", batch_shape=[4], event_shape=[], dtype=float32)
tfp.distributions.Normal("Broadcasting_Scale", batch_shape=[2, 4], event_shape=[], dtype=float32)

Các ví dụ thú vị trên là Broadcasting Scale phân phối. Các loc tham số có hình dạng [4] , và scale tham số có hình dạng [2, 1] . Sử dụng NumPy quy tắc phát sóng , hình dạng batch là [2, 4] . Một cách tương đương (nhưng ít thanh lịch và không-recommended) để xác định "Broadcasting Scale" phân phối sẽ là:

describe_distributions(
    [tfd.Normal(loc=[[0., 1., 2., 3], [0., 1., 2., 3.]],
                scale=[[1., 1., 1., 1.], [5., 5., 5., 5.]])])
tfp.distributions.Normal("Normal", batch_shape=[2, 4], event_shape=[], dtype=float32)

Chúng ta có thể thấy tại sao ký hiệu phát sóng lại hữu ích, mặc dù nó cũng là một nguồn gây đau đầu và lỗi.

Lấy mẫu phân phối vô hướng

Có hai điều chính chúng ta có thể làm với các bản phân phối: chúng ta có thể sample từ họ và chúng ta có thể tính toán log_prob s. Đầu tiên chúng ta hãy khám phá việc lấy mẫu. Nguyên tắc cơ bản là khi chúng ta lấy mẫu từ một phân phối, kết quả tensor có hình dạng [sample_shape, batch_shape, event_shape] , nơi batch_shapeevent_shape được cung cấp bởi các Distribution đối tượng, và sample_shape được cung cấp bởi các cuộc gọi đến sample . Đối với các bản phân phối vô hướng, event_shape = [] , do đó tensor trở về từ mẫu sẽ có hình dạng [sample_shape, batch_shape] . Hãy thử nó:

def describe_sample_tensor_shape(sample_shape, distribution):
    print('Sample shape:', sample_shape)
    print('Returned sample tensor shape:',
          distribution.sample(sample_shape).shape)

def describe_sample_tensor_shapes(distributions, sample_shapes):
    started = False
    for distribution in distributions:
      print(distribution)
      for sample_shape in sample_shapes:
        describe_sample_tensor_shape(sample_shape, distribution)
      print()

sample_shapes = [1, 2, [1, 5], [3, 4, 5]]
describe_sample_tensor_shapes(poisson_distributions, sample_shapes)
tfp.distributions.Poisson("One_Poisson_Scalar_Batch", batch_shape=[], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1,)
Sample shape: 2
Returned sample tensor shape: (2,)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5)

tfp.distributions.Poisson("Three_Poissons", batch_shape=[3], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 3)
Sample shape: 2
Returned sample tensor shape: (2, 3)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 3)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 3)

tfp.distributions.Poisson("Two_by_Three_Poissons", batch_shape=[2, 3], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 2, 3)
Sample shape: 2
Returned sample tensor shape: (2, 2, 3)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 2, 3)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 2, 3)

tfp.distributions.Poisson("One_Poisson_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 1)
Sample shape: 2
Returned sample tensor shape: (2, 1)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 1)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 1)

tfp.distributions.Poisson("One_Poisson_Expanded_Batch", batch_shape=[1, 1], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 1, 1)
Sample shape: 2
Returned sample tensor shape: (2, 1, 1)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 1, 1)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 1, 1)
describe_sample_tensor_shapes(normal_distributions, sample_shapes)
tfp.distributions.Normal("Standard", batch_shape=[], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1,)
Sample shape: 2
Returned sample tensor shape: (2,)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5)

tfp.distributions.Normal("Standard_Vector_Batch", batch_shape=[1], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 1)
Sample shape: 2
Returned sample tensor shape: (2, 1)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 1)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 1)

tfp.distributions.Normal("Different_Locs", batch_shape=[4], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 4)
Sample shape: 2
Returned sample tensor shape: (2, 4)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 4)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 4)

tfp.distributions.Normal("Broadcasting_Scale", batch_shape=[2, 4], event_shape=[], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 2, 4)
Sample shape: 2
Returned sample tensor shape: (2, 2, 4)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 2, 4)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 2, 4)

Đó là về tất cả những gì để nói về sample : tensors mẫu trở lại có hình dạng [sample_shape, batch_shape, event_shape] .

Tính toán log_prob Đối Scalar phân phối

Bây giờ chúng ta hãy nhìn vào log_prob , mà là hơi phức tạp hơn. log_prob mất như là đầu vào một (không rỗng) tensor đại diện cho vị trí (s) mà tại đó để tính toán log_prob cho việc phân phối. Trong trường hợp đơn giản nhất, tensor này sẽ có một hình dạng của các hình thức [sample_shape, batch_shape, event_shape] , nơi batch_shapeevent_shape trận đấu hàng loạt và sự kiện hình dạng của phân phối. Nhớ lại một lần nữa rằng các bản phân phối vô hướng, event_shape = [] , do đó tensor đầu vào có hình dạng [sample_shape, batch_shape] Trong trường hợp này, chúng tôi nhận lại một tensor hình dạng [sample_shape, batch_shape] :

three_poissons = tfd.Poisson(rate=[1., 10., 100.], name='Three Poissons')
three_poissons
<tfp.distributions.Poisson 'Three_Poissons' batch_shape=[3] event_shape=[] dtype=float32>
three_poissons.log_prob([[1., 10., 100.], [100., 10., 1]])  # sample_shape is [2].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[  -1.       ,   -2.0785608,   -3.2223587],
       [-364.73938  ,   -2.0785608,  -95.39484  ]], dtype=float32)>
three_poissons.log_prob([[[[1., 10., 100.], [100., 10., 1.]]]])  # sample_shape is [1, 1, 2].
<tf.Tensor: shape=(1, 1, 2, 3), dtype=float32, numpy=
array([[[[  -1.       ,   -2.0785608,   -3.2223587],
         [-364.73938  ,   -2.0785608,  -95.39484  ]]]], dtype=float32)>

Lưu ý cách trong ví dụ đầu tiên, đầu vào và đầu ra có hình dạng [2, 3] và trong ví dụ thứ hai họ có hình dạng [1, 1, 2, 3] .

Đó sẽ là tất cả những gì có thể nói, nếu nó không được phát sóng. Dưới đây là các quy tắc khi chúng tôi tính đến việc phát sóng. Chúng tôi mô tả nó một cách tổng quát đầy đủ và lưu ý các đơn giản hóa cho các phân phối vô hướng:

  1. Xác định n = len(batch_shape) + len(event_shape) . (Đối với các bản phân phối vô hướng, len(event_shape)=0 .)
  2. Nếu tensor đầu vào t có ít hơn n kích thước, pad hình dạng của nó bằng cách thêm kích thước của kích thước 1 ở bên trái cho đến khi nó có chính xác n chiều. Gọi kết quả tensor t' .
  3. Broadcast các n kích thước ngoài cùng bên phải của t' chống lại [batch_shape, event_shape] của phân phối bạn đang tính toán một log_prob cho. Cụ thể hơn: cho kích thước nơi t' đã phù hợp với phân phối, không làm gì cả, và cho kích thước nơi t' có một singleton, lặp lại rằng singleton số lượng thích hợp của thời đại. Bất kỳ tình huống nào khác là một lỗi. (Đối với các bản phân phối vô hướng, chúng tôi chỉ phát sóng chống batch_shape , vì event_shape = [] .)
  4. Bây giờ chúng ta cuối cùng có thể tính toán log_prob . Các tensor kết quả sẽ có hình dạng [sample_shape, batch_shape] , nơi sample_shape được định nghĩa là bất kỳ kích thước của t hay t' bên trái của n -rightmost kích thước: sample_shape = shape(t)[:-n] .

Điều này có thể là một mớ hỗn độn nếu bạn không biết nó có nghĩa là gì, vì vậy hãy làm một số ví dụ:

three_poissons.log_prob([10.])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-16.104412 ,  -2.0785608, -69.05272  ], dtype=float32)>

Các tensor [10.] (với hình dạng [1] ) được phát sóng trên batch_shape 3, vì vậy chúng tôi đánh giá khả năng ghi cả ba Poissons' ở giá trị 10.

three_poissons.log_prob([[[1.], [10.]], [[100.], [1000.]]])
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy=
array([[[-1.0000000e+00, -7.6974149e+00, -9.5394836e+01],
        [-1.6104412e+01, -2.0785608e+00, -6.9052719e+01]],

       [[-3.6473938e+02, -1.4348087e+02, -3.2223587e+00],
        [-5.9131279e+03, -3.6195427e+03, -1.4069575e+03]]], dtype=float32)>

Trong ví dụ trên, các tensor đầu vào có hình dạng [2, 2, 1] , trong khi các đối tượng phân phối có hình dạng lô 3. Vì vậy, đối với mỗi [2, 2] kích thước mẫu, giá trị duy nhất cung cấp được broadcats cho mỗi của ba Poisson.

Một cách có thể hữu ích để nghĩ về nó: vì three_poissonsbatch_shape = [2, 3] , một cuộc gọi đến log_prob phải mất một tensor có chiều cuối cùng là 1 hoặc 3; bất cứ điều gì khác là một lỗi. (Các quy tắc phát sóng NumPy điều trị các trường hợp đặc biệt của một đại lượng vô hướng như là hoàn toàn tương đương với một tensor hình dạng [1] .)

Hãy kiểm tra sườn của chúng tôi bằng cách chơi với sự phân bố Poisson phức tạp hơn với batch_shape = [2, 3] :

poisson_2_by_3 = tfd.Poisson(
    rate=[[1., 10., 100.,], [2., 20., 200.]],
    name='Two-by-Three Poissons')
poisson_2_by_3.log_prob(1.)
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[  -1.       ,   -7.697415 ,  -95.39484  ],
       [  -1.3068528,  -17.004269 , -194.70169  ]], dtype=float32)>
poisson_2_by_3.log_prob([1.])  # Exactly equivalent to above, demonstrating the scalar special case.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[  -1.       ,   -7.697415 ,  -95.39484  ],
       [  -1.3068528,  -17.004269 , -194.70169  ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 1., 1.], [1., 1., 1.]])  # Another way to write the same thing. No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[  -1.       ,   -7.697415 ,  -95.39484  ],
       [  -1.3068528,  -17.004269 , -194.70169  ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 10., 100.]])  # Input is [1, 3] broadcast to [2, 3].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[ -1.       ,  -2.0785608,  -3.2223587],
       [ -1.3068528,  -5.14709  , -33.90767  ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 10., 100.], [1., 10., 100.]])  # Equivalent to above. No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[ -1.       ,  -2.0785608,  -3.2223587],
       [ -1.3068528,  -5.14709  , -33.90767  ]], dtype=float32)>
poisson_2_by_3.log_prob([[1., 1., 1.], [2., 2., 2.]])  # No broadcasting.
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[  -1.       ,   -7.697415 ,  -95.39484  ],
       [  -1.3068528,  -14.701683 , -190.09653  ]], dtype=float32)>
poisson_2_by_3.log_prob([[1.], [2.]])  # Equivalent to above. Input shape [2, 1] broadcast to [2, 3].
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[  -1.       ,   -7.697415 ,  -95.39484  ],
       [  -1.3068528,  -14.701683 , -190.09653  ]], dtype=float32)>

Các ví dụ trên liên quan đến việc phát qua lô, nhưng hình dạng mẫu trống. Giả sử chúng ta có một tập hợp các giá trị và chúng ta muốn lấy xác suất nhật ký của từng giá trị tại mỗi điểm trong lô. Chúng tôi có thể làm điều đó theo cách thủ công:

poisson_2_by_3.log_prob([[[1., 1., 1.], [1., 1., 1.]], [[2., 2., 2.], [2., 2., 2.]]])  # Input shape [2, 2, 3].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy=
array([[[  -1.       ,   -7.697415 ,  -95.39484  ],
        [  -1.3068528,  -17.004269 , -194.70169  ]],

       [[  -1.6931472,   -6.087977 ,  -91.48282  ],
        [  -1.3068528,  -14.701683 , -190.09653  ]]], dtype=float32)>

Hoặc chúng tôi có thể để phát sóng xử lý thứ nguyên lô cuối cùng:

poisson_2_by_3.log_prob([[[1.], [1.]], [[2.], [2.]]])  # Input shape [2, 2, 1].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy=
array([[[  -1.       ,   -7.697415 ,  -95.39484  ],
        [  -1.3068528,  -17.004269 , -194.70169  ]],

       [[  -1.6931472,   -6.087977 ,  -91.48282  ],
        [  -1.3068528,  -14.701683 , -190.09653  ]]], dtype=float32)>

Chúng tôi cũng có thể (có lẽ hơi ít tự nhiên hơn) để việc phát sóng chỉ xử lý thứ nguyên lô đầu tiên:

poisson_2_by_3.log_prob([[[1., 1., 1.]], [[2., 2., 2.]]])  # Input shape [2, 1, 3].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy=
array([[[  -1.       ,   -7.697415 ,  -95.39484  ],
        [  -1.3068528,  -17.004269 , -194.70169  ]],

       [[  -1.6931472,   -6.087977 ,  -91.48282  ],
        [  -1.3068528,  -14.701683 , -190.09653  ]]], dtype=float32)>

Hoặc chúng ta có thể cho phép phát sóng xử lý cả hàng loạt kích thước:

poisson_2_by_3.log_prob([[[1.]], [[2.]]])  # Input shape [2, 1, 1].
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy=
array([[[  -1.       ,   -7.697415 ,  -95.39484  ],
        [  -1.3068528,  -17.004269 , -194.70169  ]],

       [[  -1.6931472,   -6.087977 ,  -91.48282  ],
        [  -1.3068528,  -14.701683 , -190.09653  ]]], dtype=float32)>

Ở trên hoạt động tốt khi chúng tôi chỉ có hai giá trị mà chúng tôi muốn, nhưng giả sử chúng tôi có một danh sách dài các giá trị mà chúng tôi muốn đánh giá tại mọi điểm lô. Vì vậy, ký hiệu sau, bổ sung thêm kích thước của kích thước 1 vào phía bên phải của hình dạng, cực kỳ hữu ích:

poisson_2_by_3.log_prob(tf.constant([1., 2.])[..., tf.newaxis, tf.newaxis])
<tf.Tensor: shape=(2, 2, 3), dtype=float32, numpy=
array([[[  -1.       ,   -7.697415 ,  -95.39484  ],
        [  -1.3068528,  -17.004269 , -194.70169  ]],

       [[  -1.6931472,   -6.087977 ,  -91.48282  ],
        [  -1.3068528,  -14.701683 , -190.09653  ]]], dtype=float32)>

Đây là một thể hiện của ký hiệu lát strided , đó là giá trị biết.

Trở lại với three_poissons cho đầy đủ, ví dụ vẻ giống như:

three_poissons.log_prob([[1.], [10.], [50.], [100.]])
<tf.Tensor: shape=(4, 3), dtype=float32, numpy=
array([[  -1.       ,   -7.697415 ,  -95.39484  ],
       [ -16.104412 ,   -2.0785608,  -69.05272  ],
       [-149.47777  ,  -43.34851  ,  -18.219261 ],
       [-364.73938  , -143.48087  ,   -3.2223587]], dtype=float32)>
three_poissons.log_prob(tf.constant([1., 10., 50., 100.])[..., tf.newaxis])  # Equivalent to above.
<tf.Tensor: shape=(4, 3), dtype=float32, numpy=
array([[  -1.       ,   -7.697415 ,  -95.39484  ],
       [ -16.104412 ,   -2.0785608,  -69.05272  ],
       [-149.47777  ,  -43.34851  ,  -18.219261 ],
       [-364.73938  , -143.48087  ,   -3.2223587]], dtype=float32)>

Phân phối đa biến

Bây giờ chúng ta chuyển sang phân phối đa biến, có dạng sự kiện không trống. Hãy xem xét các phân phối đa thức.

multinomial_distributions = [
    # Multinomial is a vector-valued distribution: if we have k classes,
    # an individual sample from the distribution has k values in it, so the
    # event_shape is `[k]`.
    tfd.Multinomial(total_count=100., probs=[.5, .4, .1],
                    name='One Multinomial'),
    tfd.Multinomial(total_count=[100., 1000.], probs=[.5, .4, .1],
                    name='Two Multinomials Same Probs'),
    tfd.Multinomial(total_count=100., probs=[[.5, .4, .1], [.1, .2, .7]],
                    name='Two Multinomials Same Counts'),
    tfd.Multinomial(total_count=[100., 1000.],
                    probs=[[.5, .4, .1], [.1, .2, .7]],
                    name='Two Multinomials Different Everything')

]

describe_distributions(multinomial_distributions)
tfp.distributions.Multinomial("One_Multinomial", batch_shape=[], event_shape=[3], dtype=float32)
tfp.distributions.Multinomial("Two_Multinomials_Same_Probs", batch_shape=[2], event_shape=[3], dtype=float32)
tfp.distributions.Multinomial("Two_Multinomials_Same_Counts", batch_shape=[2], event_shape=[3], dtype=float32)
tfp.distributions.Multinomial("Two_Multinomials_Different_Everything", batch_shape=[2], event_shape=[3], dtype=float32)

Lưu ý cách trong ba ví dụ cuối cùng, các batch_shape luôn là [2] , nhưng chúng ta có thể sử dụng phát sóng hoặc là có một chia sẻ total_count hoặc chia sẻ probs (hoặc không), bởi vì dưới mui xe họ đang phát sóng có hình dạng tương tự.

Việc lấy mẫu rất đơn giản, dựa trên những gì chúng ta đã biết:

describe_sample_tensor_shapes(multinomial_distributions, sample_shapes)
tfp.distributions.Multinomial("One_Multinomial", batch_shape=[], event_shape=[3], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 3)
Sample shape: 2
Returned sample tensor shape: (2, 3)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 3)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 3)

tfp.distributions.Multinomial("Two_Multinomials_Same_Probs", batch_shape=[2], event_shape=[3], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 2, 3)
Sample shape: 2
Returned sample tensor shape: (2, 2, 3)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 2, 3)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 2, 3)

tfp.distributions.Multinomial("Two_Multinomials_Same_Counts", batch_shape=[2], event_shape=[3], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 2, 3)
Sample shape: 2
Returned sample tensor shape: (2, 2, 3)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 2, 3)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 2, 3)

tfp.distributions.Multinomial("Two_Multinomials_Different_Everything", batch_shape=[2], event_shape=[3], dtype=float32)
Sample shape: 1
Returned sample tensor shape: (1, 2, 3)
Sample shape: 2
Returned sample tensor shape: (2, 2, 3)
Sample shape: [1, 5]
Returned sample tensor shape: (1, 5, 2, 3)
Sample shape: [3, 4, 5]
Returned sample tensor shape: (3, 4, 5, 2, 3)

Tính toán xác suất nhật ký cũng đơn giản như nhau. Hãy làm một ví dụ với phân phối Chuẩn đa biến theo đường chéo. (Đa thức không thân thiện lắm với quá trình phát sóng, vì những ràng buộc về số lượng và xác suất có nghĩa là việc phát sóng thường tạo ra các giá trị không thể chấp nhận được.) Chúng tôi sẽ sử dụng một loạt 2 phân phối 3 chiều có cùng thang đo trung bình nhưng khác nhau (độ lệch chuẩn):

two_multivariate_normals = tfd.MultivariateNormalDiag(loc=[1., 2., 3.], scale_identity_multiplier=[1., 2.])
two_multivariate_normals
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[2] event_shape=[3] dtype=float32>

(Lưu ý rằng mặc dù chúng tôi sử dụng các bản phân phối nơi quy mô là bội số của bản sắc, đây không phải là một hạn chế về, chúng tôi có thể vượt qua scale thay vì scale_identity_multiplier .)

Bây giờ, hãy đánh giá xác suất nhật ký của mỗi điểm lô ở mức trung bình của nó và ở mức trung bình được dịch chuyển:

two_multivariate_normals.log_prob([[[1., 2., 3.]], [[3., 4., 5.]]])  # Input has shape [2,1,3].
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[-2.7568154, -4.836257 ],
       [-8.756816 , -6.336257 ]], dtype=float32)>

Chính xác tương đương, chúng ta có thể sử dụng https://www.tensorflow.org/api_docs/cc/class/tensorflow/ops/strided-slice để chèn thêm một hình dạng = 1 chiều ở giữa một hằng số:

two_multivariate_normals.log_prob(
    tf.constant([[1., 2., 3.], [3., 4., 5.]])[:, tf.newaxis, :])  # Equivalent to above.
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[-2.7568154, -4.836257 ],
       [-8.756816 , -6.336257 ]], dtype=float32)>

Mặt khác, nếu chúng ta không chèn kích thước thêm, chúng tôi vượt qua [1., 2., 3.] đến điểm lô hàng đầu tiên và [3., 4., 5.] đến thứ hai:

two_multivariate_normals.log_prob(tf.constant([[1., 2., 3.], [3., 4., 5.]]))
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-2.7568154, -6.336257 ], dtype=float32)>

Kỹ thuật chỉnh sửa hình dạng

Bộ mô phỏng định hình lại

Các Reshape bijector có thể được sử dụng để định hình lại event_shape của một phân phối. Hãy xem một ví dụ:

six_way_multinomial = tfd.Multinomial(total_count=1000., probs=[.3, .25, .2, .15, .08, .02])
six_way_multinomial
<tfp.distributions.Multinomial 'Multinomial' batch_shape=[] event_shape=[6] dtype=float32>

Chúng tôi tạo ra một đa thức với một hình dạng trường hợp [6] . Các Reshape Bijector cho phép chúng ta điều này như là một bản phân phối với một hình dạng trường hợp [2, 3] .

Một Bijector đại diện cho một khả vi, one-to-one chức năng trên một tập con mở của \({\mathbb R}^n\). Bijectors được sử dụng kết hợp với TransformedDistribution , mà mô hình phân phối \(p(y)\) về sự phân bố cơ sở \(p(x)\) và một Bijector đại diện \(Y = g(X)\). Hãy xem nó trong hành động:

transformed_multinomial = tfd.TransformedDistribution(
    distribution=six_way_multinomial,
    bijector=tfb.Reshape(event_shape_out=[2, 3]))
transformed_multinomial
<tfp.distributions.TransformedDistribution 'reshapeMultinomial' batch_shape=[] event_shape=[2, 3] dtype=float32>
six_way_multinomial.log_prob([500., 100., 100., 150., 100., 50.])
<tf.Tensor: shape=(), dtype=float32, numpy=-178.22021>
transformed_multinomial.log_prob([[500., 100., 100.], [150., 100., 50.]])
<tf.Tensor: shape=(), dtype=float32, numpy=-178.22021>

Đây là điều duy nhất các Reshape bijector có thể làm: nó không thể bật khía cạnh sự kiện vào kích thước hàng loạt hoặc ngược lại.

Phân phối độc lập

Các Independent phân phối được sử dụng để điều trị một bộ sưu tập độc lập, không-hẳn-giống hệt nhau (hay còn gọi là một lô) phân phối là một bản phân phối duy nhất. Chính xác hơn, Independent cho phép chuyển đổi kích thước trong batch_shape để kích thước trong event_shape . Chúng tôi sẽ minh họa bằng ví dụ:

two_by_five_bernoulli = tfd.Bernoulli(
    probs=[[.05, .1, .15, .2, .25], [.3, .35, .4, .45, .5]],
    name="Two By Five Bernoulli")
two_by_five_bernoulli
<tfp.distributions.Bernoulli 'Two_By_Five_Bernoulli' batch_shape=[2, 5] event_shape=[] dtype=int32>

Chúng ta có thể coi đây là một mảng hai phần năm các đồng tiền với các xác suất liên quan đến người đứng đầu. Hãy đánh giá xác suất của một tập hợp các số một và số không cụ thể, tùy ý:

pattern = [[1., 0., 0., 1., 0.], [0., 0., 1., 1., 1.]]
two_by_five_bernoulli.log_prob(pattern)
<tf.Tensor: shape=(2, 5), dtype=float32, numpy=
array([[-2.9957323 , -0.10536052, -0.16251892, -1.609438  , -0.2876821 ],
       [-0.35667497, -0.4307829 , -0.9162907 , -0.7985077 , -0.6931472 ]],
      dtype=float32)>

Chúng ta có thể sử dụng Independent để tắt chức năng này thành hai "bộ lăm Bernoulli" khác nhau, đó là hữu ích nếu chúng ta muốn xem xét một "hàng" của đồng xu flips sắp lên trong một mô hình được coi là một kết quả duy nhất:

two_sets_of_five = tfd.Independent(
    distribution=two_by_five_bernoulli,
    reinterpreted_batch_ndims=1,
    name="Two Sets Of Five")
two_sets_of_five
<tfp.distributions.Independent 'Two_Sets_Of_Five' batch_shape=[2] event_shape=[5] dtype=int32>

Về mặt toán học, chúng tôi đang tính toán xác suất nhật ký của mỗi "tập hợp" năm bằng cách tính tổng xác suất nhật ký của năm lần lật đồng xu "độc lập" trong tập hợp, đó là nơi mà phân phối có tên:

two_sets_of_five.log_prob(pattern)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-5.160732 , -3.1954036], dtype=float32)>

Chúng ta có thể đi xa hơn nữa và sử dụng Independent để tạo ra một bản phân phối nơi sự kiện cá nhân là một tập hợp của hai bởi lăm Bernoulli:

one_set_of_two_by_five = tfd.Independent(
    distribution=two_by_five_bernoulli, reinterpreted_batch_ndims=2,
    name="One Set Of Two By Five")
one_set_of_two_by_five.log_prob(pattern)
<tf.Tensor: shape=(), dtype=float32, numpy=-8.356134>

Nó đáng chú ý là từ quan điểm của sample , sử dụng Independent có gì thay đổi:

describe_sample_tensor_shapes(
    [two_by_five_bernoulli,
     two_sets_of_five,
     one_set_of_two_by_five],
    [[3, 5]])
tfp.distributions.Bernoulli("Two_By_Five_Bernoulli", batch_shape=[2, 5], event_shape=[], dtype=int32)
Sample shape: [3, 5]
Returned sample tensor shape: (3, 5, 2, 5)

tfp.distributions.Independent("Two_Sets_Of_Five", batch_shape=[2], event_shape=[5], dtype=int32)
Sample shape: [3, 5]
Returned sample tensor shape: (3, 5, 2, 5)

tfp.distributions.Independent("One_Set_Of_Two_By_Five", batch_shape=[], event_shape=[2, 5], dtype=int32)
Sample shape: [3, 5]
Returned sample tensor shape: (3, 5, 2, 5)

Như một bài tập chia tay cho người đọc, chúng tôi khuyên xem xét sự khác biệt và tương đồng giữa một lô vector của Normal phân phối và một MultivariateNormalDiag phân phối từ góc độ khả năng lấy mẫu và đăng nhập. Làm sao chúng ta có thể sử dụng Independent để xây dựng một MultivariateNormalDiag từ một lô Normal không? (Lưu ý rằng MultivariateNormalDiag không thực sự thực hiện theo cách này.)