Версия Python¶

In [1]:
!python -V
Python 3.12.6

Импорт необходимых библиотек¶

In [2]:
# Подавление предупреждений
import warnings
for warn in [UserWarning, FutureWarning]: warnings.filterwarnings("ignore", category = warn)

import os
import numpy as np
import torch
import torch.nn as nn
import pandas as pd
import jupyterlab as jlab

Версии необходимых библиотек¶

In [3]:
packages = [
    "Torch", "NumPy", "Pandas", "JupyterLab",
]

package_objects = [
    torch, np, pd, jlab
]

versions = list(map(lambda obj: obj.__version__, package_objects))

pkgs = {"Библиотека": packages, "Версия": versions}
df_pkgs = pd.DataFrame(data = pkgs)
df_pkgs.index.name = "№"
df_pkgs.index += 1

display(df_pkgs)

path_to_reqs = "."
reqs_name = "requirements.txt"

def get_packages_and_versions():
    """Генерация строк с библиотеками и их версиями в формате: библиотека==версия"""
    
    for package, version in zip(packages, versions):
        yield f"{package.lower()}=={version}\n"

with open(os.path.join(path_to_reqs, reqs_name), "w", encoding = "utf-8") as f:
    f.writelines(get_packages_and_versions())
Библиотека Версия
№
1 Torch 2.2.2
2 NumPy 1.26.4
3 Pandas 2.2.3
4 JupyterLab 4.2.5

ReLU (Rectified Linear Unit)¶

Замена всех отрицательных значений на 0, при этом положительные значения остаются без изменений

$$ \text{ReLU}(x) = \begin{cases} 0 & \text{если } x \leq 0 \\ x & \text{если } x > 0 \end{cases} $$

In [4]:
# Создание объекта ReLU
g = nn.ReLU()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации ReLU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([0.0000, 1.1458])

ELU (Exponential Linear Unit)¶

Преобразование отрицательных значений на $\alpha (\exp(x) - 1)$, делающее их менее агрессивными по сравнению с ReLU, с сохранением положительных значений без изменений

$$ \text{ELU}(x) = \begin{cases} x & \text{если } x > 0 \\ \alpha (\exp(x) - 1) & \text{если } x \leq 0 \end{cases} $$

In [5]:
# Создание объекта ELU
g = nn.ELU(alpha = 1.0)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации ELU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-0.9049,  1.1458])

PReLU (Parametric ReLU)¶

Преобразование значений, оставляя положительные значения без изменений, и умножая отрицательные значения на обучаемый параметр $\alpha$ $\text{где } \alpha \text{ — обучаемый параметр}$

$$ \text{PReLU}(x) = \begin{cases} x & \text{если } x \geq 0 \\ \alpha x & \text{если } x < 0 \end{cases} $$

In [6]:
# Создание объекта PReLU
g = nn.PReLU(num_parameters = 4, init = 0.25)

# Генерация случайного тензора
input = torch.tensor([
    [ 0.6465, -0.9450, -0.5559, -1.5250],
    [-1.4968, -1.1030,  0.5872, -0.7036]
]) # torch.randn(2, 4)

# Применение функции активации PReLU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([[ 0.6465, -0.9450, -0.5559, -1.5250],
        [-1.4968, -1.1030,  0.5872, -0.7036]])
Выходные данные: tensor([[ 0.6465, -0.2362, -0.1390, -0.3812],
        [-0.3742, -0.2758,  0.5872, -0.1759]], grad_fn=<PreluKernelBackward0>)

LeakyReLU¶

Преобразование значений, оставляя положительные значения без изменений, и умножая отрицательные значения на коэффициент $\alpha$, $\text{где } \alpha = \text{negative_slope}$

$$ \text{LeakyReLU}(x) = \begin{cases} x & \text{если } x \geq 0 \\ \alpha x & \text{если } x < 0 \end{cases} $$

In [7]:
# Создание объекта LeakyReLU
g = nn.LeakyReLU(negative_slope = 0.01)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации LeakyReLU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-0.0235,  1.1458])

ReLU6¶

Преобразование значений меньше 0 в 0, больше 6 в 6, и оставляя остальные значения без изменений

$$ \text{ReLU6}(x) = \min(\max(0, x), 6) $$

In [8]:
# Создание объекта ReLU6
g = nn.ReLU6()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458, 6.2345]) # torch.randn(3)

# Применение функции активации ReLU6 к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458,  6.2345])
Выходные данные: tensor([0.0000, 1.1458, 6.0000])

RReLU (Randomized Leaky ReLU)¶

Преобразование значений, оставляя положительные значения без изменений, и умножая отрицательные значения на случайно выбранный коэффициент $\alpha$

$$ \text{RReLU}(x) = \begin{cases} x & \text{если } x \geq 0 \\ \alpha x & \text{если } x < 0 \end{cases} $$

In [9]:
# Создание объекта RReLU
g = nn.RReLU(lower = 0.125, upper = 0.333)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458, 6.2345]) # torch.randn(3)

# Применение функции активации RReLU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458,  6.2345])
Выходные данные: tensor([-0.6328,  1.1458,  6.2345])

SELU (Scaled Exponential Linear Unit)¶

Преобразование значений, сохраняя положительные без изменений и масштабируя их параметром $scale$, а отрицательные параметрами $\alpha$ и $scale$ $\text{где } \text{scale} \approx 1.0507 \text{ и } \alpha \approx 1.6733$

$$ \text{SELU}(x) = \text{scale} \times (\max(0, x) + \min(0, \alpha \times (\exp(x) - 1))) $$

In [10]:
# Создание объекта SELU
g = nn.SELU()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации SELU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-1.5909,  1.2039])

CELU (Continuously Differentiable Exponential Linear Unit)¶

Преобразование отрицательных значений с использованием экспоненциальной функции и параметра $\alpha$ и сохраняя положительные значения без изменений

$$ \text{CELU}(x) = \begin{cases} x & \text{если } x \geq 0 \\ \alpha \times (\exp(x / \alpha) - 1) & \text{если } x < 0 \end{cases} $$

In [11]:
# Создание объекта CELU
g = nn.CELU(alpha = 1.0)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации CELU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-0.9049,  1.1458])

GELU (Gaussian Error Linear Unit)¶

Преобразование значений, приближая их к гауссовому распределению

$$ \text{GELU}(x) = x \times \Phi(x) \quad \text{где } \Phi(x) \text{ - функция кумулятивного распределения для нормального распределения (}approximate=none\text{)} $$

$$ \text{GELU}(x) = 0.5 \times x \times \left(1 + \tanh\left(\sqrt{\frac{2}{\pi}} \left(x + 0.044715 x^3\right)\right)\right) \quad \text{где } approximate=tanh $$

In [12]:
# Создание объекта GELU
g = nn.GELU(approximate = "none") # none | tanh

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации GELU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-0.0219,  1.0015])

Sigmoid¶

Преобразование значений в диапазоне от 0 до 1

$$ \text{Sigmoid}(x) = \sigma(x) = \frac{1}{1 + e^{-x}} $$

In [13]:
# Создание объекта Sigmoid
g = nn.Sigmoid()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации Sigmoid к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([0.0869, 0.7587])

SiLU (Sigmoid Linear Unit)¶

Комбинирование значений $x$ с их сигмоидными преобразованиями $\sigma(x) = \frac{1}{1 + e^{-x}}$

$$ \text{SiLU}(x) = x \times \sigma(x) \quad \text{где } \sigma(x) = \frac{1}{1 + e^{-x}} \text{ - сигмоидная функция} $$

In [14]:
# Создание объекта SiLU
g = nn.SiLU()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации SiLU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-0.2043,  0.8694])

LogSigmoid¶

Преобразование значений с помощью логарифма сигмоидной функции

$$ \text{LogSigmoid}(x) = \log \left( \frac{1}{1 + \exp(-x)} \right) $$

In [15]:
# Создание объекта LogSigmoid
g = nn.LogSigmoid()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации LogSigmoid к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-2.4435, -0.2761])

Hardsigmoid¶

Преобразование значений, которые меньше или равны $−3$, в 0, значения, которые больше или равны $3$, в 1, и значения между $−3$ и $3$, с помощью функции $\frac{x}{6} + 0.5$

$$ \text{Hardsigmoid}(x) = \begin{cases} 0 & \text{если } x \leq -3 \\ 1 & \text{если } x \geq 3 \\ \frac{x}{6} + 0.5 & \text{иначе} \end{cases} $$

In [16]:
# Создание объекта Hardsigmoid
g = nn.Hardsigmoid()

# Генерация случайного тензора
input = torch.tensor([-3.3526, 3.1458, -2.0256, 1.7843]) # torch.randn(4)

# Применение функции активации Hardsigmoid к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-3.3526,  3.1458, -2.0256,  1.7843])
Выходные данные: tensor([0.0000, 1.0000, 0.1624, 0.7974])

Tanh¶

Преобразование значений в диапазоне от -1 до 1

$$ \text{Tanh}(x) = \tanh(x) = \frac{\exp(x) - \exp(-x)}{\exp(x) + \exp(-x)} $$

In [17]:
# Создание объекта Tanh
g = nn.Tanh()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации Tanh к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-0.9821,  0.8164])

Tanhshrink¶

Преобразование значений, где из самих входных значений вычитается гиперболический $tanh$

$$ \text{Tanhshrink}(x) = x - \tanh(x) $$

In [18]:
# Создание объекта Tanhshrink
g = nn.Tanhshrink()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации Tanhshrink к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-1.3705,  0.3294])

