Кэширование вычисленного значения в качестве константы в TensorFlow

Предположим, что я хочу вычислить коэффициенты наименьших квадратов в TensorFlow, используя решение замкнутой формы. Обычно я делаю это так,

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

Где X и y являются записями TensorFlow, соответствующими ковариатам и целевой переменной соответственно.

Если бы я тогда хотел выполнить предсказание, я бы сделал что-то вроде

y_pred = tf.matmul(X, beta_hat)

Если я должен был выполнить,

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

Я бы, конечно, получил ошибку, что я не предоставил необходимое значение для placeholder y. Я хотел бы иметь гибкость для обработки beta_hat как постоянной после того, как я ее вычислил (так что мне не нужно было бы определять новый placeholder для новой матрицы ковариации для прогнозирования). Один из способов добиться этого -

# 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 ответа

Возможно, интуитивно, простейшим способом повторного использования 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})


mrry действительно представила элегантное решение. Вы должны подумать о том, чтобы правильно отнести его ответ, если это действительно то, что вы хотите.

Однако, я думаю, это хорошее место, чтобы прояснить то, что я считаю источником путаницы в отношении заполнителей... Это не обязательно направлено на человека, который задал вопрос, но я считаю, что это будет актуально для многих новичков, которые спотыкаются на этот вопрос...

Заместители должны рассматриваться как входы функций. Итак, сначала рассмотрим, как это работает в Python, а затем я покажу эквивалентную форму в Tensorflow...

Если я хочу иметь функцию, которая вычисляет выходные данные с учетом различных входов x и y, тогда я мог бы сделать это вот так:

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

В частности, я могу вызвать эту функцию с различными значениями для x и y:

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} ) )

Ключевым моментом является то, что теперь мне не нужно передавать значение для y в словаре фидов. Он постоянный и фиксируется в выражении gx. Аналогично, если x также является константой, я должен объявить ее так:

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

Как вы можете видеть, поскольку все мои переменные постоянны, мне вообще не нужен словарь фида. Это эквивалент Tensorflow вызова функции без аргументов, например h().

Однако, как и раньше, когда я вызываю h, может потребоваться повторная оценка графика каждый раз. Поэтому у меня есть два варианта.

  • Я могу вычислить результат в numpy, а затем обернуть это значение константой tensorflow.
  • Я могу вычислить вывод в тензорном потоке, запустить его в сеансе, чтобы получить значение numpy, а затем обернуть его в константу.

В первом варианте я сделал бы что-то вроде этого

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

Теперь я предварительно вычислил значение функции вне Tensorflow, а затем обернул это значение как константу, чтобы использовать его в других функциях tenorflow.

И наоборот, вы только рассмотрите вариант 2, если ваша функция использует некоторые сложные функции Tensorflow, или если ваша функция занимает очень много времени, и вы думаете, что она будет быстрее для компьютера в Tensorflow:

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

Чтобы понять, что здесь происходит, напомните, что

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

Поэтому мне не нужно передавать фид dict. Фрагмент sess.run( h ) запускает это умножение внутри tensorflow и возвращает его как массив Numpy. Затем, наконец, я переношу это значение с помощью tf.constant, чтобы использовать его в других функциях Tensorflow.

licensed under cc by-sa 3.0 with attribution.