神经网络可以处理冗余输入吗?

Posted

技术标签:

【中文标题】神经网络可以处理冗余输入吗?【英文标题】:Can neural networks handle redundant inputs? 【发布时间】:2020-08-08 15:23:47 【问题描述】:

我有一个全连接的神经网络,每一层的神经元数量如下[4, 20, 20, 20, ..., 1]。我正在使用 TensorFlow,4 个实值输入对应于空间和时间的特定点,即 (x, y, z, t),1 个实值输出对应于该点的温度。损失函数只是我的预测温度与 (x, y, z, t) 中该点的实际温度之间的均方误差。我有一组训练数据点,其输入结构如下:


(x,y,z,t):

(0.11,0.12,1.00,0.41)

(0.34,0.43,1.00,0.92)

(0.01,0.25,1.00,0.65)

...

(0.71,0.32,1.00,0.49)

(0.31,0.22,1.00,0.01)

(0.21,0.13,1.00,0.71)


也就是说,你会注意到训练数据在z中都有相同的冗余值,但xyt一般不冗余。然而,我发现由于冗余,我的神经网络无法在这些数据上进行训练。特别是,每次我开始训练神经网络时,它似乎都失败了,损失函数变成了nan。但是,如果我改变神经网络的结构,使得每一层的神经元数量为[3, 20, 20, 20, ..., 1],即现在数据点只对应于 (x, y, t) 的输入,那么一切都可以正常工作,训练就是全部正确的。但是有什么办法可以克服这个问题吗? (注意:任何变量是否相同都会发生,例如xyt 可能是多余的并导致此错误。)我还尝试了不同的激活函数(例如 ReLU)并改变网络中的层数和神经元数,但这些更改并不能解决问题。

我的问题:有没有办法在保持冗余z 作为输入的同时训练神经网络?我目前正在考虑的特定训练数据集恰好具有所有z 冗余,但总的来说,我将来会有来自不同z 的数据。因此,寻求一种确保神经网络能够在当前时刻稳健地处理输入的方法。

下面编码了一个最小的工作示例。运行此示例时,损失输出为 nan,但如果您只是取消注释第 12 行中的 x_z 以确保 x_z 现在有变化,则不再有任何问题。但这不是解决方案,因为目标是使用原始的 x_z 和所有常量值。

import numpy as np 
import tensorflow as tf

end_it = 10000 #number of iterations
frac_train = 1.0 #randomly sampled fraction of data to create training set
frac_sample_train = 0.1 #randomly sampled fraction of data from training set to train in batches
layers = [4, 20, 20, 20, 20, 20, 20, 20, 20, 1]
len_data = 10000
x_x = np.array([np.linspace(0.,1.,len_data)])
x_y = np.array([np.linspace(0.,1.,len_data)])
x_z = np.array([np.ones(len_data)*1.0])
#x_z = np.array([np.linspace(0.,1.,len_data)])
x_t = np.array([np.linspace(0.,1.,len_data)])
y_true = np.array([np.linspace(-1.,1.,len_data)])

N_train = int(frac_train*len_data)
idx = np.random.choice(len_data, N_train, replace=False)

x_train = x_x.T[idx,:]
y_train = x_y.T[idx,:]
z_train = x_z.T[idx,:]
t_train = x_t.T[idx,:]
v1_train = y_true.T[idx,:] 

sample_batch_size = int(frac_sample_train*N_train)

np.random.seed(1234)
tf.set_random_seed(1234)
import logging
logging.getLogger('tensorflow').setLevel(logging.ERROR)
tf.logging.set_verbosity(tf.logging.ERROR)

