본문 바로가기

ML & DL/Pytorch(base)

'찐비전공자를 위한' : Pytorch 기초 다루기 - 행렬 연산, 사칙 연산

  • 시작.

이번에는 Numpy & torch.tensor를 가지고 기본적인 연산을 해보겠습니다.

 

제가 class를 써서 계속 구현해보는 이유는 

 

  1. 기본적으로 모든 framework가 class를 통해 이루어져 있기 때문입니다.
  2. 우리는 class에 익숙해지고, 각 인자의 흐름을 정확하게 파악할 필요가 있습니다.
  3. 그에 대한 적응력을 기르고자 항상 무언가를 새로 배우면, class와 접목시켜보는 것이 중요합니다.

그리고 이번에는 간단한 bfs알고리즘을 함수 인자로 구현해보는 시도도 해보겠습니다. 

 

즉 이번에는 자료 구조 + 알고리즘 + class + torch.tensor + Numpy 의 활용입니다.

 

시작합니다.

 


Pytorch Basic arithmetics. 

 

 

1. Numpy array, torch.tensor 조작하기

 

import numpy as np 
import torch
from collections import deque

 

torch.tensor에서 주로 쓰는 자료형.

  • FloatTensor : 32비트 float형 
  • DoubleTensor : 64비트 float형 
  • ByteTensor : 8비트 integer형 True / Flase 당연히 integer는 0,1이니까
  • LongTensro : 64비트 integer형

그런데 뒤에서 다루겠지만, 앵간하면 FloatTensor나 DoubleTensor를 쓰기 권장합니다. 

알고리즘 문제를 풀 때에는 보통 자료형에 대해 직접적으로 물어보거나, 혹은 사이즈 문제로 인해(주로 dp나 greedy) float나 long long int, long long float 를 써야 할 때 제외하고 대부분은 int만 써도 거의 무방했는데 말이죠. 

 

seed = np.array([1.,2.,3.,4.])
f_tensor = torch.FloatTensor(seed)
print(f_tensor)

d_tensor = torch.DoubleTensor(seed)
print(d_tensor)

b_tensor = torch.ByteTensor(seed)
print(b_tensor)

l_tensor = torch.LongTensor(seed)
print(l_tensor)

bool_tensor = torch.ByteTensor([True,False])
print(bool_tensor)
# 결과 
tensor([1., 2., 3., 4.])
tensor([1., 2., 3., 4.], dtype=torch.float64)
tensor([1, 2, 3, 4], dtype=torch.uint8)
tensor([1, 2, 3, 4])
tensor([1, 0], dtype=torch.uint8)

 

 

2. 사칙 연산

element-wise한 연산을 하거나, 내장 module 등을 사용할 수도 있습니다. 

 

  • 행렬의 성질 : AE == EA == A
E = torch.tensor([[1,0],[0,1]])
tensor1 = torch.tensor([[3,5],[6,1]])
tensor2 = torch.tensor([[8,1],[1,1]])

위처럼 두 주어진 2 * 2 크기의 행렬이 있을 때 

 

res = torch.tensor(np.dot(E,tensor1))


print(res)
print(tensor1)

flag=1
for x in range(len(res)):
    for y in range(len(res[x])):
        if res[x][y]==tensor1[x][y]:
            continue 
        else:
            flag=0
            break 
    if flag==0:
        break 

if flag==1:
    print("the two tensor is same")
else:
    print("The two tensor is different each other")
# 결과
tensor([[3, 5],
        [6, 1]])
tensor([[3, 5],
        [6, 1]])
the two tensor is same

 

element-wise

addres = tensor1+tensor2 

print(addres)

address = tensor1.add(tensor2)

print(address)
#결과
tensor([[11,  6],
        [ 7,  2]])
tensor([[11,  6],
        [ 7,  2]])

 

 

  • 곱셈의 경우 조금 사정이 다릅니다. 

1) element-wise한 곱셈 : 각 위치에 대응되는 원소와의 곱 결과를 리턴합니다.

mulress = tensor1 * tensor2 

print(mulress)
#결과.
[[29.  8.]
 [49.  7.]]

 

 

2) 고등학교 시절 배운 행렬 곱셈. 

 - IF A !=B and A not E and B not E: AB !=BA

tensor1 = torch.tensor([[3.,5.],[6.,1.]])
tensor2 = torch.tensor([[8.,1.],[1.,1.]])

matrixres = np.dot(tensor1,tensor2)
print(matrixres)
#결과
[[29.  8.]
 [49.  7.]]

 

 

3. 평균 계산

 

  • 흠 여기에서 다시 위에서 언급한 torch.tensor의 자료형이 중요하게 다가왔습니다.
tensor1 = torch.tensor([[3.,5.],[6.,1.]])
tensor2 = torch.tensor([[3,5],[6,1]])
print("{:.2f}".format(tensor1.mean()))

print("{:.2d}".format(tensor2.mean()))

tensor1의 경우 원소 뒤에 .을 찍어 자료형을 int가 아닌 float형으로 만들어주었고, 결과 역시 잘 나왔지만

tensor2의 경우 int형으로 선언하였고 int에 대한 평균을 구하려고 했더니 잘 되지 않습니다.

#결과.

3.75
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
/tmp/ipykernel_66196/1870130014.py in <module>
      3 print("{:.2f}".format(tensor1.mean()))
      4 
----> 5 print("{:.2d}".format(tensor2.mean()))

RuntimeError: mean(): input dtype should be either floating point or complex dtypes. Got Long instead

 

앵간하면 그러니 float를 위주로 다루는 것이 좋겠습니다. 

 

  • dim 

