逻辑回归(LogisticRegression)

如果我们根据身高、体重、年龄等来预测是否发生某种疾病,结果只有两种,1发生了,0没发生。这样就构成了一个二分类问题,而我们在处理二分类问题时常常会使用sign函数(取正负号),y为-1或者+1。但是这样处理对0.5左右的样本不友好,我们希望用概率来量化这件事情。

所以需要调用sigmoid这个函数,将f(x)范围控制在(0,1)

s i g m o i d ( x ) = 1 1 + e − x sigmoid(x) = \frac {1} {1+e^{-x}} sigmoid(x)=1+ex1

这样得到的sigmoid转换过后的值可以在某种程度上认为是位于+1或者-1的概率。

那么解决了概率问题过后,还需要考虑我们的机器怎么评估错误率,即损失函数该怎么写。

其实还是从最大似然来的。对于我们手上拿到的数据即理想中的函数f(x),如果我们的理论函数h(x)与f(x)接近,那么生成的数据集D也是相似的。

如果是二分类问题:

P ( y ∣ x ) = { f ( x ) if  y = + 1 1 − f ( x ) if  y = − 1 P(y|x) = \begin{cases} f(x) &\text{if } y = +1 \\ 1-f(x) &\text{if } y = -1 \end{cases} P(yx)={f(x)1f(x)if y=+1if y=1

那么对于数据集 D = { ( x 1 , + 1 ) , ( x 2 , − 1 ) , . . . , ( x n , + 1 ) } D=\{(x_1, +1), (x_2, -1), ..., (x_n, +1)\} D={(x1,+1),(x2,1),...,(xn,+1)}

f(x)产生该数据的概率就为
P ( x 1 ) ∗ f ( x 1 ) ∗ P ( x 2 ) ∗ ( 1 − f ( x 2 ) ) ∗ . . . ∗ P ( x n ) ∗ f ( x n ) P(x_1)*f(x_1)*P(x_2)*(1-f(x_2))*...*P(x_n)*f(x_n) P(x1)f(x1)P(x2)(1f(x2))...P(xn)f(xn)
那么h(x)产生该数据的概率就为
P ( x 1 ) ∗ h ( x 1 ) ∗ P ( x 2 ) ∗ ( 1 − h ( x 2 ) ) ∗ . . . ∗ P ( x n ) ∗ h ( x n ) P(x_1)*h(x_1)*P(x_2)*(1-h(x_2))*...*P(x_n)*h(x_n) P(x1)h(x1)P(x2)(1h(x2))...P(xn)h(xn)
实际上f(x)产生D的概率往往很大,所以我们就需要使 P ( x 1 ) ∗ h ( x 1 ) ∗ P ( x 2 ) ∗ ( 1 − h ( x 2 ) ) ∗ . . . ∗ P ( x n ) ∗ h ( x n ) P(x_1)*h(x_1)*P(x_2)*(1-h(x_2))*...*P(x_n)*h(x_n) P(x1)h(x1)P(x2)(1h(x2))...P(xn)h(xn)最大的h(x)

也就是让 ∏ i = 1 n h ( y n ∗ x n ) \displaystyle\prod_{i=1}^n h(y_n*x_n) i=1nh(ynxn) 最大

接下来再把w放进去,就是让 ∏ i = 1 n s i g m o i d ( y n ∗ w ∗ x n ) \displaystyle\prod_{i=1}^n sigmoid(y_n*w*x_n) i=1nsigmoid(ynwxn) 最大

为了让连乘变连加,需要加上 ln ⁡ \ln ln,然后让最大变最小,加个负号。

− ∑ i = 1 n ln ⁡ ( s i g m o i d ( y n ∗ w ∗ x n ) ) -\sum_{i=1}^n \ln(sigmoid(y_n*w*x_n)) i=1nln(sigmoid(ynwxn))

那么就是让上式变为最小。而我们把sigmoid函数代入过后公式变化成以下形式:
ln ⁡ ( 1 + e − y w x ) \ln(1+e^{-ywx}) ln(1+eywx)
而这个函数我们称之为交叉熵。那么我们的损失函数就是:

ln ⁡ ( 1 + e − y w x ) n \frac {\ln(1+e^{-ywx})} {n} nln(1+eywx)

解决了损失函数的问题,我们还要解决一个非常重要的问题,就是怎么学习,怎么去更新我们的权重。

为了让损失函数最小(函数是一个开口向上的函数),我们首先考虑将损失函数微分,希望能找到梯度为0的w。

这部分稍稍复杂了一些,后面再单独写一篇文章介绍吧,需要用梯度下降法更新参数,可以用随机梯度,可以用固定梯度。

固定梯度如下:

w t + 1 ​ ← w t − η ∗ ∇ l o s s ( w t ​ ) ∥ ∇ l o s s ( w t ​ ) ∥ w_{t+1}​ \gets w_t−\frac{η* \nabla loss(w_t​)}{\lVert \nabla loss(w_t​)\rVert} wt+1wtloss(wt)ηloss(wt)

∇ l o s s ( w t ​ ) ∥ ∇ l o s s ( w t ​ ) ∥ \frac{ \nabla loss(w_t​)}{\lVert \nabla loss(w_t​)\rVert} loss(wt)loss(wt)是梯度方向的单位向量,每次更新都在梯度方向的反方向上走η。

但是为了让梯度在开始时快一些,快到谷底时慢一些,就在每一步时乘上梯度的模长:
w t + 1 ​ ← w t − η ∗ ∇ l o s s ( w t ​ ) ∥ ∇ l o s s ( w t ​ ) ∥ ∗ ∥ ∇ l o s s ( w t ​ ) ∥ w t + 1 ​ ← w t − η ∗ ∇ l o s s ( w t ​ ) \begin{aligned} w_{t+1}​ &\gets w_t−\frac{η* \nabla loss(w_t​)}{\lVert \nabla loss(w_t​)\rVert}*\lVert \nabla loss(w_t​)\rVert \\ w_{t+1}​ &\gets w_t−η* \nabla loss(w_t​) \end{aligned} wt+1wt+1wtloss(wt)ηloss(wt)loss(wt)wtηloss(wt)

梯度 ∇ l o s s ( w t ) \nabla loss(w_t) loss(wt)就是损失函数在 w t w_t wt处的微分。

#!/usr/local/bin/python3
# -*- coding: UTF-8 -*-

import numpy as np


class LogisticRegression:

    def __init__(self, learn_rate, step):
        self.learn_rate = learn_rate
        self.step = step

    @staticmethod
    def sigmoid(s):
        return 1/(1+np.exp(-s))

    @staticmethod
    def loss_func(self, w, X, y):
        loss = np.sum(np.log(1+np.exp(-X.dot(w)*y)))/(X.shape[0])
        return loss

    def get_gradient(self, w, X, y):
        y = y.reshape(-1, 1)
        _gradient = np.sum(self.sigmoid(-X.dot(w)*y)*(-y*X), axis=0)/(X.shape[0])
        return _gradient.reshape((X.shape[1], 1))

    def fit(self, X, y):
        w = np.zeros((X.shape[1], 1))
        for i in range(0, self.step):
            gradient = self.get_gradient(w, X, y)
            w = w - self.learn_rate*gradient
        return w

    def predict(self, X, y):
        w = self.fit(X, y)
        new_y = self.sigmoid(X.dot(w))
        return new_y


if __name__ == "__main__":
    def boxmullersampling(mu=0, sigma=1, size=1):
        u = np.random.uniform(size=size)
        v = np.random.uniform(size=size)
        z = np.sqrt(-2 * np.log(u)) * np.cos(2 * np.pi * v)
        return mu + z * sigma

    x1 = boxmullersampling(1.5, 0.1, 100)
    x1 = [[x, x+1, x*2] for x in list(x1)]
    y1 = [-1 for x in range(0, 100)]
    x2 = boxmullersampling(2, 0.2, 100)
    x2 = [[x, x+1.5, x*3] for x in list(x2)]
    y2 = [1 for x in range(0, 100)]
    train_x = np.array(x1+x2)
    train_y = np.array(y1+y2)

    cls = LogisticRegression(1, 1000)

    _tmp1 = cls.fit(train_x, train_y)
    y_pred = cls.predict(train_x, train_y)

    print(_tmp1)
    print(y_pred, train_y)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值