深度学习中nan和inf的解决

目录

1、 nan 和inf产生原因

2、解决方法

2.1、学习率要设置小一些

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

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

2.4、对loss部分做处理

2.5、对梯度的处理

2.6 手动梯度重写

2.7、其它操作

总结


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

pytorch
torch.isnan(x)

2.4、对loss部分做处理

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

训练深度学习网络时候,出现Nan是什么原因,怎么才能避免? - 知乎

可以看到有一个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
)

比如设置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这种方式来处理。总的处理方式可以按照我写的博客来依次使用。

 参考文章: 深度学习中nan和inf的解决_dddeee的专栏-CSDN博客_nan和inf

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值