빠른 시작: Personalizer 클라이언트 라이브러리

Important

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

Azure AI Personalizer 클라이언트 라이브러리를 시작하여 기본 학습 루프를 설정하세요. 학습 루프는 결정 및 피드백 시스템입니다. 애플리케이션은 서비스에서 결정 순위를 요청한 다음 최상위 선택을 사용하고 결과에서 보상 점수를 계산합니다. 보상 점수를 서비스에 반환합니다. 시간이 지남에 따라 Personalizer는 AI 알고리즘을 사용하여 지정된 상황에 대해 더 나은 결정을 내립니다. 샘플 애플리케이션을 설정하려면 다음 단계를 따릅니다.

예제 시나리오

이 빠른 시작에서 식료품 전자 소매점은 웹 사이트에서 각 고객에게 관련성 있고 개인 설정 제품을 표시하여 수익을 늘리려고 합니다. 기본 페이지에는 잠재 고객에게 준비된 식사 제품을 표시하는 "추천 제품" 섹션이 있습니다. 전자 소매업체는 구매 가능성을 최대화하기 위해 올바른 제품을 올바른 고객에게 표시하는 방법을 결정하려고 합니다.

Personalizer 서비스는 보충 학습을 사용하여 자동화되고 확장 가능하며 적응 가능한 방식으로 이 문제를 해결합니다. 작업 및 해당 기능, 컨텍스트 기능, 보상 점수를 만드는 방법을 알아봅니다. Personalizer 클라이언트 라이브러리를 사용하여 순위 및 보상 API를 호출할 것입니다.

참조 설명서 | 라이브러리 소스 코드 | 패키지(NuGet) | .NET 코드 샘플

필수 조건

  • Azure 구독 - 체험 구독 만들기
  • 최신 버전의 .NET Core
  • Azure 구독이 있으면 Azure Portal에서 Personalizer 리소스를 만들어 키와 엔드포인트를 가져옵니다. 배포 후 리소스로 이동을 선택합니다.
    • 애플리케이션을 Personalizer API에 연결하려면 만든 리소스의 키와 엔드포인트가 필요합니다. 이 빠른 시작의 뒷부분에 나오는 코드에 키와 엔드포인트를 붙여넣습니다.
    • 평가판 가격 책정 계층(F0)을 통해 서비스를 사용해보고, 나중에 프로덕션용 유료 계층으로 업그레이드할 수 있습니다.

모델 구성

모델 업데이트 빈도 변경

Azure Portal에서 Personalizer 리소스의 구성 페이지로 이동하고 모델 업데이트 빈도를 30초로 변경합니다. 이 짧은 기간은 모델을 빠르게 학습시켜 각 반복에 대해 권장 작업이 어떻게 변경되는지 확인할 수 있습니다.

Change model update frequency

보상 대기 시간 변경

Azure Portal에서 Personalizer 리소스의 구성 페이지로 이동하고 보상 대기 시간을 10분으로 변경합니다. 이는 모델이 권장 사항을 보낸 후 해당 권장 사항에서 보상 피드백을 받기 위해 대기하는 시간을 결정합니다. 보상 대기 시간이 지날 때까지 학습이 발생하지 않습니다.

Change reward wait time

새 C# 애플리케이션 만들기

선호하는 편집기 또는 IDE에서 .NET Core 애플리케이션을 새로 만듭니다.

콘솔 창(예: cmd, PowerShell 또는 Bash)에서 dotnet new 명령을 사용하여 personalizer-quickstart라는 새 콘솔 앱을 만듭니다. 이 명령은 Program.cs라는 단일 소스 파일을 사용하여 간단한 "Hello World" C# 프로젝트를 만듭니다.

dotnet new console -n personalizer-quickstart

새로 만든 앱 폴더로 디렉터리를 변경합니다. 그러면 다음을 사용하여 애플리케이션을 빌드합니다.

dotnet build

빌드 출력에 경고나 오류가 포함되지 않아야 합니다.

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

클라이언트 라이브러리 설치

애플리케이션 디렉터리 내에서 다음 명령을 사용하여 .NET용 Personalizer 클라이언트 라이브러리를 설치합니다.

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

Visual Studio IDE를 사용하는 경우 클라이언트 라이브러리는 다운로드 가능한 NuGet 패키지로 제공됩니다.

코드 블록 1: 샘플 데이터 생성

