TensorFlow Dağıtımları: Nazik Bir Giriş

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyin Not defterini indir

Bu not defterinde TensorFlow Dağıtımlarını (kısaca TFD) keşfedeceğiz. Bu not defterinin amacı, TFD'nin tensör şekillerini ele alışını anlamak da dahil olmak üzere, öğrenme eğrisini yavaşça yukarıya çıkarmaktır. Bu defter, soyut kavramlardan ziyade örnekleri önceden sunmaya çalışır. İlk önce işleri yapmanın kanonik kolay yollarını sunacağız ve en genel soyut görünümü sona saklayacağız. Eğer daha soyut ve referans tarzı öğretici tercih türünü iseniz, kontrol TensorFlow Dağılımları Şekilleri anlama . Burada materyalin hakkında herhangi bir sorunuz varsa, temas tereddüt (veya katılmak) yok TensorFlow Olasılık posta listesi . Yardım etmekten mutluluk duyarız.

Başlamadan önce uygun kütüphaneleri içe aktarmamız gerekiyor. Bizim genel kütüphane tensorflow_probability . Geleneksel olarak, biz genelde dağılımları kütüphanesine bakın tfd .

Tensorflow Hevesli TensorFlow için bir zorunluluk yürütme ortamıdır. TensorFlow istekli'de her TF işlemi anında değerlendirilir ve bir sonuç üretir. Bu, TensorFlow'un TF işlemlerinin daha sonra yürütülecek bir grafiğe düğümler eklediği standart "grafik" modunun aksinedir. Bu not defterinin tamamı TF Eager kullanılarak yazılmıştır, ancak burada sunulan kavramların hiçbiri buna dayanmaz ve TFP grafik modunda kullanılabilir.

import collections

import tensorflow as tf
import tensorflow_probability as tfp
tfd = tfp.distributions

try:
  tf.compat.v1.enable_eager_execution()
except ValueError:
  pass

import matplotlib.pyplot as plt

Temel Tek Değişkenli Dağılımlar

Hemen konuya girelim ve normal bir dağılım oluşturalım:

n = tfd.Normal(loc=0., scale=1.)
n
<tfp.distributions.Normal 'Normal' batch_shape=[] event_shape=[] dtype=float32>

Ondan bir örnek alabiliriz:

n.sample()
<tf.Tensor: shape=(), dtype=float32, numpy=0.25322816>

Birden fazla örnek çizebiliriz:

n.sample(3)
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-1.4658079, -0.5653636,  0.9314412], dtype=float32)>

Bir günlük probunu değerlendirebiliriz:

n.log_prob(0.)
<tf.Tensor: shape=(), dtype=float32, numpy=-0.9189385>

Birden çok günlük olasılığını değerlendirebiliriz:

n.log_prob([0., 2., 4.])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-0.9189385, -2.9189386, -8.918939 ], dtype=float32)>

Geniş bir dağıtım yelpazemiz var. Bir Bernoulli deneyelim:

b = tfd.Bernoulli(probs=0.7)
b
<tfp.distributions.Bernoulli 'Bernoulli' batch_shape=[] event_shape=[] dtype=int32>
b.sample()
<tf.Tensor: shape=(), dtype=int32, numpy=1>
b.sample(8)
<tf.Tensor: shape=(8,), dtype=int32, numpy=array([1, 0, 0, 0, 1, 0, 1, 0], dtype=int32)>
b.log_prob(1)
<tf.Tensor: shape=(), dtype=float32, numpy=-0.35667497>
b.log_prob([1, 0, 1, 0])
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([-0.35667497, -1.2039728 , -0.35667497, -1.2039728 ], dtype=float32)>

Çok Değişkenli Dağılımlar

Çapraz kovaryanslı çok değişkenli bir normal oluşturacağız:

nd = tfd.MultivariateNormalDiag(loc=[0., 10.], scale_diag=[1., 4.])
nd
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[] event_shape=[2] dtype=float32>

Bunu daha önce yarattığımız tek değişkenli normalle karşılaştırırsak, farklı olan ne?

tfd.Normal(loc=0., scale=1.)
<tfp.distributions.Normal 'Normal' batch_shape=[] event_shape=[] dtype=float32>

Biz tek değişkenli Normal bir olduğunu görüyoruz event_shape ait () bu bir sayıl dağıtım olduğunu belirtmek. Çok değişkenli normal bir sahiptir event_shape arasında 2 bu dağılımın temel [etkinlik alanı] (https://en.wikipedia.org/wiki/Event_ (probability_theory)) gösteren, iki boyutludur.

Örnekleme daha önce olduğu gibi çalışır:

nd.sample()
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-1.2489667, 15.025171 ], dtype=float32)>
nd.sample(5)
<tf.Tensor: shape=(5, 2), dtype=float32, numpy=
array([[-1.5439653 ,  8.9968405 ],
       [-0.38730723, 12.448896  ],
       [-0.8697963 ,  9.330035  ],
       [-1.2541095 , 10.268944  ],
       [ 2.3475595 , 13.184147  ]], dtype=float32)>
nd.log_prob([0., 10])
<tf.Tensor: shape=(), dtype=float32, numpy=-3.2241714>

Çok değişkenli normaller genel olarak köşegen kovaryansa sahip değildir. TFD, burada kullandığımız tam kovaryans belirtimi dahil olmak üzere çok değişkenli normaller oluşturmak için birden çok yol sunar.

nd = tfd.MultivariateNormalFullCovariance(
    loc = [0., 5], covariance_matrix = [[1., .7], [.7, 1.]])
data = nd.sample(200)
plt.scatter(data[:, 0], data[:, 1], color='blue', alpha=0.4)
plt.axis([-5, 5, 0, 10])
plt.title("Data set")
plt.show()

png

Çoklu Dağıtımlar

İlk Bernoulli dağıtımımız tek bir adil yazı turasını temsil ediyordu. Ayrıca bağımsız Bernoulli dağıtımlarının toplu oluşturabilir, kendi parametreleri ile her biri tek içinde Distribution nesne:

b3 = tfd.Bernoulli(probs=[.3, .5, .7])
b3
<tfp.distributions.Bernoulli 'Bernoulli' batch_shape=[3] event_shape=[] dtype=int32>

Bunun ne anlama geldiği konusunda net olmak önemlidir. Yukarıdaki çağrı aynı Python içerdiği için ne üç bağımsız Bernoulli dağılımları, tanımlar Distribution nesne. Üç dağıtım ayrı ayrı manipüle edilemez. Not nasıl batch_shape olduğu (3,) üç dağılımları bir toplu gösteren ve event_shape olan () tek tek dağılımlar tek değişkenli etkinlik alanı vardır gösteren.

Dediğimiz Eğer sample , hepimiz üçten örnek alın:

b3.sample()
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([0, 1, 1], dtype=int32)>
b3.sample(6)
<tf.Tensor: shape=(6, 3), dtype=int32, numpy=
array([[1, 0, 1],
       [0, 1, 1],
       [0, 0, 1],
       [0, 0, 1],
       [0, 0, 1],
       [0, 1, 0]], dtype=int32)>

Diyoruz halinde prob , (bu da aynı şekil semantik sahip log_prob ; Kullandığımız prob , ancak açıklık için, bu küçük Bernoulli örneklerle log_prob genellikle uygulamalarda tercih edilir) bir vektör olarak geçebilir ve bu değer elde edilmiştir, her para olasılığını değerlendirmek :

b3.prob([1, 1, 0])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([0.29999998, 0.5       , 0.29999998], dtype=float32)>

