Skip to main content

High order layers in pytorch

Project description

Piecewise Polynomial in PyTorch

This is a PyTorch implementation of my tensorflow repository and is more complete due to the flexibility of PyTorch.

Lagrange Polynomial, Piecewise Lagrange Polynomial, Discontinuous Piecewise Lagrange Polynomial, Fourier Series, sum and product layers in PyTorch. The sparsity of using piecewise polynomial layers means that by adding new segments the representational power of your network increases, but the time to complete a forward step remains constant. Implementation includes simple fully connected layers, convolution layers and deconvolutional layers using these models. This is a PyTorch implementation of this paper including extension to Fourier Series and convolutional neural networks.

Collab Notebook

Using simple high order layers Simple function approximation

Using simple high order MLP 2d function approximation

Idea

The idea is extremely simple - instead of a single weight at the synapse, use n-weights. The n-weights describe a piecewise polynomial (or other complex function) and each of the n-weights can be updated independently. A Lagrange polynomial and Gauss Lobatto points are used to minimize oscillations of the polynomial. The same approach can be applied to any "functional" synapse, and I also have Fourier series synapses in this repo as well. This can be implemented as construction of a polynomial or Fourier kernel followed by a standard pytorch layer where a linear activation is used.

In the image below each "link" instead of being a single weight, is a function of both x and a set of weights. These functions can consist of an orthogonal basis functions for efficient approximation.

Why

Using higher order polynomial representations might allow networks with much fewer total weights.

Fully Connected Layer Types

All polynomials are Lagrange polynomials with Chebyshev interpolation points.

A helper function is provided in selecting and switching between these layers

from high_order_layers_torch.layers import *
layer1 = high_order_fc_layers(
    layer_type=layer_type,
    n=n,
    in_features=784,
    out_features=100,
    segments=segments,
)

where layer_type is one of

layer_type representation
continuous piecewise polynomial using sum at the neuron
continuous_prod piecewise polynomial using products at the neuron
discontinuous discontinuous piecewise polynomial with sum at the neuron
discontinuous_prod discontinous piecewise polynomial with product at the neuron
polynomial single polynomial (non piecewise) with sum at the neuron
polynomial_prod single polynomial (non piecewise) with product at the neuron
product Product
fourier fourier series with sum at the neuron

n is the number of interpolation points per segment for polynomials or the number of frequencies for fourier series, segments is the number of segments for piecewise polynomials, alpha is used in product layers and when set to 1 keeps the linear part of the product, when set to 0 it subtracts the linear part from the product.

Convolutional Layer Types

conv_layer = high_order_convolution_layers(layer_type=layer_type, n=n, in_channels=3, out_channels=6, kernel_size=5, segments=segments, rescale_output=rescale_output, periodicity=periodicity)

All polynomials are Lagrange polynomials with Chebyshev interpolation points.

layer_type representation
continuous(1d,2d) piecewise continuous polynomial
discontinuous(1d,2d) piecewise discontinuous polynomial
polynomial(1d,2d) single polynomial
fourier(1d,2d) fourier series convolution

h and p refinement

p refinement is taking an existing network and increasing the polynomial order of that network without changing the network output. This allow the user to train a network at low polynomial order and then use that same network to initialize a network with higher polynomial order. This is particularly useful since a high order polynomial network will often converge poorly without the right initialization, the lower order network provides a good initial solution. The function for changing the order of a network is

