你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

Azure AI 个性化体验创建服务多槽入门

重要

从 2023 年 9 月 20 日开始,将无法创建新的个性化体验创建服务资源。 个性化体验创建服务将于 2026 年 10 月 1 日停用。

使用多槽个性化(预览版),你可以以会向用户显示多个操作(例如某个产品或某个内容片段)的 Web 布局、轮播和列表中的内容为目标。 使用个性化体验创建服务多槽 API,你可以让个性化体验创建服务中的 AI 模型从用户界面中的位置进行考虑和学习,了解哪些用户上下文和产品会驱动某些行为。 例如,个性化体验创建服务可能会了解到,某些产品或内容作为边栏或页脚会比作为页面上的主要突出显示项目吸引更多的点击。

本指南介绍如何使用个性化体验创建服务多槽 API。

参考文档 | 库源代码 | 包 (NuGet) | 多槽概念 | 示例

先决条件

  • Azure 订阅 - 免费创建订阅
  • .NET Core 的当前版本。
  • 拥有 Azure 订阅后,在 Azure 门户中创建个性化体验创建服务资源,获取密钥和终结点。 部署后,选择”转到资源”。
    • 将需要从创建的资源获取密钥和终结点,以便将应用程序连接到个性化体验创建服务 API。 稍后在快速入门中将密钥和终结点粘贴到下方的代码中。
    • 可以使用免费定价层 (F0) 试用该服务,然后再升级到付费层进行生产。

设置

将个性化体验创建服务实例升级到多槽

注意

多槽个性化设置(预览版)会影响个性化体验创建服务的其他功能。 无法撤消此更改。 在启用多槽个性化设置之前,请参阅多槽个性化设置(预览版)

  1. 如要在 Azure 门户中禁用自动优化,请在个性化体验创建服务资源中,在“资源管理”下的“模型和学习设置”页上,将“自动优化”关闭并保存。

注意

只有禁用自动优化,多槽个性化设置才有效。 未来将支持多槽个性化设置的自动优化。

  1. 如要在 Azure 门户中将个性化体验创建服务升级到多槽,请在个性化体验创建服务资源中,在“资源管理”下的“模型和学习设置”页上,选择“导出学习设置”。 下载的 json 文件中的“arguments”字段将以“--cb_explore_adf”开头。 将此项更改为 --ccb_explore_adf 并保存文件。 CB(上下文 Bandits)和 CCB(条件上下文 Bandits)是个性化体验创建服务分别用于单槽和多槽个性化设置的算法。 ADF(操作依赖功能)意味着操作由功能来表示/识别。

更改前的学习设置

更改后的学习设置

在门户的同一选项卡的“导入学习设置”下浏览,以查找最近修改的 json 文件并将其上传。 这会将个性化体验创建服务实例更新为“多槽”个性化体验创建服务,现在将支持多槽排名和奖励调用。

更改模型更新频率

在 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 Azure.AI.Personalizer --version 2.0.0-beta.2

在偏好的编辑器或 IDE 中打开项目目录中的 Program.cs 文件。 添加以下 using 指令:

using System;
using Azure;
using Azure.AI.Personalizer;
using System.Collections.Generic;
using System.Linq;

对象模型

个性化体验创建服务客户端是一个 PersonalizerClient 对象,它使用包含你的密钥的 Azure.AzureKeyCredential 向 Azure 进行身份验证。

若要查询每个槽中内容的唯一最佳项目,请创建 PersonalizerRankMultiSlotOptions 对象,然后将其传递给 PersonalizerClient.RankMultiSlot。 RankMultiSlot 方法返回 PersonalizerMultiSlotRankResult

若要将奖励分数发送到个性化体验创建服务,请创建 PersonalizerRewardMultiSlotOptions,然后将它传递给 PersonalizerClient.RewardMultiSlot 方法以及相应的事件 ID。

在本快速入门中,可以很容易地获得奖励评分。 在生产系统中,确定哪些因素会影响奖励评分以及影响程度可能是一个复杂的过程,你的判断可能会随时改变。 此设计决策应是个性化体验创建服务体系结构中的主要决策之一。

代码示例

这些代码片段演示如何使用适用于 .NET 的个性化体验创建服务客户端库执行以下任务:

验证客户端

在本部分中,你将执行以下两项操作:

  • 指定密钥和终结点
  • 创建个性化体验创建服务客户端

首先,将以下行添加到 Program 类。 请确保从个性化体验创建服务资源中添加密钥和终结点。

重要

转到 Azure 门户。 如果在“先决条件”部分中创建的个性化体验创建服务资源已成功部署,请单击“后续步骤”下的“转到资源”按钮 。 在资源的“密钥和终结点”页的“资源管理”下可以找到密钥和终结点 。

完成后,请记住将密钥从代码中删除,并且永远不要公开发布该密钥。 对于生产环境,请考虑使用安全的方法来存储和访问凭据。 例如,Azure 密钥保管库

private const string ServiceEndpoint  = "https://REPLACE-WITH-YOUR-PERSONALIZER-RESOURCE-NAME.cognitiveservices.azure.com";
private const string ResourceKey = "<REPLACE-WITH-YOUR-PERSONALIZER-KEY>";

接下来,构造排名和奖励 URL。

static PersonalizerClient InitializePersonalizerClient(Uri url)
{
    return new PersonalizerClient(url, new AzureKeyCredential(ResourceKey));
}

获取以操作形式表示的内容选项

