共用方式為


快速入門:個人化工具用戶端程式庫

重要

從 2023 年 9 月 20 日起,您將無法建立新的個人化工具資源。 個人化工具服務將於 2026 年 10 月 1 日淘汰。

開始使用 Azure AI 個人化服務工具用戶端程式庫來設定基本學習迴圈。 學習迴圈是決策和意見反應的系統:應用程式會向服務要求決策排名,然後使用排名最高的選擇,並從結果計算獎勵分數。 它會將獎勵分數傳回給服務。 經過一段時間後,個人化服務工具會使用 AI 演算法,以針對任何指定的內容做出更好的決策。 請遵循下列步驟來設定應用程式範例。

範例案例

在本快速入門中,雜貨電子零售商想要在其網站上向每位客戶顯示相關和個人化產品來增加營收。 在主頁面上有「精選產品」區段,可向潛在客戶顯示調理食品產品。 電子零售商想要判斷如何向正確的客戶顯示相符的產品,藉此大幅提高購買的可能性。

個人化服務工具使用增強式學習,以自動化、可調整且可調適的方式解決此問題。 您將了解如何建立動作,以及其特徵、內容特徵獎勵分數。 您將使用個人化工具用戶端程式庫來呼叫排名和獎勵 API

參考文件 | 程式庫原始程式碼 | 套件 (NuGet) | .NET 程式碼範例

必要條件

  • Azure 訂用帳戶 - 建立免費帳戶
  • 最新版 .NET Core
  • 擁有 Azure 訂閱之後,在 Azure 入口網站中建立個人化工具資源,以取得您的金鑰和端點。 在其部署後,選取 [前往資源]
    • 您需要來自所建立資源的金鑰和端點,以將應用程式連線至個人化工具 API。 您稍後會在快速入門中將金鑰和端點貼到下列程式碼中。
    • 您可以使用免費定價層 (F0) 來試用服務,之後可升級至付費層以用於實際執行環境。

模型設定

變更模型更新頻率

在 Azure 入口網站中,前往個人化服務工具資源的 [設定] 頁面,將 [模型更新頻率] 變更為 30 秒。 這個較短的時間將會快速定型模型,讓您可以查看建議的動作會如何針對每個反覆項目變更。

變更模型更新頻率

變更奬勵等待時間

在 Azure 入口網站中,前往個人化服務工具資源的 [設定] 頁面,將 [奬勵等待時間] 變更為 10 分鐘。 這會決定模型在傳送建議之後等候的時間長度,以接收來自該建議的獎勵意見反應。 直到獎勵等候時間過後,才會進行定型。

變更奬勵等待時間

建立新的 C# 應用程式

在您慣用的編輯器或 IDE 中,建立新的 .NET Core 應用程式。

在主控台視窗中 (例如 cmd、PowerShell 或 Bash),使用 dotnet new 命令建立名為 personalizer-quickstart 的新主控台應用程式。 此命令會建立簡單的 "Hello World" C# 專案,內含單一原始程式檔:Program.cs

dotnet new console -n personalizer-quickstart

將目錄變更為新建立的應用程式資料夾。 接著,使用下列命令來建置應用程式:

dotnet build

建置輸出應該不會有警告或錯誤。

...
Build succeeded.
 0 Warning(s)
 0 Error(s)
...

安裝用戶端程式庫

在應用程式目錄中,使用下列命令安裝適用於 .NET 的個人化工具用戶端程式庫:

dotnet add package Microsoft.Azure.CognitiveServices.Personalizer --version 1.0.0

提示

如果您使用 Visual Studio IDE,則可以取得可下載 NuGet 套件形式的用戶端程式庫。

程式碼區塊 1:產生範例資料

