【问题标题】:Not fully connected layer in tensorflow张量流中的非全连接层
【发布时间】:2019-05-19 23:17:13
【问题描述】:

我想创建一个网络,其中输入层的节点只连接到下一层的一些节点。这是一个小例子:

到目前为止,我的解决方案是将i1h1 之间的边的权重设置为零,并且在每个优化步骤之后,我将权重乘以一个矩阵(我称之为矩阵掩码矩阵),其中每个条目除了在i1h1 之间的边的权重条目之外是1。 (见下面的代码)

这种方法对吗?或者这对 GradientDescent 有影响吗?是否有另一种方法可以在 TensorFlow 中创建这种网络?

import tensorflow as tf
import tensorflow.contrib.eager as tfe
import numpy as np

tf.enable_eager_execution()


model = tf.keras.Sequential([
  tf.keras.layers.Dense(2, activation=tf.sigmoid, input_shape=(2,)),  # input shape required
  tf.keras.layers.Dense(2, activation=tf.sigmoid)
])


#set the weights
weights=[np.array([[0, 0.25],[0.2,0.3]]),np.array([0.35,0.35]),np.array([[0.4,0.5],[0.45, 0.55]]),np.array([0.6,0.6])]

model.set_weights(weights)

model.get_weights()

features = tf.convert_to_tensor([[0.05,0.10 ]])
labels =  tf.convert_to_tensor([[0.01,0.99 ]])


mask =np.array([[0, 1],[1,1]])

#define the loss function
def loss(model, x, y):
  y_ = model(x)
  return tf.losses.mean_squared_error(labels=y, predictions=y_)

