सहायता Kaggle पर TensorFlow साथ ग्रेट बैरियर रीफ की रक्षा चैलेंज में शामिल हों

ग्रेडिएंट और स्वचालित भेदभाव का परिचय

TensorFlow.org पर देखें GitHub पर स्रोत देखें नोटबुक डाउनलोड करें

स्वचालित भेदभाव और ग्रेडिएंट

स्वचालित भेदभाव जैसे मशीन सीखने एल्गोरिदम को लागू करने के लिए उपयोगी है backpropagation तंत्रिका नेटवर्क को प्रशिक्षित करने के लिए।

इस गाइड में, आप TensorFlow साथ ढ़ाल गणना करने के लिए, विशेष रूप से में तरीकों का पता लगाने जाएगा उत्सुक निष्पादन

सेट अप

import numpy as np
import matplotlib.pyplot as plt

import tensorflow as tf

कंप्यूटिंग ग्रेडियेंट

स्वचालित रूप से अंतर करने के लिए, TensorFlow क्या संचालन फॉरवर्ड पास दौरान किस क्रम में हो याद करने की जरूरत है। फिर, पिछड़े पास दौरान, TensorFlow गणना ढ़ाल को उलटे क्रम में संचालन की इस सूची को पार करता।

ढाल टेप

TensorFlow प्रदान करता है tf.GradientTape स्वचालित भेदभाव के लिए एपीआई; कि कुछ आदानों, आम तौर पर के संबंध में एक गणना की ढाल कंप्यूटिंग, है tf.Variable रों। TensorFlow "रिकॉर्ड" प्रासंगिक संचालन एक के संदर्भ के अंदर निष्पादित tf.GradientTape एक "टेप" पर। TensorFlow तो यह है कि टेप का उपयोग कर एक "रिकॉर्ड" गणना के ढ़ाल गणना करने के लिए उपयोग करता है रिवर्स मोड भेदभाव

ये रहा एक सरल उदाहरण:

x = tf.Variable(3.0)

with tf.GradientTape() as tape:
  y = x**2

एक बार जब आप कुछ कार्य रिकॉर्ड किया है, का उपयोग GradientTape.gradient(target, sources) कुछ लक्ष्य (अक्सर एक हानि) कुछ स्रोत (अक्सर मॉडल चर) के सापेक्ष की ढाल की गणना करने के:

# dy = 2x * dx
dy_dx = tape.gradient(y, x)
dy_dx.numpy()
6.0

ऊपर के उदाहरण scalars का उपयोग करता है, लेकिन tf.GradientTape किसी भी टेन्सर पर के रूप में आसानी से काम करता है:

w = tf.Variable(tf.random.normal((3, 2)), name='w')
b = tf.Variable(tf.zeros(2, dtype=tf.float32), name='b')
x = [[1., 2., 3.]]

with tf.GradientTape(persistent=True) as tape:
  y = x @ w + b
  loss = tf.reduce_mean(y**2)

की ढाल प्राप्त करने के लिए loss दोनों चर के संबंध में, आप के लिए स्रोत के रूप में दोनों पारित कर सकते हैं gradient विधि। टेप कैसे स्रोतों पारित कर रहे हैं के बारे में लचीला है और सूचियों या शब्दकोशों में से किसी नेस्टेड संयोजन स्वीकार करते हैं और ढाल उसी तरह (देखें संरचित वापस आ जाएगी tf.nest )।

[dl_dw, dl_db] = tape.gradient(loss, [w, b])

प्रत्येक स्रोत के संबंध में ढाल में स्रोत का आकार होता है:

print(w.shape)
print(dl_dw.shape)
(3, 2)
(3, 2)

यहाँ फिर से ढाल की गणना है, इस बार चर के एक शब्दकोश को पारित कर रहा है:

my_vars = {
    'w': w,
    'b': b
}

grad = tape.gradient(loss, my_vars)
grad['b']
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-1.6920902, -3.2363236], dtype=float32)>

एक मॉडल के संबंध में ग्रेडिएंट

यह इकट्ठा करने के लिए आम tf.Variables एक में tf.Module या उसके उपवर्गों (में से एक layers.Layer , keras.Model ) के लिए checkpointing और निर्यात

ज्यादातर मामलों में, आप एक मॉडल के प्रशिक्षण योग्य चर के संबंध में ग्रेडिएंट की गणना करना चाहेंगे। चूंकि के सभी उपवर्गों tf.Module में अपने चर कुल Module.trainable_variables संपत्ति, आप कोड की कुछ लाइनों में इन ढ़ाल गणना कर सकते हैं:

layer = tf.keras.layers.Dense(2, activation='relu')
x = tf.constant([[1., 2., 3.]])

with tf.GradientTape() as tape:
  # Forward pass
  y = layer(x)
  loss = tf.reduce_mean(y**2)

# Calculate gradients with respect to every trainable variable
grad = tape.gradient(loss, layer.trainable_variables)
for var, g in zip(layer.trainable_variables, grad):
  print(f'{var.name}, shape: {g.shape}')
dense/kernel:0, shape: (3, 2)
dense/bias:0, shape: (2,)

टेप जो देखता है उसे नियंत्रित करना

डिफ़ॉल्ट व्यवहार एक trainable तक पहुँचने के बाद सभी कार्यों रिकॉर्ड करने के लिए है tf.Variable । इसके कारण हैं:

  • टेप को यह जानने की जरूरत है कि पीछे के पास में ग्रेडियेंट की गणना करने के लिए आगे के पास में कौन से संचालन रिकॉर्ड करना है।
  • टेप में मध्यवर्ती आउटपुट के संदर्भ हैं, इसलिए आप अनावश्यक संचालन रिकॉर्ड नहीं करना चाहते हैं।
  • सबसे आम उपयोग के मामले में एक मॉडल के सभी प्रशिक्षित चर के संबंध में नुकसान की ढाल की गणना करना शामिल है।

उदाहरण के लिए, निम्नलिखित एक ढाल की गणना करने के कारण विफल रहता है tf.Tensor डिफ़ॉल्ट रूप से नहीं "देखा" है, और tf.Variable trainable नहीं है:

# A trainable variable
x0 = tf.Variable(3.0, name='x0')
# Not trainable
x1 = tf.Variable(3.0, name='x1', trainable=False)
# Not a Variable: A variable + tensor returns a tensor.
x2 = tf.Variable(2.0, name='x2') + 1.0
# Not a variable
x3 = tf.constant(3.0, name='x3')

with tf.GradientTape() as tape:
  y = (x0**2) + (x1**2) + (x2**2)

grad = tape.gradient(y, [x0, x1, x2, x3])

for g in grad:
  print(g)
tf.Tensor(6.0, shape=(), dtype=float32)
None
None
None

आप सूचीबद्ध कर सकते हैं वैरिएबल का उपयोग कर टेप द्वारा देखा जा रहा GradientTape.watched_variables विधि:

[var.name for var in tape.watched_variables()]
['x0:0']

tf.GradientTape हुक कि क्या है या नहीं देखा है पर उपयोगकर्ता नियंत्रण दे देता है।

एक के संबंध में रिकॉर्ड ढ़ाल के लिए tf.Tensor , आप कॉल करने की आवश्यकता GradientTape.watch(x) :

x = tf.constant(3.0)
with tf.GradientTape() as tape:
  tape.watch(x)
  y = x**2

# dy = 2x * dx
dy_dx = tape.gradient(y, x)
print(dy_dx.numpy())
6.0

इसके विपरीत, सब देख रहा है के डिफ़ॉल्ट व्यवहार को निष्क्रिय करने के लिए tf.Variables , सेट watch_accessed_variables=False जब ढाल टेप का निर्माण। यह गणना दो चर का उपयोग करती है, लेकिन केवल एक चर के लिए ढाल को जोड़ती है:

x0 = tf.Variable(0.0)
x1 = tf.Variable(10.0)

with tf.GradientTape(watch_accessed_variables=False) as tape:
  tape.watch(x1)
  y0 = tf.math.sin(x0)
  y1 = tf.nn.softplus(x1)
  y = y0 + y1
  ys = tf.reduce_sum(y)

चूंकि GradientTape.watch पर आमंत्रित नहीं किया गया x0 , कोई ढाल यह के संबंध में गणना की जाती है:

# dys/dx1 = exp(x1) / (1 + exp(x1)) = sigmoid(x1)
grad = tape.gradient(ys, {'x0': x0, 'x1': x1})

print('dy/dx0:', grad['x0'])
print('dy/dx1:', grad['x1'].numpy())
dy/dx0: None
dy/dx1: 0.9999546

इंटरमीडिएट परिणाम

तुम भी अंदर गणना की मध्यवर्ती मूल्यों के संबंध में उत्पादन की ढ़ाल अनुरोध कर सकते हैं tf.GradientTape संदर्भ।

x = tf.constant(3.0)

with tf.GradientTape() as tape:
  tape.watch(x)
  y = x * x
  z = y * y

# Use the tape to compute the gradient of z with respect to the
# intermediate value y.
# dz_dy = 2 * y and y = x ** 2 = 9
print(tape.gradient(z, y).numpy())
18.0

डिफ़ॉल्ट रूप से, द्वारा आयोजित संसाधनों GradientTape जैसे ही जारी कर रहे हैं GradientTape.gradient विधि कहा जाता है। एक ही संगणना के ऊपर कई ढ़ाल की गणना करने के लिए, के साथ एक ढाल टेप बनाने persistent=True । यह करने के लिए कई कॉल की अनुमति देता है gradient विधि के रूप में संसाधनों जारी कर रहे हैं जब टेप वस्तु कचरा एकत्र है। उदाहरण के लिए:

x = tf.constant([1, 3.0])
with tf.GradientTape(persistent=True) as tape:
  tape.watch(x)
  y = x * x
  z = y * y

print(tape.gradient(z, x).numpy())  # [4.0, 108.0] (4 * x**3 at x = [1.0, 3.0])
print(tape.gradient(y, x).numpy())  # [2.0, 6.0] (2 * x at x = [1.0, 3.0])
[  4. 108.]
[2. 6.]
del tape   # Drop the reference to the tape

प्रदर्शन पर नोट्स

  • ग्रेडिएंट टेप के संदर्भ में संचालन करने से जुड़ा एक छोटा ओवरहेड है। सबसे उत्सुक निष्पादन के लिए यह ध्यान देने योग्य लागत नहीं होगी, लेकिन आपको अभी भी उन क्षेत्रों के आसपास टेप संदर्भ का उपयोग करना चाहिए जहां इसकी आवश्यकता है।

  • ग्रैडिएंट टेप, बैकवर्ड पास के दौरान उपयोग के लिए इनपुट और आउटपुट सहित मध्यवर्ती परिणामों को संग्रहीत करने के लिए मेमोरी का उपयोग करते हैं।

    दक्षता के लिए, कुछ ऑप्स (जैसे ReLU ) और उनके मध्यवर्ती परिणाम रखने की जरूरत नहीं है कि वे फॉरवर्ड पास दौरान कम कर दिए हैं कर रहे हैं। हालांकि, अगर आप का उपयोग persistent=True अपने टेप पर, कुछ भी त्याग दिया जाता है और अपने शिखर स्मृति उपयोग अधिक हो जाएगी।

गैर-अदिश लक्ष्यों के ग्रेडिएंट

एक ग्रेडिएंट मूल रूप से एक स्केलर पर एक ऑपरेशन है।

x = tf.Variable(2.0)
with tf.GradientTape(persistent=True) as tape:
  y0 = x**2
  y1 = 1 / x

print(tape.gradient(y0, x).numpy())
print(tape.gradient(y1, x).numpy())
4.0
-0.25

इस प्रकार, यदि आप कई लक्ष्यों के ग्रेडिएंट के लिए पूछते हैं, तो प्रत्येक स्रोत के लिए परिणाम है:

  • लक्ष्यों के योग का ग्रेडिएंट, या समकक्ष
  • प्रत्येक लक्ष्य के ग्रेडिएंट का योग।
x = tf.Variable(2.0)
with tf.GradientTape() as tape:
  y0 = x**2
  y1 = 1 / x

print(tape.gradient({'y0': y0, 'y1': y1}, x).numpy())
3.75

इसी तरह, यदि लक्ष्य अदिश नहीं हैं तो योग के ढाल की गणना की जाती है:

x = tf.Variable(2.)

with tf.GradientTape() as tape:
  y = x * [3., 4.]

print(tape.gradient(y, x).numpy())
7.0

इससे नुकसान के संग्रह के योग का ग्रेडिएंट या तत्व-वार हानि गणना के योग का ग्रेडिएंट लेना आसान हो जाता है।

आप प्रत्येक आइटम के लिए एक अलग ढाल की जरूरत है, का उल्लेख Jacobians

कुछ मामलों में आप जैकोबियन को छोड़ सकते हैं। तत्व-वार गणना के लिए, योग का ढाल प्रत्येक तत्व के व्युत्पन्न को उसके इनपुट-तत्व के संबंध में देता है, क्योंकि प्रत्येक तत्व स्वतंत्र है:

x = tf.linspace(-10.0, 10.0, 200+1)

with tf.GradientTape() as tape:
  tape.watch(x)
  y = tf.nn.sigmoid(x)

