<jupyter_start><jupyter_text>**Transform classical data into quantum data (quantum circuit)**<jupyter_code>## Prepare classical data
(x_train, y_train), (x_test, y_test) = load_mnist(prepro=True)
#show_meta_info(x_train, x_test, y_train)

## Reduce the origianl task into binary-classification
x_train, y_train = filter_36(x_train, y_train)
x_test, y_test = filter_36(x_test, y_test)
#print("\n\n After preprocessing : \n\n")
#show_meta_info(x_train, x_test, y_train)

## Down-sampling the data itself for fitting the qbit-limitation (about 20 bits)
dwn_im = lambda im, dwn_shap : tf.image.resize(im, dwn_shap).numpy()
x_tra_dwn = dwn_im(x_train, (4, 4))
x_tst_dwn = dwn_im(x_test, (4, 4))   ## 4 x 4 = 16 < 20 bit hardware limitation..
x_tra, y_tra = remove_contradicting(x_tra_dwn, y_train) # dwn_im may let the img become similar
#show_meta_info(x_tra, x_tst_dwn, y_tra)

## Encode the data as quantum circuits
THRESHOLD = 0.5
x_tra_bin = np.array(x_tra > THRESHOLD, dtype=np.float32)
x_tst_bin = np.array(x_tst_dwn > THRESHOLD, dtype=np.float32)
#_ = remove_contradicting(x_train_bin, y_tra)  # num of data may not enough..
show_meta_info(x_tra_bin, x_tst_bin, y_tra)

## package binary image into quantum circuit
x_tra_circ = [ convert_to_circuit(bin_im) for bin_im in x_tra_bin ]
x_tst_circ = [ convert_to_circuit(bin_im) for bin_im in x_tst_bin ]
SVGCircuit(x_tra_circ[0])<jupyter_output>Number of unique images: 10387
Number of unique 3s:  4912
Number of unique 6s:  5426
Number of unique contradicting labels (both 3 and 6):  49

Initial number of images:  12049
Remaining non-contradicting unique images:  10338<jupyter_text>**Build Qauntum Classifier**<jupyter_code>## Convert circuit into tf.tensor
x_train_tfcirc = tfq.convert_to_tensor(x_train_circ)
x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)

demo_builder = CircuitLayerBuilder(data_qubits = cirq.GridQubit.rect(4,1),
                                   readout=cirq.GridQubit(-1,-1))

circuit = cirq.Circuit()
demo_builder.add_layer(circuit, gate = cirq.XX, prefix='xx')
SVGCircuit(circuit)
def create_quantum_model():
    """Create a QNN model circuit and readout operation to go along with it."""
    data_qubits = cirq.GridQubit.rect(4, 4)  # a 4x4 grid.
    readout = cirq.GridQubit(-1, -1)         # a single qubit at [-1,-1]
    circuit = cirq.Circuit()
    
    # Prepare the readout qubit.
    circuit.append(cirq.X(readout))
    circuit.append(cirq.H(readout))
    
    builder = CircuitLayerBuilder(
        data_qubits = data_qubits,
        readout=readout)

    # Then add layers (experiment by adding more).
    builder.add_layer(circuit, cirq.XX, "xx1")
    builder.add_layer(circuit, cirq.ZZ, "zz1")

    # Finally, prepare the readout qubit.
    circuit.append(cirq.H(readout))

    return circuit, cirq.Z(readout)

model_circuit, model_readout = create_quantum_model()
# Build the Keras model.
model = tf.keras.Sequential([
    # The input is the data-circuit, encoded as a tf.string
    tf.keras.layers.Input(shape=(), dtype=tf.string),
    # The PQC layer returns the expected value of the readout gate, range [-1,1].
    tfq.layers.PQC(model_circuit, model_readout),
])<jupyter_output><empty_output><jupyter_text>Next, describe the training procedure to the model, using the `compile` method.

Since the the expected readout is in the range `[-1,1]`, optimizing the hinge loss is a somewhat natural fit. 

Note: Another valid approach would be to shift the output range to `[0,1]`, and treat it as the probability the model assigns to class `3`. This could be used with a standard a `tf.losses.BinaryCrossentropy` loss.

To use the hinge loss here you need to make two small adjustments. First convert the labels, `y_train_nocon`, from boolean to `[-1,1]`, as expected by the hinge loss.<jupyter_code>y_tra_hinge = 2.0*y_tra-1.0
y_tst_hinge = 2.0*y_tst-1.0

def hinge_accuracy(y_true, y_pred):
    y_true = tf.squeeze(y_true) > 0.0
    y_pred = tf.squeeze(y_pred) > 0.0
    result = tf.cast(y_true == y_pred, tf.float32)

    return tf.reduce_mean(result)

model.compile(
    loss=tf.keras.losses.Hinge(),
    optimizer=tf.keras.optimizers.Adam(),
    metrics=[hinge_accuracy])

print(model.summary())
EPOCHS = 1
BATCH_SIZE = 32

NUM_EXAMPLES = len(x_tra_tfcirc)
x_train_tfcirc_sub = x_train_tfcirc[:NUM_EXAMPLES]
y_train_hinge_sub = y_train_hinge[:NUM_EXAMPLES]
qnn_history = model.fit(
      x_train_tfcirc_sub, y_train_hinge_sub,
      batch_size=32,
      epochs=EPOCHS,
      verbose=1,
      validation_data=(x_test_tfcirc, y_test_hinge))

qnn_results = model.evaluate(x_test_tfcirc, y_test)<jupyter_output>Train on 10338 samples, validate on 1968 samples
Epoch 1/3
  832/10338 [=>............................] - ETA: 44:03 - loss: 1.0300 - hinge_accuracy: 0.3225
{'fraction_non_alphanumeric': 0.11081359423274974, 'fraction_numerical': 0.026364572605561278, 'mean_word_length': 3.9958847736625516, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '522035', 'license': 'Apache-2.0', 'n_tokens_mistral': 1923, 'n_tokens_neox': 1794, 'n_words': 442, 'path': 'q_mnist.ipynb', 'repo_name': 'HuangChiEn/tfq_tutorial_refactor', 'size': '4855'}