對數機率迴歸(邏輯迴歸)

邏輯迴歸

  1.  邏輯迴歸的模型是一個非線性模型
  2.  sigmoid函數,又稱邏輯迴歸函數。但是它本質上又是一個線性迴歸模型
  3.  因爲除去sigmoid映射函數關係,其他的步驟,算法都是線性迴歸
  4.  可以說,邏輯迴歸,都是以線性迴歸爲理論支持的
  5.  只不過,線性模型,無法做到sigmoid的非線性形式,sigmoid可以輕鬆處理0/1分類問題

 邏輯迴歸
 

  1.  找一個合適的預測函數,一般表示爲h函數,該函數就是需要找的分類函數,它用來預測輸入數據的判斷結果
  2.  構造一個損失函數,該函數表示預測輸出(h)與訓練數據類別(y)之間的偏差,可以是二者之間的差(h-y)或者其他的形式。綜合考慮所有訓練數據的“損失”,將損失求和或者求平均,記爲J(\theta )函數,表示所有訓練數據預測值與實際類別的偏差。
  3. 顯然,J(\theta )函數的值越小表示預測函數越精確,所以這一步需要做的就是找到J(\theta )函數的最小值,這裏利用梯度下降法

 Logistic函數

    如果忽略二分類問題中的y的取值是一個離散的取值(0或1),我們繼續使用線性迴歸來預測y的取值。這樣做會導致y的取值並不爲0或1。邏輯迴歸使用一個函數來歸一化y值,使y的取值在區間(0,1)內,這個函數稱爲Logistic函數,也稱Sigmoid函數,函數公式如下:

函數圖形如下:

Z是線性迴歸函數,所以也可以表示成:

對於線性邊界的情況,邊界形式如下:

構造預測函數爲:

函數h_{\theta }(x)的值有特殊的含義,它表示結果取1的概率,因此對於輸入x分類結果爲類別1和類別0的概率分別爲:

損失函數

對於任何機器學習問題,都需要明確損失函數:

但是在LR模型解決的二分類問題中,這個函數形式爲:

這個損失函數通常稱作是對數損失(logloss),由於y的取值爲0或者1,因此可以表示爲:

優化求解

現在我們已經確定了模型的損失函數,那麼接下來就是根據這個損失函數,不斷優化模型參數從而獲得擬合數據的最佳模型。重新看一下損失函數,其本質上是;關於模型中線性方程部分兩個參數w和b的函數:

其中:

z=w^{T}x+b

現在的學習任務轉化爲數學優化的形式即爲:

由於損失函數連續可微,我們可以藉助梯度下降法進行優化求解,對於這個核心參數的更新方式如下:

 

代碼及代碼解釋如下:

import numpy as np
class LogisticRegression(object):
#初始化參數(學習率,最大迭代次數,seed爲種子數,seed相同時,numpy.random.seed()生成的隨機數相同
    def __init__(self,learning_rate=0.1,max_iter=100,seed=None):
        self.seed = seed
        self.lr = learning_rate
        self.max_iter = max_iter
#進行訓練,np.random.normal()的意思是一個正態分佈,normal這裏是正態的意思。
# 參數loc(float):正態分佈的均值,對應着這個分佈的中心。loc=0說明這一個以Y軸爲對稱軸的正態分佈,
# 參數scale(float):正態分佈的標準差,對應分佈的寬度,scale越大,正態分佈的曲線越矮胖,scale越小,曲線越高瘦。
# 參數size(int 或者整數元組):輸出的值賦在shape裏,默認爲None。
    def fit(self,x,y):
        np.random.seed(self.seed)
        self.w = np.random.normal(loc=0.0,scale=1.0,size=x.shape[1])
        self.b = np.random.normal(loc=0.0,scale=1.0)
        self.x = x
        self.y = y
        for i in range(self.max_iter):
            self._update_step()
    def _sigmoid(self,z):
        return 1.0/(1.0+np.exp(-z))
# 返回一個邏輯函數
    def _f(self,x,w,b):
        z = x.dot(w) + b
        return self._sigmoid(z)
    def predict_prob(self,x=None):
        if x is None:
            x = self.x
        y_preb = self._f(x,self.w,self.b)
