펌 : https://eagle705.github.io/cslog/2018/06/14/PyTorch_0.4/

 

PyTorch 0.4 version up 정리

14 Jun 2018 in ComputerScience on DeepLearning



본 문서는 PyTorch v0.4에서 변경된 APIs를 기술하기 위한 문서입니다. 공식 Migration guide를 참조했습니다.

A. 소개

B. Migration Guide

  • Tensors와 Variables가 합쳐짐
  • 0-dimensional (scalar) Tensors를 지원함
  • Backprop을 위한 값들을 저장하지 않게 했던 volatileflag가 Depreciation됨
  • dtypes, devices그리고 Numpy-style Tensor 생성 함수가 추가됨 (테스트 필요)
  • Writing device-agnostic code (무슨뜻이지)
  • 새로운 edge-case constraints가 nn.Module안에서 submodules, parameters, buffers 이름등으로 생김

Tensors와 Variables가 합쳐짐

torch.Tensor와 torch.autograd.Variable이 이젠 같은 클래스가 되었습니다. 

torch.Tensor 클래스는 old Variable 처럼 히스토리 추적이 가능하게 되었습니다.

예전엔 딱 자료구조용인 Tensor를 선언하고 그걸 Variable로 랩핑해줬다면, 지금은 마치 TensorFlow의 Tensor처럼 안에 기능이 다 통합 된 것 같습니다.

 

지금도 Variable랩핑이 전 처럼 가능하지만, 리턴값은 전과 다르게 torch.Tensor로 나올 것 입니다.

 

즉, 예전코드와 호환은 잘 된다는 것이겠지요. 다만 예전 코드에서의 Variable은 의미상으론 읽기 편해지나, 기능상으론 redundant해졌다고도 볼 수 있을 것 같습니다.

 

 

 

Tensor의 type() 함수가 변경됨

예전엔 type()함수가 데이터의 타입을 리턴했습니다만,

지금은 torch.Tensor를 리턴합니다.(Variable도 그렇겠죠??) 에전처럼 데이터타입을 보고싶으면, 
type() 대신 x.type() 또는 isinstance() 를 사용해야합니다.

 

일반적인 Python의 쓰임과 달라서 저도 많이 당황했습니다.

 

>>> x = torch.DoubleTensor([1, 1, 1]) >>> print(type(x)) # was torch.DoubleTensor "<class 'torch.Tensor'>" >>>

 

print(x.type()) # OK: 'torch.DoubleTensor' 'torch.DoubleTensor' >>> print(isinstance(x, torch.DoubleTensor)) # OK: True True

 

 

autograd가 tracking history를 하는 시점?

autograd는 Tensor의 gradient를 계산하기 위해 computational graph를 고려하기 위해 고안되었었습니다. autograd의 핵심 flag인 requires_grad는 이제 Tensors의 attribute가 되었습니다. 그렇다면 이제 Tensors는 과연 언제부터 computational graph를 고려하게 될까요? 아래 예제를 통해 보시면 직접 requires_grad flag를 True로 지정해줘야함을 알 수 있습니다.

 

>>> x = torch.ones(1) # create a tensor with requires_grad=False (default) >>> x.requires_grad False >>> y = torch.ones(1) # another tensor with requires_grad=False >>> z = x + y >>> # both inputs have requires_grad=False. so does the output >>> z.requires_grad False >>> # then autograd won't track this computation. let's verify! >>> z.backward() RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn >>> >>> # now create a tensor with requires_grad=True >>> w = torch.ones(1, requires_grad=True) >>> w.requires_grad True >>> # add to the previous result that has require_grad=False >>> total = w + z >>> # the total sum now requires grad! >>> total.requires_grad True >>> # autograd can compute the gradients as well >>> total.backward() >>> w.grad tensor([ 1.]) >>> # and no computation is wasted to compute gradients for x, y and z, which don't require grad >>> z.grad == x.grad == y.grad == None True

 

requires_grad flag 조작하기

 

 

default 값은 False로 되어있습니다. 아래와 같이 변경할 수 있습니다. 함수 뒤에가 _ 이면 in-place(새로 대입할 필요가 없는, 그 자리에서 교체되는)로 보시면 됩니다

>>> existing_tensor.requires_grad_() >>> existing_tensor.requires_grad True >>> my_tensor = torch.zeros(3, 4, requires_grad=True) >>> my_tensor.requires_grad True

Tensor와 Variables가 합쳐졌다면, 그럼 .data은 어떻게 된거죠?

.data는 원래 Variables로 부터 Tensor를 추출해내는데 사용되었었습니다. y = x.data는 이제 다음과 같은 의미를 갖는데, y는 Tensor가 되고 x와 같은 데이터를 share(복사가 아니라 share라는게 매우 중요)합니다. 하지만 x의 computational history와 분리되고 requires_grad=False 처리가 됩니다. 

하지만 .data는 다소 unsafe할때가 있습니다. x.data의 변화가 autograd에 의해서 추적이 안되기 때문에, gradient를 계산할 때 값이 잘못될 수 있습니다. (.data는 값을 복사하는게 아니라 share하기 때문에 값이 바뀌면 당연히 gradient에 영향을 줘야하는데 require_grad=False니 못주는 상황입니다.) 
그러므로 추천하기로는 x.detach()사용을 권합니다. 얘는 .data와 비슷한 역할(share data, require_grad=False)을 하지만 값이 바뀌면 autograd가 바뀐걸 알 수 있습니다. 기억할건, 왠만하면 .detach()를 사용하면 된다는 것입니다. 아래 예제를 보시죠.

Tensor.detach() 사용할 때 (권장),

>>> a = torch.tensor([1,2,3.], requires_grad = True) >>> out = a.sigmoid() >>> c = out.detach() >>> c.zero_() tensor([ 0., 0., 0.]) >>> out # modified by c.zero_() !! tensor([ 0., 0., 0.]) >>> out.sum().backward() # Requires the original value of out, but that was overwritten by c.zero_() RuntimeError: one of the variables needed for gradient computation has been modified by an

Tensor.data 사용할 때 (비추),

>>> a = torch.tensor([1,2,3.], requires_grad = True) >>> out = a.sigmoid() >>> c = out.data >>> c.zero_() tensor([ 0., 0., 0.]) >>> out # out was modified by c.zero_() tensor([ 0., 0., 0.]) >>> out.sum().backward() >>> a.grad # The result is very, very wrong because `out` changed! tensor([ 0., 0., 0.])

Scalar Tensors지원 (0-dimensional Tensors)

이전 버전에서는 Tensor vector에서 인덱싱하면 Python number를 줬지만 Variablevector에서는 Tensor vector와는 다르게(inconsistently!) vector of size (1,)을 리턴했습니다. sum함수도 마찬가지였습니다. 이제는 numpy.array 스타일처럼 Scalar Tensor를 지원합니다. (.item() 을 주목해서보자. 나중에 사용할 수도)

>>> torch.tensor(3.1416) # create a scalar directly tensor(3.1416) >>> torch.tensor(3.1416).size() # scalar is 0-dimensional torch.Size([]) >>> torch.tensor([3]).size() # compare to a vector of size 1 torch.Size([1]) >>> >>> vector = torch.arange(2, 6) # this is a vector >>> vector tensor([ 2., 3., 4., 5.]) >>> vector.size() torch.Size([4]) >>> vector[3] # indexing into a vector gives a scalar tensor(5.) >>> vector[3].item() # .item() gives the value as a Python number 5.0 >>> mysum = torch.tensor([2, 3]).sum() >>> mysum tensor(5) >>> mysum.size() torch.Size([])

losses 계산

기존 패턴은 total_loss += loss.data[0] 방식이었습니다. 0.4.0 전에는 loss도 Variable에 랩핑된 텐서로써 (1,) size를 가졌었습니다. 하지만 0.4.0에서는 loss는 이제 0 dimension을 갖는 scalar입니다. loss.item() 을 사용하세요. scalar에서부터 Python number를 얻을 땐 앞으로 .item()을 사용해야합니다. 
Note that if you don’t convert to a Python number when accumulating losses, you may find increased memory usage in your program. This is because the right-hand-side of the above expression used to be a Python float, while it is now a zero-dim Tensor. The total loss is thus accumulating Tensors and their gradient history, which may keep around large autograd graphs for much longer than necessary.

Deprecation of volatile flag

volatile는 이전 버전에서는 주로 inference할때 많이 사용되었었습니다. volatile flag는 이제 deprecated 되었고, 효과가 없습니다. 전에는 Variable에서 volatile=True 조건이면 autograd가 추적하지 않았지만 이젠 torch.no_grad(), torch.set_grad_enabled(grad_mode) 외에 다른 것들로 대체 되었습니다.

