지난번 포스팅에서는 TensorFlow 의 tf.constant() 로 텐서를 만드는 방법(https://rfriend.tistory.com/718)을 소개하였습니다. 이번 포스팅에서는 TensorFlow 의 변수 (Variable) 에 대해서 소개하겠습니다. 

 

(1) TensorFlow 변수(tf.Variable)는 무엇이고, 상수(tf.constant)는 무엇이 다른가? 

(2) TensorFlow 변수를 만들고(tf.Variable), 값을 변경하는 방법 (assign)

(3) TensorFlow 변수 연산 (operations)

(4) TensorFlow 변수 속성 정보 (attributes)

(5) 변수를 상수로 변환하기 (converting tf.Variable to tf.constant)

 

 

(1) TensorFlow 변수(tf.Variable)는 무엇이고, 상수(tf.constant)와는 무엇이 다른가? 

 

 텐서플로의 튜토리얼의 소개를 보면 "변수는 프로그램 연산에서 공유되고 유지되는 상태를 표현하는데 사용을 권장("A TensorFlow variable is the recommended way to represent shared, persistent state your program manipulates.") 한다고 합니다. 말이 좀 어려운데요, 변수와 상수를 비교해보면 금방 이해가 갈 것입니다. 

 

 텐서플로 변수(Variable)는 값의 변경이 가능(mutable value)한 반면에, 상수(constant)는 값의 변경이 불가능(immutable value)합니다. 변수는 값의 변경이 가능하고 공유되고 유지되는 특성 때문에 딥러닝 모델을 훈련할 때 자동 미분 값의 back-propagations 에서 가중치를 업데이트한 값을 저장하는데 사용이 됩니다. 변수는 초기화(initialization)가 필요합니다. 

 

TensorFlow Variable, 변수

 

 

 

(2) TensorFlow 변수를 만들고(tf.Variable), 값을 변경하는 방법 (assign)

 

TensorFlow 변수는 tf.Variable(value, name, dtype, shape) 의 메소드를 사용해서 만들 수 있습니다. 

 

import tensorflow as tf
print(tf.__version__)
#2.7.0


## After construction, the type and shape of the variable are fixed.
v = tf.Variable([1, 2])
print(v)
#<tf.Variable 'Variable:0' shape=(2,) dtype=int32, numpy=array([1, 2], dtype=int32)>

 

 

변수의 값을 변경할 때는 assign() 메소드를 사용합니다.  assign_add(), assign_sub() 를 사용해서 덧셈이나 뺄셈 연산을 수행한 후의 결과로 변수의 값을 업데이트 할 수도 있습니다. 

 

## The value can be changed using one of the assign methods.
v.assign([3, 4])

print(v)
#<tf.Variable 'Variable:0' shape=(2,) dtype=int32, numpy=array([3, 4], dtype=int32)>


## The value can be changed using one of the assign methods.
v.assign_add([10, 20])

print(v)
#<tf.Variable 'Variable:0' shape=(2,) dtype=int32, numpy=array([13, 24], dtype=int32)>

 

 

 

tf.Variable(value, shape=tf.TensorShape(None)) 처럼 shape 매개변수를 사용하면 형태(shape)를 정의하지 않은 상태에서 변수를 정의할 수도 있습니다. 

 

## The shape argument to Variable's constructor allows you to 
## construct a variable with a less defined shape than its initial-value
v = tf.Variable(1., shape=tf.TensorShape(None))

print(v)
#<tf.Variable 'Variable:0' shape=<unknown> dtype=float32, numpy=1.0>


v.assign([[1.]])
#<tf.Variable 'UnreadVariable' shape=<unknown> dtype=float32, 
#numpy=array([[1.]], dtype=float32)>

 

 

 

(3) TensorFlow 변수 연산 (operations)

 

변수는 텐서 연산의 인풋(inputs to operations)으로 사용될 수 있습니다. 아래 예에서는 변수와 상수간 원소 간 곱과 합을 구해보았습니다. 

 

## Variable created with Variable() can be used as inputs to operations. 
## Additionally, all the operators overloaded for the Tensor class are carried over to variables. 
w = tf.Variable([1., 2.])
x = tf.constant([3., 4.])

## element-wise product
tf.math.multiply(w, x)
# <tf.Tensor: shape=(2,), dtype=float32, numpy=array([3., 8.], dtype=float32)>



## element-wise addition
w + x
# <tf.Tensor: shape=(2,), dtype=float32, numpy=array([4., 6.], dtype=float32)>

 

 

 

(4) TensorFlow 변수 속성 정보 (attributes)

 

텐서플로의 변수에서 이름(name), 형태(shape), 데이터 유형(dtype), 연산에 이용되는 디바이스(device) 속성정보를 조회할 수 있습니다. 

 

## Attributes
v = tf.Variable([1., 2.], name='MyTensor')
print('name:', v.name)
print('shape:', v.shape)
print('dtype:', v.dtype)
print('device:', v.device)

# name: MyTensor:0
# shape: (2,)
# dtype: <dtype: 'float32'>
# device: /job:localhost/replica:0/task:0/device:CPU:0

 

 

 

(5) 변수를 상수로 변환하기 (converting tf.Variable to tf.constant)

 

변수를 상수로 변환하려면 tf.convert_to_tensor(Variable) 메소드를 사용합니다. 

 

## converting tf.Variable to Tensor
c = tf.convert_to_tensor(v)

print(c)
# tf.Tensor([1. 2.], shape=(2,), dtype=float32)

 

 

 

[ Reference ]

* TensorFlow Variable: https://www.tensorflow.org/api_docs/python/tf/Variable

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅은 페이스북의 R User Group 에서 아이디어 토론 주제로 올라왔길레 한번 짜봤던 코드입니다.  


'0'과 '1'로 가지는 긴 벡터를 처음부터 끝까지 쭉 훓어가면서 


(1) '0'이 연속으로 3번 나오면 그 구간을 기준으로 나누어서 

=> (2) 나누어진 구간을 새로운 벡터로 생성하기


입니다.  


'0'이 연속으로 나오는 회수는 분석가가 필요로 하는 회수로 지정할 수 있도록 매개변수(argument)로 지정해서 프로그래밍해보겠습니다. 


간단한 예제 벡터로서, '0'과 '1'을 원소로 해서 30개의 무작위수(이항분포 랜덤 샘플링, 0이 80%, 1이 20%)로 구성된 벡터를 생성해보겠습니다. 


 

> rm(list=ls()) # clean all


# Sample vector


> set.seed(123) # for reproducibility

> vec_raw <- sample(c(0, 1), size=30, replace=TRUE, prob=(c(0.8, 0.2)))

> vec_raw

 [1] 0 0 0 1 1 0 0 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0

> vec <- vec_raw # copy for manipulation




R 프로그래밍을 하고자 하는 일의 아웃풋 이미지는 아래와 같습니다. 

'0'이 연속 세번 나오는 구간을 노란색으로 표시를 했습니다. 이 구간을 기준으로 원래의 벡터를 구분해서 나눈 후에(split), 각각을 새로운 벡터로 생성하여 분리해주는 프로그램을 짜보는 것이 이번 포스팅의 주제입니다. 




아래에 wihle() 반복문, if(), else if() 조건문, assign() 함수를 사용해서 위의 요건을 수행하는 코드를 짜보았습니다. 원래 벡터에서 제일 왼쪽에 있는 숫자가 '0'이 아니면 하나씩 빼서 빈 벡터에 차곡차곡 쌓아 놓구요, '0'이 3개 나란히 있는 '000'이 나타나면 그동안 쌓아두었던 벡터를 잘라내서 다른 이름의 벡터로 저장하고, '000'도 그 다음 이름의 벡터로 저장하면서 원래 벡터에서 '000'을 빼놓도록 하는 반복문입니다. assign() 함수는 저장하는 객체의 이름에 paste()를 사용해서 변화하는 숫자를 붙여주고자 할 때 사용하는 함수인데요, 자세한 사용법 예제는 여기(=> http://rfriend.tistory.com/108)를 참고하세요. 


아래 프로그램에서 'bin_range'에 할당하는 숫자를 변경하면 원하는 회수만큼 '0'이 반복되었을 때 구간을 분할하도록 조정할 수 있습니다.  가령, '0'이 연속 10번 나오면 분할하고 싶으면 bin_range <- 10  이라고 할당해주면 됩니다. 



##---------------------------------------------

# Vector Bin Split by successive '0's criteria

##---------------------------------------------


# Setting

vec_tmp <- c() # null vector

bin_range <- 3 # successive '0's criterion

vec_idx <- 1

vec_num <- 1


# Zero_Bin_Splitter

while (length(vec) > 0){

  if (sum(vec[1:bin_range], na.rm=T) != 0){

    vec_tmp[vec_idx] <- vec[1]

    vec <- vec[-1]

    vec_idx <- vec_idx + 1

  } else if (is.null(vec_tmp)){

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  } else {

    assign(paste0("vec_split_", vec_num), vec_tmp)

    vec_tmp <- c() # initialization

    vec_num <- vec_num + 1

    assign(paste0("vec_split_", vec_num), vec[1:bin_range])

    vec <- vec[-(1:bin_range)]

    vec_idx <- 1 # initialization

    vec_num <- vec_num + 1

  }

}


# delete temp vector and arguments

rm(vec, vec_tmp, bin_range, vec_idx, vec_num)




아래는 위의 프로그램을 실행시켰을 때의 결과입니다.  원래 의도했던대로 잘 수행되었네요. 




=====================================================

아래의 코드는 페이스북 R User Group의 회원이신 June Young Lee 님께서 data.table 라이브러리를 사용해서 짜신 것입니다.  코드도 간결할 뿐만 아니라, 위에 제가 짠 코드보다 월등히 빠른 실행속도를 자랑합니다. 대용량 데이터를 빠른 속도로 조작, 처리하려면 data.table 만한게 없지요. 아래 코드는 저도 공부하려고 옮겨 놓았습니다. 


똑같은 일을 하는데 있어서도 누가 어떤 로직으로 무슨 패키지, 함수를 사용해서 짜느냐에 따라서 복잡도나 연산속도가 이렇게 크게 차이가 날 수 있구나 하는 좋은 예제가 될 것 같습니다. 이런게 프로그래밍의 묘미이겠지요? ^^  June Young Lee 님께 엄지척! ^^b



# by June Young Lee


library(data.table)


# 0이 세번 연속으로 들어 있는 아무 벡터 생성

vec <- c(0,0,0,3,10,2,3,0,4,0,0,0,1,2,50,4,0,0,32,1,0,0,0,1,1)


# data.table함수인 rleid이용하여 연속인 행들을 구분하는 idx1 컬럼 생성 

dt <- data.table(v1=vec, idx1=rleid(vec))


# idx1 기준으로 갯수를 세어 N 컬럼 생성 

dt[,N:=.N, by=idx1]


# 0이 세번 연속으로 나온 그룹(=N컬럼변수가 3인)을 idx2로 체크하기 

dt[v1==0&N==3,idx2:=1L]


# split 하기 위한 기준 벡터를 마찬가지로 rleid함수를 이용하여 idx3로 생성


dt[,idx3:=rleid(idx2)]


# data.table의 split 함수 적용하고, lapply 적용하여 v1 칼럼만 추출 

res <- lapply(split(dt, by="idx3"), function(x) x[,v1])

res

# $`1`

# [1] 0 0 0

# $`2`

# [1] 3 10 2 3 0 4

# $`3`

# [1] 0 0 0

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`5`

# [1] 0 0 0

# $`6`

# [1] 1 1


# 0이 3번 연속으로 나온 놈들을 남겨두도록 작성한 이유는, 

# 위치확인 & 확인용입니다. 

# 필요없으면, 아래 정도의 코드를 추가하면 되겠네요.


res2 <- res[!unlist(lapply(res, function(x) length(x)==3&sum(x)==0))]

res2

# $`2`

# [1] 3 10 2 3 0 4

# $`4`

# [1] 1 2 50 4 0 0 32 1

# $`6`

# [1] 1 1

 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되셨다면 아래의 '공감~'를 눌러주세요. 



728x90
반응형
Posted by Rfriend
,