Skip to content

MLContext.Model.Load overloads that take file path instead of stream. #3008

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

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions src/Microsoft.ML.Data/Model/ModelOperationsCatalog.cs
Original file line number Diff line number Diff line change
@@ -182,12 +182,25 @@ public ITransformer Load(Stream stream, out DataViewSchema inputSchema)
}

/// <summary>
/// Load the model and its input schema from the stream.
/// Load the model and its input schema from the file.
/// </summary>
/// <param name="modelPath">Path to model.</param>
/// <param name="inputSchema">Will contain the input schema for the model. If the model was saved using older APIs
/// it may not contain an input schema, in this case <paramref name="inputSchema"/> will be null.</param>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth calling out the behavior for older models having a null input schema? That should never be the case now right?

/// <returns>The loaded model.</returns>
public ITransformer Load(string modelPath, out DataViewSchema inputSchema)
{
using (var stream = File.OpenRead(modelPath))
return Load(stream, out inputSchema);
}

/// <summary>
/// Load the model from the stream.
/// </summary>
/// <param name="stream">A readable, seekable stream to load from.</param>
/// <returns>A model of type <see cref="CompositeDataLoader{IMultiStreamSource, ITransformer}"/> containing the loader
/// and the transformer chain.</returns>
public IDataLoader<IMultiStreamSource> Load(Stream stream)
public IDataLoader<IMultiStreamSource> LoadDataLoader(Stream stream)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make a similar change for the Save method?

        /// <summary>
        /// Save the model to the file.
        /// </summary>
        /// <param name="model">The trained model to be saved.</param>
        /// <param name="filePath">Path where model should be saved.</param>
        public void Save<TSource>(IDataLoader<TSource> model, string filePath)
        {
            using (var stream = File.Create(filePath))
                Save(model, stream);
        }

These are no longer symmetric.

Copy link
Member Author

@codemzs codemzs Mar 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes but this is Load API PR. I have created another PR to rename this for Save API #3019

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eerhardt @TomFinley feels we shouldn't change for Save because in .Net we have ReadChar, ReadInt, ReadDouble, etc but Write() is just one loaded set of methods. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont quite follow is there more discussion on github?


In reply to: 267100385 [](ancestors = 267100385)

{
_env.CheckValue(stream, nameof(stream));

@@ -205,6 +218,18 @@ public IDataLoader<IMultiStreamSource> Load(Stream stream)
}
}

/// <summary>
/// Load the model from the file.
/// </summary>
/// <param name="modelPath">Path to model.</param>
/// <returns>A model of type <see cref="CompositeDataLoader{IMultiStreamSource, ITransformer}"/> containing the loader
/// and the transformer chain.</returns>
public IDataLoader<IMultiStreamSource> LoadDataLoader(string modelPath)
{
using (var stream = File.OpenRead(modelPath))
return LoadDataLoader(stream);
}

