본문 바로가기
R

1강. 딥러닝 첫걸음, 텐서 (tensor) 만들기

by 슬통이 2021. 10. 4.
반응형

torch와의 첫만남

torch패키지를 설치했으니, 한번 만나봐야한다. 다음의 명령어를 통하여 torch를 불러보자.

library(torch)

텐서 (tensor) 만들기

텐서가 무엇이냐! 무언가 대단한 것처럼 보이나, 결국 우리가 R을 배웠을때 사용했던 matrix의 개념을 확장시킨 것이라고 생각하면 된다. 결국 다차원 행렬, 혹은 Array인 것이다.

우리가 많이 쓰는 행렬도 Array에 속하지만, 보통 Array라는 용어는 3차원 이상의 행렬을 암시한다. 이름부터 멋있는 딥러닝인데 다른 용어들이 Array같이 다른 패키지에서 사용되는 것들이랑 동일하면 격이 떨어지므로, 텐서 (tensor) 라고 붙였다. 토치 설명서에 따르면 텐서는 R의 Array와 비슷하나, GPU 계산에도 쓸 수 있다고 나와있다. (응, 그냥 Array.)

또한, 프로그래밍 언어에서 어떤 변수를 만들때, 만든다고 하지않고, “선언한다" 라고 한다. 따라서 앞으로 만든다는 말 대신”선언"이라는 용어를 사용하겠다.

빈 텐서 만들기

속이 빈 5행 3열의 텐서은 다음과 같이 선언한다. 주의할 점은 우리가 이번에 만들 빈 (empty) 텐서와 뒤에서 만들어 볼 0 텐서는 다르다; 빈 텐서은 0과 근접한 쓰레기값이 들어있는 반면에, 0 텐서에는 정말 0이 들어있다.

x <- torch_empty(5, 3)

# 텐서 x값 확인
x
## torch_tensor
##  0  0  0
##  0  0  0
##  0  0  0
##  0  0  0
##  0  0  0
## [ CPUFloatType{5,3} ]
# 텐서 x의 크기 확인
dim(x)
## [1] 5 3

정말 5행 3열의 텐서가 만들어졌다. 이건 마치 우리가 R을 처음 시작하고 텐서을 만드는 것과 아주 유사해서, 실제 R을 만져본 사람이라면 뒤로 빨리 넘기고 싶어하는 욕구가 솓구칠 것이다. 결과값을 잘 살펴보자. CPUFloatType에서 우리는 현재 만든 rand_tensor는 CPU에서 접근이 가능하며, 실수 (float) 타입이라는 것을 이야기해준다.

랜덤 텐서

텐서의 각 자리에 0에서 1사이의 난수로 채워서 만드는 방법이다. torch_rand() 함수를 사용한다.

rand_tensor <-  torch_rand(5, 3)
rand_tensor
## torch_tensor
##  0.6456  0.2337  0.8439
##  0.0745  0.3891  0.3778
##  0.4917  0.7978  0.5682
##  0.9178  0.5938  0.7440
##  0.4264  0.8547  0.7184
## [ CPUFloatType{5,3} ]

참고로 이렇게 만들어진 텐서에는 R에서 텐서과 어레이(array)에 접근할 때사용한 모든 문법들을 사용해서 접근할 수 있다.

rand_tensor[,2]
## torch_tensor
##  0.2337
##  0.3891
##  0.7978
##  0.5938
##  0.8547
## [ CPUFloatType{5} ]
rand_tensor[1:3,]
## torch_tensor
##  0.6456  0.2337  0.8439
##  0.0745  0.3891  0.3778
##  0.4917  0.7978  0.5682
## [ CPUFloatType{3,3} ]
rand_tensor[3:4,c(1, 3)]
## torch_tensor
##  0.4917  0.5682
##  0.9178  0.7440
## [ CPUFloatType{2,2} ]

위의 예제에서 벌써부터 일부 R유저들은 감격의 눈물을 흘릴 수 있다.

주의하기

그렇다, Rtorch에서 텐서의 첫번째 위치는 1부터 시작한다. 이 너무나도 당연한 진리는 파이썬에서는 통하지 않는다.

단위 텐서

4행 4열의 단위 텐서 (identity matrix)를 선언하는 방법은 다음과 같다.

x <- torch_eye(4)
x
## torch_tensor
##  1  0  0  0
##  0  1  0  0
##  0  0  1  0
##  0  0  0  1
## [ CPUFloatType{4,4} ]

영(0) 텐서

텐서의 요소들이 모두 0으로 채워진 3행 5열의 텐서을 선언하는 것은 다음과 같이 torch_zeros() 함수를 사용한다.

x <- torch_zeros(3, 5)
x
## torch_tensor
##  0  0  0  0  0
##  0  0  0  0  0
##  0  0  0  0  0
## [ CPUFloatType{3,5} ]

