February 2016

Volume 31 Number 2

Microsoft Azure - Azure Service Fabric、Q 学習、三目並べ

Jesus Aguilar

クラウド コンピューティングのイノベーションによって、分散コンピューティングと機械学習アプリケーションの導入ハードルが下がり、専門性や高価なインフラストラクチャを必要とするニッチなテクノロジから、ソフトウェア開発者やソリューション アーキテクトならだれでも利用できるコモディティ サービスまで広がっています。今回は、次期 Azure PaaS サービスである Azure Service Fabric の分散コンピューティングとストレージ機能を利用した強化学習手法の実装について取り上げます。このアプローチの可能性を示すため、Azure Service Fabric とその Reliable Actors プログラミング モデルを使用して、三目並べゲームの次の一手を予測するインテリジェントなバックエンドを作成します。映画「WarGames」をご覧になりましたか。

Q 学習入門

現在では、おすすめ、顔認識、不正行為の検出など、革新的なデータ駆動型ソリューションがあらゆる場所で見られます。ソフトウェア エンジニアリング チームは、教師あり学習手法と教師なし学習手法を使用して、こうしたソリューションを実装しています。これらのアプローチをさまざまなソリューションに当てはめることができますが、中には難しいものもあります。

強化学習とは、一連の状態と遷移として表現できるシナリオを扱う手法です。他の機械学習アプローチとは対照的に、強化学習では、ラベル付けされた情報からモデルをトレーニングしたり (教師あり学習)、ラベル付けされていないデータからモデルをトレーニング (教師なし学習) してパターンを一般化することはありません。代わりに、一連の状態と遷移としてモデル化できる問題を扱います。

最終状態 (別称、吸収状態) に至るまでを一連の状態として表現できるシナリオがあるとします。障害物を避けるロボットや、敵を倒すゲームの人工知能 (AI) を考えてください。多くの場合、特定の状況に至るまでの一連の状態とは、エージェント/ロボット/AI キャラクターにとって次の最善の 1 手を決定することに他なりません。

Q 学習は、強化学習手法の 1 つで、反復的な報酬メカニズムを使用して、ステート マシン モデルにおける最適な遷移経路を見つけます。Q 学習は、状態と遷移の数が有限の場合に非常に有効に機能します。今回は、Service Fabric を使用してエンド ツー エンドの Q 学習ソリューションをビルドする方法を紹介し、三目並べの遊び方を "学習" するインテリジェントなバックエンドを作成します (ステート マシンのシナリオは、マルコフ決定過程 (MDP) とも呼ばれます)。

最初に、Q 学習に関する基本理論を説明します。図 1 に示す状態と遷移を考えてください。任意の状態にあるエージェントが、最小限の遷移回数で最終目標 (Gold) の状態に到達するには、次にどの状態に遷移するかを明らかにする必要があります。この問題に取り組む方法の 1 つとして、各状態に報酬値を割り当てます。報酬は、ある状態への遷移が最終目標への到達が近いかどうかの評価を示します。

最終目標の状態に至るまでの一連の状態遷移
図 1 最終目標の状態に至るまでの一連の状態遷移

シンプルですね。課題は、どのようにして各状態の報酬を決めるかです。Q 学習アルゴリズムでは、吸収 (ゴールド) 状態に至るまでの状態に報酬を再帰的に反復して割り当てることで、報酬を決めます。このアルゴリズムでは、ある状態の報酬を計算するとき、その次の状態から報酬を割り引いて求めます。状態に 2 つの報酬がある場合 (その状態からの経路が複数存在する場合)、報酬値が最も高いものを優先します。この割引はシステムに大きく影響します。アルゴリズムでは、報酬を割り引くことで、最終目標状態から遠い位置にある状態の報酬値を低くし、最終目標状態に近い状態ほど高い値を割り当てます。

報酬を計算するアルゴリズムの例として、図 1 の状態遷移図を見てください。この図では、最終目標の経路が以下のように 3 つあります。

1->5->4->G

1->5->3->4->G

1->5->3->2->4->G

総当りの遷移を使用 (グラフ中の考え得るすべての経路を反復処理) してアルゴリズムを実行すると、有効な経路に対して報酬が計算され、割り当てられます。報酬は、割引係数 0.9 を使用して計算します。

1(R=72)->5(R=81)->4(R=90)->G (R=100) 

1(R=64)->5(R=72)->3(R=81)->4(R=90)-> G(R=100)

1(R=58)->5(R=64)->3(R=72)->2(R=81)->4(R=90)-> G(R=100)

一部の状態には複数の報酬があるため、最も高い値が優先されます。図 2 は、最終的な報酬の割り当てを示しています。

最終的な報酬
図 2 最終的な報酬

この情報を基に、エージェントは最も高い報酬が設定された状態に遷移することで、任意の状態から最終目標状態への最適な経路を特定できます。たとえば、エージェントが状態 5 にある場合、遷移先の選択肢には状態 3 と状態 4 がありますが、状態 4 の方が報酬値が高いため、状態 4 が選択されます。

Azure Service Fabric

次期 Azure PaaS サービスである Service Fabric を利用すると、Reliable Actors と Reliable Services という 2 つの最上位レベルのプログラミング モデルを使用して分散アプリケーションを作成できます。これらのプログラミング モデルを利用すると、分散プラットフォームのインフラストラクチャ リソースを最大限に活用できます。Service Fabric では、分散アプリケーションの保守と運用に関連した、非常に難易度の高いタスクが処理されます。いくつか挙げると、障害からの回復、リソースを効果的に利用するためのサービスの分散、更新の実行、サイド バイ サイドのバージョン管理などです。

Service Fabric では、クラスターを利用でき、基盤となるインフラストラクチャを意識する必要のない高度な抽象化が提供されます。コードは Service Fabric クラスターのノードで実行されます。複数ノードのクラスターは、開発用の 1 台のコンピューターでホストすることも、運用環境のワークロード用の複数の (仮想または物理) サーバーでホストすることもできます。このプラットフォームは、アクターとサービスのライフサイクルと、インフラストラクチャ障害からの回復を管理します。

Service Fabric では、Reliable Actors と Reliable Services にステートフル セマンティクスが導入されています。この機能が開発者エクスペリエンスに完全に統合されるため、アーキテクチャに外部ストレージ層を含める (たとえば、外部ストレージやキャッシュ層と依存関係を持つ) ことなく分散型で可用性の高い方法でデータを永続化するアプリケーションを開発できます。

Service Fabric 内に Q 学習アルゴリズムをサービスとして実装することで、分散コンピューティングと低遅延の状態ストレージ機能を所有するメリットが得られ、アルゴリズムの実行、結果の永続化、およびクライアントのアクセス用に全体を信頼できるエンドポイントとして公開することができます。これらの機能はすべて、統合プログラミングや管理スタックと一緒に、1 つのソリューションとして提供されます。外部ストレージ、キャッシュ、メッセージング システムなどのコンポーネントをアーキテクチャに追加する必要はありません。つまり、同じ統合プラットフォーム内にコンピューティング、データ、およびサービスが存在するソリューションを利用できます。すばらしいソリューションですね。

Q 学習と Reliable Actors

アクター モデルを使用すると、大規模同時実行アプリケーションの設計が簡素化されます。アクター モデルでは、アクターが基本コンピューティング単位です。アクターは、機能と状態の境界を表します。アクターは、分散システム内のオブジェクト エンティティと考えることができます。Service Fabric では、アクターのライフサイクルを管理します。障害が発生した場合は、Service Fabric が正常なノードにアクターのインスタンスを自動的に再作成します。たとえば、ステートフルなアクターがなんらかの理由でクラッシュしたり、アクターを実行しているノード (VM) に障害が発生した場合、アクターはすべての状態 (データ) が元のまま、別のコンピューターに自動的に再作成されます。

Service Fabric は、アクター インスタンスに対するアクセス方法も管理します。Service Fabric を使用すると、一度に実行されるメソッドはいつでも特定のアクターの 1 つのメソッドのみであることが保証されます。同じアクターに対する呼び出しが 2 つ同時に行われた場合、一方の呼び出しはキューに入れられ、もう一方は実行されます。つまり、アクター内では、コードの競合状態、ロック、および同期を心配する必要はありません。

