How to use onnx model in mlnet c#, passing inputs and getting outputs - ml.net

I trained a model using pytorch
I exported it to onnx format and tested in python that it works (it does)
I want to know how I can use this in ml.net in c#
The usage in python looks like this
the model in netorn looks like
I found an example that uses
using the packages Microsoft.ML, Microsoft.ML.OnnxRuntime and Microsoft.ML.OnnxTransformer
and is able to use an onnx model but that works with images and since I am very new to this I am unable to figure out how to load the model and make a prediction and get the value of the action which is an array of float size 6.

I found this to be useful for this
you'll also have to define your model schema.
here is the class I ended up with for this model
using Microsoft.ML;
using Microsoft.ML.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace godot_net_server
{
class OnnxModelScorer
{
private readonly string modelLocation;
private readonly MLContext mlContext;
public OnnxModelScorer(string modelLocation, MLContext mlContext)
{
this.modelLocation = modelLocation;
this.mlContext = mlContext;
}
public class ModelInput
{
[VectorType(10)]
[ColumnName("input.1")]
public float[] Features { get; set; }
}
private ITransformer LoadModel(string modelLocation)
{
Console.WriteLine("Read model");
Console.WriteLine($"Model location: {modelLocation}");
// Create IDataView from empty list to obtain input data schema
var data = mlContext.Data.LoadFromEnumerable(new List<ModelInput>());
// Define scoring pipeline
var pipeline = mlContext.Transforms.ApplyOnnxModel(modelFile: modelLocation, outputColumnNames: new[] { "31","34" }, inputColumnNames: new[] { "input.1" });
// Fit scoring pipeline
var model = pipeline.Fit(data);
return model;
}
public class Prediction
{
[VectorType(6)]
[ColumnName("31")]
public float[] action{ get; set; }
[VectorType(1)]
[ColumnName("34")]
public float[] state { get; set; }
}
private IEnumerable<float> PredictDataUsingModel(IDataView testData, ITransformer model)
{
Console.WriteLine("");
Console.WriteLine("=====Identify the objects in the images=====");
Console.WriteLine("");
IDataView scoredData = model.Transform(testData);
IEnumerable<float[]> probabilities = scoredData.GetColumn<float[]>("31");
var a = probabilities.ToList();
a.Count.ToString();
return a[0];
}
public IEnumerable<float> Score(IDataView data)
{
var model = LoadModel(modelLocation);
return PredictDataUsingModel(data, model);
}
}
}
this is how I used it to get a prediction
MLContext mlContext = new MLContext();
// Load trained model
var modelScorer = new OnnxModelScorer("my_ppo_1_model.onnx", mlContext);
List<ModelInput> input = new List<ModelInput>();
input.Add(new ModelInput()
{
Features = new[]
{
3.036393f,11.0f,2.958097f,0.0f,0.0f,0.0f,0.015607f,0.684984f,0.0f,0.0f
}
});
var action = modelScorer.Score(mlContext.Data.LoadFromEnumerable(input));
The result is as I was expecting it to be

Related

Integrate Blazor with Chart.js: how to pass an object

