ที่ราบสูงที่แห้งแล้ง

ดูบน TensorFlow.org ทำงานใน Google Colab ดูแหล่งที่มาบน GitHub ดาวน์โหลดโน๊ตบุ๊ค

ในตัวอย่างนี้ คุณจะสำรวจผลลัพธ์ของ McClean ปี 2019 ที่บอกว่าไม่ใช่แค่โครงสร้างเครือข่ายนิวรัลควอนตัมเท่านั้นที่จะทำได้ดีเมื่อพูดถึงการเรียนรู้ โดยเฉพาะอย่างยิ่ง คุณจะเห็นว่าวงจรควอนตัมสุ่มกลุ่มใหญ่บางกลุ่มไม่ได้ทำหน้าที่เป็นเครือข่ายนิวรัลควอนตัมที่ดี เพราะมันมีการไล่ระดับสีที่หายไปเกือบทุกที่ ในตัวอย่างนี้ คุณจะไม่ได้ฝึกแบบจำลองใดๆ สำหรับปัญหาการเรียนรู้ที่เฉพาะเจาะจง แต่ให้เน้นที่ปัญหาที่ง่ายกว่าในการทำความเข้าใจพฤติกรรมของการไล่ระดับสี

ติดตั้ง

pip install tensorflow==2.7.0

ติดตั้ง TensorFlow Quantum:

pip install tensorflow-quantum
# Update package resources to account for version changes.
import importlib, pkg_resources
importlib.reload(pkg_resources)
<module 'pkg_resources' from '/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/pkg_resources/__init__.py'>

ตอนนี้นำเข้า TensorFlow และการพึ่งพาโมดูล:

import tensorflow as tf
import tensorflow_quantum as tfq

import cirq
import sympy
import numpy as np

# visualization tools
%matplotlib inline
import matplotlib.pyplot as plt
from cirq.contrib.svg import SVGCircuit

np.random.seed(1234)
2022-02-04 12:15:43.355568: E tensorflow/stream_executor/cuda/cuda_driver.cc:271] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

1. สรุป

วงจรควอนตัมสุ่มที่มีบล็อกจำนวนมากที่มีลักษณะเช่นนี้ (\(R_{P}(\theta)\) คือการหมุน Pauli แบบสุ่ม):

