Створення тензора у Pytorch
Pytorch – це платформа машинного навчання на основі Python, яка в основному використовується для наукових обчислень та створення моделей глибокого навчання.
Тензор – популярна концепція в машинному навчанні та науці про дані. Тензори - це узагальнений термін для матриць та векторів. Простіше кажучи, тензори можуть бути будь-чим — n-мірним масивом, вектором або матрицею. У цій статті ми розглянемо кілька методів, які використовуються для створення тензорів у Pytorch.
Метод створення тензорів
Тензор можна створювати декількома способами. Деякі з важливих методів наведені нижче.
Як перший крок ми повинні спочатку імпортувати факел, перш ніж переходити до різних методів створення тензорів.
Синтаксис
#importing torch import torch
Підхід 1. Створення тензора за допомогою Ones().
Використовуючи функцію ones(), тензор створюється в такий спосіб, що його елементам присвоєно значення 1.
Давайте напишемо код у програмуванні на Python для створення тензора за допомогою функції ones().
приклад
#creating a tensor using ones() tensor_using_ones = torch.ones(4,4,5) #prints the created tensor print(tensor_using_ones)
Вихід
З наведеного нижче висновку бачимо, що у новоствореному тензорі всі записи встановлені на «1» у вигляді (4,4,5).
tensor([[[1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1. , 1.], [1., 1., 1., 1., 1.]], [[1., 1., 1., 1., 1.], [1., 1., 1. , 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.]], [[1., 1. , 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1. ., 1., 1., 1.]], [[1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.], [1., 1., 1., 1., 1.]]])
Підхід 2. Створення тензора з використанням нулів()
Ми використовуємо функцію Zeros() для створення тензора, всі елементи якого встановлені в 0. Використовуючи це значення можна передавати у вигляді списку, кортежу або порожніх значень. Тензори можуть бути створені будь-яким із цих способів.
Використовуючи функцію Zeros(), тензор створюється в такий спосіб, що його елементи встановлені в 0.
приклад
#creating a tensor using zeros() tensor_using_zeros = torch.zeros(4,4,5) #prints the created tensor print(tensor_using_zeros)
Вихід
З наведеного нижче висновку бачимо, що у новоствореному тензорі всі записи встановлені на «0» у формі (4,4,5).
tensor([[[0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0. , 0.], [0., 0., 0., 0., 0.]], [[0., 0., 0., 0., 0.], [0., 0., 0. , 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]], [[0., 0. , 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0 ., 0., 0., 0.]], [[0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.], [0., 0., 0., 0., 0.]]])
Підхід 3. Створення тензора за допомогою arange() — приклад 1.
Функція arange() використовується для створення одновимірного (1D) тензора.
Синтаксис
Функція arange() має наступний синтаксис.
torch.arange(start,end,step-size)
кінець - немає зазначеної форми
Ми пишемо код на Python для створення одновимірного тензора зі специфікаціями форми (5,30,2).
приклад
#creating a tensor using arange tensor_using_arange = torch.arange(5, 30,2) #prints the created tensor print(tensor_using_arange)
Вихід
Тут створюється одномірний тензор з початковим елементом 5 кінцевим елементом 29 (тобто 30-1) із загальною різницею (кроком) «2».
tensor([ 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29])
Підхід 4. Створення тензора за допомогою arange() — приклад 2
Давайте подивимося інший приклад створення тензора в Pytorch, де початкове значення негативне, тобто -5 і крок «1».
Ми пишемо код на Python для створення одновимірного тензора зі специфікаціями форми (-5,25,1).
приклад
#creating a tensor using arange tensor_using_arange1 = torch.arange(-5, 25,1) #prints the created tensor print(tensor_using_arange1)
Вихід
Тут створюється одномірний тензор із початковим елементом як -5, кінцевим елементом як 24 (тобто 25-1) із загальною різницею (кроком) «1».
tensor([-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24])
Підхід 5. Створення тензора за допомогою rand()
Інший метод, що часто використовується, створення тензора в Python — це метод rand(). Тензор, що містить випадкові числа, буде створено за допомогою методу rand() для зазначеної фігури.
Синтаксис
Синтаксис методу rand() показаний нижче.
приклад
Тепер, щоб зрозуміти створення тензора за допомогою методу rand(), напишемо код мовою програмування Python.
#creating a tensor using rand() tensor_using_rand = torch.rand(5, 5) #prints the created tensor print(tensor_using_rand)
Вихід
З наведеного висновку видно, що тензор форми 5X5 створюється з використанням методу rand(). Елементи всередині тензора є випадковими числами рівномірного розподілу.
tensor([[0.2000, 0.6068, 0.3968, 0.6269, 0.8587], [0.1754, 0.5050, 0.8569, 0.5139, 0.9026], [0.2634, 0.6 0.6873, 0.2214], [0.9257, 0.6045, 0.4763, 0.5107, 0.7925], [0.7465, 0.0279, 0.4937, 0.0989, 0.8806]
Підхід 6. Створення тензора за допомогою full()
Метод full() Tensor використовується щоразу, коли бажано, щоб елементи Tensor мали однакове число.
Синтаксис
Синтаксис методу full() є наступним.
torch.full(size, fill_value, *, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)
fill_value - значення (те саме число), яке ми хочемо мати в Тензорі.
Припустимо, ми хочемо створити тензор, який має той самий номер (4), що й усі елементи. Ми можемо написати код мовою програмування Python, як показано нижче. Значення fill_value вказано як 4. Це значення, яке буде спільним елементом у Тензорі.
приклад
#creating a tensor using full method tensor1 = torch.full((5,5,4) #prints the created tensor print(tensor1)
Вихід
Для наведеного вище коду ми даємо тензор з матрицею 5X5 з 4 елементами як всі елементи.
tensor([[4, 4, 4, 4, 4], [4, 4, 4, 4, 4], [4, 4, 4, 4, 4], [4, 4, 4, 4, 4] , [4, 4, 4, 4, 4]])
Підхід 7: Створення тензора за допомогою linspace
Синтаксис
Метод Linspace() має наступний синтаксис.
linspace (start, stop, num, endpoint=True, retstep=False, dtype=None, axis=0)
низький – стартовий елемент (включно)
висококласний елемент (включно)
num - кількість елементів, які будуть згенеровані у тензорі
приклад
Ми використовуємо метод Linspace() для створення одновимірного (1D) тензора. Наприклад, у разі ми вказуємо форму (2,20,4). У нашому прикладі 2 - початковий елемент, 20 - кінцевий елемент, а 4 - кількість елементів, які будуть присутні в одновимірному тензорі.
#creating a tensor using linspace tensor_using_linspace = torch.linspace(2, 20, 4) #prints the created tensor print(tensor_using_linspace)
Вихід
Ми бачимо, що наведений вище код дає результат нижче, де створюється одномірний тензор із 4 елементами.
Підхід 8. Створення тензора за допомогою randint()
Метод randint() створює тензор з випадковими цілими числами, згенерованими відповідно до зазначеної форми.
Синтаксис
torch.randint(low,high,shape)
низький – стартовий елемент (включно)
елітний елемент (ексклюзивний)
shape - форма Тензора
приклад
Ми пишемо код Python, використовуючи методи randint(), як показано нижче.
#creating a tensor using randint tensor_using_randint = torch.randint(10,100, (2,2)) #prints the created tensor print(tensor_using_randint)
Вихід
У наведеному нижче висновку бачимо тензор з випадковими елементами, згенерованими між формою (10,100,(5,5)). Тут ми отримуємо тензор із випадковими елементами від 10 до 99 у формі матриці 5X5.
tensor([[71, 32, 56, 34, 21], [37, 71, 98, 63, 40], [41, 45, 27, 41, 50], [51, 42, 43, 71, 14] , [87, 52, 77, 88, 85]])
Підхід 9: Створення тензора за допомогою Eye()
Метод очей() корисний, коли хочемо створити 2D-тензор, у якому елементи по діагоналі рівні 1, інші елементи — нулям.
Синтаксис
Тепер ми пишемо код Python, використовуючи метод Eye().
Приклад -1
#creating a tensor using eye tensor1_using_eye = torch.eye(4, 4) #prints the created tensor print(tensor1_using_eye)
Вихід
Тепер ми створили 2D-тензор 4X4, використовуючи метод Eye().
tensor([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.], [0., 0 ., 0., 1.]])
Погляньмо ще один приклад створення тензора з використанням функції ока.
Приклад-2
#creating a tensor using eye tensor2_using_eye = torch.eye(3) #prints the created tensor print(tensor2_using_eye)
Вихід
Тепер ми створили 2D-тензор 3X3, використовуючи метод Eye(), як показано нижче.
tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))
Підхід 10. Створення тензора з допомогою методу Complex().
Метод complex() — це останній метод, який ми збираємось розглянути. Він використовується для створення комплексного тензора, де створений тензор матиме як уявну, так і дійсну частину.
Синтаксис
Метод complex() має наступний синтаксис.
torch.complex(real part, imaginary part)
приклад
Код методу complex() написано нижче.
real_tensor = torch.rand(4,5) print(real_tensor) imaginary_tensor = torch.rand(4, 5) print(imaginary_tensor) tensor_using_complex = torch.complex(real_tensor, imaginary_tensor) #prints created tensor print(tensor_using_complex)
Вихід
Таким чином, ми створили складний Тензор, що має уявну та дійсну частини форми 4X5.
Tensor([[0.8284, 0.7833, 0.4952, 0.7634, 0.9098], [0.6373, 0.3428, 0.0352, 0.6095, 0.8066], [0.7292,6.0 24], [0.2920, 0.2676, 0.4669, 0.2841, 0.5974] ]) tensor([[0.5236, 0.9037, 0.7622, 0.7044, 0.3362], [0.8543, 0.6088, 0.4882, 0.0563, 0.9318], [0.7075, 0.1176, 0.6272, 0.3318, 0.3751], [0.6317, 0.1482, 0.3772, 0.6941, 0.9126]] ten5 (3. 037j, 0.4952+0.7622j, 0.7634+0.7044 j, 0.9098+0.3362j], [0.6373+0.8543j, 0.3428+0.6088j, 0.0352+0.4882j, 0.6095+0.0563j, 0.8066+0.9318j], [0.7292+0.7075j, 0.0703+0.1176j, 0.3846+0.6272j, 0.6653+0.3318j, 0.1924+0.3752j], .1482j, 0.4669+0.3772j, 0.2841 +0.6941j, 0.5974+0.9126j]])
Висновок
З цієї статті ми дізналися кілька способів створення тензорів у Pytorch. Рекомендуємо також ознайомитись з нашими статтями.
Статті з цієї тематики:
- Як спростити створення нових документів у Linux за допомогою шаблонів
- 6 кращих програм для створення та запису музики в Linux
- Редактор субтитрів Aegisub — безкоштовний інструмент для створення та зміни субтитрів
- 5 кращих завантажувальних програм/інструментів з USB-накопичувача для створення завантажувальної ОС
- Топ-20 найкращих інструментів побудови графіків для Linux для створення наукових графіків
- Початок роботи з OpenAI Gym: створення середовищ тренажерного залу
- Створення та ініціалізація списків у Java та Groovy
- Як створити шаблон ОС Ubuntu та Debian на Proxmox VE
- Створення шаблонів Rocky/Alma/CentOS на Proxmox VE
- Створення віртуальних середовищ Python та керування ними за допомогою virtualenv в Ubuntu/Debian
- Створення функцій у JavaScript
- Як створювати анімацію та переходи у Vue.js
- 5 корисних порад щодо створення адаптивних макетів
- Створення ігрових ресурсів за допомогою інструментів малювання Arcade
- Як створювати динамічні веб-сторінки за допомогою шаблонів Jinja у FastAPI
ML: Введення в PyTorch: 1. Тензор
PyTorch необхідно встановити, обравши правильну конфігурацію на сторінці проекту та підключити бібліотеку:
import torch e = torch.eye(2) # одинична матриця 2x2 print(e) # tensor([[1., 0.], [0., 1.]])
Для читання цього документи бажано впевнене володіння бібліотекою numpyзокрема такими поняттями як розмірність тензора, його форма і те, як вони змінюються при різних операціях. Нижче розглянемо основи роботи з тензорами. Наступні документи присвячені обчислювальним графам та нейронним мережам у PyTorch.
Створення тензорів
Тензори з речовими компонентами типу float32 створюються так:
v = torch.empty(10) # вектор з 10 елементів float32 (сміття) m = torch.empty(2, 3) # матриця форми (2,3) float32 (сміття) x = torch.empty_like(m) # зі сміттям , такої ж форми як m
x = torch.tensor( [ [0,1,2], [3,4,6] ] ) # матриця форми (2,3) int64 зі списку s = torch.tensor(137.) # скаляр (dim=0 ) типу float32 (точка!) s = torch.tensor(137, dtype=torch.double)# теж, але типу float64 (хоча і без точки) e = torch.from_numpy( np.eye(3) ) # з numpy- тензора (з його пам'яттю) print( x ) # tensor([[0., 1., 2.], [3., 4., 6.]]) print( x.numpy() ) # numpy-тензор ( у тій самій пам'яті)
Основні властивості тензора - це його розмірність (число індексів), форма (розмірності індексів), тип елементів та їх кількість:
print( x.dim(), x.type(), x.numel() ) # розмірність, тип елементів, їх кількість print( x.size(), x.shape ) # torch.Size([2, 3] ) форма (еквівалентні) print( tuple(x.shape), *x.shape ) # (2, 3) 2 3 форма print( s.item(), s.dim() ) # 137 0 (для скаляра або матриці з одним ел.)
Тензор можна створювати з допомогою конструкторів. Доступні такі типи (замість torch.FloatTensor(. ) можна писати torch.Tensor(. )):
HalfTensor - float16, ShortTensor - int16, CharTensor - int8, FloatTensor - float32, IntTensor - int32, ByteTensor - uint8, DoubleTensor - float64, LongTensor - int64, BoolTensor - bool.
x = torch.Tensor(2,3) # матриця (2,3) з 6 елементів float32 (сміття) y = x.long() # int64 новий тензор y = x.float() # float32 новий тензор y = x. double() # float64 новий тензорТип елементів можна також вказувати у методі empty або функціях ініціалізації (див. нижче):
x = torch.empty(2,3, dtype=torch.float64)# матриця 2x3 зі сміттям float64 x = torch.empty(2,3, dtype=torch.double) # теж саме y = torch.zeros(2,3 , dtype=torch.int64) # матриця 2x3 нулів int64 y = torch.zeros(2,3, dtype=torch.long) # теж саме y.element_size() # 8 - розмір у байтах елемента
Ініціалізація значень
При створенні тензора можна відразу ініціалізувати значенням (за замовчуванням типу float == float32):
y = torch.zeros (2, 3) # матриця 2x3 з нулів типу float32 x = torch.zeros_like(y) # такої ж форми як y з нулів x = torch.ones (2, 3) # матриця 2x3 з одиниць x = torch.ones_like(y) # такої ж форми як y з одиниць x = torch.full((2, 3), 3.14159265) # заповнити матрицю 2x3 числом pi x = torch.eye (3) # одинична матриця 3x3 x = torch. eye (2,3) # "поодинока" не квадратна [[1., 0., 0.], # [0., 1., 0.]] x = torch.linspace(0,2,5) # [ 0.0,0.5,1.0,1.5,2.0] [beg,end], num x = torch.rand (2, 3) # 2x3 рівномірно случ.матриця [0. 1] x = torch.randn(2, 3) # 2x3 нормально случ.матриця (mean=0, var=1) x = torch.empty(3).uniform_(0, 1) # вектор з рівномірним розподілом [0. .1] x = torch.empty(3).normal_(mean=0,std=1) # вектор з нормальним розподілом
x = torch.arange(4) # [0,1,2,3] [0,end) x = torch.arange(2,14,3) # [2,5,8,11] [beg,end) , step x = torch.randperm(10) # [8,6,9,3,5,0,1,4,7,2] - случ.перестановка x = torch.randint (1, 10, (2,3) )) # 2x3 случ.целых з інтервалу [1. 10)
Управління пам'яттю
У багатьох методів є друга версія, ім'я якої закінчується підкресленням (in-place функції). Наприклад, експонента від елементів тензора: x.exp() або torch.exp(x) повертає новий тензор Метод x.exp_() обчислює експоненту від елементів x і записує значення місце цих елементів, тобто. нова пам'ять не виділяється (метод item() повертає єдиний елемент тензор як число):
x = torch.ones(1) # вектор із однієї компоненти y = x.exp() # різна пам'ять: print(x); y[0]=2; print(x.item()) # tensor([1.]) 1.0 y = x.exp_() # одна пам'ять: print(x); y[0]=2; print(x.item()) # tensor([2.7183]) 2.0
Цю ж семантику використовує метод fill_, який надає всім елементам дане значення або метод обнулення всіх елементів zero_:
x = torch.empty(2, 3).fill_(-1) # матриця з -1 x.zero_() # тепер нулі (у тому ж тензор)
Оператор присвоєння, як завжди, відбувається за посиланням (дані розшаруються). Якщо потрібна копія даних, її можна отримати шляхом clone або copy_:
y = x # y і z - один об'єкт y = x.clone() # копіювання y.copy_(x) # копіює в себе x (broadcastable) y = torch.empty_like(x).copy_(x) # виділяємо пам'ять копіюємо
Для "звичайних" тензорів copy_ і clone призводять до однакового результату. Різниця починається при побудові графа обчислень (див. нижче).
Операції
Більшість операцій з тензорами такі ж, як і в бібліотеці. numpy:
x1, x2 = torch.ones(2,3), torch.ones(2,3) x1[0] = 2 # змінам перший рядок на 2 [[2., 2., 2.], [1., 1 ., 1.]] x2[:,1] = 3 # змін другу колонку на 3 [ [1., 3., 1.], [1., 3., 1.]] y = x1 + x2 # складаємо [[3., 5., 3.], [2., 4., 2.]]
Додавання, віднімання, множення і розподіл (без згортки) можна робити інкрементним (у тій же пам'яті):
x += y x.add_(y) # до тензора x додали y (інкриментація) x.add_(y, alpha=2.0) # x += 2*y (так швидше для великих тензорів) x *= y x.mul_ (y)
За допомогою тензора індексів можна робити вибірки елементів:
a = torch.ByteTensor(2,3).random_() # випадкові байти [[185, 16, 242], [223, 147, 202]] print(a > 128) # [[ True, False, True], [True, True, True]] a[a > 128] # [185, 242, 223, 147, 202] - елементи, великі 128
Корисним прикладом таких операцій є синхронне перемішування тензорів (створюється нова пам'ять):
X = torch.arange(10) # [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Y = -X # [ 0,-1,-2,-3,-4, -5,-6,-7,-8,-9] idx = torch.randperm(X.shape[0]) # перестановка індексів X = X[idx] # [ 4, 9, 7, 3, 2, 0 , 5, 6, 1, 8] Y = Y [idx] # [-4,-9,-7,-3,-2, 0,-5,-6,-1,-8]
Конкатенацію тензорів можна робити вздовж певної осі або стосами:
x1 = torch.arange(-3,0) # [-3, -2, -1] x2 = torch.arange(3) # [ 0, 1, 2] torch.cat([x1, x2], dim= 0) # [-3, -2, -1, 0, 1, 2] конкатенація torch.stack([x1, x2]) # [[-3, -2, -1], [ 0, 1, 2] ]
Тензорні згортки
Усі функції цього розділу є у двох еквівалентних версіях: x.mm(y) і torch.mm(x,y).
v.dot(u) # cкалярне множення - тільки 1D векторів x.mv(v) # матриця на вектор: (n,s) (s) = (n) x.mm(y) # тільки 2D матриць : (n, s) (s,m) = (n,m) x.bmm(y) # для 3D тензорів: (b, n,s) (b, s,m) = (b, n,m) x.matmul( y) аналог з numpy: (j, 1, n, s) (k, s, m) = (j, k, n, m) torch.addmv (t, m, v, beta = 1, alpha = 1 ) # alpha * (m @ v) + beta * t torch.addmm (t, m1, m2, beta = 1, alpha = 1) # alpha * (m1 @ m2) + beta * t
Багато функцій виконують обчислення або з усіма елементами або по одному з індексів (вздовж осі). Нижче це вісь 1 (Другий індекс):
x = torch.Tensor([[2,1,3], [1,2,1]]) x.sum(1) # [6., 4.] сума x.mean(1) # [2., 1.33] середнє x.prod(1) # [6., 2.] твір x.argmin(1) # [1, 2] v,i = x.topk(2, dim=1) # 2 найбільших значень та їх індексів
Зміна форми
Форма тензора (число індексів та їх розмірності) змінюється функціями view і reshape:
torch.arange(6).view(2,3) # tensor([[0, 1, 2], torch.arange(6).reshape(2,3) # [3, 4, 5]])
У цих функціях перераховуються нові розмірності індексів. повинно збігатися з числом елементів). Одна з розмірностей може дорівнювати -1 і тоді її буде пораховано автоматично. Наприклад, вище можна було б написати view(-1,3) або view(2,-1). Можна також змінювати форму, використовуючи інший тензор з тим самим числом елементів:
a, b = torch.zeros(2,5), torch.zeros(10) b.view_as(a)
Нагадаємо, що тензори прийнято зображати у табличній формі: вектор (ndim=1) - це рядок чисел, матриця форми (rows, cols) – це прямокутна таблиця з rows рядками та cols колонками. Тривимірний тензор (три індекси, ndim=3) зображують у вигляді стопки матриць:
Операція транспонування (перестановка місцями стовпців та рядків): t() або t_() дозволена тільки для 2D матриць:
z = x.t() # сам x не змінюється z = x.t_() # змінюється x
Перестановка будь-яких двох осей (індексів) у тензорах довільної розмірності робиться методом transpose:
x.transpose(1, 2) # перестановка осей 1 та 2
Ще більш універсальний спосіб перестановки індексів:
x = torch.randn(3, 5) x.permute(1, 0) == x.t() # еквівалентно транспонування x = torch.randn(2, 3, 5) x.permute(2, 0, 1).size () # torch.Size ([5, 2, 3])
Елементи у пам'яті
Форма тензора (x.size() або x.shape) - це спосіб нумерації лінійно впорядкованих елементів тензора за допомогою x.dim() індексів Метод x.stride() дає набір зсувів на які множаться індекси для отримання відповідного елемента (від покажчика на початковий):
x[i,j,k] = x + stride[0]*i # shape[1]*shape[0] + stride[1]*j # shape[0] + stride[2]*k # 1 (псевдокод )
x = torch.arange(935).view(5,17,11) # 11*17 print( x.stride(), x[1,1,1].item() ) #< (187, 11, 1 ) 199 = 187 +11 +1
При зміні форми view змінюється x.stride(), але не дані у пам'яті. Однак, при цьому може виникнути ситуація, коли рядки матриці в пам'яті йти не послідовно один за одним. Нижче на малюнку тензор x це 2D матриця форми (2,3). Її елементи у пам'яті йдуть послідовно та рядково. Операція транспонування (без підкреслення) вихідний тензор не змінює та повертає посилання на ті ж дані, але з іншим stride. В результаті її рядки в пам'яті "переплутуються". Якщо викликати метод y.contiguous(), то буде створено нова пам'ять, куди перенесуться елементи, щоб рядки йшли послідовно:
Перевірити чи знаходяться рядки тензора в пам'яті послідовно можна так:
print( x.is_contiguous() ) # True - безперервно рядково
Для довільної розмірності цей метод перевіряє умову stride[i]=stride[i+1]*size[i+1] всім індексів. Якщо це не так, методом x = x.contiguous() можна перенести дані у безперервний фрагмент.
Зміна форми методом reshape спочатку перевіряє is_contiguous і якщо вона False викликає contiguous (Створює нову пам'ять). Після цього викликається view. Так, у прикладі на картинках для операції y=x.t() тензори x,y мають загальні дані (за посиланням), а після y = y.contiguous() вже немає.
Таким чином, reshape може повернути дані як за посиланням, так і за значенням. Метод view повертає завжди за значенням, але може видати помилку, якщо порушується умова is_contiguous.
Обчислення на GPU
Перемножимо дві великі одиничні матриці. На центральному процесорі Intel i3-3220 3.3GHz, 16Gb це займе 35 секунд:
x1 = torch.eye(10000) y1 = torch.eye(10000) z1 = x1.mm(y1) # 35s
Якщо комп'ютер має графічну карту (наприклад, NVIDIA GeForce GTX 1050 Ti), то можна створити додатковий обчислювальний пристрій (нуль означає номер графічної карти):
torch.cuda.device_count() # кількість доступних GPU cpu = torch.device("cpu") gpu = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
Повторимо ці ж обчислення, створюючи та перемножуючи тензори безпосередньо в GPU:
x1 = torch.eye(10000, device = gpu) y1 = torch.eye(10000, device = gpu) z1 = x1.mm(y1) # 0.04s
Насправді частіше тензори створюються в пам'яті CPU, потім методом to пересилаються в GPU, там перемножуються і повертаються назад у CPU:
x1 = torch.eye(10000).to(gpu) y1 = torch.eye(10000).to(gpu) z1 = x1.mm(y1).to(cpu) # 2s
Розмір зайнятої пам'яті GPU можна отримати так:
from GPUtil import showUtilization as gpu_usage gpu_usage()
torch.cuda.empty_cache()
Тензори. Векторно-матричні операції
Настав час познайомитись з однією з фундаментальних можливостей пакету PyTorch – векторно-матричними обчисленнями. На одному з минулих занять ми з вами вже бачили, як можна поелементно множити один тензор на інший:
a = torch.arange(1, 10).view(3, 3) b = torch.arange(10, 19).view(3, 3) r1 = a * b r2 = torch.mul(a, b)
tensor([[ 10, 22, 36],
[ 52, 70, 90],
[112, 136, 162]])
Матричне множення
Але якщо нам потрібно виконати саме матричне множення, тобто рядки однієї матриці множити на стовпці інший і результати складати:
то для цього слід використовувати спеціальні функції та оператори. Почнемо із функцій. Отже, щоб перемножити дві матриці a і b за всіма правилами математики часто використовують функцію:
c = torch.matmul(a, b) # Перемноження з ефектом транслювання
c = torch.mm(a, b) # Перемноження без ефекту транслювання
Обидві ці функції повертають двомірний тензор з результатом матричного множення:
tensor([[ 84, 90, 96],
[201, 216, 231],
[318, 342, 366]])
Однак функція torch.mm виконує перемноження без можливості транслювання, а функція torch.matmul дозволяє транслювати тензор у разі потреби узгодження розмірностей. Давайте поясню це на конкретному прикладі. Оголосимо ще один вектор:
v = torch.LongTensor([-1, -2, -3])
Тут використовується тип long, щоб типи даних тензорів a і v збігалися. Тоді будуть цілком допустимі команди:
c = torch.matmul(a, v) # матриця на вектор c = torch.matmul(v, a) # вектор на матрицю
Але неприпустима команда:
c = torch.mm(a, v) # помилка
оскільки тут необхідні матриці (тензори) узгоджених розмірів. Яку саме функцію використовувати на практиці, залежить від конкретної задачі, що розв'язується.
Або замість цих функцій можна використовувати аналогічні методи:
У PyTorch додатково до звичайного матричного множення додається ще одна команда:
яка перемножує матриці, так званим, батчам (пакетам). Це часто використовується у нейронних мережах. Принцип роботи цієї функції (та аналогічного методу) простіше показати на конкретному прикладі:
bx = torch.randn(7, 3, 5) by = torch.randn(7, 5, 4) bc = torch.bmm(bx, by) bc.size() # тензор 7x3x4
Як бачите, перша вісь визначає кількість матриць у батчі (пакеті), які потрібно перемножити між собою. Тобто, перемножуються незалежно сім матриць між собою і на виході формується також сім матриць, але з розмірами 3x4.
Векторне множення
Аналогічні операції можна виконувати з векторами. Математично, якщо у нас є два вектори:
то їх множення можна реалізувати двома способами:
Перше множення називається скалярним та реалізується або через функцію torch.dot (або аналогічний метод):
a = torch.arange(1, 10, dtype=torch.float32) b = torch.ones(9) c = torch.dot(a, b) # tensor (45.)
Або за допомогою оператора @:
Інший спосіб множення (зовнішнє множення векторів) реалізується за допомогою функції (або аналогічного методу):
отримаємо результат у вигляді наступної матриці:
tensor([[1., 1., 1., 1., 1., 1., 1., 1., 1.],
[2., 2., 2., 2., 2., 2., 2., 2., 2.],
[3., 3., 3., 3., 3., 3., 3., 3., 3.],
[4., 4., 4., 4., 4., 4., 4., 4., 4.],
[5., 5., 5., 5., 5., 5., 5., 5., 5.],
[6., 6., 6., 6., 6., 6., 6., 6., 6.],
[7., 7., 7., 7., 7., 7., 7., 7., 7.],
[8., 8., 8., 8., 8., 8., 8., 8., 8.],
[9., 9., 9., 9., 9., 9., 9., 9., 9.]])
Розмноження вектора на матрицю
Нарешті розглянемо множення вектора на матрицю. Це також можна записати двома способами:
Для реалізації другого способу, задамо одномірний та двовимірний тензор:
a = torch.FloatTensor([1,2,3]) b = torch.arange(4, 10, dtype=torch.float32).view(2, 3) # матриця 2x3
І, потім, скористаємося функцією:
r = torch.mv(b, a) # tensor ([32., 50.])
r = b.mv(a) # tensor ([32., 50.])
Причому, зверніть увагу, функції (або методі) mv тензор b повинен являти собою матрицю, а тензор a - вектор. Якщо їх поміняти місцями:
r = torch.mv(a, b) # помилка
Елементи лінійної алгебри
З вищої математики добре відомо, що можна використовувати матриці для вирішення систем лінійних рівнянь. Для цього в PyTorch існує модуль linalg. Давайте розглянемо деякі його функції.
Припустимо, є квадратна матриця 3x3:
a = torch.FloatTensor([(1, 2, 3), (1, 4, 9), (1, 8, 27)])
Насамперед обчислимо ранг цього тензора, щоб бути впевненим, що він складається з лінійно незалежних рядків та стовпців:
torch.linalg.matrix_rank(a) # ранг дорівнює 3
Якщо ранг матриці збігається з її розмірністю, значить вона здатна описувати систему з трьох незалежних лінійних рівнянь. У нашому випадку, система рівнянь матиме вигляд:
Тут - деякі числа лінійного рівняння. Наприклад, візьмемо їх рівними:
y = torch.FloatTensor([10, 20, 30])
Тоді коріння рівняння можна визначити за допомогою функції solve:
torch.linalg.solve(a, y) # tensor ([-5.0000, 10.0000, -1.6667])
Інший спосіб розв'язання цієї системи лінійних рівнянь можливий через обчислення зворотної матриці. Спочатку рівняння можна записати у векторно-матричному вигляді:
Звідки отримуємо рішення:
На рівні пакету PyTorch це робиться так:
invA = torch.linalg.inv(a) # Обчислення зворотної матриці x = torch.mv(invA, y) # обчислення коренів
tensor([-5.0000, 10.0000, -1.6667])
Звичайно, я представив лише приклади використання модуля linalg. Наводити всі функції немає сенсу, оскільки вони мають досить спеціалізоване призначення і фахівці у своїх галузях легко зможуть ними скористатися. Для повноти картини я наведу список найбільш характерних функцій, щоб ви знали можливості модуля linalg.
