Skip to content

Commit

Permalink
Add support for Torch's Conv1d strides and ConvTranspose1d (#145)
Browse files Browse the repository at this point in the history
* Added Torch's ConvTranspose1d support and Torch's Conv1d and ConvTranspose1d stride support.

* More tests for ConvTranspose1d

* Trying to fix AVX tests

* Strided convolution wrapper

* Templated strided convolution wrapper with test

* clang-format

* Trying to fix alignment issues on different platforms

* More test fixes

* Comments and re-organizing

---------

Co-authored-by: jatin <[email protected]>
  • Loading branch information
fcaspe and jatinchowdhury18 authored Sep 30, 2024
1 parent 2ca066e commit 32b8664
Show file tree
Hide file tree
Showing 24 changed files with 2,121 additions and 4 deletions.
1 change: 1 addition & 0 deletions RTNeural/Model.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "config.h"
#include "conv1d/conv1d.h"
#include "conv1d/conv1d.tpp"
#include "conv1d/strided_conv1d.h"
#include "conv2d/conv2d.h"
#include "conv2d/conv2d.tpp"
#include "dense/dense.h"
Expand Down
26 changes: 25 additions & 1 deletion RTNeural/conv1d/conv1d.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ class Conv1D final : public Layer<T>
/** Returns the name of this layer. */
std::string getName() const noexcept override { return "conv1d"; }

/** Performs a stride step for this layer. */
RTNEURAL_REALTIME inline void skip(const T* input)
{
// insert input into a circular buffer
std::copy(input, input + Layer<T>::in_size, state[state_ptr]);

// set state pointers to particular columns of the buffer
setStatePointers();

state_ptr = (state_ptr == state_size - 1 ? 0 : state_ptr + 1); // iterate state pointer forwards
}

