Skip to content
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

Unable to retrieve saved machines #743

Closed
casasgomezuribarri opened this issue Feb 19, 2021 · 7 comments
Closed

Unable to retrieve saved machines #743

casasgomezuribarri opened this issue Feb 19, 2021 · 7 comments

Comments

@casasgomezuribarri
Copy link

Describe the bug
I am unable to retrieve a saved trained machine.

I have a few machines that I have already tuned stored on my computer. I have been retrieving them using machine("mymach_1.jlso", X, y) to make plots and inspect well each tuning process. However, today I haven't been able to retrieve any of them, I get the following error:

Click to see error
[warn | JLSO]: MethodError: no method matching deserialize(::Serialization.Serializer{Base.GenericIOBuffer{Array{UInt8,1}}}, ::Type{Machine{CompositeModel,C} where C})
Closest candidates are:
  deserialize(::Serialization.AbstractSerializer) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.4\Serialization\src\Serialization.jl:735
  deserialize(::Serialization.AbstractSerializer, !Matched::Type{Method}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.4\Serialization\src\Serialization.jl:923
  deserialize(::Serialization.AbstractSerializer, !Matched::Type{Core.MethodInstance}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.4\Serialization\src\Serialization.jl:983
  ...
[warn | JLSO]: KeyError: key MLJGLMInterface [caf8df21-4939-456d-ac9c-5fefbfb04c0c] not found
ERROR: LoadError: TaskFailedException:
TypeError: in new, expected DataType, got Type{JLSO.var"#32#34"{NamedTuple{(:best_model, :best_history_entry, :history, :best_report, :plotting),Tuple{CompositeModel,NamedTuple{(:model, :measure, :measurement, :per_fold),Tuple{CompositeModel,Array{LogLoss{Float64},1},Array{Float64,1},Array{Array{Float64,1},1}}},Array{NamedTuple{(:model, :measure, :measurement, :per_fold),Tuple{CompositeModel,Array{LogLoss{Float64},1},Array{Float64,1},Array{Array{Float64,1},1}}},1},NamedTuple{(:model1, :model2, :compiler, :judge, :machines, :report_given_machine),Tuple{NamedTuple{(:measures, :oob_measurements),Tuple{Array{LogLoss{Float64},1},Array{Float64,1}}},NamedTuple{(:measures, :oob_measurements),Tuple{Array{LogLoss{Float64},1},Array{Float64,1}}},Nothing,NamedTuple{(:deviance, :dof_residual, :stderror, :vcov),Tuple{Float64,Float64,Array{Float64,1},Array{Float64,2}}},Array{Machine{M<:MLJModelInterface.Model,C} where C,1},OrderedCollections.LittleDict{Any,Any,Array{Any,1},Array{Any,1}}}},NamedTuple{(:parameter_names, :parameter_scales, :parameter_values, :measurements),Tuple{Array{String,1},Array{Symbol,1},Array{Any,2},Array{Float64,1}}}}},Dict{Symbol,Any},Symbol}}
Stacktrace:
 [1] macro expansion at C:\Users\ivica\.julia\packages\JLSO\zhJ7y\src\file_io.jl:131 [inlined]
 [2] (::JLSO.var"#31#33"{JLSO.JLSOFile,Dict{Symbol,Any},Symbol})() at .\threadingconstructs.jl:126

I don't understand most of what it says there, but I saw the line [warn | JLSO]: KeyError: key MLJGLMInterface [caf8df21-4939-456d-ac9c-5fefbfb04c0c] not found so I tried ] add MLJGLMInterface and using MLJGLMInterface. However, I still got a very similar error:

Click to see error
[warn | JLSO]: MethodError: no method matching deserialize(::Serialization.Serializer{Base.GenericIOBuffer{Array{UInt8,1}}}, ::Type{Machine{CompositeModel,C} where C})
Closest candidates are:
  deserialize(::Serialization.AbstractSerializer) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.4\Serialization\src\Serialization.jl:735
  deserialize(::Serialization.AbstractSerializer, !Matched::Type{Method}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.4\Serialization\src\Serialization.jl:923
  deserialize(::Serialization.AbstractSerializer, !Matched::Type{Core.MethodInstance}) at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.4\Serialization\src\Serialization.jl:983
  ...
[warn | JLSO]: EOFError: read end of file
ERROR: LoadError: TaskFailedException:
TypeError: in new, expected DataType, got Type{JLSO.var"#32#34"{NamedTuple{(:best_model, :best_history_entry, :history, :best_report, :plotting),Tuple{CompositeModel,NamedTuple{(:model, :measure, :measurement, :per_fold),Tuple{CompositeModel,Array{LogLoss{Float64},1},Array{Float64,1},Array{Array{Float64,1},1}}},Array{NamedTuple{(:model, :measure, :measurement, :per_fold),Tuple{CompositeModel,Array{LogLoss{Float64},1},Array{Float64,1},Array{Array{Float64,1},1}}},1},NamedTuple{(:model1, :model2, :compiler, :judge, :machines, :report_given_machine),Tuple{NamedTuple{(:measures, :oob_measurements),Tuple{Array{LogLoss{Float64},1},Array{Float64,1}}},NamedTuple{(:measures, :oob_measurements),Tuple{Array{LogLoss{Float64},1},Array{Float64,1}}},Nothing,NamedTuple{(:deviance, :dof_residual, :stderror, :vcov),Tuple{Float64,Float64,Array{Float64,1},Array{Float64,2}}},Array{Machine{M<:MLJModelInterface.Model,C} where C,1},OrderedCollections.LittleDict{Any,Any,Array{Any,1},Array{Any,1}}}},NamedTuple{(:parameter_names, :parameter_scales, :parameter_values, :measurements),Tuple{Array{String,1},Array{Symbol,1},Array{Any,2},Array{Float64,1}}}}},Dict{Symbol,Any},Symbol}}
Stacktrace:
 [1] macro expansion at C:\Users\ivica\.julia\packages\JLSO\zhJ7y\src\file_io.jl:131 [inlined]
 [2] (::JLSO.var"#31#33"{JLSO.JLSOFile,Dict{Symbol,Any},Symbol})() at .\threadingconstructs.jl:126

*note that the abovementioned line now says [warn | JLSO]: EOFError: read end of file instead.

I am not able to provide a reproducible code because newly saved machines seem to be correctly retrieved. I only have a problem accessing those that were tuned and saved in the past.

Additional context
After I tuned my last machine (the last one of those I am not able to retrieve), I wanted to add the package StatsPlots to make a violin plot. However, I added StatPlots instead by mistake, which downgraded several packages in my environment. I rapidly did ] rm StatPlots, ] add StatsPlots and ] update, which caused several changes in my Project.toml file. At that point I wasn't having any problems and I continued working on my project. It was only the next day that I realised that I was unable to retrieve any of the machines I had saved up to that point, although newly trained and saved machines seemed to be okay to recover. I also added the PlotlyJS package to my environment during my last session before this problem appeared. I am unsure of the importance of all this, but everything else I did that day was in the line of what I typically would do in a normal day.