個人化服務工具是要在接收和解譯即時資料的應用程式上執行。 在此快速入門中,您將使用範例程式碼在雜貨網站上產生虛構的客戶動作。 下列程式碼區塊定義三個主要方法:GetActionsGetCoNtextGetRewardScore

  • GetActions 會傳回雜貨網站需要排名的選項清單。 在此範例中,動作是膳食產品。 每個動作選擇都有可能會影響使用者後續行為的詳細資料 (功能)。 動作是作為排名 API 的輸入使用

  • GetCoNtext 會傳回模擬的客戶造訪。 它會選取隨機化詳細資料 (內容功能),例如:哪位客戶在場與造訪發生的時間。 一般而言,內容代表應用程式、系統、環境或使用者的目前狀態。 內容物件是作為排名 API 的輸入。

    本快速入門的內容功能十分簡單。 不過,在實際生產系統中,設計您的功能評估其有效性十分重要。 如需指導,請參閱所連結的文件。

  • GetRewardScore 它會傳回介於零到一之間的分數,代表客戶互動的成功。 它會使用簡單的邏輯來判斷不同內容如何回應不同的動作選擇。 例如,特定使用者總是給素食與純素食產品打 1.0 分,給其他產品打 0.0 分。 在實際案例中,個人化工具將自排名和獎勵 API 呼叫中傳送的資料學習使用者偏好設定。 您不會在範例程式碼中明確定義這些設定。

    在實際執行環境系統中,獎勵分數應設計成符合營運目標和 KPI。 判斷如何計算獎勵計量可能需要一些實驗。

    在下方程式碼中,使用者對動作的偏好設定和回應會硬式編碼為一系列的條件陳述式,而說明文字會包含在程式碼中以供示範之用。

  1. 尋找您的金鑰和端點。

    重要

    前往 Azure 入口網站。 如果您在 [必要條件] 區段中建立的個人化工具資源成功部署,請按一下 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。

    完成時,請記得從程式碼中移除金鑰,且不要公開張貼金鑰。 在生產環境中,請考慮使用安全的方式來儲存及存取您的認證。 例如,Azure金鑰保存庫

  2. 在文字編輯器或 IDE 中開啟 Program.cs,然後貼上下列程式碼。

    using Microsoft.Azure.CognitiveServices.Personalizer;
    using Microsoft.Azure.CognitiveServices.Personalizer.Models;
    
    class Program
    {
        private static readonly string ApiKey = "REPLACE_WITH_YOUR_PERSONALIZER_KEY";
        private static readonly string ServiceEndpoint = "REPLACE_WITH_YOUR_ENDPOINT_URL";
    
        static PersonalizerClient InitializePersonalizerClient(string url)
        {
            PersonalizerClient client = new PersonalizerClient(
                new ApiKeyServiceClientCredentials(ApiKey))
            { Endpoint = url };
    
            return client;
        }
    
        static Dictionary<string, ActionFeatures> actions = new Dictionary<string, ActionFeatures>
        {
        {"pasta", new ActionFeatures(
                        new BrandInfo(company: "pasta_inc"),
                        new ItemAttributes(
                            quantity: 1,
                            category: "Italian",
                            price: 12),
                        new DietaryAttributes(
                            vegan: false,
                            lowCarb: false,
                            highProtein: false,
                            vegetarian: false,
                            lowFat: true,
                            lowSodium: true))},
        {"bbq", new ActionFeatures(
                        new BrandInfo(company: "ambisco"),
                        new ItemAttributes(
                            quantity: 2,
                            category: "bbq",
                            price: 20),
                        new DietaryAttributes(
                            vegan: false,
                            lowCarb: true,
                            highProtein: true,
                            vegetarian: false,
                            lowFat: false,
                            lowSodium: false))},
        {"bao", new ActionFeatures(
                        new BrandInfo(company: "bao_and_co"),
                        new ItemAttributes(
                            quantity: 4,
                            category: "Chinese",
                            price: 8),
                        new DietaryAttributes(
                            vegan: false,
                            lowCarb: true,
                            highProtein: true,
                            vegetarian: false,
                            lowFat: true,
                            lowSodium: false))},
        {"hummus", new ActionFeatures(
                        new BrandInfo(company: "garbanzo_inc"),
                        new ItemAttributes(
                            quantity: 1,
                            category: "Breakfast",
                            price: 5),
                        new DietaryAttributes(
                            vegan: true,
                            lowCarb: false,
                            highProtein: true,
                            vegetarian: true,
                            lowFat: false,
                            lowSodium: false))},
        {"veg_platter", new ActionFeatures(
                        new BrandInfo(company: "farm_fresh"),
                        new ItemAttributes(
                            quantity: 1,
                            category: "produce",
                            price: 7),
                        new DietaryAttributes(
                            vegan: true,
                            lowCarb: true,
                            highProtein: false,
                            vegetarian: true,
                            lowFat: true,
                            lowSodium: true ))},
    };
    
        static IList<RankableAction> GetActions()
        {
            IList<RankableAction> rankableActions = new List<RankableAction>();
            foreach (var action in actions)
            {
                rankableActions.Add(new RankableAction
                {
                    Id = action.Key,
                    Features = new List<object>() { action.Value }
                });
            }
    
            return rankableActions;
        }
    
        public class BrandInfo
        {
            public string Company { get; set; }
            public BrandInfo(string company)
            {
                Company = company;
            }
        }
    
        public class ItemAttributes
        {
            public int Quantity { get; set; }
            public string Category { get; set; }
            public double Price { get; set; }
            public ItemAttributes(int quantity, string category, double price)
            {
                Quantity = quantity;
                Category = category;
                Price = price;
            }
        }
    
        public class DietaryAttributes
        {
            public bool Vegan { get; set; }
            public bool LowCarb { get; set; }
            public bool HighProtein { get; set; }
            public bool Vegetarian { get; set; }
            public bool LowFat { get; set; }
            public bool LowSodium { get; set; }
            public DietaryAttributes(bool vegan, bool lowCarb, bool highProtein, bool vegetarian, bool lowFat, bool lowSodium)
            {
                Vegan = vegan;
                LowCarb = lowCarb;
                HighProtein = highProtein;
                Vegetarian = vegetarian;
                LowFat = lowFat;
                LowSodium = lowSodium;
    
            }
        }
    
        public class ActionFeatures
        {
            public BrandInfo BrandInfo { get; set; }
            public ItemAttributes ItemAttributes { get; set; }
            public DietaryAttributes DietaryAttributes { get; set; }
            public ActionFeatures(BrandInfo brandInfo, ItemAttributes itemAttributes, DietaryAttributes dietaryAttributes)
            {
                BrandInfo = brandInfo;
                ItemAttributes = itemAttributes;
                DietaryAttributes = dietaryAttributes;
            }
        }
    
        public static Context GetContext()
        {
            return new Context(
                    user: GetRandomUser(),
                    timeOfDay: GetRandomTimeOfDay(),
                    location: GetRandomLocation(),
                    appType: GetRandomAppType());
        }
    
        static string[] timesOfDay = new string[] { "morning", "afternoon", "evening" };
    
        static string[] locations = new string[] { "west", "east", "midwest" };
    
        static string[] appTypes = new string[] { "edge", "safari", "edge_mobile", "mobile_app" };
    
        static IList<UserProfile> users = new List<UserProfile>
    {
        new UserProfile(
            name: "Bill",
            dietaryPreferences: new Dictionary<string, bool> { { "low_carb", true } },
            avgOrderPrice: "0-20"),
        new UserProfile(
            name: "Satya",
            dietaryPreferences: new Dictionary<string, bool> { { "low_sodium", true} },
            avgOrderPrice: "201+"),
        new UserProfile(
            name: "Amy",
            dietaryPreferences: new Dictionary<string, bool> { { "vegan", true }, { "vegetarian", true } },
            avgOrderPrice: "21-50")
    };
    
        static string GetRandomTimeOfDay()
        {
            var random = new Random();
            var timeOfDayIndex = random.Next(timesOfDay.Length);
            Console.WriteLine($"TimeOfDay: {timesOfDay[timeOfDayIndex]}");
            return timesOfDay[timeOfDayIndex];
        }
    
        static string GetRandomLocation()
        {
            var random = new Random();
            var locationIndex = random.Next(locations.Length);
            Console.WriteLine($"Location: {locations[locationIndex]}");
            return locations[locationIndex];
        }
    
        static string GetRandomAppType()
        {
            var random = new Random();
            var appIndex = random.Next(appTypes.Length);
            Console.WriteLine($"AppType: {appTypes[appIndex]}");
            return appTypes[appIndex];
        }
    
        static UserProfile GetRandomUser()
        {
            var random = new Random();
            var userIndex = random.Next(users.Count);
            Console.WriteLine($"\nUser: {users[userIndex].Name}");
            return users[userIndex];
        }
    
        public class UserProfile
        {
            // Mark name as non serializable so that it is not part of the context features
            [NonSerialized()]
            public string Name;
            public Dictionary<string, bool> DietaryPreferences { get; set; }
            public string AvgOrderPrice { get; set; }
    
            public UserProfile(string name, Dictionary<string, bool> dietaryPreferences, string avgOrderPrice)
            {
                Name = name;
                DietaryPreferences = dietaryPreferences;
                AvgOrderPrice = avgOrderPrice;
            }
        }
    
        public class Context
        {
            public UserProfile User { get; set; }
            public string TimeOfDay { get; set; }
            public string Location { get; set; }
            public string AppType { get; set; }
    
            public Context(UserProfile user, string timeOfDay, string location, string appType)
            {
                User = user;
                TimeOfDay = timeOfDay;
                Location = location;
                AppType = appType;
            }
        }
        public static float GetRewardScore(Context context, string actionId)
        {
            float rewardScore = 0.0f;
            string userName = context.User.Name;
            ActionFeatures actionFeatures = actions[actionId];
            if (userName.Equals("Bill"))
            {
                if (actionFeatures.ItemAttributes.Price < 10 && !context.Location.Equals("midwest"))
                {
                    rewardScore = 1.0f;
                    Console.WriteLine($"\nBill likes to be economical when he's not in the midwest visiting his friend Warren. He bought {actionId} because it was below a price of $10.");
                }
                else if (actionFeatures.DietaryAttributes.LowCarb && context.Location.Equals("midwest"))
                {
                    rewardScore = 1.0f;
                    Console.WriteLine($"\nBill is visiting his friend Warren in the midwest. There he's willing to spend more on food as long as it's low carb, so Bill bought {actionId}.");
                }
                else if (actionFeatures.ItemAttributes.Price >= 10 && !context.Location.Equals("midwest"))
                {
                    rewardScore = 1.0f;
                    Console.WriteLine($"\nBill didn't buy {actionId} because the price was too high when not visting his friend Warren in the midwest.");
                }
                else if (actionFeatures.DietaryAttributes.LowCarb && context.Location.Equals("midwest"))
                {
                    rewardScore = 1.0f;
                    Console.WriteLine($"\nBill didn't buy {actionId} because it's not low-carb, and he's in the midwest visitng his friend Warren.");
                }
            }
            else if (userName.Equals("Satya"))
            {
                if (actionFeatures.DietaryAttributes.LowSodium)
                {
                    rewardScore = 1.0f;
                    Console.WriteLine($"\nSatya is health conscious, so he bought {actionId} since it's low in sodium.");
                }
                else
                {
                    Console.WriteLine($"\nSatya did not buy {actionId} because it's not low sodium.");
                }
            }
            else if (userName.Equals("Amy"))
            {
                if (actionFeatures.DietaryAttributes.Vegan || actionFeatures.DietaryAttributes.Vegetarian)
                {
                    rewardScore = 1.0f;
                    Console.WriteLine($"\nAmy likes to eat plant-based foods, so she bought {actionId} because it's vegan or vegetarian friendly.");
                }
                else
                {
                    Console.WriteLine($"\nAmy did not buy {actionId} because it's not vegan or vegetarian.");
                }
            }
            return rewardScore;
        }
        // ...
    
  3. 在指示的位置中貼上您的金鑰和端點。 您的端點格式為 https://<your_resource_name>.cognitiveservices.azure.com/

    重要

    完成時,請記得從程式碼中移除金鑰,且不要公開張貼金鑰。 在生產環境中,請使用安全的方式來儲存和存取您的認證,例如 Azure Key Vault。 如需詳細資訊,請參閱 Azure AI 服務安全性一文。

程式碼區塊 2:逐一查看學習迴圈

下一個程式碼區塊會定義 main 方法,並關閉指令碼。 其會執行學習迴圈反覆運算,在其中會產生內容 (包括客戶)、使用排名 API 要求該內容中的動作排名、計算獎勵分數,並使用獎勵 API 將該分數傳回至個人化服務工具服務。 其在每一個步驟都會將相關資訊列印至主控台。

在此案例中,每當呼叫排名時會判斷哪項產品應顯示於「精選產品」區段中。 接著,獎勵呼叫指出使用者是否購買精選產品。 獎勵會透過一般 EventId 值與其決策相關聯。

    static void Main(string[] args)
    {
        int iteration = 1;
        bool runLoop = true;

        // Get the actions list to choose from personalizer with their features.
        IList<RankableAction> actions = GetActions();

        // Initialize Personalizer client.
        PersonalizerClient client = InitializePersonalizerClient(ServiceEndpoint);

        do
        {
            Console.WriteLine("\nIteration: " + iteration++);

            // Get context information.
            Context context = GetContext();

            // Create current context from user specified data.
            IList<object> currentContext = new List<object>() {
            context
        };

            // Generate an ID to associate with the request.
            string eventId = Guid.NewGuid().ToString();

            // Rank the actions
            var request = new RankRequest(actions: actions, contextFeatures: currentContext, eventId: eventId);
            RankResponse response = client.Rank(request);

            Console.WriteLine($"\nPersonalizer service thinks {context.User.Name} would like to have: {response.RewardActionId}.");

            float reward = GetRewardScore(context, response.RewardActionId);

            // Send the reward for the action based on user response.
            client.Reward(response.EventId, new RewardRequest(reward));

            Console.WriteLine("\nPress q to break, any other key to continue:");
            runLoop = !(GetKey() == "Q");

        } while (runLoop);
    }

        private static string GetKey()
    {
        return Console.ReadKey().Key.ToString().Last().ToString().ToUpper();
    }

}

