MRが楽しい

MRやVRについて学習したことを書き残す

公式チュートリアル「MR and Azure 307 9章」を試してみる

本日はチュートリアルの実施枠です。
Academyの「MR and Azure 307: Machine learning」の実施内容をまとめます。
docs.microsoft.com
前回記事の続きです。
bluebirdofoz.hatenablog.com
今回は「Chapter 9」です。

Chapter 9:Create the ProductPrediction class

次に作成するクラスは、ProductPredictionクラスです。
このクラスは以下の機能を担当します。
・現在の日付と時刻で Machine Learning Service インスタンスを照会する。
JSONレスポンスを使用可能なデータにデシリアライズします。
・データの解釈を行い、3つの推奨製品を取得する。

1.Script フォルダに移動します。
フォルダの中で右クリックし、Create -> C# Script をクリックします。
作成したスクリプトの名前を ProductPrediction に設定します。
f:id:bluebirdofoz:20181123124203j:plain

2.以下の通り、スクリプトを編集します。
・ProductPrediction.cs

// 名前空間の追加
using System;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using Newtonsoft.Json;
using UnityEngine.Networking;
using System.Runtime.Serialization;
using System.Collections;

public class ProductPrediction : MonoBehaviour {
    /// <summary>
    /// This object represents the Prediction request
    /// It host the day of the year and hour of the day
    /// The product must be left blank when serialising
    /// このオブジェクトは、予測要求を表します
    /// それは1年のうちの日付と1時間おきの時間をホストします
    /// シリアル化するときは、製品を空白のままにしておく必要があります
    /// </summary>
    public class RootObject
    {
        public Inputs Inputs { get; set; }
    }

    public class Inputs
    {
        public Input1 input1 { get; set; }
    }

    public class Input1
    {
        public List<string> ColumnNames { get; set; }
        public List<List<string>> Values { get; set; }
    }

    /// <summary>
    /// This object containing the deserialised Prediction result
    /// It host the list of the products
    /// and the likelihood of them being sold at current date and time
    /// このオブジェクトは、デシリアライズされた予測結果を含みます
    /// 現在の日付と時刻に販売される可能性と製品のリストをホストします
    /// </summary>
    public class Prediction
    {
        public Results Results { get; set; }
    }

    public class Results
    {
        public Output1 output1;
    }

    public class Output1
    {
        public string type;
        public Value value;
    }

    public class Value
    {
        public List<string> ColumnNames { get; set; }
        public List<List<string>> Values { get; set; }
    }


    /// <summary>
    /// The 'Primary Key' from your Machine Learning Portal
    /// Machine Learning ポータルの「API Key」を設定する
    /// </summary>
    private string authKey = "-- Insert your service authentication key here --";

    /// <summary>
    /// The 'Request-Response' Service Endpoint from your Machine Learning Portal
    /// Machine Learning ポータルの「リクエストURI(POST)」を設定する
    /// </summary>
    private string serviceEndpoint = "-- Insert your service endpoint here --";

    /// <summary>
    /// The Hour as set in Windows
    /// Windowsで設定された時間
    /// </summary>
    private string thisHour;

    /// <summary>
    /// The Day, as set in Windows
    /// Windowsで設定された日付(日)
    /// </summary>
    private string thisDay;

    /// <summary>
    /// The Month, as set in Windows
    /// Windowsで設定された日付(月)
    /// </summary>
    private string thisMonth;

    /// <summary>
    /// The Numeric Day from current Date Conversion
    /// 現在の日付を変換した年内の経過日数
    /// </summary>
    private string dayOfTheYear;

    /// <summary>
    /// Dictionary for holding the first (or default) provided prediction 
    /// from the Machine Learning Experiment
    /// 機械学習実験からの最初の(またはデフォルトの)予測を保持するための Dictionary クラス
    /// </summary>    
    private Dictionary<string, string> predictionDictionary;

    /// <summary>
    /// List for holding product prediction with name and scores
    /// 製品の予測を名前とスコアで保持するためのリスト
    /// </summary>
    private List<KeyValuePair<string, double>> keyValueList;


    /// <summary>
    /// Called on initialization
    /// 初期化処理
    /// </summary>
    void Start()
    {
        // Call to get the current date and time as set in Windows
        // Windowsで設定されている現在の日付と時刻を取得する
        GetTodayDateAndTime();

        // Call to set the HOUR in the UI
        // UIで時間を設定するための呼び出し
        ShelfKeeper.instance.SetTime(thisHour);

        // Call to set the DATE in the UI
        // UIで日付を設定するための呼び出し
        ShelfKeeper.instance.SetDate(thisDay, thisMonth);

        // Run the method to Get Predication from Azure Machine Learning
        // Azure Machine LearningからPredicationを取得するメソッドの実行
        StartCoroutine(GetPrediction(thisHour, dayOfTheYear));
    }

