рдЬрдбрд╝рддрд╛ (SGDm) рдХрд╛ рд╡рд┐рдЪрд╛рд░, рд╕реНрдХреЗрд▓рд┐рдВрдЧ рдХрд╛ рд╡рд┐рдЪрд╛рд░ (рдЕрдбрдЧрд╛рд░реНрдб) рдФрд░ рд╡рд░реНрдЧреАрдХрд░рдг рд╕рдорд╕реНрдпрд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдорд╢реАрди рд▓рд░реНрдирд┐рдВрдЧ рдореЗрдВ рдирд┐рдпрдорд┐рддреАрдХрд░рдг рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИред

рдЗрд╕рдХреЗ рдмрд╛рдж рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдбреЗрдЯрд╛рд╕реЗрдЯ рдпрд╣рд╛рдВ рд╕реЗ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдкрд╛рд░рд┐рдд рдХрд╛рдЧрд▓ рдкреНрд░рддрд┐рдпреЛрдЧрд┐рддрд╛ рд╕реЗ рд▓рд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ ред
рдбреЗрдЯрд╛ рдЯреИрдм рдкрд░, рдЖрдк рд╕рднреА рдлрд╝реАрд▓реНрдб рдХрд╛ рд╡рд┐рд╡рд░рдг рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВред

рд╕рднреА рд╕реНрд░реЛрдд рдХреЛрдб рдпрд╣рд╛рдВ рд▓реИрдкрдЯреЙрдк рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рд╣реИрдВ ред

рд╣рдо рдбреЗрдЯрд╛ рд▓реЛрдб рдХрд░рддреЗ рд╣реИрдВ, рдЬрд╛рдВрдЪреЗрдВ рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЖрдо рддреМрд░ рдкрд░ рд╣реИ:

import numpy as np import pandas as pd dataset = pd.read_csv('../input/ghouls-goblins-and-ghosts-boo/train.csv') #   X_test = pd.read_csv('../input/ghouls-goblins-and-ghosts-boo/test.csv') #   print(dataset.shape) print(dataset[:10]) 



рдкреНрд░рдХрд╛рд░ рдлрд╝реАрд▓реНрдб (Ghoul, рднреВрдд, рднреВрдд) рдХреЗ рдореВрд▓реНрдпреЛрдВ рдХреЛ рдХреЗрд╡рд▓ 0, 1 рдФрд░ 2 рджреНрд╡рд╛рд░рд╛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

рд░рдВрдЧ - рдХреЛ рднреА рдкреВрд░реНрд╡-рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ (рд╣рдореЗрдВ рдореЙрдбрд▓ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЗрд╡рд▓ рд╕рдВрдЦреНрдпрд╛рддреНрдордХ рдореВрд▓реНрдпреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ)ред рд╣рдо рдЗрд╕рдХреЗ рд▓рд┐рдП LabelEncoder рдФрд░ OneHotEncoder рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред рдЕрдзрд┐рдХ рдЬрд╛рдирдХрд╛рд░реА ред

 from sklearn.preprocessing import LabelEncoder, OneHotEncoder labelencoder_X_1 = LabelEncoder() X_train[:, 4] = labelencoder_X_1.fit_transform(X_train[:, 4]) labelencoder_X_2 = LabelEncoder() X_test[:, 4] = labelencoder_X_2.fit_transform(X_test[:, 4]) labelencoder_Y_2 = LabelEncoder() Y_train = labelencoder_Y_2.fit_transform(Y_train) one_hot_encoder = OneHotEncoder(categorical_features = [4]) X_train = one_hot_encoder.fit_transform(X_train).toarray() X_test = one_hot_encoder.fit_transform(X_test).toarray() 

рдЦреИрд░, рдЗрд╕ рдмрд┐рдВрджреБ рдкрд░ рд╣рдорд╛рд░рд╛ рдбреЗрдЯрд╛ рддреИрдпрд╛рд░ рд╣реИред рдпрд╣ рд╣рдорд╛рд░реЗ рдореЙрдбрд▓ рдХреЛ рдкреНрд░рд╢рд┐рдХреНрд╖рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдиреА рд╣реБрдИ рд╣реИред

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ рдЕрдбрдЧрд╛рд░реНрдб рд▓рд╛рдЧреВ рдХрд░реЗрдВ :

рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, рдпрд╣ рд╕реНрдЯреЛрдХреЗрд╕реНрдЯрд┐рдХ рдХреНрд░рдорд┐рдХ рд╡рдВрд╢ рдХрд╛ рдПрдХ рд╕рдВрд╢реЛрдзрди рд╣реИ, рдЬрд┐рд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдореИрдВрдиреЗ рдкрд┐рдЫрд▓реА рдмрд╛рд░ рд▓рд┐рдЦрд╛ рдерд╛: habr.com/en/post/472300

рдпрд╣ рд╡рд┐рдзрд┐ рдкреНрд░рддреНрдпреЗрдХ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдкреИрд░рд╛рдореАрдЯрд░ (рд╕реНрдХреЗрд▓рд┐рдВрдЧ рдХреЗ рд╡рд┐рдЪрд╛рд░) рдХреЗ рд▓рд┐рдП рд╕рднреА рдкрд┐рдЫрд▓реЗ рдЧреНрд░реЗрдбрд┐рдПрдВрдЯ рдХреЗ рдЗрддрд┐рд╣рд╛рд╕ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддреА рд╣реИред рдпрд╣ рдЖрдкрдХреЛ рдмрдбрд╝реЗ рдЧреНрд░реЗрдбрд┐рдПрдВрдЯ рд╡рд╛рд▓реЗ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рд▓рд┐рдП рд╕реАрдЦрдиреЗ рдХреЗ рдЪрд░рдг рдХреЗ рдЖрдХрд╛рд░ рдХреЛ рдХрдо рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ:



рдЬреА рд╕реНрдХреЗрд▓рд┐рдВрдЧ рдкреИрд░рд╛рдореАрдЯрд░ рд╣реИ (g0 = 0)
weight - рдкреИрд░рд╛рдореАрдЯрд░ (рд╡рдЬрди)
рдПрдкреНрд╕рд┐рд▓реЙрди рд╢реВрдиреНрдп рджреНрд╡рд╛рд░рд╛ рд╡рд┐рднрд╛рдЬрди рдХреЛ рд░реЛрдХрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЫреЛрдЯрд╛ рд╕рд╛ рд╕реНрдерд┐рд░рд╛рдВрдХ рд╣реИ

рдбреЗрдЯрд╛рд╕реЗрдЯ рдХреЛ 2 рднрд╛рдЧреЛрдВ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░реЗрдВ:
рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдирдореВрдирд╛ (рдЯреНрд░реЗрди) рдФрд░ рд╕рддреНрдпрд╛рдкрди (рд╡реИрд▓):

 from sklearn.model_selection import train_test_split x_train, x_val, y_train, y_val = train_test_split(X_train, Y_train, test_size = 0.2) 

рдореЙрдбрд▓ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдереЛрдбрд╝реА рддреИрдпрд╛рд░реА:

 import torch import numpy as np device = 'cuda' if torch.cuda.is_available() else 'cpu' def make_train_step(model, loss_fn, optimizer): def train_step(x, y): model.train() yhat = model(x) loss = loss_fn(yhat, y) loss.backward() optimizer.step() optimizer.zero_grad() return loss.item() return train_step 

рд╕реНрд╡ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдореЙрдбрд▓:

 from torch import optim, nn model = torch.nn.Sequential( nn.Linear(10, 270), nn.ReLU(), nn.Linear(270, 3)) lr = 0.01 n_epochs = 500 loss_fn = torch.nn.CrossEntropyLoss() optimizer = optim.Adagrad(model.parameters(), lr=lr) train_step = make_train_step(model, loss_fn, optimizer) from sklearn.utils import shuffle for epoch in range(n_epochs): x_train, y_train = shuffle(x_train, y_train) #    X = torch.FloatTensor(x_train) y = torch.LongTensor(y_train) loss = train_step(X, y) print(loss) 