執行程式

從應用程式目錄使用 dotnet dotnet run 命令來執行應用程式。

dotnet run

在第一次反覆運算時,個人化服務工具會建議隨機動作,因為其尚未完成任何定型。 您可以選擇性地執行更多反覆運算。 大約 10 分鐘之後,服務會開始顯示其建議的改善。

快速入門程式會詢問幾個問題以收集使用者偏好 (稱為特性),然後提供最佳的動作。

產生許多事件以供分析 (選擇性)

比如,您可以從本快速入門示範案例輕鬆產生 5,000 個事件,這足以讓您獲得使用新手模式、線上模式、執行離線評估,以及建立功能評估的體驗。 將上述 main 方法取代為:

    static void Main(string[] args)
    {
    int iteration = 1;
    int runLoop = 0;

    // Get the actions list to choose from personalizer with their features.
    IList<RankableAction> actions = GetActions();

    // Initialize Personalizer client.
    PersonalizerClient client = InitializePersonalizerClient(ServiceEndpoint);

    do
    {
        Console.WriteLine("\nIteration: " + iteration++);

        // Get context information.
        Context context = GetContext();

        // Create current context from user specified data.
        IList<object> currentContext = new List<object>() {
            context
        };

        // Generate an ID to associate with the request.
        string eventId = Guid.NewGuid().ToString();

        // Rank the actions
        var request = new RankRequest(actions: actions, contextFeatures: currentContext, eventId: eventId);
        RankResponse response = client.Rank(request);

        Console.WriteLine($"\nPersonalizer service thinks {context.User.Name} would like to have: {response.RewardActionId}.");

        float reward = GetRewardScore(context, response.RewardActionId);

        // Send the reward for the action based on user response.
        client.Reward(response.EventId, new RewardRequest(reward));

        runLoop = runLoop + 1;

    } while (runLoop < 1000);
}

