銀の光と碧い空

クラウドなインフラとC#なアプリ開発の狭間にいるエンジニアの日々

Microsoft Q# Coding Contest – Summer 2020の復習(4) 量子機械学習

Warmupラウンドの最後、D1とD2の量子機械学習のまとめです。

Problem - D1 - Codeforces

Problem - D2 - Codeforces

D1、D2は問題の構造としては同じで、ある教師データを元に適切な量子機械学習のモデルとパラメーターを返すだけの操作を実装します。採点時の時間制限のため、学習そのものはローカルで実行しておき、その結果のみを提出する必要があります。D1とD2の違いは教師データのみです。

namespace Solution {
    open Microsoft.Quantum.MachineLearning;

    operation Solve () : (ControlledRotation[], (Double[], Double)) {
        // your code here
    }
}

量子機械学習ライブラリの使い方については公式ドキュメントに加えて

docs.microsoft.com

このハンズオンリポジトリが問題そのものにかなり近いとあります。

github.com

というわけでこのリポジトリのコードをほぼそのまま使ってみることにします。まず提出用コードを書く前に、トレーニングするためのコードを書きます。量子機械学習ライブラリはNuGetで追加しておく必要があります。

  <ItemGroup>
    <PackageReference Include="Microsoft.Quantum.MachineLearning" Version="0.12.20072031" />
  </ItemGroup>

教師データはいったんローカルファイルに保存しておき読み込みます。そのため、C#コードを書いています。トレーニングを行う操作と、検証する操作の2つのQ#コードを呼び出しています。

using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Quantum.Simulation.Core;
using Microsoft.Quantum.Simulation.Simulators;
using Quantum.QuantumClassification;

namespace QuantumClassification
{
    class Program
    {
        public static async Task Main()
        {
            using var reader = File.OpenRead(@"training_data1.json");
            var trainingData = await JsonSerializer.DeserializeAsync<TrainingData>(reader);
            using var targetMachine = new QuantumSimulator();

            //トレーニング実施
            var (optimizedParameters, optimizedBias) = await TrainModel.Run(
                targetMachine,
                new QArray<QArray<double>>(trainingData.Features.Select(vector => new QArray<double>(vector))),
                new QArray<long>(trainingData.Labels),
                new QArray<QArray<double>>(new[] { new QArray<double>(new[] { 1.0, 2.0 }) }));

            //学習に使ってデータを使ってトレーニング結果を検証
            var tolerance = 0.0005;
            var nMeasurements = 10_000;
            var classified = await ClassifyModel.Run(
                targetMachine,
                new QArray<QArray<double>>(trainingData.Features.Select(vector => new QArray<double>(vector))),
                optimizedParameters,
                optimizedBias,
                tolerance,
                nMeasurements);

            var missedCount = trainingData.Labels.Zip(classified).Count(t => t.First != t.Second);

        }
    }

    class TrainingData
    {
        public double[][] Features { get; set; }
        public long[] Labels { get; set; }
    }

}

そのQ#コードがこちらです。こちらもMLADS2020-QuantumClassificationのコードをほぼそのまま利用しています。トレーニングするためのAPIがTrainSequentialClassifierで、トレーニング結果を使って分類するAPIがEstimateClassificationProbabilitiesなので両者を呼び出すのに必要な引数を準備しているコードが多くを占めています。

docs.microsoft.com

docs.microsoft.com

namespace Quantum.QuantumClassification {

    open Microsoft.Quantum.Convert;
    open Microsoft.Quantum.Intrinsic;
    open Microsoft.Quantum.Canon;
    open Microsoft.Quantum.Arrays;
    open Microsoft.Quantum.MachineLearning;
    open Microsoft.Quantum.Math;

    
    function DefaultSchedule(samples : Double[][]) : SamplingSchedule {
        return SamplingSchedule([
            0..Length(samples) - 1
        ]);
    }

    function ClassifierStructure() : ControlledRotation[] {
        return [
            ControlledRotation((0, new Int[0]), PauliY, 0)
        ];
    }

    operation TrainModel(
        trainingVectors : Double[][],
        trainingLabels : Int[],
        initialParameters : Double[][]
    ) : (Double[], Double) {
        let samples = Mapped(
            LabeledSample,
            Zip(trainingVectors, trainingLabels)
        );
        let (optimizedModel, nMisses) = TrainSequentialClassifier(
            Mapped(
                SequentialModel(ClassifierStructure(), _, 0.0),
                initialParameters
            ),
            samples,
            DefaultTrainingOptions()
                w/ LearningRate <- 2.0
                w/ Tolerance <- 0.0005,
            DefaultSchedule(trainingVectors),
            DefaultSchedule(trainingVectors)
        );
        Message($"Training complete, found optimal parameters: {optimizedModel::Parameters}, {optimizedModel::Bias} with {nMisses} misses");
        return (optimizedModel::Parameters, optimizedModel::Bias);
    }

    operation ClassifyModel(
        samples : Double[][],
        parameters : Double[],
        bias : Double,
        tolerance  : Double,
        nMeasurements : Int
    )
    : Int[] {
        let model = Default<SequentialModel>()
            w/ Structure <- ClassifierStructure()
            w/ Parameters <- parameters
            w/ Bias <- bias;
        let probabilities = EstimateClassificationProbabilities(
            tolerance, model,
            samples, nMeasurements
        );
        return InferredLabels(model::Bias, probabilities);
    }
}

この中で、ClassifierStructure 操作は、トレーニングに使う量子回路を表現しています。おそらくモデルの性質にあわせて調整するものなのですが、今回はもともとのサンプルコードをそのまま使って望み通りの結果が得られたのでそのまま使っています。それぞれのデータを元にこのコードを実行し、その結果を返すコードを書けばD1、D2それぞれの回答になります。

namespace Solution {
    open Microsoft.Quantum.MachineLearning;

    function ClassifierStructure() : ControlledRotation[] {
        return [
            ControlledRotation((0, new Int[0]), PauliY, 0)
        ];
    }

    operation Solve () : (ControlledRotation[], (Double[], Double)) {
        return (ClassifierStructure(), ([3.196000000000002,2.0], 0.016867554144068545));
    }
}
namespace Solution {
    open Microsoft.Quantum.MachineLearning;

    function ClassifierStructure() : ControlledRotation[] {
        return [
            ControlledRotation((0, new Int[0]), PauliY, 0)
        ];
    }

    operation Solve () : (ControlledRotation[], (Double[], Double)) {
        return (ClassifierStructure(), ([1.0,2.0], 0.25070000000000003));
    }
}