操作表示你希望个性化体验创建服务从中选择最佳内容项的内容选择。 将以下方法添加到 Program 类,以表示操作及其特征的集合。

private static IList<PersonalizerRankableAction> GetActions()
{
    IList<PersonalizerRankableAction> actions = new List<PersonalizerRankableAction>
    {
        new PersonalizerRankableAction(
            id: "Red-Polo-Shirt-432",
            features:
            new List<object>() { new { onSale = "true", price = "20", category = "Clothing" } }
        ),

        new PersonalizerRankableAction(
            id: "Tennis-Racket-133",
            features:
            new List<object>() { new { onSale = "false", price = "70", category = "Sports" } }
        ),

        new PersonalizerRankableAction(
            id: "31-Inch-Monitor-771",
            features:
            new List<object>() { new { onSale = "true", price = "200", category = "Electronics" } }
        ),

        new PersonalizerRankableAction(
            id: "XBox-Series X-117",
            features:
            new List<object>() { new { onSale = "false", price = "499", category = "Electronics" } }
        )
    };

    return actions;
}

获取槽

槽构成了用户将与之交互的页面。 个性化体验创建服务将决定要在定义的每个槽中显示哪个操作。 可以从特定槽中排除操作(显示为 ExcludeActions)。 BaselineAction 是在不使用个性化体验创建服务的情况下会显示的槽的默认操作。

本快速入门提供了简单的槽功能。 在生产系统中,确定和评估特征可能是一件非常重要的事情。

private static IList<PersonalizerSlotOptions> GetSlots()
{
    IList<PersonalizerSlotOptions> slots = new List<PersonalizerSlotOptions>
    {
        new PersonalizerSlotOptions(
            id: "BigHeroPosition",
            features: new List<object>() { new { size = "large", position = "left" } },
            excludedActions: new List<string>() { "31-Inch-Monitor-771" },
            baselineAction: "Red-Polo-Shirt-432"

        ),

        new PersonalizerSlotOptions(
            id: "SmallSidebar",
            features: new List<object>() { new { size = "small", position = "right" } },
            excludedActions: new List<string>() { "Tennis-Racket-133" },
            baselineAction: "XBox-Series X-117"
        ),
    };

    return slots;
}

获取上下文的用户首选项

将以下方法添加到 Program 类,以从命令行获取用户的日期时间输入及用户所在设备的类型。 这些方法将用作上下文特征。

static string GetTimeOfDayForContext()
{
    string[] timeOfDayFeatures = new string[] { "morning", "afternoon", "evening", "night" };

    Console.WriteLine("\nWhat time of day is it (enter number)? 1. morning 2. afternoon 3. evening 4. night");
    if (!int.TryParse(GetKey(), out int timeIndex) || timeIndex < 1 || timeIndex > timeOfDayFeatures.Length)
    {
        Console.WriteLine("\nEntered value is invalid. Setting feature value to " + timeOfDayFeatures[0] + ".");
        timeIndex = 1;
    }

    return timeOfDayFeatures[timeIndex - 1];
}
static string GetDeviceForContext()
{
    string[] deviceFeatures = new string[] { "mobile", "tablet", "desktop" };

    Console.WriteLine("\nWhat is the device type (enter number)? 1. Mobile 2. Tablet 3. Desktop");
    if (!int.TryParse(GetKey(), out int deviceIndex) || deviceIndex < 1 || deviceIndex > deviceFeatures.Length)
    {
        Console.WriteLine("\nEntered value is invalid. Setting feature value to " + deviceFeatures[0] + ".");
        deviceIndex = 1;
    }

    return deviceFeatures[deviceIndex - 1];
}

这两个方法都使用 GetKey 方法从命令行读取用户的选择。

private static string GetKey()
{
    return Console.ReadKey().Key.ToString().Last().ToString().ToUpper();
}
private static IList<object> GetContext(string time, string device)
{
    return new List<object>()
    {
        new { time = time },
        new { device = device }
    };
}

创建学习循环

个性化体验创建服务学习循环是一个 RankMultiSlotRewardMultiSlot 调用周期。 在本快速入门中,用于个性化内容的每个排名调用都后接一个奖励调用,该奖励调用让个性化体验创建服务知道该服务的表现如何。

以下代码会循环调用这样一个循环:通过命令行询问用户的首选项,将该信息发送给个性化体验创建服务来为每个槽选择最佳操作,向客户显示所选项以让他们从列表中进行选择,然后向个性化体验创建服务发送奖励评分,指出服务在做选择时的表现如何。

