자습서: .NET 웹앱에 Personalizer 추가

Important

2023년 9월 20일부터 새 Personalizer 리소스를 만들 수 없습니다. Personalizer 서비스는 2026년 10월 1일에 사용 중지됩니다.

Personalizer 루프를 사용하여 C# .NET 웹앱을 사용자 지정하여 작업(기능 포함) 및 컨텍스트 기능을 기반으로 사용자에게 올바른 콘텐츠를 제공합니다.

이 자습서에서는 다음 방법을 알아봅니다.

  • Personalizer 키 및 엔드포인트 설정
  • 기능 수집
  • 순위 및 보상 API 호출
  • rewardActionId로 지정된 상위 작업 표시

웹앱에 가장 적합한 콘텐츠 선택

웹 페이지에 표시할 단일 상위 항목(rewardActionId)으로 개인 설정해야 하는 작업 목록(일부 형식의 콘텐츠)이 있는 경우 웹앱은 Personalizer를 사용해야 합니다. 작업 목록의 예로는 뉴스 기사, 단추 배치 위치 및 제품 이름에 대한 단어 선택 항목이 있습니다.

컨텍스트 기능과 함께 작업 목록을 Personalizer 루프로 보냅니다. Personalizer는 최상의 단일 작업을 선택한 다음 웹앱에 해당 작업을 표시합니다.

이 자습서에서 작업은 다음과 같은 음식 유형입니다.

  • 파스타
  • 아이스크림
  • 주스
  • 샐러드
  • 팝콘
  • 커피
  • 스프

Personalizer가 작업에 대해 학습할 수 있도록 각 순위 API 요청을 사용하여 기능컨텍스트 기능이 있는 작업을 모두 보냅니다.

모델의 기능은 웹앱 사용자 기반의 멤버 간에 집계(그룹화)할 수 있는 작업 또는 컨텍스트에 대한 정보입니다. 기능은 개별적으로 특정(예: 사용자 ID) 또는 매우 구체적이지 않습니다 (예: 정확한 하루 중 시간).

기능이 포함된 작업

각 작업(콘텐츠 항목)에는 음식 항목을 구분하는 데 도움이 되는 기능이 있습니다.

기능은 Azure Portal에서 루프 구성의 일부로 구성되지 않습니다. 대신 각 Rank API 호출을 통해 JSON 개체로 전송됩니다. 이를 통해 작업 및 해당 기능이 시간이 지남에 따라 증가, 변경 및 축소할 수 있으므로 Personalizer가 추세를 따를 수 있습니다.

 /// <summary>
  /// Creates personalizer actions feature list.
  /// </summary>
  /// <returns>List of actions for personalizer.</returns>
  private IList<RankableAction> GetActions()
  {
      IList<RankableAction> actions = new List<RankableAction>
      {
          new RankableAction
          {
              Id = "pasta",
              Features =
              new List<object>() { new { taste = "savory", spiceLevel = "medium" }, new { nutritionLevel = 5, cuisine = "italian" } }
          },

          new RankableAction
          {
              Id = "ice cream",
              Features =
              new List<object>() { new { taste = "sweet", spiceLevel = "none" }, new { nutritionalLevel = 2 } }
          },

          new RankableAction
          {
              Id = "juice",
              Features =
              new List<object>() { new { taste = "sweet", spiceLevel = "none" }, new { nutritionLevel = 5 }, new { drink = true } }
          },

          new RankableAction
          {
              Id = "salad",
              Features =
              new List<object>() { new { taste = "sour", spiceLevel = "low" }, new { nutritionLevel = 8 } }
          },

          new RankableAction
          {
              Id = "popcorn",
              Features =
              new List<object>() { new { taste = "salty", spiceLevel = "none" }, new { nutritionLevel = 3 } }
          },

          new RankableAction
          {
              Id = "coffee",
              Features =
              new List<object>() { new { taste = "bitter", spiceLevel = "none" }, new { nutritionLevel = 3 }, new { drink = true } }
          },

          new RankableAction
          {
              Id = "soup",
              Features =
              new List<object>() { new { taste = "sour", spiceLevel = "high" }, new { nutritionLevel =  7} }
          }
      };

      return actions;
  }

컨텍스트 기능

