Skip to content

LightGBM doesn't work during F5 of a .NET Core application #482

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
eerhardt opened this issue Jul 3, 2018 · 10 comments
Closed

LightGBM doesn't work during F5 of a .NET Core application #482

eerhardt opened this issue Jul 3, 2018 · 10 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@eerhardt
Copy link
Member

eerhardt commented Jul 3, 2018

System information

  • OS version/distro: Windows
  • .NET Version (eg., dotnet --info):
> dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   2.1.400-preview-009063
 Commit:    dd0179a67c

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\2.1.400-preview-009063\

Host (useful for support):
  Version: 2.1.1
  Commit:  6985b9f684

Issue

            // STEP 4: Add learner
            // Add a learning algorithm to the pipeline. 
            // This is a classification scenario (What type of iris is this?)
            pipeline.Add(new LightGbmClassifier());
//            pipeline.Add(new StochasticDualCoordinateAscentClassifier());
  • What happened?
Unhandled Exception: System.InvalidOperationException: Entry point 'Trainers.LightGbmClassifier' not found
   at Microsoft.ML.Runtime.EntryPoints.EntryPointNode..ctor(IHostEnvironment env, IChannel ch, ModuleCatalog moduleCatalog, RunContext context, String id, String entryPointName, JObject inputs, JObject outputs, Boolean checkpoint, String stageId, Single cost, String label, String group, String weight)
   at Microsoft.ML.Runtime.EntryPoints.EntryPointNode.ValidateNodes(IHostEnvironment env, RunContext context, JArray nodes, ModuleCatalog moduleCatalog, String label, String group, String weight)
   at Microsoft.ML.Runtime.EntryPoints.EntryPointGraph..ctor(IHostEnvironment env, ModuleCatalog moduleCatalog, JArray nodes)
   at Microsoft.ML.Runtime.Experiment.Compile()
   at Microsoft.ML.LearningPipeline.Train[TInput,TOutput]()
   at MLLightGBMSmokeTest.Program.Main(String[] args) in C:\Users\eerhardt\source\repos\MLLightGBMSmokeTest\Program.cs:line 72
  • What did you expect?
    I expected the walk through app to run successfully.

Notes

When F5 debugging a .NET Core app in VS, or using dotnet run on the command line, .NET Core doesn't have all the dependencies copied to the output directory. Instead, references that come from NuGet packages are executed from the NuGet package cache folder.
Since LightGBM comes from a separate NuGet package than the rest of the Microsoft.ML, it is loaded from a separate folder than the rest of the Microsoft.ML package.

I've looked through the ComponentCatalog code, and it appears if I force the Microsoft.ML.LightGBM.dll to be loaded first, I can workaround the issue.