適用於此快速入門的資源程式碼可在 GitHub 上取得。

參考文件 | 套件 (npm) | 快速入門程式碼範例

必要條件

  • Azure 訂用帳戶 - 建立免費帳戶
  • 安裝 Node.js 和 npm (已使用 Node.js v14.16.0 和 npm 6.14.11 進行驗證)。
  • 擁有 Azure 訂閱之後,在 Azure 入口網站中建立個人化工具資源,以取得您的金鑰和端點。 在其部署後,選取 [前往資源]
    • 您需要來自所建立資源的金鑰和端點,以將應用程式連線至個人化工具 API。 您稍後會在快速入門中將金鑰和端點貼到下列程式碼中。
    • 您可以使用免費定價層 (F0) 來試用服務,之後可升級至付費層以用於實際執行環境。

模型設定

變更模型更新頻率

在 Azure 入口網站中,前往個人化服務工具資源的 [設定] 頁面,將 [模型更新頻率] 變更為 30 秒。 這個較短的時間將會快速定型模型,讓您可以查看建議的動作會如何針對每個反覆項目變更。

變更模型更新頻率

變更奬勵等待時間

在 Azure 入口網站中,前往個人化服務工具資源的 [設定] 頁面,將 [奬勵等待時間] 變更為 10 分鐘。 這會決定模型在傳送建議之後等候的時間長度,以接收來自該建議的獎勵意見反應。 直到獎勵等候時間過後,才會進行定型。

變更奬勵等待時間

建立新的 Node.js 應用程式

在主控台視窗 (例如 cmd、PowerShell 或 Bash) 中,為您的應用程式建立新的目錄,並瀏覽至該目錄。

