This commit is contained in:
Markus Cozowicz 2017-09-18 15:08:41 -04:00
Родитель 0f51caf506
Коммит e25a055fbf
35 изменённых файлов: 460 добавлений и 102 удалений

Просмотреть файл

@ -25,6 +25,7 @@
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
<Use64BitIISExpress />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -82,11 +83,11 @@
<Reference Include="Microsoft.Data.Services.Client, Version=5.8.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.Data.Services.Client.5.8.2\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.13.9.1126, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.9\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory, Version=3.16.1.24801, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.16.1\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll</HintPath>
</Reference>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform, Version=3.13.9.1126, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.13.9\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll</HintPath>
<Reference Include="Microsoft.IdentityModel.Clients.ActiveDirectory.Platform, Version=3.16.1.24801, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.3.16.1\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Rest.ClientRuntime, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.Rest.ClientRuntime.2.3.7\lib\net452\Microsoft.Rest.ClientRuntime.dll</HintPath>

Просмотреть файл

@ -11,7 +11,7 @@
<package id="Microsoft.Data.Edm" version="5.8.2" targetFramework="net452" />
<package id="Microsoft.Data.OData" version="5.8.2" targetFramework="net452" />
<package id="Microsoft.Data.Services.Client" version="5.8.2" targetFramework="net452" />
<package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="3.13.9" targetFramework="net462" />
<package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="3.16.1" targetFramework="net462" />
<package id="Microsoft.Net.Compilers" version="2.1.0" targetFramework="net462" developmentDependency="true" />
<package id="Microsoft.Rest.ClientRuntime" version="2.3.7" targetFramework="net462" />
<package id="Microsoft.Rest.ClientRuntime.Azure" version="3.3.6" targetFramework="net462" />

Просмотреть файл

@ -1,52 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VowpalWabbit" publicKeyToken="a76afd1645210483" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0"/>
<assemblyIdentity name="VowpalWabbit" publicKeyToken="a76afd1645210483" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VowpalWabbit.Core" publicKeyToken="a76afd1645210483" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0"/>
<assemblyIdentity name="VowpalWabbit.Core" publicKeyToken="a76afd1645210483" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VowpalWabbit.Common" publicKeyToken="a76afd1645210483" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0"/>
<assemblyIdentity name="VowpalWabbit.Common" publicKeyToken="a76afd1645210483" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0"/>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
<assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.diagnostics>
<trace autoflush="true" indentsize="0">
<listeners>
<add name="myAppInsightsListener" type="Microsoft.ApplicationInsights.TraceListener.ApplicationInsightsTraceListener, Microsoft.ApplicationInsights.TraceListener"/>
<add name="myAppInsightsListener" type="Microsoft.ApplicationInsights.TraceListener.ApplicationInsightsTraceListener, Microsoft.ApplicationInsights.TraceListener" />
</listeners>
</trace>
</system.diagnostics>

Просмотреть файл

@ -13,17 +13,31 @@ using System.Threading.Tasks;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Download blobs in background.
/// </summary>
public class AzureBlobBackgroundDownloader : IDisposable
{
/// <summary>
/// Download finished event handler.
/// </summary>
public delegate void DownloadedEventHandler(object sender, byte[] data);
/// <summary>
/// Download failed event handler.
/// </summary>
public delegate void FailedEventHandler(object sender, Exception e);
/// <summary>
/// Download finished event handler.
/// </summary>
public event DownloadedEventHandler Downloaded;
/// <summary>
/// Download failed event handler.
/// </summary>
public event FailedEventHandler Failed;
private IDisposable disposable;
private readonly Uri blobAddress;
@ -32,6 +46,9 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
private bool downloadImmediately;
/// <summary>
/// Creates a new instance.
/// </summary>
public AzureBlobBackgroundDownloader(string blobAddress, TimeSpan interval, bool downloadImmediately = false, string storageConnectionString = null)
{
if (blobAddress == null)
@ -120,6 +137,9 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}
}
/// <summary>
/// Disposes the object.
/// </summary>
public void Dispose()
{
if (this.disposable != null)

Просмотреть файл

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.props" Condition="Exists('$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@ -55,10 +56,6 @@
<DocumentationFile>$(SolutionDir)bin\x64\Release\Microsoft.Research.MultiWorldTesting.ClientLibrary.XML</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="GitLink, Version=2.4.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\gitlink.2.4.0\lib\net45\GitLink.exe</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.AI.PerfCounterCollector, Version=2.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.2.0\lib\net45\Microsoft.AI.PerfCounterCollector.dll</HintPath>
<Private>True</Private>
@ -209,6 +206,8 @@
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\packages\MSBuildTasks.1.5.0.214\build\MSBuildTasks.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\MSBuildTasks.1.5.0.214\build\MSBuildTasks.targets'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.props'))" />
<Error Condition="!Exists('$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.targets'))" />
</Target>
<PropertyGroup>
<PostBuildEvent>$(ProjectDir)\..\docgen.bat "$(ProjectDir)\..\"</PostBuildEvent>
@ -217,8 +216,8 @@
<AssemblyInfo CodeLanguage="CS" OutputFile="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs" AssemblyTitle="Microsoft.Research.MultiWorldTesting.ClientLibrary" AssemblyDescription="" AssemblyConfiguration="" AssemblyCompany="Microsoft Corp" AssemblyProduct="Microsoft.Research.MultiWorldTesting.ClientLibrary" AssemblyCopyright="Copyright (C) Microsoft Corp 2014-2016" AssemblyTrademark="" ComVisible="false" CLSCompliant="false" Guid="a991c863-165c-4fd5-b388-45b13df358a8" AssemblyVersion="$(ClientLibraryAssemblyVersion)" AssemblyFileVersion="$(ClientLibraryAssemblyVersion)" />
</Target>
<Target Name="AfterBuild">
<Exec Command="$(SolutionDir)packages\gitlink.2.4.0\lib\net45\GitLink.exe ..\.. -f $(SolutionDir)\mwt-ds.sln -p $(Platform) -c $(Configuration) -d $(OutDir) -errorsaswarnings -u https://github.com/Microsoft/mwt-ds" Condition="'$(BuildNuget)' == 'true'" />
<Exec Command=".nuget\nuget pack $(ProjectDir)\$(ProjectName).nuspec -Version $(ClientLibraryAssemblyVersion) -Prop &quot;Configuration=Release;Platform=x64&quot; -Prop SolutionDir=$(SolutionDir) -OutputDirectory $(OutDir) " WorkingDirectory="$(SolutionDir)" Condition="'$(BuildNuget)' == 'true'" />
</Target>
<Import Project="$(SolutionDir)\packages\MSBuildTasks.1.5.0.214\build\MSBuildTasks.targets" Condition="Exists('$(SolutionDir)\packages\MSBuildTasks.1.5.0.214\build\MSBuildTasks.targets')" />
</Project>
<Import Project="$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.targets" Condition="Exists('$(SolutionDir)\packages\GitLink.3.1.0\build\GitLink.targets')" />
</Project>

