pytorch - wanho choiwanochoi.com/lecture/pytorch.pdf · 2019-10-11 · torch.tensor() allocates a...

100
PyTorch Wanho Choi (wanochoi.com)

Upload: others

Post on 06-Aug-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

PyTorch

Wanho Choi (wanochoi.com)

Page 2: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Background & Theories

Page 3: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Introduction

Page 4: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

PyTorchOpen source and free machine learning library based on the Torch library

Developed by FAIR’s (Facebook AI Research) Group

Supported languages: Python, C++

Page 5: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

PyTorch vs TensorFlow• PyTorch

‣Define-and-Run• TensorFlow

‣Define-by-Run : peculiar framework such as placeholders & sessions

Page 6: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

PyTorch vs TensorFlow

https://www.developereconomics.com/tensorflow-vs-pytorch

Page 7: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

PyTorch Basics

Page 8: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Versionimport torch

print(torch.__version__) # 1.1.0

Facebook CTO Mike Schroepfer announces the release of PyTorch 1.0 at Facebook developer conference F8 on May 2, 2018 at the McEnery Convention Center in San Jose, California (Image Credit: Facebook)

Page 9: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensor• Simply

‣Multi-dimensional array

‣Generalized matrix

• Strictly

‣ Matrix: just a collection of numbers inside brackets

‣ Tensors have some transformation properties when changing coordinate system.

• In PyTorch

‣Unit of data

‣Array + Automatic Differentiation + GPU Acceleration

Page 10: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensor

0D tensor

1D tensor

4D tensor2D tensor

3D tensor

scalar

vector

matrix

cube

a vector of cube

Page 11: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensor

0D tensor

1D tensor

4D tensor2D tensor

3D tensor

variable

array

gray scale image

RGB image

video

Page 12: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensor: shapeimport torch

a = torch.tensor(3.14)print(a)                                  # tensor(3.1400)print(a.shape, a.size())                  # torch.Size([]) torch.Size([])

b = torch.tensor([1.414])print(b)                                  # tensor([1.4140])print(b.shape, b.size())                  # torch.Size([1]) torch.Size([1])

c = torch.tensor([1., 2., 3.])print(c)                                  # tensor([1., 2., 3.])print(c.shape, c.size())                  # torch.Size([3]) torch.Size([3])

d = torch.tensor([[1, 2], [3, 4], [5, 6]])print(d)                                  # tensor([[1, 2], [3, 4], [5, 6]])print(d.shape, d.size())                  # torch.Size([3, 2]) torch.Size([3, 2])

e = torch.tensor([[[1, 2, 3], [3, 4, 5]], [[5, 6, 7], [7, 8, 9]]])print(e)                                  # tensor([[[1, 2, 3], [3, 4, 5]], [[5, 6, 7], [7, 8, 9]]])print(e.shape, e.size())                  # torch.Size([2, 2, 3]) torch.Size([2, 2, 3])print(e.shape[0], e.shape[1], e.shape[2]) # 2 2 3print(e.size(0), e.size(1), e.size(2))    # 2 2 3

Page 13: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensor: dtypeimport numpy as npimport torch

a = np.array([[1, 2], [3, 4]])print(a.dtype)  # int64

b = np.array([[1., 2.], [3., 4.]])print(b.dtype)  # float64

aa = torch.from_numpy(a)print(aa.dtype) # torch.int64

bb = torch.from_numpy(b)print(bb.dtype) # torch.float64

aa = torch.from_numpy(a).float()print(aa.dtype) # torch.float32

aa = torch.FloatTensor(a)print(aa.dtype) # torch.float32

a = aa.int()print(a.dtype)  # torch.int32

Page 14: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensor: dataimport torch

a = torch.tensor([[1, 2], [3, 4]])print(type(a))          # <class ‘torch.Tensor'>print(a)                # tensor([[1, 2], [3, 4]])

print(a[0][0], a[0][1]) # tensor(1) tensor(2)print(a[1][0], a[1][1]) # tensor(3) tensor(4)

print(a[0][0].item())   # 1print(a[0][1].item())   # 2print(a[1][0].item())   # 3print(a[1][1].item())   # 4

b = a.dataprint(type(b))          # <class ‘torch.Tensor'>print(b)                # tensor([[1, 2], [3, 4]])

print(b[0][0], b[0][1]) # tensor(1) tensor(2)print(b[1][0], b[1][1]) # tensor(3) tensor(4)

print(b[0][0].item())   # 1print(b[0][1].item())   # 2print(b[1][0].item())   # 3print(b[1][1].item())   # 4

Page 15: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Interoperability with NumPyimport numpy as npimport torch

a = [[1, 2], [3, 4]]print(type(a)) # <class ‘list>

b = np.array(a)print(type(b)) # <class ‘numpy.ndarray>

c = torch.tensor(b)print(type(c)) # <class ‘torch.Tensor>

c = torch.from_numpy(b)print(type(c)) # <class ‘torch.Tensor>

c = torch.as_tensor(b)print(type(c)) # <class ‘torch.Tensor>

d = c.numpy()print(type(d)) # <class 'numpy.ndarray>

NumPy Array

PyTorch Tensor

b = torch.from_numpy(a)

a = b.numpy()

b = torch.tensor(a)b = torch.as_tensor(a)

Page 16: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Copy vs Reference• PyTorch tensors share the memory buffer of NumPy ndarrays.

• Thus, changing one will affects the others.

• torch.tensor() allocates a new memory by copying the ndarray.

• torch.tensor() is an alias for the default tensor type, e.g. torch.FloatTensor().

• If you want avoid a copy, use torch.from_numpy() or torch.as_tensor().

Page 17: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Copy vs Referenceimport numpy as npimport torch

a = np.array([[1.]])

b = torch.tensor(a)      # copyc = torch.FloatTensor(a) # copyd = torch.from_numpy(a)  # referencee = torch.as_tensor(a)   # reference

print(b.item()) # 1.0print(c.item()) # 1.0print(d.item()) # 1.0print(e.item()) # 1.0

a[0][0] = 2

print(b.item()) # 1.0print(c.item()) # 1.0print(d.item()) # 2.0print(e.item()) # 2.0

a[0][0] = 0

b[0][0] = 111print(a[0][0]) # 0.0

c[0][0] = 222print(a[0][0]) # 0.0

d[0][0] = 333print(a[0][0]) # 333

e[0][0] = 444print(a[0][0]) # 444

a = np.array([[12345.]]) # a new object

print(b.item()) # 111.0print(c.item()) # 222.0print(d.item()) # 333.0print(e.item()) # 444.0

2/21/2

Page 18: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensors on GPUimport numpy as npimport torch

a = np.array([1, 2, 3])

c = torch.from_numpy(a).float()print(c) # tensor([1., 2., 3.])

c = torch.from_numpy(a).float().to(‘cpu’)print(c) # tensor([1., 2., 3.])

g = torch.from_numpy(a).float().to(‘cuda’)print(g) # tensor([1., 2., 3.], device=‘cuda:0’)

DEVICE = ‘cuda’ if torch.cuda.is_available() else ‘cpu’

g = torch.from_numpy(a).float().to(DEVICE)print(g) # tensor([1., 2., 3.], device=‘cuda:0’)

if torch.cuda.is_available():  print(torch.cuda.device_count()) # 1: the number of GPUs available  print(torch.cuda.current_device()) # 0: the index of a currently selected device  print(torch.cuda.get_device_name(‘cuda’)) # GeForce RTX 2080 Ti: the name of the device

Page 19: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Available Devices

Page 20: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Random Tensorimport torch

a = torch.randn(1) # 1 by 1 tensorb = torch.randn(1) # 1 by 1 tensorprint(a, b)

c = torch.randn(2, 3) # 2 by 3 tensorprint(c)

# above this you will get different results for each runtorch.manual_seed(123)# blow this will always produce the same results

a = torch.randn(1) # 1 by 1 tensorb = torch.randn(1) # 1 by 1 tensorprint(a, b)

c = torch.randn(2, 3) # 2 by 3 tensorprint(c)

Uniform Distribution

frequency

torch.rand()

68%

95%

Normal Distribution

frequency

torch.randn()

Page 21: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Matrix Multiplication

[1 23 45 6] [1 2 3

4 5 6] =9 12 1519 26 3329 40 51

3 × 33 × 2 2 × 3

import torch

a = torch.tensor([[1, 2], [3, 4], [5, 6]])print(a.shape) # torch.Size([3, 2])

b = torch.tensor([[1, 2, 3], [4, 5, 6]])print(b.shape) # torch.Size([2, 3])

c = torch.mm(a, b)print(c) # tensor([[ 9, 12, 15], [19, 26, 33], [29, 40, 51]])

c = a.mm(b)print(c) # tensor([[ 9, 12, 15], [19, 26, 33], [29, 40, 51]])

c = torch.matmul(a, b)print(c) # tensor([[ 9, 12, 15], [19, 26, 33], [29, 40, 51]])

c = a @ bprint(c) # tensor([[ 9, 12, 15], [19, 26, 33], [29, 40, 51]])

Page 22: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Autograd: Automatic DifferentiationAn engine for computing gradients (or Jacobians).

Autograd automatically calculates the gradients (or Jacobians) by applying chain rules, tracing from the root to the leaves of the graph.

If you set a Tensor’s attribute .requires_grad as True, it starts to track all operations on it.

When you finish your computation you can call backward() and have all the gradients (or Jacobians) computed automatically.

The result will be accumulated into .grad attribute.

.requires_grad_() changes an existing tensor’s requires_grad flag in-place.

Page 23: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

.requires_gradEvery Tensor has a flag: .requires_grad.

Default value: False

If False, the tensor will be excluded from the gradient computation.

Tensors that must be included in the gradient computation must explicitly set this flag to True.

input.requires_grad = False ➜ output.requires_grad = False (automatically)

input.requires_grad = True ➜ output.requires_grad = True (automatically)

This flag can also be set when the tensor is first created, and can be changed later.

Using it, you can freeze a part of the graph to avoid gradient computation.

Page 24: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

.requires_gradimport torch

DEVICE = 'cuda' if torch.cuda.is_available() else ‘cpu'

a = torch.rand(1, requires_grad=True).to(DEVICE)b = torch.rand(1, requires_grad=True).to(DEVICE)print(a.requires_grad, b.requires_grad) # True True

a = torch.rand(1, dtype=torch.float).to(DEVICE)b = torch.rand(1, dtype=torch.float).to(DEVICE)print(a.requires_grad, b.requires_grad) # False False

a.requires_grad_()b.requires_grad_()print(a.requires_grad, b.requires_grad) # True True

Page 25: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

.requires_gradimport torch

x = torch.tensor([1.0]) # requires_grad=False by defaulty = torch.tensor([2.0]) # requires_grad=False by defaultz = torch.tensor([3.0], requires_grad=True)

a = x + yprint(a.requires_grad) # False

b = a + zprint(b.requires_grad) # True

a.requires_grad_(True)print(a.requires_grad) # True

1x

2y

3z

3a

6b

False

True

Page 26: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Autogradimport torch

x = torch.tensor(2.0, requires_grad=True)print(x) # tensor(2., requires_grad=True)

y = 3*x*x + 4*x + 5print(y) # tensor(25., grad_fn=<AddBackward0>)

print(x.grad) # None

y.backward() # compute gradients

print(x.grad) # tensor(16.)

x = 2.0

16x = 2

y = 3x2 + 4x + 5

∂y∂x

= 6x + 4

x = 225

Page 27: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Autogradimport torch

x = torch.ones(2, 2, requires_grad=True)y = x + 3z = y*y + 1f = z.mean()

print(f) # tensor(17., grad_fn=<AddBackward0>)

f.backward() # gradient computation

print(x.grad) # tensor([[2., 2.], [2., 2.]])

[1 11 1] [4 4

4 4] [17 1717 17] 17

y = x + 3 z = y2 + 1 f =14

4

∑i=1

zi

x y z f

∂f∂xi

=∂f∂zi

∂zi

∂yi

∂yi

∂xi

=14

× 2y × 1 =12

y =12

(x + 3)

when x = 1∂f∂xi

= 2

Page 28: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Autogradimport torch

x = torch.tensor(3.0)w = torch.tensor(4.0, requires_grad=True)b = torch.tensor(5.0, requires_grad=True)

print(x.item()) # 3.0print(w.item()) # 4.0print(b.item()) # 5.0

y = w * x + bprint(y.item()) # 17.0

y.backward() # gradient computation

print(w.grad.item()) # 3.0print(b.grad.item()) # 1.0 y = wx + b

∂y∂w

= x

∂y∂b

= 1

17 = 4 × 3 + 5

Page 29: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

no_grad()import torch

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

y = x + 1print(y.requires_grad) # True

# not to calculate the gradient for the variable zwith torch.no_grad():  z = x + 1  print(z.requires_grad) # False

Page 30: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Tensor vs VariableVariables are wrappers for Tensors.

Variable = Tensor + (= gradient computation)

Variables are the part of the autograd package.

The Variable API has been deprecated:

Variables are no longer necessary to use autograd with Tensors.

Autograd automatically supports Tensors with .requires_grad to True.

α

Page 31: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

In-Place OperationsIn PyTorch, many methods exist in two versions:: with / without an underscore(_) suffixex) add(…), add_(…)

The underscore(_) indicates in-place operations in PyTorch.

Methods that ends in an underscore(_) change the tensor in-place.

In general, in-place operations increase performance, but can lead to problems and worse performance in PyTorch.

It is recommended not to use in-place operations in most cases for efficiency.

https://stackoverflow.com/questions/52920098/what-does-the-underscore-suffix-in-pytorch-functions-mean

Page 32: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

In-Place Operations

https://pytorch.org/docs/stable/notes/autograd.html

Page 33: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

In-Place Operationsimport torch

a = torch.tensor([1, 2, 3])print(id(a)) # THE RESULT: (1)

a += 10print(id(a)) # THE RESULT: (2)

a = a + 10print(id(a)) # THE RESULT: (3)

# (1) and (2) are same, but (3) different from them.# (2): in-place operator (same object)# (3): a new object was created

a = torch.tensor([1, 2, 3])b = a + 10print(b) # tensor([11, 12, 13])print(a is b) # False

b = a.add(10)print(b) # tensor([11, 12, 13])print(a is b) # False

b = a.add_(10)print(b) # tensor([11, 12, 13])print(a is b) # True

Page 34: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Linear Regression

Page 35: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Reference

Page 36: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Formulationyi = wxi + b (i = 1,2,3,⋯, N)Model:

Error:

Li =12

( ̂yi − yi)2

known predicted

= ̂yi − (wxi + b) = ̂yi − wxi − b

∂Ei

∂w= − xi

∂Ei

∂b= − 1

Loss:

Ei = ̂yi − yi

=12

E2i

∂Li

∂w=

∂Li

∂Ei

∂Ei

∂w= Ei ⋅ (−xi) = − xiEi

∂Li

∂b=

∂Li

∂Ei

∂Ei

∂b= Ei ⋅ (−1) = − Ei

C =1N

N

∑i=1

LiCost:

∂C∂w

=1N

N

∑i=1

∂Li

∂w=

1N

N

∑i=1

(−xiEi)

∂C∂b

=1N

N

∑i=1

∂Li

∂b=

1N

N

∑i=1

(−Ei)

Page 37: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Pure NumPy Implementationimport numpy as np

# y = w*x+b: w=2, b=1x = np.array([1, 2, 3, 4, 5], dtype=‘float32')y = np.array([3, 5, 7, 9, 11], dtype=‘float32')

w = np.random.randn(1) # weightb = np.random.randn(1) # bias

for epoch in range(10000): # iteration

# prediction y_predicted = w * x + b error = y - y_predicted

# gradient computation (manually) w_grad = (-x * error).mean() b_grad = (-error).mean()

# update w -= 0.01 * w_grad b -= 0.01 * b_grad

print(w, b) # [2.00000001] [0.99999998]

∂C∂w

=N

∑i=1

(−xiEi)

∂C∂b

=N

∑i=1

(−Ei)

known predicted

Ei = ̂yi − yi

Page 38: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Pure NumPy Implementationimport numpy as np

# y = w*x+b: w=2, b=1x = np.array([1, 2, 3, 4, 5], dtype=‘float32')y = np.array([3, 5, 7, 9, 11], dtype=‘float32')

w = np.random.randn(1) # weightb = np.random.randn(1) # bias

for epoch in range(10000): # iteration

# prediction y_predicted = w * x + b error = y - y_predicted

# gradient computation (manually) w_grad = (-x * error).mean() b_grad = (-error).mean()

# update w -= 0.01 * w_grad b -= 0.01 * b_grad

print(w, b) # [2.00000001] [0.99999998]

wn+1 = wn − α∂E∂w

bn+1 = bn − α∂E∂b

Page 39: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

How does it work?• Exactly the same process as solving Ax = b with the steepest descent method

yi = axi + b (i = 1,2,3,⋯, N)Model: E =N

∑i=1

(yi − axi − b)2

argmina,b

E

∂E∂a

= 2N

∑i=1

(yi − axi − b) ⋅ (−xi) = 0

∂E∂b

= 2N

∑i=1

(yi − axi − b) ⋅ (−1) = 0

a (N

∑i=1

x2i ) + b (

N

∑i=1

xi) = (N

∑i=1

xiyi)

a (N

∑i=1

xi) + bN = (N

∑i=1

yi)

∑Ni=1 x2

i ∑Ni=1 xi

∑Ni=1 xi N [a

b]=∑N

i=1 xiyi

∑Ni=1 yiAx = b

f(x) =12

xTAx − bTx + c

∂f∂x

= 0

Ax = b a

b(2,1)

energy contours

steepest direction

initial random guess

learning rate

Page 40: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

LinearRegressionCommon.pyimport numpy as npimport torch

# how many points?N = 1000

# the ground-truth valuesW = 2.0 # weightB = 1.0 # bias

def Data(): np.random.seed(13) # random seed x = np.random.rand(N, 1) # input: 1D array noise = 0.1 * np.random.rand(N, 1) # noise: 1D array y = (W * x + B) + noise # outout that has some noise indices = np.arange(N) # point indices np.random.shuffle(indices) # shuffled indices train_indices = indices[:round(0.8*N)] # the first 80 random indices for train set valid_indices = indices[round(0.8*N):] # the the remaining indices for validation set x_train, y_train = x[train_indices], y[train_indices] x_valid, y_valid = x[valid_indices], y[valid_indices] return x_train, y_train, x_valid, y_valid

Page 41: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #1from LinearRegressionCommon import *

w = np.random.randn(1) # weightb = np.random.randn(1) # bias

x_train, y_train, _, _ = Data()

for epoch in range(10000): # iteration

# prediction y_predicted = w * x_train + b error = y_train - y_predicted

# gradient computation (manually) w_grad = (-x_train * error).mean() b_grad = (-error).mean()

# update w -= 0.01 * w_grad b -= 0.01 * b_grad

print(w, b) # [2.00322528] [1.0482713]

Page 42: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #2from LinearRegressionCommon import *

DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'

w = torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE)b = torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE)

x_train, y_train, _, _ = Data()

x_train = torch.from_numpy(x_train).float().to(DEVICE)y_train = torch.from_numpy(y_train).float().to(DEVICE)

for epoch in range(10000):

y_predicted = w * x_train + b error = y_train - y_predicted

cost = (error*error).mean() cost.backward() # gradient computation (automatically)

with torch.no_grad(): w -= 0.01 * w.grad b -= 0.01 * b.grad w.grad.zero_() b.grad.zero_()

print(w.item(), b.item()) # 2.003204107284546 1.048282265663147

Page 43: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #3from LinearRegressionCommon import *

DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'

w = torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE)b = torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE)

x_train, y_train, _, _ = Data()

x_train = torch.from_numpy(x_train).float().to(DEVICE)y_train = torch.from_numpy(y_train).float().to(DEVICE)

optimizer = torch.optim.SGD([w, b], lr=0.01)

for epoch in range(10000):

y_predicted = w * x_train + b error = y_train - y_predicted

cost = (error*error).mean() cost.backward()

optimizer.step() optimizer.zero_grad()

print(w.item(), b.item()) # 2.003204107284546 1.048282265663147

Page 44: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #4from LinearRegressionCommon import *

DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'

w = torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE)b = torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE)

x_train, y_train, _, _ = Data()

x_train = torch.from_numpy(x_train).float().to(DEVICE)y_train = torch.from_numpy(y_train).float().to(DEVICE)

CostFunc = torch.nn.MSELoss()optimizer = torch.optim.SGD([w, b], lr=0.01)

for epoch in range(10000):

y_predicted = w * x_train + b

cost = CostFunc(y_train, y_predicted) cost.backward()

optimizer.step() optimizer.zero_grad()

print(w.item(), b.item()) # 2.003204107284546 1.048282265663147

Page 45: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #5from LinearRegressionCommon import *

DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'

class Model(torch.nn.Module):

def __init__(self): super().__init__() self.w = torch.nn.Parameter(torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE)) self.b = torch.nn.Parameter(torch.randn(1, requires_grad=True, dtype=torch.float, device=DEVICE))

def forward(self, x): return self.w * x + self.b

x_train, y_train, _, _ = Data()

x_train = torch.from_numpy(x_train).float().to(DEVICE)y_train = torch.from_numpy(y_train).float().to(DEVICE)

model = Model().to(DEVICE)CostFunc = torch.nn.MSELoss()optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

1/2

Page 46: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #5for epoch in range(10000):

model.train()

y_predicted = model(x_train)

cost = CostFunc(y_train, y_predicted) cost.backward()

optimizer.step() optimizer.zero_grad()

print(model.state_dict())print(model.w, model.b)print(model.w.item(), model.b.item()) # 2.003204107284546 1.048282265663147

2/2

Page 47: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #6from LinearRegressionCommon import *

DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'

class Model(torch.nn.Module):

def __init__(self): super().__init__() self.layer = torch.nn.Linear(1, 1)

def forward(self, x): return self.layer(x)

x_train, y_train, _, _ = Data()x_train = torch.from_numpy(x_train).float().to(DEVICE)y_train = torch.from_numpy(y_train).float().to(DEVICE)

model = Model().to(DEVICE)CostFunc = torch.nn.MSELoss()optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

1/2

Page 48: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Example #6for epoch in range(10000):

model.train()

y_predicted = model(x_train)

cost = CostFunc(y_train, y_predicted) cost.backward()

optimizer.step() optimizer.zero_grad()

print(model.state_dict())print(model.layer.weight, model.layer.bias)print(model.layer.weight.item(), model.layer.bias.item()) # 2.003204107284546 1.048282265663147

2/2

Page 49: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Practical Example

https://medium.com/dsnet/linear-regression-with-pytorch-3dde91d60b50

Input Layer

Hidden Layer

Output Layer

Page 50: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

import numpy as npimport torch

class Model(torch.nn.Module):

def __init__(self): super().__init__() self.layer = torch.nn.Linear(3, 2) # 3: inputs, 2: outputs

def forward(self, x): return self.layer(x)

# input data: (temperature, rainfall, humidity)x_train = np.array([[73, 67, 43], [91, 88, 64], [87, 134, 58], [102, 43, 37], [69, 96, 70]])

# output data: (apples, oranges)y_train = np.array([[56, 70], [81, 101], [119, 133], [22, 37], [103, 119]])

x_train = torch.from_numpy(x_train).float()y_train = torch.from_numpy(y_train).float()

model = Model()CostFunc = torch.nn.MSELoss()optimizer = torch.optim.SGD(model.parameters(), lr=0.0001)

1/2

Practical Example

Page 51: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

for epoch in range(10000):

model.train()

y_predicted = model(x_train)

cost = CostFunc(y_train, y_predicted) cost.backward()

optimizer.step() optimizer.zero_grad()

# testprint(model(x_train))

# predictionx_test = np.array([[80, 70, 50]])x_test = torch.from_numpy(x_test).float();print(model(x_test))

2/2

Practical Example

Page 52: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

XOR Problem

Page 53: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

XOR Problem

https://mc.ai/intro-to-deep-learning-with-pytorch-part-1/

Non-linear Problem!

Page 54: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

XOR Problem• Minsky and Papert proved

that XOR problem cannot be solved by a single perceptron.

Page 55: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

torch.nn.Sequential• A container that contains other modules

• It concatenates the a series of modules.

• It applies each module in sequence to produce its output.

Page 56: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

import numpy as npimport torch

X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])Y = torch.FloatTensor([[0], [1], [1], [0]])

INPUT_DIM  = 2HIDDEN_DIM = 10OUTPUT_DIM = 1

model = torch.nn.Sequential(            torch.nn.Linear(INPUT_DIM, HIDDEN_DIM),            torch.nn.ReLU(),            torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM),            torch.nn.Sigmoid()) # MUST for non-linearity

CostFunc  = torch.nn.BCELoss() # Binary Cross Entropy Lossoptimizer = torch.optim.SGD(model.parameters(), lr=0.01)

for epoch in range(10000):    Y_predicted = model(X)    cost = CostFunc(Y_predicted, Y)    cost.backward()    optimizer.step()    model.zero_grad()

Y_predicted = model(X)

print(np.squeeze(Y_predicted.detach().numpy())) # [0.01351878 0.98831743 0.9887106  0.01278798]print(np.squeeze((Y_predicted+0.5).int().detach().numpy())) # [0 1 1 0]

Page 57: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

import numpy as npimport torch

X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])Y = torch.FloatTensor([[0], [1], [1], [0]])

INPUT_DIM  = 2HIDDEN_DIM = 10OUTPUT_DIM = 1

linear1 = torch.nn.Linear(INPUT_DIM, HIDDEN_DIM)actfnc1 = torch.nn.ReLU()linear2 = torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM)actfnc2 = torch.nn.Sigmoid() # MUST for non-linearity

model   = torch.nn.Sequential(linear1, actfnc1, linear2, actfnc2)

CostFunc  = torch.nn.BCELoss() # Binary Cross Entropy Lossoptimizer = torch.optim.SGD(model.parameters(), lr=0.01)

for epoch in range(10000):    Y_predicted = model(X)    cost = CostFunc(Y_predicted, Y)    cost.backward()    optimizer.step()    model.zero_grad()

Y_predicted = model(X)

print(np.squeeze(Y_predicted.detach().numpy())) # [0.02886132 0.9477684  0.9471025  0.07047193]print(np.squeeze((Y_predicted+0.5).int().detach().numpy())) # [0 1 1 0]

Page 58: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

class Model(torch.nn.Module)• It contains two main methods.

• The first method(__init__) defines layers components of the network.

• In the second method(forward) we wire the network and put every component in the desired order.

Page 59: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

import numpy as npimport torch

X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])Y = torch.FloatTensor([[0], [1], [1], [0]])

INPUT_DIM  = 2HIDDEN_DIM = 10OUTPUT_DIM = 1

class Model(torch.nn.Module):

    def __init__(self, INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM):        super().__init__()

        self.linear1 = torch.nn.Linear(INPUT_DIM, HIDDEN_DIM)        self.actfnc1 = torch.nn.ReLU()        self.linear2 = torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM)        self.actfnc2 = torch.nn.Sigmoid()

    def forward(self, x):        x = self.actfnc1( self.linear1(x) )        x = self.actfnc2( self.linear2(x) )        return x

model = Model(INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM)

CostFunc  = torch.nn.BCELoss() # Binary Cross Entropy Lossoptimizer = torch.optim.SGD(model.parameters(), lr=0.01)

.

.

.

Page 60: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

class Model(torch.nn.Module):

    def __init__(self, INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM):        super().__init__()

        self.linear1 = torch.nn.Linear(INPUT_DIM,  HIDDEN_DIM)        self.linear2 = torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM)

    def forward(self, x):        x = torch.relu   ( self.linear1(x) )        x = torch.sigmoid( self.linear2(x) )        return x

class Model(torch.nn.Module):

    def __init__(self, INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM):        super().__init__()

        self.linear1 = torch.nn.Linear(INPUT_DIM, HIDDEN_DIM)        self.actfnc1 = torch.nn.ReLU()        self.linear2 = torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM)        self.actfnc2 = torch.nn.Sigmoid()

    def forward(self, x):        x = self.actfnc1( self.linear1(x) )        x = self.actfnc2( self.linear2(x) )        return x

Method #1

Method #2

Page 61: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

class Model(torch.nn.Module):

    def __init__(self, INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM):        super().__init__()

        self.layer1 = torch.nn.Sequential( torch.nn.Linear(INPUT_DIM,  HIDDEN_DIM), torch.nn.ReLU()    )        self.layer2 = torch.nn.Sequential( torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM), torch.nn.Sigmoid() )

    def forward(self, x):        x = self.layer1(x)        x = self.layer2(x)        return x

class Model(torch.nn.Module):

    def __init__(self, INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM):        super().__init__()

        self.linear1 = torch.nn.Linear(INPUT_DIM,  HIDDEN_DIM)        self.linear2 = torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM)

    def layer1(self, x):        return torch.relu( self.linear1(x) )

    def layer2(self, x):        return torch.sigmoid( self.linear2(x) )

    def forward(self, x):        x = self.layer1(x)        x = self.layer2(x)        return x

Method #3

Method #4

Page 62: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

MNIST CIFAR-10/100

Page 63: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

torchvision• The torchvision package consists of popular datasets, model architectures, and common

image transformations for compute vision.

‣ torchvision.datasets: MNIST, Fashion-MNIST, CIFAR, etc.

‣ torchvision.io: video

‣ torchvision.models: classification, object detection, etc.

‣ torchvision.transforms: image transformations

Page 64: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

torchvision.transform.Compose• It creates a series of transformations.

• It compose several transformations together.

• All the transformations in the Compose are applied to the input data one by one.

import torchvision

normalize = torchvision.transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])

transformations = torchvision.transforms.Compose( [torchvision.transforms.RandomHorizontalFlip(), torchvision.transforms.RandomVerticalFlip(), torchvision.transforms.ToTensor(), normalize] )

Page 65: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Image Normalization• Normalization reduces the skewness, and helps to learn faster and better.

• If the given dataset is already in range [0.0, 1.0], you can skip the normalization.

• image = ( image - mean ) / std

• (mean, std) = (0.5, 0.5) ➜ image = ( image − 0.5 ) / 0.5: (0.0, 1.0) range ➜ (−1.0, +1.0) range

• torchvision.transforms.Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]) : Each 0.5 for each channel (Red, Green, Blue)

Page 66: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

import numpy as npimport torch, torchvision

pixels = np.random.randint(low=0, high=256, size=(5, 5, 3)) # 5x5 RGB imageprint(type(pixels)) # <class ‘numpy.ndarray'>print(np.min(pixels), ' ~ ', np.max(pixels)) # 0 ~ 255

pixels = pixels.astype('float32') / 255 # normalization: [0, 255] to [0.0, 1.0]print(type(pixels)) # <class ‘numpy.ndarray'>print(np.min(pixels), ' ~ ', np.max(pixels)) # 0.0 ~ 1.0

image = torch.from_numpy(pixels) # to a tensorprint(type(image)) # <class ‘torch.Tensor'>print(torch.min(image).item(), ' ~ ', torch.max(image).item()) # 0.0 ~ 1.0

# transforms = torch vision.transforms.ToTensor()transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])image = transforms(pixels) # apply transformationsprint(type(image)) # <class ‘torch.Tensor'>print(torch.min(image).item(), ' ~ ', torch.max(image).item()) # 0.0 ~ 1.0

transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor(), torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])image = transforms(pixels) # apply transformationsprint(type(image)) # <class ‘torch.Tensor'>print(torch.min(image).item(), ' ~ ', torch.max(image).item()) # -1.0 ~ 1.0

image = torchvision.transforms.functional.normalize(image, mean=(0.5, 0.5, 0.5), std=(0.5, 0.5, 0.5))print(torch.min(image).item(), ' ~ ', torch.max(image).item()) # -1.0 ~ 1.0

Page 67: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

MNISTimport numpy as npimport torch, torchvisionfrom matplotlib import pyplot as plt

transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])bs = 64 # batch size

train_dataset = torchvision.datasets.MNIST(root='./data/', train=True, transform=transforms, download=True)test_dataset = torchvision.datasets.MNIST(root='./data/', train=False, transform=transforms)

print(train_dataset.data.shape) # torch.Size([60000, 28, 28])print(len(train_dataset), len(test_dataset)) # 60000 10000

train_dataloader = torch.utils.data.DataLoader(dataset=train_dataset, BATCH_SIZE=bs, shuffle=True)test_dataloader = torch.utils.data.DataLoader(dataset=test_dataset, BATCH_SIZE=bs, shuffle=True)

print(train_dataloader.dataset.data.shape) # torch.Size([60000, 28, 28])print(len(train_dataloader), len(test_dataloader)) # 938=round(60000/bs), 157=round(10000/bs)print(len(train_dataloader.dataset), len(test_dataloader.dataset)) # 60000 10000

1/2

Page 68: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

MNISTfor batch_index, (images, labels) in enumerate(train_dataloader): print(labels.shape) # torch.Size([64]) print(images.shape) # torch.Size([64, 1, 28, 28]) print(images[0].shape) # torch.Size([1, 28, 28]) images = np.transpose(images, (0,2,3,1)) # channel first order -> channel last order print(images.shape) # torch.Size([64, 28, 28, 1]) print(images[0].shape) # torch.Size([28, 28, 1]) plt.imshow(images[0].reshape((28, 28)), cmap=‘gray') plt.show() break

for batch_index in range(len(train_dataset)): itr = iter(train_dataloader) images, labels = itr.next() print(labels.shape) # torch.Size([64]) print(images.shape) # torch.Size([64, 1, 28, 28]) print(images[0].shape) # torch.Size([1, 28, 28]) images = np.transpose(images, (0,2,3,1)) # channel first order -> channel last order print(images.shape) # torch.Size([64, 28, 28, 1]) print(images[0].shape) # torch.Size([28, 28, 1]) plt.imshow(images[0].reshape((28, 28)), cmap=‘gray') plt.show() break

2/2

Page 69: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

MNIST

Page 70: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-10import numpy as npimport torch, torchvisionfrom matplotlib import pyplot as plt

transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])bs = 64 # batch size

train_dataset = torchvision.datasets.CIFAR10(root='./data/CIFAR10', train=True, transform=transforms, download=True)test_dataset = torchvision.datasets.CIFAR10(root='./data/CIFAR10', train=False, transform=transforms)

print(train_dataset.data.shape) # (50000, 32, 32, 3)print(len(train_dataset), len(test_dataset)) # 50000 10000

train_dataloader = torch.utils.data.DataLoader(dataset=train_dataset, BATCH_SIZE=bs, shuffle=True)test_dataloader = torch.utils.data.DataLoader(dataset=test_dataset, BATCH_SIZE=bs, shuffle=True)

print(train_dataloader.dataset.data.shape) # (50000, 32, 32, 3)print(len(train_dataloader), len(test_dataloader)) # 782=round(50000/bs), 157=round(10000/bs)print(len(train_dataloader.dataset), len(test_dataloader.dataset)) # 50000 10000

1/2

Page 71: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-10for batch_index, (images, labels) in enumerate(train_dataloader): print(labels.shape) # torch.Size([64]) print(images.shape) # torch.Size([64, 3, 32, 32]) print(images[0].shape) # torch.Size([3, 32, 32]) images = np.transpose(images, (0,2,3,1)) # channel first order -> channel last order print(images.shape) # torch.Size([64, 32, 32, 3]) print(images[0].shape) # torch.Size([32, 32, 3]) plt.imshow(images[0].reshape((32, 32, 3))) plt.show() break

for batch_index in range(len(train_dataset)): itr = iter(train_dataloader) images, labels = itr.next() print(labels.shape) # torch.Size([64]) print(images.shape) # torch.Size([64, 3, 32, 32]) print(images[0].shape) # torch.Size([3, 32, 32]) images = np.transpose(images, (0,2,3,1)) # channel first order -> channel last order print(images.shape) # torch.Size([64, 32, 32, 3]) print(images[0].shape) # torch.Size([32, 32, 3]) plt.imshow(images[0].reshape((32, 32, 3))) plt.show() break

2/2

Page 72: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-10

Page 73: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-100import numpy as npimport torch, torchvisionfrom matplotlib import pyplot as plt

transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])bs = 64 # batch size

