Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
tensorflow
GitHub Repository: tensorflow/docs-l10n
Path: blob/master/site/pt-br/quantum/tutorials/mnist.ipynb
25118 views
Kernel: Python 3
#@title Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.

Classificação MNIST

Este tutorial cria uma rede neural quântica (QNN) para classificar uma versão simplificada do MNIST, semelhante à abordagem usada em Farhi et al. O desempenho da rede neural quântica nesse problema de dados clássicos é comparado a uma rede neural clássica.

Configuração

!pip install tensorflow==2.7.0

Instale o TensorFlow Quantum:

!pip install tensorflow-quantum==0.7.2
# Update package resources to account for version changes. import importlib, pkg_resources importlib.reload(pkg_resources)

Agora importe o TensorFlow e as dependências de módulo:

import tensorflow as tf import tensorflow_quantum as tfq import cirq import sympy import numpy as np import seaborn as sns import collections # visualization tools %matplotlib inline import matplotlib.pyplot as plt from cirq.contrib.svg import SVGCircuit

1. Carregue os dados

Neste tutorial, você criará um classificador binário para distinguir entre os dígitos 3 e 6, seguindo Farhi et al. Esta seção abrange o tratamento de dados que:

  • Carrega os dados brutos do Keras.

  • Filtra o dataset para apenas 3s e 6s.

  • Reduz a escala das imagens para que caibam em um computador quântico.

  • Remove qualquer exemplo contraditório.

  • Converte as imagens binárias em circuitos Cirq.

  • Converte os circuitos Cirq em circuitos do TensorFlow Quantum.

1.1 Carregue os dados brutos

Carregue o dataset MNIST distribuído com o Keras.

