2015년 6월 29일 월요일

SPP net

SPP-net은 기존 conv-net과 달리 분류기(fc층)에 입력하기 위한 특징 갯수를 입력 영상 크기에 무관하게 추출할 수 있는 장점을 가진다.


기존 cnn에서 자동차나 등대는 crop 후 고정된 크기로 변경되어 conv층에 입력된다. 이는 fc 층이 항상 고정된 특징(즉, 고정된 크기의 특징맵) 수를 요구하기 때문이다.
SPP-net 에서 이미지는 크기 변경없이 그대로 입력된다. 이로 인해 fc층 직전 특징맵의 크기가 달라지는데 맵 크기가 달라져도 SPP에 의해 고정된 특징 수 추출이 가능하다.


아래에서는 물체 위치 추출 입장에서 spp 적용을 요약한다:


논문에서 R-CNN과 비교하여 주로 설명하는 이유는 물체 detection을 위한 후보 영역(window) 생성을 R-CNN처럼 Selective search를 사용하기 때문이다.

기존 R-CNN은 테스트 할 대상 이미지 내에서 2,000개 후보 영역을 생성시켜 각 영역을 검증하며, 2,000개의 영역은 중복되어 생성되기 때문에 계산량이 큰 문제가 발생한다.
각 후보 window는 227x227 크기로 만들어 net를 통해 특징을 생성하고 이 값을 SVM을 통해 분류한다.


이와 비교하여 SPP의 가장 큰 장점은 특징맵(conv-pool 반복 층에서 마지막 conv층 출력 map)$^{(1)}$ 크기와 무관하게 고정된 특징 수를 추출 한다는 것이다.  따라서, conv계산 한번 만으로 모든 후보 영역(window)에 대해 동일한 spp 특징(분류기(svm나 fc layer)에 넣기 위한) 수를 만들 수 있다.


상기 그림은 3-level spp를 사용하고 있고, bin이 4x4, 2x2, 1x1인 경우를 보여준다.  각 bin에서 max pooling을 사용한다.


R-CNN과 SPP 차이를 요약하면 R-CNN은 이미지 영역에서 직접 특징을 추출하지만, SPP는 특징 맵 영역에서 window-wise 특징을 추출한다.


만일 4-level SPP(spatial pyramid pool)를 사용한다면 (즉, 1x1, 2x2, 3x3, 6x6로 전부 50개) 3-level 보다 더 많은 수의 특징을 추출한다. 이 때 마지막 conv층 특징 map 이미지 수가 256개라면 50x256=12,800개 특징 추출이 가능하다.




위치 추출 학습에서 positive sample을 만들기 위해서는 ground-truth 윈도를 사용한다. negative sample은 약 30%정도 positive와 겹치게 추출한다. 만일 negative끼리 70%이상 겹치면 제거한다. 이렇게 만든 정/부 샘플을 SVM으로 학습하게 된다.




적용 방법은 2가지로 대상 이미지를 1-scale/5-scale 두가지로 크기 변경 후 실행하는데,
1-scale에서는 대상 이미지 작은 변의 크기(min(w,h)=s)를 688로 크기 변경 후에 실행.
5-scale에서는 s를 {480,570,688,864,1200}으로 스케일링 후 각기 적용한다.





특징 맵의 가시화



화살표는 특징 맵 내 가장 강한 응답과 이미지 내에서 해당 위치를 보여준다. Pooling을 통하여 이러한 특징을 추출한다.
차량에서는 윈도와 바퀴 부분이 강한 특징으로 추출된다. 우측은 다양한 영상 입력에 대해 강한 응답을 나타내는 영역을 보여준다. 차 윈도나 바퀴와 비슷한 외형 물체들이 표시 되었다.
두번째 그림에서는 ^ 형상 이미지 영역과 v 형상 이미지 영역에서 강한 response를 나타낸다.





이미지(또는 윈도) 크기에 무관하게 특징 갯수를 정하는 방법