Просмотреть файл

@ -2,8 +2,14 @@
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// When a model cannot be found.
/// </summary>
public class ModelNotFoundException : Exception
{
/// <summary>
/// Creates a new instance.
/// </summary>
public ModelNotFoundException(string message) : base(message) { }
}
}

Просмотреть файл

@ -101,6 +101,9 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
this.initialized = true;
}
/// <summary>
/// Disposes the resources.
/// </summary>
public void Dispose()
{
foreach (var p in typeof(PerformanceCounters).GetProperties())
@ -115,39 +118,75 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}
}
/// <summary>
/// Number of interactions queued.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.NumberOfItems64)]
public PerformanceCounter InteractionExamplesQueue { get; private set; }
/// <summary>
/// Total number of interactions seen.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.NumberOfItems64)]
public PerformanceCounter InteractionExamplesTotal { get; private set; }
/// <summary>
/// Number of interactions processed per second.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.RateOfCountsPerSecond32)]
public PerformanceCounter InteractionExamplesPerSec { get; private set; }
/// <summary>
/// Number of bytes (from interaction) per second.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.RateOfCountsPerSecond64)]
public PerformanceCounter InteractionExamplesBytesPerSec { get; private set; }
/// <summary>
/// Average size of interactions (in bytes).
/// </summary>
[PerformanceCounterType(PerformanceCounterType.AverageCount64)]
public PerformanceCounter AverageInteractionExampleSize { get; private set; }
/// <summary>
/// Average size of interactions (in bytes) - Base.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.AverageBase)]
public PerformanceCounter AverageInteractionExampleSizeBase { get; private set; }
/// <summary>
/// Number of observations queued.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.NumberOfItems64)]
public PerformanceCounter ObservationExamplesQueue { get; private set; }
/// <summary>
/// Total number of observations seen.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.NumberOfItems64)]
public PerformanceCounter ObservationExamplesTotal { get; private set; }
/// <summary>
/// Number of observations per second.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.RateOfCountsPerSecond32)]
public PerformanceCounter ObservationExamplesPerSec { get; private set; }
/// <summary>
/// Number of bytes of observations per second.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.RateOfCountsPerSecond64)]
public PerformanceCounter ObservationExamplesBytesPerSec { get; private set; }
/// <summary>
/// Average observation size in bytes.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.AverageCount64)]
public PerformanceCounter AverageObservationExampleSize { get; private set; }
/// <summary>
/// Average observation size in bytes - Base.
/// </summary>
[PerformanceCounterType(PerformanceCounterType.AverageBase)]
public PerformanceCounter AverageObservationExampleSizeBase { get; private set; }

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EnterpriseLibrary.TransientFaultHandling" version="6.0.1304.0" targetFramework="net45" />
<package id="gitlink" version="2.4.0" targetFramework="net45" developmentDependency="true" />
<package id="GitLink" version="3.1.0" targetFramework="net462" developmentDependency="true" />
<package id="Microsoft.ApplicationInsights" version="2.2.0" targetFramework="net45" />
<package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.2.0" targetFramework="net45" />
<package id="Microsoft.ApplicationInsights.TraceListener" version="2.2.0" targetFramework="net45" />

