-
Notifications
You must be signed in to change notification settings - Fork 1.9k
CollectionDataSource (train on top of memory collection instead of loading data from file) #106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
f69d659
55b6e46
b166f05
a1761b1
12d1b9e
ebcf448
110e205
62ab575
1da42ca
0cac7dc
ca9c031
d78afa3
ab86b09
ebe6f33
04ff469
9698d19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,7 +14,7 @@ namespace Microsoft.ML.Runtime.Api | |
| /// <summary> | ||
| /// A helper class to create data views based on the user-provided types. | ||
| /// </summary> | ||
| internal static class DataViewConstructionUtils | ||
| public static class DataViewConstructionUtils | ||
| { | ||
| public static IDataView CreateFromList<TRow>(IHostEnvironment env, IList<TRow> data, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Since we in this file, @tfinley@gmail.com do you have any opinion about IList?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why you would prefer So the reason I would prefer a specific implementer of an interface rather than the interface itself is to avoid having to go through the virtual function table. (Which is why it's often better to write In reply to: 187205023 [](ancestors = 187205023)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess its just personal. I just don't like IList. In same time, IList allow you to pass Arrays, so this solve problem with Array support. In reply to: 187207615 [](ancestors = 187207615,187205023) |
||
| SchemaDefinition schemaDefinition = null) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,7 @@ namespace Microsoft.ML.Runtime.Api | |
| /// <summary> | ||
| /// An internal class that holds the (already validated) mapping between a custom type and an IDataView schema. | ||
| /// </summary> | ||
| internal sealed class InternalSchemaDefinition | ||
| public sealed class InternalSchemaDefinition | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I have similar concerns about this class being public. If we can avoid it, possibly by moving some of the logic current in |
||
| { | ||
| public readonly Column[] Columns; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Text; | ||
| using Microsoft.ML.Runtime; | ||
| using Microsoft.ML.Runtime.Api; | ||
| using Microsoft.ML.Runtime.Data; | ||
| using Microsoft.ML.Runtime.EntryPoints; | ||
|
|
||
| namespace Microsoft.ML | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ML.Data #Closed
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TextLoader is part of just ML, should I change it as well? In reply to: 187221578 [](ancestors = 187221578)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good question... seems they both should be in data. the argument went about like this - can a user has out of the box experience with just ML namespace. In reply to: 187221799 [](ancestors = 187221799,187221578)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move to Data. My PR will move TextLoader to ML.Data. #Resolved |
||
| { | ||
| public class MemoryCollection<TInput> : ILearningPipelineLoader | ||
| where TInput : class | ||
| { | ||
| public ILearningPipelineStep ApplyStep(ILearningPipelineStep previousStep, Experiment experiment) | ||
| { | ||
| Contracts.Assert(previousStep == null); | ||
| _dataViewEntryPoint = new Data.DataViewReference(); | ||
| var importOutput = experiment.Add(_dataViewEntryPoint); | ||
| return new MemoryCollectionPipelineStep(importOutput.Data); | ||
| } | ||
|
|
||
| private readonly IList<TInput> _listCollection; | ||
| private readonly IEnumerable<TInput> _enumerableCollection; | ||
|
|
||
| private Data.DataViewReference _dataViewEntryPoint; | ||
| private IDataView _dataView; | ||
|
|
||
| public MemoryCollection(IList<TInput> collection) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
public constructor required comments. #Resolved |
||
| { | ||
| //need validation at some point | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
How about we at least validate to ensure that these aren't |
||
| _listCollection = collection; | ||
| } | ||
|
|
||
| public MemoryCollection(IEnumerable<TInput> collection) | ||
| { | ||
| //need validation at some point | ||
| _enumerableCollection = collection; | ||
| } | ||
|
|
||
| public void SetInput(IHostEnvironment env, Experiment experiment) | ||
| { | ||
| if (_listCollection!=null) | ||
| { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ctrl-k-d, also, maybe since these are one-liners we can omit these vebose |
||
| _dataView = DataViewConstructionUtils.CreateFromList(env, _listCollection); | ||
| } | ||
| if (_enumerableCollection!=null) | ||
| { | ||
| _dataView = DataViewConstructionUtils.CreateFromEnumerable(env, _listCollection); | ||
| } | ||
| env.CheckValue(_dataView, nameof(_dataView)); | ||
| experiment.SetInput(_dataViewEntryPoint.Data, _dataView); | ||
| } | ||
|
|
||
| private class MemoryCollectionPipelineStep : ILearningPipelineDataStep | ||
| { | ||
| public MemoryCollectionPipelineStep(Var<IDataView> data) | ||
| { | ||
| Data = data; | ||
| Model = null; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unnecessary, the default value is That's fine, but I'd go one step further... What I'd do is change the |
||
| } | ||
|
|
||
| public Var<IDataView> Data { get; } | ||
| public Var<ITransformModel> Model { get; } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| using Microsoft.ML.Data; | ||
| using Microsoft.ML.Runtime; | ||
| using Microsoft.ML.Runtime.CommandLine; | ||
| using Microsoft.ML.Runtime.Data; | ||
| using Microsoft.ML.Runtime.EntryPoints; | ||
|
|
||
| [assembly: LoadableClass(typeof(void), typeof(InMemoryDataView), null, typeof(SignatureEntryPointModule), "InMemoryDataView")] | ||
| namespace Microsoft.ML.Runtime.EntryPoints | ||
| { | ||
| public class InMemoryDataView | ||
| { | ||
| public sealed class Input | ||
| { | ||
| [Argument(ArgumentType.Required, ShortName = "data", HelpText = "Pointer to IDataView in memory", SortOrder = 1)] | ||
| public IDataView Data; | ||
| } | ||
|
|
||
| public sealed class Output | ||
| { | ||
| [TlcModule.Output(Desc = "The resulting data view", SortOrder = 1)] | ||
| public IDataView Data; | ||
| } | ||
|
|
||
| [TlcModule.EntryPoint(Name = "Data.DataViewReference", Desc = "Pass dataview from memory to experiment")] | ||
| public static Output ImportData(IHostEnvironment env, Input input) | ||
| { | ||
| Contracts.CheckValue(env, nameof(env)); | ||
| var host = env.Register("DataViewReference"); | ||
| env.CheckValue(input, nameof(input)); | ||
| EntryPointUtils.CheckInputArgs(host, input); | ||
| return new Output { Data = input.Data }; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using Microsoft.ML; | ||
| using Microsoft.ML.Runtime; | ||
| using Microsoft.ML.Runtime.Api; | ||
| using Microsoft.ML.Runtime.Data; | ||
| using Microsoft.ML.TestFramework; | ||
| using Microsoft.ML.Trainers; | ||
| using Microsoft.ML.Transforms; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using Xunit; | ||
| using Xunit.Abstractions; | ||
|
|
||
| namespace Microsoft.ML.EntryPoints.Tests | ||
| { | ||
| public class MemoryCollectionTests : BaseTestClass | ||
| { | ||
| public MemoryCollectionTests(ITestOutputHelper output) | ||
| : base(output) | ||
| { | ||
|
|
||
| } | ||
|
|
||
| [Fact] | ||
| public void ConstructorDoesntThrow() | ||
| { | ||
| Assert.NotNull(new MemoryCollection<Input>(new List<Input>())); | ||
| Assert.NotNull(new MemoryCollection<Input>(new List<Input>() { new Input { Number1 = 1, String1 = "1" } })); | ||
| Assert.NotNull(new MemoryCollection<Input>(new Input[0])); | ||
| Assert.NotNull(new MemoryCollection<Input>(new Input[1] { new Input { Number1 = 1, String1 = "1" } })); | ||
| Assert.NotNull(new MemoryCollection<Input>(null)); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void CanSuccessfullyApplyATransform() | ||
| { | ||
| var collection = new MemoryCollection<Input>(new List<Input>() { new Input { Number1 = 1, String1 = "1" } }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just an observation... if we had instead structured this as a static utility method somewhere, then we could avoid having the double-specification of the So: if we had a input type |
||
|
|
||
| using (var environment = new TlcEnvironment()) | ||
| { | ||
| Experiment experiment = environment.CreateExperiment(); | ||
| ILearningPipelineDataStep output = collection.ApplyStep(null, experiment) as ILearningPipelineDataStep; | ||
|
|
||
| Assert.NotNull(output.Data); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If you really meant the |
||
| Assert.NotNull(output.Data.VarName); | ||
| Assert.Null(output.Model); | ||
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void CanSuccessfullyEnumerated() | ||
| { | ||
| var collection = new MemoryCollection<Input>(new List<Input>() { | ||
| new Input { Number1 = 1, String1 = "1" }, | ||
| new Input { Number1 = 2, String1 = "2" }, | ||
| new Input { Number1 = 3, String1 = "3" } | ||
| }); | ||
|
|
||
| using (var environment = new TlcEnvironment()) | ||
| { | ||
| Experiment experiment = environment.CreateExperiment(); | ||
| ILearningPipelineDataStep output = collection.ApplyStep(null, experiment) as ILearningPipelineDataStep; | ||
|
|
||
| experiment.Compile(); | ||
| collection.SetInput(environment, experiment); | ||
| experiment.Run(); | ||
|
|
||
| IDataView data = experiment.GetOutput(output.Data); | ||
| Assert.NotNull(data); | ||
|
|
||
| using (var cursor = data.GetRowCursor((a => true))) | ||
| { | ||
| var IDGetter = cursor.GetGetter<float>(0); | ||
| var TextGetter = cursor.GetGetter<DvText>(1); | ||
|
|
||
| Assert.True(cursor.MoveNext()); | ||
|
|
||
| float ID = 0; | ||
| IDGetter(ref ID); | ||
| Assert.Equal(1, ID); | ||
|
|
||
| DvText Text = new DvText(); | ||
| TextGetter(ref Text); | ||
| Assert.Equal("1", Text.ToString()); | ||
|
|
||
| Assert.True(cursor.MoveNext()); | ||
|
|
||
| ID = 0; | ||
| IDGetter(ref ID); | ||
| Assert.Equal(2, ID); | ||
|
|
||
| Text = new DvText(); | ||
| TextGetter(ref Text); | ||
| Assert.Equal("2", Text.ToString()); | ||
|
|
||
| Assert.True(cursor.MoveNext()); | ||
|
|
||
| ID = 0; | ||
| IDGetter(ref ID); | ||
| Assert.Equal(3, ID); | ||
|
|
||
| Text = new DvText(); | ||
| TextGetter(ref Text); | ||
| Assert.Equal("3", Text.ToString()); | ||
|
|
||
| Assert.False(cursor.MoveNext()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| [Fact] | ||
| public void CanTrain() | ||
| { | ||
| var pipeline = new LearningPipeline(); | ||
| var collection = new MemoryCollection<IrisData>(new List<IrisData>() { | ||
| new IrisData { SepalLength = 1f, SepalWidth = 1f ,PetalLength=0.3f, PetalWidth=5.1f, Label=1}, | ||
| new IrisData { SepalLength = 1f, SepalWidth = 1f ,PetalLength=0.3f, PetalWidth=5.1f, Label=1}, | ||
| new IrisData { SepalLength = 1.2f, SepalWidth = 0.5f ,PetalLength=0.3f, PetalWidth=5.1f, Label=0} | ||
| }); | ||
|
|
||
| pipeline.Add(collection); | ||
|
|
||
| pipeline.Add(new ColumnConcatenator(outputColumn: "Features", | ||
| "SepalLength", "SepalWidth", "PetalLength", "PetalWidth")); | ||
|
|
||
| pipeline.Add(new StochasticDualCoordinateAscentClassifier()); | ||
| PredictionModel<IrisData, IrisPrediction> model = pipeline.Train<IrisData, IrisPrediction>(); | ||
|
|
||
| IrisPrediction prediction = model.Predict(new IrisData() | ||
| { | ||
| SepalLength = 3.3f, | ||
| SepalWidth = 1.6f, | ||
| PetalLength = 0.2f, | ||
| PetalWidth = 5.1f, | ||
| }); | ||
|
|
||
| } | ||
|
|
||
| public class Input | ||
| { | ||
| [Column("0")] | ||
| public float Number1; | ||
|
|
||
| [Column("1")] | ||
| public string String1; | ||
| } | ||
|
|
||
| public class IrisData | ||
| { | ||
| [Column("0")] | ||
| public float Label; | ||
|
|
||
| [Column("1")] | ||
| public float SepalLength; | ||
|
|
||
| [Column("2")] | ||
| public float SepalWidth; | ||
|
|
||
| [Column("3")] | ||
| public float PetalLength; | ||
|
|
||
| [Column("4")] | ||
| public float PetalWidth; | ||
| } | ||
|
|
||
| public class IrisPrediction | ||
| { | ||
| [ColumnName("Score")] | ||
| public float[] PredictedLabels; | ||
| } | ||
|
|
||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes me nervous... If you make this public rather than internal, then it is no longer appropriate to have what are currently
Asserts on the public methods. They'll have to beChecks, with suitably verbose error messages, properly called in the context ofenv, and so on, and so on. This is more work than I think you want to have right now.Do you need all of the functions in this class? This was originally a supporting utility class for the pre-ML.NET API. You don't need all of it... in fact I think you only need a couple of them. I might choose to either (1) introduce another public class with the methods you need being accessible through that or (2) change this class so that it remains public but everything in it has its access member changed to
internal, except the one or two facilities you actually need. #ClosedUh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I miss these blobs of text so much. (I'm serious right now) #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Someone already did that for me. All I had to do is to look for method reference :)
In reply to: 187203478 [](ancestors = 187203478)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks Ivan, it's nice to be appreciated. :D :D
In reply to: 187203886 [](ancestors = 187203886)