توزيعات TensorFlow: مقدمة لطيفة

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

في هذا الكمبيوتر الدفتري ، سوف نستكشف توزيعات TensorFlow (اختصارًا TFD). الهدف من هذا الكمبيوتر المحمول هو رفع منحنى التعلم بلطف ، بما في ذلك فهم معالجة TFD لأشكال التوتر. يحاول هذا الكمبيوتر الدفتري تقديم أمثلة قبل المفاهيم المجردة بدلاً من عرضها. سنقدم طرقًا أساسية سهلة للقيام بالأشياء أولاً ، وحفظ العرض المجرد الأكثر عمومية حتى النهاية. إذا كنت من النوع الذي يفضل تعليمي أكثر تجريدا ونمط المرجع، تحقق من فهم TensorFlow التوزيعات الأشكال . إذا كان لديك أي أسئلة حول المواد هنا، لا تتردد في الاتصال (أو الانضمام) في TensorFlow قائمة الاحتمالات البريدية . يسعدنا تقديم المساعدة.

قبل أن نبدأ ، نحتاج إلى استيراد المكتبات المناسبة. مكتبتنا الشاملة هي tensorflow_probability . المتعارف عليه أن نشير عموما إلى المكتبة التوزيعات كما tfd .

Tensorflow حريصة هي بيئة تنفيذ الملحة لTensorFlow. في TensorFlow الشغوف ، يتم تقييم كل عملية TF على الفور وتنتج نتيجة. هذا على عكس وضع "الرسم البياني" القياسي في TensorFlow ، حيث تضيف عمليات TF العقد إلى الرسم البياني الذي يتم تنفيذه لاحقًا. تمت كتابة هذا الكمبيوتر الدفتري بالكامل باستخدام TF Eager ، على الرغم من عدم اعتماد أي من المفاهيم المقدمة هنا على ذلك ، ويمكن استخدام TFP في وضع الرسم البياني.

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

توزيعات متعددة المتغيرات

سننشئ متغيرًا عاديًا متعدد المتغيرات بتغاير قطري:

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 ، مما يدل على الأساسي [مساحة مخصصة] (https://en.wikipedia.org/wiki/Event_ (probability_theory)) من هذا التوزيع هو ثنائي الأبعاد.

يعمل أخذ العينات تمامًا كما كان من قبل:

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>

لا تحتوي القواعد المعيارية متعددة المتغيرات بشكل عام على تغاير قطري. تقدم 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()
<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)>

إذا نسميه prob ، (وهذا له نفس دلالات الشكل كما log_prob ، ونحن نستخدم prob مع هذه الأمثلة برنولي الصغيرة من أجل الوضوح، على الرغم من log_prob ويفضل عادة في تطبيقات) ونحن يمكن أن تمر عليه النواقل وتقييم احتمال كل عملة ذات العوائد تلك القيمة :

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

لماذا تتضمن واجهة برمجة التطبيقات شكل الدُفعة؟ لغويا، يمكن للمرء أن تؤدي نفس العمليات الحسابية عن طريق إنشاء قائمة التوزيعات وبالتكرار عبر لهم for حلقة (على الأقل في وضع حرصا، في وضع TF الرسم البياني كنت في حاجة الى tf.while حلقة). ومع ذلك ، فإن وجود مجموعة (يحتمل أن تكون كبيرة) من التوزيعات ذات المعلمات المتماثلة أمر شائع للغاية ، واستخدام الحسابات الموجهة كلما أمكن ذلك هو عنصر أساسي في القدرة على إجراء عمليات حسابية سريعة باستخدام مسرعات الأجهزة.

استخدام المستقل لتجميع الدُفعات للأحداث

في الجزء السابق، أنشأنا b3 ، واحد Distribution الكائن الذي يمثل ثلاثة عملة تقلب. إذا كنا نسميها b3.prob على ناقلات \(v\)، و \(i\)كان 'ال دخول احتمال أن \(i\)عملة ال يأخذ قيمة \(v[i]\).

