Задание. Предложите другую функцию активации, в комментариях напишите её формулу. Добейтесь 100-процентной классификации, применяя вашу функцию активации и, возможно, изменяя другие параметры обучения. Вычислите выведите на печать accuracy.

import numpy as np
import torch
from torch import nn
import matplotlib.pyplot as plt
%matplotlib inline

#генерация линейно неразделимых множеств
np.random.seed(0)
N = 100 # количество элементов в классе
D = 2 # размерность
K = 3 # количество классов
X = np.zeros((N*K,D))
num_train_examples = X.shape[0]
y = np.zeros(N*K, dtype='uint8')
for j in range(K):
    ix = range(N*j,N*(j+1))
    r = np.linspace(0.0,1,N) # радиус
    t = np.linspace(j*4,(j+1)*4,N) + np.random.randn(N)*0.2
    X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
    y[ix] = j
fig = plt.figure()
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.brg)
plt.xlim([-1,1])
plt.ylim([-1,1])

N_FEATURES_SMALL = 2
N_CLASSES_SMALL = 3
NUM_EPOCH = 500

def create_model(activation, hid_size=50, num_features=N_FEATURES_SMALL, n_out=N_CLASSES_SMALL):
    model = nn.Sequential()
    model.add_module('l1', nn.Linear(num_features, hid_size))
    model.add_module('activation1', activation())
    model.add_module('l2', nn.Linear(hid_size, hid_size))
    model.add_module('activation2', activation())
    model.add_module('l3', nn.Linear(hid_size, n_out))

    print(model)
    return model

def train_model(model, X_train, y_train):
    loss_function = nn.CrossEntropyLoss()
    opt = torch.optim.SGD(model.parameters(), lr=0.1)

    loss_history, var_layer_1, var_layer_2, var_layer_3 = [], [], [], []

    for epoch_num in range(NUM_EPOCH):
        opt.zero_grad()

        y_predicted = model(X_train)
        loss = loss_function(y_predicted, y_train)
        reg = 0.01 * (torch.norm(model[0].weight) \
                      + torch.norm(model[2].weight) \
                      + torch.norm(model[4].weight))
        loss += reg
        loss.backward()
        opt.step()

        loss_history.append(loss.item())

        var_layer_1.append(torch.sum(torch.abs(model[0].weight.grad)).item()/model[0].weight.shape[0])
        var_layer_2.append(torch.sum(torch.abs(model[2].weight.grad)).item()/model[2].weight.shape[0])
        var_layer_3.append(torch.sum(torch.abs(model[4].weight.grad)).item()/model[4].weight.shape[0])

    return model, loss_history, var_layer_1, var_layer_2, var_layer_3

X_torch = torch.FloatTensor(X)
y_torch = torch.LongTensor(y)

model_sigmoid = create_model(nn.Sigmoid)

%%time
out_sigmoid = train_model(model_sigmoid, X_torch, y_torch)

plt.plot(np.array(out_sigmoid[2][100:]), label="динамика изменения сигмоиды на первом слое")
plt.plot(np.array(out_sigmoid[3][100:]), label="динамика изменения сигмоиды на втором слое")
plt.plot(np.array(out_sigmoid[4][100:]), label="динамика изменения сигмоиды на третьем слое")
plt.title('Сумма градиентов')
plt.legend()

# plot the classifiers- SIGMOID
h = 0.02
x_min = X[:, 0].min() - 1
x_max = X[:, 0].max() + 1

y_min = X[:, 1].min() - 1
y_max = X[:, 1].max() + 1

xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                     np.arange(y_min, y_max, h))

X_draw = torch.FloatTensor(np.c_[xx.ravel(), yy.ravel()])

with torch.no_grad():
    Z = model_sigmoid(X_draw).numpy()
Z = np.argmax(Z, axis=1)
Z = Z.reshape(xx.shape)
fig = plt.figure()
plt.contourf(xx, yy, Z, cmap=plt.cm.brg, alpha=0.3)
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.brg)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())

выполни -адание + дай подробное описание к этоу коду и своему