1. 程式人生 > >機器學習筆記(三):線性迴歸大解剖(程式碼部分)

機器學習筆記(三):線性迴歸大解剖(程式碼部分)

這裡,讓我手把手教你如何用邏輯迴歸分析資料

根據學生分數預測是否錄取:

#必備3個庫
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

讓我們讀入資料:

import os
path = "data" + os.sep + "LogiReg_data.txt";   # os.sep 表示的是檔案分割符 “ / ” 因為win和linux分隔符不同所以 為了跨平臺就不直接寫

pdData = pd.read_csv(path,header=None,names=['Exam1','Exam2','Admitted']);  #header預設使用第一行做標題行,這裡改為自己設定
pdData.shape;    #資料的緯度  (100 , 3)  
pdData.head()    #不加引數表示預設顯示5行

前兩列資料表示分數,最後一列表示是否錄取:

在這裡插入圖片描述

讓我們簡單看一下,錄取和沒錄取在不同分數上的顯示情況

positive = pdData[pdData['Admitted' ] == 1]        #  錄取的集合
nagative = pdData[pdData['Admitted' ] == 0]      #  沒錄取集合

fig, ax=plt.subplots(figsize=(10,5))   # ax前面必須有fig  figsize設定子圖的寬和高

#分1 做x  分2做y 
ax.scatter(positive['Exam1'],positive['Exam2'],s=30,c='blue',marker='o',label='Admitted') #s=點大小  c='顏色' marker=標記
ax.scatter(nagative['Exam1'],nagative['Exam2'],s=30,c='r',marker='x',label='Not Admitted')
ax.legend()

ax.set_xlabel('Exam1 score')
ax.set_ylabel('Exam2 score'
plt.show()

可以看到有明顯的分界線:在這裡插入圖片描述

接下來就是正式的分析了 我們的目標是:建立分類器 ,求出3個引數(分數1的引數,分數2的引數,誤差) 設定閾值,根據閾值判斷是否錄取(這裡>=0.5就當作錄取)

要完成的模組: #1.sigmoid:對映到概率(0-1)的函式 g(z) = 1/(1+e^-z) #2.model: 返回預測結果值 #3.cost: 根據引數計算損失 #4.gradient: 計算每個引數的梯度方向 #5.descent: 進行引數更新 #6.accuracy: 計算精度

1.sigmoid 函式

值域(0-1) g(-∞) = 0 g(+∞) = 1 g(0)=0.5

def sigmoid(z):
    return 1/(1+np.exp(-z))

2.model

就是用資料作為sigmoid的輸入,從而構造出預測函 資料 (1,x1,x2) * (θ0 , θ1 , θ2)的轉置 = θ0+θ1X1+θ2X2 然後傳到 sigmoid中得到結果

def model(X,theta):
    return sigmoid( np.dot(X,theta.T) )   
#結果中 θ0需要和1相乘,資料中缺少1這1列,所以要補進
pdData.insert(0,'Ones',1); #在第0列(第一列之前,索引之後 插入列名為‘ones’,所有資料為1的一列
data = pdData.as_matrix()  #將pdData從DataFrame 轉為矩陣,方便進行矩陣乘法
cols = data.shape[1] 	#資料有幾列  4   ones  exam1  exam2  admitte
X = data[:,0:cols-1]  #資料矩陣X  1,分數1,分數2
y = data[:,cols-1:cols];  # 結果(是否選上
theta=np.zeros([1,3]) # 3個引數   引數個數=資料個數+1

3.損失函式

這裡就是對數似然函式 * -1/m [加-是因為要把梯度上什轉為梯度下降,1/m是為了求平均] 似然函式 = 每個 x 對應的 P(y|x;θ) 的連乘 , 然後取對數得到對數似然函式

損失函式(loss function)是用來衡量模型的預測值f(x)與真實值Y的不一致程度,它是一個非負實值函式,損失函式越小,模型越優(還需考慮過擬合等問題)

這裡選擇用 似然函式*-1/m 作為損失函式,是不是因為 似然函式越大越好 , 加一個 負號就越小越好 , 所以只要平均似然函式的值越來越小了,就說明越來越好了?

損失函式用: J(θ) = -1/m * sum( (y*log(hθ(X)) + (1-y)log(1-hθ(x)) ) )

def cost(X,y,theta):
    tt = y-model(X,theta)
    left = np.multiply(tt,tt)
    return np.sum(left)/(len(X))

用 1/m * sum( (y - hθ(X) )^2 ) 做損失函式 效果相同

def cost(X,y,theta):
     left = np.multiply(y,np.log(model(X,theta)    
     right = np.multiply(1-y,np.log(1-model(X,theta)))
     return np.sum(left+right)/(-len(X)

4.求梯度

多維影象的梯度,也就是多維影象在某點各個方向的斜率中斜率最大的那個

演算法就是對J(θ) 求偏導 dJ(θ)/dθ = -1/m * sum( (yi - hθ(xi))xij )

def gradient(X,y,theta):
    grad = np.zeros(theta.shape) #有幾個θ,就找幾個梯度
    
     # dJ(θ)/dθ = -1/m * sum( (yi - hθ(xi))xij )
    error = (model(X,theta) - y).ravel();  #  yi - hθ(xi) 將-號提取進來   ravel將多維降為一維矩陣 ,因為原error是列向量,所以作用和專T類似 
    #求每個θi的梯度
    for j in range (theta.shape[1]):
        term = np.multiply(error,X[:,j])  #Xij   表示的是 X的每一個樣本,針對每一個引數θj 都只取和θj有關的那一列即X( , j)行不限
        grad[0,j] = np.sum(term) / len(X)
    return grad

4 (附屬) 停止策略

梯度更新需要有個頭,有個結束點

#設計了3種不同的停止策略
STOP_ITER = 0 #按照迭代次數停止 (一次迭代即更新一次梯度值)
STOP_COST = 1 #目標函式兩次迭代之間的損失(差異)很小   
STOP_GRAD = 2 #兩次迭代之間梯度沒什麼變化


def stopCriterion(type,value,threshold):  
    #設定3種不同的停止策略
    if type == STOP_ITER: return  value > threshold   
    elif type == STOP_COST: return  abs(value[-1]-value[-2]) < threshold
    elif type == STOP_GRAD: return  np.linalg.norm(value) < threshold;   #求2範數  sqrt(x1^2+x2^2+.....)

4 (附屬) 對資料進行洗牌

打亂(防止自己收集資料的時候按照某種規律收集)

def shuffleData(data):
    np.random.shuffle(data)  #洗牌資料
    #重新獲取資料
    cols = data.shape[1] #資料有幾列  4   ones  exam1  exam2  admitted
    X = data[:,0:cols-1]  #資料矩陣X  1,分數1,分數2
    y = data[:,cols-1:cols];  # 結果(是否選上)
    return X,y

5,6重點!

檢視不同梯度下降策略對時間的影響

import time
def descent(data, theta, batchSize, stopType ,thresh, alpha):  #batchSize  =  1 隨機梯度下降    =樣本個數 梯度下降   =1-樣本個數  小批量樣本夏婧
    init_time = time.time();#開始時候的時間                 #thresh  閾值   alpha學習率
    i = 0;#迭代次數   
    k = 0;#batch ,即當前已經消耗的樣本數量
    X,y = shuffleData(data)
    grad = np.zeros(theta.shape) #梯度
    costs = [cost(X,y,theta)] #損失值
    
    #***5.descent: 進行引數更新***
    while True:
        grad = gradient(X[k:k+batchSize],y[k:k+batchSize],theta)
        #更新引數 , 讓θ 不停的靠近梯度為0的點
        theta = theta - alpha*grad;
        
        costs.append(cost(X,y,theta))  #用新的 θ 計算出新的損失值
        i += 1; #迭代次數 +1
    
        k+=batchSize 
        if k >= len(X):    
            k=0;
            X,y = shuffleData(data);  #重新洗牌
        
        value = 0;
        if stopType == STOP_ITER:  value = i;
        elif stopType == STOP_COST: value = costs;
        elif stopType == STOP_GRAD: value = grad;
        if stopCriterion(stopType,value,thresh):break;  #是否跳出
        

    return theta,i-1,costs,grad,time.time()-init_time

功能函式,畫圖

#功能函式,畫圖
def runExpe(data, theta, batchSize, stopType, thresh, alpha):
    #import pdb; pdb.set_trace();
    theta, iter, costs, grad, dur = descent(data, theta, batchSize, stopType, thresh, alpha)
    name = "Original" if (data[:,1]>2).sum() > 1 else "Scaled"
    name += " data - learning rate: {} - ".format(alpha)
    if batchSize==n: strDescType = "Gradient"
    elif batchSize==1:  strDescType = "Stochastic"
    else: strDescType = "Mini-batch ({})".format(batchSize)
    name += strDescType + " descent - Stop: "
    if stopType == STOP_ITER: strStop = "{} iterations".format(thresh)
    elif stopType == STOP_COST: strStop = "costs change < {}".format(thresh)
    else: strStop = "gradient norm < {}".format(thresh)
    name += strStop
    print ("***{}\nTheta: {} - Iter: {} - Last cost: {:03.2f} - Duration: {:03.2f}s".format(
        name, theta, iter, costs[-1], dur))
    fig, ax = plt.subplots(figsize=(12,8))
    ax.plot(np.arange(len(costs)), costs, 'r')
    ax.set_xlabel('Iterations')
    ax.set_ylabel('Cost')
    ax.set_title(name.upper() + ' - Error vs. Iteration')
    plt.show()
    return theta

資料標準化

((a-均值)/方差)

from sklearn import preprocessing as pp
scaled_data = data.copy()
scaled_data[:, 1:3] = pp.scale(data[:, 1:3])
n=100   
#損失值 < 0.000001時停
theta = runExpe(scaled_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001)

6.accuracy: 計算精度


def predict(X,theda):  #設定閾值   預測 大於0.5就是可以
    return [1 if x>=0.5 else 0 for x in model(X,theta)]

scaled_X = scaled_data[:, :3]
y = scaled_data[:, 3]
predictions = predict(scaled_X, theta)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]

accuracy = (sum(map(int, correct)) % len(correct))
print ('accuracy = {0}%'.format(accuracy))

在這裡插入圖片描述