机器学习算法-LR

发布于 2020-09-24  125 次阅读


逻辑回归(LR)

线性模型

基本概念

给定一个由d个属性决定的示例$x=(x_{1},x_{2},...)$,线性模型试图学得一个通过属性的线性组合来预测的函数,即
$$f(x)=w_{1}x_{1}+w_{2}x_{2}+...+w_{d}x_{d}+b$$
也可以写成向量形式
$$f(x)=w^{T}x+b$$
其中$w=(w_{1},w_{2},...)$,而$w$与$b$确定后,模型就得以确定

线性回归

针对确定$w$与$b$,均方误差是回归任务中最常用的性能度量,因此我们可以使均方误差最小化,针对函数
$$f(x_{i})=wx_{i}+b$$
我们希望
$$J(w,b)=\frac{1}{2}\sum_{i=1}^{m}(f(x_{i})-y)^{2}$$
得到最小值,即$$f(x_{i})\approx y_{i}$$
我们一般直接将b化为w的一部分,然后对w求偏导得

$$\frac{\partial J}{\partial w}=\sum_{i=1}^{m}(f(x_{i})- y_{i})*x_{i}$$

对于单w来说可以通过导数等于0得到其最优解,但是针对多属性函数,则需要使用梯度下降方法得到其全局最优解,梯度下降的具体推导需要使用多元泰勒展开,此不赘述,要记住梯度的计算,即
$$\nabla f(w_{1},w_{2},...)=(\frac{\partial f}{\partial w_{1}},\frac{\partial f}{\partial w_{2}},...)$$
而梯度下降的数学公式则是
$$w_{t+1}=w_{t}-\eta*\nabla f(w_{t})$$
其中$\eta$代表学习率,一般我们使用$\eta=\frac{\eta}{t+1}+a$来防止步长过大和过小,通过迭代后我们可以获得我们想要的$w$值

对数回归

Sigmoid函数

它的作用是将线性的z值转化为接近0或1的y值
$$y=\frac{1}{1+e^{-z}}$$
其特殊之处在于
$$\frac{\partial f}{\partial z}=f(z)*(1-f(z))$$

数学推导

若将y视为x作为正例的可能性,1-y是其反例的可能性,两者比值为
$$\frac{y}{1-y}$$
称为几率,将其取对数后可以得到
$$ln\frac{P(y=1|x)}{P(y=0|x)}=w^Tx+b$$
定义似然函数为
$$L(w)=\prod_{m}^{i=1}{P(y^{(i)}=1|x^{(i)})^{(y^{(i)})}·P(y^{(i)}=0|x^{(i)})^{(1-y^{(i)})}}$$
为简便书写,令
$$P(y^{(i)}=1|x^{(i)}) = h_{w}(x^{(i)})$$
则似然函数则是
$$L(w)=\prod_{m}^{i=1}{h_{w}(x^{(i)}))^{y^{(i)}}·(1-h_{w}(x^{(i)}))^{1-y^{(i)}}}$$
采取对数似然
$$log(l(w))=-\sum_{i=1}^{m}{[y_{i} log h_{w}(x^{(i)})+(1−y_{i})log(1−h_{w}(x^{(i)})]}$$
再令
$$J(w)=log(l(w))$$
再对其求偏导得

$$\frac{\partial J}{\partial w}=-\sum_{i=1}^{m}{(y^{(i)}-h_{w}(x^{(i)}))x^{(i)}_{j} }$$

小结

至此你会发现逻辑回归和线性回归的拉普拉斯算子得到的是一样的,当然可能是巧合吧(至于是不是得靠数学推导),接下来会说明多种梯度下降算法

梯度下降

正常的梯度下降中梯度计算如下
$$\nabla{J(x)}=\frac{1}{n}\nabla\sum_{i=1}^{n}{J(x_i)}$$
如果使用梯度下降法(批量梯度下降法),那么每次迭代过程中都要对$n$个样本进行求梯度,所以开销非常大,随机梯度下降的思想就是随机采样一个样本$J(x_{i})$来更新参数,那么计算开销就从$\mathcal{O}{(n)}$下降到$\mathcal{O}{(1)}$ 。
其中更新公式如下

批量梯度下降

随机梯度下降

相关代码

#梯度下降
def gradacent(inputdata, inputlable):
    datamat = np.mat(inputdata)
    lablemat = np.mat(inputlable).transpose()
    m, n = np.shape(datamat)
    alpha = 0.001
    cycle = 500
    weights = np.ones((n, 1))
    for k in range(cycle):
        h = sigmoid(datamat * weights)
        error = h - lablemat
        weights = weights - alpha * datamat.transpose() * error  #核心
    return weights

#随机梯度下降
def randomgradacent(inputdata, inputlable, numiter=150):
    datamat = np.array(inputdata)
    m, n = np.shape(datamat)
    weights = np.ones((1, n))
    for j in range(numiter):
        dataindex = list(range(m))
        for k in range(m):
            alpha = 4 / (1.0 + j + k) + 0.01
            randindex = int(random.uniform(0, len(dataindex)))
            h = sigmoid(np.dot(datamat[randindex], weights.transpose()))
            error = h - inputlable[randindex]
            weights = weights - alpha * error * datamat[randindex] #核心
            del (dataindex[randindex])
    return weights.transpose()

代码

import numpy as np
import random


def loaddata():
    data = []
    lable = []
    with open('testSet.txt') as fr:
        while True:
            line = fr.readline()
            if line:
                linearr = line.strip().split()
                data.append([1.0, float(linearr[0]), float(linearr[1])])
                lable.append(int(linearr[2]))
            else:
                break
                pass
    return data, lable


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


def gradacent(inputdata, inputlable):
    datamat = np.mat(inputdata)
    lablemat = np.mat(inputlable).transpose()
    m, n = np.shape(datamat)
    alpha = 0.001
    cycle = 500
    weights = np.ones((n, 1))
    for k in range(cycle):
        h = sigmoid(datamat * weights)
        error = h - lablemat
        weights = weights - alpha * datamat.transpose() * error
    return weights


def randomgradacent(inputdata, inputlable, numiter=150):
    datamat = np.array(inputdata)
    m, n = np.shape(datamat)
    weights = np.ones((1, n))
    for j in range(numiter):
        dataindex = list(range(m))
        for k in range(m):
            alpha = 4 / (1.0 + j + k) + 0.01
            randindex = int(random.uniform(0, len(dataindex)))
            h = sigmoid(np.dot(datamat[randindex], weights.transpose()))
            error = h - inputlable[randindex]
            weights = weights - alpha * error * datamat[randindex]
            del (dataindex[randindex])
    return weights.transpose()


if __name__ == "__main__":
    data, label = loaddata()
    weights1 = randomgradacent(data, label, 2000)
    weights2 = gradacent(data, label)
    h = sigmoid(np.dot(data, weights2))
    length = len(h)
    error = 0
    for i in range(length):
        if h[i][0] > 0.5:
            if label[i] != 1:
                error += 1
        else:
            if label[i] != 0:
                error += 1
    print(error / length)

总结

这次可能文字有点少,都是数学推导,这也是我头一次完整的能够把一个模型的数学推导写出来,所以更多的是总结了自己在数学方面的一些问题,下一篇应该是SVM吧:star2:


浪子三唱,不唱悲歌