이번 포스팅에서는 PyTorch 의 Torchvision.datasets 모듈에 내장되어 있는 데이터 중에서 하나를 가져와서 시각화해보는 작업을 소개하겠습니다. 

 

(1) Torchvision.DataSets 모듈 소개

(2) torchvision.datasets 모듈에서 CIFAR10 데이터셋 다운로드하고 압축풀기

(3) torchvision.datasets 모듈에서 가져온 CIFAR10 데이터셋 살펴보기

(4) CIFAR10 이미지 시각화하기

 

 

 

(1) Torchvision.DataSets 모듈 소개

 

PyTorch 패키지의 Torchvision 은 torchvision.datasets 모듈에 다양한 종류의 내장된 데이터셋(built-in datasets)과 편리한 유틸리티 클래스(utility classes)를 제공합니다. 

 

Torchvision 의 DataSets 모듈에 내장되어 있는 데이터셋의 과업 목적 종류별로 몇가지 예를 들어보면요,  

 

- Image Classification: CIFAR10, CIFAR100, MNIST, FashionMNIST, Caltech101, ImageNet 등

- Image Detection or Segmentation: CocoDetection, celebA, Cityscapes, VOCSegmentation 등

- Optical Flow: FlyingChairs, FlyingThings3D, HD1K, KittiFlow, Sintel 등

- Stereo Matching: CarlaStereo, Kitti2015Stereo, CREStereo, SintelStereo 등

- Image Pairs: LFWPairs, PhotoTour

- Image Captioning: CocoCaptions

- Video Classification: HMDB51, Kinetics, UCF101

- Base Classes for Custom Datasets: DatasetFolder, ImageFolder, VisionDataset

 

등이 있습니다. 

 

모든 데이터셋이 PyTorch의 torch.utils.data.Dataset 의 하위클래스이므로 __getitem__ 과 __len__ 메소드를 가지고 있으며, torch.utils.data.DataLoader 메소드를 적용해서 batch size 나 shuffle 여부 등의 매개변수를 적용해서 데이터 로딩을 편리하게 할 수 있습니다. 

 

[참고 사이트] https://pytorch.org/vision/stable/datasets.html

 

 

 

(2) torchvision.datasets 모듈에서 CIFAR10 데이터셋 다운로드하고 압축풀기

 

아래와 같이 CIFAR10 을 다운로드할 경로를 설정해주고, datasets.CIFAR10(directory, download=True, train=True) 처럼 학습에 사용한다는 옵션(train=True) 부여해서 다운로드하면 됩니다. 데이터셋 크기가 꽤 큰 편이어서 시간이 좀 걸려요. 

 

import torch
from torchvision import datasets
import numpy as np

print(torch.__version__)
# 1.10.0


## downloading CIFAR10 data from torchvision.datasets
## reference: https://pytorch.org/vision/stable/datasets.html
img_dir = '~/Downloads/CIFAR10'  # set with yours
cifar10 = datasets.CIFAR10(
    img_dir, download=True, train=True)

# Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to /Users/lhongdon/Downloads/CIFAR10/cifar-10-python.tar.gz
# 170499072/? [09:27<00:00, 300518.28it/s]

# Extracting /Users/lhongdon/Downloads/CIFAR10/cifar-10-python.tar.gz to /Users/lhongdon/Downloads/CIFAR10

 

[참고 사이트] CIFAR10 데이터셋에 대한 자세한 소개는 http://www.cs.toronto.edu/~kriz/cifar.html 를 참고하세요. 

 

 

 

(3) torchvision.datasets 모듈에서 가져온 CIFAR10 데이터셋 살펴보기

 

CIFAR10 데이터셋은 이미지 데이터(data)와 분류 범주 (targets) 의 dictionary 로 되어있습니다. 

 

## getting image data and targets
cifar10_data = cifar10.data
cifar10_targets = cifar10.targets


## information on FMNIST dataset
print('CIFAR10 data shape:', cifar10_data.shape) # array
print('CIFAR10 targets shape:', len(cifar10_targets)) # list

# CIFAR10 data shape: (50000, 32, 32, 3)
# CIFAR10 targets shape: 50000

 

 

이미지 데이터는 32 (폭) x 32 (높이) x 3 (RGB 채널) 크기의 데이터가 50,000 개 들어있는 다차원 배열 (ND array) 입니다. 

 

## ND-array
cifar10_data[0]

# array([[[ 59,  62,  63],
#         [ 43,  46,  45],
#         [ 50,  48,  43],
#         ...,
#         [158, 132, 108],
#         [152, 125, 102],
#         [148, 124, 103]],

#        [[ 16,  20,  20],
#         [  0,   0,   0],
#         [ 18,   8,   0],
#         ...,
#         [118,  84,  50],
#         [120,  84,  50],
#         [109,  73,  42]],
#        ...,
#        [[177, 144, 116],
#         [168, 129,  94],
#         [179, 142,  87],
#         ...,
#         [216, 184, 140],
#         [151, 118,  84],
#         [123,  92,  72]]], dtype=uint8)

 

 

10개의 범주를 가지는 목표 범주(targets)는 0~9 의 정수가 들어있는 리스트(list) 입니다. cicar10.classes 로 각 범주의 이름에 접근할 수 있습니다. 

 

## list
cifar10_targets[:20]

# [6, 9, 9, 4, 1, 1, 2, 7, 8, 3, 4, 7, 7, 2, 9, 9, 9, 3, 2, 6]


## target_labels : target_classes
for i in zip(np.unique(cifar10_targets), cifar10.classes):
    print(i[0], ':', i[1])
    
# 0 : airplane
# 1 : automobile
# 2 : bird
# 3 : cat
# 4 : deer
# 5 : dog
# 6 : frog
# 7 : horse
# 8 : ship
# 9 : truck

 

 

 

(4) CIFAR10 이미지 시각화하기

 

10개 범주의 각 클래스에서 10개의 이미지를 가져와서 10 x 10 grid 에 subplots 을 그려보겠습니다. 

 

import matplotlib.pyplot as plt
import numpy as np

row_num, col_num = len(cifar10.classes), 10
fig, ax = plt.subplots(row_num, col_num, figsize=(10, 10))

for label_class, plot_row in enumerate(ax):
    ## array of index per each target classes
    label_idx = np.where(np.array(cifar10_targets) == label_class)[0]
    for i, plot_cell in enumerate(plot_row):
        if i == 0:
            ## adding class label at ylabel axis
            plot_cell.set_ylabel(cifar10.classes[label_class], 
                                 fontsize=12)
            ## no ticks at x, y axis
            plot_cell.set_yticks([])
            plot_cell.set_xticks([])
            idx = label_idx[i]
            img = cifar10_data[idx]
            plot_cell.imshow(img)
        else:
            # turn off axis
            plot_cell.axis('off')
            # pick the first 10 images from each classes
            idx = label_idx[i]
            img = cifar10_data[idx]
            plot_cell.imshow(img)
            
# Adjust the padding between and around subplots     
plt.tight_layout(pad=0.5)

 

CIFAR10 plots

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