Versions

VSCode: v1.53.2
OS: Windows 10
Julia: v1.4.2

output of

] status

  [336ed68f] CSV v0.8.3
  [f6006082] EvoTrees v0.6.0
  [38e38edf] GLM v1.3.11
  [b1bec4e5] LIBSVM v0.5.0
  [a5e1c1ea] LatinHypercubeSampling v1.7.3
  [add582a8] MLJ v0.16.0
  [c6f25543] MLJDecisionTreeInterface v0.1.1
  [caf8df21] MLJGLMInterface v0.1.3
  [61c7150f] MLJLIBSVMInterface v0.1.2
  [1b6a4a23] MLJMultivariateStatsInterface v0.2.1
  [03970b2e] MLJTuning v0.6.2
  [442fdcdd] Measures v0.3.1
  [58dd65bb] Plotly v0.3.0
  [f0f68f2c] PlotlyJS v0.14.0
  [91a5bcdd] Plots v1.6.12
  [f27b6e38] Polynomials v1.2.0
  [8523bd24] ShapML v0.3.0 #master (https://github.com/nredell/ShapML.jl.git)
  [a5390f91] ZipFile v0.9.3
@ablaom
Copy link
Member

ablaom commented Feb 21, 2021

Mmm, that's frustrating. Thanks for reporting.

This sounds to me like a serialization issue, rather than something specific to MLJ. So you may get more help by posting an issue at JLSO.jl or on Julia discourse. cc @oxinabox . I can tell you that:

  • The last time the serialization code was touched in MLJBase was 0.15.3 (non-breaking change on the user side)

  • The code defining the GLM implementation of the MLJ mode interface has lived in MLJGLMInterface since MLJModels 0.13. Previously it lived in MLJModels

  • JLSO files are supposed to store the complete state of packages (Manifest.toml) in their metadata. See the JLSO docs for more. Here is an example retrieving this information that works for me:

using MLJ

X, y = @load_iris;
model = (@load DecisionTreeClassifier)()
mach = machine(model, X, y) |> fit!

MLJ.save("junk.jlso", mach)

using JLSO
jlso = JLSO.read("junk.jlso", JLSOFile);
jlso.julia
jlso.manifest

So if this works for your file, you should be able to reproduce your environment to the state at save time.

@oxinabox
Copy link

using Pkg
using​ JLSO

jlso ​=​ JLSO​.​read​(​"​junk.jlso​"​, JLSOFile);

Pkg.activate(jlso)

Should load up an environment with exactly the dependencies you had loaded before.
With the exact same versions.
If a package has changed something in a newer version then you should still be able to load since this will install the older version.

There is also an open issue, and an inlcuded hacky method and a PR for a less hacky method to also load the packages.
invenia/JLSO.jl#94

@casasgomezuribarri casasgomezuribarri changed the title Unable to retrieved saved machines Unable to retrieve saved machines Feb 22, 2021
@casasgomezuribarri
Copy link
Author

casasgomezuribarri commented Feb 22, 2021

Thank you both for your attention, but none of the things suggested seems to work.

Perhaps sharing one of the .jlso files I am unable to retrieve helps? GitHub doesn't support files of this type, so I got a Wetransfer link:

https://wetransfer.com/downloads/8c047cfa950f1c75458c83e475a0610f20210222151207/bd74d0eee87a97b5b6fa03aaf2560f5920210222151227/815269

And here is the code I am using to try to retrieve my saved machines (the code is a bit longer because I am stacking models and the learning network has to be defined and exported first), which works with a newly saved machine but not the file above.

using MLJ
using EvoTrees
using DataFrames

# Define learning network
begin
    # source nodes
    X = source()
    y = source()
    # node 1
    logmodel = (@load LogisticClassifier pkg=MLJLinearModels)()
    log = machine(logmodel, X, y)
    LOG = predict(log, X)
    # node 2
    evomodel = (@load EvoTreeClassifier)()
    evo = machine(evomodel, X, y)
    EVO = predict(evo, X)
    # define new model type for node 3
    mutable struct Compiler <: Static
    end
    # and its transform method
    import MLJBase
    function MLJBase.transform(c::Compiler,_,y1, y2)
        prob_y1 = pdf.(y1, 1)
        prob_y2 = pdf.(y2, 1)
        X_stack = DataFrame(:Prob_1_LOG => prob_y1, :Prob_1_EVO => prob_y2)
        return X_stack
    end
    # node 3
    compiler = Compiler()
    comp= machine(compiler)
    X_stack = MLJ.transform(comp, LOG, EVO)
    # node 4
    metamodel = (@load LogisticClassifier pkg=MLJLinearModels)()
    meta = machine(metamodel, X_stack, y)
    META = predict(meta, X_stack)
end

# Export as a new model type
mach = machine(Probabilistic(), X, y; predict = META)
@from_network mach begin
    mutable struct CompositeModel
        model1 = logmodel
        model2 = evomodel
        compiler = compiler
        judge = metamodel
    end
end

# Fit and save a machine
begin
    X = rand(100, 10)
    y = coerce(rand([0, 1], 100), OrderedFactor)
    mymodel = CompositeModel()
    mymach = machine(mymodel, X_train, y_train) |> fit!
    MLJ.save("junk.jlso", mymach)
end

# Retrieve new and old machines 
success = machine("junk.jlso", X_train, y_train)
fail = machine("model1_trained_Run1.jlso", X_train, y_train)

# Trying what was suggested here
using JLSO
jlso_success = JLSO.read("junk.jlso", JLSOFile)
jlso_success.julia
jlso_success.manifest

jlso_fail = JLSO.read("model1_trained_Run1.jlso", JLSOFile)
jlso_fail.julia
jlso_fail.manifest

#note that the number of packages in each manifest is different

using Pkg
Pkg.activate(jlso_fail)
still_fails = machine("model1_trained_Run1.jlso", X, y)

Pkg.activate(jlso_success)
still_fails = machine("model1_trained_Run1.jlso", X, y)