주어진 스케치는 특징 맵 크기가 13x13이다. 요구된 bin이 3x3이라면 필기한 방법을 통해 얻어진 win크기 5와, stride 4를 적용하여 특징을 추출한다. 그러면 3x3개의 bin이 정의되므로 모두 9개 특징 추출이 가능하다.
여기서 a=13(맵 크기)이고 n=3(bin 수)에 대한 경우를 살펴 보았다. a가 바뀔경우라도 win과 stride만 재 계산해서 적용하면 항상 고정된 bin 수를 얻을 수 있다.



(주1) 일반적 CNN의 경우, 입력 영상 크기가 달라지면 FC층에 입력되기 직전인 마지막 conv층 맵 크기가 달라지므로 맵 크기에 따라 정의되는 특징 갯수가 달라지게 된다.




References
[1] Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition, PAMI 2015.



2015년 6월 3일 수요일

Multi-Box prediction

학습

물체 위치를 나타내는 출력노드가 400개(100개의 위치), 각 위치에 대한 confidence를 나타내는 출력노드가 100개인 CNN이 있다. 학습 되기 전의 CNN 내부 파라메터는 임의로 초기화되어 있다.

CNN은 하나의 학습 이미지에 대해 100개의 위치와 각 위치에 대한 100개의 conf를 출력하도록 구성된다. 이때 위치는 $p_i$, conf는 $s_i$로 표시한다. 따라서, 각 위치와 conf는 인덱스 i로 연결되어 있다.


학습 샘플은 내부에 복수 개의 물체를 가지고 있다. 따라서 ground-truth는 cnn이 예측한 위치와 일치하는 것도 있고, 그렇지 않은 것도 있을 것이다.

이때 위치가 일치하는 것 만을 학습에 반영시키기를 원한다. 즉 gt와 겹치지 않은 prediction은 학습에 기여하지 못한다.

따라서 예측된 위치가 gt 위치와 일치하는 것에 해당하는 i를 선정한다. 현 단계에서 물체 부류정보는 전혀 반영되어 있지 않다.


i=1~100이기 때문에 gt와 겹치는 위치를 가진 i만을 선정한다$^{(1)}$. 예를 들면 i={2,50,72}라고 하면 100개의 prediction 중에서 3개만 true와 일치하는 경우이다.



loss 함수의 정의는 box위치의 예측 값인 $p_i$가 true의 위치와 일치할 수록 좋고, conf $s_i$는 클 수록 좋게 구성한다.

따라서 loss함수를 수학적으로 모델링하고 이 loss함수를 minimize하도록 CNN의 파라메터들은 update된다.

학습동안 CNN의 파라메터들을 적절히 조정되서 $p_i$가 true와 가능하면 일치되도록, $s_i$는 가능하면 크지도록 갱신이 발생하게 된다.



상기한 네트웍 구조와 최적화 방법의 장점은 각 학습 sample이 입력되었을 때, 출력 값 중 true 라벨과 겹치는 prediction은 몇 개 되지 않기 때문에 갱신 연산이 빠르다는 것이다.

학습이 끝나면 새로운 이미지에 대한 출력은 prediction box위치가 가능하면 true와 잘 겹치고 conf도 큰 출력을 주게 된다.




분류

테스트할 이미지에 대해 다수의 내부 crop을 추출한다. 각 추출 crop를 CNN에 넣으면 100 개의 box와 conf 값이 출력된다.

또한 이 crop은 class를 prediction하는 부류 분류기에도 입력한다. 분류기는 crop이 무슨 물체를 가졌는지 출력을 주게된다. 따라서 각 crop은 부류에 대한 확률 값을 가지게 된다.


crop에 대한 데이터를 모으면, 각 crop에서 나온 100개 box의 위치 정보, 각 box의 conf 값, 또 각 crop은 부류에 속하는 확률 값을 가진다.


이때 특정 부류에 대한 box들을 모으고 각 box의 conf 값을 이용하여 nonmax supp하면 위치 detection이 가능하다.














각주
(1) Jaccard similarity.  두 영역의 교집합과 합집합의 비가 threshold(예를 들면 0.5) 이상인지를 평가.  $s_j(a,b)= {{a \cap b} \over {a \cup b}}$