API neden toplu şekil içeriyor? Semantik, tek dağılımları bir listesini oluşturarak ve onlara yineleme ile aynı hesaplamaları gerçekleştirebilir for (bir ihtiyacım olacağını TF grafik modunda, istekli modunda en azından döngü tf.while döngü). Bununla birlikte, (potansiyel olarak büyük) bir dizi özdeş parametreli dağılıma sahip olmak son derece yaygındır ve mümkün olduğunda vektörleştirilmiş hesaplamaların kullanılması, donanım hızlandırıcıları kullanarak hızlı hesaplamalar yapabilmek için önemli bir bileşendir.

Grupları Olaylarda Birleştirmek İçin Bağımsız Kullanma

Bir önceki bölümde, oluşturulan b3 , tek Distribution üç sikke çevirir temsil nesnesi. Biz denilen Eğer b3.prob bir vektör $ v $ üzerinde, $ i $ 'inci girdi i inci sikke, $ $ olasılığı değeri $ v [i] $ alır oldu.

Bunun yerine aynı temel aileden bağımsız rastgele değişkenler üzerinde bir "ortak" dağılım belirtmek istediğimizi varsayalım. Bunun anlamı, bu yeni dağıtım için, içinde, matematiksel olarak farklı bir nesnedir prob bir vektör $ v $ üzerinde sikke bütün dizisinin vektör $ v $ maçları olasılığını temsil eden tek bir değer döndürecektir.

Bunu nasıl tamamlayacağız? Biz denilen bir "Üst düzey" dağıtım kullanmak Independent bir dağılım alır ve olay şekle taşındı toplu şekli ile yeni bir dağıtım verir:

b3_joint = tfd.Independent(b3, reinterpreted_batch_ndims=1)
b3_joint
<tfp.distributions.Independent 'IndependentBernoulli' batch_shape=[] event_shape=[3] dtype=int32>

Orijinal o şekil karşılaştırın b3 :

b3
<tfp.distributions.Bernoulli 'Bernoulli' batch_shape=[3] event_shape=[] dtype=int32>

Söz olarak, bu görüyoruz Independent : Olay şeklinde toplu şekil taşındı b3_joint tek dağılımı (olup batch_shape = () , üç boyutlu bir etkinlik alanı (aşırı) event_shape = (3,) ).

Semantiği kontrol edelim:

b3_joint.prob([1, 1, 0])
<tf.Tensor: shape=(), dtype=float32, numpy=0.044999998>

Aynı sonucu elde etmek için alternatif bir yolu kullanarak hesaplama olasılıkları olacaktır b3 ve (toplayarak, günlük olasılıkları kullanılır daha olağan durumda ya) çarpılarak azalma elle yapın:

tf.reduce_prod(b3.prob([1, 1, 0]))
<tf.Tensor: shape=(), dtype=float32, numpy=0.044999994>

Indpendent kullanıcı daha açık istenilen kavramını temsil edilmesini sağlar. Bunu kesinlikle gerekli olmasa da son derece yararlı buluyoruz.

Eğlenceli gerçekler:

  • b3.sample ve b3_joint.sample farklı kavramsal uygulamaları, ama ayırt çıkışları vardır: bağımsız dağılımlarının bir toplu kullanılarak yığın oluşturuldu tek bir dağılım arasındaki fark Independent numune alırken, ihtimalleri işlem zaman değil gösterir kadar.
  • MultivariateNormalDiag trivially sayıl kullanılarak uygulanabilecek Normal ve Independent (aslında bu şekilde uygulanmadı, ancak bu olabilir) dağılımları.

Çok Değişkenli Dağılım Partileri

Üç tam kovaryanslı iki boyutlu çok değişkenli normalden oluşan bir toplu iş oluşturalım:

nd_batch = tfd.MultivariateNormalFullCovariance(
    loc = [[0., 0.], [1., 1.], [2., 2.]],
    covariance_matrix = [[[1., .1], [.1, 1.]], 
                         [[1., .3], [.3, 1.]],
                         [[1., .5], [.5, 1.]]])
nd_batch
<tfp.distributions.MultivariateNormalFullCovariance 'MultivariateNormalFullCovariance' batch_shape=[3] event_shape=[2] dtype=float32>

