在TensorFlow中将计算值缓存为常量

时间:2015-11-22 18:54:40

标签: python numpy constants linear-regression tensorflow

假设我想使用闭合形式解决方案计算TensorFlow中的最小二乘系数。通常情况下,我会这样做,

beta_hat = tf.matmul(
           tf.matmul(tf.matrix_inverse(tf.matmul(tf.transpose(X), X)), tf.transpose(X)), y
)

Xy分别是与协变量和目标变量对应的TensorFlow占位符。

如果我想进行预测,我会做类似的事情,

y_pred = tf.matmul(X, beta_hat)

如果我要执行,

sess.run(y_pred, feed_dict={X: data_X})

我当然会收到错误,因为我没有为占位符y提供必要的值。在计算它之后,我希望能够灵活地将beta_hat视为常量(这样我就不需要为新的协变量矩阵定义一个新的占位符来进行预测)。实现这一目标的一种方法是,

# Make it constant.
beta_hat = sess.run(beta_hat, feed_dict={X: data_X, y: data_y})
y_pred = tf.matmul(X, beta_hat)

我想知道是否有更优雅的方法将张量处理为常量,这样我既不需要执行会话也不需要获取常量,也不需要为输入数据创建单独的占位符来用于预测。

以下是一些示例代码,用于演示我所描述的情况。

import numpy as np
import tensorflow as tf


n, k = 100, 5
X = tf.placeholder(dtype=tf.float32, shape=[None, k])
y = tf.placeholder(dtype=tf.float32, shape=[None, 1])

beta = np.random.normal(size=(k, ))
data_X = np.random.normal(size=(n, k))

data_y = data_X.dot(beta)
data_y += np.random.normal(size=data_y.shape) / 3.0
data_y = np.atleast_2d(data_y).T

# Convert to 32-bit precision.
data_X, data_y = np.float32(data_X), np.float32(data_y)

# Compute the least squares solution.
beta_hat = tf.matmul(
    tf.matmul(tf.matrix_inverse(tf.matmul(tf.transpose(X), X)),
              tf.transpose(X)), y
)

# Launch the graph
sess = tf.Session()
sess.run(tf.initialize_all_variables())

print "True beta: {}".format(beta)
print "Est. beta: {}".format(
    sess.run(beta_hat, feed_dict={X: data_X, y: data_y}).ravel()
)

# # This would error.
# y_pred = tf.matmul(X, beta_hat)
# print "Predictions:"
# print sess.run(y_pred, feed_dict={X: data_X})

# Make it constant.
beta_hat = sess.run(beta_hat, feed_dict={X: data_X, y: data_y})

# This will no longer error.
y_pred = tf.matmul(X, beta_hat)
print "Predictions:"
print sess.run(y_pred, feed_dict={X: data_X})

2 个答案:

答案 0 :(得分:2)

也许与直觉相反,在后续步骤中重复使用beta_hat作为常量的最简单方法是将其分配给tf.Variable

n, k = 100, 5
X = tf.placeholder(dtype=tf.float32, shape=[None, k])
y = tf.placeholder(dtype=tf.float32, shape=[None, 1])

beta = np.random.normal(size=(k, ))
data_X = np.random.normal(size=(n, k))

data_y = data_X.dot(beta)
data_y += np.random.normal(size=data_y.shape) / 3.0
data_y = np.atleast_2d(data_y).T

# Convert to 32-bit precision.
data_X, data_y = np.float32(data_X), np.float32(data_y)

# Compute the least squares solution.
beta_hat = tf.matmul(
    tf.matmul(tf.matrix_inverse(tf.matmul(tf.transpose(X), X)),
              tf.transpose(X)), y
)

beta_hat_cached = tf.Variable(beta_hat)

# Launch the graph
sess = tf.Session()

print "True beta: {}".format(beta)
# Run the initializer, which computes `beta_hat` once:
sess.run(beta_hat_cached.initializer, feed_dict={X: data_X, y: data_y})
# To access the value of `beta_hat`, "run" the variable to read its contents.
print "Est. beta: {}".format(beta_hat_cached.ravel())

# Use the cached version to compute predictions.
y_pred = tf.matmul(X, beta_hat_cached)
print "Predictions:"
print sess.run(y_pred, feed_dict={X: data_X})