Просмотреть файл

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Research.MultiWorldTesting.ClientLibrary;
using System.Threading.Tasks;
namespace ClientDecisionServiceTest
{
@ -14,7 +15,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestADFExplorationResult()
public async Task TestADFExplorationResult()
{
joinServer.Reset();
@ -36,7 +37,7 @@ namespace ClientDecisionServiceTest
for (int i = 1; i <= 100; i++)
{
var adfContext = new TestADFContext(i);
int[] action = ds.ChooseRanking(uniqueKey, adfContext);
int[] action = await ds.ChooseRankingAsync(uniqueKey, adfContext);
Assert.AreEqual(i, action.Length);
@ -55,7 +56,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestADFModelUpdateFromStream()
public async Task TestADFModelUpdateFromStream()
{
joinServer.Reset();
@ -92,7 +93,7 @@ namespace ClientDecisionServiceTest
int numActions = rg.Next(5, 20);
var context = TestADFContextWithFeatures.CreateRandom(numActions, rg);
int[] action = ds.ChooseRanking(uniqueKey, context);
int[] action = await ds.ChooseRankingAsync(uniqueKey, context);
Assert.AreEqual(numActions, action.Length);

Просмотреть файл

@ -17,7 +17,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestSingleActionDSUploadSingleEvent()
public async Task TestSingleActionDSUploadSingleEvent()
{
joinServer.Reset();
@ -35,7 +35,7 @@ namespace ClientDecisionServiceTest
.Create<TestContext>(dsConfig)
.ExploitUntilModelReady(new ConstantPolicy<TestContext>()))
{
chosenAction = ds.ChooseAction(uniqueKey, new TestContext());
chosenAction = await ds.ChooseActionAsync(uniqueKey, new TestContext());
}
Assert.AreEqual(1, joinServer.RequestCount);
@ -48,7 +48,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestSingleActionDSUploadMultipleEvents()
public async Task TestSingleActionDSUploadMultipleEvents()
{
joinServer.Reset();
@ -64,8 +64,8 @@ namespace ClientDecisionServiceTest
.ExploitUntilModelReady(new ConstantPolicy<TestContext>()))
{
int chosenAction1 = ds.ChooseAction(uniqueKey, new TestContext());
int chosenAction2 = ds.ChooseAction(uniqueKey, new TestContext());
int chosenAction1 = await ds.ChooseActionAsync(uniqueKey, new TestContext());
int chosenAction2 = await ds.ChooseActionAsync(uniqueKey, new TestContext());
ds.ReportReward(1.0f, uniqueKey);
ds.ReportOutcome(JsonConvert.SerializeObject(new { value = "test outcome" }), uniqueKey);
}
@ -148,7 +148,7 @@ namespace ClientDecisionServiceTest
}
[TestMethod]
public void TestMultiActionDSUploadSingleEvent()
public async Task TestMultiActionDSUploadSingleEvent()
{
joinServer.Reset();
@ -164,7 +164,7 @@ namespace ClientDecisionServiceTest
//.WithTopSlotEpsilonGreedy(.2f)
.ExploitUntilModelReady(new ConstantPolicy<TestContext>()))
{
chosenActions = ds.ChooseRanking(uniqueKey, new TestContext());
chosenActions = await ds.ChooseRankingAsync(uniqueKey, new TestContext());
}
Assert.AreEqual(1, joinServer.RequestCount);
@ -177,7 +177,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestMultiActionDSUploadMultipleEvents()
public async Task TestMultiActionDSUploadMultipleEvents()
{
joinServer.Reset();
@ -191,8 +191,8 @@ namespace ClientDecisionServiceTest
//.WithTopSlotEpsilonGreedy(.2f)
.ExploitUntilModelReady(new ConstantPolicy<TestContext>()))
{
int[] chosenAction1 = ds.ChooseRanking(uniqueKey, new TestContext());
int[] chosenAction2 = ds.ChooseRanking(uniqueKey, new TestContext());
int[] chosenAction1 = await ds.ChooseRankingAsync(uniqueKey, new TestContext());
int[] chosenAction2 = await ds.ChooseRankingAsync(uniqueKey, new TestContext());
ds.ReportReward(1.0f, uniqueKey);
ds.ReportOutcome(new { value = "test outcome" }, uniqueKey);
}
@ -202,7 +202,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestMultiActionDSUploadSelective()
public async Task TestMultiActionDSUploadSelective()
{
joinServer.Reset();
@ -233,7 +233,7 @@ namespace ClientDecisionServiceTest
{
for (int i = 0; i < numEvents; i++)
{
int[] chosenAction1 = ds.ChooseRanking(uniqueKey, new TestContext());
int[] chosenAction1 = await ds.ChooseRankingAsync(uniqueKey, new TestContext());
}
}
// Some events must have been dropped so the total count cannot be same as original

Просмотреть файл

@ -16,7 +16,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestDevModeSettingsAndExampleLog()
public async Task TestDevModeSettingsAndExampleLog()
{
joinServer.Reset();
@ -57,7 +57,7 @@ namespace ClientDecisionServiceTest
Random rg = new Random(i);
int numActions = rg.Next(5, 20);
var context = TestADFContextWithFeatures.CreateRandom(numActions, rg);
int[] action = ds.ChooseRanking(interId, context);
int[] action = await ds.ChooseRankingAsync(interId, context);
ds.ReportReward(i / 100f, obserId);
eventIdList.Add(interId);

Просмотреть файл

@ -31,7 +31,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(0)]
public void InitialFullExplorationTest()
public async Task InitialFullExplorationTest()
{
var recorder = new MyRecorder();
@ -53,7 +53,7 @@ namespace ClientDecisionServiceTest
using (var ds = DecisionService.CreateJson(config, metaData:metaData).WithRecorder(recorder))
{
var decision = ds.ChooseRanking("abc", "{\"a\":1,\"_multi\":[{\"b\":2}]}");
var decision = await ds.ChooseRankingAsync("abc", "{\"a\":1,\"_multi\":[{\"b\":2}]}");
// since there's not a model loaded why should get 100% exploration
// Assert.AreEqual(1f, recorder.LastExplorerState.Probability);
@ -61,7 +61,7 @@ namespace ClientDecisionServiceTest
model.Position = 0;
ds.UpdateModel(model);
decision = ds.ChooseRanking("abc", "{\"a\":1,\"_multi\":[{\"b\":2}, {\"b\":3}]}");
decision = await ds.ChooseRankingAsync("abc", "{\"a\":1,\"_multi\":[{\"b\":2}, {\"b\":3}]}");
// Assert.AreNotEqual(1f, recorder.LastExplorerState.Probability);
var vwState = recorder.LastMapperState as VWState;

Просмотреть файл

@ -95,7 +95,7 @@ namespace ClientDecisionServiceTest
}
[TestMethod]
public void TestDSLocalModelUpdate()
public async Task TestDSLocalModelUpdate()
{
string vwArgs = "--cb_explore_adf --epsilon 0.2 --cb_type dr -q ::";
DecisionServiceLocal<FoodContext> dsLocal = new DecisionServiceLocal<FoodContext>(vwArgs, 1, TimeSpan.MaxValue);
@ -107,17 +107,17 @@ namespace ClientDecisionServiceTest
// Generate interactions and ensure the model updates at the right frequency
// (updates every example initially)
prevModel = dsLocal.Model;
dsLocal.ChooseAction(guid1, context, 1);
await dsLocal.ChooseActionAsync(guid1, context, 1);
dsLocal.ReportRewardAndComplete((float)1.0, guid1);
Assert.IsTrue(!dsLocal.Model.SequenceEqual(prevModel));
// Set the model to update every two examples
prevModel = dsLocal.Model;
dsLocal.ModelUpdateInterval = 2;
dsLocal.ChooseAction(guid1, context, 1);
await dsLocal.ChooseActionAsync(guid1, context, 1);
dsLocal.ReportRewardAndComplete((float)1.0, guid1);
Assert.IsFalse(!dsLocal.Model.SequenceEqual(prevModel));
dsLocal.ChooseAction(guid2, context, 1);
await dsLocal.ChooseActionAsync(guid2, context, 1);
dsLocal.ReportRewardAndComplete((float)2.0, guid1);
Assert.IsTrue(!dsLocal.Model.SequenceEqual(prevModel));
}

Просмотреть файл

@ -18,7 +18,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestRcv1ModelUpdateFromStream()
public async Task TestRcv1ModelUpdateFromStream()
{
joinServer.Reset();
@ -63,7 +63,7 @@ namespace ClientDecisionServiceTest
DateTime timeStamp = DateTime.UtcNow;
int action = ds.ChooseAction(uniqueKey, context);
int action = await ds.ChooseActionAsync(uniqueKey, context);
// verify the actions are in the expected range
Assert.IsTrue(action >= 1 && action <= numActions);

Просмотреть файл

@ -5,6 +5,7 @@ using System;
using System.IO;
using VW.Serializer;
using Microsoft.Research.MultiWorldTesting.Contract;
using System.Threading.Tasks;
namespace ClientDecisionServiceTest
{
@ -36,7 +37,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(0)]
public void TestSingleActionOfflineModeCustomLogger()
public async Task TestSingleActionOfflineModeCustomLogger()
{
var dsConfig = new DecisionServiceConfiguration("") { OfflineMode = true, OfflineApplicationID = "" };
@ -54,7 +55,7 @@ namespace ClientDecisionServiceTest
{
for (int i = 0; i < numChooseAction; i++)
{
ds.ChooseAction(i.ToString(), new TestContext());
await ds.ChooseActionAsync(i.ToString(), new TestContext());
}
Assert.AreEqual(numChooseAction, recorder.NumRecord);
@ -85,7 +86,7 @@ namespace ClientDecisionServiceTest
[TestCategory("Client Library")]
[Priority(1)]
[Ignore]
public void TestSingleActionOnlineModeCustomLogger()
public async Task TestSingleActionOnlineModeCustomLogger()
{
var dsConfig = new DecisionServiceConfiguration(MockCommandCenter.SettingsBlobUri)
{
@ -103,7 +104,7 @@ namespace ClientDecisionServiceTest
{
for (int i = 0; i < numChooseAction; i++)
{
ds.ChooseAction(i.ToString(), new TestContext());
await ds.ChooseActionAsync(i.ToString(), new TestContext());
}
Assert.AreEqual(numChooseAction, recorder.NumRecord);
@ -134,7 +135,7 @@ namespace ClientDecisionServiceTest
[ExpectedException(typeof(Exception))]
[TestCategory("Client Library")]
[Priority(0)]
public void TestMultiActionOfflineModeArgument()
public async Task TestMultiActionOfflineModeArgument()
{
var metaData = new ApplicationClientMetadata
{
@ -145,14 +146,14 @@ namespace ClientDecisionServiceTest
new DecisionServiceConfiguration("") { OfflineMode = true, OfflineApplicationID = "" },
metaData))
{
ds.ChooseAction("", "{}");
await ds.ChooseActionAsync("", "{}");
}
}
[TestMethod]
[TestCategory("Client Library")]
[Priority(0)]
public void TestMultiActionOfflineModeCustomLogger()
public async Task TestMultiActionOfflineModeCustomLogger()
{
var dsConfig = new DecisionServiceConfiguration("") { OfflineMode = true, OfflineApplicationID = "" };
var metaData = new ApplicationClientMetadata
@ -168,7 +169,7 @@ namespace ClientDecisionServiceTest
{
for (int i = 0; i < numChooseAction; i++)
{
ds.ChooseAction(i.ToString(), new TestContext());
await ds.ChooseActionAsync(i.ToString(), new TestContext());
}
Assert.AreEqual(numChooseAction, recorder.NumRecord);
@ -197,7 +198,7 @@ namespace ClientDecisionServiceTest
[TestMethod]
[TestCategory("Client Library")]
[Priority(1)]
public void TestMultiActionOnlineModeCustomLogger()
public async Task TestMultiActionOnlineModeCustomLogger()
{
joinServer.Reset();
@ -216,7 +217,7 @@ namespace ClientDecisionServiceTest
{
for (int i = 0; i < numChooseAction; i++)
{
ds.ChooseAction(i.ToString(), new TestContext());
await ds.ChooseActionAsync(i.ToString(), new TestContext());
}
Assert.AreEqual(numChooseAction, recorder.NumRecord);

Просмотреть файл

@ -1,43 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0"/>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.Services.Client" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0"/>
<assemblyIdentity name="Microsoft.Data.OData" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.8.1.0" newVersion="5.8.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VowpalWabbit.Core" publicKeyToken="a76afd1645210483" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0"/>
<assemblyIdentity name="VowpalWabbit.Core" publicKeyToken="a76afd1645210483" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VowpalWabbit" publicKeyToken="a76afd1645210483" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0"/>
<assemblyIdentity name="VowpalWabbit" publicKeyToken="a76afd1645210483" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="VowpalWabbit.Common" publicKeyToken="a76afd1645210483" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0"/>
<assemblyIdentity name="VowpalWabbit.Common" publicKeyToken="a76afd1645210483" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.4.0.0" newVersion="8.4.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0"/>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
<assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" /></startup></configuration>

Просмотреть файл

@ -7,18 +7,34 @@ using VW.Serializer;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Base class for all Vowpal Wabbit contenxt mappers.
/// </summary>
public abstract class VWBaseContextMapper<TVowpalWabbit, TContext, TAction>
: IUpdatable<Stream>, IDisposable, IContextMapper<TContext, TAction>
where TVowpalWabbit : class, IDisposable
{
/// <summary>
/// Type inspector used to extract schema information.
/// </summary>
protected ITypeInspector typeInspector;
/// <summary>
/// The pool of VW objects.
/// </summary>
protected VowpalWabbitThreadedPredictionBase<TVowpalWabbit> vwPool;
/// <summary>
/// True if development mode enabled (additional logging).
/// </summary>
protected bool developmentMode;
/// <summary>
/// Constructor using a memory stream.
/// </summary>
/// <param name="vwModelStream">The VW model memory stream.</param>
/// <param name="developmentMode">True if development mode enabled (additional logging).</param>
/// <param name="typeInspector">Type inspector used to extract schema information.</param>
protected VWBaseContextMapper(
Stream vwModelStream = null,
ITypeInspector typeInspector = null,
@ -86,6 +102,13 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
public Task<PolicyDecision<TAction>> MapContextAsync(TContext context)
{
if (this.vwPool == null)
@ -100,8 +123,19 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected abstract VowpalWabbitThreadedPredictionBase<TVowpalWabbit> CreatePool(VowpalWabbitSettings settings);
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected abstract PolicyDecision<TAction> MapContext(TVowpalWabbit vw, TContext context);
}
}

Просмотреть файл

@ -8,6 +8,10 @@ using VW.Serializer;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Vowpal Wabbit based explorer using C# based features.
/// </summary>
/// <typeparam name="TContext"></typeparam>
public sealed class VWExplorer<TContext> :
VWBaseContextMapper<VowpalWabbit<TContext>, TContext, ActionProbability[]>,
IContextMapper<TContext, ActionProbability[]>, INumberOfActionsProvider<TContext>
@ -18,7 +22,6 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
/// <summary>
/// Constructor using a memory stream.
/// </summary>
/// <param name="vwModelStream">The VW model memory stream.</param>
public VWExplorer(Stream vwModelStream = null, ITypeInspector typeInspector = null, bool developmentMode = false)
: base(vwModelStream, typeInspector, developmentMode)
{
@ -31,12 +34,23 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
this.multiSerializer = this.serializer as IVowpalWabbitMultiExampleSerializerCompiler<TContext>;
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected override VowpalWabbitThreadedPredictionBase<VowpalWabbit<TContext>> CreatePool(VowpalWabbitSettings settings)
{
return new VowpalWabbitThreadedPrediction<TContext>(settings);
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected override PolicyDecision<ActionProbability[]> MapContext(VowpalWabbit<TContext> vw, TContext context)
{
if (this.developmentMode)
@ -63,6 +77,9 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
return PolicyDecision.Create(ap, state);
}
/// <summary>
/// Returns the number of actions defined by this context.
/// </summary>
public int GetNumberOfActions(TContext context)
{
if (this.multiSerializer == null)

Просмотреть файл

@ -7,15 +7,29 @@ using System;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Vowpal Wabbit based policy for string JSON based context.
/// </summary>
public class VWJsonPolicy :
VWBaseContextMapper<VowpalWabbit, string, int>,
IPolicy<string>
{
/// <summary>
/// Creates a new instance.
/// </summary>
public VWJsonPolicy(Stream vwModelStream = null)
: base(vwModelStream)
{
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected override PolicyDecision<int> MapContext(VowpalWabbit vw, string context)
{
using (var vwJson = new VowpalWabbitJsonSerializer(vw))
@ -28,26 +42,46 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected override VowpalWabbitThreadedPredictionBase<VowpalWabbit> CreatePool(VowpalWabbitSettings settings)
{
return new VowpalWabbitThreadedPrediction(settings);
}
}
/// <summary>
/// Vowpal Wabbit based ranker for string JSON based context.
/// </summary>
public class VWJsonRanker :
VWBaseContextMapper<VowpalWabbit, string, int[]>,
IRanker<string>, INumberOfActionsProvider<string>
{
/// <summary>
/// Creates a new instance.
/// </summary>
public VWJsonRanker(Stream vwModelStream = null)
: base(vwModelStream)
{
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected override VowpalWabbitThreadedPredictionBase<VowpalWabbit> CreatePool(VowpalWabbitSettings settings)
{
return new VowpalWabbitThreadedPrediction(settings);
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected override PolicyDecision<int[]> MapContext(VowpalWabbit vw, string context)
{
using (var vwJson = new VowpalWabbitJsonSerializer(vw))
@ -63,6 +97,9 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}
}
/// <summary>
/// Returns the number of actions defined by this context.
/// </summary>
public int GetNumberOfActions(string context)
{
return VowpalWabbitJsonSerializer.GetNumberOfActionDependentExamples(context);

Просмотреть файл

@ -11,20 +11,37 @@ using VW.Serializer;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Vowpal Wabbit based explorer for JSON context.
/// </summary>
public sealed class VWJsonExplorer :
VWBaseContextMapper<VowpalWabbit, string, ActionProbability[]>,
IContextMapper<string, ActionProbability[]>, INumberOfActionsProvider<string>
{
/// <summary>
/// Creates a new instance.
/// </summary>
public VWJsonExplorer(Stream vwModelStream = null, bool developmentMode = false)
: base(vwModelStream, developmentMode: developmentMode)
{
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected override VowpalWabbitThreadedPredictionBase<VowpalWabbit> CreatePool(VowpalWabbitSettings settings)
{
return new VowpalWabbitThreadedPrediction(settings);
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected override PolicyDecision<ActionProbability[]> MapContext(VowpalWabbit vw, string context)
{
using (var vwJson = new VowpalWabbitJsonSerializer(vw))
@ -50,6 +67,9 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}
}
/// <summary>
/// Returns the number of actions defined by this context.
/// </summary>
public int GetNumberOfActions(string context)
{
return VowpalWabbitJsonSerializer.GetNumberOfActionDependentExamples(context);

Просмотреть файл

@ -6,23 +6,37 @@ using VW.Serializer;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Vowpal Wabbit based policy.
/// </summary>
/// <typeparam name="TContext"></typeparam>
public class VWPolicy<TContext>
: VWBaseContextMapper<VowpalWabbit<TContext>, TContext, int>, IPolicy<TContext>
{
/// <summary>
/// Constructor using a memory stream.
/// </summary>
/// <param name="vwModelStream">The VW model memory stream.</param>
public VWPolicy(Stream vwModelStream = null, ITypeInspector typeInspector = null, bool developmentMode = false)
: base(vwModelStream, typeInspector, developmentMode)
{
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected override VowpalWabbitThreadedPredictionBase<VowpalWabbit<TContext>> CreatePool(VowpalWabbitSettings settings)
{
return new VowpalWabbitThreadedPrediction<TContext>(settings);
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected override PolicyDecision<int> MapContext(VowpalWabbit<TContext> vw, TContext context)
{
if (this.developmentMode)

Просмотреть файл

@ -9,6 +9,10 @@ using VW.Serializer;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Vowpal Wabbit based ranker.
/// </summary>
/// <typeparam name="TContext"></typeparam>
public class VWRanker<TContext> :
VWBaseContextMapper<VowpalWabbit<TContext>, TContext, int[]>,
IRanker<TContext>, INumberOfActionsProvider<TContext>
@ -18,7 +22,6 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
/// <summary>
/// Constructor using a memory stream.
/// </summary>
/// <param name="vwModelStream">The VW model memory stream.</param>
public VWRanker(Stream vwModelStream = null, ITypeInspector typeInspector = null, bool developmentMode = false)
: base(vwModelStream, typeInspector, developmentMode)
{
@ -30,11 +33,22 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
}) as IVowpalWabbitMultiExampleSerializerCompiler<TContext>;
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected override VowpalWabbitThreadedPredictionBase<VowpalWabbit<TContext>> CreatePool(VowpalWabbitSettings settings)
{
return new VowpalWabbitThreadedPrediction<TContext>(settings);
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected override PolicyDecision<int[]> MapContext(VowpalWabbit<TContext> vw, TContext context)
{
if (this.developmentMode)
@ -54,12 +68,19 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
return PolicyDecision.Create(actions, state);
}
/// <summary>
/// Returns the number of actions defined by this context.
/// </summary>
public int GetNumberOfActions(TContext context)
{
return this.serializer.GetNumberOfActionDependentExamples(context);
}
}
/// <summary>
/// Vowpal Wabbit based ranker using C# defined context and action dependent features.
/// </summary>
public class VWRanker<TContext, TActionDependentFeature> :
VWBaseContextMapper<VowpalWabbit<TContext, TActionDependentFeature>, TContext, int[]>,
IRanker<TContext>, INumberOfActionsProvider<TContext>
@ -69,7 +90,6 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
/// <summary>
/// Constructor using a memory stream.
/// </summary>
/// <param name="vwModelStream">The VW model memory stream.</param>
public VWRanker(
Func<TContext, IReadOnlyCollection<TActionDependentFeature>> getContextFeaturesFunc,
Stream vwModelStream = null,
@ -80,11 +100,22 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
this.getContextFeaturesFunc = getContextFeaturesFunc;
}
/// <summary>
/// Sub classes must override and create a new VW pool.
/// </summary>
protected override VowpalWabbitThreadedPredictionBase<VowpalWabbit<TContext, TActionDependentFeature>> CreatePool(VowpalWabbitSettings settings)
{
return new VowpalWabbitThreadedPrediction<TContext, TActionDependentFeature>(settings);
}
/// <summary>
/// Determines the action to take for a given context.
/// This implementation should be thread-safe if multithreading is needed.
/// </summary>
/// <param name="vw">The Vowpal Wabbit instance to use.</param>
/// <param name="context">A user-defined context for the decision.</param>
/// <returns>A decision tuple containing the index of the action to take (1-based), and the Id of the model or policy used to make the decision.
/// Can be null if the Policy is not ready yet (e.g. model not loaded).</returns>
protected override PolicyDecision<int[]> MapContext(VowpalWabbit<TContext, TActionDependentFeature> vw, TContext context)
{
if (this.developmentMode)
@ -104,6 +135,9 @@ namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
return PolicyDecision.Create(actions, state);
}
/// <summary>
/// Returns the number of actions defined by this context.
/// </summary>
public int GetNumberOfActions(TContext context)
{
var adfs = this.getContextFeaturesFunc(context);

Просмотреть файл

@ -7,9 +7,15 @@ using System.Threading.Tasks;
namespace Microsoft.Research.MultiWorldTesting.ClientLibrary
{
/// <summary>
/// Defines the policy state for a VowpalWabbit model.
/// </summary>
[JsonObject(Id = "stvw")]
public class VWState
{
/// <summary>
/// The model id used at scoring time.
/// </summary>
[JsonProperty("m")]
public string ModelId { get; set; }
}

Просмотреть файл

@ -33,12 +33,17 @@ namespace Microsoft.Research.MultiWorldTesting.JoinUploader
/// </summary>
protected readonly BatchingConfiguration batchConfig;
private readonly CancellationTokenSource cancellationTokenSource;
/// <summary>
/// Cancellation token used for shutdown
/// </summary>
protected CancellationToken cancellationToken;
/// <summary>
/// Constructs an uploader object.
/// </summary>
/// <param name="batchConfig">Optional; The batching configuration that controls the buffer size.</param>
/// <param name="developmentMode">If true, enables additional logging and disables batching.</param>
public BaseEventUploader(BatchingConfiguration batchConfig = null, bool developmentMode = false)
{
this.cancellationTokenSource = new CancellationTokenSource();
@ -96,6 +101,9 @@ namespace Microsoft.Research.MultiWorldTesting.JoinUploader
/// </summary>
public event EventUploaderSuccessEventHandler SuccessHandler;
/// <summary>
/// Invoke when a batch completed.
/// </summary>
public event EventUploaderCompletedEventHandler CompletionHandler;
internal void FireErrorHandler(Exception e)

Просмотреть файл

@ -6,10 +6,19 @@ using System.Threading.Tasks.Dataflow;
namespace Microsoft.Research.MultiWorldTesting.JoinUploader
{
/// <summary>
/// Delegate definition for success.
/// </summary>
public delegate void EventUploaderSuccessEventHandler(object source, int eventCount, int sumSize, int inputQueueSize);
/// <summary>
/// Delegate definition for error.
/// </summary>
public delegate void EventUploaderErrorEventHandler(object source, Exception e);
/// <summary>
/// Delegate definition for completion.
/// </summary>
public delegate void EventUploaderCompletedEventHandler(object source, string blockName, Task task);
/// <summary>

Просмотреть файл

@ -48,7 +48,8 @@ namespace Microsoft.Research.MultiWorldTesting.JoinUploader
/// <param name="batchConfig">Optional; The batching configuration that controls the buffer size.</param>
/// <param name="loggingServiceBaseAddress">Optional; The address of a custom HTTP logging service. When null, the join service address is used.</param>
/// <param name="httpClient">Optional; The custom <see cref="IHttpClient"/> object to handle HTTP requests.</param>
public EventUploader(BatchingConfiguration batchConfig = null, string loggingServiceBaseAddress = null, IHttpClient httpClient = null, bool developmentMode = false)
/// <param name="developmentMode">If true, enables additional logging and disables batching.</param>
public EventUploader(BatchingConfiguration batchConfig = null, string loggingServiceBaseAddress = null, IHttpClient httpClient = null, bool developmentMode = false)
: base(batchConfig, developmentMode)
{
this.loggingServiceBaseAddress = loggingServiceBaseAddress ?? ServiceConstants.JoinAddress;

Просмотреть файл

@ -31,6 +31,7 @@ namespace Microsoft.Research.MultiWorldTesting.JoinUploader
/// </summary>
/// <param name="eventHubConnectionString">The Azure Stream Analytics connection string.</param>
/// <param name="batchConfig">Optional; The batching configuration to used when uploading data.</param>
/// <param name="developmentMode">If true, enables additional logging and disables batching.</param>
public EventUploaderASA
(
string eventHubConnectionString,
@ -126,7 +127,7 @@ namespace Microsoft.Research.MultiWorldTesting.JoinUploader
/// <summary>
/// Uploads a single event to EventHub asynchronously.
/// </summary>
/// <param name="events">The event to upload.</param>
/// <param name="evt">The event to upload.</param>
/// <returns>A Task object.</returns>
private Task UploadToEventHubAsync(EventData evt)
{

Просмотреть файл

@ -30,17 +30,34 @@ namespace Microsoft.Research.MultiWorldTesting.JoinUploader
/// </summary>
public string Key { get; set; }
// int, int[]
/// <summary>
/// The action or ranking (int, int[])
/// </summary>
public object Value { get; set; }
/// <summary>
/// The supplied context.
/// </summary>
public object Context { get; set; }
/// <summary>
/// The state of the explorer (e.g. model id to be able to correlate back).
/// </summary>
public object ExplorerState { get; set; }
/// <summary>
/// The state of the mapper (e.g. model id to be able to correlate back).
/// </summary>
public object MapperState { get; set; }
/// <summary>
/// If pipeline overflows, we drop stochastically.
/// </summary>
public float? ProbabilityOfDrop { get; set; }
/// <summary>
/// Create epsilon greedy interaction???
/// </summary>
public static Interaction CreateEpsilonGreedy<TContext>(string key, TContext context, int action, float probability)
{
return Interaction.Create(key, context, action, new GenericExplorerState { Probability = probability });

Просмотреть файл

@ -58,6 +58,9 @@ namespace Microsoft.Research.MultiWorldTesting.JoinUploader
/// </summary>
event EventUploaderSuccessEventHandler SuccessHandler;
/// <summary>
/// Invoked on pipeline completion.
/// </summary>
event EventUploaderCompletedEventHandler CompletionHandler;
}
}

Просмотреть файл

@ -8,18 +8,30 @@ using System.Threading.Tasks;
namespace Microsoft.Research.MultiWorldTesting.JoinUploader
{
/// <summary>
/// Custom JSON.NET serializer for interactions.
/// </summary>
public class InteractionJsonConverter : JsonConverter
{
/// <summary>
/// True, if it's an interaction type.
/// </summary>
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Interaction);
}
/// <summary>
/// Not supported.
/// </summary>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
/// <summary>
/// Serializes the interaction.
/// </summary>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var v = value as Interaction;

Просмотреть файл

@ -6,18 +6,44 @@
namespace Microsoft.Research.MultiWorldTesting.Contract
{
/// <summary>
/// Various blob and container blob names
/// </summary>
public class ApplicationBlobConstants
{
// Model blobs
/// <summary>
/// Container name for models.
/// </summary>
public const string ModelContainerName = "mwt-models";
/// <summary>
/// Blob name for latest model.
/// </summary>
public const string LatestModelBlobName = "current";
// Settings blobs
/// <summary>
/// Container name for settings.
/// </summary>
public const string SettingsContainerName = "mwt-settings";
/// <summary>
/// Blob name for client settings.
/// </summary>
public const string LatestClientSettingsBlobName = "client";
/// <summary>
/// Blob name for trainer settings.
/// </summary>
public const string LatestTrainerSettingsBlobName = "trainer";
/// <summary>
/// Blob name for extra settings.
/// </summary>
public const string LatestExtraSettingsBlobName = "extra";
/// <summary>
/// Container name for offline evaluation.
/// </summary>
public const string OfflineEvalContainerName = "mwt-offline-eval";
}
}

Просмотреть файл

@ -5,8 +5,14 @@ using System.Net;
namespace Microsoft.Research.MultiWorldTesting.Contract
{
/// <summary>
/// Helper class to download meta data.
/// </summary>
public static class ApplicationMetadataUtil
{
/// <summary>
/// Download and deserialize settings.
/// </summary>
public static TMetadata DownloadMetadata<TMetadata>(string blobUri)
{
string jsonMetadata = "";

Просмотреть файл

@ -6,8 +6,14 @@
namespace Microsoft.Research.MultiWorldTesting.Contract
{
/// <summary>
/// Unused class.
/// </summary>
public class ApplicationSettingConstants
{
/// <summary>
/// Unused.
/// </summary>
public const string UseLatestModelSetting = "latest";
}
}

Просмотреть файл

@ -27,10 +27,20 @@ namespace Microsoft.Research.MultiWorldTesting.Contract
/// </summary>
public enum TrainFrequency
{
/// <summary>
/// Low frequency.
/// </summary>
Low = 0,
/// <summary>
/// High frequency.
/// </summary>
High
}
/// <summary>
/// Defines the content of the client settings file.
/// </summary>
public class ApplicationClientMetadata
{
/// <summary>
@ -68,9 +78,15 @@ namespace Microsoft.Research.MultiWorldTesting.Contract
/// </summary>
public string AppInsightsKey { get; set; }
/// <summary>
/// Amount of exploration to apply if no model is available yet.
/// </summary>
public float InitialExplorationEpsilon { get; set; }
}
/// <summary>
/// Defines the content of the "extra" settings file.
/// </summary>
public class ApplicationExtraMetadata
{
/// <summary>

Просмотреть файл

@ -6,15 +6,39 @@
namespace Microsoft.Research.MultiWorldTesting.Contract
{
/// <summary>
/// Service constants used for old join service.
/// </summary>
public class ServiceConstants
{
// Join Server
/// <summary>
/// public address of join server.
/// </summary>
public const string JoinAddress = "http://decisionservice.cloudapp.net";
/// <summary>
/// Path for joining.
/// </summary>
public const string JoinPostAddress = "/join";
/// <summary>
/// Authentication header.
/// </summary>
public const string TokenAuthenticationScheme = "Bearer";
/// <summary>
/// Authentication scheme.
/// </summary>
public const string ConnectionStringAuthenticationScheme = "AzureStorage";
/// <summary>
/// Azure container name.
/// </summary>
public const string IncompleteContainerPrefix = "incomplete";
/// <summary>
/// Azure container name.
/// </summary>
public const string JoinedBlobContainerPrefix = "joined-examples";
}
}