توزیع های TensorFlow: مقدمه ای ملایم

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

در این نوت بوک، توزیع TensorFlow (به اختصار TFD) را بررسی خواهیم کرد. هدف این نوت بوک این است که شما را به آرامی در منحنی یادگیری بالا ببرد، از جمله درک نحوه کار TFD با اشکال تانسور. این دفترچه سعی دارد به جای مفاهیم انتزاعی، مثال هایی را قبل از آن ارائه کند. ابتدا روش‌های آسان متعارف را برای انجام کارها ارائه می‌کنیم و کلی‌ترین نمای انتزاعی را تا پایان ذخیره می‌کنیم. اگر شما نوع که ترجیح آموزش انتزاعی تر و مرجع سبک هستید، لطفا درک TensorFlow توزیع اشکال . اگر شما هر گونه سوال در مورد مواد در اینجا، برای تماس با دریغ نکنید (و یا پیوستن به) TensorFlow لیست احتمال پستی . ما خوشحالیم که کمک می کنیم.

قبل از شروع، باید کتابخانه های مناسب را وارد کنیم. کتابخانه کلی ما این است tensorflow_probability . طبق قرارداد، ما به طور کلی به کتابخانه توزیع به عنوان مراجعه tfd .

Tensorflow مشتاق یک محیط اجرای ضروری برای TensorFlow است. در TensorFlow eager، هر عملیات 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()

png

توزیع های چندگانه

اولین توزیع برنولی ما نشان دهنده یک تلنگر از یک سکه منصفانه بود. ما همچنین می توانید دسته ای از توزیع برنولی مستقل ایجاد، هر کدام با پارامتر خود، در یک 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)>

چرا API شامل شکل دسته ای است؟ از جنبه معنایی، یک نفر می تواند محاسبات همان با ایجاد یک لیست از توزیع و تکرار بیش از آنها را با یک انجام for حلقه (حداقل در حالت مشتاق، در حالت نمودار TF شما می خواهم نیاز به یک tf.while حلقه). با این حال، داشتن یک مجموعه (بالقوه بزرگ) از توزیع‌های پارامتری یکسان بسیار رایج است، و استفاده از محاسبات بردار در هر زمان که ممکن است، یک عنصر کلیدی در توانایی انجام محاسبات سریع با استفاده از شتاب‌دهنده‌های سخت‌افزاری است.

استفاده از مستقل برای جمع آوری دسته ها به رویدادها

در بخش قبلی، ما ایجاد b3 ، یک Distribution شی که به نمایندگی از سه سکه flips. اگر ما به نام 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 ؛ نمونه برداری با یک اسکالر N بازده یک "نفر توسط 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 توزیع اشکال .
  • بهره وری کل عوامل شامل یک انتزاع قدرتمند شناخته شده به عنوان Bijectors ، که در رابطه با TransformedDistribution ، بازده انعطاف پذیر، راه های ترکیبی به راحتی توزیع جدید است که تحولات معکوس از توزیعهای موجود هستند ایجاد کنید. ما سعی خواهیم کرد به نوشتن یک آموزش در مورد این زودی، اما در این میان، بررسی اسناد و مدارک