提交 6239ec55 authored 作者: James Bergstra's avatar James Bergstra

added rmodule and tests

上级 4412f107
"""Define RModule, a Module providing random number streams in Theano graphs."""
__docformat__ = "restructuredtext en"
import sys
import functools
from functools import partial
from collections import deque
import numpy
from ..compile import (SymbolicInputKit, SymbolicInput,
Module, KitComponent, module, Method, Member, In, Component)
from ..gof import Container
from ..tensor import raw_random
class RandomKit(SymbolicInputKit):
def __init__(self, name, value = None):
super(RandomKit, self).__init__(name)
self.value = value
def gen(self, op, *args, **kwargs):
random_state_result = raw_random.random_state_type()
new_r, out = op(random_state_result, *args, **kwargs)
self.add_input(SymbolicInput(random_state_result, update = new_r))
out.rng = new_r
out.auto = self
return out
def distribute(self, value, indices, containers):
rg = partial(numpy.random.RandomState(int(value)).randint, 2**30)
elems = deque(zip(indices, containers))
i = 0
while elems:
index, container = elems.popleft()
while i <= index:
curr = rg()
i += 1
rs = numpy.random.RandomState(int(curr))
container.data = rs
def binomial(self, *args, **kwargs):
return self.gen(raw_random.binomial, *args, **kwargs)
def uniform(self, *args, **kwargs):
return self.gen(raw_random.uniform, *args, **kwargs)
def normal(self, *args, **kwargs):
return self.gen(raw_random.normal, *args, **kwargs)
def random_integers(self, *args, **kwargs):
return self.gen(raw_random.random_integers, *args, **kwargs)
rk = RandomKit('rk', 0xBAD5EED)
class RModule(Module):
"""Module providing random number streams in Theano graphs."""
def __init__(self, components = {}, **kwcomponents):
super(RModule, self).__init__(components, **kwcomponents)
self.random = RandomKit('rkit')
self._rkit = KitComponent(self.random)
def __wrapper__(self, x):
x = module.wrap(x)
if isinstance(x, Method):
x.kits += [self.random]
return x
def _instance_seed(self, inst, seed, recursive = True):
seedgen = numpy.random.RandomState(seed)
if recursive:
#Here, we recurse through all the components (inst2) contained in (inst)
#and seeds each subcomponent that is an RModule
for path, c in self.flat_components_map(True):
if isinstance(c, RModule):
inst2 = inst
for name in path:
inst2 = inst2[name]
# A Kit (c._rkit.kit) contains a list of io.SymbolicIn instances
# and the distribute method takes a value (seed), a list of indices
# and a list of corresponding Container instances. In this
# situation it will reseed all the rngs using the containers
# associated to them.
c._rkit.kit.distribute(seedgen.random_integers(2**30),
xrange(len(inst2._rkit)), inst2._rkit)
else:
self._rkit.kit.distribute(seedgen.random_integers(2**30), xrange(len(inst._rkit)), inst._rkit)
class RandomStreamsInstance(object):
"""RandomStreamsInstance"""
def __init__(self, random_streams, memo, default_seed):
self.random_streams = random_streams
self.memo = memo
self.default_seed = default_seed
def initialize(self, seed=None):
"""Initialize each random stream
:param seed: each random stream will be assigned a unique state that depends
deterministically on this value.
:type seed: None or integer in range 0 to 2**30
:rtype: None
"""
self.seed(seed)
def seed(self, seed=None):
"""Re-initialize each random stream
:param seed: each random stream will be assigned a unique state that depends
deterministically on this value.
:type seed: None or integer in range 0 to 2**30
:rtype: None
"""
seed = self.default_seed if seed is None else seed
seedgen = numpy.random.RandomState(seed)
for old_r, new_r in self.random_streams.random_state_results:
old_r_seed = seedgen.randint(2**30)
old_r_container = self.memo[old_r].value
if old_r_container.value is None:
old_r_container.value = numpy.random.RandomState(old_r_seed)
else:
old_r_container.value.seed(old_r_seed)
def __getitem__(self, item):
"""Retrieve the numpy RandomState instance associated with a particular stream
:param item: a result of type RandomStateType, associated with this RandomStream
:rtype: numpy RandomState (or None, before initialize)
"""
for old_r, new_r in self.random_streams.random_state_results:
if item is old_r:
container = self.memo[item].value
return container.value
raise KeyError(item)
def __setitem__(self, item, val):
"""Set the numpy RandomState instance associated with a particular stream
:param item: a result of type RandomStateType, associated with this RandomStream
:param val: the new value
:type val: numpy RandomState
:rtype: None
"""
if type(val) is not numpy.random.RandomState:
raise TypeError('only values of type RandomState are permitted', val)
for old_r, new_r in self.random_streams.random_state_results:
if item is old_r:
container = self.memo[item].value
container.value = val
return
raise KeyError(item)
class RandomStreams(Component):
"""Module with similar interface to numpy.random (numpy.random.RandomState)"""
random_state_results = []
"""A list of pairs of the form (input_r, output_r). This will be over-ridden by the module
instance to contain stream generators.
"""
default_instance_seed = None
"""Instance variable should take None or integer value. Used to seed the random number
generator that provides seeds for member streams"""
def __init__(self, seed=None):
super(RandomStreams, self).__init__()
self.random_state_results = []
self.default_instance_seed = seed
def allocate(self, memo):
for old_r, new_r in self.random_state_results:
assert old_r not in memo
memo[old_r] = In(old_r,
value=Container(old_r, storage=[None]),
update=new_r,
mutable=True)
def build(self, mode, memo):
#print 'MODE', mode
#returns a list of containers
return RandomStreamsInstance(self, memo, self.default_instance_seed)
def gen(self, op, *args, **kwargs):
random_state_result = raw_random.random_state_type()
new_r, out = op(random_state_result, *args, **kwargs)
out.rng = random_state_result
self.random_state_results.append((random_state_result, new_r))
return out
def binomial(self, *args, **kwargs):
"""Return a symbolic binomial sample
"""
return self.gen(raw_random.binomial, *args, **kwargs)
def uniform(self, *args, **kwargs):
return self.gen(raw_random.uniform, *args, **kwargs)
def normal(self, *args, **kwargs):
return self.gen(raw_random.normal, *args, **kwargs)
def random_integers(self, *args, **kwargs):
return self.gen(raw_random.random_integers, *args, **kwargs)
__docformat__ = "restructuredtext en"
import sys
import unittest
import numpy as N
from theano.tensor.rmodule import *
from theano import tensor
from theano import compile, gof
class T_RandomStreams(unittest.TestCase):
def test_basics(self):
m = Module()
m.random = RandomStreams(234)
m.fn = Method([], m.random.uniform((2,2)))
made = m.make()
made.random.initialize()
fn_val0 = made.fn()
fn_val1 = made.fn()
rng_seed = numpy.random.RandomState(234).randint(2**30)
rng = numpy.random.RandomState(rng_seed)
#print fn_val0
numpy_val0 = rng.uniform(size=(2,2))
numpy_val1 = rng.uniform(size=(2,2))
#print numpy_val0
assert numpy.all(fn_val0 == numpy_val0)
assert numpy.all(fn_val1 == numpy_val1)
def test_seed_in_initialize(self):
m = Module()
m.random = RandomStreams(234)
m.fn = Method([], m.random.uniform((2,2)))
made = m.make()
made.random.initialize(seed=888)
fn_val0 = made.fn()
fn_val1 = made.fn()
rng_seed = numpy.random.RandomState(888).randint(2**30)
rng = numpy.random.RandomState(rng_seed)
#print fn_val0
numpy_val0 = rng.uniform(size=(2,2))
numpy_val1 = rng.uniform(size=(2,2))
#print numpy_val0
assert numpy.all(fn_val0 == numpy_val0)
assert numpy.all(fn_val1 == numpy_val1)
def test_seed_fn(self):
m = Module()
m.random = RandomStreams(234)
m.fn = Method([], m.random.uniform((2,2)))
made = m.make()
made.random.initialize(seed=789)
made.random.seed(888)
fn_val0 = made.fn()
fn_val1 = made.fn()
rng_seed = numpy.random.RandomState(888).randint(2**30)
rng = numpy.random.RandomState(rng_seed)
#print fn_val0
numpy_val0 = rng.uniform(size=(2,2))
numpy_val1 = rng.uniform(size=(2,2))
#print numpy_val0
assert numpy.all(fn_val0 == numpy_val0)
assert numpy.all(fn_val1 == numpy_val1)
def test_getitem(self):
m = Module()
m.random = RandomStreams(234)
out = m.random.uniform((2,2))
m.fn = Method([], out)
made = m.make()
made.random.initialize(seed=789)
made.random.seed(888)
rng = numpy.random.RandomState()
rng.set_state(made.random[out.rng].get_state())
fn_val0 = made.fn()
fn_val1 = made.fn()
numpy_val0 = rng.uniform(size=(2,2))
numpy_val1 = rng.uniform(size=(2,2))
assert numpy.all(fn_val0 == numpy_val0)
assert numpy.all(fn_val1 == numpy_val1)
def test_setitem(self):
m = Module()
m.random = RandomStreams(234)
out = m.random.uniform((2,2))
m.fn = Method([], out)
made = m.make()
made.random.initialize(seed=789)
made.random.seed(888)
rng = numpy.random.RandomState(823874)
made.random[out.rng] = numpy.random.RandomState(823874)
fn_val0 = made.fn()
fn_val1 = made.fn()
numpy_val0 = rng.uniform(size=(2,2))
numpy_val1 = rng.uniform(size=(2,2))
assert numpy.all(fn_val0 == numpy_val0)
assert numpy.all(fn_val1 == numpy_val1)
class T_test_module(unittest.TestCase):
def test_state_propagation(self):
if 1:
print >> sys.stderr, "RModule deprecated"
else:
x = tensor.vector()
rk = RandomKit('rk', 1000)
f = compile.function([x, (rk, [gof.Container(r = gof.generic, storage = [123], name='bla')])], rk.binomial(tensor.shape(x)))
print "RK", rk.value
f['rk'] = 9873456
print "RK", rk.value
rvals = [f([1,2,3,4,6, 7, 8]) for i in xrange(5)]
print rvals
for i in xrange(5-1):
for j in xrange(i+1, 5):
assert not N.all(rvals[i] == rvals[j])
def test_B(self):
"""Test that random numbers change from call to call!
Also, make sure that the seeding strategy doesn't change without failing a test.
Random numbers can't be too random or experiments aren't repeatable. Email theano-dev
before updating the `rvals` in this test.
"""
class B(RModule):
def __init__(self):
super(B, self).__init__()
self.x = compile.Member(tensor.dvector())
self.r = self.random.uniform(tensor.shape(self.x))
self.f = compile.Method([self.x], self.r)
class E(RModule):
def __init__(self):
super(E, self).__init__()
self.b = B()
self.f = compile.Method([self.b.x], self.b.r)
b = E()
m = b.make()
m.seed(1000)
#print m.f(N.ones(5))
#print m.f(N.ones(5))
#print m.f(N.ones(5))
rvals = ["0.74802375876 0.872308123517 0.294830748897 0.803123780003 0.6321109955",
"0.00168744844365 0.278638315678 0.725436793755 0.7788480779 0.629885140994",
"0.545561221664 0.0992011009108 0.847112593242 0.188015424144 0.158046201298",
"0.054382248842 0.563459168529 0.192757276954 0.360455221883 0.174805216702",
"0.961942907777 0.49657319422 0.0316111492826 0.0915054717012 0.195877184515"]
for i in xrange(5):
s = " ".join([str(n) for n in m.f(N.ones(5))])
print s
assert s == rvals[i]
if __name__ == '__main__':
from theano.tests import main
main("test_rmodule")
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论