- dim = 0 으로 설정하면 n차원 행렬의 세로 행에 대한 평균만을 구하고

- dim = 1 으로 설정하면 n차원 행렬의 가로 열에 대한 평균을 구합니다.
print("{}".format(tensor1.mean(dim=0)))
print("{}".format(tensor2.mean(dim=1)))
print("{}".format(tensor1.mean(dim=0)))
print("{}".format(tensor2.mean(dim=1)))
#결과
tensor([4.5000, 3.0000])
tensor([4.5000, 1.0000])
tensor([4.5000, 3.0000])
tensor([4.5000, 1.0000])
 

그런데 왜 {:.2f}를 쓸 수 없는 지 궁금하네요.

tensor3 = torch.tensor([[1,2],[3,4]])
print("{:.2f}".format(tensor3.mean()))
#결과.
---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
/tmp/ipykernel_66196/1599467410.py in <module>
      1 tensor3 = torch.tensor([[1,2],[3,4]])
----> 2 print("{:.2f}".format(tensor3.mean()))

RuntimeError: mean(): input dtype should be either floating point or complex dtypes. Got Long instead.

 

 

 

4. 클래스와 결합.

  • 기본적인 사칙연산을 구현해보세요.
  • 출력 함수 구현 시, 부모 클래스와 자식 클래스의 호출을 구분하세요.
  • 간단한 2차원 지도가 torch.tensor로 주어지면, 이를 통해 bfs를 구현해보세요. 시작점은 좌측 상단이고, 도착점은 우측 하단입니다. 시작점에서 도착점으로 가는 최소 경로를 구하세요. 
  • 'kwargs'키워드를 사용해보세요.
  • super를 써서 상속을 받을 때, 현재 상속을 받는 클래스를 명시하세요. 

 

https://www.acmicpc.net/problem/2178

 

2178번: 미로 탐색

첫째 줄에 두 정수 N, M(2 ≤ N, M ≤ 100)이 주어진다. 다음 N개의 줄에는 M개의 정수로 미로가 주어진다. 각각의 수들은 붙어서 입력으로 주어진다.

www.acmicpc.net

요 문제를 각색해 보았습니다. 

 

  • 부모 클래스
class basic:

    def __init__(self,tensor1,tensor2):
        self.tensor1 = tensor1 
        self.tensor2 = tensor2 
        print("basic parents class is called")
        #self.basic_tensor = torch.tensor(self.tensor1+1)
    
    def show(self):
        print("basic : tensor showing :")
        print('tensor1')
        for x in range(len(self.tensor1)):
            for y in range(len(self.tensor1[x])):
                print(self.tensor1[x][y],end=' ')
            print()
        
        print()
        print('tensor2')
        for x in range(len(self.tensor2)):
            for y in range(len(self.tensor2[x])):
                print(self.tensor2[x][y],end=' ')
            print()
        print()

 

  • 자식 클래스
class specific(basic):
    #private
    queue = deque()

    #일단 specific에 대한 생성자를 먼저 생성해야겠지?
    def __init__(self,**kwargs):
        self.tensor1 = kwargs['tensor1']
        self.tensor2 = kwargs['tensor2']

        self.map = kwargs['map']
        self.visited = [[False]*len(kwargs['map'][0]) for _ in range(len(kwargs['map']))]
        print("specific class is called")

        #basic 클래스가 만들어질 때 자동대로 basic에 대한 self가 만들어지기 때문에
        # 인자로 self.tensor1,self.tensor2만 보내주면 됩니다.  
        super(specific,self).__init__(self.tensor1,self.tensor2)


    def torch_bfs(self):
        
        
        dx = [1,-1,0,0]
        dy = [0,0,1,-1]

        self.queue.append([0,0])
        self.visited[0][0]=True
        cnt=0 
        while self.queue:
            
            curx,cury = self.queue.popleft()

            for i in range(4):
                nx = curx+dx[i]
                ny = cury+dy[i]

                if nx>=0 and nx<len(self.map) and ny>=0 and ny<len(self.map[0]):
                    if self.visited[nx][ny]==False and self.map[nx][ny]==0:
                        self.visited[nx][ny]=True 
                        self.queue.append([nx,ny])
                        cnt+=1
        
        return cnt 

    
    #두 행렬에 대한 dot product를 리턴 
    def torch_dot(self):
        res = torch.tensor(np.dot(tensor1,tensor2))

        return res 
    
    def torch_multiple(self):
        res = torch.tensor(tensor1 * tensor2)
        return res

 

  • Input
x  = torch.tensor([[1.,2.],[7.,3.]])
y = torch.tensor([[6.,3.],[8.,2.]])

mapp = torch.tensor([ [0,1,1,1,1], [0,0,0,1,0],[1,0,0,1,1],[1,0,0,0,0]])

specific_ = specific(tensor1 = x, tensor2 = y, map=mapp)

 

  • Output
specific_.show()

# 결과
basic : tensor showing :
tensor1
tensor(1.) tensor(2.) 
tensor(7.) tensor(3.) 

tensor2
tensor(6.) tensor(3.) 
tensor(8.) tensor(2.)
specific_.torch_bfs()

#결과
9
specific_.torch_dot()

#결과
tensor([[8., 1.],
        [1., 1.]], dtype=torch.float64)
specific_.torch_multiple()

#결과
tensor([[8., 0.],
        [0., 1.]])

 

 

이상입니다. 

간단한 구현이었지만 class, 자료구조, 알고리즘, torch, numpy 연산을 한꺼번에 공부할 수 있어서 

재미있는데, 또 나만 재미있는 거겠죠?