#define the gradient calculation
def grad(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss(model, inputs, targets)
  return loss_value, tape.gradient(loss_value, model.trainable_variables) 

#create optimizer an global Step
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
global_step = tf.train.get_or_create_global_step()


#optimization step
loss_value, grads = grad(model, features, labels)
optimizer.apply_gradients(zip(grads, model.variables),global_step)

#masking the optimized weights 
weights=(model.get_weights())[0]
masked_weights=tf.multiply(weights,mask)
model.set_weights([masked_weights])

【问题讨论】:

  • 这只是一个玩具示例,还是您正在为这个特定示例寻找解决方案?
  • 如果以下答案之一解决了您的问题,请接受点击答案旁边的复选标记 (✔) 将其标记为“已回答” - 请参阅 @ 987654322@

标签: python tensorflow machine-learning keras neural-network


【解决方案1】:

如果您正在为您提供的特定示例寻找解决方案,您可以简单地使用tf.keras 功能 API 并定义两个 Dense 层,其中一个连接到前一层的两个神经元,另一个仅连接到神经元之一:

from tensorflow.keras.layer import Input, Lambda, Dense, concatenate
from tensorflow.keras.models import Model

inp = Input(shape=(2,))
inp2 = Lambda(lambda x: x[:,1:2])(inp)   # get the second neuron 

h1_out = Dense(1, activation='sigmoid')(inp2)  # only connected to the second neuron
h2_out = Dense(1, activation='sigmoid')(inp)  # connected to both neurons
h_out = concatenate([h1_out, h2_out])

out = Dense(2, activation='sigmoid')(h_out)

model = Model(inp, out)

# simply train it using `fit`
model.fit(...)

【讨论】:

  • 是否可以传递索引数组,而不是切片[:,1:2]?类似[:,[1,2,5]]
  • @L.B.据我所知,与 Numpy 不同,这是不可能的,因为索引应该是切片或标量。但是,您可以为此使用tf.gather,例如:tf.gather(x, [1,2,5], axis=1),这相当于x[:, [1,2,5]]
【解决方案2】:

您的解决方案和本文中其他答案所建议的其他问题的问题在于,它们并没有阻止训练这个重量。它们允许梯度下降来训练不存在的权重,然后回顾性地覆盖它。这将导致网络根据需要在此位置具有零,但会对您的训练过程产生负面影响,因为反向传播计算不会看到掩码步骤,因为它不是 TensorFlow 图的一部分,因此梯度下降将遵循一条路径,其中包含该权重确实对结果有影响(它没有)的假设。

更好的解决方案是将掩蔽步骤作为 TensorFlow 图的一部分包含在内,以便将其纳入梯度下降中。由于屏蔽步骤只是通过稀疏二进制矩阵mask 进行元素乘法,因此您可以使用tf.multiplymask 矩阵作为元素矩阵乘法包含在图形定义中。

遗憾的是,这意味着要告别用户友好的 keras、layers 方法,并采用更具体的方法来使用 TensorFlow。我看不到使用图层 API 的明显方法。

请参阅下面的实现,我已尝试提供 cmets 解释每个阶段发生的情况。

import tensorflow as tf

## Graph definition for model

# set up tf.placeholders for inputs x, and outputs y_
# these remain fixed during training and can have values fed to them during the session
with tf.name_scope("Placeholders"):
    x = tf.placeholder(tf.float32, shape=[None, 2], name="x")   # input layer
    y_ = tf.placeholder(tf.float32, shape=[None, 2], name="y_") # output layer

# set up tf.Variables for the weights at each layer from l1 to l3, and setup feeding of initial values
# also set up mask as a variable and set it to be un-trianable
with tf.name_scope("Variables"):
    w_l1_values = [[0, 0.25],[0.2,0.3]]
    w_l1 = tf.Variable(w_l1_values, name="w_l1")
    w_l2_values = [[0.4,0.5],[0.45, 0.55]]
    w_l2 = tf.Variable(w_l2_values, name="w_l2")

    mask_values = [[0., 1.], [1., 1.]]
    mask = tf.Variable(mask_values, trainable=False, name="mask")


# link each set of weights as matrix multiplications in the graph. Inlcude an elementwise multiplication by mask.
# Sequence takes us from inputs x to output final_out, which will be compared to labels fed to placeholder y_
l1_out = tf.nn.relu(tf.matmul(x, tf.multiply(w_l1, mask)), name="l1_out")
final_out = tf.nn.relu(tf.matmul(l1_out, w_l2), name="output")


## define loss function and training operation
with tf.name_scope("Loss"):
    # some loss defined as a function of graph output: final_out and labels: y_
    loss = tf.nn.sigmoid_cross_entropy_with_logits(logits=final_out, labels=y_, name="loss")

with tf.name_scope("Train"):
    # some optimisation strategy, arbitrary learning rate
    optimizer = tf.train.AdamOptimizer(learning_rate=0.001, name="optimizer_adam")
    train_op = optimizer.minimize(loss, name="train_op")


# create session, initialise variables and train according to inputs and corresponding labels
# This should show that the values of the first layer weights change, but the one set to 0 remains at 0
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    initial_l1_weights = sess.graph.get_tensor_by_name("Variables/w_l1:0")
    print(initial_l1_weights.eval())

    inputs = [[0.05, 0.10]]
    labels = [[0.01, 0.99]]
    ans = sess.run(train_op, feed_dict={"Placeholders/x:0": inputs, "Placeholders/y_:0": labels})

    train_steps = 1
    for i in range(train_steps):
        initial_l1_weights = sess.graph.get_tensor_by_name("Variables/w_l1:0")
    print(initial_l1_weights.eval())

或者使用 today 提供的答案作为对 keras 友好的选项。

【讨论】:

  • 仅供参考,我的回答不包括你提到的额外重量,因为它有两个独立的密集层。
  • “会对您的培训过程产生负面影响”究竟是什么意思?这是否意味着优化可能不起作用,或者是否意味着训练过程更慢?由于我最初的问题要复杂得多(9180 个输入节点),我真的很想继续使用 keras。
  • 我的意思是您正在根据一个成本函数优化您的网络,该成本函数不是您评估其成功/失败的成本函数。您通过优化网络来训练网络,就好像这个权重存在,然后将其取出。与优化不存在的权重相比,这势必会产生较差的结果。
  • 想象你有一个总和:a + b + c = 100,你想优化它。这样做会得到 a*=33.3, *b = 33.3, *c*=33.4。完美的。但是然后设置 *a*=0 并且您有一个次优解决方案。反复这样做可能会慢慢地朝着最优解前进,但它永远不会到达那里,并且在像你这样的非凸问题中可能会让你陷入糟糕的局部最小值。
  • 重读后,似乎today提供的答案并没有导致这个问题。所以就这样吧。 (我会相应地修改我的答案)。
【解决方案3】:

这里有多种选择。

首先,您可以在示例中使用动态屏蔽方法。我相信这会按预期工作,因为梯度 w.r.t.被屏蔽的参数将为零(当您更改未使用的参数时,输出是恒定的)。这种方法很简单,即使在训练期间您的掩码不是恒定的,也可以使用它。

其次,如果您事先知道哪些权重将始终为零,则可以使用tf.get_variable 组合权重矩阵以获取子矩阵,然后将其与tf.constant 张量连接,例如:

weights_sub = tf.get_variable("w", [dim_in, dim_out - 1])
zeros = tf.zeros([dim_in, 1])
weights = tf.concat([weights_sub, zeros], axis=1)

此示例将使您的权重矩阵的一列始终为零。

最后,如果你的掩码更复杂,你可以在一个扁平的向量上使用tf.get_variable,然后用所用索引上的变量值组合一个tf.SparseTensor

weights_used = tf.get_variable("w", [num_used_vars])
indices = ...  # get your indices in a 2-D matrix of shape [num_used_vars, 2]
dense_shape = tf.constant([dim_in, dim_out])  # this is the final shape of the weight matrix
weights = tf.SparseTensor(indices, weights_used, dense_shape)

编辑:这可能无法与 Keras 的 set_weights 方法结合使用,因为它需要 Numpy 数组,而不是张量。

【讨论】:

    猜你喜欢
    • 2018-04-11
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    • 2019-04-09
    • 2019-03-30
    • 2018-07-31
    相关资源
    最近更新 更多