воскресенье, 30 декабря 2018 г.

PyTorch: использование GPU

В этом посте мы рассмотрим, как в PyTorch использовать несколько GPU, с помощью DataParallel.

С PyTorch очень легко использовать GPU. Вы можете поместить модель на GPU:

device = torch.device("cuda:0")
model.to(device)

Затем вы можете скопировать все свои тензоры в GPU:

mytensor = my_tensor.to(device)

Обратите внимание, что простой вызов my_tensor.to(device) возвращает новую копию my_tensor на GPU вместо переписывания my_tensor. Вы должны назначить его на новый тензор и использовать этот тензор на GPU.

Естественно выполнять прямое и обратное распространение на нескольких GPU. Однако по умолчанию Pytorch будет использовать только один GPU. Вы можете легко запустить свои операции на нескольких GPU, заставляя вашу модель работать параллельно, используя DataParallel:

model = nn.DataParallel(model)

Рассмотрим это подробней.

Импорт и параметры

Импортируйте модули PyTorch и определите параметры.

# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader

# Параметры и DataLoaders
input_size = 5
output_size = 2

batch_size = 30
data_size = 100

Устройство

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")


Фиктивный набор данных

Создаем фиктивный (случайный) набор данных. Вам только необходимо реализовать getitem

class RandomDataset(Dataset):

    def init(self, size, length):
        self.len = length
        self.data = torch.randn(length, size)

    def getitem(self, index):
        return self.data[index]

    def len(self):
        return self.len

rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size),
                         batch_size=batch_size, shuffle=True)


Выполняем модель

Теперь мы можем увидеть размеры вводных и выводных тензоров.

for data in rand_loader:
    input = data.to(device)
    output = model(input)
    print("Outside: input size", input.size(),
          "output_size", output.size())

Вывод:

In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
        In Model: input size torch.Size([15, 5]) 
                  output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([15, 5]) 
                  output size torch.Size([15, 2])
        In Model: input size torch.Size([15, 5]) 
                  output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([15, 5]) 
                  output size torch.Size([15, 2])
        In Model: input size torch.Size([15, 5]) 
                  output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
        In Model: input size torch.Size([5, 5]) 
                  output size torch.Size([5, 2])
        In Model: input size torch.Size([5, 5]) 
                  output size torch.Size([5, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])

Результаты

Если у вас нет GPU или один GPU, то в этом случае, когда мы пакетизируем 30 вводов и 30 выводов, модель получает 30 и выводит 30 как ожидается. Но если у нас есть несколько GPU, тогда мы можем получить результаты как следующие.

2 GPU

Если у вас 2 GPU, тогда вы увидите:

# на 2 GPU
Let's use 2 GPUs!
    In Model: input size torch.Size([15, 5]) 
              output size torch.Size([15, 2])
    In Model: input size torch.Size([15, 5]) 
              output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([15, 5]) 
              output size torch.Size([15, 2])
    In Model: input size torch.Size([15, 5]) 
              output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([15, 5]) 
              output size torch.Size([15, 2])
    In Model: input size torch.Size([15, 5]) 
              output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([5, 5]) 
              output size torch.Size([5, 2])
    In Model: input size torch.Size([5, 5]) 
              output size torch.Size([5, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])

3 GPU

Если у вас 3 GPU, тогда вы увидите:

Let's use 3 GPUs!
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
    In Model: input size torch.Size([10, 5]) 
              output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])

8 GPU

Если у вас 8 GPU, тогда вы увидите:

Let's use 8 GPUs!
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([4, 5]) 
              output size torch.Size([4, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
    In Model: input size torch.Size([2, 5]) 
              output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])


Заключение

DataParallel разделяет ваши данные автоматически и отсылает рабочие задания на несколько моделей на нескольких GPU. После того как все модели завершат своя работу, DataParallel собирает и объединяет результаты перед тем как их вернуть.


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

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

PyTorch: нейронные сети

PyTorch: создание классификатора изображений


суббота, 29 декабря 2018 г.

PyTorch: создание классификатора изображений

В предыдущих постах мы рассмотрели, как определить нейронные сети, вычислить потери и сделать обновления весов сети.

Теперь можно задуматься,

А как насчет данных?

Как правило, когда вам приходится иметь дело с изображениями, текстом, аудио или видео данными, вы можете использовать стандартные Python пакеты, которые загружают данные в NumPy массив. Затем вы можете преобразовать этот массив в torch.*Tensor.

  • Для изображений полезны такие пакеты, как Pillow, OpenCV
  • Для аудио, такие пакеты, как scipy и librosa
  • Для текста - либо загрузка на основе Python, либо на основе Cython, либо NLTK и SpaCy будут полезны

Специально для изображений существует пакет под названием torchvision, в котором есть загрузчики данных для общераспространенных наборов данных, таких как Imagenet, CIFAR10, MNIST и т.д. И преобразователи данных для изображений, а именно, torchvision.datasets и torch.utils.data.DataLoader.

Эти пакеты дают огромное преимущество и позволяют избежать написания стандартного кода.

В этом посте мы будем использовать набор данных CIFAR10. У него есть классы: ‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’. Изображения в CIFAR-10 имеют размер 3x32x32, то есть 3-канальные цветные изображения размером 32x32 пикселей.


Создание классификатора изображений

Мы выполним следующие шаги по порядку:

  1. Загрузим и нормализуем обучающие и тестовые наборы данных CIFAR10, используя torchvision
  2. Определим сверточную нейронную сеть
  3. Определим функцию потерь
  4. Обучим сеть на тренировочных данных
  5. Протестируем сеть на тестовых данных

1. Загрузка и нормализация CIFAR10

Используя torchvision, загрузить CIFAR10 крайне просто.

import torch
import torchvision
import torchvision.transforms as transforms

Выходные данные torchvision наборов данных представляют собой изображения PILImage диапазона [0, 1]. Мы преобразуем их в тензоры нормализованного диапазона [-1, 1].

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

trainset = torchvision.datasets.CIFAR10(root='./data', 
                                        train=True,
                                        download=True, 
                                        transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, 
                                          batch_size=4,
                                          shuffle=True, 
                                          num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data', 
                                       train=False,
                                       download=True, 
                                       transform=transform)
testloader = torch.utils.data.DataLoader(testset, 
                                         batch_size=4,
                                         shuffle=False, 
                                         num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

Вывод:

Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz 
to ./data/cifar-10-python.tar.gz
Files already downloaded and verified

Выведем несколько тренировочных изображений:

# -*- coding: utf-8 -*-
import matplotlib.pyplot as plt
import numpy as np

# функция для показа изображения

def imshow(img):
    img = img / 2 + 0.5     # денормализуем
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# получаем несколько случайных тренировочных изображений
dataiter = iter(trainloader)
images, labels = dataiter.next()

# показываем изображения
imshow(torchvision.utils.make_grid(images))
# печатаем метки
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

Вывод:

cat plane ship frog


2. Определяем сверточную нейронную сеть

Скопируем нейронную сеть из предыдущего поста и модифицируем ее для приема 3-канальных изображений (вместо 1-канальных изображений как было определено).

import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):
    def init(self):
        super(Net, self).init()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()


3. Определяем функцию потери и оптимизатор

Будем использовать классификацию кросс-энтропийных потерь и SGD с импульсом.

import torch.optim as optim

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)


4. Тренировка сети

Для тренировки сети нам просто нужно пройти цикл нашего итератора данных и передать вводные данные в сеть и оптимизировать.

# -*- coding: utf-8 -*-

# проходим в цикле по набору данных несколько раз
for epoch in range(2):  

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # получаем вводные данные
        inputs, labels = data

        # обнуляем параметр gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # печатаем статистику
        running_loss += loss.item()
        if i % 2000 == 1999:    # печатаем каждые 2000 мини-пакетов
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Тренировка завершена')

Вывод:

[1,  2000] loss: 2.187
[1,  4000] loss: 1.852
[1,  6000] loss: 1.672
[1,  8000] loss: 1.566
[1, 10000] loss: 1.490
[1, 12000] loss: 1.461
[2,  2000] loss: 1.389
[2,  4000] loss: 1.364
[2,  6000] loss: 1.343
[2,  8000] loss: 1.318
[2, 10000] loss: 1.282
[2, 12000] loss: 1.286
Тренировка завершена


5. Тестируем сеть с помощью тестовых данных

Мы обучили сеть за 2 прохода по набору тренировочных данных. Но нам нужно проверить, научилась ли сеть вообще чему-либо.

Мы проверим это, спрогнозировав метку класса, которую выдает нейронная сеть, и проверим ее на основании истины. Если прогноз правильный, мы добавляем пример в список правильных прогнозов.

Хорошо, первый шаг. Давайте покажем изображение из тестового набора для ознакомления.

dataiter = iter(testloader)
images, labels = dataiter.next()

# печатаем изображения
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', 
      ' '.join('%5s' % classes[labels[j]] for j in range(4)))

Вывод:

GroundTruth: cat ship ship plane

Теперь посмотрим, что нейронная сеть думает об этих примерах:

outputs = net(images)

Выводы - это рассчитанные вероятности для 10 классов. Чем больше вероятность для класса, тем вероятнее нейронная сеть предполагает, что изображение принадлежит этому классу. Таким образом, возьмем индекс наибольшей вероятности:

_, predicted = torch.max(outputs, 1)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))

Вывод:

Predicted: cat ship car ship

Результаты кажутся весьма хорошими.

Посмотрим как сеть отработает на целом наборе данных.

# -*- coding: utf-8 -*-

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print('Аккуратность сети на 10000 тестовых изображений: %d %%' % (
    100 * correct / total))

Вывод:

Аккуратность сети на 10000 тестовых изображений: 54 %

Это выглядит лучше, чем простой случай, где аккуратность 10% (выбор класса из 10 классов случайным образом). Кажется сеть обучилась чему-то.

Посмотрим на каких классах прогнозы выполнились хорошо, а на каких не очень хорошо:

# -*- coding: utf-8 -*-

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1


for i in range(10):
    print('Аккуратность %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))

Вывод:

Аккуратность plane : 57 %
Аккуратность   car : 73 %
Аккуратность  bird : 49 %
Аккуратность   cat : 54 %
Аккуратность  deer : 18 %
Аккуратность   dog : 20 %
Аккуратность  frog : 58 %
Аккуратность horse : 74 %
Аккуратность  ship : 70 %
Аккуратность truck : 66 %


Тренировка на GPU

Также как тензор перемещается на GPU, также и сеть можно просто переместить на GPU.

Сначала зададим устройство как первое видимое CUDA устройство, если CUDA доступно:

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# Предполгаем, что у нас CUDA машина, 
# поэтому должно напечататься CUDA устройство:

print(device)

Вывод:

cuda:0

В следующих примерах предполагается, что устройство является CUDA устройством.

Тогда этот метод будет рекурсивно проходить по всем модулям и преобразовывать их параметры и буфферы в CUDA тензоры:

net.to(device)

Необходимо помнить, что требуется отправлять вводные данные и цели на каждом шагу также в GPU:

inputs, labels = inputs.to(device), labels.to(device)

На самом деле здесь не будет значительного увеличения скорости по сравнению с выполнением в CPU, потому что наша сеть очень мала.


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

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

PyTorch: Autograd, автоматическая дифференциация

PyTorch: нейронные сети

четверг, 27 декабря 2018 г.

PyTorch: нейронные сети

Нейронные сети могут быть построены с использованием пакета torch.nn.

torch.nn зависит от autograd в определении моделей и их дифференцировании. nn.Module содержит слои и метод forward(input), который возвращает output.

Например, посмотрите на эту сеть, которая классифицирует цифровые изображения:

Это простая сеть прямой связи. Она принимает вход, передает его через несколько слоев один за другим, а затем, наконец, возвращает вывод.

Типичная процедура обучения для нейронной сети следующая:

  1. Определение нейронной сети, которая имеет некоторые изучаемые параметры (или веса)
  2. Итерация по набору входных данных
  3. Обработка ввода через сеть
  4. Рассчет потери (насколько далеки результаты от правильных)
  5. Распространение градиентов обратно на параметры сети
  6. Обновление весов сети, как правило, используя простое правило обновления:
    weight = weight - learning_rate * gradient

Определение сети

Давайте определим сеть:

# -*- coding: utf-8 -*-
import torch
import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):

    def init(self):
        super(Net, self).init()
        # 1 канал ввода изображения, 
        # 6 каналов вывода, 
        # 5x5 квадратное сверточное ядро
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # аффинная операция: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # Максимальное объединение через (2, 2) окно
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # Если размер - это квадрат,
        # тогда вы можете задать только одно число
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, self.num_flat_features(x))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

    def num_flat_features(self, x):
        # все измерения, исключая измерение пакета
        size = x.size()[1:]  
        num_features = 1
        for s in size:
            num_features *= s
        return num_features


net = Net()
print(net)

Вывод:

Net(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=400, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)

Вам просто нужно определить функцию forward, а функция backward (где вычисляются градиенты) автоматически определена для вас, используя autograd. Вы можете использовать любую из операций Tensor в функции forward.

Изучаемые параметры модели возвращаются net.parameters()

params = list(net.parameters())
print(len(params))
print(params[0].size())  # conv1's .weight

Вывод:

10
torch.Size([6, 1, 5, 5])

Попробуем случайный 32x32 ввод (Ожидаемый размер ввода для этой net(LeNet) - 32x32.) Для использования этой сети на MNIST наборе следует изменить размер изображений в наборе на 32x32.

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)

Вывод:

tensor([[-0.0233,  0.0159, -0.0249,  0.1413,  
          0.0663,  0.0297, -0.0940, -0.0135,
          0.1003, -0.0559]], grad_fn=<AddmmBackward>)

Обнулим градиентные буферы всех параметров и backprops со случайными градиентами:

net.zero_grad()
out.backward(torch.randn(1, 10))

Примечание

torch.nn поддерживает только мини-пакеты. Весь torch.nn пакет поддерживает только вводы, которые являются мини-пакетами примеров, а не отдельным примером.

Например, nn.Conv2d будет принимать 4D тензор
nSamples x nChannels x Height x Width.

Если у вас есть отдельный пример, просто используйте input.unsqueeze(0) для того чтобы добавить поддельное измерение пакета.

Рассмотрим все классы, которые встрчались ранее.

  • torch.Tensor - многомерный массив с поддержкой autograd операций как backward(). Также содержит градиенты тензора.
  • nn.Module - модуль нейронной сети. Удобный способ инкапсуляции параметров с вспомогательными интрументами для их перемещения в GPU, экспорта, загрузки и т. д.
  • nn.Parameter - это разновидность Tensor, который автоматически регистрируется как параметр при назначении в качестве атрибута Module.
  • autograd.Function - реализует прямое и обратное определения (forward and backward) autograd операции. Каждая Tensor операция создает как минимум один Function узел, который подключается к функциям, которые создали Tensor и кодирует его историю.

На данный момент мы рассмотрели:

  • Определение нейронной сети
  • Обработка входных данных и обратный вызов

Еще осталось:

  • Вычисление потери
  • Обновление весов сети

Функция потери

Функция потери принимает (выходную, целевую) пару входных данных и вычисляет значение, определяющее, насколько далеко вывод от цели.

Существует несколько разных функций потери в пакете torch.nn. Простая потеря: nn.MSELoss, которая вычисляет среднеквадратичную ошибку между вводом и целью.