mkdir myapp && cd myapp

執行 npm init -y 命令以建立 package.json 檔案。

npm init -y

在您慣用的編輯器或 IDE 中,建立名為 personalizer-quickstart.js 的新 Node.js 指令碼,並為您的資源端點和訂用帳戶金鑰建立變數。

安裝用戶端程式庫

使用下列命令安裝適用於 Node.js 的個人化工具用戶端程式庫:

npm install @azure/cognitiveservices-personalizer --save

安裝本快速入門的其餘 npm 套件:

npm install @azure/ms-rest-azure-js @azure/ms-rest-js readline-sync uuid --save

程式碼區塊 1:產生範例資料

個人化服務工具是要在接收和解譯即時資料的應用程式上執行。 在此快速入門中,您將使用範例程式碼在雜貨網站上產生虛構的客戶動作。 下列程式碼區塊定義三個主要方法:getActionsListgetContextFeaturesListgetReward

  • getActionsList 會傳回雜貨網站需要排名的選項清單。 在此範例中,動作是膳食產品。 每個動作選擇都有可能會影響使用者後續行為的詳細資料 (功能)。 動作是作為排名 API 的輸入使用

  • getContextFeaturesList 會傳回模擬的客戶造訪。 它會選取隨機化詳細資料 (內容功能),例如:哪位客戶在場與造訪發生的時間。 一般而言,內容代表應用程式、系統、環境或使用者的目前狀態。 內容物件是作為排名 API 的輸入。

    本快速入門的內容功能十分簡單。 不過,在實際生產系統中,設計您的功能評估其有效性十分重要。 如需指導,請參閱所連結的文件。

  • getReward 會提示使用者針對服務作出的建議評分為成功或失敗。 它會傳回介於零到一之間的分數,代表客戶互動的成功。 在實際案例中,個人化服務工具會從即時客戶互動中學習使用者偏好。

    在實際執行環境系統中,獎勵分數應設計成符合營運目標和 KPI。 判斷如何計算獎勵計量可能需要一些實驗。

在文字編輯器或整合式開發環境中開啟 personalizer-quickstart.js,並將其貼上至下列程式碼中。

const uuidv1 = require('uuid/v1');
const Personalizer = require('@azure/cognitiveservices-personalizer');
const CognitiveServicesCredentials = require('@azure/ms-rest-azure-js').CognitiveServicesCredentials;
const readline = require('readline-sync');

function getReward() {
  const answer = readline.question("\nIs this correct (y/n)\n");
  if (answer.toLowerCase() === 'y') {
    console.log("\nGreat| Enjoy your food.");
    return 1;
  }
  console.log("\nYou didn't like the recommended food choice.");
  return 0;
}

function getContextFeaturesList() {
  const timeOfDayFeatures = ['morning', 'afternoon', 'evening', 'night'];
  const tasteFeatures = ['salty', 'sweet'];

  let answer = readline.question("\nWhat time of day is it (enter number)? 1. morning 2. afternoon 3. evening 4. night\n");
  let selection = parseInt(answer);
  const timeOfDay = selection >= 1 && selection <= 4 ? timeOfDayFeatures[selection - 1] : timeOfDayFeatures[0];

  answer = readline.question("\nWhat type of food would you prefer (enter number)? 1. salty 2. sweet\n");
  selection = parseInt(answer);
  const taste = selection >= 1 && selection <= 2 ? tasteFeatures[selection - 1] : tasteFeatures[0];

  console.log("Selected features:\n");
  console.log("Time of day: " + timeOfDay + "\n");
  console.log("Taste: " + taste + "\n");

  return [
    {
      "time": timeOfDay
    },
    {
      "taste": taste
    }
  ];
}

function getExcludedActionsList() {
  return [
    "juice"
  ];
}

function getActionsList() {
  return [
    {
      "id": "pasta",
      "features": [
        {
          "taste": "salty",
          "spiceLevel": "medium"
        },
        {
          "nutritionLevel": 5,
          "cuisine": "italian"
        }
      ]
    },
    {
      "id": "ice cream",
      "features": [
        {
          "taste": "sweet",
          "spiceLevel": "none"
        },
        {
          "nutritionalLevel": 2
        }
      ]
    },
    {
      "id": "juice",
      "features": [
        {
          "taste": "sweet",
          "spiceLevel": "none"
        },
        {
          "nutritionLevel": 5
        },
        {
          "drink": true
        }
      ]
    },
    {
      "id": "salad",
      "features": [
        {
          "taste": "salty",
          "spiceLevel": "low"
        },
        {
          "nutritionLevel": 8
        }
      ]
    }
  ];
}

程式碼區塊 2:逐一查看學習迴圈

下一個程式碼區塊會定義 main 方法,並關閉指令碼。 其會執行學習迴圈反覆運算,在其中詢問使用者在命令列上的偏好,並將該資訊傳送至個人化服務工具以選取最佳動作。 其會向使用者呈現選取的動作,使用者能使用命令列進行選擇。 然後,其會將獎勵分數傳送給個人化服務工具,以通知服務在其選取範圍中的執行表現。

個人化工具學習迴圈是排名獎勵呼叫的循環。 在本快速入門中,用於個人化內容的每個排名呼叫後面都會接著獎勵呼叫,以告訴個人化工具該服務的成效為何。

  1. 將下列程式碼新增至 personalizer-quickstart.js

  2. 尋找您的金鑰和端點。 您的端點格式為 https://<your_resource_name>.cognitiveservices.azure.com/

    重要

    前往 Azure 入口網站。 如果您在 [必要條件] 區段中建立的個人化工具資源成功部署,請按一下 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。

    完成時,請記得從程式碼中移除金鑰,且不要公開張貼金鑰。 在生產環境中,請考慮使用安全的方式來儲存及存取您的認證。 例如,Azure金鑰保存庫

  3. 在指示的位置中貼上您的金鑰和端點。

    重要

    完成時,請記得從程式碼中移除金鑰,且不要公開張貼金鑰。 在生產環境中,請使用安全的方式來儲存和存取您的認證,例如 Azure Key Vault。 如需有關安全性的詳細資訊,請參閱 Azure AI 服務安全性 (部分機器翻譯) 一文。

    async function main() {
    
        // The key specific to your personalization service instance; e.g. "0123456789abcdef0123456789ABCDEF"
        const serviceKey = "PASTE_YOUR_PERSONALIZER_SUBSCRIPTION_KEY_HERE";
      
        // The endpoint specific to your personalization service instance; 
        // e.g. https://<your-resource-name>.cognitiveservices.azure.com
        const baseUri = "PASTE_YOUR_PERSONALIZER_ENDPOINT_HERE";
      
        const credentials = new CognitiveServicesCredentials(serviceKey);
      
        // Initialize Personalization client.
        const personalizerClient = new Personalizer.PersonalizerClient(credentials, baseUri);
      
      
        let runLoop = true;
      
        do {
      
          let rankRequest = {}
      
          // Generate an ID to associate with the request.
          rankRequest.eventId = uuidv1();
      
          // Get context information from the user.
          rankRequest.contextFeatures = getContextFeaturesList();
      
          // Get the actions list to choose from personalization with their features.
          rankRequest.actions = getActionsList();
      
          // Exclude an action for personalization ranking. This action will be held at its current position.
          rankRequest.excludedActions = getExcludedActionsList();
      
          rankRequest.deferActivation = false;
      
          // Rank the actions
          const rankResponse = await personalizerClient.rank(rankRequest);
      
          console.log("\nPersonalization service thinks you would like to have:\n")
          console.log(rankResponse.rewardActionId);
      
          // Display top choice to user, user agrees or disagrees with top choice
          const reward = getReward();
      
          console.log("\nPersonalization service ranked the actions with the probabilities as below:\n");
          for (let i = 0; i < rankResponse.ranking.length; i++) {
            console.log(JSON.stringify(rankResponse.ranking[i]) + "\n");
          }
      
          // Send the reward for the action based on user response.
      
          const rewardRequest = {
            value: reward
          }
      
          await personalizerClient.events.reward(rankRequest.eventId, rewardRequest);
      
          runLoop = continueLoop();
      
        } while (runLoop);
      }
      
      function continueLoop() {
        const answer = readline.question("\nPress q to break, any other key to continue.\n")
        if (answer.toLowerCase() === 'q') {
          return false;
        }
        return true;
      }
    
    main()
    .then(result => console.log("done"))
    .catch(err=> console.log(err));
    

執行程式

使用 Node.js 命令從您的應用程式目錄執行應用程式。

node personalizer-quickstart.js

逐一查看幾個學習迴圈。 大約 10 分鐘之後,服務會開始顯示其建議的改善。

適用於此快速入門的資源程式碼可在 GitHub 上取得。

參考文件 | 程式庫原始程式碼 | 套件 (pypi) | 快速入門程式碼範例

必要條件

  • Azure 訂用帳戶 - 建立免費帳戶
  • Python 3.x
  • 設定 Azure 訂用帳戶後,在 Azure 入口網站中建立個人化工具資源並取得您的金鑰和端點。 在其部署後,選取 [前往資源]
    • 您需要已建立資源的金鑰和端點以將應用程式連線至個人化工具 API,您會從中貼上下方快速入門程式碼。
    • 您可以使用免費定價層 (F0) 來試用服務,之後可升級至付費層以用於實際執行環境。

模型設定

變更模型更新頻率

在 Azure 入口網站中,前往個人化服務工具資源的 [設定] 頁面,將 [模型更新頻率] 變更為 30 秒。 這個較短的時間將會快速定型模型,讓您可以查看建議的動作會如何針對每個反覆項目變更。

變更模型更新頻率

變更奬勵等待時間

在 Azure 入口網站中,前往個人化服務工具資源的 [設定] 頁面,將 [奬勵等待時間] 變更為 10 分鐘。 這會決定模型在傳送建議之後等候的時間長度,以接收來自該建議的獎勵意見反應。 直到獎勵等候時間過後,才會進行定型。

變更奬勵等待時間

建立新的 Python 應用程式

建立名為 personalizer_quickstart.py 的新 Python 檔案。

安裝用戶端程式庫

使用 pip 安裝個人化服務工具用戶端程式庫:

pip install azure-cognitiveservices-personalizer

程式碼區塊 1:產生範例資料