I want to display in my Blazor WebAssembly application, some graphs with Chart.js. I tried to use Chartjs.Blazor.Fork but I have few errors, for example I have opened another post about here.
So, after a day without results, I decided to start my own component. I follow the instruction I found in a blog. Basically, I have my Razor component called Chart.razor with the following code
#inject IJSRuntime JSRuntime
<canvas id="#Id"></canvas>
#code {
public enum ChartType
{
Pie,
Bar
}
[Parameter]
public string Id { get; set; }
[Parameter]
public ChartType Type { get; set; }
[Parameter]
public string[] Data { get; set; }
[Parameter]
public string[] BackgroundColor { get; set; }
[Parameter]
public string[] Labels { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Here we create an anonymous type with all the options
// that need to be sent to Chart.js
var config = new
{
Type = Type.ToString().ToLower(),
Options = new
{
Responsive = true,
Scales = new
{
YAxes = new[]
{
new { Ticks = new {
BeginAtZero=true
} }
}
}
},
Data = new
{
Datasets = new[]
{
new { Data = Data, BackgroundColor = BackgroundColor}
},
Labels = Labels
}
};
await JSRuntime.InvokeVoidAsync("setup", Id, config);
}
}
then I have my own mychart.js script to update the chart
window.setup = (id,config) => {
var ctx = document.getElementById(id).getContext('2d');
new Chart(ctx, config);
}
So, I use this code
<Chart Id="bar1" Type="#Chart.ChartType.Bar"
Data="#(new[] { " 10", "9" } )"
BackgroundColor="#(new[] { " yellow","red"} )"
Labels="#(new[] { " Fail","Ok" } )">
</Chart>
Ugly code but it is working. Now, I can display a graph in my page. Cool! What I want to display is something more complex, because I have to show a stacked bar graph with groups and the configuration is quite complicated.
I want to replace the config you can see in the page with for example a class. In this class I want to collect all configuration, like Type, Options, Data, Labels and so on, and pass them in the await JSRuntime.InvokeVoidAsync("setup", Id, config);`
For starting I created my base class like
public abstract class ConfigBase
{
protected ConfigBase(ChartType chartType)
{
Type = chartType;
}
public ChartType Type { get; }
public string CanvasId { get; } = Guid.NewGuid().ToString();
}
My problem is how to transform this class to obtain a valid object for the JavaScript to execute correctly new Chart(ctx, config);.
OK, it was quite easy. I saw a lot of different technics for that but there is a very basic simple way
await JSRuntime.InvokeVoidAsync("setup", Config.CanvasId, Config);

How to add multiple custom columns to IDataView in ML.NET

I'd like to add two custom columns after loading my IDataView from file. In each row, the new column values should be natural logarithm of the values.
My environment is:
Windows 10 Pro, Version 10.0.19043 Build 19043
ML.NET version 1.6.0
Microsoft Visual Studio Professional 2022 Preview (64-bit), Version 17.0.0 Preview 3.1
Here is what I have done so far. This is a standalone, reproducible program that demonstrates the problem.
using Microsoft.ML;
using Microsoft.ML.Transforms;
namespace TestLog {
public static class Program {
private class InputData {
public double Velocity { get; set; }
public double Thrust { get; set; }
}
private class CustomMappingOutput {
public double LogVelocity { get; set; }
public double LogThrust { get; set; }
}
private class TransformedData : InputData {
public double LogVelocity { get; set; }
public double LogThrust { get; set; }
}
[CustomMappingFactoryAttribute("LogVelocity")]
private class LogVelocityCustomAction : CustomMappingFactory<InputData, CustomMappingOutput> {
public static void CustomAction(InputData input, CustomMappingOutput output)
=> output.LogVelocity = (float) Math.Log(input.Velocity);
public override Action<InputData, CustomMappingOutput> GetMapping() => CustomAction;
}
[CustomMappingFactoryAttribute("LogThrust")]
private class LogThrustCustomAction : CustomMappingFactory<InputData, CustomMappingOutput> {
public static void CustomAction(InputData input, CustomMappingOutput output)
=> output.LogThrust = (float)Math.Log(input.Thrust);
public override Action<InputData, CustomMappingOutput> GetMapping() => CustomAction;
}
public static void Run() {
var mlContext = new MLContext();
var samples = new List<InputData> {
new InputData { Velocity= 0.006467, Thrust = 1.614237 },
new InputData { Velocity= 0.53451, Thrust = 1.068356 },
new InputData { Velocity= 0.278578, Thrust = 0.216861 },
new InputData { Velocity= 0.014179, Thrust = 0.119712 },
new InputData { Velocity= 0.392814, Thrust = 3.915486 }
};
var data = mlContext.Data.LoadFromEnumerable(samples);
var pipeline = mlContext.Transforms.CustomMapping(new LogVelocityCustomAction().GetMapping(),
contractName: "LogVelocity")
.Append(mlContext.Transforms.CustomMapping(new LogThrustCustomAction().GetMapping(),
contractName: "LogThrust"));
var transformer = pipeline.Fit(data);
// Now save the transform pipeline so that it can be reloaded by another process.
mlContext.Model.Save(transformer, data.Schema, "customTransform.zip");
// We load the saved transform and use it, as if it was in another program.
var loadedTransform = mlContext.Model.Load("customTransform.zip", out _);
// Now we can transform the data.
var transformedIDataView = loadedTransform.Transform(data);
var newDataEnumerable = mlContext.Data.CreateEnumerable<TransformedData>(transformedIDataView,
reuseRowObject: true);
var newIDataView = mlContext.Data.LoadFromEnumerable(newDataEnumerable);
// Save newIDataView as a CSV file so we can verify the transformations.
var path = #"../../../transformed.csv";
var mlContextSave = new MLContext();
using var stream = File.Create(path);
mlContextSave.Data.SaveAsText(newIDataView,
stream,
separatorChar: ',',
headerRow: true,
schema: false);
}
static void Main() {
Program.Run();
Console.WriteLine("Hit return to exit");
Console.ReadLine();
}
}
}
At end of the program, the transformed IDataView is saved as a “.csv” file.
It looks like:
As you can see, the “Velocity” column was not transformed.
Any help or suggestions will be greatly appreciated.
Charles
You're casting the log to float, then assigning it to a variable of type double. Have you tried casting to double?
I'm not sure why it's working for thrust but not velocity. Perhaps the very small number in the first line of velocity is causing an exception.

How to write unit test for ActionFilter when using Service Locator

I am planning to write an ActionFilter for business validation and in which some services will be resolved via Service Locator(I know this is not good practice and as far as possible i avoid Service Locator pattern, but for this case i want to use it).
OnActionExecuting method of the filter is something like this:
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
// get validator for input;
var validator = actionContext.HttpContext.RequestServices.GetService<IValidator<TypeOfInput>>();// i will ask another question for this line
if(!validator.IsValid(input))
{
//send errors
}
}
Is it possible to write unit test for above ActionFilterand how?
Here is an sample on how to create a mock (using XUnit and Moq framework) to verify that the IsValid method is called and where the mock returns an false.
using Dealz.Common.Web.Tests.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using System;
using Xunit;
namespace Dealz.Common.Web.Tests.ActionFilters
{
public class TestActionFilter
{
[Fact]
public void ActionFilterTest()
{
/****************
* Setup
****************/
// Create the userValidatorMock
var userValidatorMock = new Mock<IValidator<User>>();
userValidatorMock.Setup(validator => validator
// For any parameter passed to IsValid
.IsValid(It.IsAny<User>())
)
// return false when IsValid is called
.Returns(false)
// Make sure that `IsValid` is being called at least once or throw error
.Verifiable();
// If provider.GetService(typeof(IValidator<User>)) gets called,
// IValidator<User> mock will be returned
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock.Setup(provider => provider.GetService(typeof(IValidator<User>)))
.Returns(userValidatorMock.Object);
// Mock the HttpContext to return a mockable
var httpContextMock = new Mock<HttpContext>();
httpContextMock.SetupGet(context => context.RequestServices)
.Returns(serviceProviderMock.Object);
var actionExecutingContext = HttpContextUtils.MockedActionExecutingContext(httpContextMock.Object, null);
/****************
* Act
****************/
var userValidator = new ValidationActionFilter<User>();
userValidator.OnActionExecuting(actionExecutingContext);
/****************
* Verify
****************/
// Make sure that IsValid is being called at least once, otherwise this throws an exception. This is a behavior test
userValidatorMock.Verify();
// TODO: Also Mock HttpContext.Response and return in it's Body proeprty a memory stream where
// your ActionFilter writes to and validate the input is what you desire.
}
}
class User
{
public string Username { get; set; }
}
class ValidationActionFilter<T> : IActionFilter where T : class, new()
{
public void OnActionExecuted(ActionExecutedContext context)
{
throw new NotImplementedException();
}
public void OnActionExecuting(ActionExecutingContext actionContext)
{
var type = typeof(IValidator<>).MakeGenericType(typeof(T));
var validator = (IValidator<T>)actionContext.HttpContext
.RequestServices.GetService<IValidator<T>>();
// Get your input somehow
T input = new T();
if (!validator.IsValid(input))
{
//send errors
actionContext.HttpContext.Response.WriteAsync("Error");
}
}
}
internal interface IValidator<T>
{
bool IsValid(T input);
}
}
HttpContextUtils.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Collections.Generic;
namespace Dealz.Common.Web.Tests.Utils
{
public class HttpContextUtils
{
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
IList<IFilterMetadata> filters,
IDictionary<string, object> actionArguments,
object controller
)
{
var actionContext = new ActionContext() { HttpContext = context };
return new ActionExecutingContext(actionContext, filters, actionArguments, controller);
}
public static ActionExecutingContext MockedActionExecutingContext(
HttpContext context,
object controller
)
{
return MockedActionExecutingContext(context, new List<IFilterMetadata>(), new Dictionary<string, object>(), controller);
}
}
}
As you can see, it's quite a mess, you need to create plenty of mocks to simulate different responses of the actuall classes, only to be able to test the ActionAttribute in isolation.
I like #Tseng's above answer but thought of giving one more answer as his answer covers more scenarios (like generics) and could be overwhelming for some users.
Here I have an action filter attribute which just checks the ModelState and short circuits(returns the response without the action being invoked) the request by setting the Result property on the context. Within the filter, I try to use the ServiceLocator pattern to get a logger to log some data(some might not like this but this is an example)
Filter
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ValidationFilterAttribute>>();
logger.LogWarning("some message here");
context.Result = new JsonResult(new InvalidData() { Message = "some messgae here" })
{
StatusCode = 400
};
}
}
}
public class InvalidData
{
public string Message { get; set; }
}
Unit Test
[Fact]
public void ValidationFilterAttributeTest_ModelStateErrors_ResultInBadRequestResult()
{
// Arrange
var serviceProviderMock = new Mock<IServiceProvider>();
serviceProviderMock
.Setup(serviceProvider => serviceProvider.GetService(typeof(ILogger<ValidationFilterAttribute>)))
.Returns(Mock.Of<ILogger<ValidationFilterAttribute>>());
var httpContext = new DefaultHttpContext();
httpContext.RequestServices = serviceProviderMock.Object;
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
var actionExecutingContext = new ActionExecutingContext(
actionContext,
filters: new List<IFilterMetadata>(), // for majority of scenarios you need not worry about populating this parameter
actionArguments: new Dictionary<string, object>(), // if the filter uses this data, add some data to this dictionary
controller: null); // since the filter being tested here does not use the data from this parameter, just provide null
var validationFilter = new ValidationFilterAttribute();
// Act
// Add an erorr into model state on purpose to make it invalid
actionContext.ModelState.AddModelError("Age", "Age cannot be below 18 years.");
validationFilter.OnActionExecuting(actionExecutingContext);
// Assert
var jsonResult = Assert.IsType<JsonResult>(actionExecutingContext.Result);
Assert.Equal(400, jsonResult.StatusCode);
var invalidData = Assert.IsType<InvalidData>(jsonResult.Value);
Assert.Equal("some messgae here", invalidData.Message);
}

Why Glass Mapper Returning Null Values?

I'm using Glass V4. I have a set up of MVC Web Area Project.
I have installed the Glass Mapper in the Main Project (WebProject).
I'm trying to do the Glass Casting in my Area Project.
public class ContactController : SitecoreController
{
private readonly ISitecoreContext _context;
private IGlassHtml _glassHtml;
public ContactController()
: this(new SitecoreContext())
{
}
public ContactController(ISitecoreContext context)
{
_context = context;
_glassHtml = new GlassHtml(context);
}
// GET: Contact
public ActionResult ContactUs()
{
var db = Sitecore.Context.Database;
var datasource = db.GetItem(RenderingContext.Current.Rendering.DataSource);
var ViewModel = new Models.ContactUs();
ViewModel.Headerstring = datasource.Fields["Headerstring"].Value;
ViewModel.Substring = datasource.Fields["Substring"].Value;
ViewModel.Description = ((MultilistField)datasource.Fields["Description"]).GetItems().Select(s => s.Fields["Line"].Value).ToList<string>();
return View(ViewModel);
}
public ActionResult ContactUsGlass()
{
var model = _context.GetCurrentItem<ContactUsGlassModel>();
return View(model);
}
}
I'm able to get the value with the First Action Method but not with the second.
Model:
public class ContactUs
{
public string Headerstring { get; set; }
public string Substring { get; set; }
public List<string> Description { get; set; }
}
Glass Model:
public class ContactUsGlassModel
{
public virtual string Headerstring { get; set; }
public virtual string Substring { get; set; }
}
I understand I don't need to register my Namespace in Glass V4.
You should not use _context.GetCurrentItem method. Use _context.GetItem instead:
public ActionResult ContactUsGlass()
{
var model = context.GetItem<ContactUsGlassModel>(RenderingContext.Current.Rendering.DataSource);
return View(model);
}
You don't want to get model from your Sitecore.Context.Item (which is used in GetCurrentItem method. You want to get your model from the DataSource of the current rendering.
What #Marek has answered is the right way of pulling the rendering item into model. GetCurrentItem by default gives the page item being served by Sitecore. If the fields that your model needs are fields of your page item then GetCurrentItem can also fill your model. If Datasource nesting is enabled, then if the datasource is not set on the rendering, Sitecore returns the page item again.
You can inherit from GlassController and then use GetLayoutItem() to get the datasorced item. If it's null then you need to publish the template in sitecore and make sure you mappings are correct if you are not using TDS :)

Unit Testing a RenderMvcController even possible?

So I'm working with Umbraco 6.12 and having great difficulty been able to test a RenderMvcController.
I have implemented IApplicationEventHandler in my Global.ascx and Ninject is working fine and as expected when running the application - all good.
However, unit testing these controllers is a different matter. I found this, and have added the latest reply:
http://issues.umbraco.org/issue/U4-1717
I now have this lovely hack in my SetUp:
Umbraco.Web.UmbracoContext.EnsureContext(new HttpContextWrapper(new HttpContext(new HttpRequest("", "http://www.myserver.com", ""), new HttpResponse(null))), ApplicationContext.Current);
Which has got around the original UmbracoContext cannot be null, but is now throwing:
Current has not been initialized on Umbraco.Web.PublishedCache.PublishedCachesResolver. You must initialize Current before trying to read it.
The published caches resolver also seems to be hidden behind internal and protected stuff, which I can't use reflection to hack at as I can't init anything to pass into SetProperty reflection.
It's really frustrating, I'm loving v6, and using uMapper is very nice. I can inject a repo, service, command or query at will into the controllers and life is good - I just can't cover the controllers!
Any help on this would be greatly appreciated.
Thanks.
To unit test a Umbraco RenderMvcController, you need to grab the source code from github, compile the solution yourself, and get the Umbraco.Tests.dll and reference it on your test project.
In addition to that, you need to reference the SQLCE4Umbraco.dll which is distributed with the Umbraco packages, and Rhino.Mocks.dll which is internally for mocking.
To help you with this, I have compiled put the Umbraco.Tests.dll for Umbraco 6.1.5 and put it together with the Rhino.Mocks.dll and put it on this zip file.
Finally, derive your test from BaseRoutingTest, override the DatabaseTestBehavior to
NoDatabasePerFixture, and get the UmbracoContext and HttpBaseContext by calling the GetRoutingContext method, as in the code below:
using System;
using Moq;
using NUnit.Framework;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Routing;
using Umbraco.Core.Models;
using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
namespace UnitTests.Controllers
{
public class Entry
{
public int Id { get; set; }
public string Url { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public string[] Tags { get; set; }
public DateTime Date { get; set; }
}
public interface IBlogService
{
Entry GetBlogEntry(int id);
}
public class BlogEntryController : RenderMvcController
{
private readonly IBlogService _blogService;
public BlogEntryController(IBlogService blogService, UmbracoContext ctx)
: base(ctx)
{
_blogService = blogService;
}
public BlogEntryController(IBlogService blogService)
: this(blogService, UmbracoContext.Current)
{
}
public override ActionResult Index(RenderModel model)
{
var entry = _blogService.GetBlogEntry(model.Content.Id);
// Test will fail if we return CurrentTemplate(model) as is expecting
// the action from ControllerContext.RouteData.Values["action"]
return View("BlogEntry", entry);
}
}
[TestFixture]
public class RenderMvcControllerTests : BaseRoutingTest
{
protected override DatabaseBehavior DatabaseTestBehavior
{
get { return DatabaseBehavior.NoDatabasePerFixture; }
}
[Test]
public void CanGetIndex()
{
const int id = 1234;
var content = new Mock<IPublishedContent>();
content.Setup(c => c.Id).Returns(id);
var model = new RenderModel(content.Object, CultureInfo.InvariantCulture);
var blogService = new Mock<IBlogService>();
var entry = new Entry { Id = id };
blogService.Setup(s => s.GetBlogEntry(id)).Returns(entry);
var controller = GetBlogEntryController(blogService.Object);
var result = (ViewResult)controller.Index(model);
blogService.Verify(s => s.GetBlogEntry(id), Times.Once());
Assert.IsNotNull(result);
Assert.IsAssignableFrom<Entry>(result.Model);
}
private BlogEntryController GetBlogEntryController(IBlogService blogService)
{
var routingContext = GetRoutingContext("/test");
var umbracoContext = routingContext.UmbracoContext;
var contextBase = umbracoContext.HttpContext;
var controller = new BlogEntryController(blogService, umbracoContext);
controller.ControllerContext = new ControllerContext(contextBase, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(contextBase, new RouteData()), new RouteCollection());
return controller;
}
}
}
This code has only been tested in Umbraco 6.1.5.
According to the core team, you should include the Umbraco.Tests library and inherit your test from BaseUmbracoApplicationTest. That will setup a valid UmbracoApplication and UmbracoContext.
https://groups.google.com/forum/?fromgroups=#!topic/umbraco-dev/vEjdzjqmtsU
I have raised this on the Umbraco forums and there are several replies which may help you.
See here:
http://our.umbraco.org/forum/developers/api-questions/37255-How-can-I-unit-test-a-class-inheriting-from-SurfaceController
Essentially, you can .. just ... but requires some reflection because some of the key classes and interfaces are internal. As Luke's last post points out, this is because the functionality is currently a bit of a moving target.