рдореЙрдбрд▓ рд░реЗрдЯрд┐рдВрдЧ:

 #  : test_var = torch.FloatTensor(x_val) with torch.no_grad(): result = model(test_var) values, labels = torch.max(result, 1) num_right = np.sum(labels.data.numpy() == y_val) print(' {:.2f}'.format(num_right / len(y_val))) 

рдпрд╣рд╛рдВ, рдкрд░рддреЛрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреЗрд╡рд▓ 2 рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рдкреИрд░рд╛рдореАрдЯрд░ рд╣реИрдВ (рдЕрднреА рдХреЗ рд▓рд┐рдП):
рд╕реАрдЦрдиреЗ рдХреА рджрд░ рдФрд░ n_epochs (рдпреБрдЧреЛрдВ рдХреА рд╕рдВрдЦреНрдпрд╛)ред

рд╣рдо рдЗрди рджреЛ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдХреИрд╕реЗ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ, рдЗрд╕рдХреЗ рдЖрдзрд╛рд░ рдкрд░, 3 рд╕реНрдерд┐рддрд┐рдпрд╛рдВ рдЙрддреНрдкрдиреНрди рд╣реЛ рд╕рдХрддреА рд╣реИрдВ:

1 - рд╕рдм рдареАрдХ рд╣реИ, рдЕрд░реНрдерд╛рдд рдореЙрдбрд▓ рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдирдореВрдиреЗ рдкрд░ рдХрдо рдиреБрдХрд╕рд╛рди рдФрд░ рд╕рддреНрдпрд╛рдкрди рдкрд░ рдЙрдЪреНрдЪ рд╕рдЯреАрдХрддрд╛ рджрд┐рдЦрд╛рддрд╛ рд╣реИред

2 - рдЕрдВрдбрд░рдлрд╝рд┐рдЯрд┐рдВрдЧ - рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдХреЗ рдирдореВрдиреЗ рдкрд░ рдмрдбрд╝рд╛ рдиреБрдХрд╕рд╛рди рдФрд░ рд╕рддреНрдпрд╛рдкрди рдкрд░ рдХрдо рд╕рдЯреАрдХрддрд╛ред

3 - рдУрд╡рд░рдлрд┐рдЯрд┐рдВрдЧ - рдкреНрд░рд╢рд┐рдХреНрд╖рдг рдирдореВрдиреЗ рдкрд░ рдХрдо рдиреБрдХрд╕рд╛рди, рд▓реЗрдХрд┐рди рд╕рддреНрдпрд╛рдкрди рдкрд░ рдХрдо рд╕рдЯреАрдХрддрд╛ред

рдкрд╣рд▓реЗ рдХреЗ рд╕рд╛рде, рд╕рдм рдХреБрдЫ рд╕реНрдкрд╖реНрдЯ рд╣реИ :)

рджреВрд╕рд░реЗ рдХреЗ рд╕рд╛рде, рдпрд╣ рднреА рд▓рдЧрддрд╛ рд╣реИ - рд╕реАрдЦрдиреЗ рдХреА рджрд░ рдФрд░ n_epochs рдХреЗ рд╕рд╛рде рдкреНрд░рдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдПред

рдФрд░ рддреАрд╕рд░реЗ рдХреЗ рд╕рд╛рде рдХреНрдпрд╛ рдХрд░рдирд╛ рд╣реИ? рдЙрддреНрддрд░ рд╕рд░рд▓ рд╣реИ - рдирд┐рдпрдорд┐рддреАрдХрд░рдг!

