Hey @NikSchet,

The `params`

were obtained by training the hybrid model. For example, let’s define:

```
def softmax_ensemble(params, x_point=None):
results = qnodes(params, x=x_point)
softmax = torch.nn.functional.softmax(results, dim=1)
choice = torch.where(softmax == torch.max(softmax))[0][0]
return softmax[choice]
```

This function evaluates both circuits, converts to softmax vectors, and then returns the “most confident” vector (that is, the one with the largest entry). For example:

```
n_layers = 2
# the first index is for the two models
params = torch.tensor(np.random.random((2, n_layers, n_wires, 3)), requires_grad=True)
print(softmax_ensemble(params, x_train[0]))
```

The above gives a vector such as `[0.2624, 0.4455, 0.2921]`

, denoting a 44.55% probability of class `1`

.

This can be compared to the one-hot encoded version of `y_train`

:

```
import tensorflow as tf
y_soft = torch.tensor(tf.one_hot(y_train, n_classes).numpy(), requires_grad=True)
```

For example, `y_soft[0] = [1, 0, 0]`

.

We then just need to define a cost function to compare the predicted vector with the target, for example:

```
def cost(params, y_point, x_point=None):
return torch.sum(torch.abs(softmax_ensemble(params, x_point=x_point) - y_point))
```

This is a simple cost function that gives the absolute error.

Training is then as simple as:

```
opt = torch.optim.Adam([params], lr = 0.1)
for epoch in range(3):
for x_point, y_point in zip(x_train, y_soft):
opt.zero_grad()
c = cost(params, y_point=y_point, x_point=x_point)
c.backward()
opt.step()
```

The training could be improved with random selection of points from the training set, batching, and use of a different cost function.

All the code together (also requires other parts of the tutorial to be run):

```
import tensorflow as tf
n_layers = 2
# the first index is for the two models
params = torch.tensor(np.random.random((2, n_layers, n_wires, 3)), requires_grad=True)
def softmax_ensemble(params, x_point=None):
results = qnodes(params, x=x_point)
softmax = torch.nn.functional.softmax(results, dim=1)
choice = torch.where(softmax == torch.max(softmax))[0][0]
return softmax[choice]
def cost(params, y_point, x_point=None):
return torch.sum(torch.abs(softmax_ensemble(params, x_point=x_point) - y_point))
y_soft = torch.tensor(tf.one_hot(y_train, n_classes).numpy(), requires_grad=True)
opt = torch.optim.Adam([params], lr = 0.1)
for epoch in range(3):
for x_point, y_point in zip(x_train, y_soft):
opt.zero_grad()
c = cost(params, y_point=y_point, x_point=x_point)
c.backward()
opt.step()
```