42453

How to use random number in user defined tensorflow op?

Question:

How to use random number in user defined tensorflow op?

I am writing a op in cpp which need random number in the Compute function.

but It seems I should not use cpp random library directly, since that cannot control by tf.set_random_seed.

My current code is something like the following, what should I do in function some_interesting_random_function ?

#include "tensorflow/core/framework/op.h" #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/common_shape_fns.h" #include <iostream> #include <typeinfo> #include <random> using namespace tensorflow; REGISTER_OP("MyRandom") .Output("random: int32") .SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) { c->set_output(0, c->Scalar()); return Status::OK(); }); int some_interesting_random_function(){ return 10; } class MyRandomOp : public OpKernel { public: explicit MyRandomOp(OpKernelConstruction* context) : OpKernel(context) {} void Compute(OpKernelContext* context) override { Tensor* res; TensorShape shape; int dims[] = {}; TensorShapeUtils::MakeShape(dims, 0, &shape); OP_REQUIRES_OK(context, context->allocate_output(0, shape, &res)); auto out1 = res->flat<int32>(); out1(0) = some_interesting_random_function(); } }; REGISTER_KERNEL_BUILDER(Name("MyRandom").Device(DEVICE_CPU), MyRandomOp);

Answer1:

The core of all random number generation in TensorFlow is <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/lib/random/philox_random.h" rel="nofollow">PhiloxRandom</a>, generally accessed through its wrapper <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/util/guarded_philox_random.h" rel="nofollow">GuardedPhiloxRandom</a>. As explained in <a href="https://www.tensorflow.org/api_docs/python/tf/set_random_seed" rel="nofollow">tf.set_random_seed</a>, there are graph-level and op-level seeds, both of which may or may not be set. If you want to have this in your op too, you need to do a couple of things. First, your op should be declared with two optional attributes, seed and seed2; see the existing ops in <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/ops/random_ops.cc" rel="nofollow">random_ops.cc</a>. Then, in Python, you have some user API wrapping your op that makes these two values using tensorflow.python.framework.random_seed, which you have to import as tensorflow.python.framework import random_seed, and do seed1, seed2 = random_seed.get_seed(seed); this will correctly create the two seed values using the graph's seed and an optional seed parameter to the function (see <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/python/ops/random_ops.py" rel="nofollow">random_ops.py</a>). These seed1 and seed2 values are then passed as seed and seed2 attributes to your op, obviously. If you do all that, then <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/util/guarded_philox_random.h" rel="nofollow">GuardedPhiloxRandom</a> will take care of properly initializing the random number generator using the right seeds.

Now, to the kernel implementation. In addition to the things I mentioned above, you will need to combine two things: the struct template FillPhiloxRandom, declared in <a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/kernels/random_op.h" rel="nofollow">core/kernels/random_op.h</a>, which will help you fill a tensor with random data; and a Distribution, which is just an object that can be called with a random number generator to produce a value (see existing implementations in <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/lib/random/random_distributions.h" rel="nofollow">core/lib/random/random_distributions.h</a>). Now it is mostly a matter of looking at how it is done in <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/kernels/random_op.cc" rel="nofollow">core/kernels/random_op.cc</a>, and copy the bits you need. Most kernels in there are based on <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/kernels/random_op.cc#L182-L214" rel="nofollow">PhiloxRandomOp</a> (which is not publicly declared, but you can copy or adapt). This essentially holds a random number generator, allocates space in the output tensor (it assumes the first input is the desired shape) and calls FillPhiloxRandom to do the work. If this is the kind of op you are trying to create (generate some data according to some distribution), then you are all set! Your code could look something like this:

// Required for thread pool device #define EIGEN_USE_THREADS #include "tensorflow/core/framework/op_kernel.h" #include "tensorflow/core/framework/register_types.h" #include "tensorflow/core/framework/tensor.h" #include "tensorflow/core/framework/tensor_shape.h" #include "tensorflow/core/kernels/random_op.h" #include "tensorflow/core/util/guarded_philox_random.h" // Helper function to convert an 32-bit integer to a float between [0..1). // Copied from core/lib/random/random_distributions.h PHILOX_DEVICE_INLINE float Uint32ToFloat(uint32 x) { // IEEE754 floats are formatted as follows (MSB first): // sign(1) exponent(8) mantissa(23) // Conceptually construct the following: // sign == 0 // exponent == 127 -- an excess 127 representation of a zero exponent // mantissa == 23 random bits const uint32 man = x & 0x7fffffu; // 23 bit mantissa const uint32 exp = static_cast<uint32>(127); const uint32 val = (exp << 23) | man; // Assumes that endian-ness is same for float and uint32. float result; memcpy(&result, &val, sizeof(val)); return result - 1.0f; } // Template class for your custom distribution template <class Generator, typename RealType> class MyDistribution; // Implementation for tf.float32 template <class Generator> class MyDistribution<Generator, float> { public: // The number of elements that will be returned (see below). static const int kResultElementCount = Generator::kResultElementCount; // Cost of generation of a single element (in cycles) (see below). static const int kElementCost = 3; // Indicate that this distribution may take variable number of samples // during the runtime (see below). static const bool kVariableSamplesPerOutput = false; typedef Array<float, kResultElementCount> ResultType; typedef float ResultElementType; PHILOX_DEVICE_INLINE ResultType operator()(Generator* gen) { typename Generator::ResultType sample = (*gen)(); ResultType result; for (int i = 0; i < kResultElementCount; ++i) { float r = Uint32ToFloat(sample[i]); // Example distribution logic: produce 1 or 0 with 50% probability result[i] = 1.0f * (r < 0.5f); } return result; } }; // Could add implementations for other data types... // Base kernel // Copied from core/kernels/random_op.cc static Status AllocateOutputWithShape(OpKernelContext* ctx, const Tensor& shape, int index, Tensor** output) { TensorShape tensor_shape; TF_RETURN_IF_ERROR(ctx->op_kernel().MakeShape(shape, &tensor_shape)); return ctx->allocate_output(index, tensor_shape, output); } template <typename Device, class Distribution> class PhiloxRandomOp : public OpKernel { public: typedef typename Distribution::ResultElementType T; explicit PhiloxRandomOp(OpKernelConstruction* ctx) : OpKernel(ctx) { OP_REQUIRES_OK(ctx, generator_.Init(ctx)); } void Compute(OpKernelContext* ctx) override { const Tensor& shape = ctx->input(0); Tensor* output; OP_REQUIRES_OK(ctx, AllocateOutputWithShape(ctx, shape, 0, &output)); auto output_flat = output->flat<T>(); tensorflow::functor::FillPhiloxRandom<Device, Distribution>()( ctx, ctx->eigen_device<Device>(), // Multiplier 256 is the same as in FillPhiloxRandomTask; do not change // it just here. generator_.ReserveRandomOutputs(output_flat.size(), 256), output_flat.data(), output_flat.size(), Distribution()); } private: GuardedPhiloxRandom generator_; }; // Register kernel typedef Eigen::ThreadPoolDevice CPUDevice; template struct functor::FillPhiloxRandom< CPUDevice, MyDistribution<tensorflow::random::PhiloxRandom, float>>; REGISTER_KERNEL_BUILDER( Name("MyDistribution") .Device(DEVICE_CPU) .HostMemory("shape") .TypeConstraint<float>("dtype"), PhiloxRandomOp<CPUDevice, MyDistribution<tensorflow::random::PhiloxRandom, float>>); // Register kernels for more types, can use macros as in core/kernels/random_op.cc...