So I added

        static void Main(string[] args)
        {
            // workaround to ensure LightGbm assembly is loaded
            new LightGbmArguments();

And I am able to successfully use LightGBM on .NET Core during F5.

Workarounds

Any of these should allow you to workaround the issue:

  1. forcing the assembly to be loaded, like I did above: new LightGbmArguments();
  2. dotnet publish your application and running it from the published folder (since all dependencies are copied during publish)
  3. Using full .NET Framework

/cc @ericstj @TomFinley @codemzs

@codemzs
Copy link
Member

codemzs commented Jul 3, 2018

Hi Eric, were you using LightGBM nuget?

@eerhardt
Copy link
Member Author

eerhardt commented Jul 3, 2018

Yes, here is my .csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.ML.LightGBM" Version="0.3.0-preview-26703-11" />
  </ItemGroup>
  <ItemGroup>
    <None Update="iris-data.txt">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

@codemzs
Copy link
Member

codemzs commented Jul 3, 2018

I spoke with Eric offline and as a work around running dotnet publish places the dependencies in one folder.

@GalOshri GalOshri added the bug Something isn't working label Jul 3, 2018
@shauheen shauheen added this to the 0918 milestone Aug 31, 2018
@artidoro
Copy link
Contributor

artidoro commented Sep 4, 2018

There is another workaround (on the user end) as outlined in #618. It requires adding the following line to the .csproj file:
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
so that it becomes:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.1</TargetFramework>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.ML" Version="0.4.0" />
    <PackageReference Include="Microsoft.ML.LightGBM" Version="0.4.0" />
  </ItemGroup>

  <ItemGroup>
    <None Update="iris-data.txt">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>

@artidoro
Copy link
Contributor

artidoro commented Sep 5, 2018

This problem should be soon resolved by the change from the LearningPipelineAPI to the direct access API #371. Users will call directly into the LightGBM assembly, which will therefore be loaded.

@shauheen
Copy link
Contributor

@artidoro once #1020 is merged, please test and close this issue. Thanks.

@artidoro
Copy link
Contributor

The new direct access API and the changes coming with version 0.6.0 of ML.NET solved this issue.
After loading both the ML.NET nuget and the LightGBM nuget, the tutorial can be run without incurring in any errors. Note that we will update the tutorial to reflect the changes for the new API.

@bartczernicki
Copy link

Using legacy APIs (even with workaround) when upgrading to ML.NET 0.6 seems to bring this issue back up.

@shauheen
Copy link
Contributor

shauheen commented Nov 2, 2018

@artidoro can you please verify this issue with 0.7?

@shauheen shauheen reopened this Nov 2, 2018
@artidoro
Copy link
Contributor

artidoro commented Nov 2, 2018

Hello @bartczernicki! Sorry for the late reply.
As you noticed using the Legacy API will still has the issue that was pointed out in this post initially. However, I have tested again that with v0.7 the iris tutorial with LightGBM works fine. I know there have been a lot of significant changes to the API and renaming of namespaces and trainers recently, so I am going to post the updated code for the tutorial. I used the pre-release nugets (remember to install the LightGBM nuget for this code to work) but as soon as v0.7 is released you will be able to use those.

using Microsoft.ML;
using Microsoft.ML.Runtime.LightGBM;
using Microsoft.ML.Runtime.Api;
using Microsoft.ML.Runtime.Data;
using Microsoft.ML.Transforms;
using Microsoft.ML.Transforms.Categorical;
using Microsoft.ML.Transforms.Conversions;
using System;

namespace myApp
{
    class Program
    {
        // STEP 1: Define your data structures
        // IrisData is used to provide training data, and as
        // input for prediction operations
        // - First 4 properties are inputs/features used to predict the label
        // - Label is what you are predicting, and is only set when training
        public class IrisData
        {
            [Column("0")]
            public float SepalLength;

            [Column("1")]
            public float SepalWidth;

            [Column("2")]
            public float PetalLength;

            [Column("3")]
            public float PetalWidth;

            [Column("4")]
            [ColumnName("Label")]
            public string Label;
        }

        // IrisPrediction is the result returned from prediction operations
        public class IrisPrediction
        {
            [ColumnName("PredictedLabel")]
            public string PredictedLabel;
        }

        static void Main(string[] args)
        {
            // STEP 2: Create an evironment  and load your data
            var ctx = new MLContext();

            // If working in Visual Studio, make sure the 'Copy to Output Directory'
            // property of iris-data.txt is set to 'Copy always'
            string dataPath = "iris-data.txt";
            var reader = new TextLoader(ctx,
                new TextLoader.Arguments()
                {
                    Separator = ",",
                    HasHeader = true,
                    Column = new[]
                    {
                            new TextLoader.Column("SepalLength", DataKind.R4, 0),
                            new TextLoader.Column("SepalWidth", DataKind.R4, 1),
                            new TextLoader.Column("PetalLength", DataKind.R4, 2),
                            new TextLoader.Column("PetalWidth", DataKind.R4, 3),
                            new TextLoader.Column("Label", DataKind.Text, 4)
                    }
                });

            IDataView trainingDataView = reader.Read(new MultiFileSource(dataPath));

            // STEP 3: Transform your data and add a learner
            // Assign numeric values to text in the "Label" column, because only
            // numbers can be processed during model training.
            // Add a learning algorithm to the pipeline. e.g.(What type of iris is this?)
            // Convert the Label back into original text (after converting to number in step 3)
            var pipeline = new ValueToKeyMappingEstimator(ctx, "Label", "Label")
                   .Append(new ColumnConcatenatingEstimator(ctx, "Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth"))
                   .Append(new LightGbmMulticlassTrainer(ctx, "Label", "Features"))
                   .Append(new KeyToValueEstimator(ctx, "PredictedLabel"));;

            // STEP 4: Train your model based on the data set  
            var model = pipeline.Fit(trainingDataView);

            // STEP 5: Use your model to make a prediction
            // You can change these numbers to test different predictions
            var prediction = model.MakePredictionFunction<IrisData, IrisPrediction>(ctx).Predict(
                new IrisData()
                {
                    SepalLength = 3.3f,
                    SepalWidth = 1.6f,
                    PetalLength = 0.2f,
                    PetalWidth = 5.1f,
                });

            Console.WriteLine($"Predicted flower type is: {prediction.PredictedLabel}");
            Console.ReadLine();
        }
    }
}

@artidoro artidoro closed this as completed Nov 2, 2018
@ghost ghost locked as resolved and limited conversation to collaborators Mar 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

6 participants