Reference
[1] Scalable object detection using deep neural networks
[2] CVPR'2014 Tutorials




2015년 5월 13일 수요일

Vehicle place recognition

작성 중...

기 학습된 큰 규모의 CNN을 가져와서 이미 방문한 장소의 인식에 사용한다.  Overfeat를 사용하는데, overfeat는 대용량의 big data를 학습한 것이라서, 여러 사물에 대한 discriminative 능력이 뛰어날 것이라는 가정을 적용한 것이 아닐까 싶다.  이미 학습이 잘 되어 있을테니 새로운 입력 영상에 대한 특징의 분별 능력이 우수할 것이라는 가정을 사용한다.

Overfeat은 ImageNet 2012의 db를 이용하여 학습된 것인데 120만개의 이미지로 1000개의 부류를 학습하였다.

장소 인식 테스트는 Eynsham db의 9,575프레임(차량이 약 70 km을 주행하면서 얻은 데이터이다)에 대해 수행하며, 주행은 두 번으로 나누어 진행한다. 첫번째는 모델 db를 추출하는 과정, 두번째는 테스트 db를 추출해서 기 구축된 모델 db와 비교하여 장소를 인식하는 과정이다.

먼저 overfeat의 구조를 살펴 본다.
overfeat는 input층을 제외하고는 모두 21개의 층을 가진다:

Input,
conv-maxPooling-ReLU 층 2개, (6 layers)
conv-zeropadding$^{(1)}$-ReLU$^{(2)}$ 2개, (6 layers)
conv-zeropadding-ReLU-maxPooling 1개, (4 layers)
FC-ReLU 2개, (4 layers)
FC 1개(output), (1 layers)



그림 1. Overfeat의 구조. 색으로 각 layer를 표시.



장소 인식 방법은 모델 특징과 테스트 특징의 Euclidean 거리를 사용하며, overfeat에 도로 영상을 입력시켜 나온 특징(중간 층의 특징을 포함)을 상호 비교하는 것으로 인식을 수행 한다.


overfeat의 중간 층의 특징은 21개 층에서 모두 다 정의될 수 있지만 실험 결과를 보면 중간 층인 9,10 layer의 특징이 좋은 성능을 낸다.



먼저 이미지는 흑백영상으로 만들고 histogram 정규화를 해서 밝기의 variation을 줄여서 사용한다. 크기는 256x256조정해서 overfeat에 입력한다.
(overfeat 논문을 참고하면, 231x231보다 더 큰 이미지는 처리할 수 있다. 256x256으로 만들어 사용한다)


각각의 layer output을 $L_k(I),k=1,...,21$이라 하면, 비교를 위한 confusion matrix$^{(3)}$를 다음과 같이 구성한다:

$M_k(i,j)=d(L_k(I_i),L_k(I_j))$, $i=1,...,R, j=1,...,T$

여기서 $R$은 training image의 수이고, $T$는 test image의 수이다.  $M_k(i,j)$는 $i$번째 training image와 $j$번째 test image사이의 Euclidean 거리를 나타낸다.


매칭이 잘 되는 위치를 찾기 위해 거리 값이 가장 작은 특징 벡터를 가진 요소를 찾는다:

$M_k(j)=arg min M_k(i,j)$ for all $i$, and $j=1,...,T$


매칭 후보를 찾는 방법은 두 가지를 이용하는데, 첫번째는 spatial continuity조건이다.
연속된 두 테스트이미지가 주는 특징 거리의 차이가 작을 것이라는 가정이다:

$if \left\vert M_k(u-1)-M_k(u) \right\vert \le \epsilon$, $\forall u \in [j-d,j]$
여기서 $j=d,...,T$

$j$는 현재 테스트 이미지이고 $d$는 이전 시간으로 얼마 만큼 먼가를 나타낸다.

예를 들어 $d$=5라면, 현재 프레임 $j$에서 이전 5프레임 동안 두 인접 $M$값의 차이가 모두 작아야한다. 즉 traing-test 특징 값의 차이가 일정시간 동안 계속 작다면 상호 매칭으로 본다.

두번째는 매칭점 사이의 기울기 평가인데 간단하므로 논문을 참고한다.




References
[0] Convolutional neural network based place recognition, Z.Chen, M.Milford etc., 2014. Queensland Univ. of Tech.
[1] zero-padding, 이미지에 mask로 convolution연산을 하면 경계를 일부분 먹으면서 결과영상의 크기가 줄어든다. 이것을 원 이미지와 동일한 크기를 만들고 경계의 값은 0으로 채워준다.  또 다르게는 미리 경계를 0으로 두른 후에 마스크 연산을 하면 처리후 경계 부에 검은 띠 형성을 막을 수 있다.
[2] Rectifier activation function으로 $f(x)=max(0,x)$로 정의된다.
[3] Confusion matrix에서 가로(행)은 실제 부류를 나타낸다. 열은 예측된 부류를 가리킨다. 아래 표의 1행에서 실제 Cat를 Cat로 예측한 샘플은 5개, Dog로 오분류한 것은 3개, Rabbit로 분류한 것은 0이다.

(from wikipedia)










2015년 3월 8일 일요일

DNN 적용 예

Training method
mini-batch(size): 256
momentum: 0.9
weight decay: 5*10^-4
dropout ratio: 0.5
initial learning rate: 10^-2, decreased by a factor of 10(when validation set accuracy stopped improving).


Initialization of network weights
-shallow network(hidden layer 수가 작아 비교적 학습하기 용이한 구조)로 먼저 시작.
-더 깊은(복잡한) network 구조를 학습할 때 이미 학습된 shallow 구조의 weight 값을 사용.
-예를 들면, 입력 layer에 바로 붙어 있는 초기 4개의 layer와 출력단에 붙어 있는 FC(full connection) 3개는 shallow weight로 초기화하고, 중간의 나머지 layer만 random하게 초기화
-random 초기화에도 uniform이 아니라 N(0,10^-2)의 정규분포에서 weight를 샘플링


Training image의 준비
-학습에 사용할 이미지의 크기는 224x224의 고정 크기를 사용하는데 이것은 스케일 변환된 원 이미지에서 random하게 잘라낸(cropped) 샘플이다.
-학습 샘플 수를 증가시키기 위해 잘라낸 샘플을 다시 수평 flip시키고 random color shift(Krizhevsky 2012)시켜 더 많은 샘플을 만들어 낸다.


Training image size
-원 이미지를 등방성(가로, 세로방향으로 동일한 비로) 스케일링하고 여기서 학습 샘플을 추출(crop)한다. 인자 s를 scaling된 이미지의 가로, 세로 크기 중에서 더 작은 값이라 하면(즉, s=min(w,h)이다) s는 224보다는 더 커야 한다.
-s가 크다면(s>>224라면) crop image는 스케일 이미지 내의 작은 부분이 되고, 작은 물체나 물체의 부분을 표현하게 된다.


s값을 설정하는 2가지 방법
(1) 고정 크기로 s값을 사용(single-scale training): 실험에서 두개의 고정 값을 사용하였음(s=256, s=384). 먼저 s=256를 사용하여 net를 학습. s=384에서 학습 속도를 올리기 위해 s=256학습 초기치를 사용하고 더 작은 학습률(10^-3) 사용
(2) s값을 독립적으로 rescaling(multi-scale training): [s_min, s_max] 범위 사이에서 s를 랜덤하게 선택하고 이 값으로 이미지를 rescaling함. (s_min=256, s_max=512사용). 이렇게 하면 이미지 내의 물체는 각기 다른 크기가 될 수 있으므로 더 좋음.  scaling jittering(small fast variation)에 의한 training set augmentation과 유사함.
-학습 속도를 높이기 위해 먼저 single-scale model(s=384)로 학습하고 multi-scale model를 적용.





Reference
K. Simonyan and A. Zisserman, Very deep convolutional networks for large-scale image recognition, ICLR 2015.




 

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따로구현해보기