지난 포스팅에서는 두 개의 PyTorch 텐서를 합치기 (concatenating two PyTorch tensors) 에 대해서 다루었습니다.
(바로가기 ==> https://rfriend.tistory.com/781 )

 

이번 포스팅에서는 반대로 한 개의 PyTorch 텐서를 복수 개로 나누기 (splitting a tensor into multiple tensors) 하는 방법을 소개하겠습니다. 

 

(1) 하나의 PyTorch 텐서를 위-아래의 복수 개의 텐서로 나누기
      (splitting a tensor into multiple tensors vertically)

      : torch.vsplit(tensor, indices_or_sections), torch.split(tensor, split_size_or_sections, dim=0)

(2) 하나의 PyTorch 텐서를 좌-우의 복수 개의 텐서로 나누기
     (splitting a tensor into multiple tensors horizontally) 

     : torch.hsplit(tensor, indices_or_sections), torch.split(tensor, split_size_or_sections, dim=1)

 

 

먼저 PyTorch 텐서를 위-아래의 수직으로 복수 개의 텐서로 나누기를 해보겠습니다. 

 

(1) 하나의 PyTorch 텐서를 위-아래의 복수 개의 텐서로 나누기
      (splitting a tensor into multiple tensors vertically)
      : torch.vsplit(tensor, indices_or_sections), torch.split(tensor, split_size_or_sections, dim=0)

 

torch.vsplit() 과 torch.split(dim=0) 의 경우 매개변수의 사용법에 차이가 있습니다. 

 

torch.vsplit(tensor, indices_or_sections) 은 indices_or_sections 매개변수로 input tensor 의 행(row) 을 몇으로 나눌지(indices)를, 그래서 결국 몇 개의 텐서로 나누고 싶은지를 지정해주는 것입니다. 가령, 아래 예의 경우 텐서 z 는 Size[6, 6] 인데요, indices_or_sections=2 로 입력할 경우, 6/2 = 3 으로서 각 3개의 균등한 행을 가지는 2개의 텐서로 나누어줍니다. 

 

반면에, torch.split(tensor, split_size_or_sections, dim=0) 은 (a) "dim=0" 으로 행에 대해서 수직으로 텐서 분리를 수행하라고 지정을 해주어야 하고, (b) split_size_or_sections 에서 지정한 숫자만큼의 크기(split_size)로 행을 가지도록 텐서를 분리해줍니다. 가령, 아래 예의 Size[6, 6] 의 텐서 z에 대해서 torch.split(z, 3, dim=0) 으로서 split_size_or_sections = 3 을 입력해주면 각 행을 3개씩의 크기(split size)로 가지는 텐서로 분리를 해줍니다. (원래 텐서에 행이 6개 있는데, 분리할 텐서는 각 3개씩 행을 가지라고 했으므로 결과적으로 총 2개의 텐서로 분리가 됨). 

 

PyTorch: splitting a tensor into multiple tensors vertically

 

예제로 사용할 Size[6, 6]의 PyTorch 텐서 z 를 만들어보겠습니다. 

 

z = torch.arange(36).reshape(6,6)

print(z)
# tensor([[ 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, 25, 26, 27, 28, 29],
#         [30, 31, 32, 33, 34, 35]])

 

 

 

텐서 z 를 torch.vsplit(z, 2) 를 사용해서 2개의 텐서로 분리를 해보겠습니다. (indices_or_sections = 2 로 입력하면 6을 2로 나누어서 3개씩의 행을 가지는 2개의 텐서로 분리해줌)

 

## vsplit(): Splits input, a tensor with two or more dimensions, 
## into multiple tensors vertically according to indices_or_sections.
torch.vsplit(z, 2)

# (tensor([[ 0,  1,  2,  3,  4,  5],
#          [ 6,  7,  8,  9, 10, 11],
#          [12, 13, 14, 15, 16, 17]]),
#  tensor([[18, 19, 20, 21, 22, 23],
#          [24, 25, 26, 27, 28, 29],
#          [30, 31, 32, 33, 34, 35]]))

 

 

이때 반환되는 아웃풋은 두 개의 텐서를 묶어놓은 튜플(tuple)입니다. 

 

type(torch.vsplit(z, 2))
# tuple

 

 

위에서 2개로 분리한 텐서들의 묶음인 튜플에 원하는 부분의 텐서에 접근하기 위해서는 인덱싱(indexing)을 사용하면 됩니다. 아래 예에서는 2개로 분리한 텐서의 각 첫번째와 두번째 튜플에 접근해서 가져와봤습니다.  

 

## accessing a tensor after splitting
torch.vsplit(z, 2)[0]

# tensor([[ 0,  1,  2,  3,  4,  5],
#         [ 6,  7,  8,  9, 10, 11],
#         [12, 13, 14, 15, 16, 17]])


torch.vsplit(z, 2)[1]

# tensor([[18, 19, 20, 21, 22, 23],
#         [24, 25, 26, 27, 28, 29],
#         [30, 31, 32, 33, 34, 35]])

 

 

 

torch.split(z, 3, dim=0) 은 dim=0 으로 지정을 해주면 됩니다. 

 

## Splits the tensor into chunks. 
## Each chunk is a view of the original tensor.
torch.split(z, 3, dim=0)

# (tensor([[ 0,  1,  2,  3,  4,  5],
#          [ 6,  7,  8,  9, 10, 11],
#          [12, 13, 14, 15, 16, 17]]),
#  tensor([[18, 19, 20, 21, 22, 23],
#          [24, 25, 26, 27, 28, 29],
#          [30, 31, 32, 33, 34, 35]]))

 

 

## split_size_or_sections : list of sizes for each chunk
torch.split(z, [1,2,3], dim=0)

# (tensor([[0, 1, 2, 3, 4, 5]]),
#  tensor([[ 6,  7,  8,  9, 10, 11],
#          [12, 13, 14, 15, 16, 17]]),
#  tensor([[18, 19, 20, 21, 22, 23],
#          [24, 25, 26, 27, 28, 29],
#          [30, 31, 32, 33, 34, 35]]))

 

 

 

 

 

(2) 하나의 PyTorch 텐서를 좌-우의 복수 개의 텐서로 나누기
     (splitting a tensor into multiple tensors horizontally) 

     : torch.hsplit(tensor, indices_or_sections), torch.split(tensor, split_size_or_sections, dim=1)

 

이번에는 하나의 PyTorch 텐서를 좌-우 수평으로해서 복수 개의 텐서로 나누어볼텐데요, 역시 torch.hsplit() 과 torch.split(dim=1) 의 경우 매개변수의 사용법에 차이가 있습니다.

 

torch.hsplit(tensor, indices_or_sections) 은 indices_or_sections 매개변수로 input tensor 의 열(column) 을 몇으로 나눌지(indices)를, 그래서 결국 몇 개의 텐서로 나누고 싶은지를 지정해주는 것입니다. 가령, 아래 예의 경우 텐서 z 는 Size[6, 6] 인데요, indices_or_sections=2 로 입력할 경우, 6/2 = 3 으로서 각 3개의 균등한 열을 가지는 2개의 텐서로 좌-우로 나누어줍니다. 

 

반면에, torch.split(tensor, split_size_or_sections, dim=1) 은 (a) "dim=1" 으로 행에 대해서 수평으로 텐서 분리를 수행하라고 지정을 해주어야 하고, (b) split_size_or_sections 에서 지정한 숫자만큼의 크기(split_size)로 행을 가지도록 텐서를 분리해줍니다. 가령, 아래 예의 Size[6, 6] 의 텐서 z에 대해서 torch.split(z, 3, dim=0) 으로서 split_size_or_sections = 3 을 입력해주면 각 열(column)을 3개씩의 크기(split size)로 가지는 텐서로 분리를 해줍니다. (원래 텐서에 열이 6개 있는데, 분리할 텐서는 각 3개씩 열을 가지라고 했으므로 결과적으로 총 2개의 텐서로 분리가 됨). 

 

 

 

PyTorch hsplit(), split(dim=1) : splitting a tensor into multiple tensors horizontally

 

 

## hsplit(): Splits input, a tensor with two or more dimensions, 
## into multiple tensors horizontally according to indices_or_sections.

torch.hsplit(z, 2)

# (tensor([[ 0,  1,  2],
#          [ 6,  7,  8],
#          [12, 13, 14],
#          [18, 19, 20],
#          [24, 25, 26],
#          [30, 31, 32]]),
#  tensor([[ 3,  4,  5],
#          [ 9, 10, 11],
#          [15, 16, 17],
#          [21, 22, 23],
#          [27, 28, 29],
#          [33, 34, 35]]))

 

 

torch.hsplit(tensor, indices_or_sections) 에서 indices_or_sections 매개변수로 indices 넣어줄 때는 정수로 나누어지는 값을 넣어주어야 합니다. 가령, 아래 예에서는 Size[6, 6] 의 텐서에서 dimension 1 의 방향으로 4개 나누라고 지정해었더닌 6을 4로 나눌 수 없다면서 RunTimeError: torch.hsplit attempted to split along dimension 1, but size of the dimension 6 is not divisible by the split_size 4! 라는 에러가 발생했습니다. 

 

## RuntimeError
torch.hsplit(z, 4)

# RuntimeError: torch.hsplit attempted to split along dimension 1, 
# but the size of the dimension 6 is not divisible by the split_size 4!

 

 

 

torch.split(z, 3, dim=1) 에서는 dim=1 로 차원을 지정해주면 됩니다. 

 

torch.split(z, 3, dim=1)

# (tensor([[ 0,  1,  2],
#          [ 6,  7,  8],
#          [12, 13, 14],
#          [18, 19, 20],
#          [24, 25, 26],
#          [30, 31, 32]]),
#  tensor([[ 3,  4,  5],
#          [ 9, 10, 11],
#          [15, 16, 17],
#          [21, 22, 23],
#          [27, 28, 29],
#          [33, 34, 35]]))

 

 

 

torch.split() 함수에서 split_size_or_sections 매개변수에 리스트로 해서 나누고 싶은 텐서의 크기 (split_size_sections) 를 복수개로 지정해줄 수도 있습니다. 아래의 예에서는 Size[6, 6]의 텐서를 열의 개수를 1개, 2개, 3개를 가지는 총 3개의 텐서로 분리(split_size_or_sections = [1, 2, 3])해 본 것입니다. 매우 편리한 기능입니다! 

 

## split_size_or_sections : list of sizes for each chunk
torch.split(z, [1,2,3], dim=1)

# (tensor([[ 0],
#          [ 6],
#          [12],
#          [18],
#          [24],
#          [30]]),
#  tensor([[ 1,  2],
#          [ 7,  8],
#          [13, 14],
#          [19, 20],
#          [25, 26],
#          [31, 32]]),
#  tensor([[ 3,  4,  5],
#          [ 9, 10, 11],
#          [15, 16, 17],
#          [21, 22, 23],
#          [27, 28, 29],
#          [33, 34, 35]]))

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 두 개의 PyTorch 텐서를 하나로 합치는 방법을 소개하겠습니다. 

 

(1) torch.cat((x, y), dim=0), torch.concat(dim=0): 두 개의 텐서를 위-아래로 합치기 (vertically, row wise)

(2) torch.cat((x, y), dim=1), torch.concat(dim=1): 두 개의 텐서를 좌-우로 합치기 (horizontally, column wise)

(3) torch.vstack((x, y)), torch.row_stack(): 두 개의 텐서를 위-아래로 합치기 (vertically, row wise)

(4) torch.hstack((x, y)), torch.column_stack(): 두 개의 텐서를 좌-우로 합치기 (horizontally, column wise)

(5) torch.stack((x, y), dim=0): 두 개의 텐서를 새 차원(new dimension)으로 위-아래로 합치기

(6) torch.stack((x, y)dim=1): 두 개의 텐서를 새 차원(new dimension)으로 좌-우로 합치기

 

 

 

두 개의 텐서를 위-아래로 합치기 (vertically, row wise concatenation) 는 torch.cat(dim=0), torch.concat(dim=0), torch.vstack(), torch.row_stack() 로 수행 가능합니다. 

 

PyTorch: concatenate tensors in sequence vertically, row-wise

 

 

 

두 개의 텐서를 좌-우로 합치기 (horizontally, column wise concatenation) 는 torch.cat(dim=1), torch.concat(dim=1), torch.hstack(), torch.column_stack() 로 수행 가능합니다. 

 

PyTorch: concatenating tensors in sequence horizontally, column-wise

 

 

 

torch.stack((x, y), dim=0) 은 새로운 차원(new dimension)을 추가해서 두 개의 텐서를 위-아래로 합쳐주며, 

torch.stack((x, y), dim=1) 은 새로운 차원을 추가해서 두 개의 텐서를 좌-우로 합쳐주는 차이점이 있습니다. 

 

 

 

 

예제로 사용할 두 개의 PyTorch 텐서를 만들어보겠습니다. 

 

import torch

x = torch.arange(12).reshape(3, 4)
y = torch.arange(12, 24).reshape(3, 4)

print(x)
# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11]])

print(y)
# tensor([[12, 13, 14, 15],
#         [16, 17, 18, 19],
#         [20, 21, 22, 23]])

 

 

 

(1) torch.cat(dim=0), torch.concat(dim=0)
        : 두 개의 텐서를 위-아래로 합치기 (vertically, row wise)

 

## torch.cat(): concatenating in the axis 0
torch.cat((x, y), dim=0) # or torch.concat((x, y), 0)

# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19],
#         [20, 21, 22, 23]])


torch.cat((x, y), dim=0).shape
# torch.Size([6, 4])

 

 

 

(2) torch.cat((x, y), dim=1), torch.concat(dim=1)
        : 두 개의 텐서를 좌-우로 합치기 (horizontally, column wise)

 

## concatenating in the axis 1
torch.cat((x, y), dim=1) # or torch.concat((x, y), 1)

# tensor([[ 0,  1,  2,  3, 12, 13, 14, 15],
#         [ 4,  5,  6,  7, 16, 17, 18, 19],
#         [ 8,  9, 10, 11, 20, 21, 22, 23]])


torch.cat((x, y), dim=1).shape
# torch.Size([3, 8])

 

 

 

(3) torch.vstack((x, y)), torch.row_stack()
        : 
두 개의 텐서를 위-아래로 합치기 (vertically, row wise)

 

## Stack tensors in sequence vertically (row wise).
torch.vstack((x, y))

# tensor([[ 0,  1,  2,  3],
#         [ 4,  5,  6,  7],
#         [ 8,  9, 10, 11],
#         [12, 13, 14, 15],
#         [16, 17, 18, 19],
#         [20, 21, 22, 23]])


## or equivalently
torch.row_stack((x, y))


torch.row_stack((x, y)).shape
# torch.Size([6, 4])

 

 

 

(4) torch.hstack((x, y)), torch.column_stack()
        : 두 개의 텐서를 좌-우로 합치기 (horizontally, column wise)

 

## Stack tensors in sequence horizontally (column wise).
torch.hstack((x, y))

# tensor([[ 0,  1,  2,  3, 12, 13, 14, 15],
#         [ 4,  5,  6,  7, 16, 17, 18, 19],
#         [ 8,  9, 10, 11, 20, 21, 22, 23]])


## or equivalently
torch.column_stack((x, y))


torch.column_stack((x, y)).shape
# torch.Size([3, 8])

 

 

 

(5) torch.stack((x, y), dim=0)
        : 두 개의 텐서를 새 차원(new dimension)으로 위-아래로 합치기

 

위의 (1)~(4)번의 메소드 대비해서 torch.stack((x, y), dim=0) 은 새로운 차원(new dimension)을 추가해서 두 개의 텐서를 위-아래로 합쳐주며, torch.stack((x, y), dim=1) 은 새로운 차원을 추가해서 두 개의 텐서를 좌-우로 합쳐주는 차이점이 있습니다. (1)~(4)번의 텐서 합치기를 했을 때의 shape 과 (5)~(6)번의 텐서 합치기 후의 shape 을 유심히 비교해보시기 바랍니다.  

 

## Concatenates a sequence of tensors along a new dimension.
## stack with axis=0 (vertically, row-wise)
torch.stack((x, y), dim=0) # axis=0

# tensor([[[ 0,  1,  2,  3],
#          [ 4,  5,  6,  7],
#          [ 8,  9, 10, 11]],

#         [[12, 13, 14, 15],
#          [16, 17, 18, 19],
#          [20, 21, 22, 23]]])

## new dimension
torch.stack((x, y), dim=0).shape
# torch.Size([2, 3, 4])

 

 

 

(6) torch.stack((x, y)dim=1)
        : 두 개의 텐서를 새 차원(new dimension)으로 좌-우로 합치기

 

## stack with axis=1 (horizontally, column-wise)
torch.stack((x, y), dim=1) # axis=1

# tensor([[[ 0,  1,  2,  3],
#          [12, 13, 14, 15]],

#         [[ 4,  5,  6,  7],
#          [16, 17, 18, 19]],

#         [[ 8,  9, 10, 11],
#          [20, 21, 22, 23]]])


## new dimension
torch.stack((x, y), dim=1).shape
# torch.Size([3, 2, 4])

 

 

다음 포스팅에서는 이어서 '하나의 PyTorch 텐서를 복수개의 텐서로 나누기 (splitting a PyTorch tensor into multiple tensors)' 에 대해서 소개하겠습니다.  

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 PyTorch 에서 인덱싱(indexing)과 슬라이싱(slicing)을 이용해서 텐서 내 원하는 위치의 원소 부분집합을 가져오는 방법을 소개하겠습니다. PyTorch 로 딥러닝을 할 때 인풋으로 사용하는 데이테셋이 다차원의 행렬인 텐서인데요, 인덱싱과 슬라이싱을 자주 사용하기도 하고, 또 차원이 많아질 수록 헷갈리기도 하므로 정확하게 익혀놓을 필요가 있습니다. NumPy의 인덱싱, 슬라이싱을 이미 알고 있으면 그리 어렵지 않습니다. 

 

(1) 파이토치 텐서 인덱싱 (indexing of PyTorch tensor) 

(2) 파이토치 텐서 슬라이싱 (slicing of PyTorch tensor)

(3) 1개 값만 가지는 파이토치 텐서에서 숫자 가져오기 : tensor.item() 

(4) 파이토치 텐서 재구조화 (reshape)

 

 

PyTorch tensor indexing

 

 

(1) 파이토치 텐서 인덱싱 (indexing of PyTorch tensor)

 

먼저, 예제로 사용할 PyTorch tensor를 NumPy ndarray를 변환해서 만들어보겠습니다.

torch.Size([3, 5]) 의 형태를 가진 텐서를 만들었습니다. 

 

import torch
import numpy as np

## -- creating a tensor object
x = torch.tensor(
    np.arange(15).reshape(3, 5))

print(x)
# tensor([[ 0,  1,  2,  3,  4],
#         [ 5,  6,  7,  8,  9],
#         [10, 11, 12, 13, 14]])


## -- shape, size of a tensor

x.shape
# torch.Size([3, 5])

x.size(0)
# 3

x.size(1)
# 5

 

 

다양한 인덱싱 시나리오별로 예를 들어서 설명을 해보겠습니다. 

 

 

(1-1) 1번 행 전체를 인덱싱 해오기 

 

## indexing using position number
x[1]  # or equivalently x[1, :]
# tensor([5, 6, 7, 8, 9])

 

 

(1-2) 1번 행, 2번 열 원소 인덱싱 해오기

 

x[1, 2]
# tensor(7)

 

 

(1-3) 1번 열 전체를 인덱싱 해오기

 

x[:, 1]
# tensor([ 1,  6, 11])

 

 

(1-4) 1번 열, 블리언(Boolean) True 인 행만 인덱싱 해오기

 

## indexing using Boolean
x[1][[True, False, False, False, True]]
# tensor([5, 9])

 

 

(1-5) 1번 열, 텐서(tensor)의 값의 행만 인덱싱 해오기

 

## indexing using a tensor
x[1][torch.tensor([1, 3])]
# tensor([6,8])

 

 

(1-6) 1번 열을 가져오되, 행은 리스트의 위치 값 순서대로 바꾸어서 가져오기

 

## changing the position using indexing
x[1, [4,3,0,1,2]]
# tensor([9, 8, 5, 6, 7])

 

 

(1-7) 행 별로 열의 위치를 달리해서 값 가져오기

(예) 0번 행의 4번 열, 1번 행은 3번 열, 2번 행은 2번 열의 값 가져오기

 

## 행별로 인덱싱할 위치를 바뀌가면서 인덱싱하기
x[torch.arange(x.size(0)), [4,3,2]]
# tensor([ 4,  8, 12])

 

 

(1-8) 전체 행의 마지막 열(-1) 값 가져오기

 

## -1 : last elements
x[:, -1]
# tensor([ 4,  9, 14])

 

 

(1-9) 인덱싱한 위치에 특정 값을 재할당 하기

(예) 1번 행에 값 '0'을 재할당하기

 

## asinging new value using indexing
x[1] = 0


print(x)
# tensor([[ 0,  1,  2,  3,  4],
#         [ 0,  0,  0,  0,  0],   <----- '0' 으로 재할당 됨
#         [10, 11, 12, 13, 14]])

 

 

(2) 파이토치 텐서 슬라이싱 (slicing of PyTorch tensor)

 

 

(2-1) 1번 행 이후의 행의 모든 값 가져오기

 

x = torch.tensor(
    np.arange(15).reshape(3, 5))

print(x)
# tensor([[ 0,  1,  2,  3,  4],
#         [ 5,  6,  7,  8,  9],
#         [10, 11, 12, 13, 14]])


## -- slicing
x[1:]
# tensor([[ 5,  6,  7,  8,  9],
#         [10, 11, 12, 13, 14]])

 

 

(2-2) 1번 행 이후의 행 & 3번 열 이후의 열의 모든 값 가져오기

 

x[1:, 3:]
# tensor([[ 8,  9],
#         [13, 14]])

 

 

(2-3) 전체 행, 1번 열의 값 가져오기

 

x[:, 1]
# tensor([ 1,  6, 11])

 

 

(2-4) 1번 행, 전체 열의 값 가져오기

 

x[1, :]  # or equivalently x[1]
# tensor([5, 6, 7, 8, 9])

 

 

(2-5) 1번 행의, 1번과 4번 행의 값 가져오기

(생소한 코드예요. ^^;)

 

## tensor[1::3] ==> (1, None, None, 4)
x[1][1::3]
# tensor([6, 9])

 

 

 

(3) 1개 값만 가지는 파이토치 텐서에서 숫자 가져오기 : tensor.item()

 

## torch.tensor.item()
## : get a Python number from a tensor containing a single vlaue
y = torch.tensor([[5]])
print(y)
# tensor([[5]])

y.item()
# 5

 

 

만약 PyTorch tensor가 1개의 값(즉, 스칼라) 만을 가지는 것이 아니라 여러개의 값을 가지는 경우, tensor.item() 메소드를 사용하면 ValueError: only one element tensors can be converted to Python scalars 가 발생합니다. 

 

## ValueError
x.item()

# ValueError Traceback (most recent call last)
# <ipython-input-74-3396a1b2b617> in <module>
# ----> 1 x.item()

# ValueError: only one element tensors can be converted to Python scalars

 

 

 

(4) 파이토치 텐서 재구조화 (reshape)

 

NumPy의 reshape() 메소드와 동일하게 PyTorch tensor 도 reshape() 메소드를 이용해서 형태(shape)를 재구조화할 수 있습니다. 

 

torch.tensor([[0, 1, 2, 3, 4, 5]])
# tensor([[0, 1, 2, 3, 4, 5]])


## -- reshape
torch.tensor([[0, 1, 2, 3, 4, 5]]).reshape(2, 3)
# tensor([[0, 1, 2],
#         [3, 4, 5]])

 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 PyTorch tensor 와 NumPy ndarrays 의 행렬 곱셈(matrix multiplication) 성능을 비교해보겠습니다. GPU 는 Google Colab 을 사용해서 성능을 테스트했습니다. 

 

(1) PyTorch tensor & GPU 로 행렬 곱셈 (matrix multiplication) 

(2) PyTorch tensor & CPU 로 행렬 곱셈

(3) NumPy ndarrays & CPU 로 행렬 곱셈

 

 

PyTorch tensors & GPU vs. PyTorch tensors & CPU vs. NumPy ndarrays & CPU

 

 

 

m * n 행렬 A 와 n * p 행렬 B 가 있다고 했을 때, 행렬 곱셈(matrix multiplication) C = AB 는 아래와 같이 정의할 수 있습니다.  행렬 곱셈 연산은 A 의 i번째 행과 B의 j번째 열의 성분들을 각각 곱한 후 더한 것이며, 서로 독립적으로 병렬 연산 (calculation in parallel) 이 가능합니다. GPU 는 수 천 개의 cores 를 가지고 있으므로 단지 수 개의 cores를 가지는 CPU 보다 병렬로 행렬 곱셈을 매우 빠르게 수행할 수 있습니다. 

 

행렬 곱셈 (matrix multiplication)

 

 

 

(1) PyTorch tensor & GPU 로 행렬 곱셈 (matrix multiplication)

 

PyTorch를 불러오고, torch.manual_seed() 로 초기값을 설정해서 매번 동일한 난수가 발생하게 설정을 해준 다음, torch.rand() 함수로 난수를 생성해서 torch 객체를 만들어보겠습니다. 

 

import torch
print(torch.__version__)
#1.13.1+cu116

## generate the torch objects with random numbers
torch.manual_seed(1004)
x = torch.rand(1, 25600)
y = torch.rand(25600, 100)

 

 

다음으로, torch tensor 객체를 저장할 디바이스를 정의하겠습니다. 아래 코드는 GPU 를 사용할 수 있으면 (torch.cuda.is_available() == True) 'cuda' 로 디바이스를 정의하고, 그렇지 않으면 'cpu'로 디바이스를 정의합니다. 

 

## define the device
device = 'cuda' if torch.cuda.is_available() else 'cpu'

 

 

이제 x, y의 두 개 tensor 객체를 위에서 정의한 디바이스에 등록(register)해서 정보를 저장하겠습니다. 

 

## register and save the tensor objects with the device
x, y = x.to(device), y.to(device)

 

 

마지막으로, PyTorch tensor 객체인 행렬 x와 y 의 행렬 곱셉(matrix multiplication, z = (x@y))을 GPU 를 사용해서 해보겠습니다. 이때 실행 성능을 확인하기 위해 %timeit 으로 실행 시간을 측정해보겠습니다. 

 

행렬 곱셉에 25.4 µs ± 26.9 µs per loop 의 시간이 걸렸네요.  

 

## (1) run matrix multiplication of the Torch objects on GPU
%timeit z = (x@y)

# The slowest run took 6.94 times longer than the fastest. 
# This could mean that an intermediate result is being cached.
# 25.4 µs ± 26.9 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

 

 

 

(2) PyTorch tensor & CPU 로 행렬 곱셈 (matrix multiplication)

 

이번에는 PyTorch tensor 에 대해 CPU 를 사용(x.cpu(), y.cpu())해서 행렬 곱셉을 해보겠습니다. 

PyTorch tensor에 대해 CPU 로는 932 µs ± 292 µs per loop 의 시간이 소요되었으며, 이는 위의 PyTorch tensor & GPU 대비 약 37배 더 시간이 소요되었습니다. 

 

## (2) run matrix multiplication of the same tensors on CPU
x, y = x.cpu(), y.cpu()

%timeit z = (x@y)

# 932 µs ± 292 µs per loop 
# (mean ± std. dev. of 7 runs, 1000 loops each)

 

 

 

(3) NumPy ndarrays & CPU 로 행렬 곱셈

 

마지막으로 NumPy의 ndarrays 로 위의 PyTorch로 했던 행렬 곱셉을 똑같이 실행해서, 소요시간을 측정(%timeit)해 보겠습니다.

 

NumPy의 ndarrays 로 행렬 곱셉은 1.17 ms ± 46.4 µs per loop 이 걸려서, 위의 PyTorch tensor & GPU 는 NumPy ndarrays 보다 46배 빠르고, 위의 PyTorch tensor & CPU 는 NumPy ndarrays 보다 1.25 배 빠르걸로 나왔습니다

역시 GPU 가 행렬 곱셉 성능에 지대한 영향을 끼치고 있음을 확인할 수 있습니다! 

 

## (3) run matrix multiplication on NumPy arrays
import numpy as np

x = np.random.random((1, 25600))
y = np.random.random((25600, 100))

%timeit z = np.matmul(x, y)

# 1.17 ms ± 46.4 µs per loop 
# (mean ± std. dev. of 7 runs, 1000 loops each)

 

 

PyTorch tensor & GPU 의 강력한 성능을 이용해서 멋진 딥러닝 모델 학습하세요. 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요

이번 포스팅에서는 PyTorch 의 자료구조인 tensor가 무엇이고, tensor 객체를 만드는 방법(how to create a PyTorch tensor objects)을 소개하겠습니다. 

 

(1) 텐서(tensor)는 무엇인가? 

(2) PyTorch 텐서 만들고 속성정보 조회하기

(3) 0(zeros), 1(ones)로 채워진 텐서, 빈 텐서(empty tensor) 만들기

 

 

(1) 텐서(tensor)는 무엇인가? 

 

텐서(tensor) 는 PyTorch의 가장 기본적인 데이터 유형으로서, NumPy의 다차원 배열인 ndarray 와 유사합니다. 

스칼라(Scalar)는 0 차원의 텐서, 벡터(Vector)는 1 차원의 텐서, 행렬(Matrix)은 2 차원의 텐서, 그리고 3차원 이상의 다차원 행렬은 다차원 텐서입니다. PyTorch의 텐서는 스칼라, 벡터, 행렬 및 다차원 텐서를 모두 아우르는 일반화된 데이터 구조를 말합니다. 

 

PyTorch tensor objects: scalar, vector, matrix, tensor

 

PyTorch 로 GPU를 사용해서 다차원 행렬을 가지고 병렬처리를 하려면 텐서 객체로 데이터를 변환해야 합니다. 

텐서 내의 데이터 원소는 모두 같은 데이터 유형이어야 합니다. (가령, 모두 float 이거나, int 이거나 동일해야 함.)

 

 

 

(2) PyTorch 텐서 만들고 속성정보 조회하기

 

(2-1) 리스트(List)에 대해 torch.tensor(a list) 로 텐서 만들기

 

import torch

## creating a tensor object with a list of lists
x = torch.tensor([[1., 2., 3.]])

print(x)
# tensor([[1., 2., 3.]])

 

 

(2-2) NumPy의 다차원 array 를 torch.tensor(np.array) 로 텐서로 변환하기

 

## creating a tensor object with numpy array
import numpy as np

y = np.array([[1., 2., 3.], [4., 5., 6.]])
z = torch.tensor(y)

print(z)
# tensor([[1., 2., 3.],
#         [4., 5., 6.]], dtype=torch.float64)

 

 

(2-3) 텐서의 형태(shape)와 데이터 유형(dtype) 속성정보 조회하기

 

## accessing the Shape and DataType of a tensor

print(x.shape)
# torch.Size([1, 3])

print(x.dtype)
# torch.float32

 

 

 

(3) 0(zeros), 1(ones)로 채워진 텐서, 빈 텐서(empty tensor) 만들기

 

(3-1) 0 으로 채워진 텐서 만들기 : torch.zeros(size)

 

## generating a tensor object with zeros
torch.zeros((2, 4))

# tensor([[0., 0., 0., 0.],
#         [0., 0., 0., 0.]])

 

 

(3-2) 1 로 채워진 텐서 만들기 : torch.ones(size)

 

## generating a tensor object with ones
torch.ones((2, 4))

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

 

 

(3-3) 빈 텐서 만들기 : torch.empty(size)

 

## generating a tensor filled with uninitialized data.
torch.empty((2, 4))

# tensor([[0., 0., 0., 0.],
#         [0., 0., 0., 0.]])

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요! :-)

 

반응형
Posted by Rfriend

댓글을 달아 주세요