static void Main(string[] args)
{
    Console.WriteLine($"Welcome to this Personalizer Quickstart!\n" +
    $"This code will help you understand how to use the Personalizer APIs (multislot rank and multislot reward).\n" +
    $"Each iteration represents a user interaction and will demonstrate how context, actions, slots, and rewards work.\n" +
    $"Note: Personalizer AI models learn from a large number of user interactions:\n" +
    $"You won't be able to tell the difference in what Personalizer returns by simulating a few events by hand.\n" +
    $"If you want a sample that focuses on seeing how Personalizer learns, see the Python Notebook sample.");

    int iteration = 1;
    bool runLoop = true;

    IList<PersonalizerRankableAction> actions = GetActions();
    IList<PersonalizerSlotOptions> slots = GetSlots();
    PersonalizerClient client = InitializePersonalizerClient(new Uri(ServiceEndpoint));

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

        string timeOfDayFeature = GetTimeOfDayForContext();
        string deviceFeature = GetDeviceForContext();

        IList<object> currentContext = GetContext(timeOfDayFeature, deviceFeature);

        string eventId = Guid.NewGuid().ToString();

        var multiSlotRankOptions = new PersonalizerRankMultiSlotOptions(actions, slots, currentContext, eventId);
        PersonalizerMultiSlotRankResult multiSlotRankResult = client.RankMultiSlot(multiSlotRankOptions);

        for (int i = 0; i < multiSlotRankResult.Slots.Count(); ++i)
        {
            string slotId = multiSlotRankResult.Slots[i].SlotId;
            Console.WriteLine($"\nPersonalizer service decided you should display: { multiSlotRankResult.Slots[i].RewardActionId} in slot {slotId}. Is this correct? (y/n)");

            string answer = GetKey();

            if (answer == "Y")
            {
                client.RewardMultiSlot(eventId, slotId, 1f);
                Console.WriteLine("\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.");
            }
            else if (answer == "N")
            {
                client.RewardMultiSlot(eventId, slotId, 0f);
                Console.WriteLine("\nYou didn't like the recommended item. The application will send Personalizer a reward of 0 for this choice of action for this slot.");
            }
            else
            {
                client.RewardMultiSlot(eventId, slotId, 0f);
                Console.WriteLine("\nEntered choice is invalid. Service assumes that you didn't like the recommended item.");
            }
        }

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

    } while (runLoop);
}

在接下来的部分中,让我们仔细看看排名和奖励调用。 添加以下方法,这些方法在运行代码文件之前获取内容选项获取槽,以及发送多槽排名和奖励请求

  • GetActions
  • GetSlots
  • GetTimeOfDayForContext
  • GetDeviceForContext
  • GetKey
  • GetContext

请求最佳操作

为了完成排名请求,程序将要求用户指定偏好,以创建内容选项的 Context。 请求包含上下文、操作和槽及其各自的特征,以及唯一的事件 ID,用于接收响应。

本快速入门使用简单的日期时间和用户设备上下文特征。 在生产系统中,确定和评估操作与特征可能是一件非常重要的事情。

string timeOfDayFeature = GetTimeOfDayForContext();
string deviceFeature = GetDeviceForContext();

IList<object> currentContext = GetContext(timeOfDayFeature, deviceFeature);

string eventId = Guid.NewGuid().ToString();

var multiSlotRankOptions = new PersonalizerRankMultiSlotOptions(actions, slots, currentContext, eventId);
PersonalizerMultiSlotRankResult multiSlotRankResult = client.RankMultiSlot(multiSlotRankOptions);

发送奖励

为了获取可用于奖励请求的奖励评分,程序会通过命令行获取用户针对每个槽所做的选择,为该选择分配一个数值(奖励得分),然后将每个槽的唯一事件 ID、槽 ID 和奖励评分作为数值发送到奖励 API。 无需为每个槽定义奖励。

本快速入门分配一个简单的数字(0 或 1)作为奖励评分。 在生产系统中,确定何时向奖励调用发送哪种内容可能不是一个简单的过程,这取决于具体的需求。

for (int i = 0; i < multiSlotRankResult.Slots.Count(); ++i)
{
    string slotId = multiSlotRankResult.Slots[i].SlotId;
    Console.WriteLine($"\nPersonalizer service decided you should display: { multiSlotRankResult.Slots[i].RewardActionId} in slot {slotId}. Is this correct? (y/n)");

    string answer = GetKey();

    if (answer == "Y")
    {
        client.RewardMultiSlot(eventId, slotId, 1f);
        Console.WriteLine("\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.");
    }
    else if (answer == "N")
    {
        client.RewardMultiSlot(eventId, slotId, 0f);
        Console.WriteLine("\nYou didn't like the recommended item. The application will send Personalizer a reward of 0 for this choice of action for this slot.");
    }
    else
    {
        client.RewardMultiSlot(eventId, slotId, 0f);
        Console.WriteLine("\nEntered choice is invalid. Service assumes that you didn't like the recommended item.");
    }
}

运行程序

从应用程序目录,使用 dotnet run 命令运行应用程序。

dotnet run

快速入门程序会提出一些问题来收集用户的偏好(称为“特征”),然后提供排名最高的操作。

其中还有本快速入门的源代码

参考文档 | 多槽概念 | 示例

先决条件

  • Azure 订阅 - 免费创建订阅
  • 安装 Node.js 和 NPM(通过 Node.js v14.16.0 和 NPM 6.14.11 验证)。
  • 拥有 Azure 订阅后,在 Azure 门户中创建个性化体验创建服务资源,获取密钥和终结点。 部署后,选择”转到资源”。
    • 需要从创建的资源获取密钥和终结点,以便将应用程序连接到个性化体验创建服务 API。 稍后在快速入门中将密钥和终结点粘贴到下方的代码中。
    • 可以使用免费定价层 (F0) 试用该服务,然后再升级到付费层进行生产。

设置

将个性化体验创建服务实例升级到多槽

注意

多槽个性化设置(预览版)会影响个性化体验创建服务的其他功能。 无法撤消此更改。 在启用多槽个性化设置之前,请参阅多槽个性化设置(预览版)

  1. 如要在 Azure 门户中禁用自动优化,请在个性化体验创建服务资源中,在“资源管理”下的“模型和学习设置”页上,将“自动优化”关闭并保存。

注意