train_dataset = torchvision.datasets.CIFAR100(root='./data/CIFAR100', train=True, transform=transforms, download=True)test_dataset = torchvision.datasets.CIFAR100(root=‘./data/CIFAR100', train=False, transform=transforms)

print(train_dataset.data.shape) # (50000, 32, 32, 3)print(len(train_dataset), len(test_dataset)) # 50000 10000

train_dataloader = torch.utils.data.DataLoader(dataset=train_dataset, BATCH_SIZE=bs, shuffle=True)test_dataloader = torch.utils.data.DataLoader(dataset=test_dataset, BATCH_SIZE=bs, shuffle=True)

print(train_dataloader.dataset.data.shape) # (50000, 32, 32, 3)print(len(train_dataloader), len(test_dataloader)) # 782=round(50000/bs), 157=round(10000/bs)print(len(train_dataloader.dataset), len(test_dataloader.dataset)) # 50000 10000

1/2

Page 74: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-100for batch_index, (images, labels) in enumerate(train_dataloader): print(labels.shape) # torch.Size([64]) print(images.shape) # torch.Size([64, 3, 32, 32]) print(images[0].shape) # torch.Size([3, 32, 32]) images = np.transpose(images, (0,2,3,1)) # channel first order -> channel last order print(images.shape) # torch.Size([64, 32, 32, 3]) print(images[0].shape) # torch.Size([32, 32, 3]) plt.imshow(images[0].reshape((32, 32, 3))) plt.show() break

for batch_index in range(len(train_dataset)): itr = iter(train_dataloader) images, labels = itr.next() print(labels.shape) # torch.Size([64]) print(images.shape) # torch.Size([64, 3, 32, 32]) print(images[0].shape) # torch.Size([3, 32, 32]) images = np.transpose(images, (0,2,3,1)) # channel first order -> channel last order print(images.shape) # torch.Size([64, 32, 32, 3]) print(images[0].shape) # torch.Size([32, 32, 3]) plt.imshow(images[0].reshape((32, 32, 3))) plt.show() break

2/2

Page 75: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-100

Page 76: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Image Classification

Page 77: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Accuracy Evaluationimport torch

data = torch.randn(3, 10)print(data.numpy())

values, indices = torch.max(data.data, 0)print(values.numpy(), indices.numpy())

values, indices = torch.max(data.data, 1)print(values.numpy(), indices.numpy())

a = torch.tensor([1, 0, 1, 0, 1, 0])b = torch.tensor([1, 1, 1, 1, 1, 1])

c = ( a == b )print(c.numpy())print(c.sum().item())

accuracy = 100 * c.sum().item() / len(c)print(accuracy, '%')

1.3683

3 × 10 tensor-3.7282 -0.7150 -0.0891 -0.1889 -0.7913 -0.8726 -0.1097 1.9349 0.1923

-0.4194 -1.9478 0.7762 0.9239 0.3441 0.0412 0.5557 0.9953 2.2492 -0.4234

1.8282 0.1751 1.1866 0.7951 -0.8284 0.1223 -1.005 -1.3221 1.8195 -0.6615

Page 78: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Accuracy Evaluationimport torch

data = torch.randn(3, 10)print(data.numpy())

values, indices = torch.max(data.data, 0)print(values.numpy(), indices.numpy())

values, indices = torch.max(data.data, 1)print(values.numpy(), indices.numpy())

a = torch.tensor([1, 0, 1, 0, 1, 0])b = torch.tensor([1, 1, 1, 1, 1, 1])

c = ( a == b )print(c.numpy())print(c.sum().item())

accuracy = 100 * c.sum().item() / len(c)print(accuracy, '%')

1.3683

3 × 10 tensor-3.7282 -0.7150 -0.0891 -0.1889 -0.7913 -0.8726 -0.1097 1.9349 0.1923

-0.4194 -1.9478 0.7762 0.9239 0.3441 0.0412 0.5557 0.9953 2.2492 -0.4234

1.8282 0.1751 1.1866 0.7951 -0.8284 0.1223 -1.005 -1.3221 1.8195 -0.6615

1.8282

2

values

indices

0.1751

2

1.1866

2

0.9239

1

0.3441

1

0.1223

2

0.5557

1

0.9953

1

2.2492

1

0.1923

0

max.

Page 79: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Accuracy Evaluationimport torch

data = torch.randn(3, 10)print(data.numpy())

values, indices = torch.max(data.data, 0)print(values.numpy(), indices.numpy())

values, indices = torch.max(data.data, 1)print(values.numpy(), indices.numpy())

a = torch.tensor([1, 0, 1, 0, 1, 0])b = torch.tensor([1, 1, 1, 1, 1, 1])

c = ( a == b )print(c.numpy())print(c.sum().item())

accuracy = 100 * c.sum().item() / len(c)print(accuracy, '%')

1.3683

3 × 10 tensor-3.7282 -0.7150 -0.0891 -0.1889 -0.7913 -0.8726 -0.1097 1.9349 0.1923

-0.4194 -1.9478 0.7762 0.9239 0.3441 0.0412 0.5557 0.9953 2.2492 -0.4234

1.8282 0.1751 1.1866 0.7951 -0.8284 0.1223 -1.005 -1.3221 1.8195 -0.6615

1.9349

8

values

indices

2.2492

8

1.8282

0

max.

Page 80: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Accuracy Evaluationimport torch

data = torch.randn(3, 10)print(data.numpy())

values, indices = torch.max(data.data, 0)print(values.numpy(), indices.numpy())

values, indices = torch.max(data.data, 1)print(values.numpy(), indices.numpy())

a = torch.tensor([1, 0, 1, 0, 1, 0])b = torch.tensor([1, 1, 1, 1, 1, 1])

c = ( a == b )print(c.numpy())print(c.sum().item())

accuracy = 100 * c.sum().item() / len(c)print(accuracy, '%')

1

1

a

b

0

1

1

1

0

1

1

1

0

1

Page 81: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Accuracy Evaluationimport torch

data = torch.randn(3, 10)print(data.numpy())

values, indices = torch.max(data.data, 0)print(values.numpy(), indices.numpy())

values, indices = torch.max(data.data, 1)print(values.numpy(), indices.numpy())

a = torch.tensor([1, 0, 1, 0, 1, 0])b = torch.tensor([1, 1, 1, 1, 1, 1])

c = ( a == b )print(c.numpy())print(c.sum().item())

accuracy = 100 * c.sum().item() / len(c)print(accuracy, '%')

1

1

a

b

0

1

1

1

0

1

1

1

0

1

1c

c.sum().item() ➜ 3

0 1 0 1 0

Page 82: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Accuracy Evaluationimport torch

data = torch.randn(3, 10)print(data.numpy())

values, indices = torch.max(data.data, 0)print(values.numpy(), indices.numpy())

values, indices = torch.max(data.data, 1)print(values.numpy(), indices.numpy())

a = torch.tensor([1, 0, 1, 0, 1, 0])b = torch.tensor([1, 1, 1, 1, 1, 1])

c = ( a == b )print(c.numpy())print(c.sum().item())

accuracy = 100 * c.sum().item() / len(c)print(accuracy, '%')

1

1

a

b

0

1

1

1

0

1

1

1

0

1

1c

c.sum().item() ➜ 3

accuracy = 50.0 %

0 1 0 1 0

Page 83: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

MNIST Classificationimport torch, torchvision

DEVICE = 'cuda' if torch.cuda.is_available() else ‘cpu'

INPUT_DIM = 784 # = 28 x 28HIDDEN_DIM = 100OUTPUT_DIM = 10 # the number of classesTOTAL_EPOCHS = 10LEARNING_RATE = 0.01BATCH_SIZE = 2000

transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])

train_dataset = torchvision.datasets.MNIST(root='./data/', train=True, transform=transforms, download=True)test_dataset = torchvision.datasets.MNIST(root='./data/', train=False, transform=transforms)

train_dataloader = torch.utils.data.DataLoader(dataset=train_dataset, BATCH_SIZE=BATCH_SIZE, shuffle=True)test_dataloader = torch.utils.data.DataLoader(dataset=test_dataset, BATCH_SIZE=BATCH_SIZE, shuffle=True)

1/3

Page 84: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

MNIST Classificationclass Model(torch.nn.Module):

def __init__(self, INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM): super(Model, self).__init__()

self.layer1 = torch.nn.Linear(INPUT_DIM, HIDDEN_DIM) self.actfn1 = torch.nn.ReLU() self.layer2 = torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM)

def forward(self, x): y1 = self.actfn1(self.layer1(x)) y2 = self.layer2(y1) return y2

model = Model(INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM).to(DEVICE)CostFunc = torch.nn.CrossEntropyLoss()optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)

2/3

Page 85: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

MNIST Classificationfor epoch in range(TOTAL_EPOCHS): for images, labels in train_dataloader: images = images.reshape(-1, 784).to(DEVICE) # flattening

labels = labels.to(DEVICE) output = model(images) cost = CostFunc(output, labels) cost.backward() optimizer.step() optimizer.zero_grad() print('Cost: {:.4f}’.format(cost.item()))

# for the test, you don't need to do the gradient computation.with torch.no_grad(): correct = 0 for images, labels in test_dataloader: images = images.reshape(-1, 784).to(DEVICE) # flattening labels = labels.to(DEVICE) output = model(images) _, predicted = torch.max(output.data, 1) correct += (predicted == labels).sum().item() print('Accuracy: {} %'.format(100 * correct / 10000)) # Accuracy: 97.36 %

3/3

Page 86: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-10 Classificationimport torch, torchvision

DEVICE = 'cuda' if torch.cuda.is_available() else ‘cpu'

INPUT_DIM = 3072 # = 32 x 32 x 3HIDDEN_DIM = 100OUTPUT_DIM = 10 # the number of classesTOTAL_EPOCHS = 10LEARNING_RATE = 0.01BATCH_SIZE = 2000

transforms = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])