컨텍스트 기능은 Personalizer가 작업의 컨텍스트를 이해하는 데 도움이 됩니다. 이 샘플 애플리케이션의 컨텍스트에는 다음이 포함됩니다.

  • 하루 중 시간 - 아침, 오후, 저녁, 밤
  • 맛에 대한 사용자의 선호 - 짠 맛, 달콤한, 쓴 맛, 신맛, 또는 짠 맛
  • 브라우저의 컨텍스트 - 사용자 에이전트, 지리적 위치, 참조자
/// <summary>
/// Get users time of the day context.
/// </summary>
/// <returns>Time of day feature selected by the user.</returns>
private string GetUsersTimeOfDay()
{
    Random rnd = new Random();
    string[] timeOfDayFeatures = new string[] { "morning", "noon", "afternoon", "evening", "night", "midnight" };
    int timeIndex = rnd.Next(timeOfDayFeatures.Length);
    return timeOfDayFeatures[timeIndex];
}

/// <summary>
/// Gets user food preference.
/// </summary>
/// <returns>Food taste feature selected by the user.</returns>
private string GetUsersTastePreference()
{
    Random rnd = new Random();
    string[] tasteFeatures = new string[] { "salty", "bitter", "sour", "savory", "sweet" };
    int tasteIndex = rnd.Next(tasteFeatures.Length);
    return tasteFeatures[tasteIndex];
}

웹앱에서 Personalizer를 어떻게 사용할까요?

웹앱은 Personalizer를 사용하여 음식 선택 목록에서 최상의 작업을 선택합니다. 각 순위 API 호출과 함께 다음 정보를 전송하여 이 작업을 수행합니다.

  • 다음과 같은 taste 기능을 가진 작업spiceLevel
  • 날짜, 사용자 기본 설정, 브라우저의 taste 사용자 에이전트 정보 및 컨텍스트 기능과 같은 time 컨텍스트 기능
  • 주스와 같이 제외 할 작업
  • eventId는 순위 API 호출마다 다릅니다.

웹앱의 Personalizer 모델 기능

Personalizer에는 작업(콘텐츠) 및 현재 컨텍스트(사용자 및 환경)에 대한 기능이 필요합니다. 기능은 모델의 현재 컨텍스트에 작업을 맞추는 데 사용됩니다. 이 모델은 학습된 의사 결정을 내릴 수 있는 작업, 컨텍스트 및 기능에 대한 Personalizer의 과거 지식을 표현한 것입니다.

기능을 포함한 모델은 Azure Portal의 모델 업데이트 빈도 설정에 따라 일정에 따라 업데이트됩니다.

주의

이 애플리케이션의 기능은 기능 및 기능 값을 설명하기 위한 것이지만 웹앱에서 사용할 수 있는 최상의 기능은 아닙니다.

기능 및 해당 값 계획

기술 아키텍처의 모든 스키마 또는 모델에 적용할 계획 및 디자인과 동일한 방식으로 기능을 선택해야 합니다. 기능 값은 비즈니스 논리 또는 타사 시스템으로 설정할 수 있습니다. 기능 값은 매우 구체적이어서 기능 그룹 또는 기능 클래스에 적용되지 않아야 합니다.

기능 값 일반화

범주로 일반화

이 앱은 기능으로 사용 time 하지만 시간을 그룹화하여 , afternoon, eveningnight.morning 이는 시간 정보를 사용하는 예이지만, 10:05:01 UTC+2와 같은 매우 구체적인 방법이 아닙니다.

파트로 일반화

이 앱은 브라우저의 HTTP 요청 기능을 사용합니다. 이 작업은 모든 데이터가 포함된 매우 구체적인 문자열로 시작됩니다. 예를 들면 다음과 같습니다.

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/530.99 (KHTML, like Gecko) Chrome/80.0.3900.140 Safari/537.36

HttpRequestFeatures 클래스 라이브러리는 이 문자열을 개별 값이 있는 userAgentInfo 개체로 일반화합니다. 너무 구체적인 값은 빈 문자열로 설정됩니다. 요청에 대한 컨텍스트 기능이 전송되면 다음과 같은 JSON 형식이 적용됩니다.

{
  "httpRequestFeatures": {
    "_synthetic": false,
    "OUserAgent": {
      "_ua": "",
      "_DeviceBrand": "",
      "_DeviceFamily": "Other",
      "_DeviceIsSpider": false,
      "_DeviceModel": "",
      "_OSFamily": "Windows",
      "_OSMajor": "10",
      "DeviceType": "Desktop"
    }
  }
}

샘플 웹앱 사용