โดยที่ \(f(x)\) ถูกกำหนดให้เป็นค่าความคาดหวัง wrt \(Z_{a}Z_{b}\) สำหรับ qubits ใด ๆ \(a\) และ \(b\)แสดงว่ามีปัญหาที่ \(f'(x)\) มีค่าเฉลี่ยใกล้เคียงกับ 0 มาก และไม่แตกต่างกันมากนัก คุณจะเห็นสิ่งนี้ด้านล่าง:

2. การสร้างวงจรสุ่ม

การก่อสร้างจากกระดาษนั้นตรงไปตรงมา ต่อไปนี้ใช้ฟังก์ชันง่าย ๆ ที่สร้างวงจรควอนตัมแบบสุ่ม ซึ่งบางครั้งเรียกว่า เครือข่ายประสาทควอนตัม (QNN) ด้วยความลึกที่กำหนดในชุดของ qubits:

def generate_random_qnn(qubits, symbol, depth):
    """Generate random QNN's with the same structure from McClean et al."""
    circuit = cirq.Circuit()
    for qubit in qubits:
        circuit += cirq.ry(np.pi / 4.0)(qubit)

    for d in range(depth):
        # Add a series of single qubit rotations.
        for i, qubit in enumerate(qubits):
            random_n = np.random.uniform()
            random_rot = np.random.uniform(
            ) * 2.0 * np.pi if i != 0 or d != 0 else symbol
            if random_n > 2. / 3.:
                # Add a Z.
                circuit += cirq.rz(random_rot)(qubit)
            elif random_n > 1. / 3.:
                # Add a Y.
                circuit += cirq.ry(random_rot)(qubit)
            else:
                # Add a X.
                circuit += cirq.rx(random_rot)(qubit)

        # Add CZ ladder.
        for src, dest in zip(qubits, qubits[1:]):
            circuit += cirq.CZ(src, dest)

    return circuit


generate_random_qnn(cirq.GridQubit.rect(1, 3), sympy.Symbol('theta'), 2)

ผู้เขียนตรวจสอบความลาดชันของพารามิเตอร์เดียว \(\theta_{1,1}\)ตามด้วยการวาง sympy.Symbol ในวงจรที่ \(\theta_{1,1}\) จะเป็น เนื่องจากผู้เขียนไม่ได้วิเคราะห์สถิติสำหรับสัญลักษณ์อื่นใดในวงจร เรามาแทนที่ด้วยค่าสุ่มตอนนี้แทนในภายหลัง

3. การเดินวงจร

สร้างวงจรเหล่านี้บางส่วนพร้อมกับสิ่งที่สังเกตได้เพื่อทดสอบการอ้างสิทธิ์ว่าการไล่ระดับสีไม่แตกต่างกันมากนัก ขั้นแรก สร้างชุดของวงจรสุ่ม เลือกสุ่ม ZZ ที่สังเกตได้และคำนวณแบทช์และความแปรปรวนโดยใช้ TensorFlow Quantum

3.1 การคำนวณความแปรปรวนแบบแบตช์

มาเขียนฟังก์ชันตัวช่วยที่คำนวณความแปรปรวนของการไล่ระดับของค่าที่สังเกตได้ผ่านชุดของวงจร:

def process_batch(circuits, symbol, op):
    """Compute the variance of a batch of expectations w.r.t. op on each circuit that 
    contains `symbol`. Note that this method sets up a new compute graph every time it is
    called so it isn't as performant as possible."""

    # Setup a simple layer to batch compute the expectation gradients.
    expectation = tfq.layers.Expectation()

    # Prep the inputs as tensors
    circuit_tensor = tfq.convert_to_tensor(circuits)
    values_tensor = tf.convert_to_tensor(
        np.random.uniform(0, 2 * np.pi, (n_circuits, 1)).astype(np.float32))

    # Use TensorFlow GradientTape to track gradients.
    with tf.GradientTape() as g:
        g.watch(values_tensor)
        forward = expectation(circuit_tensor,
                              operators=op,
                              symbol_names=[symbol],
                              symbol_values=values_tensor)

    # Return variance of gradients across all circuits.
    grads = g.gradient(forward, values_tensor)
    grad_var = tf.math.reduce_std(grads, axis=0)
    return grad_var.numpy()[0]

3.1 ตั้งค่าและเรียกใช้

เลือกจำนวนวงจรสุ่มเพื่อสร้างพร้อมกับความลึกและจำนวน qubit ที่ควรดำเนินการ จากนั้นพล็อตผลลัพธ์

n_qubits = [2 * i for i in range(2, 7)
           ]  # Ranges studied in paper are between 2 and 24.
depth = 50  # Ranges studied in paper are between 50 and 500.
n_circuits = 200
theta_var = []

for n in n_qubits:
    # Generate the random circuits and observable for the given n.
    qubits = cirq.GridQubit.rect(1, n)
    symbol = sympy.Symbol('theta')
    circuits = [
        generate_random_qnn(qubits, symbol, depth) for _ in range(n_circuits)
    ]
    op = cirq.Z(qubits[0]) * cirq.Z(qubits[1])
    theta_var.append(process_batch(circuits, symbol, op))

plt.semilogy(n_qubits, theta_var)
plt.title('Gradient Variance in QNNs')
plt.xlabel('n_qubits')
plt.xticks(n_qubits)
plt.ylabel('$\\partial \\theta$ variance')
plt.show()
WARNING:tensorflow:5 out of the last 5 calls to <function Adjoint.differentiate_analytic at 0x7f9e3b5c68c0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
ตัวยึดตำแหน่ง22

png

พล็อตนี้แสดงให้เห็นว่าสำหรับปัญหาการเรียนรู้ของเครื่องควอนตัม คุณไม่สามารถเดาสุ่ม QNN ansatz และหวังว่าจะดีที่สุด โครงสร้างบางอย่างต้องมีอยู่ในวงจรแบบจำลองเพื่อให้การไล่ระดับสีแปรผันจนถึงจุดที่การเรียนรู้สามารถเกิดขึ้นได้

4. ฮิวริสติก

ฮิวริสติกที่น่าสนใจโดย Grant ปี 2019 เปิดโอกาสให้ผู้ใช้เริ่มสุ่มได้ใกล้เคียงกันมาก แต่ก็ไม่ทั้งหมด การใช้วงจรเดียวกันกับ McClean et al. ผู้เขียนเสนอเทคนิคการเริ่มต้นที่แตกต่างกันสำหรับพารามิเตอร์การควบคุมแบบคลาสสิกเพื่อหลีกเลี่ยงที่ราบสูงที่แห้งแล้ง เทคนิคการเริ่มต้นจะเริ่มต้นบางเลเยอร์ด้วยพารามิเตอร์ควบคุมแบบสุ่มทั้งหมด—แต่ในเลเยอร์ที่ตามมาทันที ให้เลือกพารามิเตอร์เพื่อที่การเปลี่ยนแปลงเริ่มต้นที่ทำโดยสองสามเลเยอร์แรกจะถูกยกเลิก ผู้เขียนเรียกสิ่งนี้ว่า บล็อกข้อมูลประจำตัว

ข้อได้เปรียบของฮิวริสติกนี้คือการเปลี่ยนเพียงพารามิเตอร์เดียว บล็อกอื่นๆ ทั้งหมดที่อยู่นอกบล็อกปัจจุบันจะยังคงเป็นเอกลักษณ์—และสัญญาณเกรเดียนต์จะแข็งแกร่งกว่าเมื่อก่อนมาก ซึ่งช่วยให้ผู้ใช้สามารถเลือกและเลือกตัวแปรและบล็อกที่จะปรับเปลี่ยนเพื่อรับสัญญาณเกรเดียนต์ที่แรงได้ การวิเคราะห์พฤติกรรมนี้ไม่ได้ป้องกันผู้ใช้จากการตกสู่ที่ราบสูงที่แห้งแล้งในระหว่างขั้นตอนการฝึก (และจำกัดการอัปเดตพร้อมกันอย่างสมบูรณ์) เพียงแต่รับประกันว่าคุณสามารถเริ่มต้นนอกที่ราบสูงได้

4.1 การก่อสร้าง QNN ใหม่

ตอนนี้สร้างฟังก์ชันเพื่อสร้าง QNN บล็อกข้อมูลประจำตัว การใช้งานนี้แตกต่างไปจากเอกสารเล็กน้อย สำหรับตอนนี้ ให้ดูพฤติกรรมของการไล่ระดับของพารามิเตอร์เดียวเพื่อให้สอดคล้องกับ McClean et al ดังนั้นจึงทำให้เข้าใจง่ายขึ้น

ในการสร้างบล็อคข้อมูลประจำตัวและฝึกโมเดล โดยทั่วไปคุณต้องมี \(U1(\theta_{1a}) U1(\theta_{1b})^{\dagger}\) ไม่ใช่ \(U1(\theta_1) U1(\theta_1)^{\dagger}\)เริ่มแรก \(\theta_{1a}\) และ \(\theta_{1b}\) เป็นมุมเดียวกัน แต่เรียนรู้อย่างอิสระ มิฉะนั้น คุณจะได้รับข้อมูลประจำตัวเสมอแม้หลังจากการฝึก ทางเลือกสำหรับจำนวนบล็อคข้อมูลประจำตัวนั้นเป็นแบบประจักษ์ ยิ่งบล็อกลึก ความแปรปรวนตรงกลางบล็อกยิ่งเล็กลง แต่ที่จุดเริ่มต้นและจุดสิ้นสุดของบล็อก ความแปรปรวนของการไล่ระดับพารามิเตอร์ควรมีขนาดใหญ่

def generate_identity_qnn(qubits, symbol, block_depth, total_depth):
    """Generate random QNN's with the same structure from Grant et al."""
    circuit = cirq.Circuit()

    # Generate initial block with symbol.
    prep_and_U = generate_random_qnn(qubits, symbol, block_depth)
    circuit += prep_and_U

    # Generate dagger of initial block without symbol.
    U_dagger = (prep_and_U[1:])**-1
    circuit += cirq.resolve_parameters(
        U_dagger, param_resolver={symbol: np.random.uniform() * 2 * np.pi})

    for d in range(total_depth - 1):
        # Get a random QNN.
        prep_and_U_circuit = generate_random_qnn(
            qubits,
            np.random.uniform() * 2 * np.pi, block_depth)

        # Remove the state-prep component
        U_circuit = prep_and_U_circuit[1:]

        # Add U
        circuit += U_circuit

        # Add U^dagger
        circuit += U_circuit**-1

    return circuit


generate_identity_qnn(cirq.GridQubit.rect(1, 3), sympy.Symbol('theta'), 2, 2)

4.2 การเปรียบเทียบ

ที่นี่คุณจะเห็นว่าฮิวริสติกช่วยรักษาความแปรปรวนของการไล่ระดับสีไม่ให้หายไปอย่างรวดเร็ว:

block_depth = 10
total_depth = 5

heuristic_theta_var = []

for n in n_qubits:
    # Generate the identity block circuits and observable for the given n.
    qubits = cirq.GridQubit.rect(1, n)
    symbol = sympy.Symbol('theta')
    circuits = [
        generate_identity_qnn(qubits, symbol, block_depth, total_depth)
        for _ in range(n_circuits)
    ]
    op = cirq.Z(qubits[0]) * cirq.Z(qubits[1])
    heuristic_theta_var.append(process_batch(circuits, symbol, op))

plt.semilogy(n_qubits, theta_var)
plt.semilogy(n_qubits, heuristic_theta_var)
plt.title('Heuristic vs. Random')
plt.xlabel('n_qubits')
plt.xticks(n_qubits)
plt.ylabel('$\\partial \\theta$ variance')
plt.show()
WARNING:tensorflow:6 out of the last 6 calls to <function Adjoint.differentiate_analytic at 0x7f9e3b5c68c0> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.

png

นี่เป็นการปรับปรุงที่ยอดเยี่ยมในการรับสัญญาณเกรเดียนต์ที่แรงขึ้นจาก (ใกล้) QNN แบบสุ่ม