ML: Нейронні мережі в Torch - Довідник
Інтерфейси для шарів
Цей документ містить огляд основних типів шарів, похибок і оптимізаторів у бібліотеці PyTorch. Основи PyTorch описані тут, а основи програмування на ній нейронних мереж - тут.Шари в PyTorch реалізовані у двох формах: як функції і як класи.
Перша - це звичайні функції, яким передаються параметри шару і вхідний тензор. Наприклад, лінійний шар множить вхідну матрицю форми (N, nX) на транспоновану матрицю ваг W форми (nY, nX) і опційно додає вектор зсувів bias. На виході виходить тензор (N, nY):
import torch.nn.functional as F N, nX, nY = 1, 2, 3 # кількість прикладів, входів, виходів X = torch.ones(N, nX) # матриця прикладів W = torch.ones(nY, nX) # матриця ваг B = torch.ones(nY) # вектор зміщень Y = F.linear(X, W, B) # [[3., 3., 3.]] Y = X.mm(W.t())+B # те саме Y = torch.addmm(B,X,W.t()) # те самеШари як екземпляри класів, самі створюють параметри з початковими випадковими значеннями.
Нижче обов'язковими аргументами при створенні класу є число входів nX і число виходів nY шару (число нейронів у ньому == число нових ознак). Потім екземпляру fc класу Linear через оператор ( ) передається вхідна матриця X:
import torch.nn as nn
fc = nn.Linear(nX, nY)
Y = fc(X) # tensor([[-0.1559, -0.2140, 0.1462]],
# grad_fn=<Addmm>)
Клас шару у своїх параметрів оголошує властивість requires_grad=True. Це призводить до побудови обчислювального графа при прямому поширенні по мережі і до отримання градієнтів від параметрів при зворотному поширенні:
print(fc.bias) # tensor([ 0.529, -0.103, 0.646], requires_grad=True) print(fc.bias.is_leaf) # True print(fc.bias.grad) # None L = torch.sum(Y*Y) # скалярна "функція помилки" L.backward() # запускаємо обчислення градієнтів fc.bias.grad # tensor([-0.3688, 0.6441, -1.6415])
Далі йде невеликий довідник найважливіших типів шарів, активаційних функцій і оптимізаторів. За докладнішою інформацією варто звернутися до документації.
Базові шари
Повнозв'язний шар
✒ nn.Linear… (in_features, out_features, bias=True) [doc]
Здійснює лінійне перетворення $\mathbf{y} = \mathbf{x}\cdot \mathbf{W}^T + \mathbf{b}$. Параметри шару знаходяться в атрибутах weight і bias. Матриця ваг $\mathbf{W}$ зберігається в транспонованому вигляді (щоб при перемноженні матриць $\mathbf{X}\mathbf{W}^\top$ використовувати їхні рядки, а не рядок і стовпець). Якщо необхідно задати ваги руками, це робиться поза обчислювальним графом:
fc = nn.Linear(nX, nY)
with torch.no_grad():
fc.weight.copy_(W) # встановлюємо матрицю ваг
fc.bias. copy_(B) # встановлюємо вектор зміщень
Щоб заморозити параметри (не змінювати при навчанні) достатньо
вимкнути в них обчислення градієнта: fc.weight.requires_grad = False.
Реалізація лінійного шару в torch.nn.functional виглядає таким чином:
def linear(X, weight, bias=None):
if X.dim() == 2 and bias is not None: # fused op is marginally faster
ret = torch.addmm(bias, X, weight.t()) # bias + X @ weight.t()
else:
output = X.matmul(weight.t())
if bias is not None: output += bias
ret = output
return ret
Нехай in_features=nX, out_features=nY і є зсув: bias=True.
Вхідний тензор може мати довільне число індексів (три крапки)
і шар так змінює його форму: (N,...,nX) -> (N,...,nY).
Число параметрів шару дорівнює nX*nY + nY.
Ембединг
✒ nn.Embedding… (num_embeddings, embedding_dim, padding_idx=None, max_norm=None, norm_type=2.0,
… scale_grad_by_freq=False, sparse=False, _weight=None)
Шар Embedding перетворює масив цілих чисел long на вході на матрицю (масив векторів). Докладніше див. Embedding слів. При створенні екземпляра шару обов'язкові два перших параметри: розмір словника і розмірність ембедингу. Нижче створюється шар з 5 словами і їхніми 2-вимірними (випадковими) векторами:
emb = nn.Embedding(5, 2) X = torch.tensor([0,2,1]) print (emb(X)) print(emb.weight)
tensor([[-1.3062, -1.6261],
[-0.3903, 0.0998],
[ 0.2643, 1.6466]], grad_fn=<Embedding>)
Parameter containing:
tensor([[-1.3062, -1.6261],
[ 0.2643, 1.6466],
[-0.3903, 0.0998],
[-0.5463, -0.5028],
[ 1.3299, -0.0580]], requires_grad=True)
Параметр max_norm задає максимальну довжину векторів і якщо при навчанні вона перевищується, вектор перенормовується. Як норма за замовчуванням використовується сума квадратів компонент: norm_type = 2.
При параметрі scale_grad_by_freq = True градієнти при навчанні будуть зважуватися за оберненою частотою слів у батчі (посилювати зміну більш рідкісних слів). Але батч при цьому варто брати достатньо великий. Завантажити готові вектори можна, створивши шар таким чином:
weight = torch.FloatTensor([[1, 2.3, 3],
[4, 5.1, 6]])
emb = nn.Embedding.from_pretrained(weight) # Створити шар із готовими векторами
input = torch.LongTensor([1]) # Get embeddings for index 1
emb(input) # tensor([[ 4.0, 5.1, 6.0]])
Параметри функції from_pretrained:
from_pretrained(embeddings, freeze=True, padding_idx=None,
max_norm=None, norm_type=2.0, scale_grad_by_freq=False, sparse=False)
Конволюція
✒ nn.Conv2d… (in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True,
… padding_mode='zeros')
Згортковий шар Conv2D застосовується до "картинок" висотою rows, шириною cols і з in_channels "кольоровими" каналами. Насправді графічні терміни умовні і шар Conv2D може застосовуватися не тільки при обробці зображень. Форма вхідного тензора (N, in_channels, rows, cols). На виході буде тензор (N, out_channels, rows_out, cols_out) За вихідним тензором проходить out_channels фільтрів розміром kernel_size (число або tuple). Глибина фільтра визначається числом вхідних каналів in_channels.
Нижче на малюнку in_channels=2, out_channels=3 (три фільтри), розміри фільтрів kernel_size = 2 або, що те саме kernel_size = (2,2). Фільтри за замовчуванням ковзають по картинці з кроком один stride=(1,1), тому результуюча ширина і висота картинки на 1 менші:
Параметр padding (int or tuple) задає на скільки "пікселів" необхідно розширити картинку з обох сторін, перед проходженням по ній фільтрами. Розширена область заповнюється значеннями відповідно до параметра padding_mode: 'zeros', 'reflect', 'replicate', 'circular'. Параметр dilation = 1 задає відстань між пікселями, що потрапляють у фільтр.
Для прикладу створимо "картинку" 3x4, ліва половина якої біла (1), а права - чорна (0) і пройдемо по ній фільтром 2x2, що виділяє вертикальний край:
X = torch.zeros(1,1,3,4)
X[0,0,:,:2] = 1
print(X)
conv = nn.Conv2d(1,1,kernel_size=2,bias=False)
print( conv )
with torch.no_grad():
conv.weight.copy_(torch.tensor([[-1.,1.],
[-1.,1.]]))
print( conv.weight )
print( conv.bias )
print( conv(X) )
tensor([[[[1., 1., 0., 0.],
[1., 1., 0., 0.],
[1., 1., 0., 0.]]]])
Conv2d(1,1,kernel_size=(2,2),stride=(1,1))
# задаємо ядро фільтра
tensor([[[[-1.,1.],
[-1.,1.]]]],requires_grad=True)
None
tensor([[[[ 0., -2., 0.],
[ 0., -2., 0.]]]],
grad_fn=<ThnnConv2D>)
RNN
✒ nn.RNN… (input_size, hidden_size, num_layers=1, nonlinearity='tanh', bias=True,
… batch_first=False, dropout=0, bidirectional=False) [doc]
Базовий рекурентний шар за входом $\mathbf{x}^{(t)}$ і попереднім прихованим станом $\mathbf{h}^{(t-1)}$ обчислюється новий прихований стан $\mathbf{h}^{(t)}$ : $$ \mathbf{h}^{(t)} = \tanh(\mathbf{x}^{(t)}\,\mathbf{W}_{ih} + \mathbf{b}_{ih} + \mathbf{h}^{(t-1)}\,\mathbf{W}_{hh} + \mathbf{b}_{hh}) $$
Обов'язкові параметри: input_size - розмірність входів ($\mathbf{x}$) і hidden_size - розмірність виходу ( = прихованого стану $\mathbf{h}$).
За замовчуванням розмірність входу (seq_len, batch_size, input_size), де seq_len - число RNN-комірок (входів). Індекс батча можна зробити першим за допомогою параметра batch_first. Утім тензор за замовчуванням виходить, якщо перед шаром RNN знаходиться шар Embedding з seq_len входами і з input_size = VEC_DIM розмірністю векторів.
Шар повертає кортеж з тензора всіх виходів мережі форми (seq_len, batch_size, hidden_size) і останнього прихованого стану (останній вихід) форми (num_layers, batch_size, hidden_size), де для одного шару num_layers = 1. За замовчуванням компоненти початкового прихованого стану нульові. Інакше їх можна передати другим аргументом:
X_dim = 2 # розмірність входів = dim(x) H_dim = 4 # розмірність прихованого стану = dim(h) L = 3 # кількість входів (комірок RNN-шару) B = 1 # кількість прикладів (batch_size) rnn = nn.RNN(X_dim, H_dim) X = torch.zeros(L, B, X_dim) H0 = torch.zeros(1, B, H_dim) Y, Hn = rnn(X, H0) # H0 не обов’язково, за замовчуванням і так 0 print(tuple(Y.shape), tuple(Hn.shape)) # (3, 1, 4) (1, 1, 4) print(Y[-1] == Hn[-1]) # tensor([[[True, True, True, True]]])
У шарі можна відразу задати стопку RNN-шарів (num_layers) Між шарами можна вставити режим випадкового забивання нулями виходів з ймовірністю dropout. Шар можна також перетворити на двонаправлений: bidirectional = True. У цьому випадку розмірність виходу буде (inputs, batch_size, 2*hidden_size), а розмірність початкового прихованого стану (2 * num_layers, batch_size, hidden_size).
LSTM
✒ torch.nn.LSTM… (input_size, hidden_size, num_layers=1, nonlinearity='tanh', bias=True,
… batch_first=False, dropout=0, bidirectional=False) [doc]
Аналогічний RNN. Подробиці див. у NN_RNN.html. Крім прихованого стану $\mathbf{h}$ має пам'ять комірок $\mathbf{c}$, див. документацію.
rnn = nn.LSTM(10, 20, 2) ... output, (hn, cn) = rnn(input, (h0, c0))
MultiheadAttention
✒ nn.MultiheadAttention… (embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False,
… add_zero_attn=False, kdim=None, vdim=None)
$$ \text{MultiHead}(\mathbf{Q},\mathbf{K}, \mathbf{V}) = \text{Concat}(\text{h}_1,\dots,\text{h}_H)\,\mathbf{W}^O, ~~~~~~~ \text{h}_i = \text{Attn}(\mathbf{Q}\,\mathbf{W}_i^Q,~ \mathbf{K}\,\mathbf{W}_i^K,~ \mathbf{V}\,\mathbf{W}_i^V) $$
Inputs: Q:(L,N,E), K,V:(S,N,E),
де
L - довжина послідовності target (для декодера),
N - розмір батча,
E - розмірність ембедингу (embed_dim),
S - довжина послідовності source.
Outputs: output: (L,N,E), weights: (N,L,S)
Розмірності матриць: $\mathbf{W}^Q:$ (E, E),
$\mathbf{W}^K:$ (E, kdim),
$\mathbf{W}^V:$ (E, vdim),
де kdim,vdim задаються або дорівнюють E,
але вони передаються у функцію linear, тому при множенні транспонуються.
Розмірність кожної голови дорівнює head_dim = E // num_heads.
Вихідна матриця $\mathbf{W}^O:$ це шар Linear(E, E, bias=bias).
Якщо bias є, він додається після множення
на проєкційні матриці $\mathbf{W}^Q,\mathbf{W}^K,\mathbf{W}^V$ (до кожної свій).
Якщо ембединг входів однаковий, то проєкційні матриці упаковані в in_proj_weight форми (3*E,E), інакше це параметри: q_proj_weight, k_proj_weight, v_proj_weight. Зсув (якщо він є) це in_proj_bias форми (3*E,), а вихідний шар: out_proj.
Концептуально відбуваються такі обчислення (N - розмір батча, подробиці див. у NN_Attention.html):
q = linear(Q, q_proj_weight, in_proj_bias[0 : E]) # (L,N,E) @ (E, E).T = (L,N,E) k = linear(K, k_proj_weight, in_proj_bias[E : E*2]) # (S,N,E) @ (E, kdim).T = (S,N,kdim) v = linear(V, v_proj_weight, in_proj_bias[E*2:]) # (S,N,E) @ (E, vdim).T = (S,N,vdim) q = q.contiguous().view( L, N * num_heads, head_dim).transpose(0,1) k = k.contiguous().view(-1, N * num_heads, head_dim).transpose(0,1) v = v.contiguous().view(-1, N * num_heads, head_dim).transpose(0,1) a_weights = torch.bmm(q, k.transpose(1,2)) a_weights = softmax(a_weights, dim=-1) a = torch.bmm(a_weights, v) a = a.transpose(0, 1).contiguous().view(L, N, E) a = linear(a, out_proj_weight, out_proj_bias)
TransformerEncoderLayer
✒ nn.TransformerEncoderLayer… (d_model, nhead, dim_feedforward=2048, dropout=0.1, activation='relu')
Параметри: d_model – розмірність входу (вектора одного токена), nhead – число голів (дільник параметра d_model), dim_feedforward – розмірність повнозв'язної мережі.
TransformerEncoder
✒ nn.TransformerEncoder… (encoder_layer, num_layers, norm=None)
L, N, E = 10, 32, 512 # число слов, размер батча, размерность эмбединга encoder_layer = nn.TransformerEncoderLayer(d_model=E, nhead=8) transformer_encoder = nn.TransformerEncoder (encoder_layer, num_layers=6) src = torch.rand(L, N, E) out = transformer_encoder(src) # out.shape == src.shape
Шари без параметрів
Лінеаризація тензора
✒ nn.Flatten(start_dim=1, end_dim=-1)При створенні з параметрами за замовчуванням, усі індекси крім першого (індекс прикладу) зіллються в один (для кожного прикладу вийде вектор). Шар параметрів не має.
X = torch.ones(2, 5,4,3) Y = torch.nn.Flatten()(X) print( tuple(Y.shape) ) # (2, 60)
Косинус між векторами
✒ nn.CosineSimilarity(dim=1, eps=1e-08)$$ \frac{\mathbf{u}\mathbf{v}}{ \max( |\mathbf{u}|\,|\mathbf{v}|,~~\epsilon) } $$
Input1, Input2: (*, D, *), де D - розмірність вектора і форми однакові. Output: (*,*).Нижче косинус обчислюється між рядками матриць:
u, v = torch.randn(100, 128), torch.randn(100, 128) output = nn.CosineSimilarity(dim=1, eps=1e-6) (u, v)
Відстань між векторами
✒ nn.PairwiseDistance(p=2.0, eps=1e-06, keepdim=False)$$ \bigr(\sum_{i} |x_i|^p \Bigr)^{1/p} $$
Input1, Input2: (N, D), де D - розмірність вектора. Output: (N) або (N, 1), якщо keepdim = True.Вимкнення нейронів
✒ nn.Dropout(p=0.5, inplace=False)
У режимі тренування: m.train(), з ймовірністю "p" занулює кожен елемент
матриці і ділить їх на 1-p.
У режимі оцінювання: m.eval() нічого не робить.
Завдяки діленню на 1-p середнє значення за модулем елементів у режимі тренування буде
таким же, як і в режимі оцінювання (у вхідного тензора).
m = nn.Dropout(p = 0.2)
# v v
print( m(torch.ones(10)) ) # [1.25, 1.25, 1.25, 0.0, 1.25, 1.25, 1.25, 0.0, 1.25, 1.25]
x = torch.randn(100, 100)
m.eval() # This is equivalent with self.train(False)
y = m(x)
print( (x==y).float().mean() ) # не змінюється
m.train()
y =m(x)
print(x.abs().mean(), (x*x < 1e-10).float().mean() ) # 0.8025 0.
print(y.abs().mean(), (y*y < 1e-10).float().mean() ) # 0.8001 0.206
Нормалізація тензора
✒ nn.BatchNorm1d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)За батчем (перший індекс) обчислюються середні значення і дисперсії за кожною ознакою незалежно. Потім робиться нормалізація даних і афінний зсув з навчальними параметрами $\gamma,\,\beta$. $$ y = \frac{x - \mathrm{E}[x]}{\sqrt{\mathrm{Var}[x] + \epsilon}} \odot \text{weight} + \text{bias} $$ Вхід повинен бути тензором форми (N,F) або (N,F,L). Параметр num_features = F. Для тензора (N,F,L) усереднення відбувається для dim=0,2, тобто однакові середні за кожним "входом" останнього індексу [0...L-1].
У процесі тренування середні значення згладжуються ковзним середнім з параметром momentum (якщо None - то звичайне середнє). Ці середні запам'ятовуються і потім використовуються при тестуванні (навіть з одним батчем).
m = nn.BatchNorm1d(2)
x = torch.randn(100000,2)
print( x.mean(dim=0), x.var(dim=0) ) # [-0.0005, 0.0067] [1.0058, 0.9956]
x = 2 * x + 3
print( x.mean(dim=0), x.var(dim=0) ) # [ 3.0010, 2.9867] [4.0231, 3.9824]
y = m(x)
with torch.no_grad():
print(y.mean(dim=0), y.var(dim=0)) # [ 0.0000, 0.0000] [1.0000, 1.0000]
for k, v in m.state_dict().items(): # weight :(2,)
print(f'{k:20s}: {tuple(v.shape)}') # bias :(2,)
# running_mean :(2,)
# running_var :(2,)
# num_batches_tracked:( )
MaxPool2d
✒ nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
AvgPool2d
✒ nn.AvgPool2d(kernel_size, stride=None, padding=0, ceil_mode=False, count_include_pad=True, divisor_override=None)
Активаційні функції
Сигмоїд: $\mathrm{Sigmoid}(x) = 1/(1+e^{-x})$
✒ nn.Sigmoid() [doc]
✒ F.sigmoid(x)
Tanh: $\mathrm{Tanh}(x) = (e^{x}-e^{-x})/(e^{x}+e^{-x})$
✒ nn.Tanh() [doc]
✒ F.tanh(x)
ReLU: $\mathrm{Sigmoid}(x) = \max(0,x)$
✒ nn.ReLU(inplace=False) [doc]
✒ F.relu(x, inplace=False)
ELU: $\mathrm{ELU}(x) = \max(0,x) + \min(0, \alpha\,(e^{x}-1))$
✒ nn.ELU(alpha=1.0, inplace=False) [doc]
✒ nn.elu(x, alpha=1.0, inplace=False)
Софтмакс: $\mathrm{Softmax}(x_i) = e^{x_i}/\sum_j e^{x_j}$
✒ nn.Softmax(dim=None) [doc].
✒ F.softmax(x, dim)
X = torch.tensor( [ [1.,2.,3] ] ) torch.nn.Softmax(dim=1)(X) # tensor([[0.09, 0.24, 0.66]])
Логарифмічний софтмакс: $\mathrm{LogSoftmax}(x_i) = \log \bigr(e^{x_i}/\sum_j e^{x_j}\bigr)$
✒ nn.LogSoftmax (dim=None) [doc].
Функції похибки
Середньоквадратична похибка
✒ nn.MSELoss
… (size_average=None, reduce=None, reduction='mean')
[doc]
$$
L(y,\hat{y}) = (y - \hat{y})^2
\left\{
\begin{array}{ll}
.\mathrm{mean}() & \text{if reduction='mean'}\\
.\mathrm{sum}() & \text{if reduction='sum'}
\end{array}
\right.
$$
За замовчуванням (reduction='mean') квадрати відхилень усереднюються за всіма прикладами батча і за всіма виходами (похибка це скаляр!).
loss = torch.nn.MSELoss() y = torch.rand(10, 2) # 10 прикладів, 2 виходи yb = torch.rand(10, 2) loss(y, yb) == ((y-yb)**2).mean()
Бінарна крос-ентропія
✒ nn.BCELoss… (weight=None, size_average=None, reduce=None, reduction='mean') [doc]
Передбачається, що ця похибка ставиться після сигмоїдної активації, тобто виходи моделі y і цільові значення yb знаходяться в діапазоні [0...1]. Зазвичай BCELoss використовується для мережі з одним виходом (задача для двох класів 0 і 1). Якщо класи перетинаються (тобто приклад може належати відразу до декількох класів), то число виходів дорівнює числу класів. За замовчуванням (reduction='mean') похибки усереднюються за всіма прикладами і всіма виходами:
loss = torch.nn.BCELoss() y = torch.rand(10, 2, 3) # 10 примеров, 2x3 выходов yb = torch.rand(10, 2, 3) print( loss(y, yb) ) print( -( yb*y.log() + (1-yb)*(1-y).log() ).mean() )Вектор weight з числом компонент рівних числу прикладів у батчі може по-різному зважувати різні приклади. Похибка для $i$-того прикладу ($y$ - вихід моделі, $\hat{y}$ - цільове значення): $$ L_i = -w_i\,\bigr[ \hat{y}_i\,\log y_i + (1-\hat{y}_i)\,\log (1-y_i)\bigr] $$
Крос-ентропія
✒ nn.CrossEntropyLoss… (weight=None,size_average=None,ignore_index=-100,reduce=None,reduction='mean') [doc]
Застосовується до задачі класифікації непересічних $C$ класів. Мережа повинна мати $C$ виходів, тобто в похибку з моделі входить тензор $y_{i\alpha}$ форми $(N,C)$, де $N$ - розмір батча. Цільовими є цілі числа $\hat{y}_i \in [0...C-1]$ - номери класів кожного прикладу форми (N,). Похибка для одного $i$-того прикладу і $\hat{y}_i=c$ дорівнює:
$$ L(y, c) = -\,w_c\,\log\left( \frac{\exp {y_{ic}}}{ \sum_\alpha \exp{y_{i\alpha}}}\right). $$ Ваги $w_\alpha$ (за числом класів) можуть посилювати внесок окремих класів. Таким чином, CrossEntropyLoss поєднує в собі Softmax і на виході мережі його ставити не потрібно. $C$ виходів мережі $y_{i\alpha}$ перетворюються на "ймовірності" і максимізується логарифм ймовірності правильного класу $\hat{y}_i=c$ (знак мінус - похибка мінімальна). Підсумкова похибка дорівнює середньому $L(y, c)$ за всіма прикладами батча (якщо reduction='mean').Вхідні дані у функцію похибки CE_loss(y, y_t)можуть мати форму y=(B,C):float, y_t=(B,):long або в n-вимірному випадку: y=(B,C,L1,...,Ln):float, y_t=(B,L1,...,Ln):long
B, C, L = 10, 5, 4 # прикладів, класів, виходів w = torch.rand(C) # ваги важливості класів CE_loss = nn.CrossEntropyLoss(weight=w, ignore_index=1) y = torch.randn(B, C, L) yb = torch.empty(B, L, dtype=torch.long).random_(C) loss = CE_loss(y, yb) print(loss) # похибкаВідтворимо, отримане значення:
y = -nn.Softmax(dim=1)(y).log() # мінус логарифм від софтмаксу
loss, norm = 0, 0
for b in range(B):
for l in range(L):
c = yb[b,l] # правильний клас
if c != 1: # ігноруємо клас 1
loss += w[c]*y[b, c, l] # помилка
norm += w[c] # нормування
print(loss/norm)
Логарифмічний максимум правдоподібності
✒ nn.NLLLoss… (weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean') [doc]
Результат буде таким же, як і при CrossEntropyLoss, якщо останнім шаром стоїть LogSoftmax.
Оптимізатори
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999),
eps=1e-08, weight_decay=0, amsgrad=False)
torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0,
initial_accumulator_value=0, eps=1e-10)
torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)
torch.optim.RMSprop(params, lr=0.01, alpha=0.99, weight_decay=0, momentum=0,
eps=1e-08, centered=False)
torch.optim.Rprop(params, lr=0.01, etas=(0.5, 1.2), step_sizes=(1e-06, 50))
torch.optim.SGD(params, lr, momentum=0,
dampening=0, weight_decay=0, nesterov=False)