Как сделать выборку в Tensorflow с помощью пользовательского распределения вероятностей?

avatar
user1205476
8 апреля 2018 в 00:13
4021
2
2

У меня есть вектор, например. V = [10, 30, 20, 50] из N элементов и вектор вероятности P = [.2, .3, .1, .4]. В тензорном потоке, как я могу случайным образом выбрать K элементов из V, которые подчиняются заданному распределению вероятностей P? Я хочу, чтобы выборка производилась с заменой.

Источник

Ответы (2)

avatar
Dr. Prasanna Date
21 февраля 2019 в 02:20
2

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]

Возможно, есть способы и получше, чем использование понимания списка.

avatar
Peter Szoldan
8 апреля 2018 в 01:33
3

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 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.]

user1205476
8 апреля 2018 в 02:03
0

На самом деле, мои входные числа и вероятность находятся в формате с плавающей запятой. Значения вероятности могут быть очень малы.

Peter Szoldan
8 апреля 2018 в 22:37
0

Добавлена ​​версия float32 для ответа