There are a few extra bits and pieces here. First you need to understand that <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/lib/random/philox_random.h" rel="nofollow">PhiloxRandom</a> generally produces four unsigned 32-bit integers on each step, and you have to make your random values from these. Uint32ToFloat is a helper to get a float between zero and one from one of this numbers. There are a few constants in there too. kResultElementCount is the number of values your distribution produces on each step. If you produce one value per random number form the generator, you can set it too Generator::kResultElementCount, like here (which is 4). However, for example if you want to produce double values (that is, tf.float64), you may want to use two 32-bit integers per value, so maybe you would produce Generator::kResultElementCount / 2 in that case. kElementCost is supposed to indicate how many cycles it takes your distribution to produce an element. I do not know how this is measured by the TensorFlow team, but it is just a hint to distribute the generation work among tasks (used by FillPhiloxRandom), so you can just guess something, or copy it from a similarly expensive distribution. kVariableSamplesPerOutput determines whether each call to your distribution may produce a different number of outputs; again, when this is false (which should be the common case), FillPhiloxRandom will make the value generation more efficient. PHILOX_DEVICE_INLINE (defined in <a href="https://github.com/tensorflow/tensorflow/blob/v1.11.0/tensorflow/core/lib/random/philox_random.h" rel="nofollow">core/lib/random/philox_random.h</a>) is a compiler hint to inline the function. You can add then additional implementations and kernel registrations for other data types and, if you are supporting it, for DEVICE_GPU GPUDevice (with typedef Eigen::GpuDevice GPUDevice) or even DEVICE_SYCL (with typedef Eigen::SyclDevice SYCLDevice), if you want. And about that, EIGEN_USE_THREADS is just to enable the thread pool execution device in Eigen, to make CPU implementation multi-threaded.

If your use case is different, though (for example, you want to generate some random numbers and do some other computation in addition to that), FillPhiloxRandom may not be useful to you (or it may be, but then you also need to do something else). Having a look at core/kernels/random_op.cc and the headers of the different classes should help you figure out how to use them for your problem.

Recommend

  • how to make my program to wait for a particular thread and also not affecting my GUI's interact
  • how C++ Implicitly convert c style string to a string object?
  • Type definition: expected UnionAll, got TypeVar
  • Create CSV file from c#: extra character in excel
  • How does “boxing” work in .NET?
  • C++ usrsctp callback parameters null
  • writing unicode to binary file in python
  • Pass a custom comparator through a function
  • How to get from a tuple to a tuple of references to elements in the tuple?
  • CakePHP 3: change order in dateWidget
  • Multiple sockets for clients to connect to
  • Complex trait requirements on struct
  • How to use the resource module to measure the running time of a function?
  • Why I can't use uniform1f instead of uniform4f for setting a vec4 uniform?
  • Lua: Line breaks in strings
  • SSL client cert authentication for only some URLs?
  • Python 3.2.2, error(scripts to exe)
  • Monotouch crashes with NullReferenceException on non nullable object
  • sweetalert2 inputoptions from file in select example
  • Simple linked list-C
  • Is there a way to save the selected text and highlight it again once the page is refreshed?
  • pillow imaging ImportError
  • Multicolored edittext hint
  • How to assign byte[] as a pointer in C#
  • Redux Form - Not able to type anything in input
  • Calling Worksheet functions from vba in foreign language versions of Excel
  • Django simple Captcha “No module named fields” error
  • Do I need to reset a Perl hash index?
  • How can I sort a a table with VBA with given text condition?
  • How to know when stdin is empty if it contains EOF?
  • D3 nodes and links from JSON with nested arrays of children
  • All Classes Conforming to Protocol Inherit Default Implementation
  • What is Eclipse's Declaration View used for?
  • javascript inside java/jsp code
  • Symfony2: How to get request parameter
  • Redux, normalised entities and lodash merge
  • ORA-29908: missing primary invocation for ancillary operator
  • How to format a variable of double type
  • Hits per day in Google Big Query
  • how does django model after text[] in postgresql [duplicate]