只有禁用自动优化,多槽个性化设置才有效。 未来将支持多槽个性化设置的自动优化。

  1. 如要在 Azure 门户中将个性化体验创建服务升级到多槽,请在个性化体验创建服务资源中,在“资源管理”下的“模型和学习设置”页上,选择“导出学习设置”。 下载的 json 文件中的“arguments”字段将以“--cb_explore_adf”开头。 将此项更改为 --ccb_explore_adf 并保存文件。 CB(上下文 Bandits)和 CCB(条件上下文 Bandits)是个性化体验创建服务分别用于单槽和多槽个性化设置的算法。 ADF(操作依赖功能)意味着操作由功能来表示/识别。

更改前的学习设置

更改后的学习设置

在门户的同一选项卡的“导入学习设置”下浏览,以查找最近修改的 json 文件并将其上传。 这会将个性化体验创建服务实例更新为“多槽”个性化体验创建服务,现在将支持多槽排名和奖励调用。

更改模型更新频率

在 Azure 门户中,转到个性化体验创建服务资源的“配置”页,并将“模型更新频率”更改为 30 秒。 此短暂持续时间可快速训练模型,使你可以看到建议的操作如何针对每次迭代而变化。

更改模型更新频率

更改奖励等待时间

在 Azure 门户中,转到个性化体验创建服务资源的“配置”页,并将“奖励等待时间”更改为 10 分钟。 这决定了在发送建议后模型将等待多长时间来接收来自该建议的奖励反馈。 在奖励等待时间过去之前,不会进行训练。

更改奖励等待时间

创建新的 Node.js 应用程序

在控制台窗口(例如 cmd、PowerShell 或 Bash)中,为应用创建一个新目录并导航到该目录。

mkdir myapp && cd myapp

运行 npm init -y 命令以创建 package.json 文件。

npm init -y

在首选编辑器或名为 sample.js 的 IDE 中创建新的 Node.js 应用程序,并创建资源的终结点和订阅密钥的变量。

重要

转到 Azure 门户。 如果在“先决条件”部分中创建的个性化体验创建服务资源已成功部署,请单击“后续步骤”下的“转到资源”按钮 。 在资源的“密钥和终结点”页的“资源管理”下可以找到密钥和终结点 。

完成后,请记住将密钥从代码中删除,并且永远不要公开发布该密钥。 对于生产环境,请考虑使用安全的方法来存储和访问凭据。 例如,Azure 密钥保管库

const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
const readline = require('readline-sync');
// The endpoint specific to your personalization service instance; 
// e.g. https://<your-resource-name>.cognitiveservices.azure.com
const PersonalizationBaseUrl = '<REPLACE-WITH-YOUR-PERSONALIZER-ENDPOINT>';
// The key specific to your personalization service instance; e.g. "0123456789abcdef0123456789ABCDEF"
const ResourceKey = '<REPLACE-WITH-YOUR-PERSONALIZER-KEY>';

安装用于快速入门的 NPM 包

npm install readline-sync uuid axios --save

对象模型

若要请求每个槽的单个最佳内容项,请创建一个 rankRequest,然后将 post 请求发送到 multislot/rank。 然后,响应将会分析为 rankResponse。

若要将奖励分数发送到个性化体验创建服务,请创建一个 rewards,并向 multislot/events/{eventId}/reward 发送 post 请求。

在本快速入门中,可以很容易地确定奖励评分。 在生产系统中,确定哪些因素会影响奖励评分以及影响程度可能是一个复杂的过程,你的判断可能会随时改变。 此设计决策应是个性化体验创建服务体系结构中的主要决策之一。

代码示例

这些代码片段演示了如何通过发送适用于 NodeJS 的 HTTP 请求执行以下任务:

创建基 URL

在本节中,你将使用基 URL 和请求头(使用资源密钥)构建排名和奖励 URL。

const MultiSlotRankUrl = PersonalizationBaseUrl.concat('personalizer/v1.1-preview.1/multislot/rank');
const MultiSlotRewardUrlBase = PersonalizationBaseUrl.concat('personalizer/v1.1-preview.1/multislot/events/');
const Headers = {
    'ocp-apim-subscription-key': ResourceKey,
    'Content-Type': 'application/json'
};

获取以操作形式表示的内容选项

操作表示你希望个性化体验创建服务从中选择最佳内容项的内容选择。 将以下方法添加到脚本,以表示操作及其特征的集合。

function getActions() {
    return [
        {
            'id': 'Red-Polo-Shirt-432',
            'features': [
                {
                    'onSale': 'true',
                    'price': 20,
                    'category': 'Clothing'
                }
            ]
        },
        {
            'id': 'Tennis-Racket-133',
            'features': [
                {
                    'onSale': 'false',
                    'price': 70,
                    'category': 'Sports'
                }
            ]
        },
        {
            'id': '31-Inch-Monitor-771',
            'features': [
                {
                    'onSale': 'true',
                    'price': 200,
                    'category': 'Electronics'
                }
            ]
        },
        {
            'id': 'XBox-Series X-117',
            'features': [
                {
                    'onSale': 'false',
                    'price': 499,
                    'category': 'Electronics'
                }
            ]
        }
    ];
}

获取上下文的用户首选项

将以下方法添加到脚本,以从命令行获取用户的日期时间输入及用户所在设备的类型。 它们将用作上下文特征。

