diff --git a/Project.toml b/Project.toml index 2b7c7729..438c1337 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MLJBase" uuid = "a7f614a8-145f-11e9-1d2a-a57a1082229d" authors = ["Anthony D. Blaom "] -version = "0.15.1" +version = "0.15.2" [deps] CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" diff --git a/src/MLJBase.jl b/src/MLJBase.jl index 4937a6fe..cda90d22 100644 --- a/src/MLJBase.jl +++ b/src/MLJBase.jl @@ -186,7 +186,7 @@ export orientation, reports_each_observation, spports_weights, prediction_type # measures/continuous.jl: -export mav, mae, mape, rms, rmsl, rmslp1, rmsp, l1, l2 +export mav, mae, mape, rms, rmsl, rmslp1, rmsp, l1, l2, log_cosh # measures/confusion_matrix.jl: export confusion_matrix, confmat diff --git a/src/measures/continuous.jl b/src/measures/continuous.jl index b40294e7..39045443 100644 --- a/src/measures/continuous.jl +++ b/src/measures/continuous.jl @@ -320,3 +320,33 @@ function (m::MAPE)(ŷ::Vec{<:Real}, y::Vec{<:Real}) end return ret / count end + +struct LogCosh <: Measure end + +""" + log_cosh(ŷ, y) + +Log-Cosh per-observation loss: + +``\\text{Log-Cosh Loss} = log(cosh(ŷᵢ-yᵢ))`` + +where `yᵢ` is the target and `ŷᵢ` is the output. + +For more information, run `info(log_cosh)`. +""" +const log_cosh = LogCosh() + +metadata_measure(LogCosh; + name = "log_cosh", + target_scitype = Union{Vec{Continuous},Vec{Count}}, + prediction_type = :deterministic, + orientation = :loss, + reports_each_observation = true, + is_feature_dependent = false, + supports_weights = false, + docstring = "log cosh loss; aliases: `log_cosh`.") + +function (log_cosh::LogCosh)(ŷ::Vec{<:Real}, y::Vec{<:Real}) + check_dimensions(ŷ, y) + return log.(cosh.(ŷ-y)) +end diff --git a/src/univariate_finite/arrays.jl b/src/univariate_finite/arrays.jl index ff68e611..55978e09 100644 --- a/src/univariate_finite/arrays.jl +++ b/src/univariate_finite/arrays.jl @@ -97,9 +97,10 @@ for func in [:pdf, :logpdf] u::AbstractArray{UnivariateFinite{S,V,R,P},N}, C::AbstractVector{<:Union{V, CategoricalValue{V,R}}}) where {S,V,R,P,N} - ret = Array{P,N+1}(undef, size(u)..., length(C)) + #ret = Array{P,N+1}(undef, size(u)..., length(C)) + ret = zeros(P, size(u)..., length(C)) for i in eachindex(C) - ret[fill(:,N)...,i] = broadcast($func, u, C[i]) + ret[fill(:,N)...,i] .= broadcast($func, u, C[i]) end return ret end @@ -129,8 +130,13 @@ function Base.Broadcast.broadcasted( cv::CategoricalValue) where {S,V,R,P,N} cv in classes(u) || _err_missing_class(cv) - - return get(u.prob_given_ref, int(cv), zeros(P, size(u))) + + f() = zeros(P, size(u)) #default caller function + + return Base.Broadcast.Broadcasted( + identity, + (get(f, u.prob_given_ref, int(cv)),) + ) end # pdf.(u, v) @@ -162,41 +168,33 @@ function Base.Broadcast.broadcasted( raw::Union{V,AbstractArray{V,N}}) where {S,V,R,P,N} cat = transform(classes(u), raw) - return broadcast(pdf, u, cat) + return Base.Broadcast.broadcasted(pdf, u, cat) end # logpdf.(u::UniFinArr{S,V,R,P,N}, cv::CategoricalValue) # logpdf.(u::UniFinArr{S,V,R,P,N}, v::AbstractArray{<:CategoricalValue{V,R},N}) -# logpdf.(u::UniFinArr{S,V,R,P,N}, raw::V) -# logpdf.(u::UniFinArr{S,V,R,P,N}, raw::AbstractArray{V,N}) +# logpdf.(u::UniFinArr{S,V,R,P,N}, raw::AbstractArray{V,N}) +# logpdf.(u::UniFinArr{S,V,R,P,N}, raw::V) for typ in (:CategoricalValue, - :(AbstractArray{<:CategoricalValue{V,R},N}), + :(AbstractArray{<:CategoricalValue{V,R},N}), :V, :(AbstractArray{V,N})) - if typ == :CategoricalValue || typ == :V - eval(quote - function Base.Broadcast.broadcasted( - ::typeof(logpdf), - u::UniFinArr{S,V,R,P,N}, - c::$typ) where {S,V,R,P,N} - - # Start with the pdf array - pdf_arr = pdf.(u, c) - - # Create an uninitialized array similar to pdf_arr - # this avoids mutating the initial pdf_arr - result = similar(pdf_arr) - - # Take the log of each entry in-place - @simd for j in eachindex(result) - @inbounds result[j] = log(pdf_arr[j]) - end - - return result - end - end) - else + if typ == :CategoricalValue || typ == :V eval(quote + function Base.Broadcast.broadcasted( + ::typeof(logpdf), + u::UniFinArr{S,V,R,P,N}, + c::$typ) where {S,V,R,P,N} + + # Start with the pdf array + # take advantage of loop fusion + result = log.(pdf.(u, c)) + return result + end + end) + + else + eval(quote function Base.Broadcast.broadcasted( ::typeof(logpdf), u::UniFinArr{S,V,R,P,N}, @@ -213,7 +211,8 @@ for typ in (:CategoricalValue, return result end end) - end + end + end ## PERFORMANT BROADCASTING OF mode: diff --git a/test/measures/continuous.jl b/test/measures/continuous.jl index c84ce01e..a444f2fe 100644 --- a/test/measures/continuous.jl +++ b/test/measures/continuous.jl @@ -12,6 +12,7 @@ rng = StableRNG(666899) @test isapprox(mean(l1(yhat, y, w)), mae(yhat, y, w)) @test isapprox(mean(l2(yhat, y)), 5) @test isapprox(mean(l2(yhat, y, w)), rms(yhat, y, w)^2) + @test isapprox(mean(log_cosh(yhat, y)), 1.3715546675) yhat = y .+ 1 @test isapprox(rmsl(yhat, y),