Python

파이토치 기본 기능

해파리냉채무침 2024. 4. 5. 23:53

출처: 차근차근 실습하며 배우는 파이토치 딥러닝 프로그래밍

 

파이토치의 최대 특징은 자동 미분 기능이다. 

0계 텐서는 스칼라, 1계 텐서는 벡터, 2계 텐서는 행렬을 뜻함.

import torch
r0 = torch.tensor(1.0).float()
print(type(r0))
print(r0.dtype)

<class 'torch.Tensor'>

torch.float32

 

텐서 변수가 생성될 때는 반드시 뒤에 float()을 호출해서 float32로 변환해야한다.0계 텐서이기 때문에 []가 비어있다. 만약 2행 3열인 2계텐서가 있으면 Torch.Size([2,3])이 출력된다. 

print(r0.shape)
print(r0.data)

torch.Size([])

tensor(1.)

 

1계텐서는 다음과 같다. torch.tensor 인수로 넘파이 배열을 사용했다. 

import numpy as np
r1_np = np.array([1, 2, 3, 4, 5])
print(r1_np.shape) #(5,)
r1 = torch.tensor(r1_np).float() 
print(r1.dtype) #torch.float32
print(r1.shape) #torch.Size([5])
print(r1.data) #tensor([1., 2., 3., 4., 5.])

 

2계 텐서는 행렬로 정의된다.

r2_np = np.array([[1, 5, 6], [4, 3, 2]])
print(r2_np.shape) #(2, 3)
r2 = torch.tensor(r2_np).float()
print(r2.shape) #torch.Size([2, 3])
print(r2.data) #tensor([[1., 5., 6.],[4., 3., 2.]])

 

3계 텐서 2x2씩 3개 tensor가 생성된다. 

randn 함수는 평균이 0, 분산이 1인 정규분포 난수가 생성된다. 

torch.manual_seed(123)
r3 = torch.randn((3, 2, 2))
print(r3.shape) #torch.Size([3, 2, 2])
print(r3.data)

 

4계 텐서

r4 = torch.ones((2, 3, 2, 2))
print(r4.shape)
print(r4.data)

 

정수값을 갖는 텐서 만들기

파이토치 대부분 계산은 dtype이 float32릉 사용한다.

하지만 다중분류에서 loss function인 crossEntropyLoss와 nn.NLLoss 는 손실함수를 만들때, 정수타입을 지정해야한다.

long 함수를 적용하면 dtype으로 torch.int64를 갖는 정수형 텐서가 정의된다. 

r5= r1.long()
print(r5.dtype)
print(r5)

torch.int64

tensor([1, 2, 3, 4, 5])

View 함수

view함수는 넘파이의 reshape 함수와 같다. 

view 함수를 사용해 3계 텐서를 2계 텐서로 변환한다.

r6= r3.view(3,-1)
print(r6.shape)
print(r6.data)

r3 텐서의 shape은 [3,2,2] 로 3계 텐서다. 3*2*2=12, 12개의 요소로 -1이 위치한 요소수를 x라고 하면, 3*x=12

-1로 넘겨지는 부분의 요소수는 4로 결정된다. 

 

1계 함수를 변환하면 다음과 같다.

남은 자리에 -1을 지정하면 수를 자동으로 조정한다. x=12

r7 = r3.view(-1)
print(r7.shape)
print(r7.data)

item 함수

0계 텐서에 대해서 파이썬 본래 클래스의 dtype(float , int)를 꺼낼 때 item 함수를 사용한다. loss 계산시 데이터 기록을 위한 값을 추출하는데 사용한다. 1계 텐서는 대상이 아니지만, shape가 [1]이나 [1,1] 처럼 1계 텐서나 2계 텐서라도 요소가 한개만 있는 경우는 사용 가능하다. 

item = r0.item()

print(type(item))
print(item)

<class 'float'>

1.0

max 함수

수치 연산에 대해 최댓값을 가져온다. min, mean 함수도 사용할 수 있다.

print(r2)
print(r2.max())

print(torch.max(r2, 1))

행 기준으로 최대값과 최대값의 인덱스 값을 반환한다

넘파이 변환

r2_np = r2.data.numpy()
print(type(r2_np))
print(r2_np)

자동미분 기능

