# We can convert any tensor object to `ndarray` by calling the `numpy()` method y = tf.constant([1, 2, 3], dtype=tf.int8).numpy() print(f"`y` is now a {type(y)} object and have a value == {y}")
结果输出为:
1
`y` is now a <class 'numpy.ndarray'> object and have a value == [1 2 3]
# Check all the properties of a tensor object print(f"Shape of x : {x.shape}") print(f"Another method to obtain the shape using `tf.shape(..)`: {tf.shape(x)}")
print(f"\nRank of the tensor: {x.ndim}") print(f"dtype of the tensor: {x.dtype}") print(f"Total size of the tensor: {tf.size(x)}") print(f"Values of the tensor: {x.numpy()}")
结果输出为:
1 2 3 4 5 6 7 8 9 10 11
TypeError 'tensorflow.python.framework.ops.EagerTensor' object does not support item assignment
tf.Tensor([1 1], shape=(2,), dtype=int8) TypeError 'tensorflow.python.framework.ops.EagerTensor' object does not support item assignment
Shape of x : (2,) Another method to obtain the shape using `tf.shape(..)`: [2] Rank of the tensor: 1 dtype of the tensor: <dtype: 'int8'> Total size of the tensor: 2 Values of the tensor: [1 1]
无法在Tensor对象中进行赋值有点令人沮丧。那有什么办法解决呢?我发现的一直适用于我的用例的最佳方法是创建一个掩码或使用tf.tensor_scatter_nd_update。让我们看一个例子。 Original tensor -> [1, 2, 3, 4, 5]和Output tensor we want -> [1, 200, 3, 400, 5]
# Create a tensor first. Here is another way x = tf.cast([1, 2, 3, 4, 5], dtype=tf.float32) print("Original tensor: ", x)
mask = x%2 == 0 print("Original mask: ", mask)
mask = tf.cast(mask, dtype=x.dtype) print("Mask casted to original tensor type: ", mask)
# Some kind of operation on an tensor that is of same size # or broadcastable to the original tensor. Here we will simply # use the range object to create that tensor temp = tf.cast(tf.range(1, 6) * 100, dtype=x.dtype)
# This works! arr = np.random.randint(5, size=(5,), dtype=np.int32) print("Numpy array: ", arr) print("Accessing numpy array elements based on a condition with irregular strides", arr[[1, 4]])
# This doesn't work try: print("Accessing tensor elements based on a condition with irregular strides", x[[1, 4]]) except Exception as ex: print(type(ex).__name__, ex)
结果输出为:
1 2 3
Numpy array: [3 4 4 0 1] Accessing numpy array elements based on a condition with irregular strides [4 1] InvalidArgumentError Index out of range using input dim 1; input has only 1 dims [Op:StridedSlice] name: strided_slice
# An example with a python list y = tf.convert_to_tensor([1, 2, 3]) print("Tensor from python list: ", y)
# An example with a ndarray y = tf.convert_to_tensor(np.array([1, 2, 3])) print("Tensor from ndarray: ", y)
# An example with symbolic tensors with tf.compat.v1.Graph().as_default(): y = tf.convert_to_tensor(tf.compat.v1.placeholder(shape=[None, None, None], dtype=tf.int32)) print("Tensor from python list: ", y)
结果输出为:
1 2 3
Tensor from python list: tf.Tensor([1 2 3], shape=(3,), dtype=int32) Tensor from ndarray: tf.Tensor([1 2 3], shape=(3,), dtype=int64) Tensor from python list: Tensor("Placeholder:0", shape=(None, None, None), dtype=int32)
String Tensors
1 2 3 4 5 6 7 8 9 10 11
# String as a tensor object with dtype==tf.string string = tf.constant("abc", dtype=tf.string) print("String tensor: ", string)
# String tensors are atomic and non-indexable. # This doen't work as expected! print("\nAccessing second element of the string") try: print(string[1]) except Exception as ex: print(type(ex).__name__, ex)
结果输出为:
1 2 3
String tensor: tf.Tensor(b'abc', shape=(), dtype=string) Accessing second element of the string InvalidArgumentError Index out of range using input dim 0; input has only 0 dims [Op:StridedSlice] name: strided_slice
# This won't work print("Trying to create tensor from above python sequence\n") try: z = tf.constant(y) except Exception as ex: print(type(ex).__name__, ex)
结果输出为:
1 2 3
Creating ragged tensor from python sequence: <tf.RaggedTensor [[1, 2, 3], [4, 5], [6]]> Trying to create tensor from above python sequence ValueError Can't convert non-rectangular Python sequence to Tensor.
Sparse tensors
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# Let's say you have a an array like this one # [[1 0 0] # [0 2 0] # [0 0 3]] # If there are too many zeros in your `huge` tensor, then it is wise to use `sparse` # tensors instead of `dense` one. Let's say how to create this one. We need to specify: # 1. Indices where our values are # 2. The values # 3. The actual shape
# Variables with an integer value of 2 as initial value x = tf.Variable(2) x # Nested list as initial value y = tf.Variable([[2, 3]], dtype=tf.int32) y # Tuples also work but beware it isn't the same as a nested list. # Check the difference between the current output and the previous cell output w = tf.Variable(((2, 3)), dtype=tf.int32) w # You can even pass a tensor object as an initial value t = tf.constant([1, 2,], dtype=tf.int32) z = tf.Variable(t) z
# An interesting thing to note. # You can't change the values of the tensor `t` in the above example # but you can change the values of the variable created using it
# This won't work try: t[0] = 1 except Exception as ex: print(type(ex).__name__, ex) # This also won't work try: z[0] = 10 except Exception as ex: print(type(ex).__name__, ex) # This works though print("\nOriginal variable: ", z) z[0].assign(5) print("Updated variable: ", z)
TypeError 'tensorflow.python.framework.ops.EagerTensor' object does not support item assignment TypeError 'ResourceVariable' object does not support item assignment
# Most of the properties that we saw for tensors in part1 are the same for variables print(f"Shape of variable : {z.shape}") print(f"Another method to obtain the shape using `tf.shape(..)`: {tf.shape(z)}")
print(f"dtype of the variable: {z.dtype}") print(f"Total size of the variable: {tf.size(z)}") print(f"Values of the variable: {z.numpy()}")
try: print(f"Rank: {z.ndim}") except Exception as ex: print(type(ex).__name__, ex)
# Crap! How to find out the no of dimensions then? print(f"Rank: {tf.shape(z)} or like this {z.shape}")
# Whatever operator overloading is available for a Tensor, is also available for a Variable # We have a tensor `t` and a varibale `z`. t = tf.constant([1, 2,], dtype=tf.int32) z = tf.Variable(t) print("Tensor t: ", t) print("Variable z: ", z)
print("\nThis works: ", (t+5)) print("So does this: ", (z +5))
print(f"Another example just for demonstration: {(t*5).numpy()}, {(z*5).numpy()}")
# Gather works as well tf.gather(z, indices=[1])
# Here is another interesting difference between the properties of # a tensor and a variable try: print("Is variable z trainable? ", z.trainable) print("Is tensor t trainable? ", t.trainable) except Exception as ex: print(type(ex).__name__, ex)
Shape of variable : (2,) Another method to obtain the shape using `tf.shape(..)`: [2] dtype of the variable: <dtype: 'int32'> Total size of the variable: 2 Values of the variable: [5 2]
AttributeError 'ResourceVariable' object has no attribute 'ndim'
This works: tf.Tensor([6 7], shape=(2,), dtype=int32) So does this: tf.Tensor([6 7], shape=(2,), dtype=int32) Another example just for demonstration: [ 5 10], [ 5 10]
# Create a variable instance z = tf.Variable([1, 2], dtype=tf.int32, name="z") print(f"Variable {z.name}: ", z)
# Can we change the dtype while changing the values? try: z.assign([1.0, 2.0]) except Exception as ex: print("\nOh dear...what have you done!") print(type(ex).__name__, ex) # Can we change the shape while assigning a new value? try: z.assign([1, 2, 3]) except Exception as ex: print("\nAre you thinking clearly?") print(type(ex).__name__, ex) # A way to create variable with an arbitrary shape x = tf.Variable(5, dtype=tf.int32, shape=tf.TensorShape(None), name="x") print("\nOriginal Variable x: ", x)
# Assign a proper value with a defined shape x.assign([1, 2, 3]) print("Modified Variable x: ", x)
# Try assigning a value with a diff shape now. try: x.assign([[1, 2, 3], [4, 5, 6]]) print("\nThis works!!") print("Variable value modified with a diff shape: ", x) except Exception as ex: print("\nDid you forget what we just learned?") print(type(ex).__name__, ex)
This works!! Variable value modified with a diff shape: <tf.Variable 'x:0' shape=<unknown> dtype=int32, numpy= array([[1, 2, 3],[4, 5, 6]], dtype=int32)>
try: dx = tape.gradient(z, x) dy = tape.gradient(z, y)
print(f"Gradient of z wrt x: {dx}") print(f"Gradient of z wrt y: {dy}") except Exception as ex: print("ERROR! ERROR! ERROR!\n") print(type(ex).__name__, ex)
结果输出为:
1 2
ERROR! ERROR! ERROR! RuntimeError A non-persistent GradientTape can only be used tocompute one set of gradients (or jacobians)
# Set the persistent argument with tf.GradientTape(persistent=True) as tape: z = x * y
try: dx = tape.gradient(z, x) dy = tape.gradient(z, y)
print(f"Gradient of z wrt x: {dx}") print(f"Gradient of z wrt y: {dy}") except Exception as ex: print("ERROR! ERROR! ERROR!\n") print(type(ex).__name__, ex)
结果输出为:
1 2
Gradient of z wrt x: 4.0 Gradient of z wrt y: 3.0
如果一个变量不可训练,会如何?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# What if one of the Variables is non-trainable? # Let's make y non-trainable in the above example and run # the computation again
x = tf.Variable(3.0) y = tf.Variable(4.0, trainable=False)
with tf.GradientTape() as tape: z = x * y dx, dy = tape.gradient(z, [x, y])
print(f"Variable x: {x}") print(f"Is x trainable?: {x.trainable}") print(f"\nVariable y: {y}") print(f"Is y trainable?: {y.trainable}\n")
print(f"Gradient of z wrt x: {dx}") print(f"Gradient of z wrt y: {dy}")
结果输出为:
1 2 3 4 5 6 7 8
Variable x: <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=3.0> Is x trainable?: True
Variable y: <tf.Variable 'Variable:0' shape=() dtype=float32, numpy=4.0> Is y trainable?: False
Gradient of z wrt x: 4.0 Gradient of z wrt y: None
print(f"Gradient of z wrt x: {dz_dx}") print(f"Gradient of z wrt y: {dz_dy}\n") print(f"Gradient of zz wrt x: {dzz_dx}") print(f"Gradient of zz wrt y: {dzz_dy}")
结果输出为:
1 2 3 4 5
Gradient of z wrt x: 4.0 Gradient of z wrt y: 3.0
Gradient of zz wrt x: None Gradient of zz wrt y: None
停止梯度流的更好方法是使用tf.stop_gradient(...)。为什么?
不需要访问tape
干净且具有更好的语义
1 2 3 4 5 6 7 8 9 10
# The better way! x = tf.Variable(3.0, name="x") y = tf.Variable(4.0, name="y")
with tf.GradientTape() as tape: z = x * tf.stop_gradient(y)
dz_dx, dz_dy = tape.gradient(z, [x, y]) print(f"Gradient of z wrt x: {dz_dx}") print(f"Gradient of z wrt y: {dz_dy}")
结果输出为:
1 2
Gradient of z wrt x: 4.0 Gradient of z wrt y: None
# Both variables are trainable x = tf.Variable(3.0, name="x") y = tf.Variable(4.0, name="y")
# Telling the tape: Hey! I will tell you what to record. # Don't start recording automatically! with tf.GradientTape(watch_accessed_variables=False) as tape: # Watch x but not y tape.watch(x) z = x * y
dz_dx, dz_dy = tape.gradient(z, [x, y]) print(f"Gradient of z wrt x: {dz_dx}") print(f"Gradient of z wrt y: {dz_dy}")
结果输出为:
1 2
Gradient of z wrt x: 4.0 Gradient of z wrt y: None
# What if something that you wanted to watch, # wasn't present in the computation done inside the context?
x = tf.Variable(3.0, name="x") y = tf.Variable(4.0, name="y") t = tf.Variable(5.0, name="t")
# Telling the tape: Hey! I will tell you what to record. # Don't start recording automatically! with tf.GradientTape(watch_accessed_variables=False) as tape: # Watch x but not y tape.watch(x) z = x * y # `t` isn't involved in any computation here # but what if we want to record it as well tape.watch(t)
print("Tape watching only these objects that you asked it to watch") for var in tape.watched_variables(): print(f"{var.name} and it's value is {var.numpy()}")
结果输出为:
1 2 3
Tape watching only these objects that you asked it to watch x:0 and it's value is 3.0 t:0 and it's value is 5.0
Multiple Tapes
您可以使用多个GradientTape来记录不同的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
x = tf.Variable(3.0, name="x") y = tf.Variable(4.0, name="y")
with tf.GradientTape() as tape_for_x, tf.GradientTape() as tape_for_y: # Watching different variables with different tapes tape_for_x.watch(x) tape_for_y.watch(y) z = x * y
dz_dx = tape_for_x.gradient(z, x) dz_dy = tape_for_y.gradient(z, y) print(f"Gradient of z wrt x: {dz_dx}") print(f"Gradient of z wrt y: {dz_dy}")
with tf.GradientTape() as tape1: with tf.GradientTape() as tape0: y = x * x * x first_order_grad = tape0.gradient(y, x) second_order_grad = tape1.gradient(first_order_grad, x)
print(f"Variable x: {x.numpy()}") print("\nEquation is y = x^3") print(f"First Order Gradient wrt x (3 * x^2): {first_order_grad}") print(f"Second Order Gradient wrt x (6^x): {second_order_grad}")
结果输出为:
1 2 3 4
Variable x: 3.0 Equation is y = x^3 First Order Gradient wrt x (3 * x^2): 27.0 Second Order Gradient wrt x (6^x): 18.0
with tf.GradientTape() as tape: # Change the state of x by making x = x + y x.assign_add(y) # Let's do some computation e.g z = x * x # This is equivalent to z = (x + y) * (x + y) because of above assign_add z = x * x dy = tape.gradient(z, y) print("Gradients of z wrt y: ", dy)