Data_Analysis_Track_33/Python

Python_Deeplearning_pytorch_01-2(tensor 다루기)

lsc99 2023. 10. 16. 17:11

Reshape

shape 변경

  • tensor객체.reshape(*shape) / view(*shape) 이용
        - 변환 후 값을 변경하면 원본 배열의 값도 같이 바뀐다.
            > tensor.clone(): tensor를 복제한다.

 

reshape

a=torch.rand(12)
a2 = a.reshape(3,4)
a3 = a.reshape((3,2,2))
a4 = a.reshape((3,2,-1))  #한 개 axis는 -1로 설정가능하고 그럼 계산해서 알아서 설정해 준다.
print(a.shape, a2.size(), a3.shape, a4.shape)

 

view

a5 = a.view(3,4)
a6 = a.view((3,2,2))
a7 = a.view((3,2,-1))  #한개 axis는 -1로 설정가능
print(a.shape, a5.size(), a6.shape, a7.shape)

 

값 변경

a5[0, 0] = 12.1
a2[0, 1] = 15.1

print(a5)
print(a2)
print(a) # 원본도 같이 변경되는 것을 확인

 

colne() 메소드 -> tensor 복사

# tensor복사: clone() 메소드
r = a.clone().reshape(3,4)
r[0,0] = 100.1
print(a)
print(r)

 

dummy 축 늘리기

  • None을 이용 (numpy의 newaxis 대신 None을 사용한다.)
  • unsqueeze(dim=축번호)

 

tensor객체[None 이용], tensor객체.unsqueeze(dim=축번호)

import torch
a = torch.tensor([[10,20],[10,20]])
print(a.shape)

a1, a2 = a[None, :], a.unsqueeze(dim=0)
print(a1.shape, a2.shape)

a3, a4 = a[:, :, None], a.unsqueeze(dim=-1) 
print(a3.shape, a4.shape)

a5, a6 = a3[:,None,:], a.unsqueeze(dim=1)
print(a5.shape, a6.shape)

 

dummy 축 제거

  • squeeze([dim=축번호]) 이용

 

tensor객체.squeeze(dim=축번호)

t = torch.rand(3, 1, 3, 1)
print(t.shape)

r1 = t.squeeze()  #모두 제거
print(r1.shape)

r2 = t.squeeze(dim=1) # 특정 axis 제거
print(r2.shape)

r3 = t.squeeze(dim=[1,3]) # 여러 axis의 dummy 축 제거
print(r3.shape)

 

tensor 합치기

  • torch.cat([tensorA, tensorB, ...], dim=0)

 

배열 a, b, c, d 생성

a = torch.arange(10).reshape(2,5)
b = torch.arange(10,20).reshape(2,5)
c = torch.arange(20,30).reshape(2,5)
d = torch.arange(10,19).reshape(3,3)
print(a)
print(b)
print(c)
print(d)

 

torch.cat([배열1, 배열2, ....], dim = 축번호)

torch.cat([a, b], dim=0)

torch.cat([a, b, c], dim=0)

 

dim or axis = 1

torch.cat([a, b], axis=1) # dim 대신 axis사용가능

torch.cat([a, b, c], axis=1)

 

axis = -1

torch.cat([a, b], axis=-1)

# torch.cat([a, d])  #Error

 

값의 위치(index) 변경

  • tensor 원소의 축별 index의 위치를 바꾼다.
  • tensor.transpose(axis1, axis2)
        - 두 축의 자리만 변경 할 수 있다.
  • tensor.permute(axis1, axis2, axis3, ..)
        - 두 개 이상의 축 자리를 변경한다.

 

transpose, permute

X = torch.arange(24).reshape(2, 3, 4)
print(X.shape)

y = X.transpose(1, 2)
print(y.shape)

z = X.permute(2, 0, 1)
print(z.shape)

tensor 연산 및 주요 함수

element-wise 연산

  • tensor와 상수 연산시, tensor와 tensor간 연산시 원소별로 처리한다.
  • 행렬곱 연산을 제외하고 tensor간 연산시 피연산지 tensor간에 shape이 같아야 한다.
        - shape이 다를 경우 조건이 맞으면 broadcasting을 한 뒤에 연산한다. (size가 다른 축의 경우 한개의 피연산자 size가 1일 경우 복사하여 shape을 맞춘다.)

 