/// <summary>
/// Load a transformer model and a data loader model from the stream.
/// </summary>
@@ -215,7 +240,7 @@ public ITransformer LoadWithDataLoader(Stream stream, out IDataLoader<IMultiStre
{
_env.CheckValue(stream, nameof(stream));

loader = Load(stream);
loader = LoadDataLoader(stream);
if (loader is CompositeDataLoader<IMultiStreamSource, ITransformer> composite)
{
loader = composite.Loader;
@@ -224,6 +249,18 @@ public ITransformer LoadWithDataLoader(Stream stream, out IDataLoader<IMultiStre
return new TransformerChain<ITransformer>();
}

/// <summary>
/// Load a transformer model and a data loader model from the file.
/// </summary>
/// <param name="modelPath">Path to model.</param>
/// <param name="loader">The data loader from the model stream.</param>
/// <returns>The transformer model from the model stream.</returns>
public ITransformer LoadWithDataLoader(string modelPath, out IDataLoader<IMultiStreamSource> loader)
{
using (var stream = File.OpenRead(modelPath))
return LoadWithDataLoader(stream, out loader);
}

/// <summary>
/// Create a prediction engine for one-time prediction.
/// </summary>
5 changes: 1 addition & 4 deletions test/Microsoft.ML.Core.Tests/UnitTests/TestEntryPoints.cs
Original file line number Diff line number Diff line change
@@ -5643,10 +5643,7 @@ public void LoadEntryPointModel()
{
var modelPath = GetDataPath($"backcompat/ep_model{i}.zip");
ITransformer loadedModel;
using (var stream = File.OpenRead(modelPath))
{
loadedModel = ml.Model.Load(stream, out var inputSchema);
}
loadedModel = ml.Model.Load(modelPath, out var inputSchema);
}
}
}
77 changes: 33 additions & 44 deletions test/Microsoft.ML.Functional.Tests/ModelLoading.cs
Original file line number Diff line number Diff line change
@@ -68,24 +68,19 @@ public void LoadModelAndExtractPredictor()
ITransformer loadedTransformerModel;
IDataLoader<IMultiStreamSource> loadedCompositeLoader;
ITransformer loadedTransformerModel1;
using (var fs = File.OpenRead(modelAndSchemaPath))
loadedTransformerModel = _ml.Model.Load(fs, out var loadedSchema);
using (var fs = File.OpenRead(compositeLoaderModelPath))
{
// This model can be loaded either as a composite data loader,
// a transformer model + an input schema, or a transformer model + a data loader.
var t = _ml.Model.LoadWithDataLoader(fs, out IDataLoader<IMultiStreamSource> l);
var t1 = _ml.Model.Load(fs, out var s);
loadedCompositeLoader = _ml.Model.Load(fs);
}
using (var fs = File.OpenRead(loaderAndTransformerModelPath))
{
// This model can be loaded either as a composite data loader,
// a transformer model + an input schema, or a transformer model + a data loader.
var t = _ml.Model.Load(fs, out var s);
var c = _ml.Model.Load(fs);
loadedTransformerModel1 = _ml.Model.LoadWithDataLoader(fs, out IDataLoader<IMultiStreamSource> l);
}
loadedTransformerModel = _ml.Model.Load(modelAndSchemaPath, out var loadedSchema);

// This model can be loaded either as a composite data loader,
// a transformer model + an input schema, or a transformer model + a data loader.
var t = _ml.Model.LoadWithDataLoader(compositeLoaderModelPath, out IDataLoader<IMultiStreamSource> l);
var t1 = _ml.Model.Load(compositeLoaderModelPath, out var s);
loadedCompositeLoader = _ml.Model.LoadDataLoader(compositeLoaderModelPath);

// This model can be loaded either as a composite data loader,
// a transformer model + an input schema, or a transformer model + a data loader.
var tt = _ml.Model.Load(loaderAndTransformerModelPath, out var ss);
var c = _ml.Model.LoadDataLoader(loaderAndTransformerModelPath);
loadedTransformerModel1 = _ml.Model.LoadWithDataLoader(loaderAndTransformerModelPath, out IDataLoader<IMultiStreamSource> ll);

var gam = ((loadedTransformerModel as ISingleFeaturePredictionTransformer<object>).Model
as CalibratedModelParametersBase).SubModel
@@ -125,11 +120,8 @@ public void SaveAndLoadModelWithLoader()
IDataLoader<IMultiStreamSource> loadedModel;
ITransformer loadedModelWithoutLoader;
DataViewSchema loadedSchema;
using (var fs = File.OpenRead(modelPath))
{
loadedModel = _ml.Model.Load(fs);
loadedModelWithoutLoader = _ml.Model.Load(fs, out loadedSchema);
}
loadedModel = _ml.Model.LoadDataLoader(modelPath);
loadedModelWithoutLoader = _ml.Model.Load(modelPath, out loadedSchema);

// Without deserializing the loader from the model we lose the slot names.
data = _ml.Data.LoadFromEnumerable(new[] { new InputData() });
@@ -171,8 +163,7 @@ public void LoadSchemaAndCreateNewData()

ITransformer loadedModel;
DataViewSchema loadedSchema;
using (var fs = File.OpenRead(modelPath))
loadedModel = _ml.Model.Load(fs, out loadedSchema);
loadedModel = _ml.Model.Load(modelPath, out loadedSchema);

// Without using the schema from the model we lose the slot names.
data = _ml.Data.LoadFromEnumerable(new[] { new InputData() });
@@ -298,26 +289,24 @@ private void Load(string filename, out ITransformer loadedWithSchema, out DataVi
out IDataLoader<IMultiStreamSource> loadedLoader, out ITransformer loadedWithLoader,
out IDataLoader<IMultiStreamSource> loadedLoaderWithTransformer)
{
using (var fs = File.OpenRead(filename))

try
{
loadedLoader = _ml.Model.LoadDataLoader(filename);
}
catch (Exception)
{
loadedLoader = null;
}
loadedWithSchema = _ml.Model.Load(filename, out loadedSchema);
try
{
loadedWithLoader = _ml.Model.LoadWithDataLoader(filename, out loadedLoaderWithTransformer);
}
catch (Exception)
{
try
{
loadedLoader = _ml.Model.Load(fs);
}
catch (Exception)
{
loadedLoader = null;
}
loadedWithSchema = _ml.Model.Load(fs, out loadedSchema);
try
{
loadedWithLoader = _ml.Model.LoadWithDataLoader(fs, out loadedLoaderWithTransformer);
}
catch (Exception)
{
loadedWithLoader = null;
loadedLoaderWithTransformer = null;
}
loadedWithLoader = null;
loadedLoaderWithTransformer = null;
}
}

3 changes: 1 addition & 2 deletions test/Microsoft.ML.TestFramework/DataPipe/TestDataPipeBase.cs
Original file line number Diff line number Diff line change
@@ -83,8 +83,7 @@ protected void TestEstimatorCore(IEstimator<ITransformer> estimator,

ITransformer loadedTransformer;
DataViewSchema loadedInputSchema;
using (var fs = File.OpenRead(modelPath))
loadedTransformer = ML.Model.Load(fs, out loadedInputSchema);
loadedTransformer = ML.Model.Load(modelPath, out loadedInputSchema);
DeleteOutputPath(modelPath);

// Run on train data.
Original file line number Diff line number Diff line change
@@ -153,8 +153,7 @@ private void TrainRegression(string trainDataPath, string testDataPath, string m

// When you load the model, it's a 'dynamic' transformer.
ITransformer loadedModel;
using (var stream = File.OpenRead(modelPath))
loadedModel = mlContext.Model.Load(stream, out var schema);
loadedModel = mlContext.Model.Load(modelPath, out var schema);
}

[Fact]
Original file line number Diff line number Diff line change
@@ -127,8 +127,7 @@ private void TrainRegression(string trainDataPath, string testDataPath, string m

// When you load the model, it's a 'dynamic' transformer.
ITransformer loadedModel;
using (var stream = File.OpenRead(modelPath))
loadedModel = mlContext.Model.Load(stream, out var schema);
loadedModel = mlContext.Model.Load(modelPath, out var schema);
}

[Fact]
@@ -530,8 +529,7 @@ private static void RunEndToEnd(MLContext mlContext, IDataView trainData, string

// Now we can load the model.
ITransformer loadedModel;
using (var fs = File.OpenRead(modelPath))
loadedModel = newContext.Model.Load(fs, out var schema);
loadedModel = newContext.Model.Load(modelPath, out var schema);
}

public static IDataView PrepareData(MLContext mlContext, IDataView data)
Original file line number Diff line number Diff line change
@@ -40,8 +40,7 @@ public void TrainSaveModelAndPredict()
// Load model.
ITransformer loadedModel;
DataViewSchema inputSchema;
using (var file = File.OpenRead(modelPath))
loadedModel = ml.Model.Load(file, out inputSchema);
loadedModel = ml.Model.Load(modelPath, out inputSchema);

// Create prediction engine and test predictions.
var engine = ml.Model.CreatePredictionEngine<SentimentData, SentimentPrediction>(loadedModel, inputSchema);
Original file line number Diff line number Diff line change
@@ -503,12 +503,9 @@ public void MatrixFactorizationBackCompat()
ITransformer model;
using (var ch = Env.Start("load"))
{
using (var fs = File.OpenRead(modelPath))
{
model = ML.Model.Load(fs, out var schema);
// This model was saved without the input schema.
Assert.Null(schema);
}
model = ML.Model.Load(modelPath, out var schema);
// This model was saved without the input schema.
Assert.Null(schema);
}

// Create data for testing. Note that the 2nd element is not specified in the training data so it should
3 changes: 1 addition & 2 deletions test/Microsoft.ML.Tests/Transformers/ConvertTests.cs
Original file line number Diff line number Diff line change
@@ -260,8 +260,7 @@ public void TypeConvertKeyBackCompatTest()
ITransformer modelOld;
using (var ch = Env.Start("load"))
{
using (var fs = File.OpenRead(modelPath))
modelOld = ML.Model.Load(fs, out var schema);
modelOld = ML.Model.Load(modelPath, out var schema);
}
var outDataOld = modelOld.Transform(dataView);

9 changes: 3 additions & 6 deletions test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
Original file line number Diff line number Diff line change
@@ -178,8 +178,7 @@ public void ChangePointDetectionWithSeasonalityPredictionEngineNoColumn()
//Load time series model and we will use this to pass two inputs and compare the raw score
//with "engine".
ITransformer model2 = null;
using (var file = File.OpenRead(modelPath))
model2 = ml.Model.Load(file, out var schema);
model2 = ml.Model.Load(modelPath, out var schema);

//Raw score after state gets updated with two inputs.
var engine2 = model2.CreateTimeSeriesPredictionFunction<Data, Prediction>(ml);
@@ -198,8 +197,7 @@ public void ChangePointDetectionWithSeasonalityPredictionEngineNoColumn()
//Save the model with state updated with just one input.
engine.CheckPoint(ml, modelPath + 1);
ITransformer model3 = null;
using (var file = File.OpenRead(modelPath + 1))
model3 = ml.Model.Load(file, out var schema);
model3 = ml.Model.Load(modelPath + 1, out var schema1);

//Load the model with state updated with just one input, then pass in the second input
//and raw score should match the raw score obtained by passing the two input in the first model.
@@ -265,8 +263,7 @@ public void ChangePointDetectionWithSeasonalityPredictionEngine()

// Load Model 1.
ITransformer model2 = null;
using (var file = File.OpenRead(modelPath))
model2 = ml.Model.Load(file, out var schema);
model2 = ml.Model.Load(modelPath, out var schema);

//Predict and expect the same result after checkpointing(Prediction #2).
engine = model2.CreateTimeSeriesPredictionFunction<Data, Prediction>(ml);