02. svm 구현

|

01. softmax classifier와 neural network 구현

03. RNN 구현

저번시간에 공부한 것과 별로 달라지는 것이 없다. softmax를 먼저 보고 봐야함.

Svm

loss function

전체 loss function은 아예 똑같지.. $L_i$가 다음과 같이 로 정의되며 $\frac{\partial L_i}{\partial f_k}$를 구하면 비슷해지겠지?

$\frac{\partial L_i}{\partial f_k}$ 구해보자!

  1. $k \neq y_i$이고 $L_{i,k}$가 0보다 클 때 => $\frac{\partial L_i}{\partial f_k} = 1$
  2. 나머지 경우 => $\frac{\partial L_i}{\partial f_k} = 0$

이는 forward pass시에 구할 수 있다. 코드만 살펴보면 되겠다.

import matplotlib.pyplot as plt
import numpy as np
from data import getData, showData

X, y, K = getData()
N, D = X.shape

# hyperparameters
step_size = 1e-0
reg = 1e-3
delta = 1
# initialize parameters randomly
W = 0.01 * np.random.randn(D, K)
b = np.zeros((1, K))

for i in range(200):
    scores = np.dot(X, W) + b
    # forward pass
    correctScore = scores[range(N), y]  # (N, )
    correctScoreMat = correctScore.T * np.ones( scores.T.shape )
    correctScoreMat = correctScoreMat.T  # (N, K)로 dimension 맞춰줌

    marginMat = np.ones(scores.shape) * delta  # (N, K) delta만큼 다 더해주기 위해
    L = np.maximum(scores - correctScoreMat + marginMat, 0)
    L[range(N), y] = 0  # y_i인 부분은 0이 되어야하니까!

    data_loss = np.sum(L)/N

    reg_loss = 0.5*reg*np.sum(W*W)
    loss = data_loss + reg_loss

    if i % 10 == 0:
        print("iteration {}: loss {}".format(i, loss))
    # backward
    dscores = np.ones(L.shape)
    dscores[L == 0] = 0
    dscores /= N

    dW = np.dot(X.T, dscores)
    db = np.sum(dscores, axis=0, keepdims=True)
    dW += reg*W

    W += -step_size * dW
    b += -step_size * db

# evaluate training set accuracy
scores = np.dot(X, W) + b
predicted_class = np.argmax(scores, axis=1)  # (N, )

print('training accuracy: %.2f' % (np.mean(predicted_class == y)))

01. softmax classifier와 neural network 구현

|

02. SVM 구현

03. RNN 구현

여기서는 원문을 번역겸 겸사겸사 따라해본다.

Data manipulation

먼저 데이터를 만들어보자!

import matplotlib.pyplot as plt
import numpy as np

N = 100 # class마다 점의 갯수
D = 2 # point의 dimension
K = 3 # class 갯수

##################### Data Manipulation #####################
X = np.zeros((N*K, D)) # data matrix (각 row가 data point)
y = np.zeros(N*K, dtype='uint8') # class label들

for j in range(K):
    ix = range(N*j, N*(j+1))
    r = np.linspace(0.0, 1, N) # radius
    t = np.linspace(j*4, (j+1)*4, N) + np.random.randn(N)*0.2 # theta

    X[ix] = np.c_[r*np.sin(t), r*np.cos(t)] # concatenation along the second axis.
    y[ix] = j

# visualize data
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.show()

주석에 대충 달아놨다.

  • np.c_ : list의 각 element를 second axis를 따라 concatenation해주는 함수
  • np.random.randn : “standard normal” distribution 에서 sample하는 함수
  • np.linspace(start, end, num=50) : start부터 end까지 num갯수만큼 쪼개서 array를 만들어줌
  • plt.scatter(x, y, c=y, s=40, cmap=plt.cm.Spectral) : x,y가 그릴 그림의 포인트들. s는 size인데 이는 array일 수 있고 scalar의 경우 그냥 사이즈이다. c와 cmap은 컬러 관련인데 나중에 알아보자… 별로 중요하지 않다고 생각함.