dy_dx = tape.gradient(y, x)
plt.plot(x, y, label='y')
plt.plot(x, dy_dx, label='dy/dx')
plt.legend()
_ = plt.xlabel('x')

पीएनजी

बहाव को काबू करें

एक ढाल टेप रिकॉर्ड क्योंकि आपरेशन के रूप में वे क्रियान्वित कर रहे हैं, अजगर नियंत्रण प्रवाह स्वाभाविक रूप से नियंत्रित किया जाता है (उदाहरण के लिए, if और while बयान)।

यहाँ एक अलग चर एक की प्रत्येक शाखा पर प्रयोग किया जाता है if । ग्रेडिएंट केवल उस वेरिएबल से जुड़ता है जिसका उपयोग किया गया था:

x = tf.constant(1.0)

v0 = tf.Variable(2.0)
v1 = tf.Variable(2.0)

with tf.GradientTape(persistent=True) as tape:
  tape.watch(x)
  if x > 0.0:
    result = v0
  else:
    result = v1**2 

dv0, dv1 = tape.gradient(result, [v0, v1])

print(dv0)
print(dv1)
tf.Tensor(1.0, shape=(), dtype=float32)
None

बस याद रखें कि नियंत्रण कथन स्वयं भिन्न नहीं हैं, इसलिए वे ढाल-आधारित अनुकूलक के लिए अदृश्य हैं।

के मूल्य के आधार x ऊपर के उदाहरण में, टेप या तो रिकॉर्ड result = v0 या result = v1**2 । के संबंध में ढाल x हमेशा होता है None

dx = tape.gradient(result, x)

print(dx)
None

की एक ढाल रही None

लक्ष्य एक स्रोत से जुड़ा नहीं है जब आप की एक ढाल मिल जाएगा None

x = tf.Variable(2.)
y = tf.Variable(3.)

with tf.GradientTape() as tape:
  z = y * y
print(tape.gradient(z, x))
None

यहाँ z स्पष्ट रूप से से जुड़ा नहीं है x , लेकिन वहाँ कई कम स्पष्ट तरीके कि एक ढाल अलग किया जा सकता है।

1. एक चर को एक टेंसर से बदला गया

पर अनुभाग में "को नियंत्रित करने के लिए क्या टेप देखता है" आपने देखा कि टेप स्वचालित रूप से एक देखेंगे tf.Variable नहीं बल्कि एक tf.Tensor

एक सामान्य त्रुटि अनजाने एक को बदलने के लिए है tf.Variable एक साथ tf.Tensor उपयोग करने के बजाए, Variable.assign अद्यतन करने के लिए tf.Variable । यहाँ एक उदाहरण है:

x = tf.Variable(2.0)

for epoch in range(2):
  with tf.GradientTape() as tape:
    y = x+1

  print(type(x).__name__, ":", tape.gradient(y, x))
  x = x + 1   # This should be `x.assign_add(1)`
ResourceVariable : tf.Tensor(1.0, shape=(), dtype=float32)
EagerTensor : None

2. क्या TensorFlow के बाहर गणना की गई थी

यदि गणना TensorFlow से बाहर निकलती है, तो टेप ग्रेडिएंट पथ को रिकॉर्ड नहीं कर सकता है। उदाहरण के लिए:

x = tf.Variable([[1.0, 2.0],
                 [3.0, 4.0]], dtype=tf.float32)

with tf.GradientTape() as tape:
  x2 = x**2

  # This step is calculated with NumPy
  y = np.mean(x2, axis=0)

  # Like most ops, reduce_mean will cast the NumPy array to a constant tensor
  # using `tf.convert_to_tensor`.
  y = tf.reduce_mean(y, axis=0)

print(tape.gradient(y, x))
None

3. एक पूर्णांक या स्ट्रिंग के माध्यम से ग्रेडिएंट लिया

पूर्णांक और तार भिन्न नहीं हैं। यदि कोई गणना पथ इन डेटा प्रकारों का उपयोग करता है तो कोई ग्रेडिएंट नहीं होगा।

कोई भी तार जो विभेदक हो जाने की उम्मीद है, लेकिन यह गलती से एक बनाना आसान है int निरंतर या चर यदि आप निर्दिष्ट नहीं करते dtype

x = tf.constant(10)

with tf.GradientTape() as g:
  g.watch(x)
  y = x * x

print(g.gradient(y, x))
WARNING:tensorflow:The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.int32
WARNING:tensorflow:The dtype of the target tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
WARNING:tensorflow:The dtype of the source tensor must be floating (e.g. tf.float32) when calling GradientTape.gradient, got tf.int32
None

TensorFlow स्वचालित रूप से प्रकारों के बीच नहीं डाला जाता है, इसलिए, व्यवहार में, आपको अक्सर एक लापता ग्रेडिएंट के बजाय एक प्रकार की त्रुटि मिलेगी।

4. एक स्टेटफुल ऑब्जेक्ट के माध्यम से ग्रेडिएंट लिया

राज्य ग्रेडिएंट रोकता है। जब आप किसी स्टेटफुल ऑब्जेक्ट से पढ़ते हैं, तो टेप केवल वर्तमान स्थिति का अवलोकन कर सकता है, न कि उस इतिहास को जो इसे आगे ले जाता है।

एक tf.Tensor अपरिवर्तनीय है। एक बार टेंसर बनने के बाद आप उसे बदल नहीं सकते। यह एक मूल्य है, लेकिन कोई राज्य नहीं है। सभी संचालन अब तक चर्चा भी राज्यविहीन हैं: एक के उत्पादन में tf.matmul केवल अपने आदानों पर निर्भर करता है।

एक tf.Variable आंतरिक राज्य के अपने मूल्य है। जब आप चर का उपयोग करते हैं, तो राज्य पढ़ा जाता है। एक चर के संबंध में एक ढाल की गणना करना सामान्य है, लेकिन चर की स्थिति ढाल की गणना को आगे पीछे जाने से रोकती है। उदाहरण के लिए:

x0 = tf.Variable(3.0)
x1 = tf.Variable(0.0)

with tf.GradientTape() as tape:
  # Update x1 = x1 + x0.
  x1.assign_add(x0)
  # The tape starts recording from x1.
  y = x1**2   # y = (x1 + x0)**2

# This doesn't work.
print(tape.gradient(y, x0))   #dy/dx0 = 2*(x1 + x0)
None

इसी तरह, tf.data.Dataset iterators और tf.queue रों स्टेटफुल हैं, और tensors कि उन्हें के माध्यम से पारित पर सभी ढ़ाल बंद हो जाएगा।

कोई ग्रेडिएंट पंजीकृत नहीं

कुछ tf.Operation रों गैर विभेदक होने के रूप में पंजीकृत हैं और वापस आ जाएगी None । अन्य लोग नहीं ढाल पंजीकृत किया है।

tf.raw_ops जो निम्न स्तर ऑप्स पंजीकृत ढ़ाल है पेज से पता चलता है।

आप एक नाव सेशन पंजीकृत टेप चुपचाप लौटने के बजाय एक त्रुटि फेंक होगा कोई ढाल है कि के माध्यम से एक ढाल लेने के लिए प्रयास करते हैं None । इस तरह आप जानते हैं कि कुछ गलत हो गया है।

उदाहरण के लिए, tf.image.adjust_contrast समारोह लपेटता raw_ops.AdjustContrastv2 है, जो एक ढाल हो सकता था लेकिन ढाल लागू नहीं किया गया:

image = tf.Variable([[[0.5, 0.0, 0.0]]])
delta = tf.Variable(0.1)

with tf.GradientTape() as tape:
  new_image = tf.image.adjust_contrast(image, delta)

try:
  print(tape.gradient(new_image, [image, delta]))
  assert False   # This should not happen.
except LookupError as e:
  print(f'{type(e).__name__}: {e}')
LookupError: gradient registry has no entry for: AdjustContrastv2

आप इस सेशन के माध्यम से अंतर करने के लिए की जरूरत है, आप या तो ढाल को लागू करने और यह रजिस्टर (का उपयोग करने की आवश्यकता होगी tf.RegisterGradient ) या समारोह अन्य ऑप्स का उपयोग कर फिर से लागू करते हैं।

शून्य के बजाय शून्य

कुछ मामलों में यह करने के बजाय 0 पाने के लिए सुविधाजनक होगा None असंबद्ध ढ़ाल के लिए। आप तय कर सकते आप उपयोग कर असंबद्ध ढ़ाल है जब क्या लौटने के लिए unconnected_gradients तर्क:

x = tf.Variable([2., 2.])
y = tf.Variable(3.)

with tf.GradientTape() as tape:
  z = y**2
print(tape.gradient(z, x, unconnected_gradients=tf.UnconnectedGradients.ZERO))
tf.Tensor([0. 0.], shape=(2,), dtype=float32)