Personalizer는 실시간 데이터를 수신하고 해석하는 애플리케이션에서 실행하기 위한 것입니다. 이 빠른 시작에서는 샘플 코드를 사용하여 식료품 웹 사이트에서 가상의 고객 작업을 생성합니다. 다음 코드 블록은 GetActions, GetContextGetRewardScore의 세 가지 주요 메서드를 정의합니다.

  • GetActions는 식료품 웹 사이트에서 순위를 매겨야 하는 선택 목록을 반환합니다. 이 예에서 작업은 식사 제품입니다. 각 작업 선택에는 나중에 사용자 동작에 영향을 미칠 수 있는 세부 정보(기능)가 있습니다. 작업은 순위 API의 입력으로 사용됩니다.

  • GetContext는 시뮬레이션된 고객 방문을 반환합니다. 어떤 고객이 있는지, 방문 시간과 같은 임의 세부 정보(컨텍스트 기능)를 선택합니다. 일반적으로 컨텍스트는 애플리케이션, 시스템, 환경 또는 사용자의 현재 상태를 나타냅니다. 컨텍스트 개체는 순위 API의 입력으로 사용됩니다.

    이 빠른 시작의 컨텍스트 기능은 단순합니다. 그러나 실제 프로덕션 시스템에서는 기능을 설계하고 효과를 평가하는 것이 중요합니다. 지침은 링크된 설명서를 참조하세요.

  • GetRewardScore는 고객 상호 작용의 성공을 나타내는 0과 1 사이의 점수를 반환합니다. 간단한 논리를 사용하여 다양한 컨텍스트가 다양한 작업 선택에 어떻게 반응하는지 결정합니다. 예를 들어, 특정 사용자는 항상 채식 및 비건 제품에 대해 1.0을 제공하고 다른 제품에 대해 0.0을 제공합니다. 실제 시나리오에서 Personalizer는 순위 및 보상 API 호출에서 전송된 데이터로부터 사용자 기본 설정을 학습합니다. 예제 코드와 같이 명시적으로 정의하지는 않습니다.

    실제 프로덕션 시스템에서 보상 점수는 비즈니스 목표 및 KPI에 맞게 설계되어야 합니다. 보상 메트릭을 계산하는 방법을 결정하려면 약간의 실험이 필요할 수 있습니다.

    아래 코드에서 사용자의 기본 설정과 작업에 대한 응답은 일련의 조건문으로 하드 코딩되며, 설명 텍스트는 시연 목적으로 코드에 포함되었습니다.

  1. 키와 엔드포인트를 찾습니다.

    Important

    Azure Portal로 이동합니다. 필수 구성 요소 섹션에서 만든 Personalizer 리소스가 성공적으로 배포된 경우 다음 단계 아래에서 리소스로 이동 단추를 클릭합니다. 리소스 관리 아래에 있는 리소스의 키 및 엔드포인트 페이지에서 키 및 엔드포인트를 찾을 수 있습니다.

    완료되면 코드에서 키를 제거하고 공개적으로 게시하지 마세요. 프로덕션의 경우 자격 증명을 안전하게 저장하고 액세스하는 방법을 사용하는 것이 좋습니다. 예를 들어 Azure Key Vault입니다.

  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/ 형식입니다.

    Important

    완료되면 코드에서 키를 제거하고 공개적으로 게시하지 마세요. 프로덕션의 경우 Azure Key Vault와 같은 자격 증명을 안전하게 저장하고 액세스하는 방법을 사용합니다. 자세한 내용은 Azure AI 서비스 보안 문서를 참조하세요.

코드 블록 2: 학습 루프 반복

다음 코드 블록은 main 메서드를 정의하고 스크립트를 종료합니다. 컨텍스트(고객 포함)를 생성하고, 순위 API를 사용하여 해당 컨텍스트에서 작업 순위를 요청하고, 보상 점수를 계산하고, 보상 API를 사용하여 해당 점수를 Personalizer 서비스에 다시 전달하는 학습 루프 반복을 실행합니다. 각 단계에서 관련 정보를 콘솔에 출력합니다.

이 예에서는 "추천 제품" 섹션에 표시할 제품을 결정하기 위해 각 순위 호출이 이루어집니다. 그런 다음 보상 호출은 사용자가 추천 제품을 구매했는지 여부를 나타냅니다. 보상은 공통 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

첫 번째 반복에서 Personalizer는 아직 학습을 수행하지 않았기 때문에 임의 작업을 권장합니다. 선택적으로 더 많은 반복을 실행할 수 있습니다. 약 10분 후 서비스는 권장 사항에 개선 사항을 표시하기 시작합니다.