前述のとおり、Q 学習アルゴリズムでは、吸収状態と報酬の設定状態を見つけることを目標に、状態を反復処理します。アルゴリズムで吸収状態とその報酬を特定したら、吸収状態に至るまでのすべての状態の報酬を計算します。

アクター モデルを使用すると、Q 学習アルゴリズムのコンテキストにおいてこの機能をアクターとしてモデル化できます (グラフ全体のステージを考えてください)。今回の実装では、これらの状態を表すアクターの型を QState とします。報酬を含む QState アクターへの遷移があれば、その QState アクターは経路上の各 QState アクターに、異なる型 (QTrainedState) のもう 1 つのアクター インスタンスを作成します。QTrainedState アクターは、最大報酬値と、その報酬をもたらしたその後続状態のリストを保持します。リストには、後続状態の状態トークン (グラフ内の状態を一意に特定) が含まれます。

図 3 には、アクターを使用するアルゴリズムのロジックを示しています。このロジックのシナリオは、状態トークン 3 を持つ状態が吸収状態で、報酬が 100 に設定されており、そこに至る唯一の経路には遷移前の 2 つの状態 (状態トークン 1 と 2) が配置されているという、非常に簡単なものです。各円は、アクターのインスタンスを表します。QStates は青で、QTrainedStates はオレンジです。状態トークン 3 が設定された QState に遷移プロセスが至ると、QState アクターは 2 つの QTrainedStates を作成します。それぞれ、遷移前の QStates に対応しています。状態トークン 2 を示す QTrainedState アクター (報酬 90) に対して推奨される遷移先は、状態トークン 3 です。状態トークン 1 を示す QTrainedState アクター (報酬 81) に対して推奨される遷移先は、状態トークン 2 です。

報酬の決定と永続化
図 3 報酬の決定と永続化

複数の状態に同じ報酬がもたらされることもあります。そのため、QTrainedState アクターでは、状態トークンのコレクションを子状態として永続化します。

次のコードは、QState アクターと QTrainedState アクターのインターフェイスである IQState と IQTrainedState の実装を示しています。QStates には、別の QStates への遷移と、過去に遷移が存在しない場合には遷移プロセスを開始するという 2 つの動作があります。

public interface IQState : IActor
{
  Task StartTrainingAsync(int initialTransitionValue);
  Task TransitionAsync(int? previousStateToken, int transitionValue);
}
public interface IQTrainedState:IActor
{
 .Task AddChildQTrainedStateAsync(int stateToken, double reward);
 .Task<List<int>> GetChildrenQTrainedStatesAsync();
}

IQTrainedState の実装がメソッド GetChildrenQTrainedStatesAsync とインターフェイスをとっていることに注目してください。このメソッドは、システム内のある状態に対して最も高い報酬が設定された状態を含むトレーニング済みデータを QTrainedState アクターで公開する方法です (Service Fabric のすべてのアクターは IActor から派生したインターフェイスを実装する必要があります)。

QState アクター

インターフェイスを定義したら、アクターの実装に移ります。QState アクターと TransitionAsync メソッドから始めます。このメソッドは、アルゴリズムの要であり、作業の大半を費やす箇所です。TransitionAsync は、QState アクターの新しいインスタンスを作成し、同じメソッドをもう一度呼び出すことで、別の状態への遷移を行います。

メソッドを再帰的に呼び出すと、別のアクター インスタンスを通じてメソッドを呼び出す際のオーバーヘッドを回避できると思うかもしれません。メソッドの再帰呼び出しは、1 つのノードでは計算負荷の高い操作です。これに対して、別のアクターのインスタンスを作成する場合は、Service Fabric で処理をコンピューティング リソースに水平に分散できるようにすることによって、Service Fabric の機能を有効活用します。

報酬の割り当てを管理するために、アラームを登録します。アラームは、メソッドの実行をブロックすることなく非同期処理のスケジュールを設定できるよう、アクター プログラミング モデルで新しく導入された構造です。