Hardtanh¶

Преобразование значений, которые меньше $min\_val$, в $min\_val$, которые больше $max\_val$, в $max\_val$, и оставляя значения между $min\_val$ и $max\_val$ без изменений

$$ \text{HardTanh}(x) = \begin{cases} min\_val & \text{если } x < min\_val \\ max\_val & \text{если } x > max\_val \\ x & \text{иначе} \end{cases} $$

In [19]:
# Создание объекта Hardtanh
g = nn.Hardtanh(min_val = -1.0, max_val = 1.0)

# Генерация случайного тензора
input = torch.tensor([-1.1383, 1.1630, -0.8715, 0.7228]) # torch.randn(4)

# Применение функции активации Hardtanh к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-1.1383,  1.1630, -0.8715,  0.7228])
Выходные данные: tensor([-1.0000,  1.0000, -0.8715,  0.7228])

Hardshrink¶

Преобразование значений, которые находятся между $-\lambda$ и $\lambda$ включительно, в 0, и оставляя значения, которые меньше $-\lambda$ или больше $\lambda$, без изменений

$$ \text{HardShrink}(x) = \begin{cases} x & \text{если } x > \lambda \\ x & \text{если } x < -\lambda \\ 0 & \text{иначе} \end{cases} $$

In [20]:
# Создание объекта Hardshrink
g = nn.Hardshrink(lambd = 1.1458)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации Hardshrink к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-2.3526,  0.0000])

Hardswish¶

Преобразование значений, которые меньше или равны $−3$, в 0, значения, которые больше или равны $3$, остаются без изменений, и значения между $−3$ и $3$ преобразуются с помощью функции $x \left(\frac{x+3}{6}\right)$

$$ \text{Hardswish}(x) = \begin{cases} 0 & \text{если } x \leq -3 \\ x & \text{если } x \geq 3 \\ x \left(\frac{x+3}{6}\right) & \text{иначе} \end{cases} $$

In [21]:
# Создание объекта Hardswish
g = nn.Hardswish()

# Генерация случайного тензора
input = torch.tensor([-3.3526, 3.1458, -2.0256, 1.7843]) # torch.randn(4)

# Применение функции активации Hardswish к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-3.3526,  3.1458, -2.0256,  1.7843])
Выходные данные: tensor([-0.0000,  3.1458, -0.3290,  1.4228])

Mish¶

Преобразование значений, комбинируя значения $x$ с их $softplus$-преобразованиями ($\text{Softplus}(x) = \frac{1}{\beta} \log(1 + \exp(\beta \times x))$) и примененым гиперболическим тангенсом

$$ \text{Mish}(x) = x \times \tanh(\text{Softplus}(x)) $$

In [22]:
# Создание объекта Mish
g = nn.Mish()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации Mish к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([-0.2132,  1.0198])

Softplus¶

Преобразование значений, сглаживая их, обеспечивая плавный переход между линейными и нелинейными областями, контролируемый параметром $\beta$

$$ \text{Softplus}(x) = \frac{1}{\beta} \log(1 + \exp(\beta \times x)) $$

In [23]:
# Создание объекта Softplus
g = nn.Softplus(beta = 1.0, threshold = 20.0)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458]) # torch.randn(2)

# Применение функции активации Softplus к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458])
Выходные данные: tensor([0.0909, 1.4219])

Softshrink¶

Преобразование значений, уменьшая их на величину $\lambda$ и устанавливая их в ноль, если значения по абсолютной величине меньше $\lambda$

$$ \text{Softshrink}(x) = \begin{cases} x - \lambda & \text{если } x > \lambda \\ x + \lambda & \text{если } x < -\lambda \\ 0 & \text{иначе} \end{cases} $$

In [24]:
# Создание объекта Softshrink
g = nn.Softshrink(lambd = 0.5)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458, 0.4320, -0.3791]) # torch.randn(4)

# Применение функции активации Softshrink к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458,  0.4320, -0.3791])
Выходные данные: tensor([-1.8526,  0.6458,  0.0000,  0.0000])

Softsign¶

Преобразование значений, уменьшая их величину, где входные значения делятся на сумму 1 и их абсолютного значения

$$ \text{Softsign}(x) = \frac{x}{1 + |x|} $$

In [25]:
# Создание объекта Softsign
g = nn.Softsign()

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458, 0.4320, -0.3791]) # torch.randn(4)

# Применение функции активации Softsign к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458,  0.4320, -0.3791])
Выходные данные: tensor([-0.7017,  0.5340,  0.3017, -0.2749])

Threshold¶

Преобразование значений, при котором значения, большие заданного порога ($threshold$) остаются без изменений, а значения, меньшие порога, заменяются на заданное значение ($value$)

$$ \text{Threshold}(x) = \begin{cases} x & \text{если } x > threshold \\ value & \text{иначе} \end{cases} $$

In [26]:
# Создание объекта Threshold
g = nn.Threshold(threshold = 0.1, value = 20)

# Генерация случайного тензора
input = torch.tensor([-2.3526, 1.1458, 0.4320, -0.3791]) # torch.randn(4)

# Применение функции активации Threshold к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([-2.3526,  1.1458,  0.4320, -0.3791])
Выходные данные: tensor([20.0000,  1.1458,  0.4320, 20.0000])

GLU (Gated Linear Unit)¶

Преобразование значений, при котором данные разделяется на две половины по последнему размеру, первая половина остается неизменной, а ко второй применяется сигмоидная функция, и перемножение этих частей дает результат

$$ \text{GLU}(x) = x_1 \times \sigma(x_2) $$

In [27]:
# Создание объекта GLU
g = nn.GLU(dim = -1)

# Генерация случайного тензора
input = torch.tensor([
    [-0.0915,  0.2352],
    [ 2.2440,  0.5817],
    [ 0.4528,  0.6410],
    [ 0.5200,  0.5567]
]) # torch.randn(4, 2)

# Применение функции активации GLU к входным данным
output = g(input)

# Вывод входных и выходных данных
print("Входные данные:", input)
print("Выходные данные:", output)
Входные данные: tensor([[-0.0915,  0.2352],
        [ 2.2440,  0.5817],
        [ 0.4528,  0.6410],
        [ 0.5200,  0.5567]])
Выходные данные: tensor([[-0.0511],
        [ 1.4394],
        [ 0.2966],
        [ 0.3306]])

MultiheadAttention¶

Преобразование значений, используя механизм многоголового внимания, где входные $query$, $key$ и $value$ проходят через несколько голов внимания, объединяются и линейно преобразуются в окончательный результат

$$ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h) W^O \quad \text{где } \text{head}_i = \text{Attention}(Q W_i^Q, K W_i^K, V W_i^V) $$

In [28]:
# Параметры
embed_dim = 3 # Размерность эмбеддинга
num_heads = 1 # Количество голов

# Создание объекта MultiheadAttention
multihead_attn = nn.MultiheadAttention(
    embed_dim = embed_dim,
    num_heads = num_heads,
    dropout = 0.0,
    bias = True,
    add_bias_kv = False,
    batch_first = True
)

# Генерацие случайных тензоров для query, key и value
query = torch.tensor([
    [
        [-1.4025,  0.4318,  0.3431],
        [ 1.0711,  1.3455,  0.3277]],
    [
        [ 1.3409,  1.2159,  0.9589],
        [ 0.5137,  0.4977, -0.6646]],
    [
        [-1.0612,  2.0423,  0.6509],
        [-1.0072,  0.3578, -1.0799]
    ]
]) # (batch size, target sequence length, embed_dim)
key = torch.tensor([
    [
        [-1.4025,  0.4318,  0.3431],
        [ 1.0711,  1.3455,  0.3277]],
    [
        [ 1.3409,  1.2159,  0.9589],
        [ 0.5137,  0.4977, -0.6646]],
    [
        [-1.0612,  2.0423,  0.6509],
        [-1.0072,  0.3578, -1.0799]
    ]
]) # (batch size, source sequence length, embed_dim)
value = torch.tensor([
    [
        [-1.4025,  0.4318,  0.3431],
        [ 1.0711,  1.3455,  0.3277]],
    [
        [ 1.3409,  1.2159,  0.9589],
        [ 0.5137,  0.4977, -0.6646]],
    [
        [-1.0612,  2.0423,  0.6509],
        [-1.0072,  0.3578, -1.0799]
    ]
]) # (batch size, source sequence length, embed_dim)

# Применение функции активации MultiheadAttention к входным данным
# attn_output = (batch size, target sequence length, embed_dim)
# attn_output_weights = (batch size, target sequence length, source sequence length)
attn_output, attn_output_weights = multihead_attn(query, key, value)

# Выходных данных
print("Результат внимания:", attn_output)
print("Веса внимания:", attn_output_weights)
Результат внимания: tensor([[[-0.0590, -0.0616,  0.0548],
         [-0.0655, -0.0732,  0.0653]],

        [[ 0.0648,  0.1393, -0.1462],
         [ 0.0690,  0.1480, -0.1414]],

        [[-0.0708, -0.0367,  0.2673],
         [-0.0543, -0.0098,  0.2601]]], grad_fn=<TransposeBackward0>)
Веса внимания: tensor([[[0.4234, 0.5766],
         [0.4468, 0.5532]],

        [[0.5683, 0.4317],
         [0.5315, 0.4685]],

        [[0.2942, 0.7058],
         [0.2290, 0.7710]]], grad_fn=<MeanBackward1>)