(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data() # Rescale the images from [0,255] to the [0.0,1.0] range. x_train, x_test = x_train[..., np.newaxis]/255.0, x_test[..., np.newaxis]/255.0 print("Number of original training examples:", len(x_train)) print("Number of original test examples:", len(x_test))

Filtre o dataset para manter apenas 3s e 6s, removendo as outras classes. Ao mesmo tempo, converta o rótulo, y, para booleano: True para 3 e False para 6.

def filter_36(x, y): keep = (y == 3) | (y == 6) x, y = x[keep], y[keep] y = y == 3 return x,y
x_train, y_train = filter_36(x_train, y_train) x_test, y_test = filter_36(x_test, y_test) print("Number of filtered training examples:", len(x_train)) print("Number of filtered test examples:", len(x_test))

Mostre o primeiro exemplo:

print(y_train[0]) plt.imshow(x_train[0, :, :, 0]) plt.colorbar()

1.2 Reduza a escala das imagens

Um tamanho de imagem 28 x 28 é muito grande para os computadores quânticos atuais. Redimensione a imagem para 4 x 4:

x_train_small = tf.image.resize(x_train, (4,4)).numpy() x_test_small = tf.image.resize(x_test, (4,4)).numpy()

Novamente, exiba o primeiro exemplo de treinamento, após redimensionar:

print(y_train[0]) plt.imshow(x_train_small[0,:,:,0], vmin=0, vmax=1) plt.colorbar()

1.3 Remova exemplos contraditórios

Na seção 3.3 Learning to Distinguish Digits (Aprendendo a distinguir dígitos) de Farhi et al., filtre o dataset para remover imagens rotuladas como pertencentes a ambas as classes.

Esse não é um procedimento de aprendizado de máquina padrão, mas foi incluído a fim de seguir o artigo.

def remove_contradicting(xs, ys): mapping = collections.defaultdict(set) orig_x = {} # Determine the set of labels for each unique image: for x,y in zip(xs,ys): orig_x[tuple(x.flatten())] = x mapping[tuple(x.flatten())].add(y) new_x = [] new_y = [] for flatten_x in mapping: x = orig_x[flatten_x] labels = mapping[flatten_x] if len(labels) == 1: new_x.append(x) new_y.append(next(iter(labels))) else: # Throw out images that match more than one label. pass num_uniq_3 = sum(1 for value in mapping.values() if len(value) == 1 and True in value) num_uniq_6 = sum(1 for value in mapping.values() if len(value) == 1 and False in value) num_uniq_both = sum(1 for value in mapping.values() if len(value) == 2) print("Number of unique images:", len(mapping.values())) print("Number of unique 3s: ", num_uniq_3) print("Number of unique 6s: ", num_uniq_6) print("Number of unique contradicting labels (both 3 and 6): ", num_uniq_both) print() print("Initial number of images: ", len(xs)) print("Remaining non-contradicting unique images: ", len(new_x)) return np.array(new_x), np.array(new_y)

Os números resultantes não coincidem muito com os valores relatados, mas o procedimento exato não é especificado.

Também vale a pena destacar que a filtração de exemplos contraditórios nesse ponto não evita completamente que o modelo receba exemplos de treinamento contraditórios: o próximo passo binariza os dados, o que causará ainda mais colisões.

x_train_nocon, y_train_nocon = remove_contradicting(x_train_small, y_train)

1.4 Codifique os dados como circuitos quânticos

Para processar as imagens usando um computador quântico, Farhi et al. propõem representar cada pixel com um qubit, com o estado dependendo do valor do pixel. O primeiro passo é convertê-lo a um formato binário.

THRESHOLD = 0.5 x_train_bin = np.array(x_train_nocon > THRESHOLD, dtype=np.float32) x_test_bin = np.array(x_test_small > THRESHOLD, dtype=np.float32)

Se você removesse as imagens contraditórias nesse ponto, ficaria com apenas 193, o que provavelmente não é o suficiente para um treinamento eficaz.

_ = remove_contradicting(x_train_bin, y_train_nocon)

Os qubits nos índices dos pixels com valores que excedem um limite são rotacionados através de uma porta XX.

def convert_to_circuit(image): """Encode truncated classical image into quantum datapoint.""" values = np.ndarray.flatten(image) qubits = cirq.GridQubit.rect(4, 4) circuit = cirq.Circuit() for i, value in enumerate(values): if value: circuit.append(cirq.X(qubits[i])) return circuit x_train_circ = [convert_to_circuit(x) for x in x_train_bin] x_test_circ = [convert_to_circuit(x) for x in x_test_bin]

Aqui está o circuito criado para o primeiro exemplo (os diagramas do circuito não mostram qubits com nenhuma porta):

SVGCircuit(x_train_circ[0])

Compare esse circuito aos índices em que o valor da imagem ultrapassa o limite:

bin_img = x_train_bin[0,:,:,0] indices = np.array(np.where(bin_img)).T indices

Converta esses circuitos do Cirq em tensores para o tfq:

x_train_tfcirc = tfq.convert_to_tensor(x_train_circ) x_test_tfcirc = tfq.convert_to_tensor(x_test_circ)

2. Rede neural quântica

Há pouca orientação para uma estrutura de circuito quântico que classifica imagens. Como a classificação é baseada no valor esperado do qubit de leitura, Farhi et al. propõem usar duas portas de qubits, sempre agindo no qubit de leitura. Em alguns aspectos, isso se assemelha à execução de uma RNN unitária pequena nos pixels.

2.1 Crie o circuito do modelo

O exemplo a seguir mostra essa abordagem em camadas. Cada camada usa instâncias n da mesma porta, com cada qubit de dados agindo no qubit de leitura.

Comece com uma classe simples que adicionará uma camada dessas portas a um circuito:

class CircuitLayerBuilder(): def __init__(self, data_qubits, readout): self.data_qubits = data_qubits self.readout = readout def add_layer(self, circuit, gate, prefix): for i, qubit in enumerate(self.data_qubits): symbol = sympy.Symbol(prefix + '-' + str(i)) circuit.append(gate(qubit, self.readout)**symbol)

Crie uma camada do circuito de exemplo para ver como fica:

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)

Agora crie um modelo de duas camadas, correspondendo ao tamanho do circuito de dados, e inclua as operações de preparação e leitura.

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()

2.2 Envolva o circuito do modelo em um modelo tfq-keras

Crie o modelo Keras com os componentes quânticos. Esse modelo é alimentado com os "dados quânticos", de x_train_circ, que codifica os dados clássicos. Ele usa uma camada de Circuito Quântico Parametrizado, tfq.layers.PQC, para treinar o circuito do modelo, com os dados quânticos

Para classificar essas imagens, Farhi et al. sugerem obter o valor esperado de um qubit de leitura em um circuito parametrizado, que fica entre 1 e -1.

# 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), ])

Em seguida, descreva o processo de treinamento do modelo, usando o método compile.

Como a leitura esperada está no intervalo [-1,1], a otimização da perda de articulação é uma opção um tanto natural.

Observação: outra abordagem válida seria mudar o intervalo de saída para [0,1] e tratá-lo como a probabilidade que o modelo atribui à classe 3. Isso pode ser usado com uma perda tf.losses.BinaryCrossentropy padrão.