The quickstart program asks a couple of questions to gather user preferences, known as features, then provides the top action.

분석을 위해 많은 이벤트 생성(선택 사항)

예를 들어, 이 빠른 시작 시나리오에서 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 Portal에서 Personalizer 리소스를 만들어 키와 엔드포인트를 가져옵니다. 배포 후 리소스로 이동을 선택합니다.
    • 애플리케이션을 Personalizer API에 연결하려면 만든 리소스의 키와 엔드포인트가 필요합니다. 이 빠른 시작의 뒷부분에 나오는 코드에 키와 엔드포인트를 붙여넣습니다.
    • 평가판 가격 책정 계층(F0)을 통해 서비스를 사용해보고, 나중에 프로덕션용 유료 계층으로 업그레이드할 수 있습니다.

모델 구성

모델 업데이트 빈도 변경

Azure Portal에서 Personalizer 리소스의 구성 페이지로 이동하고 모델 업데이트 빈도를 30초로 변경합니다. 이 짧은 기간은 모델을 빠르게 학습시켜 각 반복에 대해 권장 작업이 어떻게 변경되는지 확인할 수 있습니다.

Change model update frequency

보상 대기 시간 변경

Azure Portal에서 Personalizer 리소스의 구성 페이지로 이동하고 보상 대기 시간을 10분으로 변경합니다. 이는 모델이 권장 사항을 보낸 후 해당 권장 사항에서 보상 피드백을 받기 위해 대기하는 시간을 결정합니다. 보상 대기 시간이 지날 때까지 학습이 발생하지 않습니다.

Change reward wait time

새 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용 Personalizer 클라이언트 라이브러리를 설치합니다.

npm install @azure/cognitiveservices-personalizer --save

이 빠른 시작에 대해 남아 있는 npm 패키지를 설치합니다.

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

코드 블록 1: 샘플 데이터 생성

Personalizer는 실시간 데이터를 수신하고 해석하는 애플리케이션에서 실행하기 위한 것입니다. 이 빠른 시작에서는 샘플 코드를 사용하여 식료품 웹 사이트에서 가상의 고객 작업을 생성합니다. 다음 코드 블록은 getActionsList, getContextFeaturesListgetReward의 세 가지 주요 메서드를 정의합니다.

  • getActionsList는 식료품 웹 사이트에서 순위를 매겨야 하는 선택 목록을 반환합니다. 이 예에서 작업은 식사 제품입니다. 각 작업 선택에는 나중에 사용자 동작에 영향을 미칠 수 있는 세부 정보(기능)가 있습니다. 작업은 순위 API의 입력으로 사용됩니다.

  • getContextFeaturesList는 시뮬레이션된 고객 방문을 반환합니다. 어떤 고객이 있는지, 방문 시간과 같은 임의 세부 정보(컨텍스트 기능)를 선택합니다. 일반적으로 컨텍스트는 애플리케이션, 시스템, 환경 또는 사용자의 현재 상태를 나타냅니다. 컨텍스트 개체는 순위 API의 입력으로 사용됩니다.

    이 빠른 시작의 컨텍스트 기능은 단순합니다. 그러나 실제 프로덕션 시스템에서는 기능을 설계하고 효과를 평가하는 것이 중요합니다. 지침은 링크된 설명서를 참조하세요.

  • getReward는 사용자에게 서비스 권장 사항의 성공 또는 실패 점수를 매기도록 요청합니다. 고객 상호 작용의 성공을 나타내는 0과 1 사이의 점수를 반환합니다. 실제 시나리오에서 Personalizer는 실시간 고객 상호 작용에서 사용자 기본 설정을 학습합니다.

    실제 프로덕션 시스템에서 보상 점수는 비즈니스 목표 및 KPI에 맞게 설계되어야 합니다. 보상 메트릭을 계산하는 방법을 결정하려면 약간의 실험이 필요할 수 있습니다.

텍스트 편집기 또는 IDE에서 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 메서드를 정의하고 스크립트를 종료합니다. 학습 루프 반복을 실행하여 명령줄에서 사용자에게 기본 설정을 요청하고 해당 정보를 Personalizer로 보내 최상의 작업을 선택합니다. 명령줄을 사용하여 선택하는 사용자에게 선택한 작업을 표시합니다. 그런 다음 Personalizer 서비스에 보상 점수를 보내 서비스가 얼마나 잘 선택했는지 알려 줍니다.

