TensorFlow वितरण: एक कोमल परिचय

TensorFlow.org पर देखें Google Colab में चलाएं GitHub पर स्रोत देखें नोटबुक डाउनलोड करें

इस नोटबुक में, हम TensorFlow वितरण (संक्षेप में TFD) का पता लगाएंगे। इस नोटबुक का लक्ष्य आपको सीखने की अवस्था को धीरे से प्राप्त करना है, जिसमें टीएफडी को टेंसर आकृतियों से निपटने की समझ शामिल है। यह नोटबुक अमूर्त अवधारणाओं के बजाय उदाहरण प्रस्तुत करने का प्रयास करती है। हम पहले चीजों को करने के लिए विहित आसान तरीके पेश करेंगे, और अंत तक सबसे सामान्य सार दृश्य को बचाएंगे। यदि आप वह प्रकार हैं जो अधिक सार और संदर्भ-शैली ट्यूटोरियल पसंद करते हैं, तो TensorFlow वितरण आकार को समझें । यदि आपके पास यहां सामग्री के बारे में कोई प्रश्न है, तो TensorFlow प्रायिकता मेलिंग सूची से संपर्क करने (या जुड़ने) में संकोच न करें। हम मदद करने के लिए खुश हैं।

शुरू करने से पहले, हमें उचित पुस्तकालयों को आयात करने की आवश्यकता है। हमारी समग्र लाइब्रेरी tensorflow_probability । सम्मेलन द्वारा, हम आम तौर पर वितरण लाइब्रेरी को tfd रूप में tfd

Tensorflow Eager TensorFlow के लिए एक अनिवार्य निष्पादन वातावरण है। TensorFlow उत्सुक में, प्रत्येक TF ऑपरेशन का तुरंत मूल्यांकन किया जाता है और एक परिणाम उत्पन्न होता है। यह TensorFlow के मानक "ग्राफ" मोड के विपरीत है, जिसमें TF ऑपरेशन एक ग्राफ में नोड जोड़ते हैं जिसे बाद में निष्पादित किया जाता है। यह पूरी नोटबुक TF ईगर का उपयोग करके लिखी गई है, हालांकि यहां प्रस्तुत कोई भी अवधारणा उस पर निर्भर नहीं है, और टीएफपी को ग्राफ मोड में उपयोग किया जा सकता है।

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

मूल अविभाज्य वितरण

चलो सही में गोता लगाएँ और एक सामान्य वितरण बनाएँ:

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

हम इसका एक नमूना बना सकते हैं:

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

हम कई नमूने आकर्षित कर सकते हैं:

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

हम एक लॉग प्रो का मूल्यांकन कर सकते हैं:

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

हम कई लॉग संभावनाओं का मूल्यांकन कर सकते हैं:

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

हमारे पास वितरण की एक विस्तृत श्रृंखला है। आइए एक बर्नोली की कोशिश करते हैं:

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>
06638406a0
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([-0.35667497, -1.2039728 , -0.35667497, -1.2039728 ], dtype=float32)>

बहुभिन्नरूपी वितरण

हम एक विकर्ण सहसंयोजक के साथ एक बहुभिन्नरूपी सामान्य बनाएँगे:

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

इसकी तुलना हम पहले बनाए गए सामान्य इंसान से करते हैं, क्या अलग है?

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

हम देखते हैं कि event_shape सामान्य में एक event_shape है () , यह दर्शाता है कि यह एक अदिश वितरण है। बहुभिन्नरूपी सामान्य में 2 का एक event_shape है, जो इस वितरण के मूल [घटना स्थान] (https://en.wikipedia.org/wiki/Event_ (प्रायिकता_थोरी)) को दो-आयामी है।

नमूना पहले की तरह काम करता है:

nd.sample()
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-1.2489667, 15.025171 ], dtype=float32)>
nd.sample(5)
0ee2ff710
nd.log_prob([0., 10])
<tf.Tensor: shape=(), dtype=float32, numpy=-3.2241714>

बहुभिन्नरूपी मानदंडों में सामान्य रूप से विकर्ण सहसंयोजक नहीं होते हैं। TFD मल्टीवेरिएट मानदंड बनाने के लिए कई तरीके प्रदान करता है, जिसमें एक पूर्ण-सहसंयोजक विनिर्देश भी शामिल है, जिसका उपयोग हम यहां करते हैं।

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()

