1. 程式人生 > >Logistic迴歸之梯度上升優化演算法(二)

Logistic迴歸之梯度上升優化演算法(二)

Logistic迴歸之梯度上升優化演算法(二)

有了上一篇的知識儲備,這一篇部落格我們就開始Python3實戰

1、資料準備

資料集:資料集下載

資料集內容比較簡單,我們可以簡單理解為第一列X,第二列Y,第三列是分類標籤。根據標籤的不同,對這些資料點進行分類。

 

import matplotlib.pyplot as plt
import numpy as np

'''
函式說明:載入資料
Parameters:
    None
Returns:
    dataMat - 資料列表
    labelMat - 標籤列表
'''
def loadDataSet():
    dataMat = []  # 建立資料列表
    labelMat = []  # 建立標籤列表
    fr = open('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]))  # 新增標籤
    fr.close()  # 關閉檔案
    return dataMat, labelMat


'''
函式說明:繪製資料集
Parameters:
    None
Returns:
    None
'''
def plotDataSet():
    dataMat, labelMat = loadDataSet()  # 載入資料集
    dataArr = np.array(dataMat)  # 轉換成numpy的array陣列
    n = np.shape(dataMat)[0]  # 資料個數,即行數
    xcord1 = [] ; ycord1 = []  # 正樣本
    xcord2 = [] ; ycord2 = []  # 負樣本
    for i in range(n):
        if int(labelMat[i]) == 1: #1為正樣本
            xcord1.append(dataMat[i][1])
            ycord1.append(dataMat[i][2])
            # xcord1.append(dataArr[i, 1]);ycord1.append(dataArr[i, 2])
        else:                     #0為負樣本
            xcord2.append(dataMat[i][1])
            ycord2.append(dataMat[i][2])
            # xcord2.append(dataArr[i, 1]);ycord2.append(dataArr[i, 2])
    fig = plt.figure()
    ax = fig.add_subplot(111)   #新增subplot
    ax.scatter(xcord1,ycord1,s=20,c='red',marker = 's', alpha=.5,label ='1') #繪製正樣本
    ax.scatter(xcord2,ycord2,s=20,c='green',marker = 's', alpha=.5,label ='0') #繪製正樣本
    plt.title('DataSet') #繪製title
    plt.xlabel('x'); plt.ylabel('y') #繪製label
    plt.legend()
    plt.show()

if __name__ == '__main__':
    plotDataSet()

執行結果如下:

2、訓練演算法:使用梯度上升找到最佳引數

程式碼如下:

import matplotlib.pyplot as plt
import numpy as np

'''
函式說明:載入資料
Parameters:
    None
Returns:
    dataMat - 資料列表
    labelMat - 標籤列表
'''
def loadDataSet():
    dataMat = []  # 建立資料列表
    labelMat = []  # 建立標籤列表
    fr = open('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]))  # 新增標籤
    fr.close()  # 關閉檔案
    return dataMat, labelMat
'''
函式說明:sigmodi函式
Paremeters:
    inX - 資料
Returns:
    sigmoid函式
'''
def sigmoid(inX):
    return 1.0/(1 + np.exp(-inX))

'''
函式說明:梯度上升演算法
Parameters:
    dataMatIn - 資料集
    classLables - 資料標籤
Returns:
    weights.getA() - 求得的權重陣列(最優引數)
'''
def gradAscent(dataMatIn, classLables):
    dataMatrix = np.mat(dataMatIn)  #轉換成numpy的mat
    # print(dataMatrix)
    labelMat =  np.mat(classLables).transpose() #轉換成numpy的mat,並進行轉置
    # print(labelMat)
    m, n =np.shape(dataMatrix)#返回dataMatrix的大小。m為行,n為列
    alpha = 0.001  #移動補償,也就是學習速率,控制更新的幅度
    maxCycles = 500 #最大迭代次數
    weights = np.ones((n,1))
    # print(weights)
    for k in range(maxCycles):
        h = sigmoid(dataMatrix *weights) #梯度上升向量公式
        # print(h)
        #權重係數計算公式
        error = labelMat - h
        weights = weights + alpha * dataMatrix.transpose()*error
    return weights.getA()  #將矩陣轉換為陣列,返回權重陣列



if __name__ == '__main__':
    np.set_printoptions(suppress=True)
    dataMat,labelMat = loadDataSet()
    print(gradAscent(dataMat,labelMat))

其中在gradAscent()函式中的迴圈裡有一個訓練引數的計算公式,這邊我不做推導直接給出。推導網址

執行結果如圖所示:

到此我們已經求解出迴歸係數[w0,w1,w2]。通過求解出的引數,我們可以確定不同類別資料之間的分隔線,畫出決策邊界。

3、繪製決策邊界

程式碼如下:

import matplotlib.pyplot as plt
import numpy as np

'''
函式說明:載入資料
Parameters:
    None
Returns:
    dataMat - 資料列表
    labelMat - 標籤列表
'''
def loadDataSet():
    dataMat = []  # 建立資料列表
    labelMat = []  # 建立標籤列表
    fr = open('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]))  # 新增標籤
    fr.close()  # 關閉檔案
    return dataMat, labelMat


'''
函式說明:繪製資料集
Parameters:
    None
Returns:
    None
'''
def plotDataSet(weights):
    dataMat, labelMat = loadDataSet()  # 載入資料集
    dataArr = np.array(dataMat)  # 轉換成numpy的array陣列
    n = np.shape(dataMat)[0]  # 資料個數,即行數
    xcord1 = [] ; ycord1 = []  # 正樣本
    xcord2 = [] ; ycord2 = []  # 負樣本
    for i in range(n):
        if int(labelMat[i]) == 1: #1為正樣本
            xcord1.append(dataMat[i][1])
            ycord1.append(dataMat[i][2])
            # xcord1.append(dataArr[i, 1]);ycord1.append(dataArr[i, 2])
        else:                     #0為負樣本
            xcord2.append(dataMat[i][1])
            ycord2.append(dataMat[i][2])
            # xcord2.append(dataArr[i, 1]);ycord2.append(dataArr[i, 2])
    fig = plt.figure()
    ax = fig.add_subplot(111)   #新增subplot
    ax.scatter(xcord1,ycord1,s=20,c='red',marker = 's', alpha=.5,label ='1') #繪製正樣本
    ax.scatter(xcord2,ycord2,s=20,c='green',marker = 's', alpha=.5,label ='0') #繪製正樣本
    x = np.arange(-3.0,3.0,0.1)
    y = (-weights[0] - weights[1] * x) / weights[2]
    ax.plot(x,y)
    plt.title('DataSet') #繪製title
    plt.xlabel('x'); plt.ylabel('y') #繪製label
    plt.legend()
    plt.show()


'''
函式說明:sigmodi函式
Paremeters:
    inX - 資料
Returns:
    sigmoid函式
'''
def sigmoid(inX):
    return 1.0/(1 + np.exp(-inX))

'''
函式說明:梯度上升演算法
Parameters:
    dataMatIn - 資料集
    classLables - 資料標籤
Returns:
    weights.getA() - 求得的權重陣列(最優引數)
'''
def gradAscent(dataMatIn, classLables):
    dataMatrix = np.mat(dataMatIn)  #轉換成numpy的mat
    # print(dataMatrix)
    labelMat =  np.mat(classLables).transpose() #轉換成numpy的mat,並進行轉置
    # print(labelMat)
    m, n =np.shape(dataMatrix)#返回dataMatrix的大小。m為行,n為列
    alpha = 0.001  #移動補償,也就是學習速率,控制更新的幅度
    maxCycles = 500 #最大迭代次數
    weights = np.ones((n,1))
    # print(weights)
    for k in range(maxCycles):
        h = sigmoid(dataMatrix *weights) #梯度上升向量公式
        # print(h)
        error = labelMat - h
        weights = weights + alpha * dataMatrix.transpose()*error
    return weights.getA()  #將矩陣轉換為陣列,返回權重陣列



if __name__ == '__main__':
    np.set_printoptions(suppress=True)
    dataMat,labelMat = loadDataSet()
    weights = gradAscent(dataMat,labelMat)
    plotDataSet(weights)

其中繪製的分隔線設定了sigmoid函式為0,回憶上一篇內容,0是兩個分類的分解出,因此我們設定\large 0 = w_{0}x_{0}+w_{1}x_{1}+w_{2}x_{2},然後接觸X2和X1的關係式(及分割線的方程,注意X0=1)。

執行結果如下:

從分類結果可以看出,還有幾個點是錯的。但是這個方法徐良大量的計算(300次乘法),在下一篇文章會對演算法稍作改進。

4、總結

Logistic迴歸的一般過程:

  • 收集資料:採用任一方法收集資料
  • 準備資料:需要距離計算,因此要求資料型別為數值型
  • 分析資料:採用任意方法對資料進行分析
  • 訓練演算法:大部分時間用於訓練,為了找到最佳的分類迴歸洗漱
  • 測試演算法:訓練步驟完成,分類將會很快。

參考文獻: