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