# Класс CQrnnLayer

<!-- TOC -->

- [Класс CQrnnLayer](#класс-cqrnnlayer)
    - [Настройки](#настройки)
        - [Тип пулинга](#тип-пулинга)
        - [Размер скрытого слоя](#размер-скрытого-слоя)
        - [Размер окна](#размер-окна)
        - [Шаг окна](#шаг-окна)
        - [Дополнительные элементы последовательности (padding)](#дополнительные-элементы-последовательности-(padding))
        - [Функция активации](#функция-активации)
        - [Dropout](#dropout)
        - [Обработка последовательностей](#обработка-последовательностей)
    - [Обучаемые параметры](#обучаемые-параметры)
        - [Фильтры свертки](#фильтры-свертки)
        - [Свободные члены](#свободные-члены)
    - [Входы](#входы)
        - [Размер первого входа](#размер-первого-входа)
        - [Размеры второго входов](#размеры-второго-входов)
    - [Выходы](#выходы)

<!-- /TOC -->

Класс реализует квази-рекуррентный слой, применяющийся к набору последовательностей векторов.

Результатом операции является последовательность векторов, каждый вектор в которой имеет размер `GetHiddenSize()`.

В отличие от LSTM или GRU, в этом слое большая часть вычислений производится до рекуррентной части, что значительно повышает производительность на GPU.
Это достигается за счет использования одномерной свертки "по времени" ([CTimeConvLayer](ConvolutionLayers/TimeConvLayer.md)).

Реализация основана на [следующей статье](https://arxiv.org/abs/1611.01576).

## Настройки

### Тип пулинга

```c++
// Different poolings used in QRNN
enum TPoolingType {
    PT_FPooling, // f-pooling from article, uses 2 gates (Update, Forget)
    PT_FoPooling, // fo-pooling from article, uses 3 gates (Update, Forget, Output)
    PT_IfoPooling, // ifo pooling from article, uses 4 gates (Update, Forget, Output, Input)

    PT_Count
};

void SetPoolingType(TPoolingType newPoolingType);
```

Установить тип пулинга. Пулингом в QRNN называется рекуррентная часть после свертки "по времени".
Точные формулы соответствующих пулингов можно посмотреть в [статье](https://arxiv.org/abs/1611.01576).

### Размер скрытого слоя

```c++
void SetHiddenSize(int hiddenSize);
```

Установить размер скрытого слоя. Это значение влияет на размер выхода.

### Размер окна

```c++
void SetWindowSize(int windowSize);
```

Устанавливает размер окна, которым свертка "по времени" итерируется по входным последовательностям.

### Шаг окна

```c++
void SetStride(int stride);
```

Устанавливает шаг, с которым свертка "по времени" итерируется по входным последовательностям.

### Дополнительные элементы последовательности (padding)

```c++
void SetPaddingFront(int padding);
```

Устанавливает количество дополнительных нулевых элементов к началам последовательностей. По умолчанию равен `0`.

```c++
void SetPaddingBack(int padding);
```

Устанавливает количество дополнительных нулевых элементов к концам последовательностей. По умолчанию равен `0`.

### Функция активации

```c++
void SetActivation( TActivationFunction newActivation );
```

Устанавливает функцию активации, используемую в `update` гейте. По умолчанию используется `AF_Tanh`.

### Dropout

```c++
void SetDropout(float rate);
```

Устанавливает вероятность зануления `forget` гейта.

### Обработка последовательностей

```c++
// Различные подходы к обработке последовательностей
enum TRecurrentMode {
    RM_Direct,
    RM_Reverse,

    // Двунаправленный режим, при котором обе рекурренты используют одну и ту же свертку по времени
    RM_BidirectionalConcat, // возвращает объединение результатов прямой и обратной рекуррент
    RM_BidirectionalSum, // возвращает сумму результатов прямой и обратной рекуррент
    // В случае двунаправленного QRNN, где у каждой рекурренты должна быть своя свертка по времени,
    // нужно создать 2 CQrrnLayer и объединить их результаты при помощи CConcatChannelsLayer или CEltwiseSumLayer

    RM_Count
};

void SetRecurrentMode( TRecurrentMode newMode );
```

Устанавливает режим обработки последовательностей.

## Обучаемые параметры

### Фильтры свертки

```c++
CPtr<CDnnBlob> GetFilterData() cons;
```

Фильтры, содержащие веса сразу для всех гейтов, представляют собой [блоб](DnnBlob.md) размеров:

- `BatchLength` равен `1`;
- `BatchWidth` равен `gates * GetHiddenSize()`, где `gates` равен `2` если используется `PT_FPooling`, `3` в случае `PT_FoPooling` и `4` в случае `PT_IfoPooling`;
- `Height` равен `GetWindowSize()`;
- `Width` равен `1`;
- `Depth` равен `1`;
- `Channels` равен `Height * Width * Depth * Channels` у входов.

Вдоль оси `BatchWidth` матрица содержит веса гейтов в следующем порядке:

```c++
G_Update, // update gate (Z in the article)
G_Forget, // forget gate (F in the article)
G_Output, // output gate if used (O in the article)
G_Input, // input gate if used (I in the article)
```

### Свободные члены

```c++
CPtr<CDnnBlob> GetFreeTermData() const
```

Свободные члены представляют собой блоб, имеющий суммарный размер, равный `BatchWidth` блоба с фильтрами выше.

## Входы

Слой имеет от 1 до 2 входов:

1. Набор входных последовательностей векторов.
2. *[Опционально]* Начальное состояние рекуррентного слоя. Будет использовано в качестве состояния слоя перед первым шагом. Если вход не задан, то будет заполнено нулями.

### Размеры первого входа

- `BatchLength` - длина последовательности;
- `BatchWidth` - количество последовательностей в наборе;
- `Height * Width * Depth * Channels` - размер векторов в последовательностях.

### Размеры второго входа

- `BatchLength`, `Height`, `Width` и `Depth` должны быть равны `1`;
- `BatchWidth` должен быть равен `BatchWidth` у первого входа;
- `Channels` должен быть равен `GetHiddenSize()`.

## Выходы

Единственный выход содержит блоб с результатами следующего размера:

- `BatchLength` можно вычислить из размеров входа по следующей формуле `(BatchLength + GetPaddingFront() + GetPaddingBack() - (GetWindowSize() - 1)) / GetStride() + 1)`;
- `BatchWidth` равен `BatchWidth` у первого входа;
- `ListSize`, `Height`, `Width` и `Depth` равны `1`;
- `Channels` равен `2 * GetHiddenSize()`, если `GetRecurrentMode()` установлен в `RM_BidirectionalConcat`. В остальных случаях равен `GetHiddenSize()`.
