Korean
언어
English
Bengali
French
Hindi
Italian
Japanese
Korean
Malayalam
Russian
Spanish
Tamil
Turkish
Vietnamese
Shortcuts

참고

이 페이지는 docs/tutorials/09_saving_and_loading models.ipynb 에서 생성되었다.

저장, Qiskit 머신 러닝 모델 로딩 및 연속 학습

이 튜토리얼에서는 Qiskit 머신 러닝 모델을 저장하고 로드하는 방법을 다룬다. 모델을 저장하는 기능은 특히 실제 하드웨어에서 모델을 학습하는 데에 많은 시간을 소모되는 경우 매우 중요합니다. 또한 이전에 저장한 학습 모델을 재 사용하는 방법을 보여준다.

이 사용 지침서에서는 다음을 수행하는 방법에 대해 설명한다:

  • 간단한 데이터셋을 생성하고 학습/테스트 데이터셋으로 분할하고 그래프를 그려본다.

  • 모델 훈련후 저장

  • 저장된 모델 로드 및 학습 재개

  • 모델 성능 평가

  • PyTorch 하이브리드 모델

먼저 필수적인 라이브러리들을 불러들인다. 데이터 준비단계에서는 SciKit-Learn을 아주 많이 사용할 것이다. 다음 셀에서는 결과를 재현할 수 있도록 랜덤 시드값을 고정한다.

[1]:
import matplotlib.pyplot as plt
import numpy as np
from qiskit import Aer
from qiskit.algorithms.optimizers import COBYLA
from qiskit.circuit.library import RealAmplitudes
from qiskit.utils import QuantumInstance, algorithm_globals
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder, MinMaxScaler

from qiskit_machine_learning.algorithms.classifiers import VQC

from IPython.display import clear_output

algorithm_globals.random_seed = 42

두 개의 양자 시뮬레이터를 사용한다. QASM simulator를 먼저 학습에 사용한 후 statevector simulator를 사용해 학습을 재개한다. 이 튜토리얼의 접근 방식을 사용하여 클라우드에서 사용 가능한 실제 하드웨어에서 모델을 학습한 다음 로컬 시뮬레이터에서 추론을 위해 모델을 재사용한다.

[2]:
qi_qasm = QuantumInstance(
    Aer.get_backend("aer_simulator"),
    shots=1024,
    seed_simulator=algorithm_globals.random_seed,
    seed_transpiler=algorithm_globals.random_seed,
)

qi_sv = QuantumInstance(
    Aer.get_backend("aer_simulator_statevector"),
    seed_simulator=algorithm_globals.random_seed,
    seed_transpiler=algorithm_globals.random_seed,
)

1. 데이터셋 준비

Next step is to prepare a dataset. Here, we generate some data in the same way as in other tutorials. The difference is that we apply some transformations to the generated data. We generates 40 samples, each sample has 2 features, so our features is an array of shape (40, 2). Labels are obtained by summing up features by columns and if the sum is more than 1 then this sample is labeled as 1 and 0 otherwise.

[3]:
num_samples = 40
num_features = 2
features = 2 * algorithm_globals.random.random([num_samples, num_features]) - 1
labels = 1 * (np.sum(features, axis=1) >= 0)  # in { 0,  1}

Then, we scale down our features into a range of [0, 1] by applying MinMaxScaler from SciKit-Learn. Model training convergence is better when this transformation is applied.

[4]:
features = MinMaxScaler().fit_transform(features)
features.shape
[4]:
(40, 2)

Let’s take a look at the features of the first 5 samples of our dataset after the transformation.

[5]:
features[0:5, :]
[5]:
array([[0.79067335, 0.44566143],
       [0.88072937, 0.7126244 ],
       [0.06741233, 1.        ],
       [0.7770372 , 0.80422817],
       [0.10351936, 0.45754615]])

We choose VQC or Variational Quantum Classifier as a model we will train. This model, by default, takes one-hot encoded labels, so we have to transform the labels that are in the set of {0, 1} into one-hot representation. We employ SciKit-Learn for this transformation as well. Please note that the input array must be reshaped to (num_samples, 1) first. The OneHotEncoder encoder does not work with 1D arrays and our labels is a 1D array. In this case a user must decide either an array has only one feature(our case!) or has one sample. Also, by default the encoder returns sparse arrays, but for dataset plotting it is easier to have dense arrays, so we set sparse to False.

[6]:
labels = OneHotEncoder(sparse=False).fit_transform(labels.reshape(-1, 1))
labels.shape
[6]:
(40, 2)

Let’s take a look at the labels of the first 5 labels of the dataset. The labels should be one-hot encoded.

[7]:
labels[0:5, :]
[7]:
array([[0., 1.],
       [0., 1.],
       [0., 1.],
       [0., 1.],
       [1., 0.]])

Now we split our dataset into two parts: a training dataset and a test one. As a rule of thumb, 80% of a full dataset should go into a training part and 20% into a test one. Our training dataset has 30 samples. The test dataset should be used only once, when a model is trained to verify how well the model behaves on unseen data. We employ train_test_split from SciKit-Learn.

[8]:
train_features, test_features, train_labels, test_labels = train_test_split(
    features, labels, train_size=30, random_state=algorithm_globals.random_seed
)
train_features.shape
[8]:
(30, 2)

Now it is time to see how our dataset looks like. Let’s plot it.

[9]:
def plot_dataset():
    plt.scatter(
        train_features[np.where(train_labels[:, 0] == 0), 0],
        train_features[np.where(train_labels[:, 0] == 0), 1],
        marker="o",
        color="b",
        label="Label 0 train",
    )
    plt.scatter(
        train_features[np.where(train_labels[:, 0] == 1), 0],
        train_features[np.where(train_labels[:, 0] == 1), 1],
        marker="o",
        color="g",
        label="Label 1 train",
    )

    plt.scatter(
        test_features[np.where(test_labels[:, 0] == 0), 0],
        test_features[np.where(test_labels[:, 0] == 0), 1],
        marker="o",
        facecolors="w",
        edgecolors="b",
        label="Label 0 test",
    )
    plt.scatter(
        test_features[np.where(test_labels[:, 0] == 1), 0],
        test_features[np.where(test_labels[:, 0] == 1), 1],
        marker="o",
        facecolors="w",
        edgecolors="g",
        label="Label 1 test",
    )

    plt.legend(bbox_to_anchor=(1.05, 1), loc="upper left", borderaxespad=0.0)
    plt.plot([1, 0], [0, 1], "--", color="black")


plot_dataset()
plt.show()
../_images/tutorials_09_saving_and_loading_models_18_0.png

On the plot above we see:

  • Solid blue dots are the samples from the training dataset labeled as 0

  • Empty blue dots are the samples from the test dataset labeled as 0

  • Solid green dots are the samples from the training dataset labeled as 1

  • Empty green dots are the samples from the test dataset labeled as 1

We’ll train our model using solid dots and verify it using empty dots.

2. Train a model and save it

We’ll train our model in two steps. On the first step we train our model in 20 iterations.

[10]:
maxiter = 20

Create an empty array for callback to store values of the objective function.

[11]:
objective_values = []

We re-use a callback function from the Neural Network Classifier & Regressor tutorial to plot iteration versus objective function value with some minor tweaks to plot objective values at each step.

[12]:
# callback function that draws a live plot when the .fit() method is called
def callback_graph(_, objective_value):
    clear_output(wait=True)
    objective_values.append(objective_value)

    plt.title("Objective function value against iteration")
    plt.xlabel("Iteration")
    plt.ylabel("Objective function value")

    stage1_len = np.min((len(objective_values), maxiter))
    stage1_x = np.linspace(1, stage1_len, stage1_len)
    stage1_y = objective_values[:stage1_len]

    stage2_len = np.max((0, len(objective_values) - maxiter))
    stage2_x = np.linspace(maxiter, maxiter + stage2_len - 1, stage2_len)
    stage2_y = objective_values[maxiter : maxiter + stage2_len]

    plt.plot(stage1_x, stage1_y, color="orange")
    plt.plot(stage2_x, stage2_y, color="purple")
    plt.show()


plt.rcParams["figure.figsize"] = (12, 6)

As mentioned above we train a VQC model and set COBYLA as an optimizer with a chosen value of the maxiter parameter. Then we evaluate performance of the model to see how well it was trained. Then we save this model for a file. On the second step we load this model and will continue to work with it.

Here, we manually construct an ansatz to fix an initial point where to start optimization from.

[13]:
original_optimizer = COBYLA(maxiter=maxiter)

ansatz = RealAmplitudes(num_features)
initial_point = np.asarray([0.5] * ansatz.num_parameters)

We create a model and set a quantum instance to the QASM simulator we created earlier.

[14]:
original_classifier = VQC(
    ansatz=ansatz, optimizer=original_optimizer, callback=callback_graph, quantum_instance=qi_qasm
)

Now it is time to train the model.

[15]:
original_classifier.fit(train_features, train_labels)
../_images/tutorials_09_saving_and_loading_models_31_0.png
[15]:
<qiskit_machine_learning.algorithms.classifiers.vqc.VQC at 0x16ec7cdf488>

Let’s see how well our model performs after the first step of training.

[16]:
print("Train score", original_classifier.score(train_features, train_labels))
print("Test score ", original_classifier.score(test_features, test_labels))
Train score 0.8
Test score  0.8

Next, we save the model. You may choose any file name you want. Please note that the save method does not append an extension if it is not specified in the file name.

[17]:
original_classifier.save("vqc_classifier.model")

3. Load a model and continue training

To load a model a user have to call a class method load of the corresponding model class. In our case it is VQC. We pass the same file name we used in the previous section where we saved our model.

[18]:
loaded_classifier = VQC.load("vqc_classifier.model")

Next, we want to alter the model in a way it can be trained further and on another simulator. To do so, we set the warm_start property. When it is set to True and fit() is called again the model uses weights from previous fit to start a new fit. We also set quantum instance of the underlying network to the statevector simulator we created in the beginning of the tutorial. Finally, we create and set a new optimizer with maxiter is set to 80, so the total number of iterations is 100.

[19]:
loaded_classifier.warm_start = True
loaded_classifier.neural_network.quantum_instance = qi_sv
loaded_classifier.optimizer = COBYLA(maxiter=80)

Now we continue training our model from the state we finished in the previous section.

[20]:
loaded_classifier.fit(train_features, train_labels)
../_images/tutorials_09_saving_and_loading_models_41_0.png
[20]:
<qiskit_machine_learning.algorithms.classifiers.vqc.VQC at 0x16ec827b848>
[21]:
print("Train score", loaded_classifier.score(train_features, train_labels))
print("Test score", loaded_classifier.score(test_features, test_labels))
Train score 0.9333333333333333
Test score 0.8

Let’s see which data points were misclassified. First, we call predict to infer predicted values from the training and test features.

[22]:
train_predicts = loaded_classifier.predict(train_features)
test_predicts = loaded_classifier.predict(test_features)

Plot the whole dataset and the highlight the points that were classified incorrectly.

[23]:
# return plot to default figsize
plt.rcParams["figure.figsize"] = (6, 4)

plot_dataset()

# plot misclassified data points
plt.scatter(
    train_features[np.all(train_labels != train_predicts, axis=1), 0],
    train_features[np.all(train_labels != train_predicts, axis=1), 1],
    s=200,
    facecolors="none",
    edgecolors="r",
    linewidths=2,
)
plt.scatter(
    test_features[np.all(test_labels != test_predicts, axis=1), 0],
    test_features[np.all(test_labels != test_predicts, axis=1), 1],
    s=200,
    facecolors="none",
    edgecolors="r",
    linewidths=2,
)
[23]:
<matplotlib.collections.PathCollection at 0x16ec8906288>
../_images/tutorials_09_saving_and_loading_models_46_1.png

So, if you have a large dataset or a large model you can train it in multiple steps as shown in this tutorial.

4. PyTorch 하이브리드 모델

To save and load hybrid models, when using the TorchConnector, follow the PyTorch recommendations of saving and loading the models. For more details please refer to the PyTorch Connector tutorial here where a short snippet shows how to do it.

Take a look at this pseudo-like code to get the idea:

# create a QNN and a hybrid model
qnn = create_qnn()
model = Net(qnn)
# ... train the model ...

# save the model
torch.save(model.state_dict(), "model.pt")

# create a new model
new_qnn = create_qnn()
loaded_model = Net(new_qnn)
loaded_model.load_state_dict(torch.load("model.pt"))
[24]:
import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.20.0
qiskit-aer0.10.3
qiskit-ibmq-provider0.18.3
qiskit0.33.0
qiskit-machine-learning0.4.0
System information
Python version3.7.9
Python compilerMSC v.1916 64 bit (AMD64)
Python builddefault, Aug 31 2020 17:10:11
OSWindows
CPUs4
Memory (Gb)31.837730407714844
Tue Apr 05 17:09:14 2022 GMT Daylight Time

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.