From b746afc4ebb59b05fcde73a9b317d86a994275f0 Mon Sep 17 00:00:00 2001 From: Filippo Vicentini Date: Tue, 26 Mar 2019 15:45:01 +0100 Subject: [PATCH 1/7] Rename Logger to TBLogger --- src/Logger.jl | 18 +++++++++--------- src/Loggers/LogHistograms.jl | 6 +++--- src/Loggers/LogValue.jl | 4 ++-- src/TensorBoardLogger.jl | 10 +++------- src/logging.jl | 4 ++-- test/runtests.jl | 4 ++-- 6 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/Logger.jl b/src/Logger.jl index 98c18f92..5359a6af 100644 --- a/src/Logger.jl +++ b/src/Logger.jl @@ -6,7 +6,7 @@ deletes previously created events. If `purge_step::Int` is passed, every step before `purge_step` will be ignored by tensorboard (usefull in the case of restarting a crashed computation). """ -function Logger(logdir; overwrite=false, time=time(), purge_step::Union{Int,Nothing}=nothing) +function TBLogger(logdir; overwrite=false, time=time(), purge_step::Union{Int,Nothing}=nothing) if overwrite rm(logdir; force=true, recursive=true) end @@ -33,11 +33,11 @@ function Logger(logdir; overwrite=false, time=time(), purge_step::Union{Int,Noth write_event(file, ev_0) end - Logger(realpath(logdir), file, all_files, start_step) + TBLogger(realpath(logdir), file, all_files, start_step, Info) end # normally the logs don't overwrite, but if you've not given a path, you clearly don't care. -Logger() = Logger("tensorboard_logs", overwrite=true) +TBLogger() = TBLogger("tensorboard_logs", overwrite=true) # Accessors """ @@ -45,15 +45,15 @@ Logger() = Logger("tensorboard_logs", overwrite=true) Returns the directory to which Logger `lg` is writing data. """ -logdir(lg::Logger) = lg.logdir +logdir(lg::TBLogger) = lg.logdir """ get_file(lg::Logger) -> IOS Returns the main `file` IOStream object of Logger `lg`. """ -get_file(lg::Logger) = lg.file -function add_log_file(lg::Logger, path::String) +get_file(lg::TBLogger) = lg.file +function add_log_file(lg::TBLogger, path::String) file = open(path, "w") lg.all_files[path] = file return file @@ -65,7 +65,7 @@ end Returns the `file` IOStream object of Logger `lg` writing to the tag `tags1/tags2.../tagsN`. """ -function get_file(lg::Logger, tags::String...) +function get_file(lg::TBLogger, tags::String...) key = joinpath(logdir(lg), tags...) if key ∈ lg.all_files return lg.all_files[key] @@ -80,7 +80,7 @@ end Sets the iteration counter in the logger to `iter`. This counter is used by the logger when no value is passed by the user. """ -set_step(lg::Logger, iter::Int) = lg.global_step = iter +set_step(lg::TBLogger, iter::Int) = lg.global_step = iter """ iteration(lg) @@ -88,7 +88,7 @@ set_step(lg::Logger, iter::Int) = lg.global_step = iter Returns the internal iteration counter of the logger. When no step keyword is provided to the loggers, it will use this value. """ -step(lg::Logger) = lg.global_step +step(lg::TBLogger) = lg.global_step # Additional things diff --git a/src/Loggers/LogHistograms.jl b/src/Loggers/LogHistograms.jl index 87b48a93..b00baad9 100644 --- a/src/Loggers/LogHistograms.jl +++ b/src/Loggers/LogHistograms.jl @@ -8,7 +8,7 @@ passed as a tuple holding the `N+1` bin edges and the height of the `N` bins. You can also pass the raw data, and a binning algorithm from `StatsBase.jl` will be used to bin the data. """ -function log_histogram(logger::Logger, name::String, (bins,weights)::Tuple{Vector,Vector}; +function log_histogram(logger::TBLogger, name::String, (bins,weights)::Tuple{Vector,Vector}; step=nothing) summ = SummaryCollection() push!(summ.value, histogram_summary(name, bins, weights)) @@ -21,7 +21,7 @@ end Bins the values found in `data` and logs them as an histogram under the tag `name`. """ -function log_histogram(logger::Logger, name::String, data::Vector; +function log_histogram(logger::TBLogger, name::String, data::Vector; step=nothing) summ = SummaryCollection() hvals = fit(Histogram, data) @@ -34,7 +34,7 @@ end Logs the vector found in `data` as an histogram under the name `name`. """ -function log_vector(logger::Logger, name::String, data::Vector; step=nothing) +function log_vector(logger::TBLogger, name::String, data::Vector; step=nothing) summ = SummaryCollection() push!(summ.value, histogram_summary(name, collect(0:length(data)),data)) write_event(logger.file, make_event(logger, summ, step)) diff --git a/src/Loggers/LogValue.jl b/src/Loggers/LogValue.jl index 5a74b4b2..c80ace1c 100644 --- a/src/Loggers/LogValue.jl +++ b/src/Loggers/LogValue.jl @@ -3,13 +3,13 @@ Logs a Floating-point variable with name `name` at step `step` """ -function log_value(logger::Logger, name::String, value::Real; step=nothing) +function log_value(logger::TBLogger, name::String, value::Real; step=nothing) summ = SummaryCollection() push!(summ.value, scalar_summary(name, value)) write_event(logger.file, make_event(logger, summ, step=step)) end -function log_value(logger::Logger, name::String, value::Complex; step=nothing) +function log_value(logger::TBLogger, name::String, value::Complex; step=nothing) log_value(logger, name*"/re", real(value), step) log_value(logger, name*"/im", imag(value), step) end diff --git a/src/TensorBoardLogger.jl b/src/TensorBoardLogger.jl index 17f72929..f7f661e0 100644 --- a/src/TensorBoardLogger.jl +++ b/src/TensorBoardLogger.jl @@ -3,7 +3,7 @@ module TensorBoardLogger using ProtoBuf using CRC32c -#TODO: remove it. Only needed to compute histogram bins. +#TODO: remove it. Only needed to compute histogram bins. using StatsBase # Protobuffer definitions for tensorboard @@ -15,7 +15,7 @@ include("protojl/event_pb.jl") include("utils.jl") # Logging structures -mutable struct Logger +mutable struct TBLogger <: AbstractLogger logdir::String file::IOStream all_files::Dict{String, IOStream} @@ -28,13 +28,9 @@ include("Loggers/LogHistograms.jl") include("Logger.jl") -#macro tb_log(name) -# :(_tb_log($(esc(string(name))), $(esc(name)))) -#end - export log_histogram, log_value, log_vector export scalar_summary, histogram_summary, make_event -export Logger +export TBLogger export set_tb_logdir, reset_tb_logs, default_logging_session diff --git a/src/logging.jl b/src/logging.jl index 0b31e7d9..4fbfa370 100644 --- a/src/logging.jl +++ b/src/logging.jl @@ -2,7 +2,7 @@ # protobuf is broken). const SummaryCollection(;kwargs...) = Summary(value=Base.Vector{Summary_Value}(); kwargs...) -function make_event(logger::Logger, summary::Summary; +function make_event(logger::TBLogger, summary::Summary; step::Union{Nothing, Int}=nothing) # If the step is not set explicitly, get it from the logger if typeof(step) == Nothing @@ -27,4 +27,4 @@ function write_event(file::IOStream, event::Event) flush(file) end -write_event(logger::Logger, event::Event) = write_event(logger.file, event) +write_event(logger::TBLogger, event::Event) = write_event(logger.file, event) diff --git a/test/runtests.jl b/test/runtests.jl index 4494f0a8..2f275018 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,7 +2,7 @@ using TensorBoardLogger using Test @testset "Scalar Value Logger" begin - logger = Logger("log/") + logger = TBLogger("log/") @test isdir("log/") step = 1 log_value(logger, "float32", 1.25f0, step=step) @@ -17,7 +17,7 @@ using Test end @testset "Histogram Value Logger" begin - logger = Logger("log/") + logger = TBLogger("log/") @test isdir("log/") step = 1 From b0dff9a93a86091bd650568aeff51a4c5e2390b2 Mon Sep 17 00:00:00 2001 From: Filippo Vicentini Date: Tue, 26 Mar 2019 15:53:58 +0100 Subject: [PATCH 2/7] [Pulled from NeuralQuantum.jl] Implement the AbstractLogger interface. --- Project.toml | 1 + README.md | 29 ++++++++---------- src/Logger.jl | 57 ++++++++++++++++++++++++++++++++---- src/Loggers/LogHistograms.jl | 11 +++++++ src/Loggers/LogValue.jl | 5 ++++ src/TensorBoardLogger.jl | 11 +++++++ 6 files changed, 93 insertions(+), 21 deletions(-) diff --git a/Project.toml b/Project.toml index de6f34bf..53848b77 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.1.0" [deps] CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" +DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" diff --git a/README.md b/README.md index 46e2cbf6..323e80a2 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ and from [TensorBoardX](https://tensorboardx.readthedocs.io/en/latest/). To use the library you must create a `Logger` object and then log data to it. - - `Logger(dir_path)` creates a logger saving data to the folder `dir_path` + - `TBLogger(dir_path)` creates a logger saving data to the folder `dir_path` - `log_value(logger, name, val)` logs to `logger` the value `val` under the tag `name` ## Supported values @@ -25,25 +25,22 @@ At the moment, you can log the following values: ## Example ``` -using TensorBoardLogger +using TensorBoardLogger, Logging, Random -lg = Logger("runs/run-12", overwrite=true) +lg=TBLogger() -for step=1:100 - ev = log_value(lg, "quan/prova1", step*1.5, step=step) - ev = log_value(lg, "quan/prova2", step*2.5, step=step) +with_logger(lg) do + for i=1:100 + x0 = 0.5+i/30; s0 = 0.5/(i/20); + edges = collect(-5:0.1:5) + centers = collect(edges[1:end-1] .+0.05) + histvals = [exp(-((c-x0)/s0)^2) for c=centers] + data_tuple = (edges, histvals) - x0 = 0.5+step/30; s0 = 0.5/(step/20); - edges = collect(-5:0.1:5) - centers = collect(edges[1:end-1] .+0.05) - histvals = [exp(-((c-x0)/s0)^2) for c=centers] - histvals./=sum(histvals) - data_tuple = (edges, histvals) - # Log pre-binned data - log_histogram(lg, "hist/cust", data_tuple, step=step) - # Automatically bin the data - log_histogram(lg, "hist/auto", randn(1000).*s0.+x0, step=step) + @info "test" i=i j=i^2 dd=rand(10).+0.1*i hh=data_tuple + @info "test_2" i=i j=2^i hh=data_tuple delta_step=0 + end end ``` diff --git a/src/Logger.jl b/src/Logger.jl index 5359a6af..53da14b6 100644 --- a/src/Logger.jl +++ b/src/Logger.jl @@ -6,7 +6,7 @@ deletes previously created events. If `purge_step::Int` is passed, every step before `purge_step` will be ignored by tensorboard (usefull in the case of restarting a crashed computation). """ -function TBLogger(logdir; overwrite=false, time=time(), purge_step::Union{Int,Nothing}=nothing) +function TBLogger(logdir; overwrite=false, time=time(), purge_step::Union{Int,Nothing}=nothing, min_level::LogLevel=Info) if overwrite rm(logdir; force=true, recursive=true) end @@ -33,7 +33,7 @@ function TBLogger(logdir; overwrite=false, time=time(), purge_step::Union{Int,No write_event(file, ev_0) end - TBLogger(realpath(logdir), file, all_files, start_step, Info) + TBLogger(realpath(logdir), file, all_files, start_step, min_level) end # normally the logs don't overwrite, but if you've not given a path, you clearly don't care. @@ -82,8 +82,10 @@ logger when no value is passed by the user. """ set_step(lg::TBLogger, iter::Int) = lg.global_step = iter +increment_step(lg::TBLogger, iter::Int) = lg.global_step += iter + """ - iteration(lg) + step(lg) Returns the internal iteration counter of the logger. When no step keyword is provided to the loggers, it will use this value. @@ -93,8 +95,6 @@ step(lg::TBLogger) = lg.global_step # Additional things -#const default_logging_session = Ref(Logger()) - """ set_tb_logdir(logdir, overwrite=false) Start a new log in the given directory @@ -111,3 +111,50 @@ function reset_tb_logs() logdir = default_logging_session[].logdir default_logging_session[] = Logger(logdir, overwrite=true) end + + +# Implement the AbstractLogger Interface + +catch_exceptions(lg::TBLogger) = false + +min_enabled_level(lg::TBLogger) = lg.min_level + +# For now, log everything that is above the lg.min_level +shouldlog(lg::TBLogger, level, _module, group, id) = true + +function handle_message(lg::TBLogger, level, message, _module, group, id, file, line; kwargs...) + # Unpack the message + summ = SummaryCollection() + i_step = 1 + + if !isempty(kwargs) + for (key,val) in pairs(kwargs) + # special values + if key == :delta_step + i_step = val + continue + end + + data = Stack{Pair{String,Any}}() + name = message*"/$key" + push!(data, name => val) + while !isempty(data) + name, val = pop!(data) + loggable(val) ? push!(summ.value, summary_impl(name, val)) : preprocess(name, val, data) + end + end + end + iter = increment_step(lg, i_step) + write_event(lg.file, make_event(lg, summ, step=iter)) +end + +loggable(::Any) = false + +function preprocess(name, val, data) + fn = fieldnames(typeof(val)) + for f=fn + prop = getproperty(val, f) + push!(data, name*"/$f" => prop) + end + data +end diff --git a/src/Loggers/LogHistograms.jl b/src/Loggers/LogHistograms.jl index b00baad9..926a2fc7 100644 --- a/src/Loggers/LogHistograms.jl +++ b/src/Loggers/LogHistograms.jl @@ -59,3 +59,14 @@ end ## Backward compatibility log_histogram(logger, name, value, step) = log_histogram(logger, name, value; step=step) + + +# Forward +preprocess(name, val::AbstractVector, data) where T<:Complex = push!(data, name*"/re"=>real.(val), name*"/im"=>imag.(val)) +preprocess(name, val::AbstractArray, data) = push!(data, name=>vec(val)) + +loggable(::AbstractVector{T}) where T<:Real = true +summary_impl(name, val::AbstractVector) = histogram_summary(name, collect(0:length(val)),val) + +loggable(::Tuple{Vector,Vector}) = true +summary_impl(name, (bins,weights)::Tuple{Vector,Vector}) = histogram_summary(name, bins, weights) diff --git a/src/Loggers/LogValue.jl b/src/Loggers/LogValue.jl index c80ace1c..9fdb5355 100644 --- a/src/Loggers/LogValue.jl +++ b/src/Loggers/LogValue.jl @@ -21,3 +21,8 @@ end ## Backward compatibility log_value(logger, name, value, step) = log_value(logger, name, value, step=step) + +# Forward +loggable(::Real) = true +preprocess(name, val::Complex, data) = push!(data, name*"/re"=>real(val), name*"/im"=>imag(val)) +summary_impl(name, value::Real) = scalar_summary(name, value) diff --git a/src/TensorBoardLogger.jl b/src/TensorBoardLogger.jl index f7f661e0..a815d759 100644 --- a/src/TensorBoardLogger.jl +++ b/src/TensorBoardLogger.jl @@ -2,10 +2,19 @@ module TensorBoardLogger using ProtoBuf using CRC32c +using DataStructures #TODO: remove it. Only needed to compute histogram bins. using StatsBase +# Import Base methods for Logging +using Base.CoreLogging: + global_logger, LogLevel, Info + +import Base.CoreLogging: + AbstractLogger, handle_message, shouldlog, min_enabled_level, + catch_exceptions + # Protobuffer definitions for tensorboard include("protojl/tensorflow.jl") include("protojl/summary_pb.jl") @@ -20,6 +29,8 @@ mutable struct TBLogger <: AbstractLogger file::IOStream all_files::Dict{String, IOStream} global_step::Int + + min_level::LogLevel end include("logging.jl") From 64d76eaa627326ac4e32f0a12f0755f6e64a75fa Mon Sep 17 00:00:00 2001 From: Filippo Vicentini Date: Mon, 1 Apr 2019 16:21:02 +0200 Subject: [PATCH 3/7] Rename delta_step -> log_step_increment as suggested by @oxinabox Add a few docstrings Remove Coveralls --- .travis.yml | 1 - src/Logger.jl | 35 +++++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index b297c1d3..130de64b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,3 @@ script: - julia --color=yes -e 'using Pkg; Pkg.activate(); Pkg.instantiate(); Pkg.test()'; after_success: - julia -e 'using Pkg; cd(Pkg.dir("TensorBoardLogger")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' - - julia -e 'using Pkg; cd(Pkg.dir("TensorBoardLogger")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'; diff --git a/src/Logger.jl b/src/Logger.jl index 53da14b6..fee523f1 100644 --- a/src/Logger.jl +++ b/src/Logger.jl @@ -130,7 +130,7 @@ function handle_message(lg::TBLogger, level, message, _module, group, id, file, if !isempty(kwargs) for (key,val) in pairs(kwargs) # special values - if key == :delta_step + if key == :log_step_increment i_step = val continue end @@ -148,13 +148,36 @@ function handle_message(lg::TBLogger, level, message, _module, group, id, file, write_event(lg.file, make_event(lg, summ, step=iter)) end +""" + loggable(value) -> Bool + +Returns `true` if `value` is a type that can be serialized into a `Summary` +ProtoBuffer, `false` otherwise. + +This is defined to be false for `::Any`, and for every supported TensorBoard +Plugin this method should be specialized for the relative type and return true. +""" loggable(::Any) = false -function preprocess(name, val, data) - fn = fieldnames(typeof(val)) - for f=fn - prop = getproperty(val, f) - push!(data, name*"/$f" => prop) +""" + preprocess(name, val, data) + +This method takes a tag `name` and the value `val::T` which cannot be directly +serialized into TensorBoard, and pushes into the stack `data` several +name-value pairs `Pair{String,Any}` containing simpler types. Those pairs will +be serialized if possible, otherwise `preprocess` will be called recursively. + +For a struct +""" +function preprocess(name, val::T, data) where T + if isstructtype(T) + fn = fieldnames(T) + for f=fn + prop = getproperty(val, f) + push!(data, name*"/$f" => prop) + end + else + throw(ErrorException("Can't log type $T, but can't preprocess it either.\n You should define preprocess(name, val::$T, data).")) end data end From 7b63bede9d3ec08766f440c23645fcb1138c9433 Mon Sep 17 00:00:00 2001 From: Filippo Vicentini Date: Wed, 3 Apr 2019 10:32:17 +0200 Subject: [PATCH 4/7] Make `preprocess` recursive as suggested by @oxinabox - preprocess now calls itself recursively and only pushes to a list if the type can be serialized. - Remove the `loggable` function as now it's no longer needed. - There is also no need for a stack (and the DataStructures dependency). Just use a Vector for `data` in `handle_message` - Fix the readme: `delta_step` -> `log_step_increment` --- Project.toml | 1 - README.md | 2 +- src/Logger.jl | 43 ++++++++++++++---------------------- src/Loggers/LogHistograms.jl | 20 ++++++++--------- src/Loggers/LogValue.jl | 13 ++++++----- src/TensorBoardLogger.jl | 1 - 6 files changed, 34 insertions(+), 46 deletions(-) diff --git a/Project.toml b/Project.toml index 53848b77..de6f34bf 100644 --- a/Project.toml +++ b/Project.toml @@ -5,7 +5,6 @@ version = "0.1.0" [deps] CRC32c = "8bf52ea8-c179-5cab-976a-9e18b702a9bc" -DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" ProtoBuf = "3349acd9-ac6a-5e09-bcdb-63829b23a429" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" diff --git a/README.md b/README.md index 323e80a2..8b5f76aa 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ with_logger(lg) do @info "test" i=i j=i^2 dd=rand(10).+0.1*i hh=data_tuple - @info "test_2" i=i j=2^i hh=data_tuple delta_step=0 + @info "test_2" i=i j=2^i hh=data_tuple log_step_increment=0 end end ``` diff --git a/src/Logger.jl b/src/Logger.jl index fee523f1..0b750dd2 100644 --- a/src/Logger.jl +++ b/src/Logger.jl @@ -125,56 +125,47 @@ shouldlog(lg::TBLogger, level, _module, group, id) = true function handle_message(lg::TBLogger, level, message, _module, group, id, file, line; kwargs...) # Unpack the message summ = SummaryCollection() - i_step = 1 + i_step = 1 # :log_step_increment default value if !isempty(kwargs) + data = Vector{Pair{String,Any}}() + + # ∀ (k-v) pairs, decompose values into objects that can be serialized for (key,val) in pairs(kwargs) - # special values + # special key describing step increment if key == :log_step_increment i_step = val continue end - data = Stack{Pair{String,Any}}() - name = message*"/$key" - push!(data, name => val) - while !isempty(data) - name, val = pop!(data) - loggable(val) ? push!(summ.value, summary_impl(name, val)) : preprocess(name, val, data) - end + preprocess(message*"/$key", val, data) + end + + # Serialize every object + for (name,val) in data + push!(summ.value, summary_impl(name, val)) end end iter = increment_step(lg, i_step) write_event(lg.file, make_event(lg, summ, step=iter)) end -""" - loggable(value) -> Bool - -Returns `true` if `value` is a type that can be serialized into a `Summary` -ProtoBuffer, `false` otherwise. - -This is defined to be false for `::Any`, and for every supported TensorBoard -Plugin this method should be specialized for the relative type and return true. -""" -loggable(::Any) = false - """ preprocess(name, val, data) -This method takes a tag `name` and the value `val::T` which cannot be directly -serialized into TensorBoard, and pushes into the stack `data` several -name-value pairs `Pair{String,Any}` containing simpler types. Those pairs will -be serialized if possible, otherwise `preprocess` will be called recursively. +This method takes a tag `name` and the value `val::T` pair. If type `T` can be +serialized to TensorBoard then the pair is pushed to `data`, otherwise it should +call `preprocess` recursively with some simpler types, until a serializable +type is finally hit. -For a struct +For a struct, it calls preprocess on every field. """ function preprocess(name, val::T, data) where T if isstructtype(T) fn = fieldnames(T) for f=fn prop = getproperty(val, f) - push!(data, name*"/$f" => prop) + preprocess(name*"/$f", val, data) end else throw(ErrorException("Can't log type $T, but can't preprocess it either.\n You should define preprocess(name, val::$T, data).")) diff --git a/src/Loggers/LogHistograms.jl b/src/Loggers/LogHistograms.jl index 926a2fc7..256293fa 100644 --- a/src/Loggers/LogHistograms.jl +++ b/src/Loggers/LogHistograms.jl @@ -1,4 +1,3 @@ - """ log_histogram(logger, name, (bins,weights); step) @@ -56,17 +55,16 @@ function histogram_summary(name::String, edges::Vector{T1}, hist_vals::Vector{T2 Summary_Value(tag=name, histo=hp) end -## Backward compatibility -log_histogram(logger, name, value, step) = - log_histogram(logger, name, value; step=step) - -# Forward -preprocess(name, val::AbstractVector, data) where T<:Complex = push!(data, name*"/re"=>real.(val), name*"/im"=>imag.(val)) -preprocess(name, val::AbstractArray, data) = push!(data, name=>vec(val)) +## Logger Interface -loggable(::AbstractVector{T}) where T<:Real = true -summary_impl(name, val::AbstractVector) = histogram_summary(name, collect(0:length(val)),val) +# Define the type(s) that can be serialized to TensorBoard +preprocess(name, val::AbstractVector{T}, data) where T<:Real = push!(data, name=>val) +summary_impl(name, val::AbstractVector{T}) where T<:Real = histogram_summary(name, collect(0:length(val)),val) -loggable(::Tuple{Vector,Vector}) = true +preprocess(name, (bins,weights)::Tuple{Vector,Vector}, data) where T<:Real = push!(data, name=>(bins, weights)) summary_impl(name, (bins,weights)::Tuple{Vector,Vector}) = histogram_summary(name, bins, weights) + +# Split complex numbers into real/complex pairs +preprocess(name, val::AbstractVector, data) where T<:Complex = push!(data, name*"/re"=>real.(val), name*"/im"=>imag.(val)) +preprocess(name, val::AbstractArray, data) = push!(data, name=>vec(val)) diff --git a/src/Loggers/LogValue.jl b/src/Loggers/LogValue.jl index 9fdb5355..7e555a80 100644 --- a/src/Loggers/LogValue.jl +++ b/src/Loggers/LogValue.jl @@ -18,11 +18,12 @@ function scalar_summary(name::String, value::Real) Summary_Value(tag=name, simple_value=value) end -## Backward compatibility -log_value(logger, name, value, step) = - log_value(logger, name, value, step=step) -# Forward -loggable(::Real) = true -preprocess(name, val::Complex, data) = push!(data, name*"/re"=>real(val), name*"/im"=>imag(val)) +## Logger Interface + +# Define the type(s) that can be serialized to TensorBoard +preprocess(name, val::Real, data) where T<:Real = push!(data, name=>val) summary_impl(name, value::Real) = scalar_summary(name, value) + +# Split complex numbers into real/complex pairs +preprocess(name, val::Complex, data) = push!(data, name*"/re"=>real(val), name*"/im"=>imag(val)) diff --git a/src/TensorBoardLogger.jl b/src/TensorBoardLogger.jl index a815d759..0043fc9b 100644 --- a/src/TensorBoardLogger.jl +++ b/src/TensorBoardLogger.jl @@ -2,7 +2,6 @@ module TensorBoardLogger using ProtoBuf using CRC32c -using DataStructures #TODO: remove it. Only needed to compute histogram bins. using StatsBase From 00b38e04e199f3517dccd7f2d4c6eee0bbc275a2 Mon Sep 17 00:00:00 2001 From: Filippo Vicentini Date: Wed, 3 Apr 2019 10:43:48 +0200 Subject: [PATCH 5/7] Fix tests by using `step` keyword in `log_value`. ( I removed the non-keyword version in the last commit ) --- src/Loggers/LogValue.jl | 4 ++-- test/runtests.jl | 5 ----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Loggers/LogValue.jl b/src/Loggers/LogValue.jl index 7e555a80..7fd0f070 100644 --- a/src/Loggers/LogValue.jl +++ b/src/Loggers/LogValue.jl @@ -10,8 +10,8 @@ function log_value(logger::TBLogger, name::String, value::Real; step=nothing) end function log_value(logger::TBLogger, name::String, value::Complex; step=nothing) - log_value(logger, name*"/re", real(value), step) - log_value(logger, name*"/im", imag(value), step) + log_value(logger, name*"/re", real(value), step=step) + log_value(logger, name*"/im", imag(value), step=step) end function scalar_summary(name::String, value::Real) diff --git a/test/runtests.jl b/test/runtests.jl index 2f275018..875507e0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,11 +9,6 @@ using Test log_value(logger, "float64", 1.5, step=step) log_value(logger, "irr", pi, step=step) log_value(logger, "complex", 1.0 + 1.0im, step=step) - - log_value(logger, "float32", 1.25f0, step) - log_value(logger, "float64", 1.5, step) - log_value(logger, "irr", pi, step) - log_value(logger, "complex", 1.0 + 1.0im, step) end @testset "Histogram Value Logger" begin From 2998dcd34ddabc1c23b5ac5535457d377c12f40d Mon Sep 17 00:00:00 2001 From: Filippo Vicentini Date: Wed, 3 Apr 2019 10:45:24 +0200 Subject: [PATCH 6/7] Add very basic test for the logging interface. --- test/runtests.jl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 875507e0..d09ad800 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using TensorBoardLogger +using TensorBoardLogger, Logging using Test @testset "Scalar Value Logger" begin @@ -25,3 +25,24 @@ end log_histogram(logger, "hist/cust", data_tuple, step=step) log_histogram(logger, "hist/cust", rand(100), step=step) end + +@testset "LogInterface" begin + logger = TBLogger("log/") + @test isdir("log/") + + with_logger(logger) do + for i=1:100 + x0 = 0.5+i/30; s0 = 0.5/(i/20); + edges = collect(-5:0.1:5) + centers = collect(edges[1:end-1] .+0.05) + histvals = [exp(-((c-x0)/s0)^2) for c=centers] + data_tuple = (edges, histvals) + + + @info "test" i=i j=i^2 dd=rand(10).+0.1*i hh=data_tuple + @info "test2" i=i j=2^i dd=rand(10).-0.1*i hh=data_tuple log_step_increment=0 + end + end + + @test TensorBoardLogger.step(logger) == 100 +end From 793648901f35eac7deb4ab12bc3dafd0f6ce48b4 Mon Sep 17 00:00:00 2001 From: Filippo Vicentini Date: Mon, 8 Apr 2019 17:01:53 +0200 Subject: [PATCH 7/7] Silently drop things we can't serialize --- src/Logger.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Logger.jl b/src/Logger.jl index 0b750dd2..8021dabb 100644 --- a/src/Logger.jl +++ b/src/Logger.jl @@ -167,8 +167,11 @@ function preprocess(name, val::T, data) where T prop = getproperty(val, f) preprocess(name*"/$f", val, data) end - else - throw(ErrorException("Can't log type $T, but can't preprocess it either.\n You should define preprocess(name, val::$T, data).")) + + #TODO If you encounter something that can't be logged, silently drop it. + # When String/text logging will be implemented we should use it as a fallback. + #else + # throw(ErrorException("Can't log type $T, but can't preprocess it either.\n You should define preprocess(name, val::$T, data).")) end data end