アラームは、ステートフル アクターでのみ使用できます。ステートレス アクターとステートフル アクター両方に対して、似たようなパターンを実現できるよう、タイマーが用意されています。重要な考慮事項の 1 つは、アクターを使用すると、ガベージ コレクション プロセスが遅延することです。それでも、Service Fabric では、タイマー コールバックを使用中であるとは見なしません。ガベージ コレクターが作動すると、タイマーは停止します。アクターは、メソッドの実行中はガベージ コレクションの対象となりません。再帰実行を保証するためには、アラームを使用します。詳細については、bit.ly/1RmzKfr を参照してください。

目標は、アルゴリズムに関連する点では、遷移プロセスをブロックすることなく報酬を割り当てることです。一般的には、コールバックを使用してスレッド プール内で作業項目のスケジュールを設定すれば十分です。しかし、このアプローチをアクター プログラミング モデルで行うと、Service Fabric の同時実行性を生かせなくなるため好ましくありません。

Service Fabric では、一度に実行されるメソッドは 1 つのみであることが保証されます。この機能のおかげで、同時実行性を考慮せず (つまり、スレッド セーフを気にする必要なく) コードを作成できます。ただし、これにはトレードオフがあります。アクターのメソッド内に操作をラップするためにタスクやスレッドを作成することは避けなければなりません。アラームを使用すると、Service Fabric の同時実行性の保証を維持しながら、バックグラウンド処理シナリオを実装できます (図 4 参照)。

図 4 QState クラスの TransitionAsync

public abstract class QState : StatefulActor, IQState, IRemindable
{
  // ...
  public Task TransitionAsync(int? previousStateToken, int transitionValue)
  {
    var rwd = GetReward(previousStateToken, transitionValue);
    var stateToken = transitionValue;
    if (previousStateToken != null)
        stateToken = int.Parse(previousStateToken.Value + stateToken.ToString());
    var ts = new List<Task>();
    if (rwd == null || !rwd.IsAbsorbent)
      ts.AddRange(GetTransitions(stateToken).Select(p =>
        ActorProxy.Create<IQState>(ActorId.NewId(),
        "fabric:/QLearningServiceFab").TransitionAsync(stateToken, p)));
    if (rwd != null)
      ts.Add(RegisterReminderAsync("SetReward",
        Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(rwd))
        , TimeSpan.FromMilliseconds(0)
        , TimeSpan.FromMilliseconds(-1), ActorReminderAttributes.Readonly));
      return Task.WhenAll(ts);
  }
  // ...
}

(dueTime を TimeSpan.FromMilliseconds(0) に設定すると、即時実行になることに注意してください)。

IQState の実装を完了するには、次のコードを使用して StartTrainingAsync メソッドを実装します。このメソッドでアラームを使用して、長時間にわたるブロッキング呼び出しを回避します。

public Task StartTrainingAsync(int initialTransitionValue)
  {
    return RegisterReminderAsync("StartTransition",
      Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { TransitionValue =
      initialTransitionValue })), TimeSpan.FromMilliseconds(0),
      TimeSpan.FromMilliseconds(-1),
      ActorReminderAttributes.Readonly);
  }

QState クラスの実装を完成させるために、SetRewardAsync メソッドと Receive­ReminderAsync メソッドの実装について説明しましょう (図 5 参照)。SetReward メソッドは、ステートフル アクター (IQTrainedState の実装) を作成または更新します。2 回目以降の呼び出しでアクターを見つけるために、状態トークンをアクター ID として使用し、アクターをアドレス指定可能にします。

図 5 SetRewardAsync メソッドと ReceiveReminderAsync メソッド

public Task SetRewardAsync(int stateToken, double stateReward, double discount)
  {
    var t = new List<Task>();
    var reward = stateReward;
    foreach (var pastState in GetRewardingQStates(stateToken))
    {
      t.Add(ActorProxy
        .Create<IQTrainedState>(new ActorId(pastState.StateToken),
          "fabric:/QLearningServiceFab")
        .AddChildQTrainedStateAsync(pastState.NextStateToken, reward));
      reward = reward * discount;
    }
    return Task.WhenAll(t);
  }