from high_order_layers_torch.networks import interpolate_high_order_mlp
interpolate_high_order_mlp(
    network_in: HighOrderMLP, network_out: HighOrderMLP

current implementation only works with high order MLPs, not with convnets. A similar function exists for h refinement. h refinement is refining the number of segments in a layer, and is used for similar reasoning. Layers with lots of segments may be slow to converge so the user starts with a small number of segments (1 or 2) and then increases the number of segments (h) using the lower initialization. The following function currently only works for high order MLPs, not with convnets

from high_order_layers_torch.network import hp_refine_high_order_mlp
hp_refine_high_order_mlp(
    network_in: HighOrderMLP, network_out: HighOrderMLP
)

Installing

Installing locally

This repo uses poetry, so run

poetry install

and then

poetry shell

Installing from pypi

pip install high-order-layers-torch

or

poetry add high-order-layers-torch

Examples

Simple function approximation

Approximating a simple function using a single input and single output (single layer) with no hidden layers to approximate a function using continuous and discontinuous piecewise polynomials (with 5 pieces) and simple polynomials and fourier series. The standard approach using ReLU is non competitive. To see more complex see the implicit representation page here.

piecewise continuous polynomial piecewise discontinuous polynomial polynomial fourier series

python examples/function_example.py

XOR : 0.5 for x*y > 0 else -0.5

Simple XOR problem using the standard network structure (2 inputs 2 hidden 1 output) this will also work with no hidden layers. The function is discontinuous along the axis and we try and fit that function. Using piecewise discontinuous layers the model can match the function exactly. piecewise discontinuous polynomial With piecewise continuous it doesn't work quite as well. piecewise continuous polynomial Polynomial doesn't work well at all (expected). polynomial

MNIST (convolutional)

python examples/mnist.py max_epochs=1 train_fraction=0.1 layer_type=continuous2d n=4 segments=2

CIFAR100 (convolutional)

python examples/cifar100.py -m max_epochs=20 train_fraction=1.0 layer_type=polynomial segments=2 n=7 nonlinearity=False rescale_output=False periodicity=2.0 lr=0.001 linear_output=False

Variational Autoencoder

Still a WIP. Does work, but needs improvement.

python examples/variational_autoencoder.py -m max_epochs=300 train_fraction=1.0

run with nevergrad for parameter tuning

python examples/variational_autoencoder.py -m

Invariant MNIST (fully connected)

Without polynomial refinement

python examples/invariant_mnist.py max_epochs=100 train_fraction=1 mlp.layer_type=continuous mlp.n=5 mlp.p_refine=False mlp.hidden.layers=4

with polynomial refinement (p-refinement)

python examples/invariant_mnist.py max_epochs=100 train_fraction=1 layer_type=mlp.continuous mlp.n=2 mlp.target_n=5 mlp.p_refine=True

I've also added hp refinement, but it needs a lot of testing.

Implicit Representation

An example of implicit representation for image compression, language generation can be found here. I intend to explore generative models in natural language further here

PDEs in Fluid Dynamics

An example using implicit representation to solve hyperbolic (nonlinear) wave equations can be found here

Natural Language Generation

Examples using these networks for natural language generation can be found here

Generative music

No real progress here here

Test and Coverage

After installing and running

poetry shell

run

pytest

for coverage, run

coverage run -m pytest

and then

coverage report

A note on the product unit (I rarely use anymore)

The layers used here do not require additional activation functions and use a simple sum or product in place of the activation. I almost always use sum units, but product units are performed in this manner

$$ product=-1+\prod_{i}(1 + f_{i})+(1-\alpha)\sum_{i}f_{i} $$

The 1 is added to each function output to as each of the sub products is also computed. The linear part is controlled by the alpha parameter.

Notes on optimizer

The Lion optimizer seems to be the best choice since it performs better than Adam in general, but seems to work especially well for the case of polynomials.

Notes on normalization

Although you can use batchnorm, layernorm etc... work better, I've found that you can actually just use the infinity norm ("max_abs" norm) which has no parameters for this formulation (same approach seems not to work very well for standard relu networks - but need to investigate this further). The max_abs normalization is defined this way

normalized_x = x/(max(abs(x))+eps)

where the normalization is done per sample (as opposed to per batch). The way the layers are formulated, we don't want the neuron values to extend beyond [-1, 1] as the polynomial values grow rapidly beyond that range. You can also use mirror periodicity to keep the values within from growing rapidly. We want the values to cover the entire range [-1, 1] of the polynomials as the weights are packed towards the edges of each segment (though using even number of segments means you'll have a lot of weights near the origin).

Reference

@misc{Loverich2020,
  author = {Loverich, John},
  title = {High Order Layers Torch},
  year = {2020},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/jloveric/high-order-layers-torch}},
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

high_order_layers_torch-2.3.0.tar.gz (35.0 kB view details)

Uploaded Source

Built Distribution

high_order_layers_torch-2.3.0-py3-none-any.whl (37.0 kB view details)

Uploaded Python 3

File details

Details for the file high_order_layers_torch-2.3.0.tar.gz.

File metadata

  • Download URL: high_order_layers_torch-2.3.0.tar.gz
  • Upload date:
  • Size: 35.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.3.2 CPython/3.10.6 Linux/5.15.0-91-generic

File hashes

Hashes for high_order_layers_torch-2.3.0.tar.gz
Algorithm Hash digest
SHA256 88adca4378978a85f586b78ae685943b6bf4ea9e41bb6ce67d39f162078b8119
MD5 c4bf429ec28fbd7004496ae08e8d802e
BLAKE2b-256 9f076be8cfc87959c15415c96ab9782eb8d1a9b41695019217668d5bffcd1523

See more details on using hashes here.

File details

Details for the file high_order_layers_torch-2.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for high_order_layers_torch-2.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 35581e5b7b0c33b5097c30b0d26913bbcf451be33200f7c225a9f60c7a5ea329
MD5 45f79e0c863179ad3614a1eafadbbe8a
BLAKE2b-256 94bb7de5a856aaa2982188007e4178d5b526626036e571fd5b65484b73931efc

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page