لنفترض أننا بدلاً من ذلك نرغب في تحديد توزيع "مشترك" على متغيرات عشوائية مستقلة من نفس العائلة الأساسية. هذا هو كائن مختلف رياضيا، في أن لهذا التوزيع الجديد، prob على ناقلات \(v\) سيعود قيمة واحدة تمثل احتمال أن مجموعة كاملة من القطع النقدية يطابق ناقلات \(v\).

كيف نحقق هذا؟ ونحن نستخدم "عالي المستوى" توزيع دعا Independent ، والتي تأخذ توزيع وتعطي توزيع جديد مع شكل دفعة انتقل الى شكل الحدث:

b3_joint = tfd.Independent(b3, reinterpreted_batch_ndims=1)
b3_joint
<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 هو توزيع واحد ( 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 ديك تطبيقات مفاهيمية مختلفة، ولكن النتائج لا يمكن تمييزها: الفرق بين مجموعة من التوزيعات مستقلة وتوزيع واحد تم إنشاؤها من الدفعة باستخدام Independent يظهر عند حساب probabilites، وليس عند أخذ العينات.
  • 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,) ، لذلك كل متعدد المتغيرات العادي هو ثنائي الأبعاد. في هذا المثال ، لا تحتوي التوزيعات الفردية على عناصر مستقلة.

أعمال أخذ العينات:

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,) ، ونحن تمرير موتر الشكل (3, 2) ل 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 ، BE = (3, 2).

"قواعد التقييم" التي استخدمناها حتى الآن هي:

  • عينة مع عدم وجود حجة بإرجاع موتر مع شكل BE . أخذ العينات مع العددية ن عائدات لها "ن كتبها BE " موتر.
  • prob و log_prob اتخاذ موتر من شكل BE والعودة نتيجة لشكل B .

"القاعدة التقييم" الفعلية لل prob و log_prob هو أكثر تعقيدا، بطريقة العروض المحتملة للقوة والسرعة ولكن أيضا التعقيدات والتحديات. حكم الفعلية (أساسا) أن الحجة ل log_prob يجب أن يكون broadcastable ضد BE . يتم الاحتفاظ بأية أبعاد "إضافية" في الإخراج.

دعنا نستكشف الآثار المترتبة. لالعادي وحيد المتغير n ، BE = () ، لذلك 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)>

البث بهذه الطريقة هو نتيجة لتصميمنا "تمكين البث كلما أمكن ذلك" ؛ هذا الاستخدام مثير للجدل إلى حد ما ويمكن إزالته في إصدار مستقبلي من TFP.

الآن دعونا نلقي نظرة على مثال العملات الثلاث مرة أخرى:

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 شيء broadcastable ضد (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) ، لذلك فإنه يرسل ضد شكل (3) لجعل شكل بث (2, 3) .

البث قوي للغاية: هناك حالات تسمح فيها بتقليل حجم الذاكرة المستخدمة ، وغالبًا ما تجعل كود المستخدم أقصر. ومع ذلك ، قد يكون من الصعب البرمجة باستخدام. إذا كنت استدعاء log_prob وتحصل على خطأ، وعدم بث دائما ما يقرب من المشكلة.

الذهاب أبعد

في هذا البرنامج التعليمي ، قدمنا ​​(نأمل) مقدمة بسيطة. بعض المؤشرات للمضي قدماً:

  • event_shape ، batch_shape و sample_shape يمكن أن يكون رتبة التعسفي (في هذا البرنامج التعليمي هم دائما إما العددية أو رتبة 1). يؤدي هذا إلى زيادة القوة ولكن مرة أخرى يمكن أن يؤدي إلى تحديات البرمجة ، خاصةً عندما يتعلق الأمر بالبث. للغوص العميق إضافية إلى التلاعب شكل، راجع فهم TensorFlow التوزيعات الأشكال .
  • يشمل TFP تجريد قوية تعرف باسم Bijectors ، الذي جنبا إلى جنب مع TransformedDistribution ، تعطي، وطريقة التركيبية مرنة لإنشاء بسهولة التوزيعات الجديدة التي هي التحولات للانعكاس التوزيعات الحالية. سنحاول لكتابة البرنامج التعليمي حول هذا قريبا، ولكن في غضون ذلك، تحقق من وثائق