0

I'd like to create a model that predicts parameters of a circle (coordinates of center, radius).

Input is an array of points (of arc with noise):

def generate_circle(x0, y0, r, start_angle, phi, N, sigma):
    theta = np.linspace(start_angle*np.pi/180, (start_angle + phi)*np.pi/180, num=N)
    x = np.array([np.random.normal(r*np.cos(t) + x0 , sigma, 1)[0] for t in theta])
    y = np.array([np.random.normal(r*np.sin(t) + y0 , sigma, 1)[0] for t in theta])
    return x, y

n_x = 1000
start_angle = 0
phi = 90
N = 100
sigma = 0.005 
x_full = []
for i in range(n_x):
    x0 = np.random.normal(0 , 10, 1)[0]
    y0 = np.random.normal(0 , 10, 1)[0]
    r = np.random.normal(0 , 10, 1)[0]
    x, y = generate_circle(x0, y0, r, start_angle, phi, N, sigma)
    x_full.append(np.array([ [x[i], y[i]] for i in range(len(x))]))
X = torch.from_numpy(np.array(x_full))
print(X.size()) # torch.Size([1000, 100, 2])

Output: [x_c, y_c, r]

As a loss function I need to use this one: enter image description here

I tried to implement something like the following:

class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_feature, n_hidden)
        self.predict = torch.nn.Linear(n_hidden, n_output)

    def forward(self, x):
        x = F.relu(self.hidden(x))
        x = self.predict(x)
        return x

# It doesn't work, it's just an idea
def my_loss(point, params):
    arr = ((point[:, 0] - params[:, 0])**2 + (point[:, 1] - params[:, 1])**2 - params[:, 2]**2)**2
    loss = torch.sum(arr)
    return loss

# For N pairs (x, y) model predicts parameters of circle
net = Net(n_feature=N*2, n_hidden=10, n_output=3)

optimizer = torch.optim.SGD(net.parameters(), lr=1e-4)

for t in range(1000):
    prediction = net(X.view(n_x, N*2).float())
    loss = my_loss(X, prediction)
    print(f"loss: {loss}")

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

So, the question is how to correctly implement my own loss function in terms of Pytorch in this case?

Or how to change the model's structure to get expected results?

Shai
  • 111,146
  • 38
  • 238
  • 371
ashy_fox
  • 26
  • 2

1 Answers1

0

You're trying to create a loss between the predicted outputs and the inputs instead of between the predicted outputs and the true outputs. To do this you need to save the true values of x0, y0, and r when you generate them.

n_x = 1000
start_angle = 0
phi = 90
N = 100
sigma = 0.005 
x_full = []
targets = [] # <-- Here
for i in range(n_x):
    x0 = np.random.normal(0 , 10, 1)[0]
    y0 = np.random.normal(0 , 10, 1)[0]
    r = np.random.normal(0 , 10, 1)[0]
    targets.append(np.array([x0, y0, r])) # <-- Here
    x, y = generate_circle(x0, y0, r, start_angle, phi, N, sigma)
    x_full.append(np.array([ [x[i], y[i]] for i in range(len(x))]))
X = torch.from_numpy(np.array(x_full))
Y = torch.from_numpy(np.array(targets)) # <-- Here
print(X.size()) # torch.Size([1000, 100, 2])
print(Y.size()) # torch.Size([1000, 3])

Now, when you call my_loss you should use:

loss = my_loss(Y, prediction)

You are passing in all your data points every iteration of your for loop, I would split your data into smaller sections so that your model doesn't just learn to output the same values every time. e.g. you have generated 1000 points so pass in a random selection of 100 in each iteration using something like random.sample(...)

Your input numbers are pretty large which means your loss will be huge, so generate inputs between 0 and 1 and then if you need the value to be between 0 and 10 you can just multiply by 10.