[{"data":1,"prerenderedAt":1068},["ShallowReactive",2],{"technical-blog-page-content":3,"technical-blog-post-an-introduction-to-ml-net-and-the-functions-it-performs":30,"technical-blog-author-an-introduction-to-ml-net-and-the-functions-it-performs":1056},{"id":4,"title":5,"author":6,"blogTags":6,"body":7,"customExcerpt":6,"date":6,"description":11,"excerpt":6,"extension":14,"h1":15,"image":6,"meta":16,"navigation":17,"path":18,"readingStats":19,"seo":24,"stem":28,"__hash__":29},"technicalBlogPage\u002Ftechnical-blog-page.md","Technical Blog Page",null,{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"md","Technical insights, stories and opinions from our team of consultants, analysts, developers and testers.",{},true,"\u002Ftechnical-blog-page",{"text":20,"minutes":21,"time":22,"words":23},"1 min read",0.29,17400,58,{"title":25,"description":26,"keywords":27},"Technology Insights | Audacia Digital Transformation","Technical insights, stories and opinions from leading software development company Audacia's teams of consultants, analysts, developers and testers.","Digital transformation technology, Technology blog, Technology insights, software development blog, software engineering blog","technical-blog-page","XD1gWfEur9Mu64WptQ5pF-e7Ej60LGiq7sRC58wOAeQ",{"id":31,"title":32,"author":33,"blogTags":34,"body":38,"customExcerpt":1040,"date":1041,"description":43,"excerpt":6,"extension":14,"h1":6,"image":1042,"meta":1043,"navigation":17,"path":1044,"readingStats":1045,"seo":1050,"stem":1054,"__hash__":1055},"technicalBlog\u002Ftechnical-blog\u002Fan-introduction-to-ml-net-and-the-functions-it-performs.md","Developing Machine Learning services: An intro to ML.NET","Owen Lacey",[35,36,37],"NET","AI","Machine Learning",{"type":8,"value":39,"toc":1029},[40,44,47,52,69,72,84,87,94,101,114,128,131,195,204,210,213,379,386,394,406,429,432,441,548,555,558,571,625,628,635,638,644,710,717,752,755,790,813,892,901,905,908,913,916,927,931,946,949,953,960,963,967,974,981,985,994,997,1000,1004,1007,1010,1017,1025],[41,42,43],"p",{},"No longer must solutions to mathematical problems be written in 'X' (insert Python\u002FC++\u002FR here). With the increasing uptake of integrating AI and optimisation tools in software, it has never been easier for developers to learn more about these topics without having to completely abandon their tech stack.",[41,45,46],{},"Using ML.NET, developers can upskill on a technology that's becoming more and more commonplace in development. What's more is that this upskilling can be done from the comfort of a  .NET application.",[48,49,51],"h2",{"id":50},"an-introduction-to-mlnet-in-delivering-machine-learning-services","An introduction to ML.NET in delivering machine learning services",[41,53,54,55,62,63,68],{},"ML.NET was ",[56,57,61],"a",{"href":58,"rel":59},"https:\u002F\u002Fdevblogs.microsoft.com\u002Fdotnet\u002Fintroducing-ml-net-cross-platform-proven-and-open-source-machine-learning-framework\u002F",[60],"nofollow","first released in 2018",". It does exactly what the name implies: Machine Learning in .NET. It's cross-platform and ",[56,64,67],{"href":65,"rel":66},"https:\u002F\u002Fgithub.com\u002Fdotnet\u002Fmachinelearning",[60],"open source",", all the usual characteristics we've come to expect with Microsoft in recent years.",[41,70,71],{},"First and foremost, ML.NET is a framework for building machine learning models. It provides an API to define, build and train your Machine Learning pipeline. It abstracts out the parts that would otherwise alienate someone without a degree in data science.",[41,73,74,75,79,80,83],{},"If you have a good understanding of ",[76,77,78],"em",{},"what"," makes a Machine Learning model good, rather than ",[76,81,82],{},"how"," to get there, you can absolutely use ML.NET.",[41,85,86],{},"Microsoft's website outlines the usual workflow of creating a model in ML.NET:",[41,88,89],{},[90,91],"img",{"alt":92,"src":93},"Training flow chart example ","\u002Fimg\u002Ftechnical-blog\u002Fdiaramml.png",[41,95,96,100],{},[97,98,99],"strong",{},"1. Load data",": The API provides a number of ways to do this to suit your needs, including (but not limited to):",[102,103,104,108,111],"ol",{},[105,106,107],"li",{},"CSV",[105,109,110],{},"In-memory",[105,112,113],{},"SQL",[41,115,116,117,121,122,127],{},"The full list of ways to load data into an ",[118,119,120],"code",{},"IDataView"," can be found ",[56,123,126],{"href":124,"rel":125},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fmachine-learning\u002Fhow-to-guides\u002Fload-data-ml-net",[60],"here",".",[41,129,130],{},"Assume we were loading from an enumerable (i.e In-memory):",[132,133,137],"pre",{"className":134,"code":135,"language":136,"meta":11,"style":11},"language-csharp shiki shiki-themes github-light","IEnumerable\u003CModelInput> trainingData = await _dataRepository.GetTrainingDataAsync();\nIDataView dataView = MlContext.Data.LoadFromEnumerable(trainingData);\n","csharp",[118,138,139,177],{"__ignoreMap":11},[140,141,144,148,152,155,158,161,165,168,171,174],"span",{"class":142,"line":143},"line",1,[140,145,147],{"class":146},"s7eDp","IEnumerable",[140,149,151],{"class":150},"sgsFI","\u003C",[140,153,154],{"class":146},"ModelInput",[140,156,157],{"class":150},"> ",[140,159,160],{"class":146},"trainingData",[140,162,164],{"class":163},"sD7c4"," =",[140,166,167],{"class":163}," await",[140,169,170],{"class":150}," _dataRepository.",[140,172,173],{"class":146},"GetTrainingDataAsync",[140,175,176],{"class":150},"();\n",[140,178,179,181,184,186,189,192],{"class":142,"line":12},[140,180,120],{"class":146},[140,182,183],{"class":146}," dataView",[140,185,164],{"class":163},[140,187,188],{"class":150}," MlContext.Data.",[140,190,191],{"class":146},"LoadFromEnumerable",[140,193,194],{"class":150},"(trainingData);\n",[41,196,197,200,201,203],{},[97,198,199],{},"2. Create pipeline",": This step involves defining how we get from an ",[118,202,120],{}," to something that can predict an output. In this step you can concatenate and normalise your features to match the data and algorithm you're using, but most importantly you need to label which properties of your data are 'features', and which property is the 'label'.",[41,205,206],{},[90,207],{"alt":208,"src":209},"Features and labels","\u002Fimg\u002Ftechnical-blog\u002Ffeatures-and-labels.png",[41,211,212],{},"The API defines a builder pattern for appending to your pipeline which makes it easy for developers to customise how the pipeline is created:",[132,214,216],{"className":134,"code":215,"language":136,"meta":11,"style":11},"IEstimator\u003CITransformer> pipeline = MlContext.Transforms\n    \u002F\u002F Combine the features into a single features vector\n    .Concatenate(\"Features\", new[] { \"Feature0\", \"Feature1\", \"Feature2\" })\n    \u002F\u002F Normalize the features\n    .Append(MlContext.Transforms.NormalizeMinMax(\"Features\"))\n    \u002F\u002F ...\n    \u002F\u002F Other 'Append's as necessary\n    \u002F\u002F ...\n    \u002F\u002F Append the training algorithm (in this case using a regression algorithm).\n    .Append(MlContext.Regression.Trainers.LightGbm(\n        labelColumnName: \"Label\",\n        featureColumnName: \"Features\"));\n",[118,217,218,238,244,285,291,312,318,324,329,335,351,366],{"__ignoreMap":11},[140,219,220,223,225,228,230,233,235],{"class":142,"line":143},[140,221,222],{"class":146},"IEstimator",[140,224,151],{"class":150},[140,226,227],{"class":146},"ITransformer",[140,229,157],{"class":150},[140,231,232],{"class":146},"pipeline",[140,234,164],{"class":163},[140,236,237],{"class":150}," MlContext.Transforms\n",[140,239,240],{"class":142,"line":12},[140,241,243],{"class":242},"sAwPA","    \u002F\u002F Combine the features into a single features vector\n",[140,245,247,250,253,256,260,263,266,269,272,274,277,279,282],{"class":142,"line":246},3,[140,248,249],{"class":150},"    .",[140,251,252],{"class":146},"Concatenate",[140,254,255],{"class":150},"(",[140,257,259],{"class":258},"sYBdl","\"Features\"",[140,261,262],{"class":150},", ",[140,264,265],{"class":163},"new",[140,267,268],{"class":150},"[] { ",[140,270,271],{"class":258},"\"Feature0\"",[140,273,262],{"class":150},[140,275,276],{"class":258},"\"Feature1\"",[140,278,262],{"class":150},[140,280,281],{"class":258},"\"Feature2\"",[140,283,284],{"class":150}," })\n",[140,286,288],{"class":142,"line":287},4,[140,289,290],{"class":242},"    \u002F\u002F Normalize the features\n",[140,292,294,296,299,302,305,307,309],{"class":142,"line":293},5,[140,295,249],{"class":150},[140,297,298],{"class":146},"Append",[140,300,301],{"class":150},"(MlContext.Transforms.",[140,303,304],{"class":146},"NormalizeMinMax",[140,306,255],{"class":150},[140,308,259],{"class":258},[140,310,311],{"class":150},"))\n",[140,313,315],{"class":142,"line":314},6,[140,316,317],{"class":242},"    \u002F\u002F ...\n",[140,319,321],{"class":142,"line":320},7,[140,322,323],{"class":242},"    \u002F\u002F Other 'Append's as necessary\n",[140,325,327],{"class":142,"line":326},8,[140,328,317],{"class":242},[140,330,332],{"class":142,"line":331},9,[140,333,334],{"class":242},"    \u002F\u002F Append the training algorithm (in this case using a regression algorithm).\n",[140,336,338,340,342,345,348],{"class":142,"line":337},10,[140,339,249],{"class":150},[140,341,298],{"class":146},[140,343,344],{"class":150},"(MlContext.Regression.Trainers.",[140,346,347],{"class":146},"LightGbm",[140,349,350],{"class":150},"(\n",[140,352,354,357,360,363],{"class":142,"line":353},11,[140,355,356],{"class":146},"        labelColumnName",[140,358,359],{"class":150},": ",[140,361,362],{"class":258},"\"Label\"",[140,364,365],{"class":150},",\n",[140,367,369,372,374,376],{"class":142,"line":368},12,[140,370,371],{"class":146},"        featureColumnName",[140,373,359],{"class":150},[140,375,259],{"class":258},[140,377,378],{"class":150},"));\n",[41,380,381,382,385],{},"You can consider this step as a declaring a 'pointer' from your test data to the trained model. It's data-agnostic, meaning the only time you need to consider the actual data you're training with is when you call ",[118,383,384],{},"Fit()"," (below).",[132,387,392],{"className":388,"code":390,"language":391},[389],"language-text","**NB:** If you're wondering why the `LightGbm` trainer was used, as oppposed to the many other options for regression, this is because when the Model Builder tool creates your model, it attempts all algorithms appropriate to the model you're trying to build, and uses the trainer with the best score. When using the Model Builder yourself, you may find it lands on a different trainer for your example - which would be completely fine.\n","text",[118,393,390],{"__ignoreMap":11},[41,395,396,399,400,402,403,405],{},[97,397,398],{},"3. Train model",": Now we have both our ",[118,401,222],{}," and ",[118,404,120],{}," we can combine them to output a trained Machine Learning model:",[132,407,409],{"className":134,"code":408,"language":136,"meta":11,"style":11},"ITransformer model = pipeline.Fit(dataView);\n",[118,410,411],{"__ignoreMap":11},[140,412,413,415,418,420,423,426],{"class":142,"line":143},[140,414,227],{"class":146},[140,416,417],{"class":146}," model",[140,419,164],{"class":163},[140,421,422],{"class":150}," pipeline.",[140,424,425],{"class":146},"Fit",[140,427,428],{"class":150},"(dataView);\n",[41,430,431],{},"As you might have guessed, this step is the most time and resource intensive. You'll definitely want to capture the usage metrics of this steps before deploying it to any production environments.",[41,433,434,437,438,440],{},[97,435,436],{},"4. Improve model",": The ",[118,439,425],{}," method doesn't give any information as to how well the model performs. In order to get an idea of this, you can evaluate your model as below:",[132,442,444],{"className":134,"code":443,"language":136,"meta":11,"style":11},"    \u002F\u002F Optional: evaluate the model based on a different set of data than the one trained with.\n    \u002F\u002F See also: Cross-Validating your model.\n    IEnumerable\u003CModelInput> testData = await _dataRepository.GetTestDataAsync();\n    IDataView dataView = MlContext.Data.LoadFromEnumerable(testData);\n\n    \u002F\u002F Reminder: `model` is our trained ML model (`ITransformer`)\n    var testDataPredictions = model.Transform(dataView);\n\n    RegressionMetrics metrics = MlContext.Regression.Evaluate(testDataPredictions);\n",[118,445,446,451,456,481,497,502,507,525,529],{"__ignoreMap":11},[140,447,448],{"class":142,"line":143},[140,449,450],{"class":242},"    \u002F\u002F Optional: evaluate the model based on a different set of data than the one trained with.\n",[140,452,453],{"class":142,"line":12},[140,454,455],{"class":242},"    \u002F\u002F See also: Cross-Validating your model.\n",[140,457,458,461,463,465,467,470,472,474,476,479],{"class":142,"line":246},[140,459,460],{"class":146},"    IEnumerable",[140,462,151],{"class":150},[140,464,154],{"class":146},[140,466,157],{"class":150},[140,468,469],{"class":146},"testData",[140,471,164],{"class":163},[140,473,167],{"class":163},[140,475,170],{"class":150},[140,477,478],{"class":146},"GetTestDataAsync",[140,480,176],{"class":150},[140,482,483,486,488,490,492,494],{"class":142,"line":287},[140,484,485],{"class":146},"    IDataView",[140,487,183],{"class":146},[140,489,164],{"class":163},[140,491,188],{"class":150},[140,493,191],{"class":146},[140,495,496],{"class":150},"(testData);\n",[140,498,499],{"class":142,"line":293},[140,500,501],{"emptyLinePlaceholder":17},"\n",[140,503,504],{"class":142,"line":314},[140,505,506],{"class":242},"    \u002F\u002F Reminder: `model` is our trained ML model (`ITransformer`)\n",[140,508,509,512,515,517,520,523],{"class":142,"line":320},[140,510,511],{"class":163},"    var",[140,513,514],{"class":146}," testDataPredictions",[140,516,164],{"class":163},[140,518,519],{"class":150}," model.",[140,521,522],{"class":146},"Transform",[140,524,428],{"class":150},[140,526,527],{"class":142,"line":326},[140,528,501],{"emptyLinePlaceholder":17},[140,530,531,534,537,539,542,545],{"class":142,"line":331},[140,532,533],{"class":146},"    RegressionMetrics",[140,535,536],{"class":146}," metrics",[140,538,164],{"class":163},[140,540,541],{"class":150}," MlContext.Regression.",[140,543,544],{"class":146},"Evaluate",[140,546,547],{"class":150},"(testDataPredictions);\n",[41,549,550,551,554],{},"Obviously, this example is for a regression model and the ",[118,552,553],{},"RegressionMetrics"," model outputs information useful for evaluating a regression algorithm (for example, the R-squared value), but the same method is exposed for other algorithms like Classification and Forecasting.",[41,556,557],{},"At this point, you decide whether your model is good enough.",[41,559,560,563,564,566,567,570],{},[97,561,562],{},"5. Save model",": Once we're happy with how the model is performing, we can save this version of the model. Keep in mind, it's the ",[118,565,227],{}," object (the trained) that we're persisting, so that's the object we'll be passing through to the ",[118,568,569],{},"Save"," method:",[132,572,574],{"className":134,"code":573,"language":136,"meta":11,"style":11},"    const string directory = \"NAME_OF_LOCAL_DIRECTORY_HERE\";\n    var modelFilePath = $\"{directory}\u002Fmodel.zip\";\n    context.Model.Save(model, testData.Schema, modelFilePath);\n",[118,575,576,595,615],{"__ignoreMap":11},[140,577,578,581,584,587,589,592],{"class":142,"line":143},[140,579,580],{"class":163},"    const",[140,582,583],{"class":163}," string",[140,585,586],{"class":146}," directory",[140,588,164],{"class":163},[140,590,591],{"class":258}," \"NAME_OF_LOCAL_DIRECTORY_HERE\"",[140,593,594],{"class":150},";\n",[140,596,597,599,602,604,607,610,613],{"class":142,"line":12},[140,598,511],{"class":163},[140,600,601],{"class":146}," modelFilePath",[140,603,164],{"class":163},[140,605,606],{"class":258}," $\"{",[140,608,609],{"class":150},"directory",[140,611,612],{"class":258},"}\u002Fmodel.zip\"",[140,614,594],{"class":150},[140,616,617,620,622],{"class":142,"line":246},[140,618,619],{"class":150},"    context.Model.",[140,621,569],{"class":146},[140,623,624],{"class":150},"(model, testData.Schema, modelFilePath);\n",[41,626,627],{},"As you can see, the name of the model and location you'll be able to find it is entirely up to you. You can utilize this flexibility should you want to date-stamp or version-label your models, and persist older versions of the trained model. The ability to rollback to previous versions of the model would be very useful in a production environment.",[41,629,630,631,634],{},"Similarly, being able to source-control your models, and deploying them as an embedded resource in a ",[118,632,633],{},".csproj"," is another, perhaps cleaner way in ensuring this behaviour.",[41,636,637],{},"This brings an end to the training lifecycle of an ML.NET model. In the next steps we'll go into how you can use this model to make real-time predictions.",[41,639,640,643],{},[97,641,642],{},"6. Load model",":\nIn a very similar vein, we can reload our model later using the same file path.",[132,645,647],{"className":134,"code":646,"language":136,"meta":11,"style":11},"const string directory = \"NAME_OF_LOCAL_DIRECTORY_HERE\";\nvar modelFilePath = $\"{directory}\u002Fmodel.zip\";\nvar model = context.Model.Load(modelPath, out var dataViewSchema);\n",[118,648,649,664,681],{"__ignoreMap":11},[140,650,651,654,656,658,660,662],{"class":142,"line":143},[140,652,653],{"class":163},"const",[140,655,583],{"class":163},[140,657,586],{"class":146},[140,659,164],{"class":163},[140,661,591],{"class":258},[140,663,594],{"class":150},[140,665,666,669,671,673,675,677,679],{"class":142,"line":12},[140,667,668],{"class":163},"var",[140,670,601],{"class":146},[140,672,164],{"class":163},[140,674,606],{"class":258},[140,676,609],{"class":150},[140,678,612],{"class":258},[140,680,594],{"class":150},[140,682,683,685,687,689,692,695,698,701,704,707],{"class":142,"line":246},[140,684,668],{"class":163},[140,686,417],{"class":146},[140,688,164],{"class":163},[140,690,691],{"class":150}," context.Model.",[140,693,694],{"class":146},"Load",[140,696,697],{"class":150},"(modelPath, ",[140,699,700],{"class":163},"out",[140,702,703],{"class":163}," var",[140,705,706],{"class":146}," dataViewSchema",[140,708,709],{"class":150},");\n",[41,711,712,713,716],{},"This model can then be used to create a ",[76,714,715],{},"prediction engine","; something that can convert a input into a predicted output.",[132,718,720],{"className":134,"code":719,"language":136,"meta":11,"style":11},"services.AddPredictionEnginePool\u003CModelOutput, ModelOutput>()\n    .FromFile(modelFilePath));\n",[118,721,722,742],{"__ignoreMap":11},[140,723,724,727,730,732,735,737,739],{"class":142,"line":143},[140,725,726],{"class":150},"services.",[140,728,729],{"class":146},"AddPredictionEnginePool",[140,731,151],{"class":150},[140,733,734],{"class":146},"ModelOutput",[140,736,262],{"class":150},[140,738,734],{"class":146},[140,740,741],{"class":150},">()\n",[140,743,744,746,749],{"class":142,"line":12},[140,745,249],{"class":150},[140,747,748],{"class":146},"FromFile",[140,750,751],{"class":150},"(modelFilePath));\n",[41,753,754],{},"It's also important to note that if the model was stored elsewhere, the prediction engine can be loaded from a provided URI.",[132,756,758],{"className":134,"code":757,"language":136,"meta":11,"style":11},"services.AddPredictionEnginePool\u003CModelOutput, ModelOutput>()\n    .FromUri(\"URL_TO_YOUR_MODEL\");\n",[118,759,760,776],{"__ignoreMap":11},[140,761,762,764,766,768,770,772,774],{"class":142,"line":143},[140,763,726],{"class":150},[140,765,729],{"class":146},[140,767,151],{"class":150},[140,769,734],{"class":146},[140,771,262],{"class":150},[140,773,734],{"class":146},[140,775,741],{"class":150},[140,777,778,780,783,785,788],{"class":142,"line":12},[140,779,249],{"class":150},[140,781,782],{"class":146},"FromUri",[140,784,255],{"class":150},[140,786,787],{"class":258},"\"URL_TO_YOUR_MODEL\"",[140,789,709],{"class":150},[41,791,792,795,796,798,799,802,803,806,807,809,810,812],{},[97,793,794],{},"7. Make predictions",": In order to make a prediction for a single example, you need to use your loaded model to create a ",[76,797,715],{},".  A ",[118,800,801],{},"PredictionEngine"," has a single public method called ",[118,804,805],{},"Predict",", which takes your ",[118,808,154],{}," and generates a ",[118,811,734],{}," containing the predicted output.",[132,814,816],{"className":134,"code":815,"language":136,"meta":11,"style":11},"var modelInput = new ModelInput();\u002F\u002FGet\u002Fcreate your input object here.\n\nvar predictionEngine = context.Model.CreatePredictionEngine\u003CModelInput, ModelOutput>(model, true, schema);\nvar output = predictionEngine.Predict(modelInput);\n",[118,817,818,839,843,875],{"__ignoreMap":11},[140,819,820,822,825,827,830,833,836],{"class":142,"line":143},[140,821,668],{"class":163},[140,823,824],{"class":146}," modelInput",[140,826,164],{"class":163},[140,828,829],{"class":163}," new",[140,831,832],{"class":146}," ModelInput",[140,834,835],{"class":150},"();",[140,837,838],{"class":242},"\u002F\u002FGet\u002Fcreate your input object here.\n",[140,840,841],{"class":142,"line":12},[140,842,501],{"emptyLinePlaceholder":17},[140,844,845,847,850,852,854,857,859,861,863,865,868,872],{"class":142,"line":246},[140,846,668],{"class":163},[140,848,849],{"class":146}," predictionEngine",[140,851,164],{"class":163},[140,853,691],{"class":150},[140,855,856],{"class":146},"CreatePredictionEngine",[140,858,151],{"class":150},[140,860,154],{"class":146},[140,862,262],{"class":150},[140,864,734],{"class":146},[140,866,867],{"class":150},">(model, ",[140,869,871],{"class":870},"sYu0t","true",[140,873,874],{"class":150},", schema);\n",[140,876,877,879,882,884,887,889],{"class":142,"line":287},[140,878,668],{"class":163},[140,880,881],{"class":146}," output",[140,883,164],{"class":163},[140,885,886],{"class":150}," predictionEngine.",[140,888,805],{"class":146},[140,890,891],{"class":150},"(modelInput);\n",[41,893,894,895,897,898,900],{},"This quite simple process shows you how to predict a single value from your trained model. As discussed above, this doesn't necessary need to be a local model, and the ",[118,896,729],{}," allows you to inject your ",[118,899,801],{}," to allow you to utilise this behaviour in a separate application layer, if necessary.",[48,902,904],{"id":903},"future-proofing-your-machine-learning-models","Future-proofing your machine learning models",[41,906,907],{},"The above walkthrough has shown a very happy-path way of creating a model in ML.NET, but how can you be sure to give your model the best chance of retaining its benefit in future? How will this model react to new, perhaps unexpected data? The below tips are the best practice we've found in implementing ML.NET in enterprise applications:",[909,910,912],"h3",{"id":911},"re-train-periodically","Re-train periodically",[41,914,915],{},"Say the 'type' of data the model constantly predicts changes slightly. In order for your model to adapt and be able to accurately predict this new information, it's vital that the model is re-trained on this new data set.",[41,917,918,919,922,923,926],{},"We saw in the above steps how to train (",[118,920,921],{},".Fit()",") and persist (",[118,924,925],{},".Save()",") your model - perhaps you can implement some sort of versioning mechanism to replace the old worse-performing model should you re-train the model and fit it performs better on the most up-to-date dataset.",[909,928,930],{"id":929},"investigate-prediction-errors-to-make-your-model-more-robust","Investigate prediction errors to make your model more robust",[41,932,933,934,937,938,941,942,945],{},"Each time your model predicts something wrong, that is to say the ",[76,935,936],{},"predicted"," value was very different to the ",[76,939,940],{},"actual"," value, ask yourself ",[76,943,944],{},"why",". With the information the model has, what extra information could it have had to make this prediction more accurate?",[41,947,948],{},"This is a great example of the symbiotic relationship that AI and human experts can have; it's not just the model learning from the human, it also can very much work in the other way!",[909,950,952],{"id":951},"permutation-feature-importance","Permutation Feature Importance",[41,954,955,959],{},[56,956,952],{"href":957,"rel":958},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fdotnet\u002Fmachine-learning\u002Fhow-to-guides\u002Fexplain-machine-learning-model-permutation-feature-importance-ml-net",[60]," explains the 'weight' of each feature in terms of how much say it has in predicting a final value.",[41,961,962],{},"This allows you to order your features (or 'inputs') by the most important to the least - it could be that you're spending a lot of time and resources collecting data that the model deems fairly low on the priority list, or even ignoring completely. This information can be vital in increaasing the efficiency of the training and usage of your model.",[909,964,966],{"id":965},"establish-a-learning-curve","Establish a learning curve",[41,968,969,970,973],{},"A ",[76,971,972],{},"Learning Curve"," measures how much your model learns as a factor of how many training examples are used.",[41,975,976,977,980],{},"Crucially, this information helps you determine the number at which your model ",[76,978,979],{},"stops"," learning. Why spend hours collecting data for millions of examples, when a smaller amount outputs a model that performs identically that is trained in a fraction of the time?",[909,982,984],{"id":983},"beta-users","BETA users",[41,986,987,988,993],{},"Using something like ",[56,989,992],{"href":990,"rel":991},"https:\u002F\u002Fdocs.microsoft.com\u002Fen-us\u002Fazure\u002Fazure-app-configuration\u002Fuse-feature-flags-dotnet-core?tabs=core5x",[60],"Feature Flags"," can be a great way to turn on the machine learning model for a fraction of your user base.",[41,995,996],{},"Trust is vitally important for consumer buy-in, especially with something like AI. With a targeted amount of BETA users using the model in the software, you can help build confidence in your user base, and perhaps spot any teething issues with using the model in a production environment.",[41,998,999],{},"This risk-minimizing approach will undoubtedly help you get your ML.NET model off the ground.",[48,1001,1003],{"id":1002},"conclusion","Conclusion",[41,1005,1006],{},"In this post we've introduced ML.NET and the functions it performs, and shown a simple example of how to use this to build a Linear Regression model.",[41,1008,1009],{},"With the increasing implementation of AI-based solutions in software projects, we're excited to see the sort of applications this technology can have, as well as providing opportunities to upskill our employees in this extremely interesting area of technology.",[41,1011,1012,1016],{},[56,1013,1015],{"href":1014},"\u002F","Audacia"," is a software development company based in the UK, headquartered in Leeds. View more technical insights from our teams of consultants, business analysts, developers and testers on our technology insights blog.",[56,1018,1022],{"className":1019,"href":1021},[1020],"blog-button-block","https:\u002F\u002Faudacia.co.uk\u002Ftechnical-blog",[41,1023,1024],{},"Technology Insights",[1026,1027,1028],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}",{"title":11,"searchDepth":12,"depth":12,"links":1030},[1031,1032,1039],{"id":50,"depth":12,"text":51},{"id":903,"depth":12,"text":904,"children":1033},[1034,1035,1036,1037,1038],{"id":911,"depth":246,"text":912},{"id":929,"depth":246,"text":930},{"id":951,"depth":246,"text":952},{"id":965,"depth":246,"text":966},{"id":983,"depth":246,"text":984},{"id":1002,"depth":12,"text":1003},"We introduce web framework ML.NET, the functions it performs, and show a simple example of how to use ML.NET to build a Linear Regression model. ","2022-07-29T13:04:04.708Z","\u002Fimg\u002Ftechnical-blog\u002Fintrotomlnet.jpg",{},"\u002Ftechnical-blog\u002Fan-introduction-to-ml-net-and-the-functions-it-performs",{"text":1046,"minutes":1047,"time":1048,"words":1049},"9 min read",8.95,537000,1790,{"description":1051,"keywords":1052,"title":1053},"Principal Consultant at software development company Audacia, Owen Lacey, gives an intro to ML.NET, its functions and how to build a linear regression model. \n\n","ML, .NET, Machine Learning, software engineering, software development company","Developing Machine Learning services: An intro to ML.NET | Audacia","technical-blog\u002Fan-introduction-to-ml-net-and-the-functions-it-performs","Cl_ggPvQU7q-C5VmV6-tAVvZbyMfgOO2xwz4KXJkl84",{"id":1057,"title":33,"bio":1058,"extension":14,"image":1059,"meta":1060,"stem":1066,"__hash__":1067},"author\u002Fauthors\u002Fowen-lacey.md","Owen Lacey was a Principal Software Consultant at Audacia for over 8 years. He worked across a number of industries as a developer, including manufacturing, automotive repairs and no-code development. As well as development, he oversaw the delivery of a number of projects, and was involved in consultancy for more advanced features such as machine learning and Google OR Tools.","\u002Fimg\u002Fauthors\u002Fowenheadshot.jpg",{"path":1061,"description":11,"body":1062},"\u002Fauthors\u002Fowen-lacey",{"type":8,"value":1063,"toc":1064},[],{"title":11,"searchDepth":12,"depth":12,"links":1065},[],"authors\u002Fowen-lacey","p9bCaZuwDvQLbcJQDSmEWAYqzx-Ycg3DaJgRALNfKLo",1778145943582]