>>> x = torch.zeros(1, requires_grad=True) >>> with torch.no_grad(): ... y = x * 2 >>> y.requires_grad False >>> >>> is_train = False >>> with torch.set_grad_enabled(is_train): ... y = x * 2 >>> y.requires_grad False >>> torch.set_grad_enabled(True) # this can also be used as a function >>> y = x * 2 >>> y.requires_grad True >>> torch.set_grad_enabled(False) >>> y = x * 2 >>> y.requires_grad False

 

 

조금 더 이해하고 포스팅을 수정해야겠습니다.

ToDo

  • new data types
  • gpu device allocation 
    (문법이 조금 바뀜, .cuda() -> .to(torch.device("cuda:0" if torch.cuda.is_available() else "cpu")) )

Tensor Flow의 기본 변수및 함수 설명입니다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 텐서플로우의 기본적인 구성을 익힙니다.
import tensorflow as tf
 
# tf.constant: 말 그대로 상수입니다.
hello = tf.constant('Hello, TensorFlow!')
print(hello)
 
= tf.constant(10)
= tf.constant(32)
= tf.add(a, b)  # a + b 로도 쓸 수 있음
print(c)
 
# 위에서 변수와 수식들을 정의했지만, 실행이 정의한 시점에서 실행되는 것은 아닙니다.
# 다음처럼 Session 객제와 run 메소드를 사용할 때 계산이 됩니다.
# 따라서 모델을 구성하는 것과, 실행하는 것을 분리하여 프로그램을 깔끔하게 작성할 수 있습니다.
# 그래프를 실행할 세션을 구성합니다.
sess = tf.Session()
# sess.run: 설정한 텐서 그래프(변수나 수식 등등)를 실행합니다.
print(sess.run(hello))
print(sess.run([a, b, c]))
 
# 세션을 닫습니다.
sess.close()
 
cs


'Deep Learning > Tensor Flow' 카테고리의 다른 글

Tensor Flow Code 분석 - Neural Network  (0) 2017.08.28
Tensor Flow 기반의 Code 분석 코드는 git hub에 잘 정리된 Code를 퍼왔습니다.
출처 : https://github.com/golbin/TensorFlow-Tutorials


주석도 잘 달려 있었으나 제가 초보적인 관점에서 공부를 하는 마음으로 조금 더 추가 했습니다.
 
정리하다 보니 one-hot encode와 reduce_mean의 개념을 추가로 추가로 정리해야 겠네요. ^^
모두 열심히 공부합시다다



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 털과 날개가 있는지 없는지에 따라, 포유류인지 조류인지 분류하는 신경망 모델을 만들어봅니다.
import tensorflow as tf
import numpy as np
 
# [털, 날개] 라고 가정한다 
# 2가지 데이터 . 즉, input Node는 2개
# x_data 는 2가지 데이터를 가진 데이터 Set이 6개
x_data = np.array(
    [[00], [10], [11], [00], [00], [01]])
 
# [기타, 포유류, 조류] 라고 가정한다. 
# 3가지 분류  Output Node는 3개
 
# 다음과 같은 형식을 one-hot 형식의 데이터라고 합니다. ( 하나만 1, 나머지 0 )
 
# 정답(Label)을 의미하는 y_data이 one-hot 형식을 하는 이유는 ? 
# 예측 시 확률 적으로 가장 큰 값을 그것!? 으로 분류하는데 
# 만약 예측 값이 [ 0.8 , 0.1 , 0.1 ] 일 경우 
# 정답[1, 0, 0] 과의 차이를 줄여 나가는 방식으로 # W와 b를 정교화하는데 
# 이 때, ont-hot 형식이어야 차이 값 비교하는 연산을 하기 쉽기 때문이다.
# 만약 데이터가 y_data = np.array([[0],[1],[2],[0],[0],[2]))라면
# 데이터를 비교하기 어렵다. 
# 이럴 경우는 .one_hot이라는 함수를 통해 one-hot형식으로 변경 할 수 있다.
 
y_data = np.array([
    [100],  # 기타
    [010],  # 포유류
    [001],  # 조류
    [100],
    [100],
    [001]
])
 
