Zygote از استفاده از ChainRulesCore برای تعریف حساسیت های سفارشی پشتیبانی می کند. ترجیح داده می شود که حساسیت های سفارشی را با استفاده از ChainRulesCore. rrule تعریف کنید، زیرا برای بسیاری از سیستم های AD، نه فقط Zygote، کار می کنند. این حساسیت ها را می توان در بسته خود اضافه کرد، یا برای توابع Base/StdLib می توان آنها را به ChainRules. jl اضافه کرد. برای تعریف حساسیت های سفارشی با استفاده از ChainRulesCore، یک ChainRulesCore. rrule (f، args. ; kwargs.) تعریف کنید. برای اطلاعات بیشتر به مستندات پروژه ChainRules بروید. اگر با استفاده از ChainRulesCore الحاقات سفارشی خود را تعریف می کنید، نیازی به خواندن این صفحه ندارید و می توانید آن را به عنوان مستندسازی یک ویژگی قدیمی در نظر بگیرید.
این صفحه برای توضیح نحوه عملکرد Zygote و نحوه تعریف پیوست ها به طور مستقیم برای Zygote وجود دارد. تعریف الحاقیه ها به این روش آنها را برای سایر سیستم های AD قابل دسترسی نمی کند، اما به شما امکان می دهد کارهایی را انجام دهید که مستقیماً به نحوه عملکرد Zygote بستگی دارد. این اجازه می دهد تا برای تعاریف خاص از الحاقی که فقط برای Zygote تعریف شده است (که ممکن است متفاوت از تعاریف عمومی تر تعریف شده برای همه AD) تعریف شده است.
ماکرو @adjoint بخش مهمی از رابط Zygote است. سفارشی کردن پاس خود را به عقب نه تنها ممکن است، بلکه به طور گسترده مورد استفاده و تشویق است. در حالی که ابزارهای خاصی برای چیزهای معمولی مانند برش گرادیان در دسترس هستند، درک موارد الحاقی بیشترین انعطاف را به شما می دهد. ما ابتدا کمی پیشینه بیشتر در مورد اینکه این چیزهای عقب نشینی چیست ارائه می دهیم.
پول بک
گرادیان در واقع فقط قند نحوی حول تابع اساسی تر pullback است.
julia> using Zygote julia> y, back = Zygote.pullback(sin, 0.5); julia>y 0. 479425538604203
pullback دو خروجی می دهد: نتیجه تابع اصلی sin(0. 5) و یک pullback که در اینجا back نامیده می شود. back محاسبه گرادیان برای sin را اجرا می کند، مشتق را می پذیرد و یک مشتق جدید تولید می کند. از نظر ریاضی، یک محصول برداری-ژاکوبین را پیاده سازی می کند. جایی که $y = f(x)$ و گرادیان $frac<partial l><partial x>$ نوشته می شود $x08ar$، pullback $mathcal_y$ محاسبه می کند:
[بار= frac<partial l> <partial x>= frac<partial l> <partial y>frac<partial y> <partial x>= mathcal_y(x08ar)]
برای بتن ریزی، تابع $y = sin(x)$ را بگیرید.$frac<partial y> <partial x>= cos(x)$، بنابراین عقب نشینی $x08ar cos(x)$ است. به عبارت دیگر pullback (sin, x) مانند رفتار است
dsin(x) = (sin(x), ȳ >(ȳ * cos(x)،))
گرادیان تابع $l = f(x)$ را می گیرد و $l̄ = frac را در نظر می گیرد<partial l> <partial l>= 1$ و این را به عقب نشینی می دهد. در مورد گناه،
julia> function gradsin(x) _, back = dsin(x) back(1) end gradsin (generic function with 1 method) julia> gradsin(0.5) (0.8775825618903728,) julia>cos(0. 5) 0. 8775825618903728
julia> function mygradient(f, x. ) _, back = Zygote.pullback(f, x. ) back(1) end mygradient (generic function with 1 method) julia>mygradient(sin, 0. 5) (0. 8775825618903728،)
بقیه این بخش شامل جزئیات فنی بیشتری است. اگر فقط به یک شهود برای عقب نشینی نیاز دارید ، می توان آن را رد کرد. شما به طور کلی نیازی به نگرانی در مورد آن به عنوان کاربر ندارید.
اگر $ x $ و $ y $ بردار هستند ، $ frac<partial y><partial x>$ به یک ژاکوبیان تبدیل می شود. مهمتر از همه ، زیرا ما در حال اجرای حالت معکوس هستیم ، در واقع جاکوبیان ، یعنی V'J ، به جای معمول تر J*v. انتقال V به یک بردار ردیف و پشت (V'J) "معادل J'v است ، بنابراین قوانین شیب ما در واقع مجاور Jacobian را پیاده سازی می کنند. این حتی برای کد مقیاس مرتبط است: مجاور y = sin (x) x̄ = cos (x)**است. کونژوگرافی معمولاً اشتباه است اما برای کد پیچیده رفتار صحیحی ارائه می دهد. بنابراین "کشش" گاهی اوقات "محصولات وکتور ژاکوبیان" (VJPS) خوانده می شود ، و ما به قوانین حالت معکوس خود را به عنوان "مجالس" معرفی می کنیم.
Zygote برای عملیات غیر ریاضی از جمله برای نمایه سازی و ساختار داده ها دارای بسیاری از مجالس است. اگرچه این موارد هنوز هم می توانند به عنوان توابع خطی بردارها تلقی شوند ، اما اجرای آنها با یک ماتریس واقعی چند برابر نیست. در این موارد ساده ترین فکر کردن از مجاور به عنوان نوعی معکوس است. به عنوان مثال ، شیب عملکردی که یک توپی را به یک ساختار می برد (به عنوان مثال y = پیچیده (A ، B)) به طور کلی یک ساختار را به یک Tuple می برد ((ȳ. re ، ȳ. im)). شیب یک getIndex y = x [i.] SetIndex است! x̄ [من.] = ȳ ، و غیره
الحاقات سفارشی
ما می توانیم با عملکرد adjoint Zygote را به یک عملکرد جدید گسترش دهیم.
julia> mul(a, b) = a*b; julia> using Zygote: @adjoint julia> @adjoint mul(a, b) = mul(a, b), c̄ > (c̄*b, c̄*a) julia>شیب (MUL ، 2 ، 3) (3. 0 ، 2. 0)
ممکن است عجیب به نظر برسد که ما در اینجا دو بار MUL (A ، B) می نویسیم. در این حالت ما می خواهیم عملکرد MUL معمولی را برای عبور از بازپرداخت فراخوانی کنیم ، اما ممکن است شما همچنین بخواهید پاس بازگردانی را اصلاح کنید (برای مثال ، برای گرفتن نتایج واسطه ای در بازپرداخت).
انواع سفارشی
یکی از کاربردهای خوب برای مجوزهای سفارشی ، سفارشی کردن نحوه رفتار انواع شخصی شما در هنگام تمایز است. به عنوان مثال ، در مثال نکته ما متوجه شدیم که مجاور به جای یک نکته دیگر ، یک Tuple نامگذاری شده است.
پایه واردات: + ، - ساختار نقطه x :: float64 y :: float64 عرض پایان (p :: نقطه) = p. x ارتفاع (p :: نقطه) = p. y a :: point + b :: point = point (عرض (a) + عرض (b) ، ارتفاع (a) + ارتفاع (b)) a :: نقطه - b :: point = نقطه (عرض (a) - عرض (b) ، ارتفاع (a) - ارتفاع (b)) فاصله(p :: نقطه) = sqrt (عرض (p)^2 + ارتفاع (p)^2)
julia> gradient(a >Dist (A) ، Point (1 ، 2)) [1] (x = 0. 4472135954999579 ، Y = 0. 8944271909999159)
اساساً ، این به دلیل پیش فرض پیش فرض زایگوت برای گتفیلد اتفاق می افتد.
julia> gradient(a >a. x ، نقطه (1 ، 2)) ((x = 1 ، y = هیچ چیز) ،)
ما می توانیم با اصلاح ارتفاع و عرض گیرنده ها ، این کار را بیش از حد بارگذاری کنیم.
julia> @adjoint width(p::Point) = p.x, x̄ > (Point(x̄, 0),) julia> @adjoint height(p::Point) = p.y, ȳ > (Point(0, ȳ),) julia> Zygote.refresh() # currently needed when defining new adjoints julia> gradient(a > height(a), Point(1, 2)) (Point(0.0, 1.0),) julia> gradient(a >Dist (A) ، Point (1 ، 2)) [1] Point (0. 4472135954999579 ، 0. 8944271909999159)
اگر این کار را انجام دهید ، باید سازنده نقطه را نیز اضافه کنید ، تا بتواند یک شیب نقطه را اداره کند (در غیر این صورت این عملکرد خطا خواهد کرد).
julia> @adjoint Point(a, b) = Point(a, b), p̄ > (p̄.x, p̄.y) julia> gradient(x >Dist (نقطه (x ، 1)) ، 1) (0. 7071067811865475 ،)
مجایال پیشرفته
ما معمولاً برای اضافه کردن شیب هایی که Zygote نمی تواند خود را به دست آورد (به عنوان مثال ، زیرا آنها به Blas می پردازند) از مجالس سفارشی استفاده می کنیم. اما چیزهای پیشرفته تر و سرگرم کننده تری وجود دارد که می توانیم با adjoint انجام دهیم.
قلاب های شیب دار
julia> hook(f, x) = x hook (generic function with 1 method) julia> @adjoint hook(f, x) = x, x̄ >(هیچ چیز ، f (x̄))
هوک به نظر جالب نمی رسد ، زیرا هیچ کاری نمی کند. اما قسمت سرگرم کننده در مجاورت است. این امکان را به ما می دهد تا یک تابع F را در شیب x اعمال کنیم.
julia> gradient((a, b) >هوک (-، الف)*B ، 2 ، 3) (-3. 0 ، 2. 0)
ما می توانیم از این کار برای اشکال زدایی یا اصلاح شیب (به عنوان مثال قطع شیب) استفاده کنیم.
julia> gradient((a, b) > hook(ā >Show (ā) ، الف)*B ، 2 ، 3) ā = 3. 0 (3. 0 ، 2. 0)
Zygote هم قلاب و هم شیاطر را فراهم می کند ، بنابراین لازم نیست خودتان اینها را بنویسید.
بازرسی
یک مثال پیشرفته تر ، بازرسی است که در آن ما حافظه را با استفاده مجدد از بازپرداخت یک تابع در هنگام عبور به عقب ذخیره می کنیم. به شوخ طبعی:
julia> checkpoint(f, x) = f(x) checkpoint (generic function with 1 method) julia> @adjoint checkpoint(f, x) = f(x), ȳ > Zygote._pullback(f, x)[2](ȳ) julia> gradient(x >ایست بازرسی (گناه ، x) ، 1) (0. 5403023058681398 ،)
اگر یک تابع عوارض جانبی داشته باشد ، خواهیم دید که پاس برگشتی دو بار اتفاق می افتد ، همانطور که انتظار می رود.
julia> foo(x) = (println(x); sin(x)) foo (generic function with 1 method) julia> gradient(x >پاسگاه (FOO ، X) ، 1) 1 1 (0. 5403023058681398 ،)
بازتاب شیب
آسان است که بررسی کنید آیا کدی که در حال اجرا هستیم در حال تمایز است یا خیر.
isderiving() = false @adjoint isderiving() = true, _ >هیچ چی
یک مثال جالب تر این است که در واقع تشخیص تعداد سطوح لانه سازی در جریان است.
nestlevel() = 0 @adjoint nestlevel() = nestlevel()+1, _ >هیچ چی
julia> function f(x) println(nestlevel(), " levels of nesting") retu x end f (generic function with 1 method) julia> grad(f, x) = gradient(f, x)[1] grad (generic function with 1 method) julia> f(1); 0 levels of nesting julia> grad(f, 1); 1 levels of nesting julia> grad(x >X*درجه (f ، x) ، 1) ؛2 سطح لانه سازی
این سند در روز جمعه 7 آوریل 2023 با Documenter. jl تولید شد. با استفاده از نسخه جولیا 1. 8. 5.
فارکس وکسب درامد...
ما را در سایت فارکس وکسب درامد دنبال می کنید
برچسب :
نویسنده : احمد قانع پور
بازدید : 36
تاريخ : پنجشنبه
19 مرداد
1402 ساعت: :