고급기술: 영리하게 만들기

지금까지는 미리 정해진 값들, 난수나, 0과 1을 채워넣는 법을 배웠다. 하지만, 많은 경우 우리가 직접 정의한 텐서들을 다루게 될 것이다. 이번 섹션에서는 좀 더 영리하게 선언해보는 방법을 배워보자.

텐서 직접선언

가장 핵심적인 내용은 R에서 벡터와 행렬을 정의한 후 torch_tensor() 함수에 넣어주면, 그대로 가져다가 텐서로 바꿔준다는 사실이다. 다음의 예제는 2행 2열의 행렬을 정의한 후, 정의된 행렬을 사용하여 텐서를 만드는 코드이다.

y <- torch_tensor(matrix(c(1, 2, 3, 4, 5, 6), ncol = 2))
y
## torch_tensor
##  1  4
##  2  5
##  3  6
## [ CPUFloatType{3,2} ]

: 연산자 사용

위의 코드가 잘 작동한다는 사실을 알게 되면, 우리가 너무나 익숙한 R의 기본 함수들을 사용하여 텐서를 자유롭게 만들 수 있을 것이다. 앞선 예제는 : 연산자를 통하여 다음과 같이 축약 할 수 있다.

y <- torch_tensor(matrix(1:6, ncol = 2))
y
## torch_tensor
##  1  4
##  2  5
##  3  6
## [ CPULongType{3,2} ]

seq() 함수 사용

seq() 함수는 좀 더 유연한 벡터를 만들 수 있도록 해주므로, 텐서를 만들때 유용하게 사용될 것이다.

y <- torch_tensor(matrix(seq(0.1, 1, by = 0.1), ncol = 2))
y
## torch_tensor
##  0.1000  0.6000
##  0.2000  0.7000
##  0.3000  0.8000
##  0.4000  0.9000
##  0.5000  1.0000
## [ CPUFloatType{5,2} ]

위의 코드는 seq() 함수를 사용해서 벡터를 만들고, 2열을 갖는 행렬을 만든 후, 텐서로 변환을 시켰다. 단, by 옵션의 경우, 결과값이 홀수인지 짝수인지 체크해줘야 하므로, 특정 범위에서의 일정 간격 숫자를 뽑아 행렬로 만들땐 length.out 옵션이 편하다.

y <- torch_tensor(matrix(seq(0, 1, length.out = 10), ncol = 2))
y
## torch_tensor
##  0.0000  0.5556
##  0.1111  0.6667
##  0.2222  0.7778
##  0.3333  0.8889
##  0.4444  1.0000
## [ CPUFloatType{5,2} ]

둘 다 0과 1사이의 벡터를 만들었지만, 결과는 다르다는 것에 주의하자.

알아두기

텐서를 만드는 방법에 대한 핵심은 결국, 자신이 편한 방법으로 만들고 싶은 텐서와 대응되는 R 개체를 만들고, torch_tensor()에 입력 시켜주면 되는 것이다.

%>% 연산자 사용

가끔 R에서 아주 많이 쓰이는 %>% 파이프 연산자를 다른 라이브러리를 사용할 경우 적용할 생각을 못하는 경우가 있다. 왼쪽의 결과 값을 오른쪽의 입력값으로 넘겨주는 파이프 연산자 역시 torch 패키지에서 사용 가능하므로, 텐서 만드는 방법은 그야말로 무궁무진하다.

library(magrittr)
y2 <- torch_tensor(1:5 %>% diag())
y2
## torch_tensor
##  1  0  0  0  0
##  0  2  0  0  0
##  0  0  3  0  0
##  0  0  0  4  0
##  0  0  0  0  5
## [ CPULongType{5,5} ]

텐서와 행렬은 같을까?

앞에서 설명한 것처럼 텐서는 행렬의 개념을 확장시킨 것에 지나지 않겠지만, 그렇다고 같은 취급을 해서도 안된다. 그도 그럴것이 R에서 torch의 텐서와 행렬은 같지 않다. 이러한 사실은 다음과 같이 위에서 만든 텐서 x에 R의 기본 연산자인 행렬곱을 적용해보면 알 수 있다.

x <- torch_zeros(3, 5)
x %*% t(x)
## Error in t.default(x): argument is not a matrix

위의 argument is not a matrix 에러에서 우리는 정말 텐서와 행렬은 다르게 취급된다는 것을 알 수 있다. 그렇다면 ‘텐서끼리의 계산은 어떻게 할까?’ 자연스러운 의문이 든다. 다음 장에서는 텐서의 연산에 대하여 배워보자.

주의하기

R 프로그램 입장에서 torch를 사용해서 정의된 텐서와 base 패키지의 행렬(matrix)은 근본이 다른 객체(object)이다.

반응형

댓글