答案 1 :(得分:0)

mrry确实提出了一个优雅的解决方案。如果这确实是你想要的,你应该考虑将他的答案标记为正确。

但是,我认为这是一个清理我认为是占位符混淆来源的好地方......这不一定针对提出问题的人,但我相信它会与许多偶然发现这个问题的初学者有关...

占位符应该被视为功能输入。首先,让我们回顾一下Python中的工作方式,然后我将在Tensorflow中显示相应的表单......

如果我想有一个函数来计算给定各种输入xy的输出,那么我可以这样做......

def f(x,y):
    # For example... 
    return x * y 

具体来说,我可以使用xy的各种值调用此函数:

f(1,3) = 3
f(1,4) = 4
f(2,3) = 6
f(2,4) = 8

但是,在我的特定情况下,我的固定值可能为y。所以在我的情况下,将y作为参数传递是没有意义的。相反,我想将y的价值烘焙到函数中,然后改变x。为此,我可以简单地捕获y的外部值:

y = 3
def g(x):
    return x * y

现在每当我致电g时,y的固定值为3:

g(1) = 3
g(2) = 6

同样,如果我也知道x已修复,我可以捕获x的外部值:

x = 2
def h():
   return g(x)

现在,当我致电h时,我隐含地呼叫h()=g(2)=f(2,3)

那太好了,但问题是我每次拨打h时都会重做乘法,因为它等同于调用f(2,3)。因此,为了提高性能,我可以评估表达式,然后有一个函数只返回这个预先计算的值:

val = h()
def h23():
    return val

无论我调用h23多少次,乘法只执行一次(在val = h()行上)。

Tensorflow有类似的概念。

如果您想要一个可以改变两个输入的函数,那么您应该为两个实例创建占位符对象,并在会话中运行时将值传递给源字典中的函数:

dtype = tf.float64
shape = ()
x = tf.placeholder( dtype, shape )
y = tf.placeholder( dtype, shape )
fxy = f(x,y)
with tf.Session() as sess: 
    print( sess.run( fxy, {x:1,y:3} ) )
    print( sess.run( fxy, {x:1,y:4} ) )
    print( sess.run( fxy, {x:2,y:3} ) )
    print( sess.run( fxy, {x:2,y:4} ) )

但是,如果我的某个值没有改变,那么我可以直接将它初始化为常量并使用此值创建一个新函数"烘焙到它中":

y = tf.constant( 3 )
gx = f( x, y )
with tf.Session() as sess: 
    print( sess.run( gx, {x:1} ) )
    print( sess.run( gx, {x:2} ) )

关键是现在我不需要在我的Feed字典中传递y的值。它是常量并在表达式gx中捕获 同样,如果x也是一个常量,那么我应该这样声明:

x = tf.constant(2)
h = f(x,y)
with tf.Session() as sess: 
    print( sess.run( h ) )

正如您所看到的,由于我的所有变量都是不变的,因此我根本不需要输入字典。这是Tensorflow等效于调用不带参数的函数,如h()

但是,和以前一样,当我调用h时,可能需要每次重新评估图表。所以我有两个选择。

  1. 我可以在numpy中计算结果,然后用张量流常量包装该值。
  2. 我可以在tensorflow中计算输出,在会话中运行它以获取numpy值,然后将其包装在常量中。
  3. 在第一个选项中,我会做这样的事情

    fxy = tf.constant( f(2,3) ) 
    

    现在我已经预先计算了Tensorflow之外的函数的值,然后将该值包装为常量,以便我可以在其他tensorflow函数中使用它。

    相反,如果你的函数使用了一些复杂的Tensorflow内在函数,或者你的函数需要很长时间才能运行,并且你认为Tensorflow中的计算机速度会更快,你只会考虑选项2:

    with tf.Session() as sess: 
        fxy = tf.constant( sess.run( h ) )
    

    要了解这里发生了什么,请回忆一下

    h = f( tf.constant(1), tf.constant(3) )
    

    所以我不需要传递一个feed字。代码段sess.run( h )在tensorflow内运行该乘法,并将其作为Numpy数组返回。最后,我用tf.constant包装该值,以便我可以在其他Tensorflow函数中使用它。