train_dataset = torchvision.datasets.CIFAR10(root='./data/CIFAR10', train=True, transform=transforms, download=True)test_dataset = torchvision.datasets.CIFAR10(root=‘./data/CIFAR10', train=False, transform=transforms)

train_dataloader = torch.utils.data.DataLoader(dataset=train_dataset, BATCH_SIZE=BATCH_SIZE, shuffle=True)test_dataloader = torch.utils.data.DataLoader(dataset=test_dataset, BATCH_SIZE=BATCH_SIZE, shuffle=True)

1/3

Page 87: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-10 Classificationclass Model(torch.nn.Module):

def __init__(self, INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM): super(Model, self).__init__()

self.layer1 = torch.nn.Linear(INPUT_DIM, HIDDEN_DIM) self.actfn1 = torch.nn.ReLU() self.layer2 = torch.nn.Linear(HIDDEN_DIM, OUTPUT_DIM)

def forward(self, x): y1 = self.actfn1(self.layer1(x)) y2 = self.layer2(y1) return y2

model = Model(INPUT_DIM, HIDDEN_DIM, OUTPUT_DIM).to(DEVICE)CostFunc = torch.nn.CrossEntropyLoss()optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)

2/3

Page 88: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CIFAR-10 Classificationfor epoch in range(TOTAL_EPOCHS): for images, labels in train_dataloader: images = images.reshape(-1, 3072).to(DEVICE) # flattening labels = labels.to(DEVICE) output = model(images) cost = CostFunc(output, labels) cost.backward() optimizer.step() optimizer.zero_grad() print('Cost: {:.4f}’.format(cost.item()))

# for the test, you don't need to do the gradient computation.with torch.no_grad(): correct = 0 for images, labels in test_dataloader: images = images.reshape(-1, 3072).to(DEVICE) # flattening labels = labels.to(DEVICE) output = model(images) _, predicted = torch.max(output.data, 1) correct += (predicted == labels).sum().item() print('Accuracy: {} %'.format(100 * correct / 10000)) # Accuracy: 21.15 %

3/3

Page 89: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Problems• Slow convergence

• Too low accuracy

• Flattened input data

• In this process, the spatial information is lost.

• We need more accurate and efficient method.

Page 90: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CNN

Page 91: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CNN: Convolutional Neural Network

input image feature maps (n channels)

feature maps (m channels)

activation maps (m channels)

FC layer softmax

dog

cat

bird

!

convolution with n kernels pooling

convolution with m kernels pooling

activation maps (n channels)

flatten

Page 92: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CNN for MNISTclass Model(torch.nn.Module):

def __init__(self):

super(Model, self).__init__()

# (bs, 28, 28, 1) -> conv -> (bs, 28, 28, 6) -> pool -> (bs, 14, 14, 6) self.layer1 = torch.nn.Sequential( torch.nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=2), torch.nn.ReLU(), torch.nn.MaxPool2d(kernel_size=2, stride=2), torch.nn.Dropout(1 - keep_ratio))

# (bs, 14, 14, 6) -> conv -> (bs, 14, 14, 9) -> pool -> (bs, 7, 7, 9) self.layer2 = torch.nn.Sequential( torch.nn.Conv2d(in_channels=6, out_channels=9, kernel_size=3, stride=1, padding=1), torch.nn.ReLU(), torch.nn.MaxPool2d(kernel_size=2, stride=2), torch.nn.Dropout(1 - keep_ratio))

1/2

Page 93: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CNN for MNIST # FC: 7x7x9 -> 256 self.layer3 = torch.nn.Sequential( torch.nn.Linear(7*7*9, 256), torch.nn.ReLU())

# FC: 256 -> 128 self.layer4 = torch.nn.Sequential( torch.nn.Linear(256, 128), torch.nn.ReLU())

# FC: 128 -> 10 self.layer5 = torch.nn.Linear(128, 10)

def forward(self, x): x = self.layer1(x) x = self.layer2(x) x = x.reshape(x.size(0), -1) # flattening: 7x7x9 -> 441(=7x7x9) x = self.layer3(x) x = self.layer4(x) x = self.layer5(x) return x

2/2

Page 94: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CNN for CIFAR-10class Model(torch.nn.Module):

def __init__(self):

super(Model, self).__init__()

# (bs, 32, 32, 3) -> conv -> (bs, 32, 32, 6) -> pool -> (bs, 16, 16, 6) self.layer1 = torch.nn.Sequential( torch.nn.Conv2d(in_channels=3, out_channels=6, kernel_size=5, stride=1, padding=2), torch.nn.ReLU(), torch.nn.MaxPool2d(kernel_size=2, stride=2), torch.nn.Dropout(1 - keep_ratio))

# (bs, 16, 16, 6) -> conv -> (bs, 16, 16, 9) -> pool -> (bs, 8, 8, 9) self.layer2 = torch.nn.Sequential( torch.nn.Conv2d(in_channels=6, out_channels=9, kernel_size=3, stride=1, padding=1), torch.nn.ReLU(), torch.nn.MaxPool2d(kernel_size=2, stride=2), torch.nn.Dropout(1 - keep_ratio))

1/2

Page 95: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

CNN for CIFAR-10 # FC: 8x8x9 -> 256 self.layer3 = torch.nn.Sequential( torch.nn.Linear(8*8*9, 256), torch.nn.ReLU())

# FC: 256 -> 128 self.layer4 = torch.nn.Sequential( torch.nn.Linear(256, 128), torch.nn.ReLU())

# FC: 128 -> 10 self.layer5 = torch.nn.Linear(128, 10)

def forward(self, x): x = self.layer1(x) x = self.layer2(x) x = x.reshape(x.size(0), -1) # flattening: 7x7x9 -> 441(=7x7x9) x = self.layer3(x) x = self.layer4(x) x = self.layer5(x) return x

2/2

Page 96: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Autoencoder

Page 97: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Please refer to the following.

Page 98: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

GAN

Page 99: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Please refer to the following.

Page 100: PyTorch - Wanho Choiwanochoi.com/lecture/PyTorch.pdf · 2019-10-11 · torch.tensor() allocates a new memory by copying the ndarray. • torch.tensor() is an alias for the default

Q & A