Например:

output = net(input)
target = torch.randn(10)  # случайный target, например
target = target.view(1, -1)  # сделаем его такой же формы как вывод
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)

Вывод:

tensor(1.3389, grad_fn=<MseLossBackward>)

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

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
      -> view -> linear -> relu -> linear -> relu -> linear
      -> MSELoss
      -> loss

Таким образом, когда мы вызываем loss.backward(), весь граф дифференцирован в соотвествии с потерей, и все тензоры в графе, которые имеют requires_grad=True будут иметь их .grad тензор накопленный с градиентом.

Для иллюстрации проследуем по нескольким шагам в обратном направлении:

print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU

Вывод:

<MseLossBackward object at 0x7fab77615278>
<AddmmBackward object at 0x7fab77615940>
<AccumulateGrad object at 0x7fab77615940>


Backprop (Обратное распространение)

Для обратного распространения ошибки все, что нам нужно сделать, это loss.backward(). Вы должны очистить существующие градиенты, иначе градиенты будут накапливается к существующим градиентам.

Теперь мы вызовем loss.backward() и посмотрим на смещение conv1 градиентов до и после backward.

# обнуляем градиентные буферы всех параметров
net.zero_grad()  

print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

Вывод:

conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([-0.0054,  0.0011,  0.0012,  0.0148, -0.0186,  0.0087])


Обновление весов

Самым простым правилом обновления, используемым на практике, является стохастический градиентный спуск (Stochastic Gradient Descent (SGD)):

weight = weight - learning_rate * gradient

Мы можем реализовать это с помощью простого Python кода:

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

Однако, когда вы используете нейронные сети, вы хотите использовать различные правила обновления, такие как SGD, Nesterov-SGD, Adam, RMSProp и др. Чтобы включить их, существует небольшой пакет: torch.optim, который реализует все эти методы. Использовать его очень просто:

import torch.optim as optim

# создайте ваш оптимизатор
optimizer = optim.SGD(net.parameters(), lr=0.01)

# в вашем тренировочном цикле:
optimizer.zero_grad()   # обнуляем градиентные буфферы
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()    # Выполняем обновление



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

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

PyTorch: Autograd, автоматическая дифференциация

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

среда, 26 декабря 2018 г.

PyTorch: Autograd, автоматическая дифференциация

Центральным для всех нейронных сетей в PyTorch является пакет autograd.

Пакет autograd обеспечивает автоматическое разграничение всех операций на тензорах. Это определяемый-по-исполнению (define-by-run) фреймворк, что означает, что ваш backprop (обратное распространение в нейронной сети) определяется тем, как выполняется ваш код, и что каждая отдельная итерация может отличаться.

Рассмотрим это в более простых терминах на нескольких примерах.

Тензор

torch.Tensor является центральным классом пакета. Если вы установите его атрибут .require_grad равным True, тогда он начнет отслеживать все операции над ним. Когда вы закончите вычисления, вы можете вызвать .backward() и получить все градиенты вычисленные автоматически. Градиент для этого тензора будет накапливается в атрибуте .grad.

Чтобы остановить отслеживание тензора в истории вычислений, вы можете вызвать .detach().

Чтобы предотвратить отслеживание истории (и использование памяти), вы также можете обернуть блок кода в with torch.no_grad():. Это может быть особенно полезно при оценке модели, потому что модель может иметь обучаемые параметры с require_grad = True, но для которых нам не нужны градиенты.

Есть еще один класс, который очень важен для autograd реализации - Function.

Tensor и Function связаны между собой и создают ациклический граф, который кодирует полную историю вычислений. Каждый тензор имеет .grad_fn атрибут, который ссылается на Function, которая создала Tensor (за исключением тензоров, созданных пользователем - их grad_fn = None).

Если вы хотите вычислить производные, вы можете вызвать .backward() на Tensor. Если Tensor является скаляром (то есть он содержит один элемент данных), вам не нужно указывать какие-либо аргументы для backward(), однако, если в нем больше элементов, вам нужно указать gradient аргумент, который является тензором подходящей формы.

import torch

Создаем тензор и задаем requires_grad=True, чтобы отслеживать вычисления с ним:

x = torch.ones(2, 2, requires_grad=True)
print(x)

Вывод:

tensor([[1., 1.],
        [1., 1.]], requires_grad=True)

Выполняем операцию на тензоре:

y = x + 2
print(y)

Вывод:

tensor([[3., 3.],
        [3., 3.]], grad_fn=<AddBackward0>)

y был создан как результат операции, поэтому он имеет grad_fn.

print(y.grad_fn)

Вывод:

<AddBackward0 object at 0x7fef6f982438>

Выполняем еще несколько операций на y:

z = y * y * 3
out = z.mean()

print(z, out)

Вывод:

tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) 
tensor(27., grad_fn=<MeanBackward0>)

.requires_grad_( ... ) изменяет существующий requires_grad флаг тензора на месте (in-place). input флаг равен False, если не задан:

a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)

Вывод:

False
True
<SumBackward0 object at 0x7fe1db427dd8>


Градиенты

Вызовем backprop теперь. Поскольку вывод содержит единственный скаляр, out.backward() эквивалентен ut.backward(torch.tensor(1.)).

out.backward()

Напечатаем градиенты d(out)/dx

print(x.grad)

Вывод:

tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

В общем говоря, torch.autograd - это механизм для вычисления Jacobian-vector произведения.

Рассмотрим пример Jacobian-vector произведения:

x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)

Вывод:

tensor([ 840.8677,  613.5138, -778.9942], grad_fn=<MulBackward0>)

Теперь в этом случае y больше не скаляр. torch.autograd не может вычислить полный Jacobian напрямую, но если мы просто хотим Jacobian-vector произведение, просто передаем вектор в backward как аргумент:

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)

Вывод:

tensor([1.0240e+02, 1.0240e+03, 1.0240e-01])

Вы можете также остановить autograd от отслежвания истории на тензорах с .requires_grad=True с помощью обертывания блока кода в with torch.no_grad():

print(x.requires_grad)
print((x  2).requires_grad)

with torch.no_grad():
    print((x  2).requires_grad)

Вывод:

True
True
False



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

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

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

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

понедельник, 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: классификация текста с отзывами к фильмам

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

пятница, 21 декабря 2018 г.

TensorFlow: embeddings (встраивания)

В этом посте представлена концепция встраиваний (embeddings), приведен простой пример как обучить встраивание (embedding) в TensorFlow, и объясняется, как просматривать встраивания с помощью TensorBoard Embedding Projector.

Встривание (embedding) - это отображение (mapping) дискретных объектов, таких как слова, на векторы действительных чисел. Например, 300-мерное встраивание для английских слов может включать в себя:

blue:(0.01359, 0.00075997, 0.24608, ..., -0.2524, 1.0048, 0.06259)
blues:(0.01396, 0.11887, -0.48963, ..., 0.033483, -0.10007, 0.1158)
orange:(-0.24776, -0.12359, 0.20986, ..., 0.079717, 0.23865, -0.014213)
oranges:(-0.35609, 0.21854, 0.080944, ..., -0.35413, 0.38511, -0.070976)

Отдельные измерения в этих векторах обычно не имеют внутреннего значения. Вместо этого, это общие шаблоны местоположения и расстояния между векторами, которые машинное обучение использует в своих интересах.

Встраивания важны для ввода данных в машинном обучении. Классификаторы и нейронные сети в целом работают на векторах действительных чисел. Они тренируются лучше всего на плотных векторах, где все значения способствуют определению объекта. Тем не менее, многие важные вводные данные в машинном обучении, такие как слова текста, не имеют естественного векторного представления. Функции встраивания являются стандартным и эффективный способ превратить такие дискретные вводные объекты в полезные непрерывные векторы.