Softmax

Score를 구하기 (prediction)

이제 softmax를 써보자! score를 먼저 구해봅시당!

# initialize parameters randomly
W = 0.01 * np.random.randn(D, K)
b = np.zeros((1, K))

# compute class scores for a linear classifier
scores = np.dot(X, W) + b

loss 구하기

forward 부터!

먼저 $f$를 single point에 대한 score vector라고 가정한다(즉, 여기서는 3개의 숫자가 적혀진 array라고 볼 수 있다). 이 때, 해당 point에 대한 loss function은 다음과 같이 정의된다. Softmax classifier의 f를 (unnormalized) log probability들을 가지고있다고 생각하기 때문에, 이를 exponentiate하면 (unnormalized) 확률을 얻을 수 있다. 이제 이를 normalize하면 확률을 제대로 얻을 수 있다고 보는 것!

이 식을 살펴보면, $f_{y_i}$가 작은 score이면 당연히 $L_i$가 커지고, 이는 큰 loss를 야기한다. 반대의 경우는 $log(1) = 0$이니까 loss가 0에 가까워짐.

이제 Softmax classifier loss의 data loss, regularization loss를 합친 전체 식을 보면 다음과 같다.

이제 이를 코드로 써보자!

#### Loss 계산 ####

### forward ###

# get unnormalized probabilities
exp_scores = np.exp(scores)
# normalize them for each example
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)

correct_logprobs = -np.log(probs[range(N*K),y])

# compute the loss: average cross-entropy loss and regularization
data_loss = np.sum(correct_logprobs)/(N*K)
reg_loss = 0.5*reg*np.sum(W*W)
loss = data_loss + reg_loss

Backpropagation으로 gradient 구하기

이제 loss를 줄이기 위해 gradient를 구해야한다. 여기서는 중간 파라미터로 $p$를 만들어 쓴다.

요렇게 놓고 score $f$에 관한 Loss, $Li$의 gradient를 구해보자!

구하면 요렇게 나온다.

내가 해본 증명> 인데 각각 텀을 구해서 곱하면 된다.
일단 처음 term은 $ \frac{\partial L_i }{ \partial p_{y_i} } = -\frac{1}{p_{y_i}} $로 쉽게 구할 수 있다.
$\frac{\partial p_{y_i} }{ \partial f_k }$를 구하려면 (1) $k = y_i$, (2) $k \neq y_i$ 두가지에 대해서 구해야한다.
(1) $k = y_i$ 일 때,

이므로 gradient는

(2) $k \neq y_i$ 일 때,

이므로 gradient는

따라서 코드는 다음과 같다.

### backward ###
dscores = probs
dscores[range(N*K),y] -= 1
dscores /= N*K

dW = np.dot(X.T, dscores)  ## 요거야 뭐 dimension 맞추다보면 알것고...
db = np.sum(dscores, axis=0, keepdims=True)  # 사실 b는 N개가 복제된 형태라 더한 듯...
dW += reg*W # don't forget the regularization gradient

Neural network

여기서 달라진 것은 hidden layer가 하나 생긴 것 뿐.. 따라서 ReLU에 대한 미분만 제대로 할 수 있으면 된다. 코드만 일단 냉겨놔야지~

import matplotlib.pyplot as plt
import numpy as np
from data import getData, showData

X, y, K = getData()
N, D = X.shape

# hyperparameters
step_size = 1e-0
reg = 1e-3

# initialize parameters randomly
h = 100
W = 0.01 * np.random.randn(D, h)
b = np.zeros((1, h))
W2 = 0.01 * np.random.randn(h, K)
b2 = np.zeros((1, K))

for i in range(10000):
    O = np.dot(X, W) + b
    hidden = np.maximum(0, O)
    scores = np.dot(hidden, W2) + b2
    # forward pass
    exp_scores = np.exp(scores)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    correct_logprobs = -np.log(probs[range(N),y])
    data_loss = np.sum(correct_logprobs)/(N)
    reg_loss = 0.5*reg*(np.sum(W*W) + np.sum(W2*W2))
    loss = data_loss + reg_loss

    if i % 1000 == 0:
        print("iteration {}: loss {}".format(i, loss))
    # backward
    dscores = probs
    dscores[range(N),y] -= 1
    dscores /= N

    dW2 = np.dot(hidden.T, dscores)
    db2 = np.sum(dscores, axis=0, keepdims=True)
    dW2 += reg*W2
    
    dhidden = np.dot(dscores, W2.T)
    dO = dhidden
    dO[hidden <= 0] = 0
    dW = np.dot(X.T, dO)
    db = np.sum(dO, axis=0, keepdims=True)
    dW += reg*W

    W2 += -step_size * dW2
    b2 += -step_size * db2
    W += -step_size * dW
    b += -step_size * db

# evaluate training set accuracy
hidden_layer = np.maximum(0, np.dot(X, W) + b)
scores = np.dot(hidden_layer, W2) + b2
predicted_class = np.argmax(scores, axis=1)


print('training accuracy: %.2f' % (np.mean(predicted_class == y)))

Decorator - 꾸며보자!

|

Why?

flask를 쓰다보니 app.route 어쩌구저쩌구 하는 decorator가 보인다. 먼저 decorator가 어떤 역할을 하는지 살펴보자!

decorator란?

decorator는 python의 함수 본체를 꾸며주는 역할을 한다. 다음 코드를 보자.

decorator 예제

def simple_decorator(f):
    def wrapper():
        print "Entering Function"
        f()
        print "Exited Function"
    return wrapper

@simple_decorator
def hello():
    print "Hello world"

hello()

''' 
"Output"
Entering Function
Hello world
Exited Function
'''

인자를 갖는 decorator 예제

다음은 인자를 갖는 decorator 이다. 이번엔 class로 만들어보았다.

class Verbose:
    def __init__(self, f):
        print "Initializing Verbose."
        self.func = f

    def __call__(self, *args, **kwargs):
        print "Begin", self.func.__name__
        self.func(*args, **kwargs);
        print "End", self.func.__name__

@Verbose
def my_function(name):
    print "hello,", name

my_function("Hulk")

'''
"Output"
Begin my_function
hello, Hulk
End my_function
'''

다수의 decorator 지원

이런 것도 된다!

def makebold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped

def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped

@makebold
@makeitalic
def hello():
    return "hello world"

print hello() ## returns "<b><i>hello world</i></b>"

decorator에 partial을 끼얹자!

위의 예제에서, 다수의 decorator를 쓸 수 있었다. 그런데 만약, bold, italic, strike-through 등 여러가지의 decorator를 만들어야한다면?

decorator에 argument를 넣을 수는 없을까 고민이 될 것이다.

functoolspartial 함수를 사용하면 이를 만들 수 있다.

from functools import partial

def addtag(fn, tag):
    def wrapped():
        return "<" + tag + ">" + fn() + "</" + tag + ">" 
    return wrapped

makebold = partial(addtag, tag="b")
makeitalic = partial(addtag, tag="i")
    
@makebold
@makeitalic
def hello():
    return "hello world"

print(hello()) ## returns "<b><i>hello world</i></b>"

groupby로 list 내부에 그룹을 지어보자!

|

groupby import

from itertools import groupby

어떤 함수인가?

itertools.groupby(iterable[, key]) iterable을 받아서 연속된 키와 그룹을 리턴한다.

>>> from itertools import groupby
>>> things = [("animal", "bear"), ("animal", "duck"), ("plant", "cactus"), ("vehicle", "speed boat"), ("vehicle", "school bus")]
>>> group = groupby(things, lambda x: x[0])
>>> for key, items in group:
...     print(key)
...     for item in items:
...             print(item)
... 
animal
('animal', 'bear')
('animal', 'duck')
plant
('plant', 'cactus')
vehicle
('vehicle', 'speed boat')
('vehicle', 'school bus')

