From ecda74d6d60052bd76a41948e2e5dbe14c501352 Mon Sep 17 00:00:00 2001 From: ValdarT Date: Thu, 23 May 2019 14:01:17 +0300 Subject: [PATCH 1/4] add LIBSVM --- Project.toml | 1 + src/LIBSVM.jl | 417 +++++++++++++++++++++++++++++++++++++++++++++++ src/MLJModels.jl | 1 + test/LIBSVM.jl | 101 ++++++++++++ test/runtests.jl | 4 + 5 files changed, 524 insertions(+) create mode 100644 src/LIBSVM.jl create mode 100644 test/LIBSVM.jl diff --git a/Project.toml b/Project.toml index 3549bf7d..e52d2e0b 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.2.2" CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" +LIBSVM = "b1bec4e5-fd48-53fe-b0cb-9723c09d164b" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MLJBase = "a7f614a8-145f-11e9-1d2a-a57a1082229d" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" diff --git a/src/LIBSVM.jl b/src/LIBSVM.jl new file mode 100644 index 00000000..f10cfe35 --- /dev/null +++ b/src/LIBSVM.jl @@ -0,0 +1,417 @@ +module LIBSVM_ + +#> export the new models you're going to define (and nothing else): +export LinearSVC, SVC +export NuSVC, NuSVR +export EpsilonSVR +export OneClassSVM +export Linearsolver, Kernel + +#> for all Supervised models: +import MLJBase + +#> for all classifiers: +using CategoricalArrays + +#> import package: +import ..LIBSVM + +""" + LinearSVC(; kwargs...) + +Linear support vector machine classifier using LIBLINEAR: https://www.csie.ntu.edu.tw/~cjlin/liblinear/ + +See also SVC, NuSVC +""" +mutable struct LinearSVC <: MLJBase.Deterministic + solver::LIBSVM.Linearsolver.LINEARSOLVER + weights::Union{Dict, Nothing} + tolerance::Float64 + cost::Float64 + p::Float64 + bias::Float64 +end + +function LinearSVC( + ;solver::LIBSVM.Linearsolver.LINEARSOLVER = LIBSVM.Linearsolver.L2R_L2LOSS_SVC_DUAL + ,weights::Union{Dict, Nothing} = nothing + ,tolerance::Float64 = Inf + ,cost::Float64 = 1.0 + ,p::Float64 = 0.1 + ,bias::Float64= -1.0) + + model = LinearSVC( + solver + ,weights + ,tolerance + ,cost + ,p + ,bias + ) + + message = MLJBase.clean!(model) #> future proof by including these + isempty(message) || @warn message #> two lines even if no clean! defined below + + return model +end + +""" + SVC(; kwargs...) + +Kernel support vector machine classifier using LIBSVM: https://www.csie.ntu.edu.tw/~cjlin/libsvm/ + +See also LinearSVC, NuSVC +""" +mutable struct SVC <: MLJBase.Deterministic + kernel::LIBSVM.Kernel.KERNEL + gamma::Union{Float64, Symbol} + weights::Union{Dict, Nothing} + cost::Float64 + degree::Int32 + coef0::Float64 + tolerance::Float64 + shrinking::Bool + probability::Bool +end + +function SVC( + ;kernel::LIBSVM.Kernel.KERNEL = LIBSVM.Kernel.RadialBasis + ,gamma::Union{Float64,Symbol} = :auto + ,weights::Union{Dict, Nothing} = nothing + ,cost::Float64 = 1.0 + ,degree::Int32 = Int32(3) + ,coef0::Float64 = 0.0 + ,tolerance::Float64 = .001 + ,shrinking::Bool = true + ,probability::Bool = false) + + model = SVC( + kernel + ,gamma + ,weights + ,cost + ,degree + ,coef0 + ,tolerance + ,shrinking + ,probability + ) + + message = MLJBase.clean!(model) #> future proof by including these + isempty(message) || @warn message #> two lines even if no clean! defined below + + return model +end + +""" + NuSVC(; kwargs...) + +Kernel support vector machine classifier using LIBSVM: https://www.csie.ntu.edu.tw/~cjlin/libsvm/ + +See also LinearSVC, SVC +""" +mutable struct NuSVC <: MLJBase.Deterministic + kernel::LIBSVM.Kernel.KERNEL + gamma::Union{Float64,Symbol} + weights::Union{Dict, Nothing} + nu::Float64 + cost::Float64 + degree::Int32 + coef0::Float64 + tolerance::Float64 + shrinking::Bool +end + +function NuSVC( + ;kernel::LIBSVM.Kernel.KERNEL = LIBSVM.Kernel.RadialBasis + ,gamma::Union{Float64,Symbol} = :auto + ,weights::Union{Dict, Nothing} = nothing + ,nu::Float64 = 0.5 + ,cost::Float64 = 1.0 + ,degree::Int32 = Int32(3) + ,coef0::Float64 = 0. + ,tolerance::Float64 = .001 + ,shrinking::Bool = true) + + model = NuSVC( + kernel + ,gamma + ,weights + ,nu + ,cost + ,degree + ,coef0 + ,tolerance + ,shrinking + ) + + message = MLJBase.clean!(model) #> future proof by including these + isempty(message) || @warn message #> two lines even if no clean! defined below + + return model +end + +mutable struct OneClassSVM <: MLJBase.Deterministic + kernel::LIBSVM.Kernel.KERNEL + gamma::Union{Float64, Symbol} + nu::Float64 + cost::Float64 + degree::Int32 + coef0::Float64 + tolerance::Float64 + shrinking::Bool +end + +function OneClassSVM( + ;kernel::LIBSVM.Kernel.KERNEL = LIBSVM.Kernel.RadialBasis + ,gamma::Union{Float64, Symbol} = :auto + ,nu::Float64 = 0.1 + ,cost::Float64 = 1.0 + ,degree::Int32 = Int32(3) + ,coef0::Float64 = 0.0 + ,tolerance::Float64 = .001 + ,shrinking::Bool = true) + + model = OneClassSVM( + kernel + ,gamma + ,nu + ,cost + ,degree + ,coef0 + ,tolerance + ,shrinking + ) + + message = MLJBase.clean!(model) #> future proof by including these + isempty(message) || @warn message #> two lines even if no clean! defined below + + return model +end + +""" + NuSVR(; kwargs...) + +Kernel support vector machine regressor using LIBSVM: https://www.csie.ntu.edu.tw/~cjlin/libsvm/ + +See also EpsilonSVR +""" +mutable struct NuSVR <: MLJBase.Deterministic + kernel::LIBSVM.Kernel.KERNEL + gamma::Union{Float64, Symbol} + nu::Float64 + cost::Float64 + degree::Int32 + coef0::Float64 + tolerance::Float64 + shrinking::Bool +end + +function NuSVR( + ;kernel::LIBSVM.Kernel.KERNEL = LIBSVM.Kernel.RadialBasis + ,gamma::Union{Float64, Symbol} = :auto + ,nu::Float64 = 0.5 + ,cost::Float64 = 1.0 + ,degree::Int32 = Int32(3) + ,coef0::Float64 = 0. + ,tolerance::Float64 = .001 + ,shrinking::Bool = true) + + model = NuSVR( + kernel + ,gamma + ,nu + ,cost + ,degree + ,coef0 + ,tolerance + ,shrinking + ) + + message = MLJBase.clean!(model) #> future proof by including these + isempty(message) || @warn message #> two lines even if no clean! defined below + + return model +end + +""" + EpsilonSVR(; kwargs...) + +Kernel support vector machine regressor using LIBSVM: https://www.csie.ntu.edu.tw/~cjlin/libsvm/ + +See also NuSVR +""" +mutable struct EpsilonSVR <: MLJBase.Deterministic + kernel::LIBSVM.Kernel.KERNEL + gamma::Union{Float64, Symbol} + epsilon::Float64 + cost::Float64 + degree::Int32 + coef0::Float64 + tolerance::Float64 + shrinking::Bool +end + +function EpsilonSVR( + ;kernel::LIBSVM.Kernel.KERNEL = LIBSVM.Kernel.RadialBasis + ,gamma::Union{Float64, Symbol} = :auto + ,epsilon::Float64 = 0.1 + ,cost::Float64 = 1.0 + ,degree::Int32 = Int32(3) + ,coef0::Float64 = 0. + ,tolerance::Float64 = .001 + ,shrinking::Bool = true) + + model = EpsilonSVR( + kernel + ,gamma + ,epsilon + ,cost + ,degree + ,coef0 + ,tolerance + ,shrinking + ) + + message = MLJBase.clean!(model) #> future proof by including these + isempty(message) || @warn message #> two lines even if no clean! defined below + + return model +end + + +""" + get_svm_parameters(model::Union{SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM}) + +Helper function to get the parameters from the SVM model struct. +""" +function get_svm_parameters(model::Union{SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM}) + #Build arguments for calling svmtrain + model.gamma == :auto && (model.gamma = 1.0/size(matrix(X)', 1)) + params = Tuple{Symbol, Any}[] + push!(params, (:svmtype, eval(Meta.parse("LIBSVM.$(typeof(model))")))) # LIBSVM model type + for fn in fieldnames(typeof(model)) + if fn != :fit + push!(params, (fn, getfield(model, fn))) + end + end + + return params +end + + +function MLJBase.fit(model::LinearSVC, verbosity::Int, X, y) + + Xmatrix = MLJBase.matrix(X)' # notice the transpose + y_plain = MLJBase.int(y) + decode = MLJBase.decoder(y[1]) # for predict method + + cache = nothing + + result = LIBSVM.LIBLINEAR.linear_train(y_plain, Xmatrix, + weights = model.weights, solver_type = Int32(model.solver), + C = model.cost, p = model.p, bias = model.bias, + eps = model.tolerance, verbose = ifelse(verbosity > 0, true, false) + ) + + fitresult = (result, decode) + report = nothing + + return fitresult, cache, report +end + +function MLJBase.fit(model::Union{SVC, NuSVC}, verbosity::Int, X, y) + + Xmatrix = MLJBase.matrix(X)' # notice the transpose + y_plain = MLJBase.int(y) + decode = MLJBase.decoder(y[1]) # for predict method + + cache = nothing + + result = LIBSVM.svmtrain(Xmatrix, y_plain; + get_svm_parameters(model)..., + verbose = ifelse(verbosity > 0, true, false) + ) + + fitresult = (result, decode) + report = nothing + + return fitresult, cache, report +end + +function MLJBase.fit(model::Union{NuSVR, EpsilonSVR}, verbosity::Int, X, y) + + Xmatrix = MLJBase.matrix(X)' # notice the transpose + + cache = nothing + + fitresult = LIBSVM.svmtrain(Xmatrix, y; + get_svm_parameters(model)..., + verbose = ifelse(verbosity > 0, true, false) + ) + + report = nothing + + return fitresult, cache, report +end + +function MLJBase.fit(model::OneClassSVM, verbosity::Int, X) + + Xmatrix = MLJBase.matrix(X)' # notice the transpose + + cache = nothing + + fitresult = LIBSVM.svmtrain(Xmatrix; + get_svm_parameters(model)..., + verbose = ifelse(verbosity > 0, true, false) + ) + + report = nothing + + return fitresult, cache, report +end + + +function MLJBase.predict(model::LinearSVC, fitresult, Xnew) + result, decode = fitresult + (p,d) = LIBSVM.LIBLINEAR.linear_predict(result, MLJBase.matrix(Xnew)') + return decode(p) +end + +function MLJBase.predict(model::Union{SVC, NuSVC}, fitresult, Xnew) + result, decode = fitresult + (p,d) = LIBSVM.svmpredict(result, MLJBase.matrix(Xnew)') + return decode(p) +end + +function MLJBase.predict(model::Union{NuSVR, EpsilonSVR}, fitresult, Xnew) + (p,d) = LIBSVM.svmpredict(fitresult, MLJBase.matrix(Xnew)') + return p +end + +function MLJBase.predict(model::OneClassSVM, fitresult, Xnew) + (p,d) = LIBSVM.svmpredict(fitresult, MLJBase.matrix(Xnew)') + return p +end + + +# metadata +MLJBase.load_path(::Type{<:LinearSVC}) = "MLJModels.LIBSVM_.LinearSVC" +MLJBase.load_path(::Type{<:SVC}) = "MLJModels.LIBSVM_.SVC" +MLJBase.load_path(::Type{<:NuSVC}) = "MLJModels.LIBSVM_.NuSVC" +MLJBase.load_path(::Type{<:NuSVR}) = "MLJModels.LIBSVM_.NuSVR" +MLJBase.load_path(::Type{<:EpsilonSVR}) = "MLJModels.LIBSVM_.EpsilonSVR" +MLJBase.load_path(::Type{<:OneClassSVM}) = "MLJModels.LIBSVM_.OneClassSVM" + +const SVM = Union{LinearSVC, SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM} # all SVM models defined here +MLJBase.package_name(::Type{<:SVM}) = "LIBSVM" +MLJBase.package_uuid(::Type{<:SVM}) = "b1bec4e5-fd48-53fe-b0cb-9723c09d164b" +MLJBase.is_pure_julia(::Type{<:SVM}) = false +MLJBase.package_url(::Type{<:SVM}) = "https://github.com/mpastell/LIBSVM.jl" +MLJBase.input_is_multivariate(::Type{<:SVM}) = true +MLJBase.input_scitype_union(::Type{<:SVM}) = MLJBase.Continuous +MLJBase.target_scitype_union(::Type{<:Union{LinearSVC, SVC, NuSVC}}) = MLJBase.Finite +MLJBase.target_scitype_union(::Type{<:Union{NuSVR, EpsilonSVR}}) = MLJBase.Continuous +MLJBase.target_scitype_union(::Type{<:OneClassSVM}) = MLJBase.Unknown # Bool (true means inlier) + +end # module \ No newline at end of file diff --git a/src/MLJModels.jl b/src/MLJModels.jl index 3bdccd5a..73950f64 100755 --- a/src/MLJModels.jl +++ b/src/MLJModels.jl @@ -15,6 +15,7 @@ function __init__() @require NaiveBayes="9bbee03b-0db5-5f46-924f-b5c9c21b8c60" include("NaiveBayes.jl") @require ScikitLearn="3646fa90-6ef7-5e7e-9f22-8aca16db6324" include("ScikitLearn.jl") @require XGBoost = "009559a3-9522-5dbb-924b-0b6ed2b22bb9" include("XGBoost.jl") + @require LIBSVM="b1bec4e5-fd48-53fe-b0cb-9723c09d164b" include("LIBSVM.jl") end end # module diff --git a/test/LIBSVM.jl b/test/LIBSVM.jl new file mode 100644 index 00000000..ece5d791 --- /dev/null +++ b/test/LIBSVM.jl @@ -0,0 +1,101 @@ +module TestLIBSVM + +using MLJBase +using Test +using LinearAlgebra + +using MLJModels.LIBSVM_ +using CategoricalArrays + + +## CLASSIFIERS + +plain_classifier = SVC() +nu_classifier = NuSVC() +linear_classifier = LinearSVC() + +# test preservation of categorical levels: +task = load_iris(); +X, y = X_and_y(task); +train, test = partition(eachindex(y), 0.6); # levels of y are split across split + +fitresultC, cacheC, reportC = MLJBase.fit(plain_classifier, 1, + selectrows(X, train), y[train]); +fitresultCnu, cacheCnu, reportCnu = MLJBase.fit(nu_classifier, 1, + selectrows(X, train), y[train]); +fitresultCL, cacheCL, reportCL = MLJBase.fit(linear_classifier, 1, + selectrows(X, train), y[train]); +pcpred = MLJBase.predict(plain_classifier, fitresultC, selectrows(X, test)); +nucpred = MLJBase.predict(nu_classifier, fitresultCnu, selectrows(X, test)); +lcpred = MLJBase.predict(linear_classifier, fitresultCL, selectrows(X, test)); + +@test Set(classes(pcpred[1])) == Set(classes(y[1])) +@test Set(classes(nucpred[1])) == Set(classes(y[1])) +@test Set(classes(lcpred[1])) == Set(classes(y[1])) + +# test with linear data: +x1 = randn(3000); +x2 = randn(3000); +x3 = randn(3000); +X = (x1=x1, x2=x2, x3=x3); +y = x1 - x2 -2x3; +ycat = map(y) do η + η > 0 ? "go" : "stop" +end |> categorical; +train, test = partition(eachindex(ycat), 0.8); +fitresultC, cacheC, reportC = MLJBase.fit(plain_classifier, 1, + selectrows(X, train), ycat[train]); +fitresultCnu, cacheCnu, reportCnu = MLJBase.fit(nu_classifier, 1, + selectrows(X, train), ycat[train]); +fitresultCL, cacheCL, reportCL = MLJBase.fit(linear_classifier, 1, + selectrows(X, train), ycat[train]); +pcpred = MLJBase.predict(plain_classifier, fitresultC, selectrows(X, test)); +nucpred = MLJBase.predict(nu_classifier, fitresultCnu, selectrows(X, test)); +lcpred = MLJBase.predict(linear_classifier, fitresultCL, selectrows(X, test)); +@test sum(pcpred .!= ycat[test])/length(ycat) < 0.05 +@test sum(nucpred .!= ycat[test])/length(ycat) < 0.05 +@test sum(lcpred .!= ycat[test])/length(ycat) < 0.05 + + +## REGRESSORS + +plain_regressor = EpsilonSVR() +nu_regressor = NuSVR() + +# test with linear data: +fitresultR, cacheR, reportR = MLJBase.fit(plain_regressor, 1, + selectrows(X, train), y[train]); +fitresultRnu, cacheRnu, reportRnu = MLJBase.fit(nu_regressor, 1, + selectrows(X, train), y[train]); + +rpred = MLJBase.predict(plain_regressor, fitresultR, selectrows(X, test)); +nurpred = MLJBase.predict(nu_regressor, fitresultRnu, selectrows(X, test)); + +@test norm(rpred - y[test])/sqrt(length(y)) < 0.2 +@test norm(nurpred - y[test])/sqrt(length(y)) < 0.2 + + +## ANOMALY DETECTION + +oneclasssvm = OneClassSVM() + +fitresultoc, cacheoc, reportoc = MLJBase.fit(oneclasssvm, 1, + selectrows(X, train)); +ocpred = MLJBase.predict(oneclasssvm, fitresultoc, selectrows(X, test)); # output is BitArray + +# test whether the proprotion of outliers corresponds to the `nu` parameter +@test isapprox((length(train) - sum(MLJBase.predict(oneclasssvm, fitresultoc, selectrows(X, train)))) / length(train), oneclasssvm.nu, atol=0.001) +@test isapprox((length(test) - sum(ocpred)) / length(test), oneclasssvm.nu, atol=0.05) + + +## INFO + +info(LinearSVC) +info(SVC) +info(NuSVC) +info(NuSVR) +info(EpsilonSVR) +info(OneClassSVM) + +end +true diff --git a/test/runtests.jl b/test/runtests.jl index a36559ef..9db588f9 100755 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,6 +33,10 @@ end @test include("ScikitLearn.jl") end +@testset "LIBSVM" begin + @test include("LIBSVM.jl") +end + @testset "NaiveBayes" begin @test include("NaiveBayes.jl") end From 1ff113d39c075b39f1db744691db167748814616 Mon Sep 17 00:00:00 2001 From: ValdarT Date: Mon, 27 May 2019 11:52:33 +0300 Subject: [PATCH 2/4] fix LIBSVM --- src/LIBSVM.jl | 15 +++++++-------- test/LIBSVM.jl | 7 ++++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/LIBSVM.jl b/src/LIBSVM.jl index f10cfe35..45d36155 100644 --- a/src/LIBSVM.jl +++ b/src/LIBSVM.jl @@ -5,7 +5,6 @@ export LinearSVC, SVC export NuSVC, NuSVR export EpsilonSVR export OneClassSVM -export Linearsolver, Kernel #> for all Supervised models: import MLJBase @@ -287,13 +286,10 @@ Helper function to get the parameters from the SVM model struct. """ function get_svm_parameters(model::Union{SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM}) #Build arguments for calling svmtrain - model.gamma == :auto && (model.gamma = 1.0/size(matrix(X)', 1)) params = Tuple{Symbol, Any}[] push!(params, (:svmtype, eval(Meta.parse("LIBSVM.$(typeof(model))")))) # LIBSVM model type for fn in fieldnames(typeof(model)) - if fn != :fit - push!(params, (fn, getfield(model, fn))) - end + push!(params, (fn, getfield(model, fn))) end return params @@ -328,6 +324,7 @@ function MLJBase.fit(model::Union{SVC, NuSVC}, verbosity::Int, X, y) cache = nothing + model.gamma == :auto && (model.gamma = 1.0/size(Xmatrix, 1)) result = LIBSVM.svmtrain(Xmatrix, y_plain; get_svm_parameters(model)..., verbose = ifelse(verbosity > 0, true, false) @@ -345,6 +342,7 @@ function MLJBase.fit(model::Union{NuSVR, EpsilonSVR}, verbosity::Int, X, y) cache = nothing + model.gamma == :auto && (model.gamma = 1.0/size(Xmatrix, 1)) fitresult = LIBSVM.svmtrain(Xmatrix, y; get_svm_parameters(model)..., verbose = ifelse(verbosity > 0, true, false) @@ -361,6 +359,7 @@ function MLJBase.fit(model::OneClassSVM, verbosity::Int, X) cache = nothing + model.gamma == :auto && (model.gamma = 1.0/size(Xmatrix, 1)) fitresult = LIBSVM.svmtrain(Xmatrix; get_svm_parameters(model)..., verbose = ifelse(verbosity > 0, true, false) @@ -389,9 +388,9 @@ function MLJBase.predict(model::Union{NuSVR, EpsilonSVR}, fitresult, Xnew) return p end -function MLJBase.predict(model::OneClassSVM, fitresult, Xnew) +function MLJBase.transform(model::OneClassSVM, fitresult, Xnew) (p,d) = LIBSVM.svmpredict(fitresult, MLJBase.matrix(Xnew)') - return p + return categorical(p) end @@ -412,6 +411,6 @@ MLJBase.input_is_multivariate(::Type{<:SVM}) = true MLJBase.input_scitype_union(::Type{<:SVM}) = MLJBase.Continuous MLJBase.target_scitype_union(::Type{<:Union{LinearSVC, SVC, NuSVC}}) = MLJBase.Finite MLJBase.target_scitype_union(::Type{<:Union{NuSVR, EpsilonSVR}}) = MLJBase.Continuous -MLJBase.target_scitype_union(::Type{<:OneClassSVM}) = MLJBase.Unknown # Bool (true means inlier) +MLJBase.output_scitype_union(::Type{<:OneClassSVM}) = MLJBase.Finite{2} # Bool (true means inlier) end # module \ No newline at end of file diff --git a/test/LIBSVM.jl b/test/LIBSVM.jl index ece5d791..4db6254a 100644 --- a/test/LIBSVM.jl +++ b/test/LIBSVM.jl @@ -4,6 +4,7 @@ using MLJBase using Test using LinearAlgebra +import LIBSVM using MLJModels.LIBSVM_ using CategoricalArrays @@ -81,11 +82,11 @@ oneclasssvm = OneClassSVM() fitresultoc, cacheoc, reportoc = MLJBase.fit(oneclasssvm, 1, selectrows(X, train)); -ocpred = MLJBase.predict(oneclasssvm, fitresultoc, selectrows(X, test)); # output is BitArray +ocpred = MLJBase.transform(oneclasssvm, fitresultoc, selectrows(X, test)); # output is CategoricalArray{Bool} # test whether the proprotion of outliers corresponds to the `nu` parameter -@test isapprox((length(train) - sum(MLJBase.predict(oneclasssvm, fitresultoc, selectrows(X, train)))) / length(train), oneclasssvm.nu, atol=0.001) -@test isapprox((length(test) - sum(ocpred)) / length(test), oneclasssvm.nu, atol=0.05) +@test isapprox((length(train) - sum(MLJBase.transform(oneclasssvm, fitresultoc, selectrows(X, train)) .== true)) / length(train), oneclasssvm.nu, atol=0.001) +@test isapprox((length(test) - sum(ocpred .== true)) / length(test), oneclasssvm.nu, atol=0.05) ## INFO From b65fbfd27b4fb4b902f83652329ee4b02b6bc116 Mon Sep 17 00:00:00 2001 From: ValdarT Date: Mon, 27 May 2019 11:54:52 +0300 Subject: [PATCH 3/4] OneClassSVM to Unsupervised --- src/LIBSVM.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LIBSVM.jl b/src/LIBSVM.jl index 45d36155..120b342b 100644 --- a/src/LIBSVM.jl +++ b/src/LIBSVM.jl @@ -150,7 +150,7 @@ function NuSVC( return model end -mutable struct OneClassSVM <: MLJBase.Deterministic +mutable struct OneClassSVM <: MLJBase.Unsupervised kernel::LIBSVM.Kernel.KERNEL gamma::Union{Float64, Symbol} nu::Float64 From cceb87fd6856f782035678513a1181e563c9ca93 Mon Sep 17 00:00:00 2001 From: ValdarT Date: Mon, 27 May 2019 14:00:01 +0300 Subject: [PATCH 4/4] fix LIBSVM model type mapping didn't quite understand why it didn't work before but works now --- src/LIBSVM.jl | 29 +++++++++++++++++++++++++++-- test/LIBSVM.jl | 1 + 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/LIBSVM.jl b/src/LIBSVM.jl index 120b342b..d5924531 100644 --- a/src/LIBSVM.jl +++ b/src/LIBSVM.jl @@ -279,6 +279,32 @@ function EpsilonSVR( end +const SVM = Union{LinearSVC, SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM} # all SVM models defined here + + +""" + map_model_type(model::SVM) + +Helper function to map the model to the correct LIBSVM model type needed for function dispatch. +""" +function map_model_type(model::SVM) + if isa(model, LinearSVC) + return LIBSVM.LinearSVC + elseif isa(model, SVC) + return LIBSVM.SVC + elseif isa(model, NuSVC) + return LIBSVM.NuSVC + elseif isa(model, NuSVR) + return LIBSVM.NuSVR + elseif isa(model, EpsilonSVR) + return LIBSVM.EpsilonSVR + elseif isa(model, OneClassSVM) + return LIBSVM.OneClassSVM + else + error("Got unsupported model type: $(typeof(model))") + end +end + """ get_svm_parameters(model::Union{SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM}) @@ -287,7 +313,7 @@ Helper function to get the parameters from the SVM model struct. function get_svm_parameters(model::Union{SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM}) #Build arguments for calling svmtrain params = Tuple{Symbol, Any}[] - push!(params, (:svmtype, eval(Meta.parse("LIBSVM.$(typeof(model))")))) # LIBSVM model type + push!(params, (:svmtype, map_model_type(model))) # get LIBSVM model type for fn in fieldnames(typeof(model)) push!(params, (fn, getfield(model, fn))) end @@ -402,7 +428,6 @@ MLJBase.load_path(::Type{<:NuSVR}) = "MLJModels.LIBSVM_.NuSVR" MLJBase.load_path(::Type{<:EpsilonSVR}) = "MLJModels.LIBSVM_.EpsilonSVR" MLJBase.load_path(::Type{<:OneClassSVM}) = "MLJModels.LIBSVM_.OneClassSVM" -const SVM = Union{LinearSVC, SVC, NuSVC, NuSVR, EpsilonSVR, OneClassSVM} # all SVM models defined here MLJBase.package_name(::Type{<:SVM}) = "LIBSVM" MLJBase.package_uuid(::Type{<:SVM}) = "b1bec4e5-fd48-53fe-b0cb-9723c09d164b" MLJBase.is_pure_julia(::Type{<:SVM}) = false diff --git a/test/LIBSVM.jl b/test/LIBSVM.jl index 4db6254a..86a9d920 100644 --- a/test/LIBSVM.jl +++ b/test/LIBSVM.jl @@ -4,6 +4,7 @@ using MLJBase using Test using LinearAlgebra +import MLJModels import LIBSVM using MLJModels.LIBSVM_ using CategoricalArrays