2015년 2월 2일 월요일

Theano 튜토리얼

[출처] http://newsight.tistory.com/161


i파이썬 서버를 mlc서버에 있음
컨트롤 엔터가 실행
쉬프트 엔터가 다른 쉘


심볼릭 변수는, 미지수 변수 x,y등을 할당하는 것

theano.function 은 CUDA C코드를 컴파일해내는 과정이고, 여기서 최적화가 일어난다.
또한 병렬화도 자동으로 처리해준다.

theano.function(inputs, outputs)
inputs는 변수의 리스트를 넣어야한다.
outputs는 인풋들로부터 계산가능한 식이 들어옴.
outputs=f(inputs)


심볼릭 변수들에대한 연산은 그래프로써 표현됨.
심볼릭 algebra들로만 연산을 하다가,
실제로 숫자를 넣어서 계산할 때 theano.function 을 이용해야함.
(함수형 언어의 철학을 따르는 방식)
심파이, 메스메티카 등도 마찬가지의 심볼릭 알제브라를 사용함.



theano.function 은 이 그래프를 CUDA 코드로 컴파일 해주는 것임


CPU/GPU 변환은 theano코드를 실행시키기 전에 해주어야함.
그래야 theano.function 에서 컴파일을 다르게 하겠네.


print(theano.config.floatX) : default 타입 확인
#심볼릭 변수의 타입
T.scalar() : 타입을 지정하지 않는 default 타입인 float32으로 되는 듯
T.iscalar() : int 32 타입을 사용하겠다
T.lscalar() : int 64
T.dscalar() : float64


#심볼릭 변수의 차원
T.scalar() : 0차원 점
T.vector() : 1차원 직선
T.matrix() : 2차원 평면
T.tensor3() : 3차원 큐브
T.tensor4() : 4차원


x=T.matrix()
y=T.matrix()
z=T.dot(x,y)
ftnSum_mat = theano.function([x,y],z)
print(ftnSum_mat(3,4))
----- 3,4는 matrix가 아니라서 에러가 남

x=T.matrix()
y=T.matrix()
z=T.dot(x,y)
ftnSum_mat = theano.function([x,y],z)

a = np.random.random((3,3))
b = np.random.random((3,3))


print(a,b,ftnSum_mat(a,b))


---------
x=T.matrix()
y=T.matrix()
z=T.dot(x,y)
ftnSum_mat = theano.function([x,y],z)

a = np.random.random((3,3))
b = np.random.random((3,3))

print(a,b,ftnSum_mat(a,b))
#이렇게 해도 변수 타입이 넘파이는 float64인데, 씨아노는 float32라서 에러가남
따라서 아래와같이 asarray함수로 float32타입으로 바꿔주어야함.
------
x=T.matrix()
y=T.matrix()
z=T.dot(x,y)
ftnSum_mat = theano.function([x,y],z)

a = np.asarray(np.random.random((3,3)),dtype=theano.config.floatX)
b = np.asarray(np.random.random((3,3)),dtype=theano.config.floatX)

print(a,b,ftnSum_mat(a,b))

-------
결론 -> 항상 float32를 쓰도록 주의할것





python 변수는 실제로 값을 가진 변수임
symbolic 변수는 실제값이 없는 미지수 변수임
shared 변수는 심볼릭 변수인데, 값을 가지고 있는 녀석임.

GPU의 셰어드 메모리에 올라가는 변수로, theano변수가 접근 가능한
값을 가지고 있음.

--
a = np.array([[1,2],[3,4]], dtype = theano.config.floatX)
x = theano.shared(a)
#a 매트릭스가 gpu 메모리에 올라가서, theano 변수가 접근 가능함
----만약 gpu메모리가 모자라면, 여기서 올리다가 에러가 남

print(x.get_value())
x.set_value(x.get_value()+1)
print(x.get_value())


# set 은 cpu -> gpu로 메모리를 올리는 것
# get 은 gpu -> cpu로 메모리를 가져오는 것
따라서 get과 set을 최대한 안써야함.
이건 거의 디버깅 용이라고 할 수 있음.
그리고 안쓰고 어차피 function에서 한방에 계산하기때문에 필요없음
---

a = np.array([[1,2],[3,4]], dtype = theano.config.floatX)
x = theano.shared(a)
y = x**2
ftnShared = theano.function([],y)
print(ftnShared())

#
여기서는 input으로 shared변수를 전달하면 안됨.
input에는 심볼릭 변수만 전달이 가능함.
shared 변수는 함수의 인자가아님.

---


T.nnet.conv.conv2d()
함수 등에 컨벌루션 레이어가 정의되어있음.


-
x = T.scalar()
y = x**2
diff = theano.function([x],T.grad(y,[x]))
print(diff(3))
결과 : [array(6.0, dtype=float32)]
#
array가 붙은 이유는 T.grad의 반환 값이 array라서 그럼.
왜냐하면 x 하나가아니라 여러개의 변수들의 리스트로 편미분을 해서

T.grad(y,[x])
즉, y 함수를 x로 편미분하는 것임,