Встраивания также полезны в качестве результатов в машинном обучении. Потому что применяя встраивания, сопоставляющие объекты с векторами, приложения могут использовать сходство в векторном пространстве (например, евклидово расстояние или угол между векторами) как надежную и гибкую меру сходства объектов. Одним из распространенных применений является поиск ближайшего соседа. Используя те же встраивания слов, что и выше, например, покажем три ближайших соседа для каждого слова и соответствующие углы:

blue:  (red, 47.6°), (yellow, 51.9°), (purple, 52.4°)
blues:  (jazz, 53.3°), (folk, 59.1°), (bluegrass, 60.6°)
orange:  (yellow, 53.5°), (colored, 58.0°), (bright, 59.9°)
oranges:  (apples, 45.3°), (lemons, 48.3°), (mangoes, 50.4°)

По этим результатам можно сделать вывод, что яблоки и апельсины более схожи (45.3° различия), чем лимоны и апельсины (48.3° различия).

Встраивания (enbeddings) в TensorFlow

Чтобы создать встраивание слов в TensorFlow, мы сначала разбиваем текст на слова, а затем назначаем целое число каждому слову в словаре. Давайте предположим, что это уже сделано, и что word_ids является вектором этих целых чисел. Например, предложение «I have a cat.». Можно разделить на [«I», «have», «a», «cat», «.»], и тогда соответствующий тензор word_ids будет иметь форму [5]и состоять из 5 целых чисел. Чтобы создать представление этих идентификаторов слов в векторах, нам нужно создать переменную встраивание и использовать tf.nn.embedding_lookup функцию следующим образом:

word_embeddings = tf.get_variable(“word_embeddings”,
    [vocabulary_size, embedding_size])
embedded_word_ids = tf.nn.embedding_lookup(word_embeddings, word_ids)

После этого тензор embedded_word_ids будет иметь форму [5, embedding_size] в нашем примере и содержать встраивания (embeddings) (плотные векторы)(dense vectors) для каждого из 5 слов. В конце обучения word_embeddings будет содержать встраивания для всех слов в словаре.

Встраивания можно обучать во многих типах сетей, с различными функциями потери и наборами данных. Например, кто-то один может использовать рекуррентную нейронную сеть для прогнозирования следующего слова, учитывая большой набор предложений, данный перед этим, или можно обучить две сети, чтобы выполнять многоязычный перевод.

Визуализация встраиваний

TensorBoard включает в себя Embedding Projector, инструмент, который позволяет интерактивно визуализировать встраивания. Этот инструмент может читать встраивания из вашей модели и визуализировать их в двух или трех измерениях.

Embedding Projector имеет три панели:

  • Панель данных (Data panel) в левом верхнем углу, где вы можете выбрать запуск, переменную встраивания и колонки данных (data columns) для задания цвета и меток точек.
  • Панель проекций (Projections panel) в левом нижнем углу, где вы можете выбрать тип проекции.
  • Панель инспектора (Inspector panel) справа, где вы можете искать конкретные точки и увидеть список ближайших соседей.

Проекции

Embedding Projector обеспечивает три способа уменьшения размерности набора данных.

  • t-SNE: нелинейный недетерминированный алгоритм (стохастическое вложение соседей с t-распределением), который пытается сохранить локальное соседство в данных, часто за счет искажения глобальной структуры. Вы можете выбрать, следует ли вычислять двух- или трехмерные проекции.

  • PCA: линейный детерминированный алгоритм (метод главных компонент), который пытается захватить как можно большую часть изменчивости данных в как можно меньшем количестве измерений. PCA имеет тенденцию выделять крупномасштабную структуру данных, но может искажать локальное соседство. Embedding Projector вычисляет 10 главных компонентов, из которых вы можете выбрать два или три для просмотра.

  • Пользовательский (Custom): линейная проекция на горизонтальную и вертикальную оси, которую вы указываете с помощью меток в данных. Вы определяете горизонтальную ось, например, предоставляя текстовые шаблоны для "Левый" и "Правый". Embedding Projector находит все точки, метка которых соответствует "Левый" шаблону и вычисляет центр тяжести этого множества; аналогично для "Правый". Линия, проходящая через эти два центроида определяет горизонтальную ось. Вертикальная ось, аналогично, вычисляется по центроидам для точек, совпадающих с "Вверх" и "Вниз" текстовыми шаблонами.


Исследование (Exploration)

Вы можете исследовать визуально с помощью масштабирования, поворота и панорамирования с помощью нажатия-и-перетаскивания (click-and-drag). Если навести указатель мыши на какую-либо из точек, будут показаны метаданные для этой точки. Вы также можете проверить ближайшие соседние подмножества. Нажатие на точку приводит к тому, что на правой панели отображается список ближайших соседей, а также расстояния до текущей точки. Ближайший сосед точки также выделяется в проекции.

Иногда полезно ограничить вид подмножеством точек и выполнить прогнозы только по этим пунктам. Для этого вы можете выбрать точки несколькими способами:

  • После нажатия на точку также выбираются ее ближайшие соседи.
  • После поиска выбираются точки, соответствующие запросу.
  • Включение выделения, нажатие на точку и перетаскивание определяют выделяемую сферу.

Затем нажмите "Isolate nnn points" кнопку в верхней части Inspector панели на правой стороне. На следующем рисунке показаны 101 точка, выбранные и готовые, чтобы пользователь щелкнул "Isolate 101 points":

Выбор ближайших соседей “важных” в наборе данных встраиваний слов.

Фильтрация с пользовательской проекцией может быть весьма удобной. Ниже мы отфильтровали 100 ближайших соседей “politics” и спроецировали их на “worst” - “best” вектор в виде оси х. Ось у случайна. В результате справа находятся “ideas”, “science”, “perspective”, “journalism”, а слева “crisis”, “violence” и “conflict”.

Чтобы поделиться своими результатами, вы можете использовать панель закладок в правом нижнем углу и сохранить текущее состояние (включая вычисленные координаты любой проекции) в виде небольшого файла. Затем в Projector можно указать на выбор одного или более из этих файлов, создавая панель ниже. Другие пользователи могут ходить по последовательности закладок.


Метаданные

Если вы работаете со встраиванием, возможно, вы захотите прикрепить метки/изображения к точкам данных. Вы можете сделать это, сгенерировав файл метаданных, содержащий метки для каждой точки, и нажав кнопку "Load data" в панели данных Embedding Projector.

Метаданные могут быть метками или изображениями, которые хранятся в отдельном файле. Для меток формат должен быть файлом TSV (символы табуляции показаны красным цветом), чья первая строка содержит заголовки столбцов (выделено жирным шрифтом) и последующие строки содержат значения метаданных. Например:

Word\tFrequency
Airplane\t345
Car\t241
...

Предполагается, что порядок строк в файле метаданных соответствует порядку векторов в переменной встраивания, кроме заголовка. Следовательно, (i+1)-я строка в файле метаданных соответствует i-й строке переменной встраивания. Если файл метаданных TSV содержит только один столбец, мы не ожидаем строку заголовка и предполагаем, что каждая строка является меткой встраивания. Мы включаем это исключение, потому что оно соответствует обычно используемому "vocab file" формату.

Чтобы использовать изображения в качестве метаданных, необходимо создать одно спрайт изображение, состоящее из маленьких миниатюр, по одному на каждый вектор встраивания. Спрайт должен хранить миниатюры в порядке строк: первая размещенная точка данных в левом верхнем углу и последняя точка данных в правом нижнем углу, хотя последняя строка не должна быть заполнена, как показано ниже:


Embedding мини-FAQ

