In [1]:
%pylab
%load_ext watermark
%watermark
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
회로: 다섯 개의 유닛 (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 에 기울기 적용
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()))
In [ ]: