In [1]:
%pylab
%load_ext watermark
%watermark


Using matplotlib backend: MacOSX
Populating the interactive namespace from numpy and matplotlib
2016-09-13T20:09:04

CPython 3.5.2
IPython 5.0.0

compiler   : GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.28)
system     : Darwin
release    : 15.6.0
machine    : x86_64
processor  : i386
CPU cores  : 8
interpreter: 64bit

유닛, 게이트 코드

1장에서 사용했던 코드를 이용합니다.


In [2]:
class Unit(object):
    
    def __init__(self, value, grad):
        # 정방향에서 계산되는 값
        self.value = value
        # 역방향일 때 계산되는 이 유닛에 대한 회로 출력의 변화율
        self.grad = grad

        
class MultiplyGate(object):
    
    def forward(self, u0, u1):
        self.u0 = u0
        self.u1 = u1
        self.utop = Unit(self.u0.value * self.u1.value, 0.0)
        return self.utop
    
    def backward(self):
        # 출력 유닛의 기울기를 받아 곱셉 게이트의 자체 기울기와 곱하여(체인 룰) 입력 유닛의 기울기로 저장합니다.
        self.u0.grad += self.u1.value * self.utop.grad
        self.u1.grad += self.u0.value * self.utop.grad
        
        
class AddGate(object):
    
    def forward(self, u0, u1):
        self.u0 = u0
        self.u1 = u1
        self.utop = Unit(self.u0.value + self.u1.value, 0.0)
        return self.utop

    def backward(self):
        # 입력에 대한 덧셈 게이트의 기울기는 1 입니다
        self.u0.grad += 1 * self.utop.grad
        self.u1.grad += 1 * self.utop.grad

SVM 을 위한 회로 코드

회로: 다섯 개의 유닛 (x,y,a,b,c) 을 입력 받고 하나의 유닛을 출력합니다. 그리고 입력에 대한 기울기를 계산합니다


In [3]:
class Circuit(object):
    
    def __init__(self):
        # 게이트 생성
        self.mulg0 = MultiplyGate()
        self.mulg1 = MultiplyGate()
        self.addg0 = AddGate()
        self.addg1 = AddGate()

    def forward(self, x, y, a, b, c):
        self.ax = self.mulg0.forward(a, x) # a*x
        self.by = self.mulg1.forward(b, y) # b*y
        self.axpby = self.addg0.forward(self.ax, self.by) # a*x + b*y
        self.axpbypc = self.addg1.forward(self.axpby, c)  # a*x + b*y + c
        return self.axpbypc

    def backward(self, gradient_top): # 상위 게이트로 부터 기울기를 전달 받음
        self.axpbypc.grad = gradient_top
        self.addg1.backward() # axpby 와 c 에 기울기 적용
        self.addg0.backward() # ax 와 by 에 기울기 적용
        self.mulg1.backward() # b 와 y 에 기울기 적용
        self.mulg0.backward() # a 와 x 에 기울기 적용

SVM 클래스


In [4]:
class Svm(object):
  
    def __init__(self):
        # 파라메타를 랜덤하게 초기화
        self.a = Unit(1.0, 0.0) 
        self.b = Unit(-2.0, 0.0)
        self.c = Unit(-1.0, 0.0)

        self.circuit = Circuit()

    def forward(self, x, y): # x 와 y 는 유닛 객체라 가정합니다
        self.unit_out = self.circuit.forward(x, y, self.a, self.b, self.c)
        return self.unit_out
  
    def backward(self, label): # 레이블은 +1 또는 -1
        # a,b,c 의 기울기 초기화
        self.a.grad = 0.0
        self.b.grad = 0.0 
        self.c.grad = 0.0

        # 회로의 출력에 따라 당겨야 할 힘(기울기)을 계산합니다
        pull = 0.0
        if label == 1 and self.unit_out.value < 1:
            pull = 1 # 스코어가 너무 낮네요. 증가시켜야 합니다.

        if label == -1 and self.unit_out.value > -1:
            pull = -1 # 스코어가 너무 높네요. 감소시켜야 합니다.

        self.circuit.backward(pull) # x,y,a,b,c 에 기울기를 적용합니다
    
        # 0의 방향으로 각 파라메타에 비례해서 정형화 힘을 추가합니다
        self.a.grad += -self.a.value
        self.b.grad += -self.b.value
  
    def learn_from(self, x, y, label):
        self.forward(x, y) # 정방향 계산 (모든 유닛의 .value 속성을 채웁니다)
        self.backward(label) # 역방향 계산 (모든 유닛의 .grad 속성을 채웁니다)
        self.parameter_update() # 파라메타를 업데이트합니다
  
    def parameter_update(self):
        step_size = 0.03 # 학습 속도를 높이기 위해 0.03 으로 셋팅합니다.
        self.a.value += step_size * self.a.grad
        self.b.value += step_size * self.b.grad
        self.c.value += step_size * self.c.grad

경사 하강법을 적용합니다.

데이터 준비


In [5]:
data = []
labels = []
data.append([1.2, 0.7])
labels.append(1)
data.append([-0.3, -0.5])
labels.append(-1)
data.append([3.0, 0.1])
labels.append(1)
data.append([-0.1, -1.0])
labels.append(-1)
data.append([-1.0, 1.1])
labels.append(-1)
data.append([2.1, -3])
labels.append(1)

분류의 정확도를 계산하기 위한 함수


In [6]:
def eval_training_accuracy():
    num_correct = 0;
    for i in range(len(data)):
        x = Unit(data[i][0], 0.0)
        y = Unit(data[i][1], 0.0)
        true_label = labels[i]

        # 예측과 레이블이 맞는지 검사
        unit_out = svm.forward(x, y)
        predicted_label = 1 if unit_out.value > 0 else -1;
        if predicted_label == true_label:
            num_correct += 1

    return num_correct / len(data);

학습 루프


In [7]:
svm = Svm()
for iter in range(400):
    # 임의의 데이터 포인트 추출
    i = int(np.floor(np.random.random() * len(data)))
    x = Unit(data[i][0], 0.0)
    y = Unit(data[i][1], 0.0)
    label = labels[i]
    svm.learn_from(x, y, label)

    if iter % 25 == 0: # 매 25번 반복마다...
        print('training accuracy at iter %d: %f' % (iter, eval_training_accuracy()))


training accuracy at iter 0: 0.666667
training accuracy at iter 25: 0.833333
training accuracy at iter 50: 0.833333
training accuracy at iter 75: 0.833333
training accuracy at iter 100: 0.833333
training accuracy at iter 125: 0.833333
training accuracy at iter 150: 0.833333
training accuracy at iter 175: 0.833333
training accuracy at iter 200: 1.000000
training accuracy at iter 225: 0.833333
training accuracy at iter 250: 1.000000
training accuracy at iter 275: 0.833333
training accuracy at iter 300: 1.000000
training accuracy at iter 325: 1.000000
training accuracy at iter 350: 1.000000
training accuracy at iter 375: 1.000000

In [ ]: