深度学习中nan和inf的解决

1、 nan 和inf产生原因

搭建神经网络后产生的,在训练早期,模型参数可能不是很合适,会出现梯度消失和爆炸的情况,特别是有lstm,rnn这类网络的情况。nan 是not a number ,inf是无穷大。比如求损失函数会用到log,如果输入接近0,那么结果就是inf。

2、解决方法

2.1 学习率要设置小一些

2.2使用带上限的激活函数,如tf.nn.relu6

也就是对输出做了限制

2.3检查输入数据中是否有nan或inf

x = tf.constant([5.0, 4.8, 6.8, np.inf, np.nan])
x=tf.math.is_finite(x) #[True, True, True, False, False]
test=tf.reduce_all(x)  # False
assert test

2.4对loss部分做处理

loss中出现nan或inf时,用loss对参数求导显然是有问题的
logits处做处理

y_=[0.0,1]
y_conv=[0.0,1]
cross_entropy1 = y_*tf.math.log(y_conv) #输出<tf.Tensor: id=34305, shape=(2,), dtype=float32, numpy=array([nan,  0.], dtype=float32)>
cross_entropy2 =-tf.reduce_sum(cross_entropy1)#输出<tf.Tensor: id=34308, shape=(), dtype=float32, numpy=nan>
#tf.nn.softmax_cross_entropy_with_logits

可以看到有一个nan,处理方法是:

y_=[0.0,1]
y_conv=[0.0,1]
y_conv=tf.clip_by_value(y_conv,1e-10,1.0) #使用这个数值来做处理
cross_entropy1 = y_*tf.math.log(y_conv) 
cross_entropy2 =-tf.reduce_sum(cross_entropy1)

另一种处理方式:

def mask_nan(x,value):
    '''
    用value值来代替nan 或inf
    '''
    x_values= tf.add(tf.zeros_like(x),value)
	mask = tf.math.is_finite(x)
	y = tf.where(mask,x,x_values)
	return y
y_=[0.0,1]
y_conv=[0.0,1]
cross_entropy1 = y_*tf.math.log(y_conv) 
cross_entropy1 = mask_nan(cross_entropy1,3)#输出<tf.Tensor: id=34332, shape=(2,), dtype=float32, numpy=array([3., 0.], dtype=float32)>
cross_entropy2 =-tf.reduce_sum(cross_entropy1)

2.5 对梯度的处理

def log1pexp(x):
    return tf.math.log(1 + tf.exp(x))

def grad_log1pexp(x):
    with tf.GradientTape() as tape:
        tape.watch(x)
        value = log1pexp(x)
    return tape.gradient(value, x)
grad_log1pexp(tf.constant(0.)).numpy()# 输出0.5
grad_log1pexp(tf.constant(100.)).numpy()#输出nan
#或者使用
theoretical, numerical=tf.test.compute_gradient(log1pexp,[tf.constant(0.)])
print(theoretical, numerical)#输出(array([[0.5]], dtype=float32),) (array([[0.49996373]], dtype=float32),)
theoretical, numerical=tf.test.compute_gradient(log1pexp,[tf.constant(100.)])
print(theoretical, numerical)#输出(array([[nan]], dtype=float32),) (array([[nan]], dtype=float32),)

可以看到x = 100 由于数值的不稳定性而失败。
所以在求出梯度后,反向传播前,我们可以对gradient处理:

def log1pexp(x):
    return tf.math.log(1 + tf.exp(x))

def grad_log1pexp(x):
    with tf.GradientTape() as tape:
        tape.watch(x)
        value = log1pexp(x)
        gradients = tape.gradient(value, x)
    return gradients
 #有三种处理梯度的方法,先用合适的处理就行。
 gradients,_ = tf.clip_by_global_norm(gradients,clip_norm=1)
 gradients = tf.clip_by_value(gradients,clip_value_min=0,clip_value_max=1)
 gradients = tf.clip_by_norm(gradients,clip_norm=1)
 #处理完之后,做如下类似操作
 optimizer = tf.keras.optimizers.Adam(1e-3)
 optimizer.apply_gradients(zip(gradients, variables))

以上是tensorflow的方法,其它框架类似处理。

2.6 手动梯度重写

@tf.custom_gradient
def log1pexp(x):
    e = tf.exp(x)
    def grad(dy):
        return dy * (1 - 1 / (1 + e)) #dy是上一层的梯度,用链式法则,后边乘的是自己产生的梯度
    return tf.math.log(1 + e), grad

def grad_log1pexp(x):
    with tf.GradientTape() as tape:
        tape.watch(x)
        value = log1pexp(x)
    return tape.gradient(value, x)

2.7 其它操作

限制梯度更新过程中,梯度的最大范数是2. 还有其它的constrants参见官方文档

from tensorflow.keras.constraints import max_norm
model.add(Dense(64, kernel_constraint=max_norm(2.)))

这个操作不是对梯度的操作,而是在反向更新后,kernel的norm变的不符合kernel_constraint中的要求后,直接对kernel作改变。

还有一种方法:

tf.keras.optimizers.Adam(
    learning_rate=0.001,
    beta_1=0.9,
    beta_2=0.999,
    epsilon=1e-07,
    amsgrad=False,
    name='Adam',
    **kwargs
)

**kwargs: keyword arguments. Allowed to be {clipnorm, clipvalue, lr, decay}. clipnorm is clip gradients by norm; clipvalue is clip gradients by value, decay is included for backward compatibility to allow time inverse decay of learning rate. lr is included for backward compatibility, recommended to use learning_rate instead.
有clipnorm和clipvalue.
比如设置clipnorm=1,意味着,如查一个变量的梯度的范数超过了1,那么这个梯度就会用梯度正则化,grads/norm(grads),来使梯度的范数变成1.
比如clipvalue=0.5,意味着,梯度中的值小于-0.5,就会置为-0.5,如果大于0.5那么就置为0.5.
来个例子:

from sklearn.datasets import make_regression
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from matplotlib import pyplot
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))
# compile model
opt = SGD(lr=0.01, momentum=0.9, clipvalue=5.0)
model.compile(loss='mean_squared_error', optimizer=opt)
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)
# evaluate the model
train_mse = model.evaluate(trainX, trainy, verbose=0)
test_mse = model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))
# plot loss during training
pyplot.title('Mean Squared Error')
pyplot.plot(history.history['loss'], label='train')
pyplot.plot(history.history['val_loss'], label='test')
pyplot.legend()
pyplot.show()

opt = SGD(lr=0.01, momentum=0.9, clipvalue=5.0) 其中加入clipvalue是可以收敛训练的,但如果不加则loss等于Nan
但同时发现个问题,在keras配合tensorflow1.x时这个参数是起作用的,但在tensorflow 2.x中使用tf.keras则没有效果。
所以 tensorflow2.x中 对梯度进行clip的方法是,如下:

import tensorflow as tf
import numpy as np
from sklearn.datasets import make_regression
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import SGD
from matplotlib import pyplot
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)
X = X.astype(np.float32)
y = y.astype(np.float32)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='linear'))
# compile model
# opt = SGD(lr=0.01, momentum=0.9)
opt = SGD(lr=0.01, momentum=0.9)
train_ds = tf.data.Dataset.from_tensor_slices((trainX,trainy)).shuffle(10000).batch(32)
test_ds = tf.data.Dataset.from_tensor_slices((testX,testy)).batch(32)
train_loss = tf.keras.metrics.Mean()
test_loss = tf.keras.metrics.Mean()
epochs = 100
for epoch in range(epochs):
    for x,y in train_ds:
        with tf.GradientTape() as tape:
            y_=model(x)
            loss = tf.keras.losses.MeanSquaredError()(y,y_)
        gradients = tape.gradient(loss,model.trainable_variables)
        gradients = [tf.clip_by_norm(gradient,tf.constant(1.)) for gradient in gradients]# 这一行很重要
        opt.apply_gradients(zip(gradients,model.trainable_variables))
        train_loss(loss)
    print(train_loss.result().numpy())
    for x,y,in test_ds:
        y_=model(x)
        loss = tf.keras.losses.MeanSquaredError()(y,y_)
        test_loss(loss)
    print(test_loss.result().numpy())
    train_loss.reset_states()
    test_loss.reset_states()

总结

以后肯定会向tensorflow2.x走,所以可以使用tf.clip_by_XXX来专门来对梯度处理,或都是对loss使用我定义的mask_nan这种方式来处理。总的处理方式可以按照我写的博客来依次使用。

参考内容:

  • https://machinelearningmastery.com/how-to-avoid-exploding-gradients-in-neural-networks-with-gradient-clipping/
  • tensorflow官网
  • 8
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 这个警告是指在输入张量发现了 NaNInfNaN 表示不是数字(Not a Number),Inf 表示无穷大(Infinity)。在机器学习深度学习,这通常表示模型训练过程出现了问题,例如数值溢出或未处理的缺失值。为了确保模型的正确性,需要对输入数据进行检查并解决这个问题。 ### 回答2: 在机器学习深度学习模型的训练过程,我们通常会将大量的数据输入到模型进行学习。然而,由于训练数据的质量的问题或者数据处理的问题等,我们有时会在数据发现一些不合理的值,例如“NaN”或“Inf”。当出现这些问题时,我们通常会从模型的输出信息或者训练过程的日志看到这样的警告信息:“Warning: root: NaN or Inf found in input tensor.”。这个警告信息告诉我们在输入张量存在NaNInf。 首先,NaN代表不是一个数,通常出现在无穷大的运算,例如0/0。Inf代表正无穷或负无穷,通常出现在除以0的运算。当我们在处理数据时,如果一个输入张量包含NaNInf,模型的输出结果很可能是不可靠的。 发现这种问题时,我们应该尽快查找原因以及修复这个问题。通常找到这个问题的方法包括: 1. 检查数据源:首先我们应该检查我们的输入数据是否正确。如果数据源出现了问题,像数据文件损坏或数据采集出现漏洞,将大大影响整个模型的训练。 2. 检查数据预处理过程:我们的数据可能需要在输入到模型之前进行预处理,这可能就是数据出现问题的地方。例如,我们可能会对某些数据进行标准化处理,但是如果我们在标准化过程出现了0作为除数的情况,就会出现Inf。 3. 检查模型的架构和参数:如果实际数据值本身不可能包含NaNInf,则问题可能出现在模型架构、参数或模型的实现。 所以,当我们在训练模型时看到这个警告信息时,我们需要仔细检查我们的数据集,确保数据质量没有问题,并且要检查模型的架构和参数是否存在问题。正确的解决方法将有助于我们的模型在训练过程更快地走向成功。 ### 回答3: 这个警告是在深度学习模型出现的常见问题。它表示在输入张量出现了NaN(非数)或Inf(无穷大)的值。它可能是由于训练数据存在错误或缺失值等问题导致的。 在深度学习NaNInf值会影响计算结果,因为它们可能会在模型传播,导致输出结果不稳定。这可能导致训练不收敛或模型在测试时产生错误的结果。 为了解决这个问题,我们可以检查输入数据是否存在缺失值或无效值,并进行必要的数据清理和预处理。我们还可以使用数据填充或删除缺失值的方法,避免NaNInf值的出现。 此外,我们可以通过添加激活函数,正则化或减少学习率等技术来防止模型出现NaNInf值。此外,我们可以添加断言或assert语句来检查模型是否输出了异常值。这样可以帮助我们及时发现问题并解决它们。 最后,我们应该认真处理这个警告并采取必要的措施来确保我们的深度学习模型可以正常运行,并产生准确可靠的结果。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值