"Встраивание (embedding)" - это действие или вещь? И то и другое. Люди говорят о встраивании слов в векторное пространство (действие) и о произведении встраиваний слов (вещи). Общим для обоих является понятие встраивания как отображения (mapping) дискретных объектов в векторах. Создание или применение этого отображения (mapping) - это действие, но само отображение (mapping) - это вещь.

Встраивания (embeddings) являются многомерными или низкоразмерными (high-dimensional или low-dimensional)? Это зависит от того с какой стороны расматривать. 300-мерное векторное пространство слов и фраз, например, часто называют низкоразмерным (и плотным) по сравнению с миллионами слов и фраз, которые оно может содержать. Но математически оно многомерное, поскольку показывает много свойств, что резко отличаются от того, что человек интуитивно знает о 2- и 3-мерных пространствах.

Является ли встраивание (embedding) тем же, что и слой встраивания (embedding layer)? Нет. слой встраивания (embedding layer) является частью нейронной сети, но встраивание (embedding) является более общей концепцией.


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

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

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

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

четверг, 20 декабря 2018 г.

TensorFlow: AutoGraph, примеры использования

AutoGraph помогает писать код сложных графов с использованием обычного Python. За кулисами AutoGraph автоматически преобразует ваш код в эквивалентный код графов TensorFlow. AutoGraph уже поддерживает большую часть языка Python, и этот охват продолжает расти.

Настройка

Импортируем TensorFlow, AutoGraph и вспомогательные модули:

from future import division, print_function, absolute_import

import tensorflow as tf
layers = tf.keras.layers
from tensorflow.contrib import autograph


import numpy as np
import matplotlib.pyplot as plt

Стремительное исполнение (eager execution) включено для демонстрационных целей, но AutoGraph работает в окружениях как стремительного, так и в графового исполнения:

tf.enable_eager_execution()

Примечание: Преобразованный код AutoGraph предназначен для запуска во время выполнения графа. Когда стремительное исполнение включено, используйте явные графы (как показано в этом примере) или tf.contrib.eager.defun.

Автоматическое преобразование Python потока управления

AutoGraph преобразует большую часть языка Python в эквивалентный код построения графов TensorFlow.

Примечание: В реальных приложениях пакетирование важно для повышения производительности. Лучший код для преобразования в AutoGraph - это код, в котором поток управления определяется на уровне batch. Если вы принимаете решения на индивидуальном уровне example, вы должны индексировать и группировать примеры, чтобы поддерживать производительность при применении логики потока управления.

AutoGraph преобразует функцию, например:

def square_if_positive(x):
  if x > 0:
    x = x * x
  else:
    x = 0.0
  return x

В функцию, которая использует построение графа:

print(autograph.to_code(square_if_positive))

from future import print_function
import tensorflow as tf

def tf__square_if_positive(x):
  try:
    with ag__.function_scope('square_if_positive'):

      def if_true():
        with ag__.function_scope('if_true'):
          x_1, = x,
          x_1 = x_1 * x_1
          return x_1,

      def if_false():
        with ag__.function_scope('if_false'):
          x_2, = x,
          x_2 = 0.0
          return x_2,
      x = ag__.utils.run_cond(tf.greater(x, 0), if_true, if_false)
      return x
  except:
    ag__.rewrite_graph_construction_error(ag_source_map__)



tf__square_if_positive.autograph_info__ = {}

Код написанный для стремительного исполнения может выполняться в tf.Graph с теми же результатами, но с преимуществами графового исполнения:

print('Eager results: %2.2f, %2.2f' % 
(square_if_positive(tf.constant(9.0)), 
 square_if_positive(tf.constant(-9.0))))

Eager results: 81.00, 0.00

Генерируем графовую версию и выполняем ее:

# -*- coding: utf-8 -*-

tf_square_if_positive = autograph.to_graph(square_if_positive)

with tf.Graph().as_default():  
  # Работает как обычная операция: принимает тензоры, 
  # возвращает тензоры.
  # Вы можете проверять граф, 
  # используя tf.get_default_graph().as_graph_def()
  g_out1 = tf_square_if_positive(tf.constant( 9.0))
  g_out2 = tf_square_if_positive(tf.constant(-9.0))
  with tf.Session() as sess:
    print('Graph results: %2.2f, %2.2f\n' % 
          (sess.run(g_out1), sess.run(g_out2)))

Graph results: 81.00, 0.00

AutoGraph поддерживает общие Python выражения, такие как while, for, if, break, и return, с поддержкой вложенности. Сравните эту функцию со сложной графовой версией, показанной в следующих блоках кода:

# Продолжаем в цикле
def sum_even(items):
  s = 0
  for c in items:
    if c % 2 > 0:
      continue
    s += c
  return s

print('Eager result: %d' % sum_even(tf.constant([10,12,15,20])))

tf_sum_even = autograph.to_graph(sum_even)

with tf.Graph().as_default(), tf.Session() as sess:
    print('Graph result: %d\n\n' % 
          sess.run(tf_sum_even(tf.constant([10,12,15,20]))))

Eager result: 42
Graph result: 42

print(autograph.to_code(sum_even))

from future import print_function
import tensorflow as tf

def tf__sum_even(items):
  try:
    with ag__.function_scope('sum_even'):
      s = 0

      def extra_test(s_2):
        with ag__.function_scope('extra_test'):
          return True

      def loop_body(loop_vars, s_2):
        with ag__.function_scope('loop_body'):
          c = loop_vars
          continue_ = tf.constant(False)

          def if_true():
            with ag__.function_scope('if_true'):
              continue__1, = continue_,
              continue__1 = tf.constant(True)
              return continue__1,

          def if_false():
            with ag__.function_scope('if_false'):
              return continue_,
          continue_ = ag__.utils.run_cond(tf.greater(c % 2, 0), 
                                          if_true, if_false)

          def if_true_1():
            with ag__.function_scope('if_true_1'):
              s_1, = s_2,
              s_1 += c
              return s_1,

          def if_false_1():
            with ag__.function_scope('if_false_1'):
              return s_2,
          s_2 = ag__.utils.run_cond(tf.logical_not(continue_), 
                                    if_true_1, if_false_1)
          return s_2,
      s = ag__.for_stmt(items, extra_test, loop_body, (s,))
      return s
  except:
    ag__.rewrite_graph_construction_error(ag_source_map__)



tf__sum_even.autograph_info__ = {}


Декоратор

Если вам не нужен простой доступ к изначальной Python функции, используйте convert декоратор:

@autograph.convert()
def fizzbuzz(i, n):
  while i < n:
    msg = ''
    if i % 3 == 0:
      msg += 'Fizz'
    if i % 5 == 0:
      msg += 'Buzz'
    if msg == '':
      msg = tf.as_string(i)
    print(msg)
    i += 1
  return i

with tf.Graph().as_default():
  final_i = fizzbuzz(tf.constant(10), tf.constant(16))
  # Работает как обычная операция: принимает тензоры, 
  # возвращает тензоры.
  # Вы можете проверять граф, используя 
  # tf.get_default_graph().as_graph_def()
  with tf.Session() as sess:
    sess.run(final_i)

Buzz
11
Fizz
13
14
FizzBuzz


Примеры

Продемонстрируем некоторые полезные свойства языка Python

Assert

AutoGraph автоматически преобразовывает Python assert выражение в эквивалентный tf.Assert код:

@autograph.convert()
def inverse(x):
  assert x != 0.0, 'Do not pass zero!'
  return 1.0 / x

with tf.Graph().as_default(), tf.Session() as sess:
  try:
    print(sess.run(inverse(tf.constant(0.0))))
  except tf.errors.InvalidArgumentError as e:
    print('Got error message:\n    %s' % e.message)