샘플 브라우저 기반 웹앱(모든 코드가 제공됨)에는 앱을 실행하기 위해 다음 애플리케이션이 설치되어 있어야 합니다.

다음 소프트웨어를 설치합니다.

  • .NET Core 2.1 - 샘플 백 엔드 서버에서 .NET Core를 사용합니다.
  • Node.js - 클라이언트/프런트 엔드에서 이 애플리케이션을 사용합니다.
  • Visual Studio 2019 또는 .NET Core CLI - Visual Studio 2019의 개발자 환경 또는 .NET Core CLI를 사용하여 앱을 빌드하고 실행합니다.

샘플 설정

  1. Azure AI Personalizer 샘플 리포지토리를 복제합니다.

    git clone https://github.com/Azure-Samples/cognitive-services-personalizer-samples.git
    
  2. samples/HttpRequestFeatures로 이동하여 HttpRequestFeaturesExample.sln 솔루션을 엽니다.

    요청된 경우 Visual Studio에서 Personalizer용 .NET 패키지를 업데이트하도록 허용합니다.

Azure AI Personalizer 서비스 설정

  1. Azure Portal에서 Personalizer 리소스 를 만듭니다.

  2. Azure Portal의 키 및 엔드포인트 탭에서 EndpointKey1 또는 Key2(둘 중 하나가 작동함)를 찾습니다. 이러한 항목은 PersonalizerServiceEndpointPersonalizerApiKey입니다.

  3. appsettings.json에서 PersonalizerServiceEndpoint를 입력합니다.

  4. 다음 방법 중 하나를 사용하여 PersonalizerApiKey앱 비밀로 구성합니다.

    • .NET Core CLI를 사용하는 경우 이 명령을 사용할 dotnet user-secrets set "PersonalizerApiKey" "<API Key>" 수 있습니다.
    • Visual Studio를 사용하는 경우 프로젝트를 마우스 오른쪽 단추로 클릭하고 사용자 비밀 관리 메뉴 옵션을 선택하여 Personalizer 키를 구성할 수 있습니다. 이렇게 하면 Visual Studio에서 다음과 같이 키를 추가할 수 있는 secrets.json 파일이 열립니다.
    {
      "PersonalizerApiKey": "<your personalizer key here>",
    }
    

샘플 실행

다음 방법 중 하나를 사용하여 HttpRequestFeaturesExample을 빌드하고 실행합니다.

  • Visual Studio 2019: F5 키를 누릅니 다.
  • .NET Core CLI: dotnet builddotnet run

웹 브라우저를 통해 순위 요청 및 보상 요청을 보내고 해당 응답과 사용자 환경에서 추출된 http 요청 기능을 볼 수 있습니다.

Screenshot shows an example of the Http Request Feature in a web browser.

Personalizer 루프 시연

  1. 새 순위 요청 생성 단추를 선택하여 순위 API 호출에 대한 새 JSON 개체를 만듭니다. 이렇게 하면 작업(기능 포함) 및 컨텍스트 기능이 생성되고 JSON의 모양을 확인할 수 있도록 값이 표시됩니다.

    사용자 고유의 향후 애플리케이션의 경우 클라이언트, 서버, 둘의 혼합 또는 다른 서비스에 대한 호출에서 작업 및 기능 생성이 발생할 수 있습니다.

  2. 순위 요청 보내기를 선택하여 JSON 개체를 서버로 보냅니다. 서버는 Personalizer 순위 API를 호출합니다. 서버는 응답을 수신하고 표시할 상위 순위 작업을 클라이언트에 반환합니다.

  3. 보상 값을 설정한 다음 보상 요청 보내기 단추를 선택합니다. 보상 값을 변경하지 않으면 클라이언트 애플리케이션은 항상 Personalizer에 값을 1 보냅니다.

    Screenshot shows the Reward Request section.

    사용자 고유의 향후 애플리케이션의 경우 보상 점수는 서버의 비즈니스 논리와 함께 클라이언트에서 사용자의 동작으로부터 정보를 수집한 후에 생성될 수 있습니다.

샘플 웹앱 이해

샘플 웹앱에는 기능 컬렉션을 관리하고 Personalizer 엔드포인트에 대한 HTTP 호출을 보내고 받는 C# .NET 서버가 있습니다.

샘플 웹앱은 녹아웃 프런트 엔드 클라이언트 애플리케이션을 사용하여 기능을 캡처하고 단추를 클릭하고 데이터를 .NET 서버로 보내는 것과 같은 사용자 인터페이스 작업을 처리합니다.

다음 섹션에서는 개발자가 Personalizer를 사용하기 위해 이해해야 하는 서버와 클라이언트 부분에 대해 설명합니다.

순위 API: 클라이언트 애플리케이션이 서버에 컨텍스트를 보냅니다.

클라이언트 애플리케이션에서 사용자의 브라우저 사용자 에이전트를 수집합니다.

Build and run the HTTPRequestFeaturesExample project. A browser window opens to display the single page application.

순위 API: 서버 애플리케이션이 Personalizer를 호출합니다.

클라이언트 애플리케이션을 사용하는 일반적인 .NET 웹앱이며 대부분의 보일러 플레이트 코드가 제공됩니다. Personalizer와 관련이 없는 코드는 다음 코드 조각에서 제거되므로 Personalizer 관련 코드에만 집중할 수 있습니다.

Personalizer 클라이언트 만들기

서버의 Startup.cs에서 Personalizer 엔드포인트 및 키를 사용하여 Personalizer 클라이언트를 만듭니다. 클라이언트 애플리케이션은 이 앱에서 Personalizer와 통신할 필요가 없으며 대신 서버에 의존하여 해당 SDK를 호출합니다.

웹 서버의 .NET 시작 코드는 다음과 같습니다.

using Microsoft.Azure.CognitiveServices.Personalizer;
// ... other using statements removed for brevity

namespace HttpRequestFeaturesExample
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            string personalizerApiKey = Configuration.GetSection("PersonalizerApiKey").Value;
            string personalizerEndpoint = Configuration.GetSection("PersonalizerConfiguration:ServiceEndpoint").Value;
            if (string.IsNullOrEmpty(personalizerEndpoint) || string.IsNullOrEmpty(personalizerApiKey))
            {
                throw new ArgumentException("Missing Azure AI Personalizer endpoint and/or api key.");
            }
            services.AddSingleton(client =>
            {
                return new PersonalizerClient(new ApiKeyServiceClientCredentials(personalizerApiKey))
                {
                    Endpoint = personalizerEndpoint
                };
            });

            services.AddMvc();
        }

        // ... code removed for brevity
    }
}

최상의 작업 선택

서버의 PersonalizerController.cs에서 GenerateRank 서버 API는 순위 API를 호출하기 위한 준비를 요약합니다.

  • 순위 호출에 대한 새 eventId 만들기
  • 작업 목록 가져오기
  • 사용자로부터 기능 목록 가져오기 및 컨텍스트 기능 만들기
  • 필요에 따라 제외된 작업을 설정합니다.
  • 순위 API 호출, 클라이언트에 결과 반환
/// <summary>
/// Creates a RankRequest with user time of day, HTTP request features,
/// and taste as the context and several different foods as the actions
/// </summary>
/// <returns>RankRequest with user info</returns>
[HttpGet("GenerateRank")]
public RankRequest GenerateRank()
{
    string eventId = Guid.NewGuid().ToString();

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

    // Get context information from the user.
    HttpRequestFeatures httpRequestFeatures = GetHttpRequestFeaturesFromRequest(Request);
    string timeOfDayFeature = GetUsersTimeOfDay();
    string tasteFeature = GetUsersTastePreference();

    // Create current context from user specified data.
    IList<object> currentContext = new List<object>() {
            new { time = timeOfDayFeature },
            new { taste = tasteFeature },
            new { httpRequestFeatures }
    };

    // Exclude an action for personalizer ranking. This action will be held at its current position.
    IList<string> excludeActions = new List<string> { "juice" };

    // Rank the actions
    return new RankRequest(actions, currentContext, excludeActions, eventId);
}

작업(기능 포함)과 현재 컨텍스트 기능이 모두 포함되어 Personalizer에 보내지는 JSON은 다음과 같습니다.

{
    "contextFeatures": [
        {
            "time": "morning"
        },
        {
            "taste": "savory"
        },
        {
            "httpRequestFeatures": {
                "_synthetic": false,
                "MRefer": {
                    "referer": "http://localhost:51840/"
                },
                "OUserAgent": {
                    "_ua": "",
                    "_DeviceBrand": "",
                    "_DeviceFamily": "Other",
                    "_DeviceIsSpider": false,
                    "_DeviceModel": "",
                    "_OSFamily": "Windows",
                    "_OSMajor": "10",
                    "DeviceType": "Desktop"
                }
            }
        }
    ],
    "actions": [
        {
            "id": "pasta",
            "features": [
                {
                    "taste": "savory",
                    "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": "sour",
                    "spiceLevel": "low"
                },
                {
                    "nutritionLevel": 8
                }
            ]
        },
        {
            "id": "popcorn",
            "features": [
                {
                    "taste": "salty",
                    "spiceLevel": "none"
                },
                {
                    "nutritionLevel": 3
                }
            ]
        },
        {
            "id": "coffee",
            "features": [
                {
                    "taste": "bitter",
                    "spiceLevel": "none"
                },
                {
                    "nutritionLevel": 3
                },
                {
                    "drink": true
                }
            ]
        },
        {
            "id": "soup",
            "features": [
                {
                    "taste": "sour",
                    "spiceLevel": "high"
                },
                {
                    "nutritionLevel": 7
                }
            ]
        }
    ],
    "excludedActions": [
        "juice"
    ],
    "eventId": "82ac52da-4077-4c7d-b14e-190530578e75",
    "deferActivation": null
}

Personalizer rewardActionId를 클라이언트에 반환

Rank API는 선택한 최상의 작업 rewardActionId 를 서버에 반환합니다.

rewardActionId에서 반환된 작업을 표시합니다.

{
    "ranking": [
        {
            "id": "popcorn",
            "probability": 0.833333254
        },
        {
            "id": "salad",
            "probability": 0.03333333
        },
        {
            "id": "juice",
            "probability": 0
        },
        {
            "id": "soup",
            "probability": 0.03333333
        },
        {
            "id": "coffee",
            "probability": 0.03333333
        },
        {
            "id": "pasta",
            "probability": 0.03333333
        },
        {
            "id": "ice cream",
            "probability": 0.03333333
        }
    ],
    "eventId": "82ac52da-4077-4c7d-b14e-190530578e75",
    "rewardActionId": "popcorn"
}

클라이언트가 rewardActionId 작업을 표시합니다.

이 자습서에서는 값이 rewardActionId 표시됩니다.

사용자 고유의 향후 애플리케이션에서 정확한 텍스트, 단추 또는 강조 표시된 웹 페이지의 섹션일 수 있습니다. 목록은 콘텐츠의 순서가 아니라 점수의 사후 분석에 대해 반환됩니다. rewardActionId 콘텐츠만 표시되어야 합니다.

보상 API: 보상에 대한 정보 수집

보상 점수는 기능 계획과 마찬가지로 신중하게 계획해야 합니다. 보상 점수는 일반적으로 0에서 1까지의 값이어야 합니다. 이 값은 클라이언트 애플리케이션에서 사용자 동작에 따라 부분적으로, 그리고 서버에서 비즈니스 논리와 목표에 따라 부분적으로 계산할 수 있습니다.

서버가 Personalizer 리소스에 대해 Azure Portal에 구성된 보상 대기 시간 내에 Reward API를 호출하지 않는 경우 해당 이벤트에 기본 보상(Azure Portal에서도 구성됨)이 사용됩니다.

이 애플리케이션 샘플에서 값을 선택하여 보상이 선택에 미치는 영향을 확인할 수 있습니다.

이 샘플에서 추가로 학습할 수 있는 방법

이 샘플에서는 Azure Portal에서 Personalizer 리소스에 대해 구성된 몇 가지 시간 기반 이벤트를 사용합니다. 해당 값을 사용하여 재생한 다음, 이 웹앱 샘플로 돌아가서 순위 및 보상 호출에 미치는 영향을 확인합니다.

  • 보상 대기 시간
  • 모델 업데이트 빈도

재생할 추가 설정은 다음과 같습니다.

  • 기본 보상
  • 탐색 백분율

리소스 정리

이 자습서를 완료하면 다음 리소스를 클린.

  • 샘플 프로젝트 디렉터리를 삭제합니다.
  • Personalizer 리소스를 삭제합니다. 즉, Personalizer 리소스를 작업 및 컨텍스트 전용으로 간주하여 작업 주체처럼 음식을 계속 사용하는 경우에만 리소스를 재사용합니다기본.

다음 단계

  • Personalizer 작동 방식
  • 기능: 작업 및 컨텍스트와 함께 사용하는 기능에 대한 개념 알아보기
  • 보상: 보상 계산에 대해 알아보기