机器学习--逻辑回归

引入

逻辑回归是一种二分类算法,逻辑回归实质是去试图找出一个边界把样本空间一分为二,然后在不同的区域中就是不同的类别。
就如下面这张图,蓝色区域与黄色区域被一条直线分开。
在这里插入图片描述
我们可以假设这条直线是,假设有样本
( x 1 , y 1 ) , ( x 2 , y 2 ) . . . ( x m , y m ) (\textbf{x}_1,y_1), (\textbf{x}_2,y_2)...(\textbf{x}_m,y_m) (x1,y1),(x2,y2)...(xm,ym)
则我们想要找这个边界可以写成
y = x T θ + b y=\textbf{x}^T\mathbf{ \theta}+b y=xTθ+b
当把样本带入这个式子,如果y大于0就可以认为是一种类别,反之就是另一种类别。
但是上述式子算出来的值是连续的,而标记却是离散的,这样无法对应起来,所以此时就可以用后验概率来描述。
P ( y = 1 ∣ x ) P(y=1|\textbf{x}) P(y=1x)表示在 x \textbf{x} x的情况下为第一种类的概率
它的对立事件很明显就是 P ( y = 0 ∣ x ) P(y=0|\textbf{x}) P(y=0x)
P ( y = 1 ∣ x ) ≥ 0.5 P(y=1|\textbf{x}) \geq 0.5 P(y=1x)0.5时就可以认为是带入边界大于0的情况,反之就是小于0的情况。
此时就需要一个函数来把 x T θ + b \textbf{x}^T\mathbf{ \theta}+b xTθ+b的值和概率之间来对应起来了。
在逻辑回归中,采用的函数就是Sigmoid函数 S i g m o i d ( t ) = 1 1 + e − t Sigmoid(t)=\frac{1}{1+e^{-t}} Sigmoid(t)=1+et1
t ≥ 0 t \geq 0 t0 y ≥ 0.5 y \geq 0.5 y0.5很完美的就把 x T θ + b \textbf{x}^T\mathbf{ \theta}+b xTθ+b的值域映射到了概率的值域,把 x T θ + b \textbf{x}^T\mathbf{ \theta}+b xTθ+b带入得到 P ( y = 1 ∣ x ) = 1 1 + e − ( x T θ + b ) P(y=1|\textbf{x})=\frac{1}{1+e^{-(\textbf{x}^T\mathbf{ \theta}+b)}} P(y=1x)=1+e(xTθ+b)1
同样道理可以写出 P ( y = 0 ∣ x ) P(y=0|\textbf{x}) P(y=0x),只需要用1减去 P ( y = 1 ∣ x ) P(y=1|\textbf{x}) P(y=1x)
P ( y = 0 ∣ x ) = e − ( x T θ + b ) 1 + e − ( x T θ + b ) P(y=0|\textbf{x})=\frac{e^{-(\textbf{x}^T\mathbf{ \theta}+b)}}{1+e^{-(\textbf{x}^T\mathbf{ \theta}+b)}} P(y=0x)=1+e(xTθ+b)e(xTθ+b)
很明显,可以到这么一个函数函数。
Q ( x , y ) = P ( y = 1 ∣ x ) y ∗ P ( y = 0 ∣ x ) 1 − y Q(\textbf{x}, y)=P(y=1|\textbf{x})^y*P(y=0|\textbf{x})^{1-y} Q(x,y)=P(y=1x)yP(y=0x)1y
它的其实就是把上面两个概率结合了起来,可以发现在已经y确定时给出 x , \textbf{x}, xQ函数就可以给出一个预测,显然,这个值预测越大也就越符合原来已经确定的y。
那么现在问题就是需要确定一个参数 θ \theta θ使 Q Q Q对所有样本的预测尽可能的准,也就是让Q尽可能的大。
这里就可以想到,这种想法与极大似然估计是不谋而合的,所以就可以使用极大似然估计法来确定这个 θ \theta θ
设极大似然函数
L ( θ ) = ∏ i = 1 m Q ( x i , y i ) L(\theta)=\prod\limits_{i =1}^mQ(\textbf{x}_i, y_i) L(θ)=i=1mQ(xi,yi)
为了方便将其求对数
l n ( L ( θ ) ) = ∑ i = 1 m l n [ Q ( x i , y i ) ] ln(L(\theta))=\sum\limits_{i =1}^mln[Q(\textbf{x}_i, y_i)] ln(L(θ))=i=1mln[Q(xi,yi)]
把Q带入
l n ( L ( θ ) ) = ∑ i = 1 m y ∗ l n ( P ( y = 1 ∣ x i ) ) ∗ ( 1 − y ) ∗ l n ( P ( y = 0 ∣ x i ) ) ln(L(\theta))=\sum\limits_{i =1}^my*ln(P(y=1|\textbf{x}_i)) * (1 - y)*ln(P(y=0|\textbf{x}_i)) ln(L(θ))=i=1myln(P(y=1xi))(1y)ln(P(y=0xi))
P ( y = 1 ∣ x ) , P ( y = 0 ∣ x ) P(y=1|\textbf{x}),P(y=0|\textbf{x}) P(y=1x),P(y=0x)带入
l n ( L ( θ ) ) = ∑ i = 1 m y ∗ l n ( 1 1 + e x i T θ + b ) ∗ ( 1 − y ) ∗ l n ( e x i T θ + b 1 + e x i T θ + b ) ln(L(\theta))=\sum\limits_{i =1}^my*ln(\frac{1}{1 + e^{\textbf{x}_i^T\theta + b}}) * (1 - y)*ln(\frac{ e^{\textbf{x}_i^T\theta + b}}{1 + e^{\textbf{x}_i^T\theta + b}}) ln(L(θ))=i=1myln(1+exiTθ+b1)(1y)ln(1+exiTθ+bexiTθ+b)
整理合并得
l n ( L ( θ ) ) = ∑ i = 1 m y ∗ ( x i T θ + b ) + l n ( 1 − S i g m o i d ( x i T θ + b ) ) ln(L(\theta))=\sum\limits_{i =1}^my*(\textbf{x}_i^T\theta + b) +ln(1-Sigmoid(\textbf{x}_i^T\theta + b)) ln(L(θ))=i=1my(xiTθ+b)+ln(1Sigmoid(xiTθ+b))
由于最大化 l n ( L ( θ ) ) ln(L(\theta)) ln(L(θ))相当于最小化 − l n ( L ( θ ) ) -ln(L(\theta)) ln(L(θ)),设 J ( θ ) = − 1 m l n ( L ( θ ) ) J(\theta)=-\frac{1}{m}ln(L(\theta)) J(θ)=m1ln(L(θ))

J ( θ ) = − 1 m ∑ i = 1 m y ∗ ( x i T θ + b ) + l n ( 1 − S i g m o i d ( x i T θ + b ) ) J(\theta)=-\frac{1}{m}\sum\limits_{i =1}^my*(\textbf{x}_i^T\theta + b) +ln(1-Sigmoid(\textbf{x}_i^T\theta + b)) J(θ)=m1i=1my(xiTθ+b)+ln(1Sigmoid(xiTθ+b))
想要使这个函数最大化只需要求偏导,然后使用梯度下降法,或者随机梯度下降法之类的方法就可以了(这个函数是一个单峰函数)

梯度下降法求解

使用梯度下降法就需要求出梯度,首先与线性回归一样,把样本增加一列属性用于和截距 b b b匹配,同时
J ( θ ) = − 1 m ∑ i = 1 m y ∗ ( x i T θ ) + l n ( 1 − S i g m o i d ( x i T θ ) ) J(\theta)=-\frac{1}{m}\sum\limits_{i =1}^my*(\textbf{x}_i^T\theta) +ln(1-Sigmoid(\textbf{x}_i^T\theta)) J(θ)=m1i=1my(xiTθ)+ln(1Sigmoid(xiTθ))
然后对其求偏导。
∇ J ( θ ) = ( ∂ J ( θ ) ∂ θ 0 ∂ J ( θ ) ∂ θ 1 . . . ∂ J ( θ ) ∂ θ n ) \nabla J(\theta) = \begin{gathered} \begin{pmatrix} \frac{\partial J(\theta)}{\partial \theta_0}\\ \frac{\partial J(\theta)}{\partial \theta_1}\\ ...\\ \frac{\partial J(\theta)}{\partial \theta_n} \end{pmatrix} \quad \end{gathered} J(θ)=θ0J(θ)θ1J(θ)...θnJ(θ)

∇ J ( θ ) = 1 m ( ∑ i = 1 m X 0 ( i ) ( S i g m o i d ( X ( i ) θ ) − y ( i ) ) ∑ i = 1 m X 1 ( i ) ( S i g m o i d ( X ( i ) θ ) − y ( i ) ) . . . ∑ i = 1 m X n ( i ) ( S i g m o i d ( X ( i ) θ ) − y ( i ) ) ) \nabla J(\theta) = \frac{1}{m}\begin{gathered} \begin{pmatrix} \sum\limits_{i = 1}^mX_0^{(i)}(Sigmoid(X^{(i)}\theta)-y^{(i)})\\ \sum\limits_{i = 1}^mX_1^{(i)}(Sigmoid(X^{(i)}\theta)-y^{(i)})\\ ...\\ \sum\limits_{i = 1}^mX_n^{(i)}(Sigmoid(X^{(i)}\theta)-y^{(i)}) \end{pmatrix} \quad \end{gathered} J(θ)=m1i=1mX0(i)(Sigmoid(X(i)θ)y(i))i=1mX1(i)(Sigmoid(X(i)θ)y(i))...i=1mXn(i)(Sigmoid(X(i)θ)y(i))
仿照线性回归的方式,上式也可以用矩阵乘法的方式写成 ∇ J ( θ ) = 1 m X T ( S i g m o i d ( X θ ) − y ) \nabla J(\theta)=\frac{1}{m}X^T(Sigmoid(X\theta) -y) J(θ)=m1XT(Sigmoid(Xθ)y)
然后就可以numpy的矩阵乘法轻松地实现了。

决策边界

分类边界就是一开始所描绘的那种边界,逻辑回归在特征维数为2的时候分类边界就是一条直线。
使用matplotlib可以用一种很简单的方式绘制出这条边界。

def ShowBound(model, data, target, categories):
    from matplotlib.colors import ListedColormap
    axis = [np.min(data[:, 0]), np.max(data[:, 0]), np.min(data[:, 1]), np.max(data[:, 1])]
    # 确定x轴与y轴的范围

    lst = ListedColormap(['#98F5FF', '#54FF9F', '#EEE685'])
    x, y = np.meshgrid(np.linspace(axis[0], axis[1], int((axis[1] - axis[0]) / 0.01)),
                       np.linspace(axis[2], axis[3], int((axis[3] - axis[2]) / 0.01)))
     # 使用linspace方法划分坐标轴,并且用meshgrid方法生成对应的x与y矩阵
    pre = np.c_[x.ravel(), y.ravel()]
    # 把坐标组合起来
    pre_target = model.predict(pre)
    plt.contourf(x, y, pre_target.reshape(x.shape), alpha=0.5, cmap=lst)
    # 绘制等高线
    for i in range(categories):
        plt.scatter(data[target == i][:, 0], data[target == i][:, 1])
    plt.xlim(axis[0], axis[1])
    plt.ylim(axis[2], axis[3])

首先导入sklearn的逻辑回归,与pipline,然后创建一个逻辑回归实例

from LogisticRegression import ShowBound
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
log_reg = Pipeline([
    ('std', StandardScaler()),
    ('log', LogisticRegression())
])

然后导入鸢尾花数据集,只选择其两个特征以及两种类型。

iris = datasets.load_iris()
target = iris.target
data = iris.data[:, :2][target <= 1]
target = target[target <= 1]

然后使用写好的ShowBound就可以看边界了。

加入多项式特征

同样的道理,如果样本空间不是简简单单就可以使用线性划分的,就需要引入多项式特征了。