Got error message:
    assertion failed: [Do not pass zero!]
     [[node inverse/Assert/Assert (defined at /tmp/tmpzsz37apn.py:6)  
     = Assert[T=[DT_STRING], summarize=3, 
     _device="/job:localhost/replica:0/task:0/device:CPU:0"]
     (inverse/NotEqual, inverse/Assert/Assert/data_0)]]

Print

Используйте Python print функцию в графе:

@autograph.convert()
def count(n):
  i=0
  while i < n:
    print(i)
    i += 1
  return n
    
with tf.Graph().as_default(), tf.Session() as sess:
    sess.run(count(tf.constant(5)))

Lists (списки)

Добавляйте в списки (lists) в циклах (тензор list операции автоматически созданы):

@autograph.convert()
def arange(n):
  z = []
  # Мы запрашиваем dtype элемента списка
  autograph.set_element_type(z, tf.int32)
  
  for i in tf.range(n):
    z.append(i)

  # когда мы закончили со списком
  # складываем его в стек (как np.stack)
  return autograph.stack(z) 


with tf.Graph().as_default(), tf.Session() as sess:
    sess.run(arange(tf.constant(10)))

Вложенный поток контроля

@autograph.convert()
def nearest_odd_square(x):
  if x > 0:
    x = x * x
    if x % 2 == 0:
      x = x + 1
  return x

with tf.Graph().as_default():  
  with tf.Session() as sess:
    print(sess.run(nearest_odd_square(tf.constant(4))))
    print(sess.run(nearest_odd_square(tf.constant(5))))
    print(sess.run(nearest_odd_square(tf.constant(6))))

17
25
37

While цикл

@autograph.convert()
def square_until_stop(x, y):
  while x < y:
    x = x * x
  return x
    
with tf.Graph().as_default():  
  with tf.Session() as sess:
    print(sess.run(square_until_stop(tf.constant(4), tf.constant(100))))

256

For цикл

@autograph.convert()
def squares(nums):

  result = []
  autograph.set_element_type(result, tf.int64)

  for num in nums: 
    result.append(num * num)
    
  return autograph.stack(result)
    
with tf.Graph().as_default():  
  with tf.Session() as sess:
    print(sess.run(squares(tf.constant(np.arange(10)))))

[ 0  1  4  9 16 25 36 49 64 81]

Break

@autograph.convert()
def argwhere_cumsum(x, threshold):
  current_sum = 0.0
  idx = 0
  for i in tf.range(len(x)):
    idx = i
    if current_sum >= threshold:
      break
    current_sum += x[i]
  return idx

N = 10
with tf.Graph().as_default():  
  with tf.Session() as sess:
    idx = argwhere_cumsum(tf.ones(N), tf.constant(float(N/2)))
    print(sess.run(idx))

5


Взаимодействие с tf.Keras

Теперь когда вы увидели основы, давайте построим некоторые компоненты модели с помощью autograph.

Довольно просто интегрировать autograph с tf.keras.

Функции без состояния

Для функций без состояния, таких как collatz показанной ниже, простейший путь включить их в keras модель - обернуть их как слой, используя tf.keras.layers.Lambda.

import numpy as np

@autograph.convert()
def collatz(x):
  x = tf.reshape(x,())
  assert x > 0
  n = tf.convert_to_tensor((0,)) 
  while not tf.equal(x, 1):
    n += 1
    if tf.equal(x%2, 0):
      x = x // 2
    else:
      x = 3 * x + 1
      
  return n

with tf.Graph().as_default():
  model = tf.keras.Sequential([
    tf.keras.layers.Lambda(collatz, input_shape=(1,), output_shape=())
  ])
  
result = model.predict(np.array([6171]))
result

array([261], dtype=int32)

Пользовательские слои и модели

Простейший путь использовать AutoGraph с Keras слоями и моделями - это @autograph.convert() call метод.

Вот простой пример:

# K используется для проверки в каком мы режиме 
# - в тренировочном или тестовом.
K = tf.keras.backend

class StochasticNetworkDepth(tf.keras.Sequential):
  def init(self, pfirst=1.0, plast=0.5, *args,**kwargs):
    self.pfirst = pfirst
    self.plast = plast
    super().init(*args,**kwargs)
        
  def build(self,input_shape):
    super().build(input_shape.as_list())
    self.depth = len(self.layers)
    self.plims = np.linspace(self.pfirst, self.plast, 
                                          self.depth + 1)[:-1]
    
  @autograph.convert()
  def call(self, inputs):
    training = tf.cast(K.learning_phase(), dtype=bool)  
    if not training: 
      count = self.depth
      return super(StochasticNetworkDepth, self).call(inputs), count
    
    p = tf.random_uniform((self.depth,))
    
    keeps = (p <= self.plims)
    x = inputs
    
    count = tf.reduce_sum(tf.cast(keeps, tf.int32))
    for i in range(self.depth):
      if keeps[i]:
        x = self.layers[i](x)
      
    # возвращаем вывод последнего слоя и количество выполненных слоев
    return x, count

Попробуем модель на данных с формой как в MNIST наборе:

train_batch = np.random.randn(64, 28, 28, 1).astype(np.float32)

Построим простой стек conv слоев, в созданной нами модели:

with tf.Graph().as_default() as g:
  model = StochasticNetworkDepth(
        pfirst=1.0, plast=0.5)

  for n in range(20):
    model.add(
          layers.Conv2D(filters=16, activation=tf.nn.relu,
                        kernel_size=(3, 3), padding='same'))

  model.build(tf.TensorShape((None, None, None, 1)))
  
  init = tf.global_variables_initializer()

Теперь протестируем модель, чтобы убедиться, что она ведет себя так как ожидается в train и test режимах:

# Используем явную сессию здесь, таким образом мы можем установить
# train/test switch, и проверить количество слоев, 
# возвращаенное call методом
with tf.Session(graph=g) as sess:
  init.run()
 
  for phase, name in enumerate(['test','train']):
    K.set_learning_phase(phase)
    result, count = model(tf.convert_to_tensor(train_batch, 
                                               dtype=tf.float32))

    result1, count1 = sess.run((result, count))
    result2, count2 = sess.run((result, count))

    delta = (result1 - result2)
    print(name, "sum abs delta: ", abs(delta).mean())
    print("    layers 1st call: ", count1)
    print("    layers 2nd call: ", count2)
    print()

test sum abs delta:  0.0
    layers 1st call:  20
    layers 2nd call:  20

train sum abs delta:  0.0010305465
    layers 1st call:  14
    layers 2nd call:  17


Более сложный пример: внутриграфовый тренировочный цикл

В предыдущих примерах показано как AutoGraph может быть использован внутри Keras слоев и моделей. Keras модели могут также быть использованы в AutoGraph коде.

Ввиду того что написание потока контроля в AutoGraph является простым, то выполнение тренировочного цикла в TensorFlow графе должно быть также простым.

Этот пример показывает как натренировать простую Keras модель для MNIST с целым тренировочным процессом - загрузкой пакетов, вычислением градиентов, обновлением параметров, вычислением валидационной аккуратности, и повторением до схождения - все выполнено внутри графа.

Загрузка данных

(train_images, train_labels), (test_images, test_labels) = \
                                    tf.keras.datasets.mnist.load_data()

Downloading data from 
https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

Определение модели

def mlp_model(input_shape):
  model = tf.keras.Sequential((
      tf.keras.layers.Dense(100, activation='relu', 
                            input_shape=input_shape),
      tf.keras.layers.Dense(100, activation='relu'),
      tf.keras.layers.Dense(10, activation='softmax')))
  model.build()
  return model


def predict(m, x, y):
  y_p = m(tf.reshape(x, (-1, 28 * 28)))
  losses = tf.keras.losses.categorical_crossentropy(y, y_p)
  l = tf.reduce_mean(losses)
  accuracies = tf.keras.metrics.categorical_accuracy(y, y_p)
  accuracy = tf.reduce_mean(accuracies)
  return l, accuracy


