понедельник, 24 декабря 2018 г.

PyTorch: тензоры (tensors)

Что такое PyTorch?

PyTorch - это Python пакет для научных вычислений, предназначенный для двух целей:

  1. В качестве замены NumPy для использования преимуществ GPU
  2. В качестве платформы для разработки программ глубого обучения, которая предоставляет максимальную гибкость и скорость.

Тензоры (Tensors) в PyTorch

Тензоры схожи с ndarrays в NumPy, с добавлением того, что тензоры могут быть использованы на GPU для ускорения вычислений. Импортируем torch и рассмотрим несколько примеров:

from future import print_function
import torch

Контруируем 5x3 матрицу, не инициализированную:

x = torch.empty(5, 3)
print(x)

Вывод:

tensor([[1.3440e-19, 4.5673e-41, 8.3430e-18],
        [4.5673e-41, 2.5431e+30, 5.5073e+11],
        [5.2563e+05, 5.5123e+11, 1.6669e+35],
        [2.1541e+09, 3.7906e+22, 4.1644e+34],
        [7.3002e-12, 3.9694e+28, 9.4759e+21]])
print(x)

Конструируем матрицу, инициализированную случайными значениями:

x = torch.rand(5, 3)
print(x)

Вывод:

tensor([[0.4163, 0.1625, 0.9454],
        [0.8632, 0.3480, 0.1602],
        [0.3037, 0.3912, 0.3449],
        [0.3911, 0.5578, 0.7367],
        [0.6628, 0.3991, 0.3471]])
print(x)

Создадим матрицу, наполненную нулями и dtype long:

x = torch.zeros(5, 3, dtype=torch.long)
print(x)

Вывод:

tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])

Создадим тензор напрямую из данных:

x = torch.tensor([5.5, 3])
print(x)

Вывод:

tensor([5.5000, 3.0000])

Или создадим тензор, основанный на существующем тензоре. Этот метод будет повторно использовать свойства вводного тензора, например dtype, если новые значения не переданы пользователем

# new_* методы принимают размеры
x = x.new_ones(5, 3, dtype=torch.double)      
print(x)

# переопределяем dtype!
x = torch.randn_like(x, dtype=torch.float)
# результат имеет тот же размер
print(x) 

Вывод:

tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
tensor([[-0.6714,  0.7803, -1.0026],
        [-1.1583,  1.7177,  2.7201],
        [-0.1254, -0.4324, -0.6761],
        [-2.1195, -0.7945,  0.6865],
        [ 0.1464, -0.3747, -0.8441]])

Возьмем его размер:

print(x.size())

Вывод:

torch.Size([5, 3])

Примечание

torch.Size по факту является tuple, поэтому он поддерживает все tuple операции.

Операции с тензорами

Существует несколько вариантов синтаксиса для операций. В следующем примере мы рассмотрим операцию сложения.

Сложение: синтаксис 1

y = torch.rand(5, 3)
print(x + y)

Вывод:

tensor([[ 0.0732,  0.9384, -0.2489],
        [-0.6905,  2.1267,  3.0045],
        [ 0.6199,  0.4936, -0.0398],
        [-2.0623, -0.5140,  1.6162],
        [ 0.3189, -0.0327, -0.5353]])

Сложение: синтаксис 2

print(torch.add(x, y))

Вывод:

tensor([[ 0.0732,  0.9384, -0.2489],
        [-0.6905,  2.1267,  3.0045],
        [ 0.6199,  0.4936, -0.0398],
        [-2.0623, -0.5140,  1.6162],
        [ 0.3189, -0.0327, -0.5353]])

Сложение: предоставление тензора вывода как аргумента

result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)

Вывод:

tensor([[ 0.0732,  0.9384, -0.2489],
        [-0.6905,  2.1267,  3.0045],
        [ 0.6199,  0.4936, -0.0398],
        [-2.0623, -0.5140,  1.6162],
        [ 0.3189, -0.0327, -0.5353]])

Сложение: на месте (in-place)

# добавляем x к y
y.add_(x)
print(y)

Вывод:

tensor([[ 0.0732,  0.9384, -0.2489],
        [-0.6905,  2.1267,  3.0045],
        [ 0.6199,  0.4936, -0.0398],
        [-2.0623, -0.5140,  1.6162],
        [ 0.3189, -0.0327, -0.5353]])

Примечание

Любая операция, которая изменяет тензор на месте имеет постфикс _. Например: x.copy_(y), x.t_(), будут изменять x.

Вы можете использовать стандартное (как в NumPy) индексирование:

print(x[:, 1])

Вывод:

tensor([ 0.7803,  1.7177, -0.4324, -0.7945, -0.3747])

Изменение размера: если вы хотите изменить размер/форму тензора, вы можете использовать torch.view:

x = torch.randn(4, 4)
y = x.view(16)
# размер -1 выведен из других измерений
z = x.view(-1, 8)  
print(x.size(), y.size(), z.size())

Вывод:

torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])

Если у вас есть одноэлементный тензор, используйте .item() для получения значения как Python числа

x = torch.randn(1)
print(x)
print(x.item())

Вывод:

tensor([0.1550])
0.15495021641254425


NumPy мост

Преобразование Torch тензора в NumPy массив и обратно выполняется легко.

Torch тензор и NumPy массив будут иметь общие подлежащие занятые области памяти, и изменение одного из них будет изменять другой.

Преобразование Torch тензора в NumPy массив

a = torch.ones(5)
print(a)

Вывод:

tensor([1., 1., 1., 1., 1.])

b = a.numpy()
print(b)

Вывод:

[1. 1. 1. 1. 1.]

Посмотрим как изменится значение в numpy массиве.

a.add_(1)
print(a)
print(b)

Вывод:

tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]

Преобразование NumPy массива в Torch тензор

Рассмотрим как изменяется NumPy массив, преобразованный в Torch тензор автоматически

import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)

Вывод:

[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

Все тензоры на CPU, исключая CharTensor, поддерживают преобразование в NumPy и обратно.

CUDA тензоры

Тензоры могут быть перемещены на любое устройство, используя .to метод.

# выполняем этот скрипт, только если CUDA доступно
# Мы будем использовать ``torch.device`` объекты
# чтобы перемещать тензор на GPU и обратно
if torch.cuda.is_available():
    # объект CUDA устройства
    device = torch.device("cuda")
    # напрямую создаем тензор на GPU          
    y = torch.ones_like(x, device=device) 
    # или просто используем строки ``.to("cuda")``
    x = x.to(device)                     
    z = x + y
    print(z)
    # ``.to`` может также одновременно изменять dtype!
    print(z.to("cpu", torch.double))       

Вывод:

tensor([1.1550], device='cuda:0')
tensor([1.1550], dtype=torch.float64)



Читайте также другие статьи в блоге:

TensorFlow: базовая классификация

TensorFlow: классификация текста с отзывами к фильмам

Анатомия нейронных сетей