рдкрд╣рд▓реЗ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдлрд╝реЙрд░реНрдо рдХрд╛ рдиреБрдХрд╕рд╛рди рдХрд╛рд░реНрдп рдерд╛:
рдЕрддрд┐рд░рд┐рдХреНрдд рд╢рд░реНрддреЛрдВ рдХреЗ рдмрд┐рдирд╛ рдПрд▓ = рдПрдордПрд╕рдИ (рд╡рд╛рдИ, рд╡рд╛рдИ)
рдирд┐рдпрдорд┐рддреАрдХрд░рдг рдХрд╛ рд╕рд╛рд░ рдареАрдХ рдпрд╣реА рд╣реИ рдХрд┐, рдЙрджреНрджреЗрд╢реНрдп рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ рдПрдХ рд╢рдмреНрдж рдЬреЛрдбрд╝рддреЗ рд╣реБрдП, "рдареАрдХ" рдЧреНрд░реЗрдбрд┐рдПрдВрдЯ рдпрджрд┐ рдпрд╣ рдмрд╣реБрдд рдмрдбрд╝рд╛ рд╣реИред рджреВрд╕рд░реЗ рд╢рдмреНрджреЛрдВ рдореЗрдВ, рд╣рдо рдЕрдкрдиреЗ рдЙрджреНрджреЗрд╢реНрдп рд╕рдорд╛рд░реЛрд╣ рдкрд░ рдкреНрд░рддрд┐рдмрдВрдз рд▓рдЧрд╛рддреЗ рд╣реИрдВред

рдХрдИ рдирд┐рдпрдорд┐рддреАрдХрд░рдг рдХреЗ рддрд░реАрдХреЗ рд╣реИрдВред L1 рдФрд░ L2 рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рдЬрд╛рдирдХрд╛рд░реА - рдирд┐рдпрдорд┐рддреАрдХрд░рдг: craftappmobile.com/l1-vs-l2- рдЕрдирд┐рдпрдорд┐рддрдХрд░рдг / ##1322

Adagrad рд╡рд┐рдзрд┐ L2 рдирд┐рдпрдорд┐рддреАрдХрд░рдг рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИ, рдЪрд▓реЛ рдЗрд╕реЗ рд▓рд╛рдЧреВ рдХрд░реЗрдВ!

рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рд╕реНрдкрд╖реНрдЯрддрд╛ рдХреЗ рд▓рд┐рдП, рд╣рдо рдирд┐рдпрдорд┐рддреАрдХрд░рдг рдХреЗ рдмрд┐рдирд╛ рдореЙрдбрд▓ рдХреЗ рд╕рдВрдХреЗрддрдХреЛрдВ рдХреЛ рджреЗрдЦрддреЗ рд╣реИрдВ:

lr = 0.01, n_epochs = 500:
рд╣рд╛рдирд┐ = 0.44 ...
рд╕рдЯреАрдХрддрд╛: 0.71

lr = 0.01, n_epochs = 1000:
рд╣рд╛рдирд┐ = 0.41 ...
рд╕рдЯреАрдХрддрд╛: 0.75

lr = 0.01, n_epochs = 2000:
рд╣рд╛рдирд┐ = 0.39 ...
рд╕рдЯреАрдХрддрд╛: 0.75

lr = 0.01, n_epochs = 3000:
рд╣рд╛рдирд┐ = 0.367 ...
рд╕рдЯреАрдХрддрд╛: 0.76

lr = 0.01, n_epochs = 4000:
рд╣рд╛рдирд┐ = 0.355 ...
рд╕рдЯреАрдХрддрд╛: 0.72

lr = 0.01, n_epochs = 10000:
рд╣рд╛рдирд┐ = 0.285 ...
рд╕рдЯреАрдХрддрд╛: 0.69

рдпрд╣рд╛рдВ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ 4k + рдпреБрдЧ рдореЗрдВ - рдореЙрдбрд▓ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдУрд╡рд░рдлрд┐рдЯ рд╣реИред рдЕрдм рдЗрд╕рд╕реЗ рдмрдЪрдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ рд╣реИрдВ:

рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рдЕрдиреБрдХреВрд▓рди рд╡рд┐рдзрд┐ рдХреЗ рд▓рд┐рдП weight_decay рдкреИрд░рд╛рдореАрдЯрд░ рдЬреЛрдбрд╝реЗрдВ:

 optimizer = optim.Adagrad(model.parameters(), lr=lr, weight_decay = 0.001) 

Lr = 0.01 рдХреЗ рд╕рд╛рде, m_epochs = 10000:
рд╣рд╛рдирд┐ = 0.367 ...
рд╕рдЯреАрдХрддрд╛: 0.73

