I've been trying to train a network to solve exponential fits of the form s(t) = s0 * e^(-t/decay_constant). As input, the net takes s and t, and as output it should return s0 and the decay_constant.
This seems like a sufficiently simple problem that I would expect a net would be able to satisfactorely approximate.
However, I cannot get it to work. The loss does go down, and the results don't look completely random, but they are definitely worse than what I simple log-linear fit could achieve.
My setup was (takes about 20s on CPU)
- A dense net with ReLU activations
- Loss based on log-linear LSQ
- Train on random exponential decay examples
import torch
net = torch.nn.Sequential(
torch.nn.Linear(10, 64), torch.nn.ReLU(),
torch.nn.Linear(64, 64), torch.nn.ReLU(),
torch.nn.Linear(64, 32), torch.nn.ReLU(),
torch.nn.Linear(32, 2), torch.nn.ReLU(), # Want outputs to always be positive.
)
loss = torch.nn.MSELoss()
optimizer = torch.optim.Adam(net.parameters(), lr=0.005)
def signal_model(x, s0, decay):
return s0 * torch.exp(-x / decay)
# Generate some datapoints
batch_size = 4096
for episode in range(1000):
# Generate random time, decay_constant s0.
t = t = (torch.arange(5, 55, 10.) + (torch.rand(batch_size, 5) * 10)).T
decay_constant = torch.rand(batch_size) * 70 + 10
s0 = (torch.rand(batch_size) * 2 - 1) * 50 + 100
# Generate random input data
y = signal_model(t, s0, decay_constant)
data = torch.vstack((t, y)).T
# Make a prediction and calculate loss
coefficients = net(data)
l = loss(-torch.log(signal_model(t, *coefficients.T) + 1e-20), -torch.log(y + 1e-20))
optimizer.zero_grad(); l.backward(); optimizer.step()
To visualize results
import matplotlib.pyplot as plt
t_ = torch.tensor([5, 15, 25, 35, 45], dtype=torch.float32)
plotgrid = torch.arange(0, 50, 0.1)
s = signal_model(t_, 20, 30)
fig, ax = plt.subplots()
ax.plot(plotgrid, signal_model(plotgrid, 20, 30).detach().numpy())
ax.scatter(t_, s.detach().numpy(), label="True")
ax.scatter(
t_,
signal_model(t_, *net(torch.hstack((t_, s)).T)).detach().numpy(),
label="Predicted",
)
ax.legend()
I was wondering whether anyone has insights on why this doesn't work. Is there some fundamental limitation to approximating simple functions? Or is there something that jumps to the eye as inherently wrong? Would love any insights.
What didn't work:
- tweaking learning rate
- tweaking number of layers / units per layer
- tweaking batch_size
- changing loss to MSE on coefficients or on the signal directly without log
- regularizing with L1loss
- using always the same
tas input
Somewhat related question: https://stats.stackexchange.com/questions/379884/why-cant-a-single-relu-learn-a-relu