पींग

एकाधिक वितरण

हमारे पहले बर्नोली वितरण ने एक एकल उचित सिक्के के एक फ्लिप का प्रतिनिधित्व किया। हम स्वतंत्र बर्नौली वितरण का एक बैच भी बना सकते हैं, प्रत्येक अपने स्वयं के मापदंडों के साथ, एक ही Distribution वस्तु में:

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

इसका क्या मतलब है, यह स्पष्ट होना महत्वपूर्ण है। उपरोक्त कॉल तीन स्वतंत्र बर्नौली वितरण को परिभाषित करता है, जो एक ही पायथन Distribution वस्तु में समाहित होता है। तीन वितरणों को व्यक्तिगत रूप से जोड़-तोड़ नहीं किया जा सकता है। ध्यान दें कि कैसे batch_shape (3,) , तीन वितरणों के एक बैच को दर्शाता है, और event_shape है () , व्यक्तिगत वितरणों को इंगित करता है कि एक अलग घटना स्थान है।

अगर हम sample कहते हैं, हम तीनों से एक नमूना प्राप्त करते हैं:

b3.sample()
087bb830
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)>

अगर हम कहते हैं prob , (यह रूप में एक ही आकार अर्थ विज्ञान है log_prob , हम का उपयोग prob स्पष्टता के लिए इन छोटे Bernoulli उदाहरण के साथ है, हालांकि log_prob आमतौर पर अनुप्रयोगों में पसंद किया जाता है) हम इसे एक वेक्टर पारित कर सकते हैं और कहा कि मूल्य उपज प्रत्येक सिक्का की संभावना का मूल्यांकन :

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

API में बैच आकार क्यों शामिल है? शब्दार्थ एक वितरण की एक सूची बनाने और उन पर एक साथ पुनरावृत्ति द्वारा एक ही संगणना प्रदर्शन कर सकता है for (यदि आप एक आवश्यकता होगी उत्सुक मोड में कम से कम TF ग्राफ मोड में, पाश tf.while पाश)। हालाँकि, पहचान किए गए डिस्ट्रीब्यूटेड डिस्ट्रीब्यूशन का एक (संभावित बड़ा) सेट बेहद आम है, और जब भी संभव हो सदिश कंप्यूटर्स का उपयोग हार्डवेयर त्वरक का उपयोग करके तेजी से कम्प्यूटेशन करने में सक्षम होता है।

घटनाओं के लिए स्वतंत्र बैचों का उपयोग करना

पिछले अनुभाग में, हमने b3 बनाया, एक एकल Distribution ऑब्जेक्ट जो तीन सिक्का फ़्लिप का प्रतिनिधित्व करता था। अगर हम एक वेक्टर $ v $ पर b3.prob कहते हैं, तो $ i $ 'वें प्रविष्टि की संभावना थी कि $ i $ th सिक्का $ v [i] $ का मूल्य लेता है।

मान लीजिए कि हम एक ही अंतर्निहित परिवार से स्वतंत्र यादृच्छिक चर पर "संयुक्त" वितरण निर्दिष्ट करना चाहते हैं। यह गणितीय रूप से एक अलग वस्तु है, जिसमें इस नए वितरण के लिए, सदिश $ v $ पर prob एक एकल मान लौटाएगा जो इस संभावना का प्रतिनिधित्व करता है कि सिक्कों का पूरा सेट वेक्टर $ v $ से मेल खाता है।

हम इसे कैसे पूरा करेंगे? हम एक "उच्च-क्रम" वितरण का उपयोग करते हैं जिसे Independent कहा जाता है, जो वितरण लेता है और घटना के स्थान पर ले जाने वाले बैच आकार के साथ एक नया वितरण प्राप्त करता है:

0a3320980
<tfp.distributions.Independent 'IndependentBernoulli' batch_shape=[] event_shape=[3] dtype=int32>

मूल b3 के आकार की तुलना करें:

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

