以下代码段来自一段相当大的代码,但希望我能提供所有必要的信息:
y2 = tf.matmul(y1,ymask)
dist = tf.norm(ystar-y2,axis = 0)
y1和y2是128x30,ymask是30x30。 ystar是128x30。 dist是1x30。当ymask是单位矩阵时,一切正常。但是当我将它设置为全零时,除了沿着对角线的单个1(以便将所有列设置为y2中的一个为零),使用tf得到相对于y2的dist的渐变的nans。渐变(dist,[y2])。 dist的具体值为[0,0,7.9,0,...],所有ystar-y2值都在第三列的范围(-1,1)附近,其他地方为零。
我很困惑为什么在这里会发生数字问题,因为没有日志或分区,这是否会下溢?我在数学中遗漏了什么吗?
对于上下文,我这样做是为了尝试使用整个网络一次一个地训练y的各个维度。
要重现的较长版本:
import tensorflow as tf
import numpy as np
import pandas as pd
batchSize = 128
eta = 0.8
tasks = 30
imageSize = 32**2
groups = 3
tasksPerGroup = 10
trainDatapoints = 10000
w = np.zeros([imageSize, groups * tasksPerGroup])
toyIndex = 0
for toyLoop in range(groups):
m = np.ones([imageSize]) * np.random.randn(imageSize)
for taskLoop in range(tasksPerGroup):
w[:, toyIndex] = m * 0.1 * np.random.randn(1)
toyIndex += 1
xRand = np.random.normal(0, 0.5, (trainDatapoints, imageSize))
taskLabels = np.matmul(xRand, w) + np.random.normal(0,0.5,(trainDatapoints, groups * tasksPerGroup))
DF = np.concatenate((xRand, taskLabels), axis=1)
trainDF = pd.DataFrame(DF[:trainDatapoints, ])
# define graph variables
x = tf.placeholder(tf.float32, [None, imageSize])
W = tf.Variable(tf.zeros([imageSize, tasks]))
b = tf.Variable(tf.zeros([tasks]))
ystar = tf.placeholder(tf.float32, [None, tasks])
ymask = tf.placeholder(tf.float32, [tasks, tasks])
dataLength = tf.cast(tf.shape(ystar)[0],dtype=tf.float32)
y1 = tf.matmul(x, W) + b
y2 = tf.matmul(y1,ymask)
dist = tf.norm(ystar-y2,axis=0)
mse = tf.reciprocal(dataLength) * tf.reduce_mean(tf.square(dist))
grads = tf.gradients(dist, [y2])
trainStep = tf.train.GradientDescentOptimizer(eta).minimize(mse)
# build graph
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
randTask = np.random.randint(0, 9)
ymaskIn = np.zeros([tasks, tasks])
ymaskIn[randTask, randTask] = 1
batch = trainDF.sample(batchSize)
batch_xs = batch.iloc[:, :imageSize]
batch_ys = np.zeros([batchSize, tasks])
batch_ys[:, randTask] = batch.iloc[:, imageSize + randTask]
gradOut = sess.run(grads, feed_dict={x: batch_xs, ystar: batch_ys, ymask: ymaskIn})
sess.run(trainStep, feed_dict={x: batch_xs, ystar: batch_ys, ymask:ymaskIn})
答案 0 :(得分:0)
这是一个非常简单的复制品:
import tensorflow as tf
with tf.Graph().as_default():
y = tf.zeros(shape=[1], dtype=tf.float32)
dist = tf.norm(y,axis=0)
(grad,) = tf.gradients(dist, [y])
with tf.Session():
print(grad.eval())
打印:
[ nan]
问题在于tf.norm
计算sum(x**2)**0.5
。渐变为x / sum(x**2) ** 0.5
(参见例如https://math.stackexchange.com/a/84333),因此当sum(x**2)
为零时,我们将除以零。
就特殊情况而言,没有太多工作要做:x
接近全零的梯度取决于它接近的方向。例如,如果x
是单元素向量,则x
接近0的限制可以是1或-1,具体取决于它接近的零边。
因此,就解决方案而言,您只需添加一个小epsilon
:
import tensorflow as tf
def safe_norm(x, epsilon=1e-12, axis=None):
return tf.sqrt(tf.reduce_sum(x ** 2, axis=axis) + epsilon)
with tf.Graph().as_default():
y = tf.constant([0.])
dist = safe_norm(y,axis=0)
(grad,) = tf.gradients(dist, [y])
with tf.Session():
print(grad.eval())
打印:
[ 0.]
请注意,这实际上不是欧几里德规范。只要输入远大于epsilon
,它就是一个很好的近似值。