그런데 주의해야할 점이 존재한다. iterator를 돌면서 다른 key를 만나면 새 그룹을 만드는 식으로 동작하기 때문에, 같은 키라도 분산되어있으면 한 그룹에 뭉쳐지지 않는다. 다음은 소팅을 먼저 하고 grouping 시키는 wrapper를 만든 예제이다.

from itertools import groupby

def printThings(things, groupFunc):
    print("===================BEGIN===================")
    for key, group in groupFunc(things, lambda x: x[0]):
        for thing in group:
            print "A %s is a %s." % (thing[1], key)
        print " "
    print("====================END====================")

def myGroupby(iterable, func):
    tmpiterable = sorted(iterable, key=func)
    return groupby(tmpiterable, func)

things = [("animal", "bear"),
          ("vehicle", "speed boat"),
          ("animal", "duck"),
          ("plant", "cactus"),
          ("vehicle", "school bus")]
printThings(things, groupby)
printThings(things, myGroupby)
### OUTPUT ###
===================BEGIN===================
A bear is a animal.
 
A speed boat is a vehicle.
 
A duck is a animal.
 
A cactus is a plant.
 
A school bus is a vehicle.
 
====================END====================
===================BEGIN===================
A bear is a animal.
A duck is a animal.
 
A cactus is a plant.
 
A speed boat is a vehicle.
A school bus is a vehicle.
 
====================END====================

os 모듈 자주 쓰는거 정리

|

os 모듈은 script를 짤 때 많이 쓴다.

여태까지 써왔던 것을 정리하자.

함수들

search관련

Function Description Example
os.path.join(path, file) pathfile을 연결시켜준다. os.path.join('/folder','name') => '/folder/name'
os.path.splitext(name) name이름, 확장자로 분리시켜준다. os.path.splitext('/folder/name/a.html') => ('/folder/name/a', '.html')
os.listdir(path) path안의 폴더, 파일list로 만들어준다. os.listdir('.') => ['.DS_Store', '.git', ..., 'requirements.txt', 'testbed']

파일 조작 관련

Function Description Example
os.path.exists(path) path가 존재하는지 True, False로 나타낸다. os.path.exists('.') => True
os.makedirs(folder) folder를 만든다. os.makedirs(os.path.join('/home', 'testFolder))
os.rename(oldFile, newFile) rename함수. oldFilenewFile로 이동한다.(폴더 이동에도 쓰인다. os.rename('./test.txt', './temp/test.txt') : test.txt를 temp 안으로 이동

예제

import sys, os
import subprocess


def listExtFiles(path, extension):
    files = []
    for name in os.listdir(path):
        if os.path.isfile(os.path.join(path, name)):
            if name == ".DS_Store":
                continue
            filename, file_extension = os.path.splitext(name)
            if file_extension == extension:
                files.append(name)
    return files


def moveFiles(path, folder, fileList):
    if not os.path.exists(os.path.join(path, folder)):
        os.makedirs(os.path.join(path, folder))

    fileListWithPath = [os.path.join(path, file) for file in fileList]
    fileListWithNewPath = [os.path.join(path, os.path.join(folder, file)) for file in fileList]

    for oldFile, newFile in zip(fileListWithPath, fileListWithNewPath):
        print oldFile
        print newFile
        os.rename(oldFile, newFile)

def savePng(fileName, htmlName):
    try:
        check = subprocess.check_output(["webkit2png", "-F", "-z", "2", "--delay=0.5", "--filename="+fileName, htmlName], stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        string = "command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output)
        print string
    return 0

script, path = sys.argv

samsonFiles = listExtFiles(path, '.samson')
htmlFiles = listExtFiles(path, '.html')

pngFiles = [file[:-5] for file in htmlFiles]

for pngFile, htmlFile in zip(pngFiles, htmlFiles):
    html = os.path.join(path, htmlFile)
    png = os.path.join(path, pngFile)
    savePng(png, html)

pngFiles = listExtFiles(path, '.png')

moveFiles(path, "html", htmlFiles)
moveFiles(path, "png", pngFiles)
moveFiles(path, "samson", samsonFiles)