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

Generalize breaking down structures #5

Closed
oxinabox opened this issue Mar 25, 2019 · 3 comments
Closed

Generalize breaking down structures #5

oxinabox opened this issue Mar 25, 2019 · 3 comments

Comments

@oxinabox
Copy link
Member

Like Complex breaks down into two scalar plots,

For

struct Foo
   bar1::Bar
   bar2::Bar
end
struct Bar 
    x::Vector
    y::Real
    z::Real
end

Then a Foo in the above should break down into 4 scalar plots and 2 histograms.
With everything named approprately. e,g, a Foo/bar1/y, Foo/bar2/x etc.

This would allow easy logging of all the weights and baises in a Flux model.
probably also need a few other things like the ability to have a preprocess function (Tracker.data for Flux`),
and #4

@PhilipVinc
Copy link
Member

I agree. This means that preprocess should work somehow recursively.

I wonder what would be the best way to define such a preprocess function. A possible implementation might be to have a is_loggable(::Any) = false function that is always false except for types that can be read by tensorboard, and then call preprocess for all types that must be broken down in more fundamental parts.

The signature could be preprocess(name::String, value) and return a dict of names=>values.

I believe this would be somewhat expensive, but the time spent creating dicts and allocating memory should be negligible compared to the time taken by the main algorithm.

@oxinabox
Copy link
Member Author

a sketch

default_isleaf(val) = val isa Number || val isa AbstractArray
sublog(datum) = sublog(identity, default_isleaf, datum)
sublog(preprocess, datum) = sublog(preprocess, default_isleaf, datum)

function sublogs(preprocess, isleaf, datum, name="")
    subs = Pair{String, Any}[]

    for fieldname in fieldnames(T)
        subname = string(name,"/", fieldname)
        val = getfield(datum, fieldname)
        if isleaf(val)
            push!(subs, subname=>preprocess(val))
        else
            append!(
                subs,
                sublog(preprocess, isleaf, val, subname)
            )
        end
    end
    return subs
end

@PhilipVinc
Copy link
Member

Interesting. I realised that in some code that I am using to do essentially the same thing, the preprocess function is simply responsible for pushing to a queue objects, so that I could easily extend it for custom types (even if they are not leaf types).

using DataStructures
loggable(::Any) = false
loggable(::Real) = true
loggable(::AbstractVector{T}) where T<:Real = true

preprocess(name, val::Complex, data) = push!(data, name*"/re"=>real(val), name*"/im"=>imag(val))
preprocess(name, val::AbstractVector, data) where T<:Complex = push!(data, name*"/re"=>real.(val), name*"/im"=>imag.(val))
function preprocess(name, val, data)
    fn = fieldnames(typeof(val))
    for f=fn
        prop = getproperty(val, f)
        push!(data, name*"/$f" => prop)
    end
    data
end

function unpack(name, val)
    data = Stack{Pair{String,Any}}()
    push!(data, name => val)
    while !isempty(data)
        name, val = pop!(data)
        loggable(val) ? println("logged $name=>$val") : preprocess(name, val, data)
    end
end

struct test 
    a
    b
end

unpack("prova", test(1+1im,[2+3im,4+5im]))

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

2 participants