    /// <summary>
    /// Get current date and hour
    /// 現在の日付と時間を取得する
    /// </summary>
    private void GetTodayDateAndTime()
    {
        // Get today date and time
        // 今日の日時を取得する
        DateTime todayDate = DateTime.Now;

        // Extrapolate the HOUR
        // 時刻の部分を取得する
        thisHour = todayDate.Hour.ToString();

        // Extrapolate the DATE
        // 日付の部分を取得する
        thisDay = todayDate.Day.ToString();
        thisMonth = todayDate.ToString("MMM");

        // Extrapolate the day of the year
        // 年の部分を取得する
        dayOfTheYear = todayDate.DayOfYear.ToString();
    }

    /// <summary>
    /// 現在の日時をMachine Learningエンドポイントに通知し、JSON形式の応答を受け取る
    /// </summary>
    /// <param name="timeOfDay"></param>
    /// <param name="dayOfYear"></param>
    /// <returns></returns>
    private IEnumerator GetPrediction(string timeOfDay, string dayOfYear)
    {
        // Populate the request object 
        // Using current day of the year and hour of the day
        // リクエストオブジェクトに値を設定する
        // 現在の日付と時間を使用する
        RootObject ro = new RootObject
        {
            Inputs = new Inputs
            {
                input1 = new Input1
                {
                    ColumnNames = new List<string>
                    {
                        "day",
                        "hour",
                    "product"
                    },
                    Values = new List<List<string>>()
                }
            }
        };

        List<string> l = new List<string>
        {
            dayOfYear,
            timeOfDay,
            ""
        };

        ro.Inputs.input1.Values.Add(l);

        Debug.LogFormat("Score request built");

        // Serialise the request
        // リクエストをシリアライズする
        string json = JsonConvert.SerializeObject(ro);

        using (UnityWebRequest www = UnityWebRequest.Post(serviceEndpoint, "POST"))
        {
            byte[] jsonToSend = new System.Text.UTF8Encoding().GetBytes(json);
            www.uploadHandler = new UploadHandlerRaw(jsonToSend);

            www.downloadHandler = new DownloadHandlerBuffer();
            www.SetRequestHeader("Authorization", "Bearer " + authKey);
            www.SetRequestHeader("Content-Type", "application/json");
            www.SetRequestHeader("Accept", "application/json");

            yield return www.SendWebRequest();
            string response = www.downloadHandler.text;

            // Deserialize the response
            // レスポンスをデシリアライズする
            DataContractSerializer serializer;
            serializer = new DataContractSerializer(typeof(string));
            DeserialiseJsonResponse(response);
        }
    }

    /// <summary>
    /// Deserialize the response received from the Machine Learning portal
    /// Machine Learning ポータルから受け取ったレスポンスをデシリアライズする
    /// </summary>
    public void DeserialiseJsonResponse(string jsonResponse)
    {
        // Deserialize JSON
        // JSON をデシリアライズする
        Prediction prediction = JsonConvert.DeserializeObject<Prediction>(jsonResponse);
        predictionDictionary = new Dictionary<string, string>();

        for (int i = 0; i < prediction.Results.output1.value.ColumnNames.Count; i++)
        {
            if (prediction.Results.output1.value.Values[0][i] != null)
            {
                predictionDictionary.Add(prediction.Results.output1.value.ColumnNames[i], prediction.Results.output1.value.Values[0][i]);
            }
        }

        keyValueList = new List<KeyValuePair<string, double>>();

        // Strip all non-results, by adding only items of interest to the scoreList
        // scoreList に可能性のある項目だけを追加する
        for (int i = 0; i < predictionDictionary.Count; i++)
        {
            KeyValuePair<string, string> pair = predictionDictionary.ElementAt(i);
            if (pair.Key.StartsWith("Scored Probabilities"))
            {
                // Parse string as double then simplify the string key so to only have the item name
                // 文字列を二重として解析し、文字列キーを単純化してアイテム名のみを持つようにする
                double scorefloat = 0f;
                double.TryParse(pair.Value, out scorefloat);
                string simplifiedName =
                    pair.Key.Replace("\"", "").Replace("Scored Probabilities for Class", "").Trim();
                keyValueList.Add(new KeyValuePair<string, double>(simplifiedName, scorefloat));
            }
        }

        // Sort Predictions (results will be lowest to highest)
        keyValueList.Sort((x, y) => y.Value.CompareTo(x.Value));

        // Spawn the top three items, from the keyValueList, which we have sorted
        for (int i = 0; i < 3; i++)
        {
            ShelfKeeper.instance.SpawnProduct(keyValueList[i].Key, i);
        }

        // Clear lists in case of reuse
        keyValueList.Clear();
        predictionDictionary.Clear();
    }
}

f:id:bluebirdofoz:20181123124229j:plain

authKey を、第4章で取得した API Key で置き換えます。
serviceEndpoint を、第4章で取得した リクエスURI(POST) のURLで置き換えます。
f:id:bluebirdofoz:20181123124240j:plain

3.Visual Studio で変更を保存して Unity に戻ります。
f:id:bluebirdofoz:20181123124255j:plain

4.Hierarchy の MixedRealityCameraParent を開きます。
直下の MixedRealityCamera を選択し、ProductPrediction をアタッチします。
f:id:bluebirdofoz:20181123124306j:plain

Chapter 9 はここまでです。
次回は Chapter 10 を実施します。
bluebirdofoz.hatenablog.com