Основи 🔥PyTorch

Технології комп’ютерного зору

Ігор Мірошниченко

КНУ імені Тараса Шевченка, ФІТ

Вступ

Що таке PyTorch?

  • Один з найбільш популярних фреймворків для глибокого навчання, розроблений Facebook (Meta).
  • User-friendly код для глибокого навчання на Python (з можливістю запуску на GPU/GPUs)
  • Доступ до багатьох готових моделей глибокого навчання (Torch Hub/torchvision.models)
  • Повний стек: попередня обробка даних, моделювання даних, розгортання моделі у вашій програмі/хмарі
  • Спочатку розроблений і використовуваний внутрішньо компанією Facebook/Meta (зараз є відкритим кодом і використовується такими компаніями, як Tesla, Microsoft, OpenAI)

Чому PyTorch?

Що таке GPU/TPU?

GPU (Graphics Processing Unit)

TPU (Tensor Processing Unit)

Що таке тензор? 1/2

Що таке тензор? 2/2

Вступ до 🔥PyTorch

Встановлення 🔥PyTorch

  • Відвідайте сайт PyTorch: Get Started для отримання інструкцій щодо встановлення.
  • Виберіть вашу операційну систему, пакетний менеджер та версію CUDA (якщо ви плануєте використовувати GPU).
  • Скопіюйте команду встановлення та виконайте її у вашому терміналі.
pip3 install torch torchvision --index-url https://download.pytorch.org/whl/cu126

Імпорт PyTorch

import torch

print(torch.__version__)
print(torch.cuda.is_available())
2.0.1+cu118
True

Створення тензора: скаляр

Скаляр — це одне число, а в термінології тензорів — це тензор нульового рангу.

scalar = torch.tensor(7)
print(scalar)
print(f'Ранг тензора: {scalar.ndim}')
print(f'Значення тензора: {scalar.item()}')
tensor(7, device='cuda:0')
Ранг тензора: 0
Значення тензора: 7

Створення тензора: вектор

Вектор — це послідовність чисел, а в термінології тензорів — це тензор першого рангу.

vector = torch.tensor([1, 2, 3])
print(vector)
print(f'Ранг тензора: {vector.ndim}')
print(f'Розмір тензора: {vector.shape}')
tensor([1, 2, 3], device='cuda:0')
Ранг тензора: 1
Розмір тензора: torch.Size([3])