function getContextFeatures() {
    const timeOfDayFeatures = ['morning', 'afternoon', 'evening', 'night'];
    const deviceFeatures = ['mobile', 'tablet', 'desktop'];

    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 device is the user on (enter number)? 1. mobile 2. tablet 3. desktop\n');
    selection = parseInt(answer);
    const device = selection >= 1 && selection <= 3 ? deviceFeatures[selection - 1] : deviceFeatures[0];

    console.log('Selected features:\n');
    console.log('Time of day: ' + timeOfDay + '\n');
    console.log('Device: ' + device + '\n');

    return [
        {
            'time': timeOfDay
        },
        {
            'device': device
        }
    ];
}

获取槽

槽构成了用户将与之交互的页面。 个性化体验创建服务将决定要在定义的每个槽中显示哪个操作。 可以从特定槽中排除操作(显示为 ExcludeActions)。 BaselineAction 是在不使用个性化体验创建服务的情况下会显示的槽的默认操作。

本快速入门提供了简单的槽功能。 在生产系统中,确定和评估特征可能是一件非常重要的事情。

function getSlots() {
    return [
        {
            'id': 'BigHeroPosition',
            'features': [
                {
                    'size': 'large',
                    'position': 'left',
                }
            ],
            'excludedActions': ['31-Inch-Monitor-771'],
            'baselineAction': 'Red-Polo-Shirt-432'
        },
        {
            'id': 'SmallSidebar',
            'features': [
                {
                    'size': 'small',
                    'position': 'right',
                }
            ],
            'excludedActions': ['Tennis-Racket-133'],
            'baselineAction': 'XBox-Series X-117'
        }
    ];
}

发出 HTTP 请求

添加这些功能,以向个性化体验创建服务终结点发送 post 请求,以进行多槽排名和奖励调用。

async function sendMultiSlotRank(rankRequest) {
    try {
        let response = await axios.post(MultiSlotRankUrl, rankRequest, { headers: Headers })
        return response.data;
    }
    catch (err) {
        if(err.response)
        {
            throw err.response.data
        }
        console.log(err)
        throw err;
    }
}
async function sendMultiSlotReward(rewardRequest, eventId) {
    try {
        let rewardUrl = MultiSlotRewardUrlBase.concat(eventId, '/reward');
        let response = await axios.post(rewardUrl, rewardRequest, { headers: Headers })
    }
    catch (err) {
        console.log(err);
        throw err;
    }
}

获取个性化体验创建服务决策的反馈

将以下方法添加到脚本。 你将通过命令行提示来指示个性化体验创建服务是否为每个槽做出了正确的决策。

function getRewardForSlot() {
    let answer = readline.question('\nIs this correct? (y/n)\n').toUpperCase();
    if (answer === 'Y') {
        console.log('\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.\n');
        return 1;
    }
    else if (answer === 'N') {
        console.log('\nYou didn\'t like the recommended item.The application will send Personalizer a reward of 0 for this choice of action for this slot.\n');
        return 0;
    }
    console.log('\nEntered choice is invalid. Service assumes that you didn\'t like the recommended item.\n');
    return 0;
}

创建学习循环

个性化体验创建服务学习循环是一个排名奖励调用周期。 在本快速入门中,用于个性化内容的每个排名调用都后接一个奖励调用,该奖励调用让个性化体验创建服务知道该服务的表现如何。

以下代码会循环调用这样一个循环:通过命令行询问用户的首选项,将该信息发送给个性化体验创建服务来为每个槽选择最佳操作,向客户显示所选项以让他们从列表中进行选择,然后向个性化体验创建服务发送奖励评分,指出服务在做选择时的表现如何。

let runLoop = true;

(async () => {
    do {

        let multiSlotRankRequest = {};

        // Generate an ID to associate with the request.
        multiSlotRankRequest.eventId = uuidv4();

        // Get context information from the user.
        multiSlotRankRequest.contextFeatures = getContextFeatures();

        // Get the actions list to choose from personalization with their features.
        multiSlotRankRequest.actions = getActions();

        // Get the list of slots for which Personalizer will pick the best action.
        multiSlotRankRequest.slots = getSlots();

        multiSlotRankRequest.deferActivation = false;

        try {
            //Rank the actions for each slot
            let multiSlotRankResponse = await sendMultiSlotRank(multiSlotRankRequest);
            let multiSlotrewards = {};
            multiSlotrewards.reward = [];
    
            for (let i = 0; i < multiSlotRankResponse.slots.length; i++) {
                console.log('\nPersonalizer service decided you should display: '.concat(multiSlotRankResponse.slots[i].rewardActionId, ' in slot ', multiSlotRankResponse.slots[i].id, '\n'));
    
                let slotReward = {};
                slotReward.slotId = multiSlotRankResponse.slots[i].id;
                // User agrees or disagrees with Personalizer decision for slot
                slotReward.value = getRewardForSlot();
                multiSlotrewards.reward.push(slotReward);
            }
    
            // Send the rewards for the event
            await sendMultiSlotReward(multiSlotrewards, multiSlotRankResponse.eventId);
    
            let answer = readline.question('\nPress q to break, any other key to continue:\n').toUpperCase();
            if (answer === 'Q') {
                runLoop = false;
            }
        }
        catch (err) {
            console.log(err);
            throw err;
        }



    } while (runLoop);
})()

在接下来的部分中,让我们仔细看看排名和奖励调用。

添加以下方法,这些方法在运行代码文件之前获取内容选项获取上下文的用户首选项获取槽发出 HTTP 请求获取每个槽的奖励

  • getActions
  • getContextFeatures
  • getSlots
  • sendRank
  • sendReward
  • getRewardForSlot

请求最佳操作

为了完成排名请求,程序会询问用户的首选项以创建内容选项。 请求正文包含上下文、操作和槽及其各自的功能。 sendMultiSlotRank 方法采用 rankRequest,并执行多槽排名请求。

本快速入门使用简单的日期时间和用户设备上下文特征。 在生产系统中,确定和评估操作与特征可能是一件非常重要的事情。

let multiSlotRankRequest = {};

// Generate an ID to associate with the request.
multiSlotRankRequest.eventId = uuidv4();

// Get context information from the user.
multiSlotRankRequest.contextFeatures = getContextFeatures();

// Get the actions list to choose from personalization with their features.
multiSlotRankRequest.actions = getActions();

// Get the list of slots for which Personalizer will pick the best action.
multiSlotRankRequest.slots = getSlots();

multiSlotRankRequest.deferActivation = false;

//Rank the actions for each slot
try {
    let multiSlotRankResponse = await sendMultiSlotRank(multiSlotRankRequest);
}
catch (err) {
    console.log(err);
    throw err;
}

发送奖励

为了获取用于奖励请求的奖励评分,程序会通过命令行获取用户针对每个槽所做的选择,为该选择分配一个数值(奖励评分),然后将每个槽的唯一事件 ID、槽 ID 和奖励评分发送到 sendMultiSlotReward 方法。 无需为每个槽定义奖励。

本快速入门分配一个简单的数字(0 或 1)作为奖励评分。 在生产系统中,确定何时向奖励调用发送哪种内容可能不是一个简单的过程,这取决于具体的需求。

let multiSlotrewards = {};
multiSlotrewards.reward = [];

for (i = 0; i < multiSlotRankResponse.slots.length; i++) {
    console.log('\nPersonalizer service decided you should display: '.concat(multiSlotRankResponse.slots[i].rewardActionId, ' in slot ', multiSlotRankResponse.slots[i].id, '\n'));

    let slotReward = {};
    slotReward.slotId = multiSlotRankResponse.slots[i].id;
    // User agrees or disagrees with Personalizer decision for slot
    slotReward.value = getRewardForSlot();
    multiSlotrewards.reward.push(slotReward);
}

// Send the rewards for the event
await sendMultiSlotReward(multiSlotrewards, multiSlotRankResponse.eventId);

运行程序

从应用程序目录使用 Node.js 运行应用程序。

node sample.js

快速入门程序会提出一些问题来收集用户的偏好(称为“特征”),然后提供排名最高的操作。

其中还有本快速入门的源代码

多槽概念 | 示例

先决条件

  • Azure 订阅 - 免费创建订阅
  • Python 3.x
  • 拥有 Azure 订阅后,在 Azure 门户中创建个性化体验创建服务资源,获取密钥和终结点。 部署后,选择”转到资源”。
    • 需要从创建的资源获取密钥和终结点,以便将应用程序连接到个性化体验创建服务 API。 稍后在快速入门中将密钥和终结点粘贴到下方的代码中。
    • 可以使用免费定价层 (F0) 试用该服务,然后再升级到付费层进行生产。

设置

将个性化体验创建服务实例升级到多槽

注意

多槽个性化设置(预览版)会影响个性化体验创建服务的其他功能。 无法撤消此更改。 在启用多槽个性化设置之前,请参阅多槽个性化设置(预览版)

  1. 如要在 Azure 门户中禁用自动优化,请在个性化体验创建服务资源中,在“资源管理”下的“模型和学习设置”页上,将“自动优化”关闭并保存。

注意

只有禁用自动优化,多槽个性化设置才有效。 未来将支持多槽个性化设置的自动优化。

  1. 如要在 Azure 门户中将个性化体验创建服务升级到多槽,请在个性化体验创建服务资源中,在“资源管理”下的“模型和学习设置”页上,选择“导出学习设置”。 下载的 json 文件中的“arguments”字段将以“--cb_explore_adf”开头。 将此项更改为 --ccb_explore_adf 并保存文件。 CB(上下文 Bandits)和 CCB(条件上下文 Bandits)是个性化体验创建服务分别用于单槽和多槽个性化设置的算法。 ADF(操作依赖功能)意味着操作由功能来表示/识别。

更改前的学习设置

更改后的学习设置

在门户的同一选项卡的“导入学习设置”下浏览,以查找最近修改的 json 文件并将其上传。 这会将个性化体验创建服务实例更新为“多槽”个性化体验创建服务,现在将支持多槽排名和奖励调用。

更改模型更新频率

在 Azure 门户中,转到个性化体验创建服务资源的“配置”页,并将“模型更新频率”更改为 30 秒。 此短暂持续时间可快速训练模型,使你可以看到建议的操作如何针对每次迭代而变化。

更改模型更新频率

更改奖励等待时间

在 Azure 门户中,转到个性化体验创建服务资源的“配置”页,并将“奖励等待时间”更改为 10 分钟。 这决定了在发送建议后模型将等待多长时间来接收来自该建议的奖励反馈。 在奖励等待时间过去之前,不会进行训练。

更改奖励等待时间

创建新的 Python 应用程序

创建一个新的 Python 文件,为资源的终结点和订阅密钥创建变量。

重要

转到 Azure 门户。 如果在“先决条件”部分中创建的个性化体验创建服务资源已成功部署,请单击“后续步骤”下的“转到资源”按钮 。 在资源的“密钥和终结点”页的“资源管理”下可以找到密钥和终结点 。

完成后,请记住将密钥从代码中删除,并且永远不要公开发布该密钥。 对于生产环境,请考虑使用安全的方法来存储和访问凭据。 例如,Azure 密钥保管库

import json, uuid, requests

# The endpoint specific to your personalization service instance; 
# e.g. https://<your-resource-name>.cognitiveservices.azure.com
PERSONALIZATION_BASE_URL = "<REPLACE-WITH-YOUR-PERSONALIZER-ENDPOINT>"
# The key specific to your personalization service instance; e.g. "0123456789abcdef0123456789ABCDEF"
RESOURCE_KEY = "<REPLACE-WITH-YOUR-PERSONALIZER-KEY>"

对象模型

若要请求每个槽的单个最佳内容项,请创建一个 rank_request,然后将 post 请求发送到 multislot/rank。 然后响应会分析为 rank_response。

若要将奖励分数发送到个性化体验创建服务,请创建一个 rewards,并向 multislot/events/{eventId}/reward 发送 post 请求。

在本快速入门中,你可以很容易地确定奖励评分。 在生产系统中,确定哪些因素在多大程度上会影响奖励评分可能是一个复杂的过程,因为在此过程中你的判断可能会随着时间而改变。 此设计决策应是个性化体验创建服务体系结构中的主要决策之一。

代码示例

这些代码片段演示了如何通过发送适用于 Python 的 HTTP 请求执行以下任务:

创建基 URL

在本节中,你将使用基 URL 和请求头(使用资源密钥)构建排名和奖励 URL。

MULTI_SLOT_RANK_URL = '{0}personalizer/v1.1-preview.1/multislot/rank'.format(PERSONALIZATION_BASE_URL)
MULTI_SLOT_REWARD_URL_BASE = '{0}personalizer/v1.1-preview.1/multislot/events/'.format(PERSONALIZATION_BASE_URL)
HEADERS = {
    'ocp-apim-subscription-key': RESOURCE_KEY,
    'Content-Type': 'application/json'
}

获取以操作形式表示的内容选项

操作表示你希望个性化体验创建服务从中选择最佳内容项的内容选择。 将以下方法添加到脚本,以表示操作及其特征的集合。

def get_actions():
    return [
        {
            "id": "Red-Polo-Shirt-432",
            "features": [
                {
                    "onSale": "true",
                    "price": 20,
                    "category": "Clothing"
                }
            ]
        },
        {
            "id": "Tennis-Racket-133",
            "features": [
                {
                    "onSale": "false",
                    "price": 70,
                    "category": "Sports"
                }
            ]
        },
        {
            "id": "31-Inch-Monitor-771",
            "features": [
                {
                    "onSale": "true",
                    "price": 200,
                    "category": "Electronics"
                }
            ]
        },
        {
            "id": "XBox-Series X-117",
            "features": [
                {
                    "onSale": "false",
                    "price": 499,
                    "category": "Electronics"
                }
            ]
        }
    ]

获取上下文的用户首选项

将以下方法添加到脚本,以从命令行获取用户的日期时间输入及用户所在设备的类型。 它们将用作上下文特征。

def get_context_features():
    time_features = ["morning", "afternoon", "evening", "night"]
    time_pref = input("What time of day is it (enter number)? 1. morning 2. afternoon 3. evening 4. night\n")
    try:
        parsed_time = int(time_pref)
        if(parsed_time <=0 or parsed_time > len(time_features)):
            raise IndexError
        time_of_day = time_features[parsed_time-1]
    except (ValueError, IndexError):
        print("Entered value is invalid. Setting feature value to", time_features[0] + ".")
        time_of_day = time_features[0]

    device_features = ['mobile', 'tablet', 'desktop']
    device_pref = input("What type of device is the user on (enter number)? 1. mobile 2. tablet 3. desktop\n")
    try:
        parsed_device = int(device_pref)
        if(parsed_device <=0 or parsed_device > len(device_features)):
            raise IndexError
        device = device_features[parsed_device-1]
    except (ValueError, IndexError):
        print("Entered value is invalid. Setting feature value to", device_features[0]+ ".")
        device = device_features[0]

    return [
        {'time': time_of_day},
        {'device': device}
        ]

获取槽

槽构成了用户将与之交互的页面。 个性化体验创建服务将决定要在定义的每个槽中显示哪个操作。 可以从特定槽中排除操作(显示为 ExcludeActions)。 BaselineAction 是在不使用个性化体验创建服务的情况下会显示的槽的默认操作。

本快速入门提供了简单的槽功能。 在生产系统中,确定和评估特征可能是一件非常重要的事情。

def get_slots():
    return [
        {
            "id": "BigHeroPosition",
            "features": [
                {
                    "size": "large",
                    "position": "left",
                }
            ],
            "excludedActions": ["31-Inch-Monitor-771"],
            "baselineAction": "Red-Polo-Shirt-432"
        },
        {
            "id": "SmallSidebar",
            "features": [
                {
                    "size": "small",
                    "position": "right",
                }
            ],
            "excludedActions": ["Tennis-Racket-133"],
            "baselineAction": "XBox-Series X-117"
        }
    ]

发出 HTTP 请求

添加这些功能,以向个性化体验创建服务终结点发送 post 请求,以进行多槽排名和奖励调用。

