porting python class to Julialang

Viewed 148

I am seeing that Julia explicitly does NOT do classes... and I should instead embrace mutable structs.. am I going down the correct path here?? I diffed my trivial example against an official flux library but cannot gather how do I reference self like a python object.. is the cleanest way to simply pass the type as a parameter in the function??

Python

# Dense Layer
class Layer_Dense
    
    def __init__(self, n_inputs, n_neurons):
        self.weights = 0.01 * np.random.randn(n_inputs, n_neurons)
        self.biases = np.zeros((1, n_neurons))

    def forward(self, inputs):
        pass

My JuliaLang version so far


mutable struct LayerDense
    num_inputs::Int64
    num_neurons::Int64
    weights
    biases
end


function forward(layer::LayerDense, inputs)
    layer.weights = 0.01 * randn(layer.num_inputs, layer.num_neurons)
    layer.biases = zeros((1, layer.num_neurons))
end

The flux libraries version of a dense layer... which looks very different to me.. and I do not know what they're doing or why.. like where is the forward pass call, is it here in flux just named after the layer Dense???

source : https://github.com/FluxML/Flux.jl/blob/b78a27b01c9629099adb059a98657b995760b617/src/layers/basic.jl#L71-L111

struct Dense{F, M<:AbstractMatrix, B}
  weight::M
  bias::B
  σ::F
  function Dense(W::M, bias = true, σ::F = identity) where {M<:AbstractMatrix, F}
    b = create_bias(W, bias, size(W,1))
    new{F,M,typeof(b)}(W, b, σ)
  end
end

function Dense(in::Integer, out::Integer, σ = identity;
               initW = nothing, initb = nothing,
               init = glorot_uniform, bias=true)

  W = if initW !== nothing
    Base.depwarn("keyword initW is deprecated, please use init (which similarly accepts a funtion like randn)", :Dense)
    initW(out, in)
  else
    init(out, in)
  end

  b = if bias === true && initb !== nothing
    Base.depwarn("keyword initb is deprecated, please simply supply the bias vector, bias=initb(out)", :Dense)
    initb(out)
  else
    bias
  end

  return Dense(W, b, σ)
end
1 Answers

This is an equivalent of your Python code in Julia:

mutable struct Layer_Dense
    weights::Matrix{Float64}
    biases::Matrix{Float64}

    Layer_Dense(n_inputs::Integer, n_neurons::Integer) =
        new(0.01 * randn(n_inputs, n_neurons),
            zeros((1, n_neurons)))
end

forward(ld::Layer_Dense, inputs) = nothing

What is important here:

  • here I create an inner constructor only, as outer constructor is not needed; as opposed in the Flux.jl code you have linked the Dense type defines both inner and outer constructors
  • in python forward function does not do anything, so I copied it in Julia (your Julia code worked a bit differently); note that instead of self one should pass an instance of the object to the function as the first argument (and add ::Layer_Dense type signature so that Julia knows how to correctly dispatch it)
  • similarly in Python you store only weights and biases in the class, I have reflected this in the Julia code; note, however, that for performance reasons it is better to provide an explicit type of these two fields of Layer_Dense struct

like where is the forward pass call

In the code you have shared only constructors of Dense object are defined. However, in the lines below here and here the Dense type is defined to be a functor. Functors are explained here (in general) and in here (more specifically for your use case)

Related