Mixture of categorical distributions

I’m trying to create a mixture of two categorical distributions. The first distribution is uniform over the first half of the values and 0 otherwise; the second is the opposite. For simplicity I am using equal weights on each distribution, but this will be parameterized.

a = np.zeros(10)
a[:5] += 1/5
b = np.zeros(10)
b[5:] += 1/5

with pm.Model() as m:
    w = pm.Dirichlet('w', a=np.array([1, 1]))
    d1 = pm.Categorical('d1', a)
    d2 = pm.Categorical('d2', b)
    mix  = pm.Mixture('mix', w=w, comp_dists=[d1, d2])

With this code, I get the following error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/virtual_envs/py3/lib/python3.4/site-packages/pymc3/distributions/mixture.py in _comp_means(self)
    118         try:
--> 119             return tt.as_tensor_variable(self.comp_dists.mean)
    120         except AttributeError:

AttributeError: 'list' object has no attribute 'mean'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/type.py in dtype_specs(self)
    268                 'complex64': (complex, 'theano_complex64', 'NPY_COMPLEX64')
--> 269             }[self.dtype]
    270         except KeyError:

KeyError: 'object'

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/basic.py in constant(x, name, ndim, dtype)
    245     try:
--> 246         ttype = TensorType(dtype=x_.dtype, broadcastable=bcastable)
    247         if not constant.enable:

~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/type.py in __init__(self, dtype, broadcastable, name, sparse_grad)
     50         self.broadcastable = tuple(bool(b) for b in broadcastable)
---> 51         self.dtype_specs()  # error checking is done there
     52         self.name = name

~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/type.py in dtype_specs(self)
    271             raise TypeError("Unsupported dtype for %s: %s"
--> 272                             % (self.__class__.__name__, self.dtype))
    273 

TypeError: Unsupported dtype for TensorType: object

During handling of the above exception, another exception occurred:

TypeError                                 Traceback (most recent call last)
~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/basic.py in as_tensor_variable(x, name, ndim)
    193     try:
--> 194         return constant(x, name=name, ndim=ndim)
    195     except TypeError:

~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/basic.py in constant(x, name, ndim, dtype)
    265     except Exception:
--> 266         raise TypeError("Could not convert %s to TensorType" % x, type(x))
    267 

TypeError: ('Could not convert <bound method FreeRV.mean of d1> to TensorType', <class 'method'>)

During handling of the above exception, another exception occurred:

AsTensorError                             Traceback (most recent call last)
<ipython-input-105-b08e5acda0db> in <module>()
     10     d1 = pm.Categorical('d1', a)
     11     d2 = pm.Categorical('d2', b)
---> 12     mix  = pm.Mixture('mix', w=w, comp_dists=[d1, d2])

~/virtual_envs/py3/lib/python3.4/site-packages/pymc3/distributions/distribution.py in __new__(cls, name, *args, **kwargs)
     39                 raise TypeError("observed needs to be data but got: {}".format(type(data)))
     40             total_size = kwargs.pop('total_size', None)
---> 41             dist = cls.dist(*args, **kwargs)
     42             return model.Var(name, dist, data, total_size)
     43         else:

~/virtual_envs/py3/lib/python3.4/site-packages/pymc3/distributions/distribution.py in dist(cls, *args, **kwargs)
     50     def dist(cls, *args, **kwargs):
     51         dist = object.__new__(cls)
---> 52         dist.__init__(*args, **kwargs)
     53         return dist
     54 

~/virtual_envs/py3/lib/python3.4/site-packages/pymc3/distributions/mixture.py in __init__(self, w, comp_dists, *args, **kwargs)
     83 
     84             try:
---> 85                 self.mean = (w * self._comp_means()).sum(axis=-1)
     86 
     87                 if 'mean' not in defaults:

~/virtual_envs/py3/lib/python3.4/site-packages/pymc3/distributions/mixture.py in _comp_means(self)
    121             return tt.squeeze(tt.stack([comp_dist.mean
    122                                         for comp_dist in self.comp_dists],
--> 123                                        axis=1))
    124 
    125     def _comp_modes(self):

~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/basic.py in stack(*tensors, **kwargs)
   4707         dtype = scal.upcast(*[i.dtype for i in tensors])
   4708         return theano.tensor.opt.MakeVector(dtype)(*tensors)
-> 4709     return join(axis, *[shape_padaxis(t, axis) for t in tensors])
   4710 
   4711 

~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/basic.py in <listcomp>(.0)
   4707         dtype = scal.upcast(*[i.dtype for i in tensors])
   4708         return theano.tensor.opt.MakeVector(dtype)(*tensors)
-> 4709     return join(axis, *[shape_padaxis(t, axis) for t in tensors])
   4710 
   4711 

~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/basic.py in shape_padaxis(t, axis)
   4594 
   4595     """
-> 4596     _t = as_tensor_variable(t)
   4597 
   4598     ndim = _t.ndim + 1

~/virtual_envs/py3/lib/python3.4/site-packages/theano/tensor/basic.py in as_tensor_variable(x, name, ndim)
    198         except Exception:
    199             str_x = repr(x)
--> 200         raise AsTensorError("Cannot convert %s to TensorType" % str_x, type(x))
    201 
    202 # this has a different name, because _as_tensor_variable is the

AsTensorError: ('Cannot convert <bound method FreeRV.mean of d1> to TensorType', <class 'method'>)

It looks like it’s not happy that I’m providing the two dists in a list, but according to the documentation, that should be fine. Any ideas?

comp_dists accepts either a list of distribution or multidimentional distribution, for example:

with pm.Model() as m:
    w = pm.Dirichlet('w', a=np.array([1, 1]))
    d1 = pm.Categorical.dist(p=a)
    d2 = pm.Categorical.dist(p=b)
    mix  = pm.Mixture('mix', w=w, comp_dists=[d1, d2], observed=[0, 1, 9])

Note that due to shape handling issue with Categorical and Mixture, you probably need to assign shape and testval if the RV is unobserved.

2 Likes

Works great, thanks!