जैसा कि वादा किया गया है, हम देखते हैं कि Independent ने बैच शेप को इवेंट शेप में स्थानांतरित कर दिया है: b3_joint एक त्रि-आयामी ईवेंट स्पेस ( event_shape = (3,) ) पर एक एकल वितरण ( batch_shape = () event_shape = (3,) ) है।

आइए जाँच करें शब्दार्थ:

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

एक ही परिणाम प्राप्त करने का एक वैकल्पिक तरीका b3 का उपयोग करके संभावनाओं की गणना करना होगा और मैन्युअल रूप से गुणा करके कमी को करना होगा (या, अधिक सामान्य स्थिति में जहां लॉग संभावनाओं का उपयोग किया जाता है, संक्षेप):

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

Indpendent उपयोगकर्ता को वांछित अवधारणा का अधिक स्पष्ट रूप से प्रतिनिधित्व करने की अनुमति देता है। हम इसे बेहद उपयोगी मानते हैं, हालांकि यह कड़ाई से आवश्यक नहीं है।

मजेदार तथ्य:

  • b3.sample और b3_joint.sample अलग-अलग वैचारिक कार्यान्वयन हैं, लेकिन b3_joint.sample आउटपुट: स्वतंत्र वितरण के बैच के बीच अंतर और Independent का उपयोग करते हुए बैच से बनाए गए एक एकल वितरण के बीच अंतर दिखाते हैं, जब नमूने की गणना की जाती है, जब नमूना नहीं।
  • MultivariateNormalDiag को Normal और Independent वितरणों का उपयोग करके तुच्छ रूप से लागू किया जा सकता है (यह वास्तव में इस तरह से लागू नहीं किया गया है, लेकिन यह हो सकता है)।

बहुभिन्नरूपी डिस्टर्बेंस के बैच

आइए तीन पूर्ण-सहसंयोजक दो आयामी बहुभिन्नरूपी मानदंडों का एक बैच बनाएं:

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>

हम batch_shape = (3,) देखते हैं, इसलिए तीन स्वतंत्र बहुभिन्नरूपी event_shape = (2,) , और event_shape = (2,) , इसलिए प्रत्येक बहुभिन्नरूपी सामान्य दो आयामी है। इस उदाहरण में, व्यक्तिगत वितरण में स्वतंत्र तत्व नहीं हैं।

नमूना काम करता है:

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)>

चूंकि batch_shape = (3,) और event_shape = (2,) , हम log_prob करने के लिए आकार (3, 2) के log_prob को 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)>

प्रसारण, उर्फ ​​यह इतना भ्रमित क्यों है?

हमने अब तक जो कुछ किया है, उसे ध्यान में रखते हुए, हर वितरण में एक बैच आकार B और एक घटना आकृति E । आज्ञा देना BE घटना आकृतियों के संयोजन होना:

  • अविभाज्य स्केलर वितरण के लिए n और b , BE = ().
  • दो आयामी बहुभिन्नरूपी मानदंडों के लिए nd BE = (2).
  • b3 और b3_joint दोनों के लिए, BE = (3).
  • बहुभिन्नरूपी ndb के बैच के लिए ndb , BE = (3, 2).

"मूल्यांकन नियम" जो हम अब तक उपयोग कर रहे हैं वे हैं:

  • कोई तर्क के साथ नमूना आकार BE साथ एक टेंसर देता है; एक स्केलर एन के साथ नमूना एक "एन BE " टेंसर द्वारा लौटता है।
  • prob और log_prob आकार का एक टेन्सर ले BE और आकार का एक परिणाम के लौटने B

के लिए वास्तविक "मूल्यांकन नियम" prob और log_prob अधिक जटिल है, एक तरह से है कि प्रस्तावों संभावित शक्ति और गति, लेकिन यह भी जटिलता और चुनौतियों। वास्तविक नियम यह है (अनिवार्य रूप से) कि log_prob को BE खिलाफ प्रसारित किया जाना चाहिए; किसी भी "अतिरिक्त" आयाम आउटपुट में संरक्षित हैं।

आइए निहितार्थों का पता लगाएं। Univariate normal n , BE = () , इसलिए log_prob एक स्केलर की अपेक्षा करता है। यदि हम log_prob को गैर-रिक्त आकृति के साथ एक टेंसर पास log_prob , तो वे आउटपुट में बैच आयामों के रूप में दिखाई देते हैं:

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)>