Görüyoruz batch_shape = (3,) ve, bu nedenle üç bağımsız değişkenli normalleri vardır event_shape = (2,) normal her değişkenli böylece iki boyutludur. Bu örnekte, bireysel dağılımların bağımsız öğeleri yoktur.

Örnekleme çalışmaları:

nd_batch.sample(4)
<tf.Tensor: shape=(4, 3, 2), dtype=float32, numpy=
array([[[ 0.7367498 ,  2.730996  ],
        [-0.74080074, -0.36466932],
        [ 0.6516018 ,  0.9391426 ]],

       [[ 1.038303  ,  0.12231752],
        [-0.94788766, -1.204232  ],
        [ 4.059758  ,  3.035752  ]],

       [[ 0.56903946, -0.06875849],
        [-0.35127294,  0.5311631 ],
        [ 3.4635801 ,  4.565582  ]],

       [[-0.15989424, -0.25715637],
        [ 0.87479895,  0.97391707],
        [ 0.5211419 ,  2.32108   ]]], dtype=float32)>

Yana batch_shape = (3,) ve event_shape = (2,) , şekli bir tensörünün geçmesi (3, 2) için log_prob :

nd_batch.log_prob([[0., 0.], [1., 1.], [2., 2.]])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-1.8328519, -1.7907217, -1.694036 ], dtype=float32)>

Yayıncılık, aka Bu Neden Bu Kadar Kafa karıştırıcı?

Şimdiye kadar ne yaptık dışarı Öz Çıkarma, her dağıtım bir toplu şekli vardır B ve bir olay şekil E . Izin BE olay şekillerinin birleştirme be:

  • Tek değişkenli skalar dağılımlar için n ve b , BE = (). .
  • İki boyutlu değişkenli normaller için nd . BE = (2).
  • Her ikisi için de b3 ve b3_joint , BE = (3).
  • Çok değişkenli normaller parti için ndb , BE = (3, 2).

Şimdiye kadar kullandığımız "değerlendirme kuralları" şunlardır:

  • Herhangi bir bağımsız değişken ile örnek şekil ile tensörünün döner BE ; bir sayısal n getirileri bir "n ile birlikte numune BE " tensörü.
  • prob ve log_prob şekildeki bir tensör almak BE ve şekli bir sonuca dönmek B .

Gerçek "değerlendirme kuralı" prob ve log_prob daha bir bakıma, karmaşık teklifler potansiyel güç ve hız değil, aynı zamanda karmaşıklığı ve zorluklara söyledi. Gerçek kural bağımsız değişken olduğu (aslında) olan log_prob olmalıdır broadcastable karşı BE ; çıktıda herhangi bir "ekstra" boyut korunur.

Etkilerini inceleyelim. Tek değişkenli Normal için n , BE = () , böylece log_prob bir skalar bekler. Biz başarılı olursa log_prob boş olmayan bir şekle sahip bir tensörünün bu çıkış parti boyutları olarak gösterilir:

n = tfd.Normal(loc=0., scale=1.)
n
<tfp.distributions.Normal 'Normal' batch_shape=[] event_shape=[] dtype=float32>
n.log_prob(0.)
<tf.Tensor: shape=(), dtype=float32, numpy=-0.9189385>
n.log_prob([0.])
<tf.Tensor: shape=(1,), dtype=float32, numpy=array([-0.9189385], dtype=float32)>
n.log_prob([[0., 1.], [-1., 2.]])
<tf.Tensor: shape=(2, 2), dtype=float32, numpy=
array([[-0.9189385, -1.4189385],
       [-1.4189385, -2.9189386]], dtype=float32)>

İki boyutlu değişkenli normale edelim dönüş nd (parametreler gösterim amaçlı değişti):

nd = tfd.MultivariateNormalDiag(loc=[0., 1.], scale_diag=[1., 1.])
nd
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[] event_shape=[2] dtype=float32>