배열 a, b, c 생성

import torch

a = torch.arange(10).reshape(2,5)
b = torch.arange(10,20).reshape(2,5)
c = torch.arange(50, 55)

print(a)
print(b)
print(c)

 

element-wise 연산 -> 각 값에 대하여 하나씩 연산이 이루어진다.

print(a + 100)
# print(a - 100)
print(a < 5)

 

배열끼리의 element-wise 연산 

print(a + b)
# print(a == b)

# broadcasting
print(a + c)

 

주요 연산함수

여러가지 연산함수 

x=torch.arange(-4, 5).reshape(3,3)
print(x)
print(torch.abs(x))
# print(torch.sqrt(torch.abs(x)))
# print(torch.exp(x))  # torch.e**x
# print(torch.log(torch.abs(x)))
# print(torch.log(torch.exp(torch.tensor(1)))) # torch.log() 밑이 e인 로그계산
# print(torch.log10(torch.tensor(10)))         # torch.log10() 밑이 10인 로그계산
# print(torch.log2(torch.tensor(2)))           # torch.log2() 밑이 2인 로그계산
# print("=====================")

 

반올림, 내림, 올림

y = x + torch.randn((3,3))
# print(y)
# print(torch.round(y)) # 반올림
# print(torch.round(y, decimals=2)) # 소수점 둘째자리까지
# print(torch.floor(y)) # 내림
# print(torch.ceil(y)) # 올림

 

행렬곱

  • @ 연산자 또는 torch.matmul(tensor1, tensor2) 함수 이용

 

x, y 생성

x = torch.FloatTensor([[1, 2],
                       [3, 4],
                       [5, 6]
                      ])

y = torch.FloatTensor([[1, 2],
                       [1, 2],
                      ])
x.size(), y.shape

 

@ or torch.matmul(tensor1, tensor2)

z1 = x @ y
z2 = torch.matmul(x, y)
print(z1.shape, z2.shape)
print(z1)
print(z2)

 

Batch 행렬곱(Batch matrix muliplication) - bmm()

# Batch 행렬곱(Batch matrix muliplication) - bmm()
# x, y가 가지는 3개의 2차원 배열 간에 행렬곱을 처리한다.
import torch
x = torch.FloatTensor(3,4,2)
y = torch.FloatTensor(3,2,5)
z = torch.bmm(x, y)
z.shape

 

torch.nan, torch.inf

  • nan: Not a Number, 주로 결측치를 표현한다.
  • inf: infinit 무한. 
        - torch.inf: 양의 무한
        - -torch.inf: 음의 무한
  • torch.isnan(tensor)
        - 원소별 결측치 확인
  • torch.isinf(tensor)    
        - 원소별 inf 확인

 

inf: infinit 무한

print(torch.inf > 10, torch.inf < 10)
print(-torch.inf < 10, -torch.inf > 10)

 

nan 표현, nan&inf 여부 확인

print(torch.log(torch.tensor(-1))) # nan 
# print(torch.isnan(torch.tensor([1,2,torch.nan,3,4])))  # nan 여부 확인
# print(torch.isinf(torch.tensor([1,2,3,4,torch.inf])))  # inf 여부 확인

 

기술통계함수

 

tensor 생성

X=torch.randn(3,4)
print(X)

 

sum

print(torch.sum(X))
print(torch.sum(X, dim=1)) # 행끼리의 합
print(torch.sum(X, dim=1, keepdims=True))

 

mean

print(torch.mean(X))
print(torch.mean(X, dim=1)) # 행끼리의 평균
print(torch.mean(X, dim=1, keepdims=True))

 

std -> 표준편차

var -> 분산

print(torch.std(X)) # standard deviation 표준 편차
print(torch.var(X)) # variance
print(torch.var(X, dim=0))

 

max

print(torch.max(X))
print(torch.max(X, dim=0))  # return_types.max 타입객체로 반환. max값과 max값의 index를 묶어서 반환
print(torch.max(X, dim=1))
print(torch.max(X, dim=1).values, torch.max(X, dim=1).indices, sep=" || ")
print(torch.max(X, dim=0, keepdims=True))  #keepdims=True : 차원(rank)를 유지
print(torch.max(X, dim=1, keepdims=True))

 