경사하강법에서 편미분과 같은 기능이다. 

y= 2x²+2의 자동미분 계산은 다음과 같다.

 

1)  경사 계산용 변수의 정의 -> requires_grad= True로 설정한다

넘파이 배열로 x_np를 설정하고, float32인 텐서 x로 변환함.

x_np = np.arange(-2, 2.1, 0.25)
print(x_np)
x= torch.tensor(x_np,requires_grad=True,dtype=torch.float32)
print(x)

[-2. -1.75 -1.5 -1.25 -1. -0.75 -0.5 -0.25 0. 0.25 0.5 0.75 1. 1.25 1.5 1.75 2. ]

tensor([-2.0000, -1.7500, -1.5000, -1.2500, -1.0000, -0.7500, -0.5000, -0.2500, 0.0000, 0.2500, 0.5000, 0.7500, 1.0000, 1.2500, 1.5000, 1.7500, 2.0000], requires_grad=True)

 

2)  텐서 변수 계산 -> 내부에서 계산 그래프가 자동 생성됨

y도 자동적으로 텐서 변수가 된다.

y = 2 * x**2 + 2
print(y)

tensor([10.0000, 8.1250, 6.5000, 5.1250, 4.0000, 3.1250, 2.5000, 2.1250, 2.0000, 2.1250, 2.5000, 3.1250, 4.0000, 5.1250, 6.5000, 8.1250, 10.0000], grad_fn=<AddBackward0>)

import matplotlib.pyplot as plt
plt.plot(x.data, y.data)
plt.show()

이를 시각화하면 2차함수 그래프가 나온다. 

경사 계산의 대상 함수는 스칼라이기 때문에  y 값을 하나로 합친다.

z= y.sum()
print(z)

tensor(85., grad_fn=<SumBackward0>)

 

3) 계산 그래프 시각화

make_dot 함수를 호출할때 첫번째 인수는 스칼라값(계산 그래프의 대상이 될 변수), 두번째 변수 x는 미분계산 대상의 변수를 딕셔너리 형식의 파라미터 리스트로 넣어준다. 출발점 x를 텐서 변수로 하고 requries_grad 그래프를 지정하면  값을 계산해 나가면 계산 과정이 자동적으로 기록한다.

from torchviz import make_dot

g= make_dot(z, params={'x': x})
display(g)

계산 그래프는 어떤 종류의 함수가 어떤 순서로 호출되는지를 나타내는 것이 계산 그래프이다. 계산 그래프는 x값을 조금씩 움직였을 때 z값이 어떻게 변화하는지를 파이토치가 자동적으로 알려준다. 

 

4) 경사 계산 -> backward 함수 호출

파이토치 내부에서 계산 그래프를 기반으로 수치미분(편미분)이 작용하여 경사계산이 이루어진다

z.backward()

 

 

5) 경삿값 가져오기

y= 2x²+2의 미분값 4x²에 range 값이 들어간 결과가 도출된다. 

print(x.grad)

tensor([-8., -7., -6., -5., -4., -3., -2., -1., 0., 1., 2., 3., 4., 5., 6., 7., 8.])

 

6) 경삿값의 초기화

사용이 끝나면 값을 초기화해준다. 초기화하지 않으면 값이 누적된다.

x.grad.zero_()
print(x.grad)

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

 

시그모이드 경사 계산

sigmoid = torch.nn.Sigmoid()
y= sigmoid(x)
print(y)

tensor([0.1192, 0.1480, 0.1824, 0.2227, 0.2689, 0.3208, 0.3775, 0.4378, 0.5000, 0.5622, 0.6225, 0.6792, 0.7311, 0.7773, 0.8176, 0.8520, 0.8808], grad_fn=<SigmoidBackward0>)

 

시각화

plt.plot(x.data,y.data)
plt.show()

계산 그래프 시각화

그래프 상에서 sigmoid 단위로 표시되었음. 

g = make_dot(z, params={'x': x})
display(g)

경삿값 가져오기

z= sum(y)
z.backward()
print(x.grad)

tensor([0.1050, 0.1261, 0.1491, 0.1731, 0.1966, 0.2179, 0.2350, 0.2461, 0.2500, 0.2461, 0.2350, 0.2179, 0.1966, 0.1731, 0.1491, 0.1261, 0.1050])