 # Parameters controlling the transformation of other parameters

Hi everyone,

I’m exploring Bayesian Linear Regression for the first time and I’ve got some confusion about defining a model that has learnable parameters controlling the functional form of a transformation applied to other learnable parameters.

For example, I have a diminishing returns transformation applied to some parameters.

``````def diminishing_returns(spend, half_sat, shape):
return 1 / (1 + np.power(np.exp(spend / half_sat), -shape))
``````

I want the shape and saturation point of that transformation to also be learnable parameters such that I could use it like

``````def model():
sigma = pyro.sample('noise', pyro.distributions.HalfNormal(scale=10))
baseline = pyro.sample('baseline', pyro.distributions.Normal(loc=0, scale=1))

half_sat = pyro.sample('half_sat', pyro.distributions.Normal(loc=0, scale=1))
shape = pyro.sample('shape', pyro.distributions.Normal(loc=0, scale=1))

channel = pyro.sample(channel, pyro.distributions.HalfNormal(scale=1)) * diminishing_returns(feature[:, "channel"], half_sat, shape)

mean = baseline + channel

with pyro.plate('data', len(train_features.index)):
pyro.sample('obs', pyro.distributions.Normal(loc=mean, scale=sigma), obs=torch.from_numpy(train_labels.to_numpy()))
``````

How can I adjust my diminishing returns function to make this work? Are there any references that address a similar problem?

Hi @tmitchel, your implementation is almost already correct, I believe all you’ll need to do is avoid NumPy within Pyro models. Usually I transform everything from NumPy to PyTorch before starting my training loop. That ensures gradients can be propagated, and is also faster.

``````def diminishing_returns(spend, half_sat, shape):
return 1 / (1 + torch.pow(torch.exp(spend / half_sat), -shape))
# Though that seems weird, since it is equivalent to
#   return 1 / (1 + tprch.exp(-shape * spend / half_sat))
# wherein (shape, half_sat) appear to be nonidentifiable?

# Convert numpy -> torch before training.
feature_channel = torch.as_tensor(feature[:, "channel"])
data = torch.as_tensor(train_labels)

def model():
sigma = pyro.sample('noise', pyro.distributions.HalfNormal(scale=10))
baseline = pyro.sample('baseline', pyro.distributions.Normal(loc=0, scale=1))
half_sat = pyro.sample('half_sat', pyro.distributions.Normal(loc=0, scale=1))
shape = pyro.sample('shape', pyro.distributions.Normal(loc=0, scale=1))
returns = diminishing_returns(feature_channel, half_sat, shape)
channel = pyro.sample('channel', pyro.distributions.HalfNormal(scale=1)) * returns
mean = baseline + channel
with pyro.plate('data', len(data)):
pyro.sample('obs', pyro.distributions.Normal(loc=mean, scale=sigma), obs=data)
``````

Thanks so much for the fast response. It’s good to know I was on the right track!