Para usar a perda de articulação aqui, você precisa fazer dois pequenos ajustes. Primeiro, converta os rótulos, y_train_nocon, de booleano para [-1,1], conforme esperado pela perda de articulação.

y_train_hinge = 2.0*y_train_nocon-1.0 y_test_hinge = 2.0*y_test-1.0

Segundo, use uma métrica hinge_accuracy personalizada que lida com [-1, 1] corretamente como o argumento dos rótulos y_true. tf.losses.BinaryAccuracy(threshold=0.0) espera que y_true seja um booleano, e ele não pode ser usado com a perda de articulação).

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())

Treine o modelo quântico

Agora treine o modelo, o que leva cerca de 45 minutos. Se você não quiser esperar tudo isso, use um subconjunto menor de dados (defina NUM_EXAMPLES=500, abaixo). Isso não afeta o progresso do modelo durante o treinamento (ele só tem 32 parâmetros e não precisa de muitos dados para restringi-los). O uso de menos exemplos só acaba o treinamento mais cedo (5 minutos), mas é executado por tempo suficiente para mostrar que está fazendo progresso nos logs de validação.

EPOCHS = 3 BATCH_SIZE = 32 NUM_EXAMPLES = len(x_train_tfcirc)
x_train_tfcirc_sub = x_train_tfcirc[:NUM_EXAMPLES] y_train_hinge_sub = y_train_hinge[:NUM_EXAMPLES]

O treinamento desse modelo até a convergência deve alcançar uma exatidão >85% no dataset de teste.

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)

Observação: a exatidão do treinamento informa a média durante a época. A exatidão da validação é avaliada ao final de cada época.

3. Rede neural clássica

Enquanto a rede neural quântica funciona com esse problema MNIST simplificado, uma rede neural clássica básica pode facilmente superar uma QNN nessa tarefa. Depois de uma única época, uma rede neural clássica consegue alcançar uma exatidão >98% no set de holdout.

No exemplo a seguir, é usada uma rede neural clássica para o problema de classificação 3-6 com a imagem 28 x 28 inteira, em vez de fazer a subamostragem da imagem. Isso facilmente converge para uma exatidão de quase 100% no dataset de teste.

def create_classical_model(): # A simple model based off LeNet from https://keras.io/examples/mnist_cnn/ model = tf.keras.Sequential() model.add(tf.keras.layers.Conv2D(32, [3, 3], activation='relu', input_shape=(28,28,1))) model.add(tf.keras.layers.Conv2D(64, [3, 3], activation='relu')) model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2))) model.add(tf.keras.layers.Dropout(0.25)) model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(128, activation='relu')) model.add(tf.keras.layers.Dropout(0.5)) model.add(tf.keras.layers.Dense(1)) return model model = create_classical_model() model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), optimizer=tf.keras.optimizers.Adam(), metrics=['accuracy']) model.summary()
model.fit(x_train, y_train, batch_size=128, epochs=1, verbose=1, validation_data=(x_test, y_test)) cnn_results = model.evaluate(x_test, y_test)

O modelo acima tem quase 1,2 milhão de parâmetros. Para uma comparação mais justa, tente usar um modelo de 37 parâmetros, nas imagens de subamostragem:

def create_fair_classical_model(): # A simple model based off LeNet from https://keras.io/examples/mnist_cnn/ model = tf.keras.Sequential() model.add(tf.keras.layers.Flatten(input_shape=(4,4,1))) model.add(tf.keras.layers.Dense(2, activation='relu')) model.add(tf.keras.layers.Dense(1)) return model model = create_fair_classical_model() model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True), optimizer=tf.keras.optimizers.Adam(), metrics=['accuracy']) model.summary()
model.fit(x_train_bin, y_train_nocon, batch_size=128, epochs=20, verbose=2, validation_data=(x_test_bin, y_test)) fair_nn_results = model.evaluate(x_test_bin, y_test)

4. Comparação

Uma entrada de resolução mais alta e um modelo mais avançado facilita esse problema para a CNN, enquanto um modelo clássico de poder semelhante (cerca de 32 parâmetros) treina com uma exatidão parecida em uma fração do tempo. De qualquer maneira, a rede neural clássica supera facilmente a rede neural quântica. Para dados clássicos, é difícil superar uma rede neural clássica.

qnn_accuracy = qnn_results[1] cnn_accuracy = cnn_results[1] fair_nn_accuracy = fair_nn_results[1] sns.barplot(x=["Quantum", "Classical, full", "Classical, fair"], y=[qnn_accuracy, cnn_accuracy, fair_nn_accuracy])