用随机数制造一个数据集,用双曲线的公加上一个噪音来创造一个有两个标记的数据集

np.random.seed(123)
data = np.random.random(size=(400, 2)) * 3
target = np.array(5 * (data[:, 0] - 1.5) ** 2 + np.random.random(size=data[:, 0].shape) * 2
                     <= 3 * (data[:, 1] - 1.5) ** 2, dtype='int')

在这里插入图片描述
很显然这不具有线性关系,所以需要使用多项式武器~

log_reg = Pipeline([
    ('ploy', PolynomialFeatures(degree=2)),
    ('std', StandardScaler()),
    ('log', LogisticRegression())
])

使用多项式特征后再来看看边界。
在这里插入图片描述
可以发现,它也比较好的划分出了界线。

模型正则化

逻辑回归也会出现过拟合的现象,而模型正则化就是结局这一问题的好方法。
正则化的方式包括L1正则化,L2正则化,弹性网等方法,sklearn的逻辑回归自带这些参数。
需要注意的是与LASSO和Ridge不同,sklearn的逻辑回归的惩罚系数是在损失函数上的
以L2正则化为例
J ( θ ) = − C m ∑ i = 1 m y ∗ ( x i T θ ) + l n ( 1 − S i g m o i d ( x i T θ ) ) + 1 n ∑ i = 1 n θ i 2 J(\theta)=-\frac{C}{m}\sum\limits_{i =1}^my*(\textbf{x}_i^T\theta) +ln(1-Sigmoid(\textbf{x}_i^T\theta)) + \frac{1}{n}\sum\limits_{i = 1}^n\theta_i^2 J(θ)=mCi=1my(xiTθ)+ln(1Sigmoid(xiTθ))+n1i=1nθi2
其中C就是惩罚的系数,他不在L2上了,而是翻了过来,此时C越大惩罚反而越低,C越小惩罚反而越高,查看sklearn的文档,里面的逻辑回归默认使用L2正则化,C默认值是1

把degree调到25,并且降低惩罚

log_reg = Pipeline([
    ('ploy', PolynomialFeatures(degree=30)),
    ('std', StandardScaler()),
    ('log', LogisticRegression(C=1e15))
])
log_reg.fit(data, target)
ShowBound(log_reg, data, target, 2)
plt.show()

在这里插入图片描述
可以发现出现了过拟合现象,上下的决策边界变得不对称不规则。
尝试调小C的值
在这里插入图片描述
可以发现先,上下的数据又变得规则起来。
也可以调整LogisticRegression的参数来使用L1正则化和弹性网。

OVO与OVR

逻辑回归本身只支持二分类,但是可以通过一些改造使其支持多分类。
其中有OVO与OVR

OVR

OVR就是one vs rest
假设有4个类别,用四种不同颜色表示
在这里插入图片描述

那么OVR的想法就是拿出其中一个类别,与剩下的三种类别组成一个二分类问题。
在这里插入图片描述
很明显的,这样可以构成4种二分类,对于一个新来的样本,可以依次在每种二分类去预测一下属于哪一种,最终选择概率最高的那一个。

OVO

OVO的全程就是one vs one。
OVO的思路就是每次选出两个种类对其进行训练,然后对于新来的数据判断其属于哪一个种类。
最后进行一个投票,把新来的种类归为投票数最高的一个种类。
假设一共有n个种类则有 C n 2 C_n^2 Cn2中划分,其复杂度明显高于OVR
在这里插入图片描述
sklearn中的LogisticRegression中自带了这两种方法。

def __init__(self, penalty='l2', dual=False, tol=1e-4, C=1.0,
                 fit_intercept=True, intercept_scaling=1, class_weight=None,
                 random_state=None, solver='liblinear', max_iter=100,
                 multi_class='ovr', verbose=0, warm_start=False, n_jobs=1):

其中可以看出默认方式是OVR,如果想要使用OVO则需要调整multi_class=‘multinomial’。
同时把solver改成‘lbfgs’或者‘sag’ 或者 ‘newton-cg’。
就可以使用OVO了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值