class NeuralNet:
    def __init__(self, x, y, z, t, v1, layers):
        X = np.concatenate([x, y, z, t], 1)  
        self.lb = X.min(0)
        self.ub = X.max(0)
        self.X = X
        self.x = X[:,0:1]
        self.y = X[:,1:2]
        self.z = X[:,2:3]
        self.t = X[:,3:4]
        self.v1 = v1 
        self.layers = layers 
        self.weights, self.biases = self.initialize_NN(layers) 
        self.sess = tf.Session(config=tf.ConfigProto(allow_soft_placement=False,
                                                     log_device_placement=False)) 
        self.x_tf = tf.placeholder(tf.float32, shape=[None, self.x.shape[1]])
        self.y_tf = tf.placeholder(tf.float32, shape=[None, self.y.shape[1]])
        self.z_tf = tf.placeholder(tf.float32, shape=[None, self.z.shape[1]])
        self.t_tf = tf.placeholder(tf.float32, shape=[None, self.t.shape[1]])
        self.v1_tf = tf.placeholder(tf.float32, shape=[None, self.v1.shape[1]])  
        self.v1_pred = self.net(self.x_tf, self.y_tf, self.z_tf, self.t_tf) 
        self.loss = tf.reduce_mean(tf.square(self.v1_tf - self.v1_pred)) 
        self.optimizer = tf.contrib.opt.ScipyOptimizerInterface(self.loss,
                                                                method = 'L-BFGS-B',
                                                                options = 'maxiter': 50,
                                                                           'maxfun': 50000,
                                                                           'maxcor': 50,
                                                                           'maxls': 50,
                                                                           'ftol' : 1.0 * np.finfo(float).eps)
        init = tf.global_variables_initializer()  
        self.sess.run(init)
    def initialize_NN(self, layers):
        weights = []
        biases = []
        num_layers = len(layers)
        for l in range(0,num_layers-1):
            W = self.xavier_init(size=[layers[l], layers[l+1]])
            b = tf.Variable(tf.zeros([1,layers[l+1]], dtype=tf.float32), dtype=tf.float32)
            weights.append(W)
            biases.append(b) 
        return weights, biases
    def xavier_init(self, size):
        in_dim = size[0]
        out_dim = size[1]
        xavier_stddev = np.sqrt(2/(in_dim + out_dim)) 
        return tf.Variable(tf.truncated_normal([in_dim, out_dim], stddev=xavier_stddev), dtype=tf.float32)
    def neural_net(self, X, weights, biases):
        num_layers = len(weights) + 1
        H = 2.0*(X - self.lb)/(self.ub - self.lb) - 1.0
        for l in range(0,num_layers-2):
            W = weights[l]
            b = biases[l]
            H = tf.tanh(tf.add(tf.matmul(H, W), b))
        W = weights[-1]
        b = biases[-1]
        Y = tf.add(tf.matmul(H, W), b) 
        return Y
    def net(self, x, y, z, t): 
        v1_out = self.neural_net(tf.concat([x,y,z,t], 1), self.weights, self.biases)
        v1 = v1_out[:,0:1]
        return v1
    def callback(self, loss):
        global Nfeval
        print(str(Nfeval)+' - Loss in loop: %.3e' % (loss))
        Nfeval += 1
    def fetch_minibatch(self, x_in, y_in, z_in, t_in, den_in, N_train_sample):  
        idx_batch = np.random.choice(len(x_in), N_train_sample, replace=False)
        x_batch = x_in[idx_batch,:]
        y_batch = y_in[idx_batch,:]
        z_batch = z_in[idx_batch,:]
        t_batch = t_in[idx_batch,:]
        v1_batch = den_in[idx_batch,:] 
        return x_batch, y_batch, z_batch, t_batch, v1_batch
    def train(self, end_it):  
        it = 0
        while it < end_it: 
            x_res_batch, y_res_batch, z_res_batch, t_res_batch, v1_res_batch = self.fetch_minibatch(self.x, self.y, self.z, self.t, self.v1, sample_batch_size) # Fetch residual mini-batch
            tf_dict = self.x_tf: x_res_batch, self.y_tf: y_res_batch, self.z_tf: z_res_batch, self.t_tf: t_res_batch,
                       self.v1_tf: v1_res_batch
            self.optimizer.minimize(self.sess,
                                    feed_dict = tf_dict,
                                    fetches = [self.loss],
                                    loss_callback = self.callback) 
    def predict(self, x_star, y_star, z_star, t_star): 
        tf_dict = self.x_tf: x_star, self.y_tf: y_star, self.z_tf: z_star, self.t_tf: t_star
        v1_star = self.sess.run(self.v1_pred, tf_dict)  
        return v1_star

model = NeuralNet(x_train, y_train, z_train, t_train, v1_train, layers)

Nfeval = 1
model.train(end_it)

【问题讨论】:

拥有一个总是取相同值的变量应该不是什么大问题,我已经在这样的数据集上训练了多个模型。您是否正在规范您的输入?这些似乎很可能来自那个,因为z 的平均值是1,标准差是0,所以归一化值都是(1 - 1) / 0 = nan。为避免这种情况(标准差为空的值),当我对数据进行规范化时,我设置了x_std = np.std(x, axis=0); x_std[x_std &lt; 1e-6] = 1 之类的保护措施。 @jdehesa z 的值在之前的预处理步骤中都被归一化为有序统一,但是即使z != 1 仍然存在这个问题(但它们仍然是一个单一的常数,即z[i] = z[j])。因此,输入变量z 的标准差必然为 0,因为它们都是相同的。进一步明确并扩展您的建议以及它将如何提供帮助会很棒。 我提到的问题发生在变量总是取相同值的任何情况下,无论是哪个值都无关紧要。在这些情况下,归一化的结果始终为 0/0,即nan。您可以通过将标准偏差 0(或非常接近于零)替换为某个常数来避免该问题,例如1,那么归一化将是 0/1 即为零,并且该特征将不起作用。 @jdehesa 是的,但是您究竟打算在哪里替换标准偏差?在更新神经网络本身的权重?还是别处?实现您的解决方案的代码示例(例如在 TensorFlow 中)会很有帮助。 @jdehesa 我现在包含了一个最小的工作示例。如果您可以说明(例如通过示例)如何针对这些数据进行训练,以及您如何克服过去针对具有恒定输入变量的数据进行训练的问题,很高兴能够完成它。 【参考方案1】:

这是一个有趣的情况。对online tool for regression 的快速检查表明,当其中一个输入在数据集中保持不变时,即使是简单的回归也会遇到无法拟合数据点的问题。看一下algebraic solution 的二变量线性回归问题,它显示了涉及除以标准差的解决方案,在常数集中为零,这是一个问题。

就通过反向传播解决问题而言(就像您的神经网络中的情况一样),我强烈怀疑损失相对于输入 (these expressions) 的导数是罪魁祸首,并且算法是无法使用W := W - α.dZ 更新权重W,并最终保持不变。

【讨论】:

我同意并强烈怀疑这个关于输入的导数是问题的根源。我想最根本的问题是:在训练过程中可以使用什么方法来克服这个困难?如果特定输入对输出没有影响,理想情况下它的影响最终应该归零。但是在训练期间完成这项任务的一般方法是什么? 理想情况下,您可以将z 不是 1 的数据点添加到训练集中。如果您没有此类数据,我想知道将现有数据点之一从 z = 1 更改为 z = 1.000001 是否会有所帮助。 正确,在数据点的z 中添加一些变化将解决问题。但是新的问题是我现在不想在训练数据上任意调整z,因为未来的训练数据点实际上会有一个我不知道先验的微妙 z 依赖性。但就目前而言,简单的目标是输出不依赖于我的z 输入而不改变训练数据本身。【参考方案2】:

我认为你的问题出在这一行:

H = 2.0*(X - self.lb)/(self.ub - self.lb) - 1.0

X的第三列,对应z变量,self.lbself.ub都是同一个值,等于例子中的值,本例为1,所以它正在计算:

2.0*(1.0 - 1.0)/(1.0 - 1.0) - 1.0 = 2.0*0.0/0.0 - 1.0

这是nan。您可以通过几种不同的方式解决此问题,一个简单的选择是:

# Avoids dividing by zero
X_d = tf.math.maximum(self.ub - self.lb, 1e-6)
H = 2.0*(X - self.lb)/X_d - 1.0

【讨论】:

这样就解决了问题!我目前只是在针对各种数据集进行训练时测试此解决方案,并且根据其稳健性,将其作为答案。但在此期间,我已授予它赏金。 @Mathews24 谢谢,很高兴它有帮助。如果您仍然发现问题,请告诉我。

以上是关于神经网络可以处理冗余输入吗?的主要内容,如果未能解决你的问题,请参考以下文章

通过网络使用批处理重命名文件

神经网络的全连接层

145自然语言处理进阶手册--循环神经网络

感知器可以用来检测手写数字吗?

我应该标准化我的神经网络中的输入吗?

STP、RSTP