# 預測值,y_pred_proba表示邏輯函數Y,是一個概率,y_pred爲分類後的預測值
    def predict(self,x=None):
        if x is None:
            x = self.x
        y_pred_proba = self._f(x,self.w,self.b)
        y_pred = np.array([0 if y_pred_proba[i]<0.5 else 1 for i in range(len(y_pred_proba))])
        return y_pred
    def score(self,y_true=None,y_pred=None):
        if y_true is None or y_pred is None:
            y_true = self.y
            y_pred = self.predict()
        acc = np.mean([1 if y_true[i]==y_pred[i] else 0 for i in range(len(y_true))])
        return acc
#損失函數
    def loss(self,y_true=None,y_pred_proba=None):
        if y_true is None or y_pred is None:
            y_true = self.y
            y_pred_proba = self.predict_proba()
        return np.mean(-1.0*(y_true*np.log(y_pred_prob)+(1.0-y_true)*np.log(1.0-y_pred_proba)))
# 機算梯度
    def _cal_gradient(self):
        y_pred = self.predict()
        d_w = (y_pred - self.y).dot(self.x)/len(self.y)
        d_b = np.mean(y_pred - self.y)
        return d_w,d_b
# 進行迭代,利用梯度下降算法求解w,b,d_w,d_b爲損失函數L對w,b求偏導
    def _update_step(self):
        d_w,d_b = self._cal_gradient()
        self.w = self.w - self.lr*d_w
        self.b = self.b - self.lr*d_b
        return self.w, self.b
#生成數據
def generate_data(seed):
    np.random.seed(seed)
    data_size_1 = 300

    x1_1 = np.random.normal(loc=5.0,scale=1.0,size=data_size_1)
    x2_1 = np.random.normal(loc=5.0,scale=1.0,size=data_size_1)
    y_1 = [0 for _ in range(data_size_1)]

    data_size_2 = 400
    x1_2 = np.random.normal(loc=10.0,scale=2.0,size=data_size_2)
    x2_2 = np.random.normal(loc=8.0,scale=2.0,size=data_size_2)
    y_2 = [1 for _ in range(data_size_2)]
# numpy.concatenate((a1, a2, ...), axis),a1,a2 .. 相同類型的數組序列,axis 必須連接數組的軸。默認值爲0
    x1 = np.concatenate((x1_1,x1_2),axis=0)
    x2 = np.concatenate((x2_1,x2_2),axis=0)
# hstack(tup) ,參數tup可以是元組,列表,或者numpy數組,返回結果爲numpy的數組,它其實就是水平(按列順序)把數組給堆疊起來
# reshape(-1,1)表示(任意行,1列),第一列爲橫座標,第二列爲縱座標,y爲分類的值,爲0或者1
    x = np.hstack((x1.reshape(-1,1),x2.reshape(-1,1)))
    y = np.concatenate((y_1,y_2),axis=0)
# np.random.permutation():隨機排列序列                
    data_size_all = data_size_1 + data_size_2
    shuffled_index = np.random.permutation(data_size_all)
    x = x[shuffled_index]
    y = y[shuffled_index]
    return x,y
# 訓練集和測試集分割
def train_tset_split(x,y):
    splite_index = int(len(y)*0.7)
    x_train = x[:splite_index]
    y_train = y[:splite_index]
    x_test = x[splite_index:]
    y_test = y[splite_index:]
    return x_train,y_train,x_test,y_test
import matplotlib.pyplot as plt
x,y = generate_data(seed=272)
x_train,y_train,x_test,y_test = train_tset_split(x,y)
# x_train第一列爲橫座標,第二列爲縱座標,根據y_train的值(0或者1)來標記顏色
plt.scatter(x_train[:,0],x_train[:,1],c=y_train,marker=".")
plt.show()
plt.scatter(x_test[:,0], x_test[:,1], c=y_test, marker='.')
plt.show()
# s數據歸一化
x_train=(x_train-np.min(x_train,axis=0))/(np.max(x_train,axis=0)-np.min(x_train,axis=0))
x_test=(x_test-np.min(x_test,axis=0))/(np.max(x_test,axis=0)-np.min(x_test,axis=0))
# 邏輯迴歸
clf = LogisticRegression(learning_rate=0.1,max_iter=500,seed=272)
clf.fit(x_train,y_train)
# xx表示0.1-0.6 步長爲0.1
# 由於是斜向下的直線,因此y=-wx-b
split_boundary_fuc = lambda x:(-clf.b-clf.w[0]*x)/clf.w[1]
xx = np.arange(0.1,0.6,0.1)
cValue=['g','b']
plt.scatter(x_train[:,0],x_train[:,1],c=[cValue[i] for i in y_train],marker="o")
plt.plot(xx,split_boundary_fuc(xx),c='red')

運行結果爲:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章