個人化服務工具是要在接收和解譯即時資料的應用程式上執行。 為了實現此快速入門的目的,您將使用範例程式碼在雜貨網站上產生虛構的客戶動作。 下列程式碼區塊定義三個主要函式:get_actionsget_coNtextget_reward_score

  • get_actions 會傳回雜貨網站需要排名的選項清單。 在此範例中,動作是膳食產品。 每個動作選擇都有可能會影響使用者後續行為的詳細資料 (功能)。 動作是作為排名 API 的輸入使用

  • get_context 會傳回模擬的客戶造訪。 它會選取隨機化詳細資料 (內容功能),例如:哪位客戶在場與造訪發生的時間。 一般而言,內容代表應用程式、系統、環境或使用者的目前狀態。 內容物件是作為排名 API 的輸入。

    本快速入門的內容功能十分簡單。 不過,在實際生產系統中,設計您的功能評估其有效性十分重要。 如需指導,請參閱所連結的文件。

  • get_reward_score 它會傳回介於零到一之間的分數,代表客戶互動的成功。 它會使用簡單的邏輯來判斷不同內容將如何回應不同的動作選擇。 例如,特定使用者總是給素食與純素食產品打 1.0 分,給其他產品打 0.0 分。 在實際案例中,個人化工具將自排名和獎勵 API 呼叫中傳送的資料學習使用者偏好設定。 您不會在範例程式碼中明確定義這些設定。

    在實際執行環境系統中,獎勵分數應設計成符合營運目標和 KPI。 判斷如何計算獎勵計量可能需要一些實驗。

    在下方程式碼中,使用者對動作的偏好設定和回應會硬式編碼為一系列的條件陳述式,而說明文字會包含在程式碼中以供示範之用。

請遵循下列步驟設定個人化服務工具指令碼。

  1. 尋找您的金鑰和端點。

    重要

    前往 Azure 入口網站。 如果您在 [必要條件] 區段中建立的個人化工具資源成功部署,請按一下 [後續步驟] 底下的 [前往資源] 按鈕。 您可以在 [資源管理] 底下的 [金鑰和端點] 頁面中找到金鑰和端點。

    完成時,請記得從程式碼中移除金鑰,且不要公開張貼金鑰。 在生產環境中,請考慮使用安全的方式來儲存及存取您的認證。 例如,Azure金鑰保存庫

  2. 在文字編輯器或整合式開發環境中開啟 personalizer-quickstart.py,並將其貼上至下列程式碼中。

  3. 在指示的位置中貼上您的金鑰和端點。 您的端點格式為 https://<your_resource_name>.cognitiveservices.azure.com/

    重要

    完成時,請記得從程式碼中移除金鑰,且不要公開張貼金鑰。 在生產環境中,請使用安全方法來儲存和存取 Azure Key Vault 等認證。 如需詳細資訊,請參閱 Azure AI 服務安全性 (部分機器翻譯)。

from azure.cognitiveservices.personalizer import PersonalizerClient
from azure.cognitiveservices.personalizer.models import RankableAction, RewardRequest, RankRequest
from msrest.authentication import CognitiveServicesCredentials

import datetime, json, os, time, uuid, random

key = "paste_your_personalizer_key_here"
endpoint = "paste_your_personalizer_endpoint_here"

# Instantiate a Personalizer client
client = PersonalizerClient(endpoint, CognitiveServicesCredentials(key))

actions_and_features = {
    'pasta': {
        'brand_info': {
            'company':'pasta_inc'
        }, 
        'attributes': {
            'qty':1, 'cuisine':'italian',
            'price':12
        },
        'dietary_attributes': {
            'vegan': False,
            'low_carb': False,
            'high_protein': False,
            'vegetarian': False,
            'low_fat': True,
            'low_sodium': True
        }
    },
    'bbq': {
        'brand_info' : {
            'company': 'ambisco'
        },
        'attributes': {
            'qty': 2,
            'category': 'bbq',
            'price': 20
        }, 
        'dietary_attributes': {
            'vegan': False,
            'low_carb': True,
            'high_protein': True,
            'vegetarian': False,
            'low_fat': False,
            'low_sodium': False
        }
    },
    'bao': {
        'brand_info': {
            'company': 'bao_and_co'
        },
        'attributes': {
            'qty': 4,
            'category': 'chinese',
            'price': 8
        }, 
        'dietary_attributes': {
            'vegan': False,
            'low_carb': True,
            'high_protein': True,
            'vegetarian': False,
            'low_fat': True,
            'low_sodium': False
        }
    },
    'hummus': {
        'brand_info' : { 
            'company': 'garbanzo_inc'
        },
        'attributes' : {
            'qty': 1,
            'category': 'breakfast',
            'price': 5
        }, 
        'dietary_attributes': {
            'vegan': True, 
            'low_carb': False,
            'high_protein': True,
            'vegetarian': True,
            'low_fat': False, 
            'low_sodium': False
        }
    },
    'veg_platter': {
        'brand_info': {
            'company': 'farm_fresh'
        }, 
        'attributes': {
            'qty': 1,
            'category': 'produce', 
            'price': 7
        },
        'dietary_attributes': {
            'vegan': True,
            'low_carb': True,
            'high_protein': False,
            'vegetarian': True,
            'low_fat': True,
            'low_sodium': True
        }
    }
}

def get_actions():
    res = []
    for action_id, feat in actions_and_features.items():
        action = RankableAction(id=action_id, features=[feat])
        res.append(action)
    return res

user_profiles = {
    'Bill': {
        'dietary_preferences': 'low_carb', 
        'avg_order_price': '0-20',
        'browser_type': 'edge'
    },
    'Satya': {
        'dietary_preferences': 'low_sodium',
        'avg_order_price': '201+',
        'browser_type': 'safari'
    },
    'Amy': {
        'dietary_preferences': {
            'vegan', 'vegetarian'
        },
        'avg_order_price': '21-50',
        'browser_type': 'edge'},
    }

def get_context(user):
    location_context = {'location': random.choice(['west', 'east', 'midwest'])}
    time_of_day = {'time_of_day': random.choice(['morning', 'afternoon', 'evening'])}
    app_type = {'application_type': random.choice(['edge', 'safari', 'edge_mobile', 'mobile_app'])}
    res = [user_profiles[user], location_context, time_of_day, app_type]
    return res

def get_random_users(k = 5):
    return random.choices(list(user_profiles.keys()), k=k)


def get_reward_score(user, actionid, context):
    reward_score = 0.0
    action = actions_and_features[actionid]
    
    if user == 'Bill':
        if action['attributes']['price'] < 10 and (context[1]['location'] !=  "midwest"):
            reward_score = 1.0
            print("Bill likes to be economical when he's not in the midwest visiting his friend Warren. He bought", actionid, "because it was below a price of $10.")
        elif (action['dietary_attributes']['low_carb'] == True) and (context[1]['location'] ==  "midwest"):
            reward_score = 1.0
            print("Bill is visiting his friend Warren in the midwest. There he's willing to spend more on food as long as it's low carb, so Bill bought" + actionid + ".")
            
        elif (action['attributes']['price'] >= 10) and (context[1]['location'] != "midwest"):
            print("Bill didn't buy", actionid, "because the price was too high when not visting his friend Warren in the midwest.")
            
        elif (action['dietary_attributes']['low_carb'] == False) and (context[1]['location'] ==  "midwest"):
            print("Bill didn't buy", actionid, "because it's not low-carb, and he's in the midwest visitng his friend Warren.")
             
    elif user == 'Satya':
        if action['dietary_attributes']['low_sodium'] == True:
            reward_score = 1.0
            print("Satya is health conscious, so he bought", actionid,"since it's low in sodium.")
        else:
            print("Satya did not buy", actionid, "because it's not low sodium.")   
            
    elif user == 'Amy':
        if (action['dietary_attributes']['vegan'] == True) or (action['dietary_attributes']['vegetarian'] == True):
            reward_score = 1.0
            print("Amy likes to eat plant-based foods, so she bought", actionid, "because it's vegan or vegetarian friendly.")       
        else:
            print("Amy did not buy", actionid, "because it's not vegan or vegetarian.")
                
    return reward_score
    # ...

程式碼區塊 2:逐一查看學習迴圈

下一個程式碼區塊會定義 run_personalizer_cycle 函式,並在簡單的使用者摘要迴圈中呼叫函式。 其會執行學習迴圈反覆運算,在其中會產生內容 (包括客戶)、使用排名 API 要求該內容中的動作排名、計算獎勵分數,並使用獎勵 API 將該分數傳回至個人化服務工具服務。 其在每一個步驟都會將相關資訊列印至主控台。

在此案例中,每當呼叫排名時會判斷哪項產品應顯示於「精選產品」區段中。 接著,獎勵呼叫指出使用者是否購買精選產品。 獎勵會透過一般 EventId 值與其決策相關聯。

def run_personalizer_cycle():
    actions = get_actions()
    user_list = get_random_users()
    for user in user_list:
        print("------------")
        print("User:", user, "\n")
        context = get_context(user)
        print("Context:", context, "\n")
        
        rank_request = RankRequest(actions=actions, context_features=context)
        response = client.rank(rank_request=rank_request)
        print("Rank API response:", response, "\n")
        
        eventid = response.event_id
        actionid = response.reward_action_id
        print("Personalizer recommended action", actionid, "and it was shown as the featured product.\n")
        
        reward_score = get_reward_score(user, actionid, context)
        client.events.reward(event_id=eventid, value=reward_score)     
        print("\nA reward score of", reward_score , "was sent to Personalizer.")
        print("------------\n")

continue_loop = True
while continue_loop:
    run_personalizer_cycle()
    
    br = input("Press Q to exit, or any other key to run another loop: ")
    if(br.lower()=='q'):
        continue_loop = False

執行程式

Python 中包含上述所有程式碼後,您即可從應用程式目錄中加以執行。

python personalizer-quickstart.py

在第一次反覆運算時,個人化服務工具會建議隨機動作,因為其尚未完成任何定型。 您可以選擇性地執行更多反覆運算。 大約 10 分鐘之後,服務會開始顯示其建議的改善。

快速入門程式會詢問幾個問題以收集使用者偏好 (稱為特性),然後提供最佳的動作。

產生許多事件以供分析 (選擇性)

比如,您可以從本快速入門示範案例輕鬆產生 5000 個事件,這足以讓您獲得使用新手模式、線上模式、執行離線評估,以及建立功能評估的體驗。 以下列程式碼取代上述程式碼區塊中的 while 迴圈。

for i in range(0,1000):
    run_personalizer_cycle()

適用於此快速入門的資源程式碼可在 GitHub 上取得。

下載已定型的模型

如果您想要從上述範例中以 5000 個事件為基礎所定型的個人化服務工具模型,請造訪個人化服務工具範例存放庫,並下載 Personalizer_QuickStart_Model.zip 檔案。 然後,移至 Azure 入口網站中的個人化服務工具資源,移至 [設定] 頁面和 [匯入/匯出] 索引標籤,然後匯入檔案。

清除資源

若要清除 Azure AI 服務訂用帳戶,您可以刪除資源或資源群組,這將刪除任何相關的資源。

下一步