Ранг тензора можна визначити за кількістю дужок [

Створення тензора: матриця

Матриця — це таблиця чисел, а в термінології тензорів — це тензор другого рангу.

matrix = torch.tensor([[1, 2, 3],
                       [4, 5, 6]])
print(matrix)
print(f'Ранг тензора: {matrix.ndim}')
print(f'Розмір тензора: {matrix.shape}')
tensor([[1, 2, 3],
        [4, 5, 6]], device='cuda:0')
Ранг тензора: 2
Розмір тензора: torch.Size([2, 3])

Створення тензора: тензор 😅

tensor = torch.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(tensor)
print(f'Ранг тензора: {tensor.ndim}')
print(f'Розмір тензора: {tensor.shape}')
tensor([[[1, 2],
         [3, 4]],

        [[5, 6],
         [7, 8]]], device='cuda:0')
Ранг тензора: 3
Розмір тензора: torch.Size([2, 2, 2])

Генератор випадкових величин

Тензори можуть бути створені з випадковими значеннями за допомогою функції torch.rand (рівномірний розподіл) або torch.randn (нормальний розподіл) з параметром size.

torch.manual_seed(73)
random_tensor = torch.rand(2, 3, 2)
print(random_tensor)
tensor([[[0.1272, 0.6051],
         [0.3567, 0.0610],
         [0.3286, 0.2315]],

        [[0.3107, 0.8532],
         [0.1143, 0.1669],
         [0.5057, 0.3978]]], device='cuda:0')

Нульовий та одиничний тензор

zeros = torch.zeros(size=(3, 4))
zeros, zeros.dtype
(tensor([[0., 0., 0., 0.],
         [0., 0., 0., 0.],
         [0., 0., 0., 0.]], device='cuda:0'),
 torch.float32)


ones = torch.ones(size=(3, 4))
ones, ones.dtype
(tensor([[1., 1., 1., 1.],
         [1., 1., 1., 1.],
         [1., 1., 1., 1.]], device='cuda:0'),
 torch.float32)

Послідовність

torch.arange(start, end, step)

range_tensor = torch.arange(0, 10, 2)
range_tensor, range_tensor.dtype
(tensor([0, 2, 4, 6, 8], device='cuda:0'), torch.int64)

torch.range() є застарілим варіантом

Іноді може знадобитися один тензор певного типу з такою самою формою, як інший тензор.

torch.zeros_like(range_tensor), torch.ones_like(range_tensor)
(tensor([0, 0, 0, 0, 0], device='cuda:0'),
 tensor([1, 1, 1, 1, 1], device='cuda:0'))

Типи даних

У PyTorch доступно багато різних типів даних тензора.

Найпопулярніші:

  • torch.float32: за замовчуванням для чисел з плаваючою комою
  • torch.float64: подвоєна точність
  • torch.float16: напівточність
  • torch.int64: цілі числа
  • torch.uint8: беззнакові цілі числа
  • dtype: тип даних тензора
  • device: пристрій, на якому розміщено тензор
  • requires_grad: чи потрібно відстежувати градієнти для цього тензора
float_32_tensor = torch.tensor([3.0, 6.0, 9.0],
                               dtype=None,
                               device="cuda" if torch.cuda.is_available() else "cpu",
                               requires_grad=False)

float_32_tensor.shape, float_32_tensor.dtype, float_32_tensor.device
(torch.Size([3]), torch.float32, device(type='cuda', index=0))

Інформація про тензор

  • shape: форма тензора
  • dtype: тип даних тензора
  • device: пристрій, на якому розміщено тензор
  • requires_grad: чи потрібно відстежувати градієнти для цього тензора
torch.manual_seed(73)
torch.set_default_device("cuda" if torch.cuda.is_available() else "cpu")

some_tensor = torch.rand(3, 4)

print(some_tensor)
print(f"Форма тензора: {some_tensor.shape}")
print(f"Тип даних тензора: {some_tensor.dtype}")
print(f"Пристрій тензора: {some_tensor.device}")
print(f"Відстеження градієнтів: {some_tensor.requires_grad}")
tensor([[0.1272, 0.6051, 0.3567, 0.0610],
        [0.3286, 0.2315, 0.3107, 0.8532],
        [0.1143, 0.1669, 0.5057, 0.3978]], device='cuda:0')
Форма тензора: torch.Size([3, 4])
Тип даних тензора: torch.float32
Пристрій тензора: cuda:0
Відстеження градієнтів: False

Арифметика з тензорами 1/2

tensor = torch.tensor([1, 2, 3])
print(tensor + 10)
print(tensor - 10)
print(tensor * 10)
print(tensor / 10)
tensor([11, 12, 13], device='cuda:0')
tensor([-9, -8, -7], device='cuda:0')
tensor([10, 20, 30], device='cuda:0')
tensor([0.1000, 0.2000, 0.3000], device='cuda:0')

Арифметика з тензорами 2/2

  • torch.add(tensor, 10)
  • torch.sub(tensor, 10)
  • torch.mul(tensor, 10)
  • torch.div(tensor, 10)

Множення матриць

Одна з найпопулярніших операцій ML та DL.

Використовується метод torch.matmul()1 або оператор @.

Важливо

  1. Внутрішні розміри повинні збігатися:
  • (3, 2) @ (3, 2) не спрацює
  • (2, 3) @ (3, 2) спрацює
  • (3, 2) @ (2, 3) спрацює
  1. Результуюча матриця має форму зовнішніх розмірів:
  • (2, 3) @ (3, 2) -> (2, 2)
  • (3, 2) @ (2, 3) -> (3, 3)

Різниця між * та torch.matmul()

Операція Код Розрахунок
Множення елементів tensor * tensor [1*1, 2*2, 3*3] = [1, 4, 9]
Множення матриць tensor.matmul(tensor) [1*1 + 2*2 + 3*3] = [14]
tensor = torch.tensor([1.0, 2.0, 3.0])
print(tensor * tensor)
print(torch.matmul(tensor, tensor))
print(tensor @ tensor)
tensor([1., 4., 9.], device='cuda:0')
tensor(14., device='cuda:0')
tensor(14., device='cuda:0')

Метод torch.matmul() швидший за *

%%time
value = 0
for i in range(len(tensor)):
  value += tensor[i] * tensor[i]
value
CPU times: total: 0 ns
Wall time: 1.08 ms
tensor(14., device='cuda:0')


%%time
torch.matmul(tensor, tensor)
CPU times: total: 0 ns
Wall time: 0 ns
tensor(14., device='cuda:0')

Найпоширеніша помилка 1/2

tensor_A = torch.tensor([[1, 2],
                         [3, 4],
                         [5, 6]], dtype=torch.float32)

tensor_B = torch.tensor([[7, 10],
                         [8, 11], 
                         [9, 12]], dtype=torch.float32)

torch.matmul(tensor_A, tensor_B)
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
Cell In[43], line 9
      1 tensor_A = torch.tensor([[1, 2],
      2                          [3, 4],
      3                          [5, 6]], dtype=torch.float32)
      5 tensor_B = torch.tensor([[7, 10],
      6                          [8, 11], 
      7                          [9, 12]], dtype=torch.float32)
----> 9 torch.matmul(tensor_A, tensor_B)

File C:\Python\Python311\Lib\site-packages\torch\utils\_device.py:62, in DeviceContext.__torch_function__(self, func, types, args, kwargs)
     60 if func in _device_constructors() and kwargs.get('device') is None:
     61     kwargs['device'] = self.device
---> 62 return func(*args, **kwargs)

RuntimeError: mat1 and mat2 shapes cannot be multiplied (3x2 and 3x2)

Найпоширеніша помилка 2/2

  • tensor.T: транспонування тензора
torch.matmul(tensor_A, tensor_B.T)
tensor([[ 27.,  30.,  33.],
        [ 61.,  68.,  75.],
        [ 95., 106., 117.]], device='cuda:0')

min, max, mean, sum…

torch.manual_seed(73)
tensor = torch.rand(10)

print(f"Мінімум: {torch.min(tensor)}")
print(f"Максимум: {torch.max(tensor)}")
print(f"Середнє: {torch.mean(tensor)}")
print(f"Сума: {torch.sum(tensor)}")
Мінімум: 0.061036042869091034
Максимум: 0.8531807065010071
Середнє: 0.31552621722221375
Сума: 3.155261993408203

Позиційний min та max

  • torch.argmax(): повертає індекс максимального значення
  • torch.argmin(): повертає індекс мінімального значення
print(f"Позиційний мінімум: {torch.argmin(tensor)}")
print(f"Позиційний максимум: {torch.argmax(tensor)}")
Позиційний мінімум: 3
Позиційний максимум: 7

Зміна типів даних

Якщо один тензор знаходиться torch.float64, а інший — torch.float32, можуть виникнути деякі помилки. Але це легко виправити:

  • tensor = tensor.to(torch.float32)
  • tensor = tensor.type(torch.float32)
tensor_32 = torch.rand(10)
print(f"Початковий тип: {tensor_32.dtype}")

tensor_64 = tensor_32.to(torch.float64)
print(f"Новий тип: {tensor_64.dtype}")

tensor_16 = tensor_32.type(torch.float16)
print(f"Новий тип: {tensor_16.dtype}")
Початковий тип: torch.float32
Новий тип: torch.float64
Новий тип: torch.float16

Reshaping

tensor = torch.rand(10)
print(f"Початкова форма: {tensor.shape}")

tensor = tensor.reshape(5, 2)
print(f"Нова форма: {tensor.shape}")
Початкова форма: torch.Size([10])
Нова форма: torch.Size([5, 2])

Stacking

tensor_A = torch.rand(10)
tensor_B = torch.rand(10)

# Вертикальне об'єднання
stacked_v = torch.vstack((tensor_A, tensor_B))
print(f"Складена форма (вертикально): {stacked_v.shape}")

# Горизонтальне об'єднання
stacked_h = torch.hstack((tensor_A, tensor_B))
print(f"Складена форма (горизонтально): {stacked_h.shape}")
Складена форма (вертикально): torch.Size([2, 10])
Складена форма (горизонтально): torch.Size([20])

Індексація

tensor = torch.arange(1, 10).reshape(1, 3, 3)

print(f"Перша квадратна дужка:\n{tensor[0]}") 
print(f"Друга квадратна дужка: {tensor[0][0]}") 
print(f"Третя квадратна дужка: {tensor[0][0][0]}")
Перша квадратна дужка:
tensor([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]], device='cuda:0')
Друга квадратна дужка: tensor([1, 2, 3], device='cuda:0')
Третя квадратна дужка: 1


print(tensor[:, 0])
print(tensor[:, :, 1])
print(tensor[:, 1, 1])
print(tensor[0, 0, :])
tensor([[1, 2, 3]], device='cuda:0')
tensor([[2, 5, 8]], device='cuda:0')
tensor([5], device='cuda:0')
tensor([1, 2, 3], device='cuda:0')

Тензори PyTorch та NumPy

  • torch.from_numpy(ndarray): масив NumPy -> тензор PyTorch.
  • torch.Tensor.numpy(): тензор PyTorch -> масив NumPy.
import numpy as np

1array = np.arange(1.0, 8.0)
2tensor = torch.from_numpy(array).to(torch.float32)
array, tensor
1
array: масив NumPy, тип даних float64.
2
tensor: тензор PyTorch, тип даних float32.
(array([1., 2., 3., 4., 5., 6., 7.]), tensor([1., 2., 3., 4., 5., 6., 7.]))

Дякую за увагу!



Матеріали курсу

ihor.miroshnychenko@knu.ua

Data Mirosh

@ihormiroshnychenko

@aranaur

aranaur.rbind.io