딥러닝(AI학습 42)

이유진·2024년 7월 8일

--24.PyTorch 사용.ipynb--

PyTorch Basic

import numpy as np
import torch

torch.version

torch.Tensor

  • torch 의 다차원 배열 객체

https://pytorch.org/docs/stable/torch.html#tensors

np.arange(9)

tensor = torch.arange(9)
tensor

type(tensor) # torch.Tensor객체

tensor.shape

tensor.numpy()

tensor.reshape(3,3)

randoms = torch.rand((3,3)) # [0,1) 범위의 난수를 (3,3) 크기로 생성
randoms

randoms.dtype

zeros = torch.zeros((3,3))
zeros

zeros.shape

zeros.size() # <- numpy와 다른 결과!!

ones = torch.ones((3,3))
ones

torch.zeros_like(ones) # input가 동일한 shape를 가진 zeros를 만듬.

Operation

tensor

tensor * 3 # broadcast도 가능!

tensor = tensor.reshape((3,3))
tensor

tensor + tensor

torch.add(tensor, 10)

Tensor Views

https://pytorch.org/docs/stable/tensor_view.html#tensor-views

  • 기존 tensor 의 데이터는 공유하지만, shape 이 다른 view 제공
  • view() 는 reshape() 거의 같다..
  • view() 는 사실, 훨씬 오래전부터 있어왔다. (사실 reshape() 를 더 추천한다.)
  • ※ 단, 공식 예제에선 View 를 사용함.

range_nums = torch.arange(9).reshape(3, 3)
range_nums

range_nums.view(-1) # -1 은 차원 추론, (3,3) => (9,)

range_nums.view(-1, 9)

Slice & Index

tensor

tensor[1]

tensor[1, 1]

slicing

tensor[1:]

tensor[1:, 1:]

Compile

PyTorch에서 특정 디바이스에 '컴파일'된다는 것은 모델이 실행될 디바이스(CPU 또는 GPU)에 최적화된 코드를 생성하여, 해당 디바이스의 자원을 효율적으로 활용하고, 전체 연산 성능을 향상시키는 것을 의미.

이 개념은 주로 PyTorch의 torch.compile 기능과 관련이 있다. PyTorch의 torch.compile은 JIT(Just-In-Time) 컴파일러를 사용하여 모델을 실행 시점에 최적화합니다

  • 장점:
    • 성능 향상: JIT 컴파일러는 모델의 연산 그래프를 최적화하여 실행 속도를 크게 향상시킬 수 있습니다.
    • 디바이스 활용 극대화: 특정 디바이스의 특성을 최대한 활용하여 성능을 극대화할 수 있습니다.
      • 예를 들어, GPU에서 연산을 수행할 때는 GPU의 메모리와 병렬 처리 능력을 최대한 활용하는 코드를 생성합니다

arr = np.array([1, 1, 1])
arr

arr_torch = torch.from_numpy(arr)
arr_torch

arr_torch.dtype

GPU 탑재 여부 확인

torch.cuda.is_available()

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

to(device) : 특정 device에 compile

arr_torch.to(device)

AutoGrad

'기울기'를 주어 학습이 되게 하는 것

x = torch.ones(2,2, requires_grad=True) # 기울기 계산 해야한다고 컴퓨터에게 알려주는것.
x

x.grad # 학습기울기, 처음에는 없다.

https://pytorch.org/docs/stable/generated/torch.Tensor.grad.html#torch-tensor-grad

torch.Tensor.grad는 텐서(Tensor)의 기울기(gradient)를 나타냅니다.

기울기는 주로 딥러닝에서 역전파(backpropagation) 과정을 통해 계산되며,

모델의 가중치(weight)를 업데이트하는 데 사용됩니다.

1. 텐서 생성과 역전파 설정

먼저, 기울기를 계산하고자 하는 텐서를 생성하고,

이 텐서에 대한 기울기 계산이 필요하다는 것을 PyTorch에 알려줍니다 (requires_grad=True).

x = torch.tensor(2.0, requires_grad=True)
x

x.grad

2. 연산 수행

텐서를 사용하여 연산을 수행합니다. 예를 들어, 간단한 함수

y = x² 를 정의합니다.

y = x ** 2

3. 역전파 수행

backward() 함수를 호출하여 기울기를 계산합니다.

이는 자동으로 역전파를 수행하여 기울기를 x.grad에 저장합니다.

torch.Tensor.backward

https://pytorch.org/docs/stable/generated/torch.Tensor.backward.html#torch-tensor-backward

y.backward()

4. 기울기 확인

계산된 기울기는 x.grad에 저장되어 있다.

x.grad

y.grad_fn # funtion 확인 가능

x.requires_grad

'학습모드(train mode)' 와 '테스트모드(test mode)'

위에서는 '학습모드(train mode)' 가 가능하지만

학습모드가 가능하다가도 '테스트모드' 로도 가능해야 겠죠

'학습모드(train mode)' 에서는 기울기 구하는게 가능하지만

'테스트모드(test mode)' 에서는 이기능을 꺼야 겠죠

-> no_grad() 사용

no_grad() 사용

(x ** 2).requires_grad

with torch.no_grad() :

기울기를 구하지 않는 동작 수행

print((x ** 2).requires_grad)

# no_grad() 이면  기울기를 구하지 않게 됩니다.
# 따라서 batch, normalization dropout 들이 작동 안함
# 작동 속도는 train mode 일때보다 test mode 가 더 빠르겠죠.

PyTorch Data Preprocess

from torchvision import datasets, transforms

datasets : 데이터 예제들이 담겨 있다.

transforms : 전처리를 위한 데이터 변환

DataLoader()

PyTorch는 DataLoader()를 사용하여 model에 입력

DataLoader 에 batch size를 넣어 줄거다

batch_size = 32
test_batch_size = 32

base_path = r'./'

train_loader = torch.utils.data.DataLoader(

# MNIST 예제 데이터
dataset = datasets.MNIST(
    base_path,    # 저장할 디렉토리
    train = True,  # 학습용
    download = True, # 없으면 다운로드!
    transform = transforms.Compose([    # 데이터 로딩시 필요한 변환 (transform) 나열.
        transforms.ToTensor(),   # 데이터 다운 받은 뒤 Tensor로 받아옴.
        transforms.Normalize(mean = (0.5,), std = (0.5,)),    # Nomalize(mean,std)
                                                              # 평균 0.5, 표준편차 0.5로 스케일링하여 로딩
                                                              # channel 이 한개라서 1개값만 명시. (RGB 처럼 3개의 채널이면 다름.)
    ])
),
batch_size = batch_size,
shuffle = True,

)

실행하면

<- ./MNIST 디렉토리 생성

import os
os.listdir(base_path)

test_loader를 불러오자

test_loader = torch.utils.data.DataLoader(
dataset = datasets.MNIST(
base_path,
train = False, # 학습모드로 사용하는 데이터가 아니니까 False
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.5,), (0.5,)) # test 데이터로 train과 마찬가지로 스케일링
]),
),
batch_size = test_batch_size,
shuffle = True,
)

첫번째 샘플 확인

DataLoader 에서는 batch 단위로 뽑혀져 나온다

type(train_loader) # DataLoader 객체 <= iterable 하다.(for문을 돌릴 수 있다.)

images, labels = next(iter(train_loader)) # 첫번째 batch 추출

images.shape, labels.shape

(torch.Size([32, 1, 28, 28]), torch.Size([32]))

TF 에선 (batchsize, height, width, channel) 의 형태로 입력했었다.

PyTorch는 (batchsize, channel, height, width) 의 형태로 사용한다. (명심!)

만약 RGB 이미지였으면 (32, 3, 28, 28) 과 같이 되었을거다.

첫번째 이미지

images[0].shape

squeeze 를 사용하여 heigh, width 만 남기자!

torch_image = torch.squeeze(images[0])
torch_image.shape

image = torch_image.numpy()
image.shape

첫번째 label

label = labels[0].numpy()
print(label)
label.shape

np.min(image), np.max(image)

import matplotlib.pyplot as plt

plt.title(label)
plt.imshow(image, 'gray')
plt.show()

PyTorch Layer

train_loader = torch.utils.data.DataLoader(

# MNIST 예제 데이터
dataset = datasets.MNIST(
    base_path,
    train = True,
    download = True,
    transform = transforms.Compose([
        transforms.ToTensor(),
        # 직전 예제에서는 normalize 했지만, 이번 예제에서는 '모델학습'이 목적이 아니라 생략.
    ])
),
batch_size = 1,   # 이번 단원의 목적이 이미지 '하나하나'를 Layer에 넣어서 확인 해보기 위함.
# shuffle도 안함.

)

첫번째 batch

image, label = next(iter(train_loader))

image.shape, label.shape

plt.imshow(image[0, 0, :, :], 'gray')
plt.title(label[0])
plt.show()

Network 쌓기

import torch.nn as nn # nn 레이어
import torch.nn.functional as F # F 레이어

device

