Lux.jl Deep Learning Library

repository·main·Indexed 20 days ago

https://github.com/luxdl/lux.jl

A deep learning library for JuliaLang providing an elegant API with performance comparable to XLA. The ecosystem includes LuxCore.jl for abstract layers, LuxCUDA for NVIDIA GPU support, and MLDataDevices.jl for managing data transfers across CPU, CUDA, AMDGPU, Metal, oneAPI, and Reactant devices. It supports various vision architectures (CNN, ResNet, ViT, ConvMixer) and generative models like DDIM, with capabilities for Distributed Data Parallel (DDP) training via MPI.jl and NCCL.

Tokens
35.3K
Snippets
104
Records
200
Agent score
71%

What's inside Lux.jl

  1. View Performance Benchmarks for ResNet and KAN

    main
    Performance benchmarks for Lux.jl are available for ResNet architectures and Kolmogorov-Arnold Networks (KAN). These benchmarks measure runtimes and speedups, typically executed on high-end hardware such as a GeForce RTX 5090 GPU with 32GB of VRAM.
  2. Use LuxLib for high-performance neural network primitives

    main
    LuxLib serves as the high-performance backend for Lux.jl, providing optimized implementations of common neural network operations. It includes specialized functions for activations, attention mechanisms, batched operations, convolutional layers, dropout, and normalization. These primitives are designed to be used within Lux.jl models to leverage backend-specific optimizations.
  3. Estimate Jacobian trace using Hutchinson Trace Estimation

    main

    Hutchinson Trace Estimation provides a fast way to estimate the trace of a Jacobian matrix $J$ using random vectors $v$ such that $\mathbb{E}[vv^T] = I$. The estimate is calculated as $\text{Tr}(J) = \frac{1}{V} \sum_{i = 1}^V v_i^T J v_i$.

    There are three primary ways to implement this in Lux, depending on whether you use Vector-Jacobian Products (VJP), Jacobian-Vector Products (JVP), or the full Jacobian matrix.

    # Example setup for testing trace methods
    model = Chain(Dense(4 => 12,tanh), Dense(12 => 12,tanh), Dense(12 => 12,tanh), Dense(12 => 4))
    ps, st = Lux.setup(StableRNG(0), model)
    x = rand(StableRNG(0), Float32, 4, 12)
    v = (rand(StableRNG(12), Float32, 4, 12) .> 0.5f0) * 2.0f0 .- 1.0f0  # Rademacher sample
  4. Core Design Principles of Lux

    main

    Lux is a neural network framework built using pure functions to ensure compatibility with compilers and automatic differentiation (AD) systems. The core mental model relies on the following principles:

    • Immutability: Layers cannot store parameters or state internally. Instead, they store only the information required to construct them.
    • Pure Functions: Layers are implemented as pure functions. Given the same inputs, parameters, and state, they must always produce the same output.
    • State Management: Layers return a Tuple containing the result and the updated state. This includes handling stochasticity (e.g., Lux.Dropout) by passing and updating rngs within the state.
    • Separation of Concerns: Lux explicitly separates layer structures from parameter data and state variables. This makes implementing techniques like WeightNorm or SpectralNorm trivial and avoids the issues found in frameworks that couple models with parameters.
  5. Implement custom layers in Lux

    main

    Lux and Flux follow different design philosophies for custom layers:

    1. Architecture vs. Data: In Lux, the layer struct defines the architecture, while the data (trainable parameters and non-trainable states) is stored separately in ps and st.
    2. Avoid Mutables in Layers: Do not store mutable structures like Arrays directly inside a Lux layer struct. Instead, store initialization functions (e.g., () -> copy(A)) and use them within Lux.initialparameters and Lux.initialstates to perform lazy initialization.
    3. Distinguishing Parameters and States:
      • Parameters (ps): Trainable values (e.g., weights, biases). Defined via Lux.initialparameters.
      • States (st): Non-trainable values (e.g., running means in BatchNorm, or fixed constants). Defined via Lux.initialstates.
    4. Device Transfer: Since data is stored in ps and st, device transfer utilities (like gpu_device) must be applied to the parameters and states, not the layer itself.

    Example Implementation: To implement a layer computing $A imes B imes x$ where $A$ is a non-trainable state and $B$ is a trainable parameter:

    struct LuxLinear <: Lux.AbstractLuxLayer
        init_A
        init_B
    end
    
    # Use functions for lazy initialization to avoid storing arrays in the struct
    function LuxLinear(A::AbstractArray, B::AbstractArray)
        return LuxLinear(() -> copy(A), () -> copy(B))
    end
    
    # B is a parameter
    Lux.initialparameters(::AbstractRNG, layer::LuxLinear) = (B=layer.init_B(),)
    
    # A is a state
    Lux.initialstates(::AbstractRNG, layer::LuxLinear) = (A=layer.init_A(),)
    
    # Forward pass signature
    (l::LuxLinear)(x, ps, st) = st.A * ps.B * x, st
    using Lux, Random, NNlib, Zygote
    
    struct LuxLinear <: Lux.AbstractLuxLayer
        init_A
        init_B
    end
    
    function LuxLinear(A::AbstractArray, B::AbstractArray)
        return LuxLinear(() -> copy(A), () -> copy(B))
    end
    
    Lux.initialparameters(::AbstractRNG, layer::LuxLinear) = (B=layer.init_B(),)
    Lux.initialstates(::AbstractRNG, layer::LuxLinear) = (A=layer.init_A(),)
    
    (l::LuxLinear)(x, ps, st) = st.A * ps.B * x, st
  6. Automatic Differentiation (AD) support in Lux

    main

    Lux is not an AD package itself, but it is designed to compose with most Automatic Differentiation (AD) packages in the Julia ecosystem. It provides first-class support for several backends, often including special rules to enhance performance.

    Support Tiers

    • Tier I: Fully supported and extensively tested. Often includes performance optimizations. High priority for issue resolution.
    • Tier II: Supported and tested, but may lack optimal performance or have known edge-case failures (e.g., with AMDGPU).
    • Tier III: Support is unverified; not currently a priority for testing.
    • Tier IV: Not recommended. These frameworks may not be maintained and support in Lux may be removed in future breaking releases.
  7. GPU Support and Acceleration

    main

    Lux.jl supports various hardware accelerators.

    For CPU, GPU, and TPU support, use Reactant.jl. This provides XLA compilation and is the preferred way to use GPUs in Lux.jl.

    Native GPU Support

    If you require native support, you can load the following packages:

    • LuxCUDA.jl: For CUDA support.
    • Metal.jl: For Apple Metal support.
    • oneAPI.jl: For oneAPI support.
    • AMDGPU.jl: For AMDGPU support. Warning: For AMD GPU users, Reactant is strongly recommended over native AMDGPU.jl due to performance and stability issues (e.g., deadlocks).
  8. Understand the relationship between LuxCore.jl and Lux.jl

    main

    LuxCore.jl defines the abstract layers for the Lux ecosystem. It is designed to allow users to maintain compatibility with the full Lux.jl functionality without requiring the heavy dependencies associated with the main Lux.jl package.

    Dependency Rule:

    • If you are already depending on Lux.jl directly, you do not need to depend on LuxCore.jl, as all functionality from LuxCore.jl is exported via Lux.jl.