Вернуться на страницу brain2net: https://brain2net.ru/post/pytorch-primer-linejnoj-regressii/#more-202

PyTorch. Пример линейной регрессии.

In [1]:
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
%matplotlib inline

Для формирования нейронной сети на PyTorch целесообразно подготовить специальный класс, определяющий конкретный тип модели на основе родительского класса nn.Module.

В этом классе формируются два метода. Метод init , который с помощью модуля torch.nn описывает все требуемые слои нейронной сети. Метод forward задает последовательность следования слоев и соединяет выходы и входы соседних слоев.

In [2]:
# Standard model class
class LinearModel(nn.Module):
    def __init__(self, in_dim, out_dim):
        # nn.Module является базовым классом и вызывается через функцию super() при инициализации.
        super(LinearModel, self).__init__()
        self.linear = nn.Linear(in_dim, out_dim)
        
    def forward(self, x):
        out = self.linear(x)
        return out

Формируется модель, экземпляр созданного класса - в данном случае для линейной регрессии. При инициализации класса задаются размерность входа и выхода модели.

In [3]:
model = LinearModel(1, 1)

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

In [4]:
# Коэффициент скорости обучения. При большом значении результат будет "скакать". При малом - длительное время оптимизации.
learnRate = 0.01
# Выбор оптимизатора. Выбирается стохастический градиентный спуск.
optimiser =  torch.optim.SGD(model.parameters(), lr =learnRate)
# Выбор критерия оценки потерь. Выбирается среднеквадратичная ошибка.
criterion = nn.MSELoss()

Для примера необходимо задать точки на плоскости и описать их прямой, полученной в результате линейной регрессии.

In [5]:
# Случайные 9 чисел на отрезке от 0 до 10
x_train = 10 * torch.rand([12,1])
x_train
Out[5]:
tensor([[5.9051],
        [9.1780],
        [0.7733],
        [4.4426],
        [9.3145],
        [7.0795],
        [1.8495],
        [1.7166],
        [6.9200],
        [2.4705],
        [8.9358],
        [6.4141]])
In [6]:
# Случайный шум
noise = 1.2 * torch.rand([12,1])
noise
Out[6]:
tensor([[0.5665],
        [0.5460],
        [0.1558],
        [0.9380],
        [0.4195],
        [0.5166],
        [1.0239],
        [1.0381],
        [0.5585],
        [0.3709],
        [1.0199],
        [0.9724]])
In [7]:
# Линейная функция от x_train с добавлением "шума"
# Для получения матрицы [12, 1] добавляется .reshape(-1,1). Без этого будет матрица [1, 12]
y_train = torch.tensor([2*x+9 for x in x_train]).reshape(-1,1)
y_train += noise
y_train
Out[7]:
tensor([[21.3768],
        [27.9019],
        [10.7024],
        [18.8233],
        [28.0485],
        [23.6757],
        [13.7228],
        [13.4713],
        [23.3985],
        [14.3119],
        [27.8916],
        [22.8005]])

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

In [8]:
# Количество циклов - эпох
# Для примера принято 50, но реально для сходимости процесса это очень мало. Надо брать больше.
epochs = 30
for epoch in range(epochs):
    epoch += 1
    inputs = x_train
    labels = y_train
    out = model(inputs)
    # Важно отметить, что нам нужно очистить градиенты, чтобы они не накапливались в течение эпох и
    # не искажали модель. Это достигается путем вызова функции zero_grade() в
    # оптимизаторе для каждой эпохи. 
    optimiser.zero_grad()
    # Вычисление функции потеть - среднеквадратичного отклонения
    loss = criterion(out, labels)
    # Вычисление градиента
    loss.backward()
    # Очередной шаг стохастического градиентного спуска
    optimiser.step()
    predicted = model.forward(x_train)
    print('epoch {}, loss {}'.format(epoch, loss.item()))
epoch 1, loss 763.1433715820312
epoch 2, loss 58.03849792480469
epoch 3, loss 23.1838436126709
epoch 4, loss 21.287294387817383
epoch 5, loss 21.01295280456543
epoch 6, loss 20.82000732421875
epoch 7, loss 20.632692337036133
epoch 8, loss 20.447256088256836
epoch 9, loss 20.263500213623047
epoch 10, loss 20.081405639648438
epoch 11, loss 19.90095329284668
epoch 12, loss 19.722129821777344
epoch 13, loss 19.5449161529541
epoch 14, loss 19.369308471679688
epoch 15, loss 19.195281982421875
epoch 16, loss 19.0228271484375
epoch 17, loss 18.8519229888916
epoch 18, loss 18.68256950378418
epoch 19, loss 18.514738082885742
epoch 20, loss 18.348424911499023
epoch 21, loss 18.183610916137695
epoch 22, loss 18.020282745361328
epoch 23, loss 17.85843276977539
epoch 24, loss 17.698041915893555
epoch 25, loss 17.539094924926758
epoch 26, loss 17.3815860748291
epoch 27, loss 17.225500106811523
epoch 28, loss 17.070819854736328
epoch 29, loss 16.91753578186035
epoch 30, loss 16.76563262939453
In [9]:
# Коэффициенты линейной регрессии, полученные в результате стохастического градиентного спуска
coef = model.state_dict()
# Результат близкий к начальной линейной функции, использованной для формирования обучающей выборки
coef
Out[9]:
OrderedDict([('linear.weight', tensor([[3.2093]])),
             ('linear.bias', tensor([1.2132]))])

Предсказание точек методом линейной регрессии.

In [10]:
y_predict = predicted
y_predict
Out[10]:
tensor([[20.1642],
        [30.6676],
        [ 3.6949],
        [15.4708],
        [31.1059],
        [23.9331],
        [ 7.1487],
        [ 6.7221],
        [23.4213],
        [ 9.1418],
        [29.8906],
        [21.7976]], grad_fn=<AddmmBackward0>)

Графическое изображение результата.

NB Совпадение полученной линии с исходными точками плохое из-за малого количества эпох, которое было взято для примера.

Для построения графика torch.tensor преобразуются в numpy.array.

С torch.tensor предварительно проводится процедура .detach(). tensor.detach() создает тензор для которого не требуется градиент. Он отделяет выходные данные от вычислительного графика. Таким образом, никакой градиент не будет распространяться обратно вдоль этой переменной.

In [11]:
x = x_train.detach().numpy() 
plt.plot(x, y_predict.detach().numpy(), label = 'predicted')
plt.plot(x, y_train.detach().numpy(),'mo', label = 'from data')
plt.legend()
plt.grid()
plt.show()

Вернуться на страницу brain2net: https://brain2net.ru/post/pytorch-primer-linejnoj-regressii/#more-202