Machine Learning in Action ch.5.1 & 5.2 Logistic Regression

이번 5장에는 우선 로지스틱 회귀가 무엇인지 배우며, 몇 가지 최적화 알고리즘을 배우게 될 것이다. 최적화 알고리즘을 공부하면서 기울기 상승을 배우게 될 것이며, 확률적 기울기 상승을 조절하는 것에 대해 알게 될 것이다. 이러한 최적화 알고리즘은 분류기를 훈련하는 데 사용된다.

코세라 머신 러닝 공부할 때 다 했던건데 기억이 나질 않는다..
솔직히 영어로 봐서 정확히 다 알진 못했지만 다시 하려니 더 열심히 해놨으면 좋았겠다라는 생각이 들었다.


5.1 로지스틱 회구와 시그모이드 함수로 분류하기 : 다루기 쉬운 계단 함수


위와 같은 그래프를 갖는 함수를 시그모이드 함수라한다. 
시그모이드 함수는 우리가 분류 항목을 예측할 수 있는 방정식을 원할 때, 분류 항목이 2개인 경우, 시그모이드 함수는 0 또는 1로 결과값을 출력할 것이다. 

시그모이드 함수는 로지스틱 회귀에서 어떻게 쓰일까?
로지스틱 휘귀 분류기를 위해 가지고 있는 각각의 속성에 가중치를 곱한 다음 서로 더한다.
그 결과를 시그모이드에 넣고 0 과 1 사이의 수를 구하게 된다. 이 수가 0.5보다 크면 1로 분류되고 반대면 0으로 분류된다.

5.2 가장 좋은 회귀 계수를 찾기 위해 최적화 사용하기

설명한 시그모이드 함수의 입력은 z 이며, 이 z 는 다음과 같이 주어진다.

z = w0x0 + w1x1 + ... wnxn

x는 데이터이며 , 가장 좋은 계수 w를 찾고자 한다.
이를 위해서는 최적화 이론으로부터 몇 가지를 고려해야만 한다.

우리는 먼저 기울기 상승을 이용한 최적화를 알아볼 것이다. 


5.2.1 기울기 상승

기울기 상승 알고리즘은 기울기가 제공한 방향으로 한 단계 이동한다.
기울기 연산자는 항상 크게 증가하는 방향으로 향하게 된다.

이러한 단계는 멈춤 조건에 도달할 때까지 되풀이 된다.


5.2.2 훈련 : 기울기 상승을 사용하여 가장 좋은 매개변수 찾기

def loadDataSet():
    dataMat = []; labelMat = []
    fr = open('C:/Users/llewyn/Desktop/MLiA_SourceCode/machinelearninginaction/Ch05/testSet.txt')
    for line in fr.readlines():
        lineArr = line.strip().split()
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
        labelMat.append(int(lineArr[2]))
    return dataMat,labelMat

def sigmoid(inX):
    return 1.0/(1+exp(-inX))

def gradAscent(dataMatIn, classLabels):
    dataMatrix = mat(dataMatIn)             #convert to NumPy matrix
    labelMat = mat(classLabels).transpose() #convert to NumPy matrix
    m,n = shape(dataMatrix)
    alpha = 0.001
    maxCycles = 500
    weights = ones((n,1))
    for k in range(maxCycles):              #heavy on matrix operations
        h = sigmoid(dataMatrix*weights)     #matrix mult
        error = (labelMat - h)              #vector subtraction
        weights = weights + alpha * dataMatrix.transpose()* error #matrix mult
    return weights

1번째 함수는 loadDataSet() 으로서 텍스트 파일을 열어 모든 줄을 읽는다.
2번째 함수는 sigmoid()으로서 그냥 시그모이드 함수이다.
3번째 함수는 gradAscent()으로서 두 인자를 입력으로 받아 시그모이드 함수를 이용해 가중치를 리턴하는 함수이다.

5.2.3 분석 : 의사결정 경계선 플롯하기

이제 하나의 선을 만드는 데 사용된 가중치의 집합을 해결하고, 분류 항목이 서로 다른 데이터를 분리한다. 어떻게 최적화 절차를 이해하도록 선을 플롯할 수 있을까?

def plotBestFit(weights):
    import matplotlib.pyplot as plt
    dataMat,labelMat=loadDataSet()
    dataArr = array(dataMat)
    n = shape(dataArr)[0] 
    xcord1 = []; ycord1 = []
    xcord2 = []; ycord2 = []
    for i in range(n):
        if int(labelMat[i])== 1:
            xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])
        else:
            xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
    ax.scatter(xcord2, ycord2, s=30, c='green')
    x = arange(-3.0, 3.0, 0.1)
    y = (-weights[0]-weights[1]*x)/weights[2]
    ax.plot(x, y)
    plt.xlabel('X1'); plt.ylabel('X2');
    plt.show()


분류는 꽤 잘 되었다. 2~4개 정도 잘못된 점들이 있지만 선형 회귀에서 괜찮은 결과라고 생각한다.


5.2.3 훈련 : 확률적인 기울기 상승 

확률 기울기 상승 알고리즘은 아래와 같다.

def stocGradAscent0(dataMatrix, classLabels):
    m,n = shape(dataMatrix)
    alpha = 0.01
    weights = ones(n)   #initialize to all ones
    for i in range(m):
        h = sigmoid(sum(dataMatrix[i]*weights))
        error = classLabels[i] - h
        weights = weights + alpha * error * dataMatrix[i]
    return weights

변수 h 와 error 가 이제 더 이상 벡터가 아니라 단일 값이란느 것을 제외하고는 확률 기울기 상승은 기울기 상승과 유사하다는 것을 확일 할 수 있다. 


흠 .. 결과가 좋지 않다.. 전에 했던 기울기 상승 알고리즘은 괜찮은 결과를 출력했지만 
확률 기울기 상승 알고리즘은 그렇지 않은 듯 하다..
문제가 무엇일까..

그래서 우리는 데이터 집합을 200번 반복 실행하도록 수정했다.

def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    m,n = shape(dataMatrix)
    weights = ones(n)   #initialize to all ones
    for j in range(numIter):
        dataIndex = list(range(m))
        for i in range(m):
            alpha = 4/(1.0+j+i)+0.0001    #apha decreases with iteration, does not 
            randIndex = int(random.uniform(0,len(dataIndex)))#go to 0 because of the constant
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * error * dataMatrix[randIndex]
            del[dataIndex[randIndex]]
    return weights

위 코드 전의 알고리즘과 유사하지만 개선을 위해 두 가지가 추가되었다. 
1. alpha 가 매번 변한다.  -> 데이터 집합에서 발생하는 높은 빈도의 진동을 개선
2. 가중치를 갱신하는 데 있어 각각의 사례를 임의로 선택한다. -> 주기적인 변화를 줄어들게 함.



훨씬 더 나은 결과를 출력한 것을 볼 수 있다. 

결과는 기울기 상승 알고리즘과 비슷하다고 볼 수 있지만 , 계산은 훨씬 적게 사용하였다.
기울기 상승 알고리즘은 위와 같은 100개의 데이터에서는 효과적일 수 있지만
1억개의 데이터와 같은 큰 데이터에는 확률 기울기 상승 알고리즘이 더 효율적이라는 것을 알 수 있다.





댓글

이 블로그의 인기 게시물

윈도우 설치에서 파티션 설정 오류(NTFS)

[exploit writing] 1_스택 기반 오버플로우 (1) First

하둡 설치 오류 정리