--
x = T.scalar()
y = x**2
diff = theano.function([x],T.grad(y,[x])[0])
print(diff(3))
결과: 6.0
[0]을 해서, 첫번째 변수로 미분한 값만 array에서 가져옴
--

x = T.scalar()
w = theano.shared(np.array(3,dtype=theano.config.floatX),borrow=True)
obj = (1 - x*w)**2
learn_w = (w, w-0.1*T.grad(obj,w))
learn = theano.function([x],obj,updates=[learn_w])
print(learn(2))

#shared memory는 보통 아주 큰 memory를 GPU에서 사용하기 위함이고, cpu에서 gpu로 shallow copy가 유용하다. borrow=True옵션은 shallow copy이다. False옵션은 deep copy.
#updates는 list of tuples로 준다: [(before, after), (before, after), ...]이고 독립 변수가 여러 개인 경우, target의 독립 변수 당 derivative를 설정할 수 있다.  표기에서 []: list(수정가능), (): tuple(수정불가) 이다.  

updates 는
learn_w = (w, w-0.1*T.grad(obj,w))
이런식으로 정의를 해주어야함.
즉 w를 매 스탭마다 바꾸어갈 식을 정의함

function의 순서가 input 에서 output을 계산하고, 그다음 update 식을 수행함

while(){
learn(2)를 해주어야 이제 계속 업데이트 하는 것임
}
---

x = T.scalar()
w = theano.shared(np.array(3,dtype=theano.config.floatX),borrow=True)
obj = (1 - x*w)**2
learn_w = (w, w-0.1*T.grad(obj,w))
learn = theano.function([x],obj,updates=[learn_w])
print(learn(2),w.get_value())
print(learn(2),w.get_value())
print(learn(2),w.get_value())
print(learn(2),w.get_value())

----
theano는 update가 뉴럴넷을 전제로 깔고 정의됨.
굳이 이걸 안 쓰고, 업데이트를 function 으로 해줘도됨



--

import theano
import theano.tensor as T

class opt_problem:
    def __init__(self):
        # optimization variables
        self.x = theano.shared(numpy.array(100.,dtype=theano.config.floatX), borrow=True)
        self.y = theano.shared(numpy.array(100.,dtype=theano.config.floatX), borrow=True)
        self.pos = [self.x, self.y]
     
        # hyperparameters
        self.lr = theano.shared(np.array(0.05, dtype=theano.config.floatX), borrow=True) # learning rate
     
        # optimization objective
        self.ftn_eqn = (self.x ** 2) + 10*(self.y ** 2)
        self.grad = T.grad(self.ftn_eqn, self.pos)
     
        # evaluating current value
        self.ftn = theano.function([], self.ftn_eqn)
     
    def plot(self, scope=20.):
        # visualizing code
        x = np.arange(-scope, scope, 0.05)
        y = np.arange(-scope, scope, 0.05)
        X, Y = np.meshgrid(x, y)
        X = np.asarray(X, dtype=theano.config.floatX)
        Y = np.asarray(Y, dtype=theano.config.floatX)
        Z = (X ** 2) + 10*(Y ** 2)
        plt.figure()
        C = plt.contour(X,Y,Z)
     
    def build_gd(self):
        self.updates = []      
        for posi, grad in zip(self.pos, self.grad):
            self.updates.append((posi, posi - self.lr*grad))
     
# zip은 pos도 리스트고, grad도 리스트인데 각각을 지퍼로 연결해서 for문을 돌림
        # 독립 변수가 2개 이므로 각 변수에 대한 tuple의 list들 만듬

        # gradient descent function
        self.gd = theano.function([], self.ftn_eqn,
                                  updates=self.updates)
     
problem = opt_problem()
problem.plot()
problem.build_gd()

Xs = []
Ys = []
Xs.append(problem.x.get_value())  # gpu로 내부적으로 shared된 x변수는 값이 계속 바뀜
Ys.append(problem.y.get_value())
for epoch in xrange(20):
    if(epoch%5)==1:
        problem.lr.set_value(problem.lr.get_value()/2)
 
    f_val = problem.gd()
    Xs.append(problem.x.get_value())
    Ys.append(problem.y.get_value())
    plt.plot(Xs, Ys, '-ro')
    print(f_val)


    ----
    numpy에서 아이겐벡터, 아이겐 벨류 계산 가능


    -
 
   matplotlib 는 매틀랩하고 거의 똑같은 인터페이스로 구성되어있음
-
뉴럴넷 짤때, 백프로퍼게이션 구현안하고 짤 수 있음.



--
x = T.scalar()
w = theano.shared(np.array(3,dtype=theano.config.floatX),borrow=True)
obj = (1 - x*w)**2

#learn_w = (w, w-0.1*T.grad(obj,w))

learn = theano.function([x],obj)

w = w-0.1*T.grad(obj,w)
m_update = theano.function([],w)

print(learn(2), m_update())
print(learn(2), m_update())
print(learn(2), m_update())
print(learn(2), m_update())

#
update따로구현해보기