public async Task ReceiveReminderAsync(string reminderName,
  byte[] context, TimeSpan dueTime, TimeSpan period)
  {
    await UnregisterReminderAsync(GetReminder(reminderName));
    var state = JsonConvert.DeserializeObject<JObject>(
      Encoding.UTF8.GetString(context));
    if (reminderName == "SetReward")
    {
      await SetRewardAsync(state["StateToken"].ToObject<int>(),
        state["Value"].ToObject<double>(),
        state["Discount"].ToObject<double>());
    }
    if (reminderName == "StartTransition")
    {
      await TransitionAsync(null, state["TransitionValue"].ToObject<int>());
    }
  }

QTrainedState アクター

このソリューションの 2 つ目のアクターは QTrainedState です。QTrainedState アクターのデータは永続的である必要があるため、このアクターをステートフル アクターとして実装しました。

Service Fabric でステートフル アクターを実装するには、StatefulActor または StatefulActor<T> 基底クラスからクラスを派生させ、IActor から派生させたインターフェイスを実装します。T は、状態インスタンスの型で、シリアル化可能かつ参照型である必要があります。StatefulActor<T> から派生させたクラスのメソッドを呼び出すとき、Service Fabric では状態プロバイダーから状態を読み込み、呼び出しが完了すると自動的に状態を保存します。QTrainedState の場合、次のクラスを使用して状態 (永続的なデータ) を作成します。

[DataContract]
public class QTrainedStateState
{
  [DataMember]
  public double MaximumReward { get; set; }
  [DataMember]
  public HashSet<int> ChildrenQTrainedStates { get; set; }
}

図 6 に QTrainedState クラスの完全な実装を示します。IQTrainedState インターフェイスの 2 つのメソッドを実装しています。

図 6 QTrainedState クラス

public class QTrainedState : StatefulActor<QTrainedStateState>, IQTrainedState
{
  protected async override Task OnActivateAsync()
  {
    this.State =
      await ActorService.StateProvider.LoadStateAsync<QTrainedStateState>(
      Id, "qts") ??
      new QTrainedStateState() { ChildrenQTrainedStates = new HashSet<int>() };
    await base.OnActivateAsync();
  }
  protected async override Task OnDeactivateAsync()
  {
    await ActorService.StateProvider.SaveStateAsync(Id, "qts", State);
    await base.OnDeactivateAsync();
  }
  [Readonly]
  public  Task AddChildQTrainedStateAsync(int stateToken, double reward)
  {
    if (reward < State.MaximumReward)
    {
      return Task.FromResult(true);
    }
    if (Math.Abs(reward - State.MaximumReward) < 0.10)
    {
      State.ChildrenQTrainedStates.Add(stateToken);
      return Task.FromResult(true);
    }
      State.MaximumReward = reward;
      State.ChildrenQTrainedStates.Clear();
      State.ChildrenQTrainedStates.Add(stateToken);
      return Task.FromResult(true);
  }
  [Readonly]
  public Task<List<int>> GetChildrenQTrainedStatesAsync()
  {
    return Task.FromResult(State.ChildrenQTrainedStates.ToList());
  }
}

アクターとのインターフェイス

この時点で、ソリューションには、トレーニング プロセスを開始してデータを永続化するのに必要なものすべてが揃っています。しかし、クライアントがこれらのアクターとどのようにやり取りするかについてはまだ説明していません。大まかに言うと、このやり取りは、トレーニング プロセスの開始と永続化したデータのクエリから構成されます。それぞれのやり取りは、API 操作と密接に関連しています。また、RESTful 実装によって、クライアントとの統合が促進されます。

Service Fabric には、2 つのプログラミング モデルがあるだけでなく、包括的なオーケストレーションおよびプロセス管理プラットフォームの側面があります。アクターとサービスのために存在する障害復旧とリソース管理は、他のプロセスでも利用できます。たとえば、Node.js または ASP.NET 5 プロセスを Service Fabric で管理して実行すると、追加作業なしで先述の機能のメリットを享受できます。そのため、標準の ASP.NET 5 Web API アプリケーションを使用して、関連するアクターの機能を公開する API コントローラーを作成できます (図 7 参照)。