def fit(m, x, y, opt):
  l, accuracy = predict(m, x, y)
  # Autograph автоматически добавляет необходимые 
  # tf.control_dependencies здесь.
  # (Без них ничего не зависит от opt.minimize, 
  # поэтому ничего не выполняется.)
  # Это выполняется схожим образом с кодом с нетерпеливого исполнения.
  opt.minimize(l)
  return l, accuracy


def setup_mnist_data(is_training, batch_size):
  if is_training:
    ds = tf.data.Dataset.from_tensor_slices((train_images, 
                                             train_labels))
    ds = ds.shuffle(batch_size * 10)
  else:
    ds = tf.data.Dataset.from_tensor_slices((test_images, test_labels))

  ds = ds.repeat()
  ds = ds.batch(batch_size)
  return ds


def get_next_batch(ds):
  itr = ds.make_one_shot_iterator()
  image, label = itr.get_next()
  x = tf.to_float(image) / 255.0
  y = tf.one_hot(tf.squeeze(label), 10)
  return x, y 

Определение тренировочного цикла

# Используйте recursive = True 
# для того чтобы рекурсивно преобразовать функции вызванные этой.
@autograph.convert(recursive=True)
def train(train_ds, test_ds, hp):
  m = mlp_model((28 * 28,))
  opt = tf.train.AdamOptimizer(hp.learning_rate)
  
  # Мы сохраним наши потери в список. Для того чтобы AutoGraph
  # преобразовывал эти списки в их графовые эквиваленты,
  # нам необходимо определить тип элементов листов.
  train_losses = []
  autograph.set_element_type(train_losses, tf.float32)
  test_losses = []
  autograph.set_element_type(test_losses, tf.float32)
  train_accuracies = []
  autograph.set_element_type(train_accuracies, tf.float32)
  test_accuracies = []
  autograph.set_element_type(test_accuracies, tf.float32)
  
  # Этот тренировочный цикл целиком будет выполняться внутри графа.
  i = tf.constant(0)
  while i < hp.max_steps:
    train_x, train_y = get_next_batch(train_ds)
    test_x, test_y = get_next_batch(test_ds)
    step_train_loss, step_train_accuracy = fit(m, train_x, train_y, opt)
    step_test_loss, step_test_accuracy = predict(m, test_x, test_y)
    if i % (hp.max_steps // 10) == 0:
      print('Step', i, 'train loss:', step_train_loss, 'test loss:',
            step_test_loss, 'train accuracy:', step_train_accuracy,
            'test accuracy:', step_test_accuracy)
    train_losses.append(step_train_loss)
    test_losses.append(step_test_loss)
    train_accuracies.append(step_train_accuracy)
    test_accuracies.append(step_test_accuracy)
    i += 1
  
  # Мы записали значения наших потерь и аккуратностей
  # в список в графе с помощью AutoGraph.
  # Для того чтобы вернуть значения как Tensor, 
  # нам необходимо сложить их друг на друга перед их возвращением.
  return (autograph.stack(train_losses), 
          autograph.stack(test_losses),  
          autograph.stack(train_accuracies), 
          autograph.stack(test_accuracies))

Теперь построим граф и выполним тренировочный цикл:

with tf.Graph().as_default() as g:
  hp = tf.contrib.training.HParams(
      learning_rate=0.005,
      max_steps=500,
  )
  train_ds = setup_mnist_data(True, 50)
  test_ds = setup_mnist_data(False, 1000)
  (train_losses, test_losses, train_accuracies,
   test_accuracies) = train(train_ds, test_ds, hp)

  init = tf.global_variables_initializer()
  
with tf.Session(graph=g) as sess:
  sess.run(init)
  (train_losses, test_losses, train_accuracies,
   test_accuracies) = sess.run([train_losses, 
                                test_losses, 
                                train_accuracies,
                                test_accuracies])
  
plt.title('MNIST train/test losses')
plt.plot(train_losses, label='train loss')
plt.plot(test_losses, label='test loss')
plt.legend()
plt.xlabel('Training step')
plt.ylabel('Loss')
plt.show()
plt.title('MNIST train/test accuracies')
plt.plot(train_accuracies, label='train accuracy')
plt.plot(test_accuracies, label='test accuracy')
plt.legend(loc='lower right')
plt.xlabel('Training step')
plt.ylabel('Accuracy')
plt.show()

Step 0 train loss: 2.39437 test loss: 2.3646827 
train accuracy: 0.02 test accuracy: 0.036
Step 50 train loss: 0.32149887 test loss: 0.5104491 
train accuracy: 0.9 test accuracy: 0.839
Step 100 train loss: 0.33964032 test loss: 0.32525328 
train accuracy: 0.88 test accuracy: 0.898
Step 150 train loss: 0.27726996 test loss: 0.37781104 
train accuracy: 0.9 test accuracy: 0.867
Step 200 train loss: 0.18788506 test loss: 0.274848 
train accuracy: 0.94 test accuracy: 0.921
Step 250 train loss: 0.35899135 test loss: 0.26668844 
train accuracy: 0.86 test accuracy: 0.912
Step 300 train loss: 0.11612513 test loss: 0.24186224 
train accuracy: 0.98 test accuracy: 0.916
Step 350 train loss: 0.18267077 test loss: 0.25310495 
train accuracy: 0.96 test accuracy: 0.915
Step 400 train loss: 0.17388473 test loss: 0.18192373 
train accuracy: 0.96 test accuracy: 0.939
Step 450 train loss: 0.12562008 test loss: 0.19091153 
train accuracy: 0.96 test accuracy: 0.94


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

TensorFlow: стремительное исполнение (eager execution), базовое использование

TensorFlow: стремительное исполнение (eager execution), продвинутое использование

TensorFlow: Keras, базовое использование

среда, 19 декабря 2018 г.

TensorFlow Core: SavedModel CLI (интерфейс командной строки)

CLI для проверки и выполнения SavedModel

Вы можете использовать интерфейс командной строки SavedModel (CLI) для проверки и выполнения SavedModel. Например, вы можете использовать CLI для проверки SignatureDef модели. CLI позволяет быстро подтвердить, что ввод соотвествует Tensor dtype и форма соответствует требуемой в модели. Более того, если вы хотите протестировать свою модель, вы можете использовать CLI для проверки работоспособности: передавать входные данные в различных форматах (например, Python выражения), а затем извлекать вывод.

Установка интерфейса командной строки (CLI) для SavedModel

Вы можете установить сам TensorFlow одним из следующих способов:

  • Устанавливая предварительно собранный бинарный файл TensorFlow.
  • Путем сборки TensorFlow из исходного кода.

Если вы установили TensorFlow через предварительно созданный бинарный файл TensorFlow, тогда CLI SavedModel уже установлен в вашей системе по пути bin\save_model_cli.

Если вы собрали TensorFlow из исходного кода, вы должны выполнить следующую дополнительную команду для сборки save_model_cli:

$ bazel build tensorflow/python/tools:saved_model_cli


Обзор команд

CLI SavedModel поддерживает следующие две команды на MetaGraphDef в SavedModel:

  • show, который показывает вычисление для MetaGraphDef в SavedModel.
  • run, который выполняет вычисления для MetaGraphDef.

show команда

SavedModel содержит один или несколько MetaGraphDef, идентифицированных по их наборам тегов. Перед началом обслуживания модели, у вас может возникнуть вопрос, какие SignatureDef есть в каждой модели и каковы их вводы и выводы. Команда show позволяет вам изучить содержимое SavedModel в иерархическом порядке. Вот синтаксис:

usage: saved_model_cli show [-h] --dir DIR [--all]
[--tag_set TAG_SET] [--signature_def SIGNATURE_DEF_KEY]

Например, следующая команда показывает все доступные наборы тегов MetaGraphDef в SavedModel:

$ saved_model_cli show --dir /tmp/saved_model_dir
The given SavedModel contains the following tag-sets:
serve
serve, gpu

Следующая команда показывает все доступные ключи SignatureDef в MetaGraphDef:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve
The given SavedModel MetaGraphDef contains SignatureDefs with the
following keys:
SignatureDef key: "classify_x2_to_y3"
SignatureDef key: "classify_x_to_y"
SignatureDef key: "regress_x2_to_y3"
SignatureDef key: "regress_x_to_y"
SignatureDef key: "regress_x_to_y2"
SignatureDef key: "serving_default"

Если MetaGraphDef имеет несколько тегов в наборе тегов, необходимо указать все теги, где каждый тег разделен запятой. Например:

$ saved_model_cli show --dir /tmp/saved_model_dir --tag_set serve,gpu

Чтобы показать все вводы и выводы TensorInfo для определенного SignatureDef, введите ключ SignatureDef для параметра signature_def. Это очень полезно, когда вы хотите узнать значение ключа тензора, dtype и форму вводных тензоров для выполнение графа вычислений позже. Например:

$ saved_model_cli show --dir \
/tmp/saved_model_dir --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
  inputs['x'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: x:0
The given SavedModel SignatureDef contains the following output(s):
  outputs['y'] tensor_info:
      dtype: DT_FLOAT
      shape: (-1, 1)
      name: y:0
Method name is: tensorflow/serving/predict

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

$ saved_model_cli show --dir /tmp/saved_model_dir --all
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['classify_x2_to_y3']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x2:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y3:0
  Method name is: tensorflow/serving/classify

...

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['x'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: x:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['y'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: y:0
  Method name is: tensorflow/serving/predict


run команда

Вызовите команду run для запуска вычисления графа, передав вводы и затем отображая (и, опционально, сохраняя) выводы. Вот синтаксис:

usage: saved_model_cli run [-h] --dir DIR --tag_set TAG_SET 
                           --signature_def SIGNATURE_DEF_KEY 
                           [--inputs INPUTS]
                           [--input_exprs INPUT_EXPRS]
                           [--input_examples INPUT_EXAMPLES] 
                           [--outdir OUTDIR]
                           [--overwrite] [--tf_debug]

Команда run предоставляет следующие три способа передачи вводных данных в модель:

  • --inputs опция позволяет вам передавать numpy ndarray в файлах.
  • --input_exprs опция позволяет передавать Python выражения.
  • --input_examples опция позволяет передать tf.train.Example.

--inputs

Чтобы передать вводные данные в файлах, укажите параметр --inputs, который принимает следующий общий формат:

--inputs <INPUTS>

где <INPUTS> имеет один из следующих форматов:

  • <input_key>=<filename>
  • <input_key>=<filename>[<variable_name>]

Вы можете передать несколько INPUTS. Если вы передаете несколько вводов, используйте точку с запятой, чтобы отделить каждый из INPUTS.

saved_model_cli использует numpy.load для загрузки filename. filename может иметь любой из следующих форматов:

  • .npy
  • .npz
  • pickle формат

Файл .npy всегда содержит numpy ndarray. Поэтому при загрузке из файла .npy, контент будет напрямую назначен на указанный вводный тензор. Если вы укажете variable_name с этим файлом .npy, variable_name будет проигнорировано, и будет выдано предупреждение.

При загрузке из файла .npz(zip) вы можете при желании указать variable_name, чтобы определить переменную в zip-файле для загрузки ключа вводного тензора. Если вы не укажете variable_name, SavedModel CLI проверит, что в zip-файл включен только один файл, и загрузит его для указанного ключа вводного тензора.

При загрузке из pickle файла, если variable_name не указано в квадратных скобках, все, что находится внутри pickle файла, будет передано в указанный ключ тензора ввода. В противном случае SavedModel CLI примет словарь хранимый в pickle файле и значение, соответствующее variable_name будет использовано.

--input_exprs

Чтобы передать вводные данные через Python выражения, укажите параметр --input_exprs. Это может быть полезно, когда у вас нет файлов данных, лежащих поблизости, но вы хотите проверить модель с некоторыми простыми вводными данными, которые соответствуют dtype и форме SignatureDef модели. Например:

<input_key>=[[1],[2],[3]]

В дополнение к Python выражениям вы также можете передавать numpy функции. Например:

<input_key>=np.ones((32,32,3))

(обратите внимание, что модуль numpy уже доступен как np.)

--input_examples

Для передачи tf.train.Example в качестве вводных данных, укажите параметр --input_examples. Для каждого ключа ввода, он принимает список словарей, где каждый словарь является экземпляром tf.train.Example. Ключи словаря являются свойствами, а значения - это списки значений для каждого свойства. Например:

<input_key>=[{"age":[22,24],"education":["BS","MS"]}]

Сохранение вывода

По умолчанию SavedModel CLI записывает вывод в стандартный вывод. Если каталог передан параметру --outdir, выходные данные будут сохранены в виде файлов npy с именами ключей выходных тензоров из заданного каталога.

Используйте --overwrite, чтобы перезаписать существующие выходные файлы.

Интеграция отладчика TensorFlow (tfdbg)

Если установлена ​​опция --tf_debug, SavedModel CLI будет использовать TensorFlow Debugger (tfdbg) для просмотра промежуточных тензоров и времени выполнения графов или подграфов во время работы SavedModel.

Полные примеры run

Дано:

  • Ваша модель просто добавляет x1 и x2, чтобы получить выходные данные y.
  • Все тензоры в модели имеют форму (-1, 1).
  • У вас есть два файла npy:
    • /tmp/my_data1.npy, который содержит numpy ndarray [[1], [2], [3]].
    • /tmp/my_data2.npy, который содержит другой numpy ndarray [[0.5], [0.5], [0.5]].

Чтобы запустить эти два npy файла через модель для получения вывода y, выполните следующую команду:

$ saved_model_cli run --dir /tmp/saved_model_dir --tag_set serve \
--signature_def x1_x2_to_y \
--inputs 'x1=/tmp/my_data1.npy;x2=/tmp/my_data2.npy' \
--outdir /tmp/out
Result for output key y:
[[ 1.5]
 [ 2.5]
 [ 3.5]]

Давайте немного поменяем предыдущий пример. В этот раз вместо двух .npy файлов у вас есть .npz файл и pickle файл. Кроме того, вы хотите перезаписать любой существующий файл вывода. Вот команда:

$ saved_model_cli run --dir /tmp/saved_model_dir --tag_set serve \
--signature_def x1_x2_to_y \
--inputs 'x1=/tmp/my_data1.npz[x];x2=/tmp/my_data2.pkl' \
--outdir /tmp/out \
--overwrite
Result for output key y:
[[ 1.5]
 [ 2.5]
 [ 3.5]]

Вы можете задать python выражение вместо файла ввода. Например, следующая команда заменяет ввод x2 Python выражением:

$ saved_model_cli run --dir /tmp/saved_model_dir --tag_set serve \
--signature_def x1_x2_to_y --inputs x1=/tmp/my_data1.npz[x] \
--input_exprs 'x2=np.ones((3,1))'
Result for output key y:
[[ 2]
 [ 3]
 [ 4]]

Для того чтобы выполнить модель с включенным TensorFlow Debugger используйте следующую команду:

$ saved_model_cli run --dir /tmp/saved_model_dir --tag_set serve \
--signature_def serving_default --inputs x=/tmp/data.npz[x] --tf_debug


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

Основы TensorFlow Core

TensorFlow Core: тензоры (tensors)

TensorFlow Core: сохранение и восстановление моделей