# Trying the trick from the other issue
eval(Expr(:using, (Expr(:., n) for n in Symbol.(keys(Pkg.Types.Context().env.project.deps)))...))
still_fails = machine("model1_trained_Run1.jlso", X, y)

Any ideas?

@casasgomezuribarri
Copy link
Author

Additional Context
Shortly after I submitted this issue, I had another strange issue with julialang for vscode that made the Alt+Enter command stop working. It was solved after I deleted the ~\.julia\compiled\v1.4\VSCodeServer directory. This might be a problem of completely different nature, but I have been working with MLJ without issues of this kind for months now, so I thought this other problem that appeared within hours of the present one could be worth mentioning.

More information at julia-vscode/julia-vscode#1967

@oxinabox
Copy link

Did you run Pkg.activate(jlso_fail) from a fresh julia process with nothing loaded except JLSO and Pkg.
(since julia won't generally reload things after you change enviroment)

@casasgomezuribarri
Copy link
Author

Sorry, I'm not sure of what you mean

@casasgomezuribarri
Copy link
Author

I went to a plain REPL and after doing ] activate . to get an empty ] status output, I did

Pkg.add JLSO 
using JLSO
using Pkg

Then I defined the learning network, exported it and created the variables X and y with the code I shared in my previous post. To do this, I needed to using MLJ. Then, I did

jlso_fail = JLSO.read("model1_trained_Run1.jlso", JLSOFile) #copied file into pwd()
Pkg.activate(jlso_fail)
still_fails = machine("model1_trained_Run1.jlso", X, y)

I got the following error, a bit different this time:

[warn | JLSO]: KeyError: key MLJGLMInterface [caf8df21-4939-456d-ac9c-5fefbfb04c0c] not found
[warn | JLSO]: KeyError: key MLJGLMInterface [caf8df21-4939-456d-ac9c-5fefbfb04c0c] not found
[warn | JLSO]: KeyError: key MLJGLMInterface [caf8df21-4939-456d-ac9c-5fefbfb04c0c] not found
ERROR: MethodError: no method matching machine(::Array{UInt8,1}, ::Array{Float64,2}, ::CategoricalArrays.CategoricalArray{Int64,1,UInt32,Int64,CategoricalArrays.CategoricalValue{Int64,UInt32},Union{}})
Closest candidates are:
  machine(::Type{#s41} where #s41<:MLJModelInterface.Model, ::Any...) at C:\Users\ivica\.julia\packages\MLJBase\fYEbE\src\machines.jl:203
  machine(::Static, ::Any...) at C:\Users\ivica\.julia\packages\MLJBase\fYEbE\src\machines.jl:213
  machine(::MLJModelInterface.Model, ::Any, ::Any...) at C:\Users\ivica\.julia\packages\MLJBase\fYEbE\src\machines.jl:231
  ...
Stacktrace:
 [1] machine(::String, ::Array{Float64,2}, ::Vararg{Any,N} where N; kwargs::Base.Iterators.Pairs{Union{},Union{},Tuple{},NamedTuple{(),Tuple{}}}) at C:\Users\ivica\.julia\packages\MLJBase\fYEbE\src\machines.jl:702
 [2] machine(::String, ::Array{Float64,2}, ::CategoricalArrays.CategoricalArray{Int64,1,UInt32,Int64,CategoricalArrays.CategoricalValue{Int64,UInt32},Union{}}) at C:\Users\ivica\.julia\packages\MLJBase\fYEbE\src\machines.jl:694
 [3] top-level scope at REPL[26]:1

I did using MLJGLMInterface and got this:

[warn | JLSO]: KeyError: key DataFrames [a93c6f00-e57d-5684-b7b6-d8193f3e46c0] not found
Machine{ProbabilisticTunedModel{RandomSearch,}} @602 trained 1 time.
  args:
    1:  Source @074`AbstractArray{Continuous,2}`
    2:  Source @801`AbstractArray{OrderedFactor{2},1}`

Which after doing using DataFrames turned into

Machine{ProbabilisticTunedModel{RandomSearch,}} @171 trained 1 time.
  args:
    1:  Source @045`AbstractArray{Continuous,2}`
    2:  Source @500`AbstractArray{OrderedFactor{2},1}`

It looked like it had worked in the REPL, so I went to the directory where I have all my files and moved Project.toml to a different location to have an empty ] status output and repeated this opening that directory with VSCode. However, I still got the same error as in the issue post

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants