참고

This page was generated from docs/tutorials/09_saving_and_loading_models.ipynb.

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

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

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

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

  • 모델 훈련후 저장

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

  • 모델 성능 평가

  • PyTorch 하이브리드 모델

First off, we start from the required imports. We’ll heavily use SciKit-Learn on the data preparation step. In the next cell we also fix a random seed for reproducibility purposes.

[1]:
import matplotlib.pyplot as plt
import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.primitives import Sampler
from qiskit_algorithms.optimizers import COBYLA
from qiskit_algorithms.utils import 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

We will be using two quantum simulators, in particular, two instances of the Sampler primitive. We’ll start training on the first one, then will resume training on the second one. The approach shown in this tutorial can be used to train a model on a real hardware available on the cloud and then re-use the model for inference on a local simulator.

[2]:
sampler1 = Sampler()

sampler2 = Sampler()

1. 데이터셋 준비#

다음 단계는 데이터셋을 준비하는 것이다. 여기서는 다른 튜토리얼과 동일한 방법으로 일부 데이터를 생성한다. 차이점은 생성된 데이터에 일부 변환을 적용한다는 것이다. 40 개의 샘플을 생성하고, 각 샘플은 2 개의 특징을 가지고 있으므로 특징은 크기가 (40, 2) 인 배열이다. 레이블은 열별로 특징들을 합산하여 얻으며 합계가 1 보다 크면 이 샘플은 1 그렇지 않으면 0 으로 표기한다.

[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}

그런 다음 SciKit-Learn의 MinMaxScaler 를 적용하여 기능을 [0, 1] 범위로 축소한다. 이러한 변환을 적용하면 모델을 학습시킬 때 더 잘 수렴한다.

[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]])

훈련할 모델로는 VQC 혹은 변분 양자 분류기(Variational Quantum Classifier)를 선택한다. 기본적으로 이 모델은 one-hot 인코딩된 레이블을 사용하므로, {0, 1} 세트에 있는 레이블을 one-hot 표현법으로 변환해야 한다. 이 변환을 위해서 동일하게 SciKit-Learn를 이용한다. 입력 배열이 (num_samples, 1) 과 같은 형태로 바뀌게 됨에 주의한다. OneHotEncoder 인코더는 1D 배열에는 작동하지 않지만, 우리의 레이블은 1D 배열 이므로, 이 경우 사용자는 배열이 우리가 직면한 문제와 같이 하나의 feature 만을 갖는지, 또는 하나의 sample 만을 갖는 것인지 결정해야 한다. 또한 기본적으로 인코더는 희소 배열을 반환하지만, 조밀한 배열을 갖는 것이 데이터 세트를 플롯팅하기에 더 쉽기 때문에, sparseFalse 로 설정한다.

[6]:
labels = OneHotEncoder(sparse_output=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.]])

이제 데이터 세트를 훈련 데이터 세트와 테스트 데이터 세트의 두 부분으로 나눈다. 일반적으로 전체 데이터 세트의 80%는 훈련 부분에, 20%는 테스트 부분에 할당한다. 우리의 경우 훈련 데이터 세트에는 30 개의 샘플이 있다. 테스트 데이터 세트는 모델이 보이지 않는 데이터에 대해 모델이 얼마나 잘 작동하는지 확인하도록 훈련될 때 한 번만 사용하도록 한다. SciKit-Learn의 ``train_test_split``을 사용하여 데이터를 나눈다.

[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

위의 그래프를 보면 다음과 같다:

  • 안이 채워진 청색 점은 0 으로 표시된 훈련 데이터 셋의 샘플이다.

  • 비어 있는 파란색 점은 0 으로 레이블 된 테스트 데이터 셋의 샘플이다.

  • 안이 채워진 녹색 점은 1 로 표시된 훈련 데이터 셋의 샘플이다.

  • 비어 있는 초록색 점은 1 로 레이블된 테스트 데이터 셋의 샘플이다.

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

2. 모델을 훈련 시키고 저장하기#

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

[10]:
maxiter = 20

목표 함수의 값을 저장하는 콜백을 위해서 비어 있는 배열을 생성하라.

[11]:
objective_values = []

각 단계에서 목표 값을 플롯하기 위해, Neural Network Classifier & Regressor tutorial의 콜백 함수를 다시 사용하여 약간의 수정을 가한 목표 함수의 값을 iteration에 대해 플롯한다.

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

위에서 언급한 것과 같이 VQC 모델을 훈련시키고 maxiter 매개변수를 갖는 COBYLA 를 최적화 알고리즘으로 설정했다. 그런 다음 모델의 성능을 평가하여 얼마나 잘 훈련되었는지 확인하고, 이 모델을 파일로 저장한다. 두 번째 단계에서는 이 모델을 불러와서 작업을 계속한다.

여기에서, 최적화를 시작할 초기 지점을 조정하기 위해 직접 ansatz를 구성한다.

[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 sampler to the first sampler we created earlier.

[14]:
original_classifier = VQC(
    ansatz=ansatz, optimizer=original_optimizer, callback=callback_graph, sampler=sampler1
)

이제 우리의 모델을 학습해보자.

[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 0x7fb74126db20>

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.8333333333333334
Test score  0.8

다음으로 모델을 저장한다. 원하는 파일 이름을 선택할 수 있다. 파일 이름이 지정되지 않은 경우 save 메소드는 확장자를 추가하지 않는다.

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

3. 모델을 불러오고 계속 학습하기#

모델을 불러오려면 사용자가 해당 모델 클래스의 load 클래스 메소드를 호출해야 한다. 우리의 경우에는 VQC 이다. 우리는 이전 섹션에서 사용한 것과 같은 파일 이름을 사용하여 모델을 저장했다.

[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 the sampler property of the underlying network to the second instance of the Sampler primitive 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.sampler = sampler2
loaded_classifier.optimizer = COBYLA(maxiter=80)

이제 이전 섹션을 완료한 상태에서 모델을 계속 학습한다.

[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 0x7fb7411cb760>
[21]:
print("Train score", loaded_classifier.score(train_features, train_labels))
print("Test score", loaded_classifier.score(test_features, test_labels))
Train score 0.9
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)

전체 데이터 세트의 그래프를 그리고, 잘못 분류된 점을 강조 표시한다.

[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 0x7fb6e04c2eb0>
../_images/tutorials_09_saving_and_loading_models_46_1.png

따라서 거대한 데이터 세트가 있거나 거대한 모델이 있는 경우, 이 튜토리얼에 표시된 여러 단계를 거쳐 학습할 수 있다.

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 where a short snippet shows how to do it.

아이디어를 얻기 위해서 다음의 준 유사 코드를 살펴보자.

# 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.25.0
qiskit-aer0.13.0
qiskit-machine-learning0.7.0
System information
Python version3.8.13
Python compilerClang 12.0.0
Python builddefault, Oct 19 2022 17:54:22
OSDarwin
CPUs10
Memory (Gb)64.0
Mon Jun 12 11:51:03 2023 IST

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

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.