log_prob "beklediğini" şeklinde bir argüman (2,) , ancak herhangi bir argüman kabul edeceğini bu şekle karşı yayın:

nd.log_prob([0., 0.])
<tf.Tensor: shape=(), dtype=float32, numpy=-2.337877>

Ama biz "daha" örneklerde geçmesi ve tüm bunların değerlendirebilir log_prob seferde s':

nd.log_prob([[0., 0.],
             [1., 1.],
             [2., 2.]])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-2.337877 , -2.337877 , -4.3378773], dtype=float32)>

Belki daha az çekici bir şekilde, olay boyutları üzerinden yayın yapabiliriz:

nd.log_prob([0.])
<tf.Tensor: shape=(), dtype=float32, numpy=-2.337877>
nd.log_prob([[0.], [1.], [2.]])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-2.337877 , -2.337877 , -4.3378773], dtype=float32)>

Bu şekilde yayın yapmak, "mümkün olduğunda yayına izin ver" tasarımımızın bir sonucudur; bu kullanım biraz tartışmalıdır ve TFP'nin gelecekteki bir sürümünde potansiyel olarak kaldırılabilir.

Şimdi üç jeton örneğine tekrar bakalım:

b3 = tfd.Bernoulli(probs=[.3, .5, .7])

Burada, her sikke kafalarını çıkageldi olasılığını temsil etmek yayın kullanarak oldukça sezgisel:

b3.prob([1])
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([0.29999998, 0.5       , 0.7       ], dtype=float32)>

(Bu Karşılaştırması b3.prob([1., 1., 1.]) , biz kullanılan geri olurdu, b3 tanıtıldı.)

Şimdi bilmek istiyorum varsayalım, her sikke için, olasılık sikke başlarını gelir ve olasılık o kuyrukları çıkageldi. Denemeyi hayal edebiliriz:

b3.log_prob([0, 1])

Ne yazık ki, bu, uzun ve çok okunamayan bir yığın izlemesi olan bir hata üretir. b3 sahiptir BE = (3) biz geçmelidir böylece, b3.prob karşı bir şey broadcastable (3,) . [0, 1] bir şekle sahiptir (2) bu nedenle yayın etmez, ve hataya yol açabilir. Bunun yerine şunu söylemeliyiz:

b3.prob([[0], [1]])
<tf.Tensor: shape=(2, 3), dtype=float32, numpy=
array([[0.7, 0.5, 0.3],
       [0.3, 0.5, 0.7]], dtype=float32)>

Niye ya? [[0], [1]] şekle sahiptir (2, 1) bu şekil karşı yayın böylece, (3) bir yayın şekil yapmak için (2, 3) .

Yayın oldukça güçlüdür: Kullanılan bellek miktarında büyüklük sırasına göre azalmaya izin verdiği ve genellikle kullanıcı kodunu kısalttığı durumlar vardır. Bununla birlikte, programlamak zor olabilir. Eğer ararsanız log_prob ve bir hata alıyorum, yayına bir başarısızlık neredeyse her zaman bir sorundur.

daha uzağa gitmek

Bu eğitimde (umarız) basit bir giriş sağladık. Daha ileri gitmek için birkaç işaret:

  • event_shape , batch_shape ve sample_shape keyfi rütbe olabilir (bu eğitimde onlar da skaler veya rütbe 1 her zaman). Bu, gücü arttırır ancak yine de özellikle yayın söz konusu olduğunda programlama zorluklarına yol açabilir. Şekil manipülasyon içine ek bir derin dalış için bkz anlama TensorFlow Dağılımları Şekilleri .
  • TFP olarak bilinen güçlü soyutlama içerir Bijectors ile bağlantılı olarak, TransformedDistribution , kolayca mevcut dağılımlarının tersinir dönüşümler yeni dağılımları oluşturmak için esnek, kompozisyon bir şekilde elde edilir. Bu yakında bir öğretici yazmaya çalışıyorum, ama bu arada, kontrol edeceğiz belgeleri