Personalizer 학습 루프는 순위보상 호출의 주기입니다. 이 빠른 시작에서는 각 순위 호출을 수행하여 콘텐츠를 맞춤 설정한 다음, 보상 호출을 수행하여 서비스에서 얼마나 잘 수행되었는지 Personalizer에 알려줍니다.

  1. personalizer-quickstart.js에 아래 코드를 추가합니다.

  2. 키와 엔드포인트를 찾습니다. 엔드포인트는 https://<your_resource_name>.cognitiveservices.azure.com/ 형식입니다.

    Important

    Azure Portal로 이동합니다. 필수 구성 요소 섹션에서 만든 Personalizer 리소스가 성공적으로 배포된 경우 다음 단계 아래에서 리소스로 이동 단추를 클릭합니다. 리소스 관리 아래에 있는 리소스의 키 및 엔드포인트 페이지에서 키 및 엔드포인트를 찾을 수 있습니다.

    완료되면 코드에서 키를 제거하고 공개적으로 게시하지 마세요. 프로덕션의 경우 자격 증명을 안전하게 저장하고 액세스하는 방법을 사용하는 것이 좋습니다. 예를 들어 Azure Key Vault입니다.

  3. 키와 엔드포인트를 표시된 코드에 붙여넣습니다.

    Important

    완료되면 코드에서 키를 제거하고 공개적으로 게시하지 마세요. 프로덕션의 경우 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 Portal에서 Personalizer 리소스를 만들어 키와 엔드포인트를 가져옵니다. 배포 후 리소스로 이동을 선택합니다.
    • 애플리케이션을 Personalizer API에 연결하려면 만든 리소스의 키와 엔드포인트가 필요합니다. 이 키와 엔드포인트를 아래의 빠른 시작 코드에 붙여넣을 것입니다.
    • 무료 가격 책정 계층(F0)을 사용하여 서비스를 시도한 다음, 나중에 프로덕션을 위해 유료 계층으로 업그레이드할 수 있습니다.

모델 구성

모델 업데이트 빈도 변경

Azure Portal에서 Personalizer 리소스의 구성 페이지로 이동하고 모델 업데이트 빈도를 30초로 변경합니다. 이 짧은 기간은 모델을 빠르게 학습시켜 각 반복에 대해 권장 작업이 어떻게 변경되는지 확인할 수 있습니다.

Change model update frequency

보상 대기 시간 변경

Azure Portal에서 Personalizer 리소스의 구성 페이지로 이동하고 보상 대기 시간을 10분으로 변경합니다. 이는 모델이 권장 사항을 보낸 후 해당 권장 사항에서 보상 피드백을 받기 위해 대기하는 시간을 결정합니다. 보상 대기 시간이 지날 때까지 학습이 발생하지 않습니다.

Change reward wait time

새 Python 애플리케이션 만들기

personalizer-quickstart.py라는 새 Python 파일을 만듭니다.

클라이언트 라이브러리 설치

pip를 사용하여 Personalizer 클라이언트 라이브러리를 설치합니다.

pip install azure-cognitiveservices-personalizer

코드 블록 1: 샘플 데이터 생성

Personalizer는 실시간 데이터를 수신하고 해석하는 애플리케이션에서 실행하기 위한 것입니다. 이 빠른 시작에서는 샘플 코드를 사용하여 식료품 웹 사이트에서 가상의 고객 작업을 생성합니다. 다음 코드 블록은 get_actions, get_contextget_reward_score의 세 가지 코어 함수를 정의합니다.

  • get_actions는 식료품 웹 사이트에서 순위를 매겨야 하는 선택 목록을 반환합니다. 이 예에서 작업은 식사 제품입니다. 각 작업 선택에는 나중에 사용자 동작에 영향을 미칠 수 있는 세부 정보(기능)가 있습니다. 작업은 순위 API의 입력으로 사용됩니다.

  • get_context는 시뮬레이션된 고객 방문을 반환합니다. 어떤 고객이 있는지, 방문 시간과 같은 임의 세부 정보(컨텍스트 기능)를 선택합니다. 일반적으로 컨텍스트는 애플리케이션, 시스템, 환경 또는 사용자의 현재 상태를 나타냅니다. 컨텍스트 개체는 순위 API의 입력으로 사용됩니다.

    이 빠른 시작의 컨텍스트 기능은 단순합니다. 그러나 실제 프로덕션 시스템에서는 기능을 설계하고 효과를 평가하는 것이 매우 중요합니다. 지침은 링크된 설명서를 참조하세요.

  • get_reward_score는 고객 상호 작용의 성공을 나타내는 0과 1 사이의 점수를 반환합니다. 간단한 논리를 사용하여 다양한 컨텍스트가 다양한 작업 선택에 어떻게 반응할지 결정합니다. 예를 들어, 특정 사용자는 항상 채식 및 비건 제품에 대해 1.0을 제공하고 다른 제품에 대해 0.0을 제공합니다. 실제 시나리오에서 Personalizer는 순위 및 보상 API 호출에서 전송된 데이터로부터 사용자 기본 설정을 학습합니다. 예제 코드와 같이 명시적으로 정의하지는 않습니다.

    실제 프로덕션 시스템에서 보상 점수는 비즈니스 목표 및 KPI에 맞게 설계되어야 합니다. 보상 메트릭을 계산하는 방법을 결정하려면 약간의 실험이 필요할 수 있습니다.

    아래 코드에서 사용자의 기본 설정과 작업에 대한 응답은 일련의 조건문으로 하드 코딩되며, 설명 텍스트는 시연 목적으로 코드에 포함되었습니다.