nn.Conv2d()

  • in_channels: 받게 될 channel의 갯수
  • out_channels: 보내고 싶은 channel의 갯수
  • kernel_size: 만들고 싶은 kernel(weights)의 사이즈

https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html?highlight=conv2d#torch.nn.Conv2d

nn.Conv2d(in_channels = 1, out_channels = 20, kernel_size = 5, stride = 1)

PyTorch 에선 (TF와 달리 in_channel 지정 해주어야 함 (앞에서 몇개의 channel로 받을지))

샘플 이미지 채널이 1이었다 >> (1, 1, 28, 28)

out_channel : 출력 (TF 에선 커널의 개수)

layer_conv1 = nn.Conv2d(1, 20, 5, 1).to(torch.device(device))
layer_conv1

weight 꺼내기

weight = layer_conv1.weight
weight.shape

weight

weight는 '학습모드'(학습 가능한 상태) 라서, 이때는 numpy를 뽑아 낼 수 없다.

-> '말랑말랑한 Tensor' 라고도 함.

weight.numpy() # 에러

detach()는 레이어에서 떼어 내서 grad의 영향을 받지 않는다

weight = weight.detach().numpy()
weight

weight.shape

위 20개 커널 중에서 첫번째 것을 시각화 해보기

plt.imshow(weight[0, 0, :, :])
plt.colorbar()
plt.show()

Output 시각화

Conv2d를 통과시킨 후의 시각화

output_data = layer_conv1(image) # image를 layer에 통과시켜 본다.
output_data.shape

output_data # (Tensor 객체, grad_fn) <- 튜플

output_data.data # 위에서 데이터만 추출

output_data = output_data.data

output_data.data.numpy() # output_data 자체는 튜플 객체임. 그래서 .data 붙혀서 Tensor객체만 남김

cpu() 로 컴파일 하고 numpy() 변환

output = output_data.cpu().numpy()
output

image_arr = image.numpy()
image_arr.shape

image_arr : 입력

output : 출력

weight : 모델 파라미터

plt.figure(figsize=(15,30))

plt.subplot(131)
plt.title("Input")
plt.imshow(np.squeeze(image_arr), 'gray')

plt.subplot(132)
plt.title('Weight')
plt.imshow(weight[0, 0, :, :])

plt.subplot(133)
plt.title('Output')
plt.imshow(output[0, 0, :, :], 'gray')

plt.show()

F.max_pool2d (Pooling)

image.shape

nn : 그 안에 weight가 있는 레이어들. 즉, 모델 파라미터가 있고 학습대상이라는 뜻

nn.function : weight가 없는 레이어들 (ex : max pooling, activation 함수 등등 ..)

pool = F.max_pool2d(input=image, kernel_size=2, stride=2)
pool.shape

[1, 1, 28, 28]

[1, 1, 14, 14]

F 레이어는 weight가 없다 (즉, 학습되는게 아니기에 detach() 없이 곧바로 numpy() 가능)

pool_arr = pool.numpy()
pool_arr

plt.figure(figsize=(10, 15))

plt.subplot(121)
plt.title("Input")
plt.imshow(np.squeeze(image), 'gray')

plt.subplot(122)
plt.title('Output')
plt.imshow(np.squeeze(pool_arr), 'gray')

plt.show()

이제 flatten 으로 펼친 다음에 nn.Linear를 통과시켜 보겠습니다.

activation func 은 생략

nn.Linear()

TF에선 Flatten이 있었지만,

torch 에는 없다 -> reshape나 view를 사용하여 펼쳐준 뒤 Linear() 에 넣어야 한다

image.shape

flatten = image.view(1, 28 * 28) # batch size는 유지해주어야 한다.
flatten.shape

lin = nn.Linear(in_features=784, out_features=10)(flatten)
lin.shape

lin

Linear는 weight가 있다. numpy객체 확인하려면 우선 detach() 해주어야 한다.

lin.detach().numpy()

plt.imshow(lin.detach().numpy(), 'jet')
plt.show()

높은값이 붉은색, 낮은값 파란색

F.softmax()

이번에는 detach() 대신 torch.no_gard를 사용하여 꺼내볼거다.

lin.shape

with torch.no_grad() :
flatten = image.view(1, 28 * 28)
lin = nn.Linear(784, 10)(flatten)
softmax = F.softmax(lin, dim = 1) # lin이 (1, 10)이기 때문에 뒤에 있는 차원을 softmax 해야한다. (dim = 1)

softmax

np.sum(softmax.numpy())

np.argmax(softmax)

profile
독해지자

0개의 댓글