पाइटोरेक, क्रमिक तर्क क्या हैं


112

मैं PyTorch के प्रलेखन के माध्यम से पढ़ रहा हूं और एक उदाहरण मिला जहां वे लिखते हैं

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)

जहाँ x एक प्रारंभिक चर था, जिसमें से y (3-वेक्टर) का निर्माण किया गया था। सवाल यह है कि ग्रेडिएंट टेंसर के 0.1, 1.0 और 0.0001 तर्क क्या हैं? उस पर प्रलेखन बहुत स्पष्ट नहीं है।

जवाबों:


15

मूल कोड मैं अब PyTorch वेबसाइट पर नहीं मिला है।

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)

उपर्युक्त कोड के साथ समस्या यह है कि ग्रेडिएंट्स की गणना के आधार पर कोई फ़ंक्शन नहीं है। इसका मतलब है कि हम यह नहीं जानते हैं कि कितने पैरामीटर (फ़ंक्शन लेते हैं) और मापदंडों का आयाम।

इसे पूरी तरह समझने के लिए मैंने एक उदाहरण मूल के करीब बनाया:

उदाहरण 1:

a = torch.tensor([1.0, 2.0, 3.0], requires_grad = True)
b = torch.tensor([3.0, 4.0, 5.0], requires_grad = True)
c = torch.tensor([6.0, 7.0, 8.0], requires_grad = True)

y=3*a + 2*b*b + torch.log(c)    
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients,retain_graph=True)    

print(a.grad) # tensor([3.0000e-01, 3.0000e+00, 3.0000e-04])
print(b.grad) # tensor([1.2000e+00, 1.6000e+01, 2.0000e-03])
print(c.grad) # tensor([1.6667e-02, 1.4286e-01, 1.2500e-05])

मैंने मान लिया कि हमारा कार्य है y=3*a + 2*b*b + torch.log(c)और पैरामीटर दस तत्वों के अंदर हैं।

आप ऐसा सोच सकते हैं कि gradients = torch.FloatTensor([0.1, 1.0, 0.0001])यह संचायक है।

जैसा कि आप सुन सकते हैं PyTorch ऑटोग्रैड सिस्टम गणना जैकोबियन उत्पाद के बराबर है।

Jacobian

यदि आपके पास कोई कार्य है, तो जैसे हमने किया:

y=3*a + 2*b*b + torch.log(c)

जैकबियन होगा [3, 4*b, 1/c]। हालांकि, इस Jacobian नहीं कैसे PyTorch बातें निश्चित बिंदु पर ढ़ाल गणना करने के लिए कर रही है है।

PyTorch फॉरवर्ड पास और बैकवर्ड मोड ऑटोमैटिक भेदभाव का उपयोग करता है (AD) ।

इसमें कोई सांकेतिक गणित शामिल नहीं है और कोई संख्यात्मक अंतर नहीं है।

संख्यात्मक विभेदन की गणना के लिए होगी δy/δb, जहां b=1और b=1+εजहां would छोटा है।

यदि आप ग्रेडिएंट का उपयोग नहीं करते हैं y.backward():

उदाहरण 2

a = torch.tensor(0.1, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(0.1, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)

y.backward()

print(a.grad) # tensor(3.)
print(b.grad) # tensor(4.)
print(c.grad) # tensor(10.)

आप सरल कैसे आप अपने सेट के आधार पर, एक बिंदु पर परिणाम मिल जाएगा a, b, ctensors शुरू में।

सावधान रहें कि कैसे आप अपने प्रारंभ a, b, c:

उदाहरण 3:

a = torch.empty(1, requires_grad = True, pin_memory=True)
b = torch.empty(1, requires_grad = True, pin_memory=True)
c = torch.empty(1, requires_grad = True, pin_memory=True)

y=3*a + 2*b*b + torch.log(c)

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)

print(a.grad) # tensor([3.3003])
print(b.grad) # tensor([0.])
print(c.grad) # tensor([inf])

यदि आप उपयोग torch.empty()करते हैं और उपयोग नहीं करते pin_memory=Trueहैं तो आपके पास हर बार अलग परिणाम हो सकते हैं।

इसके अलावा, नोट ढ़ाल संचयकों की तरह हैं ताकि जरूरत पड़ने पर उन्हें शून्य किया जा सके।

उदाहरण 4:

a = torch.tensor(1.0, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(1.0, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)

y.backward(retain_graph=True)
y.backward()

print(a.grad) # tensor(6.)
print(b.grad) # tensor(8.)
print(c.grad) # tensor(2.)

PyTorch उपयोग की शर्तों पर अंतिम कुछ सुझाव:

PyTorch एक गतिशील कम्प्यूटेशनल ग्राफ बनाता हैआगे पास में ग्रेडिएंट्स की गणना करते समय । यह काफी हद तक पेड़ जैसा दिखता है।

इसलिए आप अक्सर सुनेंगे पत्ते इस पेड़ के हैं इनपुट tensors और जड़ है उत्पादन टेन्सर

ग्रेड को रूट से पत्ती तक ग्राफ को ट्रेस करके और श्रृंखला के नियम का उपयोग करके प्रत्येक ढाल को गुणा करके गणना की जाती है । यह गुणा भाग पिछड़े दर्रे में होता है।


बहुत बढ़िया जवाब! हालांकि, मुझे नहीं लगता कि पाइटोरेक संख्यात्मक अंतर करता है ("पिछले फ़ंक्शन के लिए PyTorch उदाहरण के लिए δy / ,b करेगा, b = 1 और b = 1 + ε के लिए जहां small छोटा है। इसलिए इसमें सांकेतिक गणित शामिल नहीं है। ") - मेरा मानना ​​है कि यह स्वचालित भेदभाव करता है।
max_max_mir

हां, यह AD या स्वचालित भेदभाव का उपयोग करता है, बाद में मैंने इस PDF में AD की तरह आगे की जाँच की , हालाँकि, जब मैंने यह उत्तर निर्धारित किया तो मुझे इसकी जानकारी नहीं थी।
वेश्या

उदाहरण उदाहरण 2 में RuntimeError: आकार में बेमेल: grad_output [0] का आकार torch.Size ([3]) और आउटपुट [0] में torch.Size ([]) की आकृति है।
एंड्रियास के।

@AndreasK।, आप सही थे, PyTorch ने हाल ही में शून्य आकार के टेंसरों को पेश किया और इसका मेरे पिछले उदाहरणों पर प्रभाव पड़ा। हटाए जाने के बाद से ये उदाहरण महत्वपूर्ण नहीं थे।
प्रोस्टी

100

व्याख्या

तंत्रिका नेटवर्क के लिए, हम आम तौर पर lossयह आकलन करने के लिए उपयोग करते हैं कि इनपुट छवि (या अन्य कार्यों) को वर्गीकृत करने के लिए नेटवर्क ने कितनी अच्छी तरह सीखा है। lossशब्द आम तौर पर एक अदिश मूल्य है। नेटवर्क के मापदंडों को अद्यतन करने के लिए, हमें lossपैरामीटर के wrt के ग्रेडिएंट की गणना करने की आवश्यकता है , जो कि वास्तव leaf nodeमें गणना ग्राफ में है (वैसे, ये पैरामीटर ज्यादातर विभिन्न परतों के वजन और पूर्वाग्रह हैं जैसे कि रूपांतरण, रैखिक और एक्सटेंशन जल्द ही)।

श्रृंखला नियम के अनुसार, lossपत्ती के नोड के लिए ग्रेडिएंट की गणना करने के लिए , हम losswrt के व्युत्पन्न को कुछ इंटरमीडिएट वैरिएबल, और लीफ वेरिएंट के मध्यवर्ती वेरिएंट को लीफ वैरिएबल में कंपेयर कर सकते हैं, एक डॉट उत्पाद करते हैं और इन सभी को योग करते हैं।

gradientएक के तर्कों Variableकी backward()विधि के लिए प्रयोग किया जाता है wrt एक चर के प्रत्येक तत्व की एक भारित योग की गणना पत्ती चरये भार केवल lossमध्यवर्ती चर के प्रत्येक तत्व को अंतिम wrt के व्युत्पन्न करते हैं।

एक ठोस उदाहरण

आइए इसे समझने के लिए एक ठोस और सरल उदाहरण लें।

from torch.autograd import Variable
import torch
x = Variable(torch.FloatTensor([[1, 2, 3, 4]]), requires_grad=True)
z = 2*x
loss = z.sum(dim=1)

# do backward for first element of z
z.backward(torch.FloatTensor([[1, 0, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_() #remove gradient in x.grad, or it will be accumulated

# do backward for second element of z
z.backward(torch.FloatTensor([[0, 1, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()

# do backward for all elements of z, with weight equal to the derivative of
# loss w.r.t z_1, z_2, z_3 and z_4
z.backward(torch.FloatTensor([[1, 1, 1, 1]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()

# or we can directly backprop using loss
loss.backward() # equivalent to loss.backward(torch.FloatTensor([1.0]))
print(x.grad.data)    

उपरोक्त उदाहरण में, पहले printका परिणाम है

2 0 0 0
[torch.FloatTensor का आकार 1x4]

जो कि बिल्कुल x से z_1 wrt का व्युत्पन्न है।

दूसरे printका परिणाम है:

0 2 0 0
[torch.FloatTensor का आकार 1x4]

जो x से z_2 wrt का व्युत्पन्न है।

अब अगर z wrt से x की व्युत्पत्ति की गणना करने के लिए [1, 1, 1, 1] के वजन का उपयोग करें, तो परिणाम है 1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx। तो कोई आश्चर्य नहीं, 3 printका उत्पादन है:

2 2 2 2
[torch.FloatTensor का आकार 1x4]

यह ध्यान दिया जाना चाहिए कि वजन वेक्टर [1, 1, 1, 1] losszt से z_1, z_2, z_3 और z_4 से बिल्कुल व्युत्पन्न है । lossWrt के व्युत्पन्न की xगणना इस प्रकार की जाती है:

d(loss)/dx = d(loss)/dz_1 * dz_1/dx + d(loss)/dz_2 * dz_2/dx + d(loss)/dz_3 * dz_3/dx + d(loss)/dz_4 * dz_4/dx

तो 4 printका आउटपुट 3 के समान है print:

2 2 2 2
[torch.FloatTensor का आकार 1x4]


1
बस एक संदेह है, हम नुकसान या जेड के लिए ग्रेडिएंट्स के लिए x.grad.data की गणना क्यों कर रहे हैं ।
प्रियांक पाठक

7
शायद मुझे कुछ याद आया, लेकिन मुझे लगता है कि आधिकारिक दस्तावेज वास्तव में gradientतर्क को बेहतर तरीके से समझा सकते थे । आपके उत्तर के लिए धन्यवाद।
नायक

3
@jdhao "यह ध्यान दिया जाना चाहिए कि वजन वेक्टर [1, 1, 1, 1]के बिल्कुल व्युत्पन्न है lossकरने के लिए wrt z_1, z_2, z_3और z_4।" मुझे लगता है कि यह कथन वास्तव में उत्तर की कुंजी है। ओपी के कोड को देखते समय एक बड़ा प्रश्न चिह्न है कि ग्रेडिएंट के लिए ये मनमाने (मैजिक) नंबर कहां से आए। आपके ठोस उदाहरण में, मुझे लगता है कि उदाहरण के लिए [1, 0, 0 0]टेंसर और lossफ़ंक्शन के बीच के संबंध को इंगित करना बहुत उपयोगी होगा ताकि कोई यह देख सके कि मान इस उदाहरण में मनमाने नहीं हैं।
a_guest

1
@smwikipedia, यह सच नहीं है। यदि हम विस्तार करते हैं loss = z.sum(dim=1), तो यह बन जाएगा loss = z_1 + z_2 + z_3 + z_4। यदि आप सरल पथरी को जानते हैं, तो आप जान पाएंगे कि lossकुल्हाड़ी का व्युत्पन्न क्या z_1, z_2, z_3, z_4है [1, 1, 1, 1]
jdhao

1
मैं तुमसे प्यार करता हूँ। मेरा शक हल किया!
ब्लैक जैक 21

45

आमतौर पर, आपके कम्प्यूटेशनल ग्राफ में एक स्केलर आउटपुट होता है loss। फिर आप losswrt वेट ( w) के ग्रेडिएंट की गणना कर सकते हैं loss.backward()। जहां का डिफ़ॉल्ट तर्क backward()है 1.0

यदि आपके आउटपुट में कई मान हैं (उदाहरण के लिए loss=[loss1, loss2, loss3]), तो आप वजन के ग्रेडिएंट्स की गणना कर सकते हैं loss.backward(torch.FloatTensor([1.0, 1.0, 1.0]))

इसके अलावा, यदि आप अलग-अलग नुकसान के लिए वजन या आयात जोड़ना चाहते हैं, तो आप उपयोग कर सकते हैं loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001]))

इसका मतलब है -0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dwएक साथ गणना करना ।


1
"यदि आप अलग-अलग नुकसानों के लिए वज़न या आयात जोड़ना चाहते हैं, तो आप loss.backward (torch.FloatTensor ([- 0.1, 1.0, 0.0001])) का उपयोग कर सकते हैं।" -> यह सच है लेकिन कुछ हद तक भ्रामक है क्योंकि मुख्य कारण जो हम पास grad_tensorsकरते हैं उन्हें अलग तरीके से तौलना नहीं है, लेकिन वे ग्रेडिएंट हैं जो संबंधित टेंसरों के प्रत्येक तत्व को लिखते हैं।
एरिन

27

यहां, फॉरवर्ड (), यानी y का आउटपुट आ 3-वेक्टर है।

तीन मान नेटवर्क के आउटपुट पर ग्रेडिएंट हैं। वे आमतौर पर 1.0 पर सेट होते हैं यदि y अंतिम आउटपुट है, लेकिन अन्य मान भी हो सकते हैं, खासकर अगर y एक बड़े नेटवर्क का हिस्सा है।

उदाहरण के लिए। यदि x इनपुट है, y = [y1, y2, y3] एक मध्यवर्ती आउटपुट है जिसका उपयोग अंतिम आउटपुट z, की गणना करने के लिए किया जाता है,

फिर,

dz/dx = dz/dy1 * dy1/dx + dz/dy2 * dy2/dx + dz/dy3 * dy3/dx

तो यहाँ, पिछड़े के तीन मूल्य हैं

[dz/dy1, dz/dy2, dz/dy3]

और फिर पीछे () dz / dx की गणना करता है


5
उत्तर के लिए धन्यवाद, लेकिन व्यवहार में यह कैसे उपयोगी है? मेरा मतलब है कि हार्डकोडिंग बैकड्रॉप के अलावा हमें [dz / dy1, dz / dy2, dz / dy3] की आवश्यकता कहां है?
hi15

क्या यह कहना सही है कि प्रदान किए गए ढाल तर्क नेटवर्क के उत्तरार्द्ध में वर्गीकृत ढाल है?
21
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.