4000 рдпреБрдЧреЛрдВ рдореЗрдВ:
рд╣рд╛рдирд┐ = 0.389 ...
рд╕рдЯреАрдХрддрд╛: 0.75

рдпрд╣ рдмрд╣реБрдд рдмреЗрд╣рддрд░ рдирд┐рдХрд▓рд╛, рд▓реЗрдХрд┐рди рд╣рдордиреЗ рдСрдкреНрдЯрд┐рдорд╛рдЗрдЬрд╝рд░ рдореЗрдВ рдХреЗрд╡рд▓ 1 рдкреИрд░рд╛рдореАрдЯрд░ рдЬреЛрдбрд╝рд╛ :)

рдЕрдм SGDm рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ (рдпрд╣ рдПрдХ рдЫреЛрдЯреЗ рд╕реЗ рд╡рд┐рд╕реНрддрд╛рд░ рдХреЗ рд╕рд╛рде рдПрдХ рд╕реНрдЯреЛрдХреЗрд╕реНрдЯрд┐рдХ рдврд╛рд▓ рд╡рдВрд╢ рд╣реИ - рдпрджрд┐ рдЖрдк рдЪрд╛рд╣реЗрдВ, рддреЛ рдЖрдВрдХрдбрд╝реЗ)ред

рд▓рдмреНрдмреЛрд▓реБрдЖрдм рдпрд╣ рд╣реИ рдХрд┐ SGD рдкреНрд░рддреНрдпреЗрдХ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рдмрд╛рдж рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдХрд╛рдлреА рдордЬрдмреВрддреА рд╕реЗ рдЕрдкрдбреЗрдЯ рдХрд░рддрд╛ рд╣реИред рдкрд┐рдЫрд▓реЗ рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐рдпреЛрдВ (рдЬрдбрд╝рддрд╛ рдХреЗ рд╡рд┐рдЪрд╛рд░) рд╕реЗ рдЧреНрд░реЗрдбрд┐рдПрдВрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП рдврд╛рд▓ рдХреЛ "рд╕реБрдЪрд╛рд░реВ" рдХрд░рдирд╛ рддрд░реНрдХрд╕рдВрдЧрдд рд╣реЛрдЧрд╛:



weight - рдкреИрд░рд╛рдореАрдЯрд░ (рд╡рдЬрди)
ia - рдЬрдбрд╝рддрд╛ рд╣рд╛рдЗрдкрд░рдкрд░рдореАрдЯрд░

рдмрд┐рдирд╛ рдЧрддрд┐ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ



рдЧрддрд┐ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд╕рд╛рде SGD:



рдпрд╣ рдмрд╣реБрдд рдмреЗрд╣рддрд░ рдирд╣реАрдВ рдирд┐рдХрд▓рд╛, рд▓реЗрдХрд┐рди рдпрд╣рд╛рдВ рдореБрджреНрджрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдРрд╕реЗ рддрд░реАрдХреЗ рд╣реИрдВ рдЬреЛ рддреБрд░рдВрдд рд╕реНрдХреЗрд▓рд┐рдВрдЧ рдФрд░ рдЬрдбрд╝рддрд╛ рдХреЗ рд╡рд┐рдЪрд╛рд░реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдПрдбрдо рдпрд╛ рдЕрдбрд╛рд▓реНрдЯрд╛, рдЬреЛ рдЕрдм рдЕрдЪреНрдЫреЗ рдкрд░рд┐рдгрд╛рдо рджрд┐рдЦрд╛рддреЗ рд╣реИрдВред рдЦреИрд░, рдЗрди рддрд░реАрдХреЛрдВ рдХреЛ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП, рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рд╕рд░рд▓ рддрд░реАрдХреЛрдВ рдореЗрдВ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдХреБрдЫ рдмреБрдирд┐рдпрд╛рджреА рд╡рд┐рдЪрд╛рд░реЛрдВ рдХреЛ рд╕рдордЭрдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред

рдзреНрдпрд╛рди рджреЗрдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдк рд╕рднреА рдХрд╛ рдзрдиреНрдпрд╡рд╛рдж!

Source: https://habr.com/ru/post/hi473348/


All Articles