Personalizer 스크립트를 설정하려면 다음 단계를 따릅니다.

  1. 키와 엔드포인트를 찾습니다.

    Important

    Azure Portal로 이동합니다. 필수 구성 요소 섹션에서 만든 Personalizer 리소스가 성공적으로 배포된 경우 다음 단계 아래에서 리소스로 이동 단추를 클릭합니다. 리소스 관리 아래에 있는 리소스의 키 및 엔드포인트 페이지에서 키 및 엔드포인트를 찾을 수 있습니다.

    완료되면 코드에서 키를 제거하고 공개적으로 게시하지 마세요. 프로덕션의 경우 자격 증명을 안전하게 저장하고 액세스하는 방법을 사용하는 것이 좋습니다. 예를 들어 Azure Key Vault입니다.

  2. 텍스트 편집기 또는 IDE에서 personalizer-quickstart.py를 열고 아래 코드를 붙여넣습니다.

  3. 키와 엔드포인트를 표시된 코드에 붙여넣습니다. 엔드포인트는 https://<your_resource_name>.cognitiveservices.azure.com/ 형식입니다.

    Important

    완료되면 코드에서 키를 제거하고 공개적으로 게시하지 마세요. 프로덕션의 경우 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를 사용하여 해당 점수를 Personalizer 서비스에 다시 전달하는 학습 루프 반복을 실행합니다. 각 단계에서 관련 정보를 콘솔에 출력합니다.

이 예에서는 "추천 제품" 섹션에 표시할 제품을 결정하기 위해 각 순위 호출이 이루어집니다. 그런 다음 보상 호출은 사용자가 추천 제품을 구매했는지 여부를 나타냅니다. 보상은 공통 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

첫 번째 반복에서 Personalizer는 아직 학습을 수행하지 않았기 때문에 임의 작업을 권장합니다. 선택적으로 더 많은 반복을 실행할 수 있습니다. 약 10분 후 서비스는 권장 사항에 개선 사항을 표시하기 시작합니다.

The quickstart program asks a couple of questions to gather user preferences, known as features, then provides the top action.

분석을 위해 많은 이벤트 생성(선택 사항)

예를 들어, 이 빠른 시작 시나리오에서 5,000개의 이벤트를 쉽게 만들 수 있으며, 이는 실습생 모드, 온라인 모드, 오프라인 평가 실행 및 기능 평가 만들기를 사용하여 환경을 쌓기에 충분합니다. 위 코드 블록의 while 루프를 다음 코드로 바꿉니다.

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

이 빠른 시작의 소스 코드는 GitHub에서 사용할 수 있습니다.

학습된 모델 다운로드

위의 예에서 5,000개의 이벤트에 대해 학습된 Personalizer 모델을 다운로드하려면 Personalizer 샘플 리포지토리를 방문하여 Personalizer_QuickStart_Model.zip 파일을 다운로드합니다. 그런 다음 Azure Portal에서 Personalizer 리소스로 이동하고 설정 페이지와 가져오기/내보내기 탭으로 이동하여 파일을 가져옵니다.

리소스 정리

Azure AI 서비스 구독을 정리하려면 리소스를 삭제하거나 리소스 그룹을 삭제하면 연결된 모든 리소스가 삭제됩니다.

다음 단계