# Keras Layer - Single QBIT

**URL:** <https://discuss.pennylane.ai/t/keras-layer-single-qbit/618>\
**Category:** PennyLane Help\
**Created:** [October 14, 2020, 2:02am UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618 "2020-10-14T02:02:11Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Hemant\_Gahankari](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/hemant_gahankari/32/332_2.png) [@Hemant\_Gahankari](https://discuss.pennylane.ai/u/Hemant_Gahankari)\
**Post date:** [October 14, 2020, 2:02am UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/1 "2020-10-14T02:02:11Z")

</div>

I am trying to create a keras layer for training a simple NN that would use a single qubit and covert centigrade to fahrenheit.

Code is as below , I am getting error , `'InvalidArgumentError: Input tensor must be at least 2D: [1] [Op:BiasAdd]'`.

The disconnect is that I am not able to understand the use of `weights_shape`. Can you explain use of `weights_shape `with examples ?

```python
import pennylane as qml
import tensorflow as tf
import numpy as np

n_qubits = 1
dev = qml.device("default.qubit", wires=n_qubits)

@qml.qnode(dev)
def qnode(inputs, weights):
    qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))
    qml.templates.StronglyEntanglingLayers(weights, wires=range(n_qubits))
    return qml.expval(qml.expval(qml.PauliZ(0)))

weight_shapes = {"weights": (1, n_qubits, 3)}

qlayer = qml.qnn.KerasLayer(qnode, weight_shapes, output_dim=1)
clayer1 = tf.keras.layers.Dense(1)
clayer2 = tf.keras.layers.Dense(1, activation="linear")
model = tf.keras.models.Sequential([clayer1, qlayer, clayer2])

X = np.array([-40, -10, 0, 8, 15, 22, 38], dtype=float)
Y = np.array([-40, 14, 32, 46, 59, 72, 100], dtype=float)

X1 = X.reshape((len(X), 1))
Y1 = Y.reshape((len(Y), 1))

opt = tf.keras.optimizers.SGD(learning_rate=0.5)
model.compile(opt, loss='mse',)

model.fit(X1, Y1, epochs=60, batch_size=1)

```

---

<div class="post-metadata">

**Author:** ![Tom\_Bromley](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/tom_bromley/32/129_2.png) [@Tom\_Bromley](https://discuss.pennylane.ai/u/Tom_Bromley)\
**Post date:** [October 14, 2020, 12:22pm UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/2 "2020-10-14T12:22:51Z")

</div>

Hey @Hemant_Gahankari,

The following is a tweak of your code that should get it working:

```python
import pennylane as qml
import tensorflow as tf
import numpy as np

n_qubits = 1
layers = 1
data_dimension = 1
dev = qml.device("default.qubit", wires=n_qubits)

@qml.qnode(dev)
def qnode(inputs, weights):
    qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))
    qml.templates.StronglyEntanglingLayers(weights, wires=range(n_qubits))
    return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)]

weight_shapes = {"weights": (layers, n_qubits, 3)}

qlayer = qml.qnn.KerasLayer(qnode, weight_shapes, output_dim=n_qubits)
clayer1 = tf.keras.layers.Dense(n_qubits)
clayer2 = tf.keras.layers.Dense(data_dimension, activation="linear")
model = tf.keras.models.Sequential([clayer1, qlayer, clayer2])

X = np.array([-40, -10, 0, 8, 15, 22, 38], dtype=np.float32)
Y = np.array([-40, 14, 32, 46, 59, 72, 100], dtype=np.float32)

X = X.reshape((len(X), data_dimension))
Y = Y.reshape((len(Y), data_dimension))

opt = tf.keras.optimizers.SGD(learning_rate=0.5)
model.compile(opt, loss='mse',)

model.fit(X, Y, epochs=60, batch_size=1)

```

Note that, although training proceeds without error, you should look at changing the hyperparameters to improve the fit. For example, I recommend lowering the learning rate, increasing the number of qubits and increasing the depth.

It looks like your use of `weight_shapes` was fine. This is a dictionary that provides the shapes of the internal weights used in the `qnode`, so that `qlayer` can randomly initialize them. For example, if we had

```python
@qml.qnode(dev)
def qnode(inputs, w1, w2, w3):
    qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))
    qml.RX(w1, wires=0)
    qml.templates.BasicEntanglerLayers(w2, wires=range(n_qubits))
    qml.Rot(w3, wires=1)
    ...

```

We would have a corresponding

```python
weight_shapes = {"w1": 1, "w2": (layers, n_qubits), "w3": 3}

```

---

<div class="post-metadata">

**Author:** ![Hemant\_Gahankari](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/hemant_gahankari/32/332_2.png) [@Hemant\_Gahankari](https://discuss.pennylane.ai/u/Hemant_Gahankari)\
**Post date:** [October 15, 2020, 6:15pm UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/3 "2020-10-15T18:15:17Z")

</div>

Hi Tom ,

Thanks a lot the clarifications. It was really helpful.

In order to make it a bit simpler , I made a few changes to the code. Since deg c and Fahrenheit are linearly related like y = mx + c , I though of having two weights through RX and RZ gates.

if I just keep clayer1 , NN converges very quickly. But if I use qlayer or clayer1+qlayer or clayer1 + qlayer + clayer2 , NN does not converge.

Do you see any problem with my approach ?

```auto
import pennylane as qml
import tensorflow as tf
import sklearn.datasets
import numpy as np

n_qubits = 1
layers = 1
data_dimension = 1

dev = qml.device("default.qubit", wires=n_qubits)

@qml.qnode(dev)
def qnode(inputs, m, c):
    qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))
    qml.RX(m, wires=0)
    qml.RZ(c, wires=0)
    return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)]

    

weight_shapes = {"m": 1, "c":1}

qlayer = qml.qnn.KerasLayer(qnode, weight_shapes, output_dim=n_qubits)
clayer1 = tf.keras.layers.Dense(n_qubits)
clayer2 = tf.keras.layers.Dense(data_dimension, activation="linear")

model = tf.keras.models.Sequential([qlayer])

X = np.array([-40, -10, 0, 8, 15, 22, 38], dtype=float)
Y = np.array([-40, 14, 32, 46, 59, 72, 100], dtype=float)
X1 = X.reshape((len(X), 1))
Y1 = Y.reshape((len(Y), 1))

model.compile(loss='mean_squared_error',

              optimizer=tf.keras.optimizers.Adam(0.01))

```

---

<div class="post-metadata">

**Author:** ![Maria\_Schuld](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/maria_schuld/32/166_2.png) [@Maria\_Schuld](https://discuss.pennylane.ai/u/Maria_Schuld)\
**Post date:** [October 16, 2020, 6:38am UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/4 "2020-10-16T06:38:41Z")

</div>

Dear @Hemant\_Gehankari,

Your question ventures away from coding issues and into the wonderful world of QML research 🙂

I can only guess here (without doing the research myself), but one thing I would be aware of is the data input scaling. It’s a great exercise to actually look what the maths of the qlayer is doing: It is a transformation where the inputs enter as sine/cosine functions. Now, these functions are periodic, and you will have to make sure that the data entering the quantum layer lies roughly in an interval of length 2\pi. You can find some more information in [the second question of this discussion thread](http://discuss.pennylane.ai/t/about-bell-states/616).

---

<div class="post-metadata">

**Author:** ![Hemant\_Gahankari](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/hemant_gahankari/32/332_2.png) [@Hemant\_Gahankari](https://discuss.pennylane.ai/u/Hemant_Gahankari)\
**Post date:** [October 16, 2020, 11:26am UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/5 "2020-10-16T11:26:06Z")

</div>

Hi ,

What I thought is that AngleEmbedding is meant to embed data into quantum states, so that it can be processed further using quantum circuits. Correct me if I am wrong.

Also I am hoping to get this simple sample working so that acts as a basis for further exploration like more qbits - more features etc. It can also act as the simplest example for many new Pennylane users.

Hope we can make it work. As mentioned if we use just one classic layer clayer1 - just one neuron , it trains in seconds and is pretty accurate.

Request you folks to help.

---

<div class="post-metadata">

**Author:** ![theodor](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/theodor/32/178_2.png) [@theodor](https://discuss.pennylane.ai/u/theodor)\
**Post date:** [October 16, 2020, 6:12pm UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/6 "2020-10-16T18:12:51Z")

</div>

Hi @Hemant_Gahankari. If I may jump in as well to comment: you are correct that the `AngleEmbedding` template is used to embed classical values into quantum states which can then be used, and optimized, in quantum circuits.

Why it’s not converging to a solution when adding a qlayer is, as @Maria_Schuld mentioned, more of a research question and is difficult to answer without diving a bit deeper into the research itself. An idea would be, as also was touched upon above, to scale the data to lie within a 2\pi interval, since the data is embedded in angles, inside this range.

Let us know if you figure out a way to train this circuit!

---

<div class="post-metadata">

**Author:** ![Hemant\_Gahankari](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/hemant_gahankari/32/332_2.png) [@Hemant\_Gahankari](https://discuss.pennylane.ai/u/Hemant_Gahankari)\
**Post date:** [October 21, 2020, 7:33am UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/7 "2020-10-21T07:33:01Z")

</div>

Hello ,

I ran the the additional example code in,

[https://pennylane.readthedocs.io/en/stable/code/api/pennylane.qnn.KerasLayer.html](https://pennylane.readthedocs.io/en/stable/code/api/pennylane.qnn.KerasLayer.html)

colab notebook and checked the data created by `sklearn.datasets.make_moons()` , followed by `X = tf.constant(data[0])`

This data is fed to the model and qlayer. This data is not in the range of 0 to 2 pi. Looks like ,

```auto
qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))

```

converts it to the angles.

Now for my example I used the following code to rescale the data ,

```python
from sklearn.preprocessing import MinMaxScaler

X = np.array([-40, 0, 8, 15, 22, 38], dtype=np.float32)
Y = np.array([-40, 32, 46, 59, 72, 100], dtype=np.float32)

X = X.reshape((len(X), 1))
Y = Y.reshape((len(Y), 1))

scale_x = MinMaxScaler()
scale_y = MinMaxScaler()

x = scale_x.fit_transform(X)
y = scale_y.fit_transform(Y)

```

This solved the problem of network not converging. With scaled data, loss converges, just even with `qlayer` alone, but converges faster if I have `clayer1 + qlayer`. But I am still not sure why the data as is was not good for the network to converge.

I If you folks are interested I can share the fully working code.

---

<div class="post-metadata">

**Author:** ![Tom\_Bromley](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/tom_bromley/32/129_2.png) [@Tom\_Bromley](https://discuss.pennylane.ai/u/Tom_Bromley)\
**Post date:** [October 21, 2020, 2:01pm UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/8 "2020-10-21T14:01:51Z")

</div>

Thanks @Hemant_Gahankari!

It is quite common even in standard ML to [preprocess the data](https://scikit-learn.org/stable/modules/preprocessing.html#standardization-or-mean-removal-and-variance-scaling), so I’m not surprised we need to do it here too.

For `AngleEmbedding`, data does not necessarily need to be in the range `[0, 2pi)`. However, the embedding is periodic so that data outside this range has an equivalent point in `[0, 2pi)`. For example, we can encode `3pi` but it will be equivalent to `pi`. Hence, if we do not normalize our data to be within the `[0, 2pi)` interval then we run the risk of unique data points (e.g., `pi` and `3pi`) being mapped to the same embedded state. Of course, the `clayer1` could learn to normalize, but this should give a motivation of why things are tougher with unnormalized data.

Another thing to consider is that the output of the quantum layer is between -1 and 1. Without a subsequent classical layer, we definitely would not be able to hit temperatures outside of this region. However, if we normalize the data then this is now possible. Again, introducing a classical layer after the quantum layer gives us a bit more freedom, but it still might be tougher with unnormalized data.

Hope this helps!  
Tom

---

<div class="post-metadata">

**Author:** ![Hemant\_Gahankari](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/hemant_gahankari/32/332_2.png) [@Hemant\_Gahankari](https://discuss.pennylane.ai/u/Hemant_Gahankari)\
**Post date:** [October 21, 2020, 5:59pm UTC](https://discuss.pennylane.ai/t/keras-layer-single-qbit/618/9 "2020-10-21T17:59:32Z")

</div>

Thank you so much. Appreciate your time and help.