図 7 API コントローラー

[Route("api/[controller]")]
public class QTrainerController : Controller
{
  [HttpGet()]
  [Route("[action]/{startTrans:int}")]
  public  async Task<IActionResult>  Start(int startTrans)
  {
    var actor = ActorProxy.Create<IQState>(ActorId.NewId(),
      "fabric:/QLearningServiceFab/");
    await actor.StartTrainingAsync(startTrans);
    return Ok(startTrans); 
  }
  [HttpGet()]
  [Route("[action]/{stateToken}")]
  public async Task<int> NextValue(int stateToken)
  {
    var actor = ActorProxy.Create<IQTrainedState>(new ActorId(stateToken),
      "fabric:/QLearningServiceFab");
    var qs = await actor.GetChildrenQTrainedStatesAsync();
    return qs.Count == 0 ? 0 : qs[new Random().Next(0, qs.Count)];
  }
}

三目並べ

残る課題は、具体的なシナリオにソリューションを活用することです。そのため、シンプルな三目並べゲームを使用します。

目標は、三目並べゲームの次の動きをクエリして予測できるように QTrainedStates のセットをトレーニングすることです。ここでは、コンピューターが両方のプレイヤーとして行動し、行動の結果から学習すると考えるのが 1 つの方法です。

実装を振り返って、QState が抽象クラスであることに注目してください。抽象クラスの考え方は、アルゴリズムの基本的な側面をカプセル化して、特定のシナリオのロジックを派生クラスに格納するというものです。シナリオによって、アルゴリズムの 3 つの部分が定義されます。それは、状態間の遷移の発生方法 (ポリシー)、吸収状態とその初期報酬値、およびアルゴリズムによって割引が適用された報酬が割り当てられる状態です。このそれぞれについて、QState クラスには、これらのセマンティクスを実装して特定のシナリオを解決できるメソッドがあります。それは、GetTransitions、GetReward、および GetRewardingQStates メソッドです。

さて、ここで問題になるのは、三目並べゲームを、一連の状態と遷移としてどのようにモデル化するかということです。

図 8 の図のゲームを考えて見ましょう。各セルには、数字が割り当てられています。各手番を、ある状態から別の状態への遷移と考えることができます。遷移値は、プレイヤーがプレイしたセルの値です。各状態トークンは、それまでの手番 (セル) と、遷移値の組み合わせになります。図 8 の例では、1、14、142 という遷移は、先攻のプレイヤーが勝利するゲーム手順をモデル化しています。この場合、14273 (勝利状態かつ吸収状態) に至るまでのすべての状態 (1 と 142) に、報酬を割り当てる必要があります。

三目並べシナリオ
図 8 三目並べシナリオ

Q 学習を振り返ると、指定する必要があるのは、すべての最終 (吸収) 状態とその初期報酬値でした。三目並べの場合、勝利、引き分け、ブロック (対戦相手が勝ちそうなときにそれを阻止するために手番を使用すること) の 3 種類の状態から報酬がもたらされます。勝利と引き分けは吸収状態で、ゲームの終了を意味します。一方、ブロックは、吸収状態ではなく、ゲームは続行します。図 9 は、三目並べゲームの GetReward メソッドの実装を示しています。

図 9 GetReward メソッド

internal override IReward GetReward(int? previousStateToken, int transitionValue)
{
  var game = new TicTacToeGame(previousStateToken,transitionValue);
  IReward rwd = null;
  if (game.IsBlock)
  {
    rwd = new TicTacToeReward() { Discount = .5, Value = 95, IsAbsorbent = false,
      StateToken = game.StateToken};
  }
  if (game.IsWin)
  {
    rwd = new TicTacToeReward() { Discount = .9, Value = 100, IsAbsorbent = true,
      StateToken = game.StateToken };
  }
  if (game.IsTie)
  {
    rwd = new TicTacToeReward() { Discount = .9, Value = 50, IsAbsorbent = true,
      StateToken = game.StateToken };
  }
  return rwd;
}

次に、状態の報酬を特定したら、初期報酬値の設定された状態に至るまでの状態をアルゴリズムに指定して、割り引かれた報酬を割り当てられるようにする必要があります。勝利のシナリオとブロックのシナリオでは、これらの状態は、勝利するプレイヤーまたはブロックするプレイヤーのそれまでの状態 (プレイ) すべてになります。引き分けシナリオの場合、両方のプレイヤーのすべての状態 (プレイ) に報酬を割り当てる必要があります。

internal override IEnumerable<IPastState> GetRewardingQStates(int stateToken)
{
  var game = new TicTacToeGame(stateToken);
  if (game.IsTie)           
    return game.GetAllStateSequence();           
  return game.GetLastPlayersStateSequence();
}

最後に、状態をアルゴリズムで反復する方法を決定する遷移ポリシーを実装する必要があります。三目並べの場合、考えられるすべての組み合わせを調査する遷移ポリシーを実装します。

internal override IEnumerable<int> GetTransitions(int stateToken)
{
  var game = new TicTacToeGame(stateToken);
  return game.GetPossiblePlays();
}

コンピューターとの対戦

この時点で、ソリューションを発行して、REST API を呼び出し、1 ~ 9 の初期遷移を指定することでトレーニングを開始できます。

トレーニングが終了したら、状態トークンを渡して推奨値を受け取ることのできるアプリケーションを、API を使用して作成できます。本稿付属のソース コードには、このバックエンドを使用するユニバーサル Windows プラットフォーム アプリが含まれています。このゲームを図 10 に示します。

三目並べゲーム
図 10 三目並べゲーム

まとめ

Q 学習と Service Fabric を使用することで、分散プラットフォームを使用してデータを計算、永続化するエンド ツー エンドのフレームワークを作成できました。このアプローチを説明するため、三目並べゲームを取り上げました。そこで、勝利、引き分け、ブロックとなる条件を示してコンピューターにゲームをプレイさせるだけで、ゲームのプレイ方法を学習し、許容できるレベルでゲームをプレイするバックエンドを作成しました。


Jesus Aguilar は、マイクロソフトの Technical Evangelism and Development チームでシニア クラウド アーキテクトを務めています。彼は、クラウドで生まれた素晴らしい企業とパートナー関係を結び、それらの企業がさまざまな規模の魅力的なエクスペリエンスを提供する支援をしています。ソフトウェア エンジニアリングとソリューション設計に情熱を傾けており、"予測分析"、"スケーラビリティ"、"同時実行"、"デザイン パターン"、"<任意の文字>aaS" などの用語に反応します。Twitter (@giventocode、英語) で彼をフォローしたり、giventocode.com (英語) で公開されている彼のブログを読んだりすることができます。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Rob Bagby、Mike Lanzetta、および Matthew Snider に心より感謝いたします。
Rob Bagby は、マイクロソフトのシニア クラウド アーキテクトです。彼は、影響力のある ISV と業務で連携し、ISV が自社のソフトウェアを Azure に実装する支援をしています。この役職に就く前はコンサルタントをしており、独立性、管理性、およびスケーラビリティの高いシステムの構築をしていました。

Mike Lanzetta は、マイクロソフトの Partner Catalyst チームの開発者で、機械学習、ビッグ データ、およびシステム プログラミングに携わっています。ワシントン大学でコンピューター サイエンスおよびエンジニアリングの修士号を取得しました。この業界では 20 年以上にわたり、ベンチャー企業から Amazon やマイクロソフトまで、さまざまな企業に勤務しています。

Matt Snider は、2008 年に Microsoft に入社し、.NET の一部の開発に取り組みました。.NET 4 のリリース後、Service Fabric チームに初代テクニカル PM として加わり、さまざまな機能領域に携わりました。現在もチームの一員として働いており、最近では主にクラスター リソース マネージャー (Orchestrator)、Reliable Collection、およびスタックのフェールオーバー/レプリケーション部分を担当しています。余暇にはビール、ハイキング、音楽などを楽しんでいます。