min, argmax

print(torch.min(X))
print(torch.min(X, dim=0))
print(torch.min(X, dim=1))

print(torch.argmax(X))
print(torch.argmax(X, dim=0)) # 각 열에서 가장 큰 애가 존재하는 인덱스
print(torch.argmax(X, dim=1)) # 각 행에서 가장 큰 애가 존재하는 인덱스

autograd(자동미분)

  • 자동 미분을 이용해 gradient를 계산하는 pytorch system.
  • 딥러닝 모델에서 weight와 bias tensor들(Parameter)은 backpropagation(역전파)를 이용해 gradient를 구해서 loss가 줄어드는 방향으로 update를 하게된다.
  • pytorch는 이런 미분 수행을 자동으로 처리해 준다.
        - gradient(기울기)를 구한다는 것은 미분을 한다는 것을 말한다.
  • tensor가 미분 가능하려면 requires_grad=True 로 설정되어 있어야 한다. (default: False)

 

tensor가 미분 가능하려면 requires_grad=True 로 설정

x = torch.tensor([1.], requires_grad=True)
# x = torch.tensor([1.])
# x.requires_grad = True
print(x)
print(x.requires_grad)

 

 

y = x ** 2
print(y)
# 계산 결과를 담은 tensor인 y는 계산 결과와 grad_fn에 어떤 계산을 했는지 정보를 담고 있다. (PowBackward0)
# 이는 y 계산에 사용된 x가 requires_grad=True이기 때문이다.

 

미분&grad값 확인

# 미분 - tensor.backward() 호출
y.backward()   # dy/dx  gradient 계산 후 결과를 x의 grad attribute에 저장한다.

x.grad
# 도함수: y` = 2x 이고 x가 1이었으므로 grad는 2

 

x, y, z(3개 이상일 경우)

x = torch.tensor([1.], requires_grad=True)
y = x ** 2   # 미분: 2x
z = y * 10   # 미분: 10  ===> 2x * 10

z.backward()
print(x.grad)

 

편미분

## 편미분
x=torch.tensor([1.],requires_grad=True)
y=torch.tensor([1.],requires_grad=True)
z= 2*x**2 + y**2
print(z)
z.backward() 
print(x.grad)  #dz/dx
print(y.grad)  #dz/dy

 

x=torch.tensor([1., 2., 3.] ,requires_grad=True)
y=torch.sum(x**2) # [x1**2 + x2**2 + x3**2]  
y.backward() #[dy/dx1, dy/dx2, dy/dx3] = [2x1, 2x2, 2x3]

print(y)
print(x.grad) # 스칼라를 벡터로 미분

 

torch.no_grad() 

  • no_grad() 구문에서 연산을 할 경우 requires_grad=True로 설정되었다 하더라도 gradient를 update하지 않는다.
  • 딥러닝 모델 학습이 끝나고 평가할 때는 gradient를 계산할 필요가 없기 때문에 no_grad 구문을 사용한다.

 

x = torch.tensor(1.0, requires_grad = True)
print(x.requires_grad)

y = x**2

print(y.requires_grad)  # 연산이 결과 requires_grad도 True -> 그래야 미분이 가능하므로.
print(x.requires_grad)
print(y)

y.backward()
print(x.grad)

 

x = torch.tensor(1.0, requires_grad = True)
print(x.requires_grad)

with torch.no_grad():
    print(x.requires_grad)
    y = x**2
    print(y.requires_grad)# 연산이 적용될 때 requires_grad가 False가 된다.
    print(y)
    
print(x.requires_grad)
# y.backward()

gradient 값 초기화

x = torch.tensor(1., requires_grad=True)
y = x**2
y.backward()
print("x의 gradient값:", x.grad )

x

 

z = x **2
z.backward()
print("x의 gradient값:", x.grad )

 

x = torch.tensor(1., requires_grad=True)
y = x**2
y.backward()
x.grad

 

gradient 값 초기화

# gradient초기화
x.grad = torch.tensor(0.)   
z = x**2
z.backward()
x.grad