/** Performs forward propagation for this layer. */
RTNEURAL_REALTIME inline void forward(const T* input, T* h) noexcept override
{
Expand Down Expand Up @@ -171,8 +183,8 @@ class Conv1D final : public Layer<T>
* @param out_sizet: the output size for the layer
* @param kernel_size: the size of the convolution kernel
* @param dilation_rate: the dilation rate to use for dilated convolution
* @param dynamic_state: use dynamically allocated layer state
* @param groups: controls connections between inputs and outputs
* @param dynamic_state: use dynamically allocated layer state
*/
template <typename T, int in_sizet, int out_sizet, int kernel_size, int dilation_rate, int groups = 1, bool dynamic_state = false>
class Conv1DT
Expand All @@ -198,6 +210,18 @@ class Conv1DT
/** Resets the layer state. */
RTNEURAL_REALTIME void reset();

/** Performs a stride step for this layer. */
RTNEURAL_REALTIME inline void skip(const T (&ins)[in_size])
{
// insert input into a circular buffer
std::copy(std::begin(ins), std::end(ins), state[state_ptr].begin());

// set state pointers to particular columns of the buffer
setStatePointers();

state_ptr = (state_ptr == state_size - 1 ? 0 : state_ptr + 1); // iterate state pointer forwards
}

template <int _groups = groups, std::enable_if_t<_groups == 1, bool> = true>
/** Performs forward propagation for this layer. */
RTNEURAL_REALTIME inline void forward(const T (&ins)[in_size]) noexcept
Expand Down
3 changes: 2 additions & 1 deletion RTNeural/conv1d/conv1d.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ Conv1D<T>::Conv1D(int in_size, int out_size, int kernel_size, int dilation, int

template <typename T>
Conv1D<T>::Conv1D(std::initializer_list<int> sizes)
: Conv1D<T>(*sizes.begin(), *(sizes.begin() + 1), *(sizes.begin() + 2), *(sizes.begin() + 3))
: Conv1D<T>(*sizes.begin(), *(sizes.begin() + 1), *(sizes.begin() + 2),
*(sizes.begin() + 3), *(sizes.begin() + 4))
{
}

Expand Down
25 changes: 25 additions & 0 deletions RTNeural/conv1d/conv1d_eigen.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ class Conv1D : public Layer<T>
/** Returns the name of this layer. */
std::string getName() const noexcept override { return "conv1d"; }

/** Performs a stride step for this layer. */
RTNEURAL_REALTIME inline void skip(const T* input)
{
// insert input into a circular buffer
state.col(state_ptr) = Eigen::Map<const Eigen::Vector<T, Eigen::Dynamic>,
RTNeuralEigenAlignment>(input, Layer<T>::in_size);

// set state pointers to the particular columns of the buffer
setStatePointers();

state_ptr = (state_ptr == state_size - 1 ? 0 : state_ptr + 1); // iterate state pointer forwards
}

/** Performs forward propagation for this layer. */
RTNEURAL_REALTIME inline void forward(const T* input, T* h) noexcept override
{
Expand Down Expand Up @@ -174,6 +187,18 @@ class Conv1DT
/** Resets the layer state. */
RTNEURAL_REALTIME void reset();

/** Performs a stride step for this layer. */
RTNEURAL_REALTIME inline void skip(const Eigen::Matrix<T, in_size, 1>& ins)
{
// insert input into a circular buffer
state.col(state_ptr) = ins;

// set state pointers to the particular columns of the buffer
setStatePointers();

state_ptr = (state_ptr == state_size - 1 ? 0 : state_ptr + 1); // iterate state pointer forwards
}

/** Performs forward propagation for this layer. */
template <int _groups = groups, std::enable_if_t<_groups == 1, bool> = true>
RTNEURAL_REALTIME inline void forward(const Eigen::Matrix<T, in_size, 1>& ins) noexcept
Expand Down
3 changes: 2 additions & 1 deletion RTNeural/conv1d/conv1d_eigen.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Conv1D<T>::Conv1D(int in_size, int out_size, int kernel_size, int dilation, int

template <typename T>
Conv1D<T>::Conv1D(std::initializer_list<int> sizes)
: Conv1D<T>(*sizes.begin(), *(sizes.begin() + 1), *(sizes.begin() + 2), *(sizes.begin() + 3))
: Conv1D<T>(*sizes.begin(), *(sizes.begin() + 1), *(sizes.begin() + 2),
*(sizes.begin() + 3), *(sizes.begin() + 4))
{
}

Expand Down
24 changes: 24 additions & 0 deletions RTNeural/conv1d/conv1d_xsimd.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ class Conv1D : public Layer<T>
/** Returns the name of this layer. */
std::string getName() const noexcept override { return "conv1d"; }

/** Performs a stride step for this layer. */
RTNEURAL_REALTIME inline void skip(const T* input)
{
// insert input into a circular buffer
vCopy(input, state[state_ptr].data(), Layer<T>::in_size);

// set state pointers to particular columns of the buffer
setStatePointers();

state_ptr = (state_ptr == state_size - 1 ? 0 : state_ptr + 1); // iterate state pointer forwards
}

/** Performs forward propagation for this layer. */
RTNEURAL_REALTIME inline void forward(const T* input, T* h) noexcept override
{
Expand Down Expand Up @@ -194,6 +206,18 @@ class Conv1DT
/** Resets the layer state. */
RTNEURAL_REALTIME void reset();

/** Performs a stride step for this layer. */
RTNEURAL_REALTIME inline void skip(const v_type (&ins)[v_in_size])
{
// insert input into a circular buffer
std::copy(std::begin(ins), std::end(ins), state[state_ptr].begin());

// set state pointers to particular columns of the buffer
setStatePointers();

state_ptr = (state_ptr == state_size - 1 ? 0 : state_ptr + 1); // iterate state pointer forwards
}

/** Performs forward propagation for this layer. */
template <int G = groups>
RTNEURAL_REALTIME inline typename std::enable_if<(G > 1), void>::type
Expand Down
3 changes: 2 additions & 1 deletion RTNeural/conv1d/conv1d_xsimd.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Conv1D<T>::Conv1D(int in_size, int out_size, int kernel_size, int dilation, int

template <typename T>
Conv1D<T>::Conv1D(std::initializer_list<int> sizes)
: Conv1D<T>(*sizes.begin(), *(sizes.begin() + 1), *(sizes.begin() + 2), *(sizes.begin() + 3))
: Conv1D<T>(*sizes.begin(), *(sizes.begin() + 1), *(sizes.begin() + 2),
*(sizes.begin() + 3), *(sizes.begin() + 4))
{
}

Expand Down
210 changes: 210 additions & 0 deletions RTNeural/conv1d/strided_conv1d.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#pragma once

#include "conv1d.h"

namespace RTNEURAL_NAMESPACE
{
/**
* Dynamic implementation of a 1-dimensional convolutional layer
* with strides.
*
* Internally, this is just a wrapper around the Conv1D layer.
*/
template <typename T>
class StridedConv1D final : public Layer<T>
{
public:
/**
* Constructs a strided convolution layer for the given dimensions.
*
* @param in_size: the input size for the layer
* @param out_size: the output size for the layer
* @param kernel_size: the size of the convolution kernel
* @param dilation: the dilation rate to use for dilated convolution
* @param stride: the stride of the convolution
*/
StridedConv1D(int in_size, int out_size, int kernel_size, int dilation, int stride, int groups = 1)
: Layer<T>(in_size, out_size)
, internal(in_size, out_size, kernel_size, dilation, groups)
, stride(stride)
{
skip_output.resize(out_size, T {});
}

StridedConv1D(std::initializer_list<int> sizes)
: StridedConv1D<T>(*sizes.begin(), *(sizes.begin() + 1), *(sizes.begin() + 2),
*(sizes.begin() + 3), *(sizes.begin() + 4), *(sizes.begin() + 5))
{
}

StridedConv1D(const StridedConv1D& other) = default;
StridedConv1D& operator=(const StridedConv1D& other) = default;

/** Resets the layer state. */
RTNEURAL_REALTIME void reset() override
{
strides_counter = 0;
std::fill(std::begin(skip_output), std::end(skip_output), T {});
internal.reset();
}

/** Returns the name of this layer. */
std::string getName() const noexcept override { return "strided_conv1d"; }

/** Performs a stride step for this layer. */
RTNEURAL_REALTIME inline void skip(const T* input)
{
internal.skip(input);
}

/** Performs forward propagation for this layer. */
RTNEURAL_REALTIME inline void forward(const T* input, T* h) noexcept override
{
if(strides_counter == 0)
{
internal.forward(input, h);
std::copy(h, h + Layer<T>::out_size, std::begin(skip_output));
}
else
{
internal.skip(input);
std::copy(std::begin(skip_output), std::end(skip_output), h);
}

strides_counter = (strides_counter == stride - 1) ? 0 : strides_counter + 1;
}

/**
* Sets the layer weights.
*
* The weights vector must have size weights[out_size][in_size][kernel_size * dilation]
*/
RTNEURAL_REALTIME void setWeights(const std::vector<std::vector<std::vector<T>>>& weights)
{
internal.setWeights(weights);
}

/**
* Sets the layer biases.
*
* The bias vector must have size bias[out_size]
*/
RTNEURAL_REALTIME void setBias(const std::vector<T>& biasVals)
{
internal.setBias(biasVals);
}

/** Returns the size of the convolution kernel. */
RTNEURAL_REALTIME int getKernelSize() const noexcept { return internal.getKernelSize(); }

/** Returns the convolution dilation rate. */
RTNEURAL_REALTIME int getDilationRate() const noexcept { return internal.getDilationRate(); }

/** Returns the number of "groups" in the convolution. */
int getGroups() const noexcept { return internal.getGroups(); }

private:
Conv1D<T> internal;

const int stride;
int strides_counter = 0;
std::vector<T> skip_output {};
};

//====================================================
/**
* Static implementation of a 1-dimensional convolution layer
* with strides.
*
* Internally, this is just a wrapper around the Conv1DT layer.
*
* @param in_sizet: the input size for the layer
* @param out_sizet: the output size for the layer
* @param kernel_size: the size of the convolution kernel
* @param dilation_rate: the dilation rate to use for dilated convolution
* @param stride: the stride of the convolution
* @param groups: controls connections between inputs and outputs
* @param dynamic_state: use dynamically allocated layer state
*/
template <typename T, int in_sizet, int out_sizet, int kernel_size, int dilation_rate, int stride, int groups = 1, bool dynamic_state = false>
class StridedConv1DT
{
Conv1DT<T, in_sizet, out_sizet, kernel_size, dilation_rate, groups, dynamic_state> internal;

int strides_counter = 0;

public:
static constexpr auto in_size = in_sizet;
static constexpr auto out_size = out_sizet;
static constexpr auto filters_per_group = in_size / groups;
static constexpr auto channels_per_group = out_size / groups;

StridedConv1DT()
: outs(internal.outs)
{
}

/** Returns the name of this layer. */
std::string getName() const noexcept { return "strided_conv1d"; }

/** Returns false since convolution is not an activation layer. */
constexpr bool isActivation() const noexcept { return false; }

/** Resets the layer state. */
RTNEURAL_REALTIME void reset()
{
internal.reset();
}

/** Performs a stride step for this layer. */
template <typename Inputs>
RTNEURAL_REALTIME inline void skip(const Inputs& ins) noexcept
{
internal.skip(ins);
}

/** Performs forward propagation for this layer. */
template <typename Inputs>
RTNEURAL_REALTIME inline void forward(const Inputs& ins) noexcept
{
if(strides_counter == 0)
internal.forward(ins);
else
internal.skip(ins);

strides_counter = (strides_counter == stride - 1) ? 0 : strides_counter + 1;
}

/**
* Sets the layer weights.
*
* The weights vector must have size weights[out_size][group_count][kernel_size * dilation]
*/
RTNEURAL_REALTIME void setWeights(const std::vector<std::vector<std::vector<T>>>& weights)
{
internal.setWeights(weights);
}

/**
* Sets the layer biases.
*
* The bias vector must have size bias[out_size]
*/
RTNEURAL_REALTIME void setBias(const std::vector<T>& biasVals)
{
internal.setBias(biasVals);
}

/** Returns the size of the convolution kernel. */
RTNEURAL_REALTIME int getKernelSize() const noexcept { return kernel_size; }

/** Returns the convolution dilation rate. */
RTNEURAL_REALTIME int getDilationRate() const noexcept { return dilation_rate; }

/** Returns the number of "groups" in the convolution. */
int getGroups() const noexcept { return groups; }

/** Reference to the internal layer weights. */
decltype(internal.outs)& outs;
};
}
Loading

0 comments on commit 32b8664

Please sign in to comment.