手撸机器学习算法 - 逻辑回归

时间:2021-06-24 18:35:36   收藏:0   阅读:0

系列文章目录:

算法介绍

今天我们一起来学习使用非常广泛的分类算法:逻辑回归,是的,你没有看错,虽然它名字里有回归,但是它确实是个分类算法,作为除了感知机以外,最最最简单的分类算法,下面我们把它与感知机对比来进行学习;

从决策边界上看

以上,对于数据中的噪声,假设噪声点实际为负类,但是被分到正类一侧,如果是感知机,则无法判断,而逻辑回归以概率为基础,如果该噪声点实际被分为正类的概率仅为52%,那么实际上它属于负类的可能性也很大,即逻辑回归认为数据的产生是有一定随机性的,相比于简单的0或1,概率值更能表现其实际情况;

输出函数

从决策边界可知,感知机的输出∈{+1,-1},而逻辑回归的输出为0~1的概率值:

如何看待逻辑回归选择Sigmoid作为概率输出函数呢,可以从以下几个点来理解:

损失函数

算法推导

从概率角度看Sigmoid

Sigmoid给出了条件概率:

函数优化求导

首先列出损失函数如下:

\[\frac{1}{N}\sum_{i=1}^N\ln(1+e^{-y_iwx_i}), yi\in{\{+1,-1\}} \]

对上述函数针对参数w求梯度:

\[\begin{equation*} \begin{split} \frac{\partial \frac{1}{N}\sum_{i=1}^{N}ln(1+e^{-y_iwx_i})}{\partial w} = \frac{1}{N}\sum_{i=1}^{N}[\frac{(-y_ix_i)(e^{-y_iwx_i})}{1+e^{-y_iwx_i}}] \end{split} \end{equation*} \]

上述梯度就是后续我们用于更新参数w的依据;

代码实现

初始化相关参数

def __init__(self,X,y,epochs=5000,eta=0.1,epsilon=0.001):
    super(LogisticRegression,self).__init__(X,y)
    self.epochs = epochs
    self.eta = eta
    self.epsilon = epsilon
    self.wk = np.array([0 for i in range(self.X.shape[1])])

Sigmoid

def sigmoid(self,x):
    return 1/(1+np.exp(-x))

经验损失函数梯度

def drhd(self,w):
    ‘‘‘
    经验误差函数的梯度
    ‘‘‘
    ew = []
    for i in range(self.X.shape[1]):
        ewi = np.mean(-self.y*self.X[:, i]*np.exp(-self.y*(self.X@w))/(1+np.exp(-self.y*(self.X@w))))
        ew.append(ewi)
    return np.array(ew)

迭代训练

def train(self):
    i_,norm = None,None
    for i in range(self.epochs):
        drhdwk = self.drhd(self.wk)
        i_,norm = i,np.linalg.norm(drhdwk)
        if np.linalg.norm(drhdwk) < self.epsilon:
            break
        self.wk = self.wk-self.eta*drhdwk
    return i_,norm,self.wk

运行结果

先看下感知机-口袋算法处理非线性分类问题:
技术图片
再来对比看下逻辑回归的分类情况:
技术图片
直觉上看,二者虽然都有一个点分类错误(这是肯定的,因为数据不是线性可分的),但是对于分类错误的×来说,逻辑回归中错误的×距离分割平面更近,也就是说模型对于这个点的判断是比较模糊而不是很肯定的,可以认为是错的不严重,这一点也会反应在损失函数中;

全部代码

import numpy as np
from 线性回归最小二乘法矩阵实现 import LinearRegression as LR
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt

class LogisticRegression(LR):
    def __init__(self,X,y,epochs=5000,eta=0.1,epsilon=0.001):
        super(LogisticRegression,self).__init__(X,y)
        self.epochs = epochs
        self.eta = eta
        self.epsilon = epsilon
        self.wk = np.array([0 for i in range(self.X.shape[1])])

    def sigmoid(self,x):
        return 1/(1+np.exp(-x))

    def h(self,x):
        ‘‘‘
        假设函数
        ‘‘‘
        return self.sigmoid(x@self.wk.T)

    def drhd(self,w):
        ‘‘‘
        经验误差函数的梯度
        ‘‘‘
        ew = []
        for i in range(self.X.shape[1]):
            ewi = np.mean(-self.y*self.X[:, i]*np.exp(-self.y*(self.X@w))/(1+np.exp(-self.y*(self.X@w))))
            ew.append(ewi)
        return np.array(ew)

    def train(self):
        i_,norm = None,None
        for i in range(self.epochs):
            drhdwk = self.drhd(self.wk)
            i_,norm = i,np.linalg.norm(drhdwk)
            if np.linalg.norm(drhdwk) < self.epsilon:
                break
            self.wk = self.wk-self.eta*drhdwk
        return i_,norm,self.wk

    def sign(self,value):
        return 1 if value>=0 else -1

    def predict(self,x):
        return self.sign(self.wk.dot(np.append([1],x)))

if __name__ == ‘__main__‘:
    X = np.array([[5,2], [3,2], [2,7], [1,4], [6,1], [4,5], [2,4.5]])
    y = np.array([-1, -1, 1, 1, -1, 1, -1, ])
    # X = np.array([[5,2], [3,2], [2,7], [1,4], [6,1], [4,5]])
    # y = np.array([-1, -1, 1, 1, -1, 1, ])
    iris = load_iris()
    X = iris.data[iris.target<2,:2]
    y = iris.target[iris.target<2]
    y[y==0] = -1

    model = LogisticRegression(X=X,y=y,epochs=10000,eta=.2,epsilon=0.0001)
    i,norm,w = model.train()
    print(f"epochs={i} -> w={w} -> norm={norm:>.8f}")
    for xi,yi in zip(X,y):
        print(yi,model.predict(xi))

    w,b = w[1:],w[0]
    positive = [x for x,y in zip(X,y) if y==1]
    negative = [x for x,y in zip(X,y) if y==-1]
    line = [(-w[0]*x-b)/w[1] for x in [-100,100]]
    plt.title(‘w=‘+str(w)+‘, b=‘+str(b))
    plt.scatter([x[0] for x in positive],[x[1] for x in positive],c=‘green‘,marker=‘o‘)
    plt.scatter([x[0] for x in negative],[x[1] for x in negative],c=‘red‘,marker=‘x‘)
    plt.plot([-100,100],line,c=‘black‘)
    plt.xlim(min([x[0] for x in X])-1,max([x[0] for x in X])+1)
    plt.ylim(min([x[1] for x in X])-1,max([x[1] for x in X])+1)
    plt.show()

    iris = load_iris()
    X = iris.data
    y = iris.target
    model = LogisticRegression(X=X,y=y,epochs=1000000,eta=.1,epsilon=0.0005)
    i,norm,w = model.train()
    print(f"epochs={i} -> w={w} -> norm={norm:>.8f}")

最后

逻辑回归几乎是机器学习中应用最为广泛的一种分类算法,由于其简单的思想、良好的数学理论、超强的可解释性,使得在推荐领域、金融领域等发挥了巨大的作用;

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!