Как сделать выборку в Tensorflow по пользовательскому распределению вероятностей?
У меня есть вектор, например, V = [10, 30, 20, 50]
из N элементов и вектора вероятности P = [.2, .3, .1, .4]
, В тензорном потоке, как я могу случайно выбрать K элементов из V, которые подчиняются заданному распределению вероятности P? Я хочу сделать выборку с заменой.
1 ответ
tf.nn.fixed_unigram_candidate_sampler
делает более или менее то, что вы хотите. Проблема в том, что он может принимать только аргументы int32 в качестве параметра unigrams (распределение вероятностей), потому что он был разработан для обработки мультиклассов с большим числом, например, языковой обработки. Вы можете умножить числа в распределении вероятностей, чтобы получить целое число, но только до предела точности.
Поместите желаемое количество образцов в num_samples
и вес вероятности в unigrams
(должно быть int32.) Параметр true_classes
должен быть заполнен тем же количеством элементов, что и num_true
, но в противном случае не имеет значения, потому что вы получите индексы обратно (а затем использовать их, чтобы вытащить образец.) unique
можно изменить на True по желанию.
Это проверенный код для вас:
import tensorflow as tf
import numpy as np
sess = tf.Session()
V = tf.constant( np.array( [[ 10, 30, 20, 50 ]]), dtype=tf.int64)
sampled_ids, true_expected_count, sampled_expected_count = tf.nn.fixed_unigram_candidate_sampler(
true_classes = V,
num_true = 4,
num_sampled = 50,
unique = False,
range_max = 4,
unigrams = [ 20, 30, 10, 40 ] # this is P, times 100
)
sample = tf.gather( V[ 0 ], sampled_ids )
x = sess.run( sample )
print( x )
Выход:
[50 20 10 30 30 30 10 30 20 50 50 50 10 50 10 30 50 50 30 30 50 10 20 30 50 50 50 50 30 50 50 30 50 50 50 50 50 50 10 50 30 50 10 50 50 10 30 50 50]
Если вы действительно хотите использовать значения вероятности float32, то вам нужно создать сэмплер из нескольких частей (для этого не существует ни одной операции), например так (проверенный код):
import tensorflow as tf
import numpy as np
sess = tf.Session()
k = 50 # number of samples you want
V = tf.constant( [ 10, 30, 20, 50 ], dtype = tf.float32 ) # values
P = tf.constant( [ 0.2, 0.3, 0.1, 0.4 ], dtype = tf.float32 ) # prob dist
cum_dist = tf.cumsum( P ) # create cumulative probability distribution
# get random values between 0 and the max of cum_dist
# we'll determine where it is in the cumulative distribution
rand_unif = tf.random_uniform( shape=( k, ), minval = 0.0, maxval = tf.reduce_max( cum_dist ), dtype = tf.float32 )
# create boolean to signal where the random number is greater than the cum_dist
# take advantage of broadcasting to create Cartesian product
greater = tf.expand_dims( rand_unif, axis = -1 ) > tf.expand_dims( cum_dist, axis = 0 )
# we get the indices by counting how many are greater in any given row
idxs = tf.reduce_sum( tf.cast( greater, dtype = tf.int64 ), 1 )
# then just gather the sample from V by the indices
sample = tf.gather( V, idxs )
# run, output
print( sess.run( sample ) )
Выход:
[20. 10. 50. 50. 20. 30. 10. 20. 30. 50. 20. 50. 30. 50. 30. 50. 50. 50. 50. 50. 50. 30. 20. 20. 20. 10. 50. 30. 30. 10. 50. 50. 50. 20. 30. 50. 30. 10. 50. 20. 30. 50. 30. 10. 10. 50. 50. 20. 50. 30.]
tf.distributions.Categorical()
может быть способ сделать это в один лайнер. Согласно этой странице, учитывая распределение вероятностей P
определены более N
ценности, tf.distributions.Categorical()
может генерировать целые числа 0, 1, ..., N-1
с вероятностями P[0], P[1], ..., P[N-1]
, Сгенерированные целые числа можно интерпретировать как индексы для вектора V
, Следующий фрагмент кода иллюстрирует это:
# Probability distribution
P = [0.2, 0.3, 0.1, 0.4]
# Vector of values
V = [10, 30, 20, 50]
# Define categorical distribution
dist = tf.distributions.Categorical(probs=P)
# Generate a sample from categorical distribution - this serves as an index
index = dist.sample().eval()
# Fetch the value at V[index] as the sample
sample = V[index]
Все это может быть сделано в один лайнер:
sample = V[tf.distributions.Categorical(probs=P).sample().eval()]
Если хотите сгенерировать K
Примеры из этого дистрибутива, оберните вышеупомянутый один вкладыш в понимание списка:
samples = [ V[tf.distributions.Categorical(probs=P).sample().eval()] for i in range(K) ]
Вывод вышеуказанного кода для K = 30:
[50, 10, 30, 50, 30, 20, 50, 30, 50, 50, 30, 50, 30, 50, 20, 10, 50, 20, 30, 30, 50, 50, 50, 30, 20, 50, 30, 30, 50, 50]
Хотя могут быть и более эффективные способы, чем использование списка.