#########
# 신경망 모델 구성
######
= tf.placeholder(tf.float32)
= tf.placeholder(tf.float32)
 
# 신경망은 2차원으로 [입력층(특성), 출력층(레이블)] -> [2, 3] 으로 정합니다.
= tf.Variable(tf.random_uniform([23], -1.1.))
 
# 편향을 각각 각 레이어의 아웃풋 갯수로 설정합니다.
# 편향은 아웃풋의 갯수, 즉 최종 결과값의 분류 갯수인 3으로 설정합니다.
= tf.Variable(tf.zeros([3]))
 
# 신경망에 가중치 W과 바이어스 b을 적용합니다
# Y' = XW + b
= tf.add(tf.matmul(X, W), b)
# 가중치와 편향을 이용해 계산한 결과 값에
# 텐서플로우에서 기본적으로 제공하는 활성화 함수인 ReLU 함수를 적용합니다.
# Activation 함수 종류는 많은 종류가 있으나, ReLU는 미분하면 0과 1의 값이 나오기 때문에 
# Deep Learning에서 계산을 줄여 성능을 높일 수 있기 때문에 많이 사용한다.
= tf.nn.relu(L)
 
# 마지막으로 softmax 함수를 이용하여 출력값을 사용하기 쉽게 만듭니다
# softmax 함수는 다음처럼 결과값을 전체합이 1인 확률로 만들어주는 함수입니다.
# 예) [8.04, 2.76, -6.52] -> [0.53 0.24 0.23]
# softmax 함수는 확률을 1로 만들어 Label 값 , 
# 즉 , one-hot encoding된 정답 값과 차이를 비교하기 쉽게 하기 위해서 사용한다.
# 참고로 ,one-hot 된 값도 모두 더하면 1, softmax 된 값도 모두 더하면 1이기 때문에 비교가 쉽다.
model = tf.nn.softmax(L)
 
# 신경망을 최적화하기 위한 비용 함수를 작성합니다.
# 각 개별 결과에 대한 합을 구한 뒤 평균을 내는 방식을 사용합니다.
# 전체 합이 아닌, 개별 결과를 구한 뒤 평균을 내는 방식을 사용하기 위해 axis 옵션을 사용합니다.
# axis 옵션이 없으면 -1.09 처럼 총합인 스칼라값으로 출력됩니다.
#        Y         model         Y * tf.log(model)   reduce_sum(axis=1)
# 예) [[1 0 0]  [[0.1 0.7 0.2]  -> [[-1.0  0    0]  -> [-1.0, -0.09]
#     [0 1 0]]  [0.2 0.8 0.0]]     [ 0   -0.09 0]]
# 즉, 이것은 예측값과 실제값 사이의 확률 분포의 차이를 비용으로 계산한 것이며,
# 이것을 Cross-Entropy 라고 합니다.
cost = tf.reduce_mean(-tf.reduce_sum(Y * tf.log(model), axis=1))
 
optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)
train_op = optimizer.minimize(cost)
 
 
#########
# 신경망 모델 학습
######
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init)
 
for step in range(100):
    sess.run(train_op, feed_dict={X: x_data, Y: y_data})
 
    if (step + 1) % 10 == 0:
        print(step + 1, sess.run(cost, feed_dict={X: x_data, Y: y_data}))
 
 
#########
# 결과 확인
# 0: 기타 1: 포유류, 2: 조류
######
# tf.argmax: 예측값과 실제값의 행렬에서 tf.argmax 를 이용해 가장 큰 값을 가져옵니다.
# 예)  [ [ 0 1 0] [ 1 0 0 ] ] -> [1 0] :
# index가 0 1 0 일 때 가장 큰 값의 index는 1 , 1 0 0 index 0 이므로 [ 1 0 ]
#     [[0.2 0.7 0.1] [0.9 0.1 0.0]] -> [1 0]
prediction = tf.argmax(model, 1)
target = tf.argmax(Y, 1)
print('예측값:', sess.run(prediction, feed_dict={X: x_data}))
print('실제값:', sess.run(target, feed_dict={Y: y_data}))
 
is_correct = tf.equal(prediction, target)
accuracy = tf.reduce_mean(tf.cast(is_correct, tf.float32))
print('정확도: %.2f' % sess.run(accuracy * 100, feed_dict={X: x_data, Y: y_data}))
 
 
cs




+ Recent posts