आइए द्वि-आयामी बहुभिन्नरूपी सामान्य nd की ओर मुड़ें (रेखांकन प्रयोजनों के लिए बदले गए):

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

log_prob आकार (2,) साथ एक तर्क "उम्मीद" करता है, लेकिन यह इस आकृति के खिलाफ प्रसारित किसी भी तर्क को स्वीकार करेगा:

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

लेकिन हम "अधिक" उदाहरणों में पास कर सकते हैं, और उनके सभी log_prob मूल्यांकन एक ही बार में कर सकते हैं:

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)>

शायद कम आकर्षक, हम घटना आयामों पर प्रसारित कर सकते हैं:

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)>

इस तरह से प्रसारण करना हमारे "जब भी संभव हो डिजाइन को सक्षम करना" का परिणाम है; यह उपयोग कुछ विवादास्पद है और संभावित रूप से टीएफपी के भविष्य के संस्करण में हटाया जा सकता है।

अब आइए फिर से तीन सिक्कों के उदाहरण देखें:

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

यहां, इस संभावना का प्रतिनिधित्व करने के लिए प्रसारण का उपयोग करके कि प्रत्येक सिक्का ऊपर आता है काफी सहज है:

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

(इसकी तुलना b3.prob([1., 1., 1.]) , जिसे हमने वापस इस्तेमाल किया होगा जहाँ b3 को पेश किया गया था।)

अब मान लें कि हम जानना चाहते हैं, प्रत्येक सिक्के के लिए, सिक्का ऊपर आने की संभावना है और संभावना यह है कि पूंछ ऊपर आती है। हम कल्पना कर सकते हैं:

b3.log_prob([0, 1])

दुर्भाग्य से, यह एक लंबे और बहुत-पढ़ने योग्य स्टैक ट्रेस के साथ एक त्रुटि पैदा करता है। b3 में BE = (3) , इसलिए हमें b3.prob पास करना होगा (3,) विरुद्ध कुछ प्रसारण योग्य। [0, 1] का आकार (2) , इसलिए यह प्रसारण नहीं करता है और एक त्रुटि बनाता है। इसके बजाय, हमें कहना होगा:

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)>

क्यों? [[0], [1]] आकार (2, 1) , इसलिए यह (2, 3) का प्रसारण आकार बनाने के लिए आकार (3) विरुद्ध प्रसारित करता है।

प्रसारण काफी शक्तिशाली है: ऐसे मामले हैं जहां यह उपयोग की जाने वाली स्मृति की मात्रा में कमी की अनुमति देता है, और यह अक्सर उपयोगकर्ता को छोटा बनाता है। हालांकि, इसके साथ प्रोग्राम करना चुनौतीपूर्ण हो सकता है। यदि आप log_prob कहते हैं और एक त्रुटि मिलती है, तो प्रसारण में विफलता लगभग हमेशा समस्या है।

आगे जाकर

इस ट्यूटोरियल में, हमने (उम्मीद है) एक सरल परिचय प्रदान किया है। आगे जाने के लिए कुछ संकेत:

  • event_shape , batch_shape और sample_shape को मनमाना रैंक sample_shape जा सकता है (इस ट्यूटोरियल में वे हमेशा स्केलर या रैंक हैं)। इससे शक्ति बढ़ती है लेकिन फिर से प्रोग्रामिंग चुनौतियों का सामना करना पड़ सकता है, खासकर जब प्रसारण शामिल होता है। आकार के हेरफेर में एक अतिरिक्त गहरी गोता लगाने के लिए, अंडरस्टैंडिंग टेंपलफ्लो वितरण आकृतियाँ देखें
  • TFP में Bijectors रूप में जाना जाने वाला एक शक्तिशाली अमूर्त शामिल है, जो TransformedDistribution के साथ मिलकर, नए वितरणों को आसानी से बनाने के लिए एक लचीला, Bijectors तरीके से पैदावार करता है जो मौजूदा वितरणों के उल्टे रूपांतरण हैं। हम जल्द ही इस पर एक ट्यूटोरियल लिखने की कोशिश करेंगे, लेकिन इस बीच, दस्तावेज़ीकरण देखें