/// <summary> /// Gets a cached prediction engine or creates a new one if not cached /// </summary> private BatchPredictionEngine <SarUsageEvent, SarScoreResult> GetOrCreateBatchPredictionEngine( IList <SarUsageEvent> usageItems, SarScoringArguments sarScoringArguments) { var arguments = new RecommenderScorerTransform.Arguments { // round the recommendations count to optimize engine cache recommendationCount = GetRoundedNumberOfResults(sarScoringArguments.RecommendationCount), includeHistory = sarScoringArguments.IncludeHistory }; // create a data column mapping var dataColumnMapping = new Dictionary <RoleMappedSchema.ColumnRole, string> { { new RoleMappedSchema.ColumnRole("Item"), "Item" }, { new RoleMappedSchema.ColumnRole("User"), "user" } }; string weightColumn = null; if (sarScoringArguments.ReferenceDate.HasValue) { // rounding the reference date to the beginning of next day to optimize engine cache DateTime referenceDate = sarScoringArguments.ReferenceDate.Value.Date + TimeSpan.FromDays(1); arguments.referenceDate = referenceDate.ToString("s"); if (sarScoringArguments.Decay.HasValue) { arguments.decay = sarScoringArguments.Decay.Value.TotalDays; } dataColumnMapping.Add(new RoleMappedSchema.ColumnRole("Date"), "date"); weightColumn = "weight"; } // create an engine cache key string cacheKey = $"{arguments.recommendationCount}|{arguments.includeHistory}|{arguments.referenceDate}|{arguments.decay}"; _tracer.TraceVerbose("Trying to find the engine in the cache"); var engine = _enginesCache.Get(cacheKey) as BatchPredictionEngine <SarUsageEvent, SarScoreResult>; if (engine == null) { _tracer.TraceInformation("Engine is not cached - creating a new engine"); IDataView pipeline = _environment.CreateDataView(usageItems, _usageDataSchema); RoleMappedData usageDataMappedData = _environment.CreateExamples(pipeline, null, weight: weightColumn, custom: dataColumnMapping); ISchemaBindableMapper mapper = RecommenderScorerTransform.Create(_environment, arguments, _recommender); ISchemaBoundMapper boundMapper = mapper.Bind(_environment, usageDataMappedData.Schema); IDataScorerTransform scorer = RecommenderScorerTransform.Create( _environment, arguments, pipeline, boundMapper, null); engine = _environment.CreateBatchPredictionEngine <SarUsageEvent, SarScoreResult>(scorer, false, _usageDataSchema); bool result = _enginesCache.Add(cacheKey, engine, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromDays(1) }); _tracer.TraceVerbose($"Addition of engine to the cache resulted with '{result}'"); } return(engine); }
/// <summary> /// Scores the input usage items. /// </summary> /// <param name="usageItems">The items to score</param> /// <param name="scoringArguments">The scoring arguments</param> /// <returns></returns> public IEnumerable <SarScoreResult> ScoreUsageItems(IList <SarUsageEvent> usageItems, SarScoringArguments scoringArguments) { if (_disposed) { throw new ObjectDisposedException(typeof(SarScorer).FullName); } if (usageItems == null) { throw new ArgumentNullException(nameof(usageItems)); } if (scoringArguments == null) { throw new ArgumentNullException(nameof(scoringArguments)); } _tracer.TraceVerbose("Getting or creating the prediction engine"); BatchPredictionEngine <SarUsageEvent, SarScoreResult> engine = GetOrCreateBatchPredictionEngine(usageItems, scoringArguments); _tracer.TraceInformation($"Getting recommendation for {usageItems.Count} input items"); lock (engine) { return(engine.Predict(usageItems, false) .OrderByDescending(x => x.Score) .Take(scoringArguments.RecommendationCount) .ToArray()); } }