def send_multi_slot_rank(rank_request):
multi_slot_response = requests.post(MULTI_SLOT_RANK_URL, data=json.dumps(rank_request), headers=HEADERS)
if multi_slot_response.status_code != 201:
    raise Exception(multi_slot_response.text)
return json.loads(multi_slot_response.text)
def send_multi_slot_reward(reward_request, event_id):
    reward_url = '{0}{1}/reward'.format(MULTI_SLOT_REWARD_URL_BASE, event_id)
    requests.post(reward_url, data=json.dumps(reward_request), headers=HEADERS)

获取个性化体验创建服务决策的反馈

将以下方法添加到脚本。 你将通过命令行提示来指示个性化体验创建服务是否为每个槽做出了正确的决策。

def get_reward_for_slot():
    answer = input('\nIs this correct? (y/n)\n').upper()
    if (answer == 'Y'):
        print('\nGreat! The application will send Personalizer a reward of 1 so it learns from this choice of action for this slot.\n')
        return 1
    elif (answer == 'N'):
        print('\nYou didn\'t like the recommended item.The application will send Personalizer a reward of 0 for this choice of action for this slot.\n')
        return 0
    print('\nEntered choice is invalid. Service assumes that you didn\'t like the recommended item.\n')
    return 0

创建学习循环

个性化体验创建服务学习循环是一个排名奖励调用周期。 在本快速入门中,用于个性化内容的每个排名调用都后接一个奖励调用,该奖励调用让个性化体验创建服务知道该服务的表现如何。

以下代码会循环调用这样一个循环:通过命令行询问用户的首选项,将该信息发送给个性化体验创建服务来为每个槽选择最佳操作,向客户显示所选项以让他们从列表中进行选择,然后向个性化体验创建服务发送奖励评分,指出服务在做选择时的表现如何。

run_loop = True

while run_loop:

    eventId = str(uuid.uuid4())
    context = get_context_features()
    actions = get_actions()
    slots = get_slots()

    rank_request = {
        "eventId": eventId,
        "contextFeatures": context,
        "actions": actions,
        "slots": slots,
        "deferActivation": False
      }

    #Rank the actions for each slot
    multi_slot_rank_response = send_multi_slot_rank(rank_request)
    multi_slot_rewards = {"reward": []}

    for i in range(len(multi_slot_rank_response['slots'])):
        print('\nPersonalizer service decided you should display: {0} in slot {1}\n'.format(multi_slot_rank_response['slots'][i]['rewardActionId'], multi_slot_rank_response['slots'][i]['id']))

        slot_reward = {'slotId': multi_slot_rank_response['slots'][i]['id']}
        # User agrees or disagrees with Personalizer decision for slot
        slot_reward['value'] = get_reward_for_slot()
        multi_slot_rewards['reward'].append(slot_reward)

    # Send the rewards for the event
    send_multi_slot_reward(multi_slot_rewards, multi_slot_rank_response['eventId'])

    answer = input('\nPress q to break, any other key to continue:\n').upper()
    if (answer == 'Q'):
        run_loop = False

在接下来的部分中,让我们仔细看看排名和奖励调用。

添加以下方法,这些方法在运行代码文件之前获取内容选项获取上下文的用户首选项获取槽发出 HTTP 请求获取每个槽的奖励

  • get_actions
  • get_context_features
  • get_slots
  • send_rank
  • send_reward
  • get_reward_for_dsot

请求最佳操作

为了完成排名请求,程序会询问用户的首选项以创建内容选项。 请求正文包含上下文、操作和槽及其各自的功能。 send_multi_slot_rank 方法采用 rankRequest,并执行多槽排名请求。

本快速入门使用简单的日期时间和用户设备上下文特征。 在生产系统中,确定和评估操作与特征可能是一件非常重要的事情。

eventId = str(uuid.uuid4())
context = get_context_features()
actions = get_actions()
slots = get_slots()

rank_request = {
    "eventId": eventId,
    "contextFeatures": context,
    "actions": actions,
    "slots": slots,
    "deferActivation": False
    }

#Rank the actions for each slot
multi_slot_rank_response = send_multi_slot_rank(rank_request)

发送奖励

为了获取用于奖励请求的奖励评分,程序会通过命令行获取用户针对每个槽所做的选择,为该选择分配一个数值(奖励评分),然后将每个槽的唯一事件 ID、槽 ID 和奖励评分发送到 send_multi_slot_reward 方法。 无需为每个槽定义奖励。

本快速入门分配一个简单的数字(0 或 1)作为奖励评分。 在生产系统中,确定何时向奖励调用发送哪种内容可能不是一个简单的过程,这取决于具体的需求。

multi_slot_rewards = {"reward": []}

for i in range(len(multi_slot_rank_response['slots'])):
    print('\nPersonalizer service decided you should display: {0} in slot {1}\n'.format(multi_slot_rank_response['slots'][i]['rewardActionId'], multi_slot_rank_response['slots'][i]['id']))

    slot_reward = {'slotId': multi_slot_rank_response['slots'][i]['id']}
    # User agrees or disagrees with Personalizer decision for slot
    slot_reward['value'] = get_reward_for_slot()
    multi_slot_rewards['reward'].append(slot_reward)

# Send the rewards for the event
send_multi_slot_reward(multi_slot_rewards, multi_slot_rank_response['eventId'])

运行程序

从应用程序目录使用 python 运行应用程序。

python sample.py

快速入门程序会提出一些问题来收集用户的偏好(称为“特征”),然后提供排名最高的操作。

其中还有本快速入门的源代码

后续步骤