【从零开始的机器学习】-07 分类问题与逻辑回归

导语

如我们在第二章中提到的,有监督学习主要分为回归问题分类问题。之前的章节我们已经介绍过一元线性回归问题,多元线性回归问题,从本章开始我们将进入另一个方向——分类问题 (Classification)。

1. 什么是分类问题?

分类问题主要针对“是不是”和“有没有”的问题,大致分为:

  • 二分类问题:比如猫狗识别,判断一张图片中是猫还是狗(是不是)
  • 多分类问题:比如阿拉伯数字识别,判断一张图片中的数字是几(是不是)
  • 多标签二分类问题:比如一个包含多个物品的图片,判断是否存在物品A,B,C,…(有没有)
  • 多标签多分类问题:图片降噪,每个像素都有0~255,总共256个类别,因此为多标签256分类问题

2. 是否可以用线性回归来解决?

举个例子,垃圾邮件过滤。假如我们用一些手段获得了一系列邮件样本X (其中包含若干个特征)以及真实标签Y(表示每个样本是否是垃圾邮件),然后设置了一个线性模型h(x),通过样本X和真实标签Y进行学习,优化参数,那我们就会得到一个拟合X的模型。在这个过程中,真实标签Y其实只有两种离散的结果:0表示非垃圾邮件,1表示垃圾邮件。为我们的线性模型h(x)输出的是连续值,所以我们需要设定一个阈值(threshold) 来判断h(x)的输出应该对应0还是1,一般的,我们习惯在h(x)<阈值时认定为0,h(x)>阈值的时候认定为1。这里为了简单,我们只用1个特征,绘制图像如下:
在这里插入图片描述
我们假设当h(x)大于等于0.5时,就认定预测标签为1,当h(x)小于0.5时,认定预测标签为0。如此划分貌似很合理,我们很正确的区分了垃圾邮件和正常邮件,纵线表示的是我们的阈值(大约3.5),从此x大于等于这个阈值时就是垃圾邮件,小于这个值时就是正常邮件。

但是,假如我们再加一个x很大的,y=1的点,会发生什么呢?答:阈值会向右移,导致误判。
在这里插入图片描述
因此,使用线性回归来处理分类问题是不合适的。从另一个角度讲,我们的标签只有0和1,但h(x)的值域却可以是负无穷到正无穷,二者并不统一。

3. 二分类

3.1 假设函数

在上面的例子中,我们使用 h θ ( x ) = θ T x h_\theta(x)=\theta^T x hθ(x)=θTx作为模型,但效果不好。这时我们进行转换,设 h θ ( x ) = g ( θ T x ) h_\theta(x)=g(\theta^Tx) hθ(x)=g(θTx) z = θ T x z = \theta^Tx z=θTx,而 g ( z ) = 1 1 + e − z g(z) = \frac{1}{1+e^{-z}} g(z)=1+ez1,则:

h θ ( x ) = 1 1 + e − θ T x h_\theta(x) = \frac{1}{1+e^{-\theta^Tx}} hθ(x)=1+eθTx1

这就是sigmoid函数(也称逻辑函数,Logistic Function,逻辑回归问题也因此得名,虽然叫回归,但其实是分类问题)

sigmoid函数的图形(例如,theta=4时)如下:

sigmoid函数有几个特性:

  1. 它的输出是0~1,满足我们对预测标签范围的要求
  2. 它可以表示待测样本是正样本的概率,即 h ( x ) = P ( y = 1 ∣ x ; θ ) h(x) = P(y=1|x;\theta) h(x)=P(y=1x;θ),h(x,θ)=0.7意味着特征为x,参数为θ的情况下,y=1的可能性是70%,而y=0的可能是就是30%,二者相加一定等于100%
  3. 它关于x=0,y=0.5中心对称,可以用y=0.5做为分水岭

至于为什么要用sigmoid函数,涉及到最大熵,在此不做展开

3.2 决定边界

我们使用sigmoid函数,并规定 h θ ( x ) ≥ 0.5 h_\theta(x)\ge0.5 hθ(x)0.5时,我们预测结果为1; h θ ( x ) < 0.5 h_\theta(x)< 0.5 hθ(x)<0.5时,我们预测结果为0。

通过观察sigmoid函数的图像,我们发现当 z ≥ 0 z\ge0 z0时, h θ ( x ) ≥ 0.5 h_\theta(x)\ge0.5 hθ(x)0.5,而 z < 0 z<0 z<0时, h θ ( x ) < 0.5 h_\theta(x)< 0.5 hθ(x)<0.5

又因为 z = θ T x z = \theta^Tx z=θTx,所以上面的观察又可以替换成:当 θ T x ≥ 0 \theta^Tx\ge0 θTx0时, h θ ( x ) ≥ 0.5 h_\theta(x)\ge0.5 hθ(x)0.5;当 θ T x < 0 \theta^Tx<0 θTx<0时, h θ ( x ) < 0.5 h_\theta(x)<0.5 hθ(x)<0.5

那么, θ T x ≥ 0 \theta^Tx\ge0 θTx0 θ T x < 0 \theta^Tx<0 θTx<0又意味着什么呢?

假设我们的模型函数是: h θ ( x ) = g ( θ 0 + θ 1 x 1 + θ 2 x 2 ) h_\theta(x)=g(\theta_0+\theta_1x_1+\theta_2x_2) hθ(x)=g(θ0+θ1x1+θ2x2),那么为了使 h θ ( x ) ≥ 0.5 h_\theta(x)\ge0.5 hθ(x)0.5,应该有 θ 0 + θ 1 x 1 + θ 2 x 2 ≥ 0 x 2 ≥ − θ 1 x 1 − θ 0 θ 2 x 2 ≥ − θ 1 θ 2 x 1 − θ 0 θ 2 \theta_0+\theta_1x_1+\theta_2x_2\ge0\\x_2\ge\frac{-\theta_1x_1-\theta_0}{\theta_2}\\x_2\ge-\frac{\theta_1}{\theta_2}x_1-\frac{\theta_0}{\theta_2} θ0+θ1x1+θ2x20x2θ2θ1x1θ0x2θ2θ1x1θ2θ0

我们将 x 2 x_2 x2视为一个关于 x 1 x_1 x1的因变量的话,就可以画出一条直线(形如:y=kx+b),例如下面这种情况,圆点表示负样本(y=0),×点表示正样本(y=1),红色虚线就是决定边界(Decision Boundary)。决定边界就是y=0和y=1的分界线,下图中边界以下的点我们会预测为0,边界以上的点我们会预测为1
决定边界
而决定边界不一定是一条直线,还可能是圆,椭圆,三角,曲线,各种线,图形。比如:如果有两个特征x1和x2,满足决定边界关系为 x 1 2 + x 2 2 ≥ 1 x_1^2+x_2^2\ge1 x12+x221,那么决定边界将是一个半径为1,以(0,0)为中心的圆,圆内的样本我们会预测为0,圆外的样本我们会预测为1,随着特征的量和指数的增加,决定边界可以变成各种各种的形状。

3.3 代价函数

线性回归的代价函数,我们使用平方误差函数, J ( θ ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 J(\theta) = \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})^2 J(θ)=2m1i=1m(hθ(x(i))y(i))2,但如果我们直接沿用这个函数,会得到一个非凸函数(代价会上下波动),导致我们无法通过求导和梯度下降法最优化参数。

为了解决这个问题,分类问题我们采用另一种代价函数:
J ( θ ) = 1 m ∑ i = 1 m C o s t ( h θ ( x ( i ) , y ( i ) ) C o s t ( h θ ( x ) , y ) = − l o g ( h θ ( x ) )        i f        y = 1 C o s t ( h θ ( x ) , y ) = − l o g ( 1 − h θ ( x ) )        i f        y = 0 J(\theta)=\frac{1}{m}\sum_{i=1}^{m}Cost(h_\theta(x^{(i)},y^{(i)})\\Cost(h_\theta(x),y)=-log(h_\theta(x)) \;\;\;if \;\;\;y=1 \\Cost(h_\theta(x),y)=-log(1-h_\theta(x)) \;\;\;if \;\;\;y=0 J(θ)=m1i=1mCost(hθ(x(i),y(i))Cost(hθ(x),y)=log(hθ(x))ify=1Cost(hθ(x),y)=log(1hθ(x))ify=0

这个代价函数的图像如下(蓝色为y=1的代价函数,红色为y=0的代价函数):

横坐标为 h θ ( x ) h_\theta(x) hθ(x),范围总是0~1,纵坐标为代价 J ( θ ) J(\theta) J(θ),当y=1时, h θ ( x ) h_\theta(x) hθ(x)越接近1,代价越小,当二者相等时,代价为0,而 h θ ( x ) h_\theta(x) hθ(x)越接近0,代价越大,当 h θ ( x ) = 0 h_\theta(x)=0 hθ(x)=0时,代价为 + ∞ +\infty +。类似地,当y=0时, h θ ( x ) h_\theta(x) hθ(x)越接近0,代价越小,越靠近1,代价越大。

可是,分段函数还是不太方便,介于我们是针对二分类问题,我们可以将cost函数合并成1个:
C o s t ( h θ ( x ) , y ) = − y l o g ( h θ ( x ) ) − ( 1 − y ) l o g ( 1 − h θ ( x ) ) Cost(h_\theta(x),y) = -ylog(h_\theta(x))-(1-y)log(1-h_\theta(x)) Cost(hθ(x),y)=ylog(hθ(x))(1y)log(1hθ(x))

这个函数和上面的分段函数其实是等价的,因为当y=1时,1-y=0,后一项会变成0;当y=0时,前一项会变成0,但如此操作之后,我们可以更加方便的进行计算和代码实现了。将 C o s t Cost Cost函数代入代价函数 J ( θ ) J(\theta) J(θ),得到:
J ( θ ) = − 1 m ∑ i = 1 m [ − y ( i ) l o g ( h θ ( x ( i ) ) ) − ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ] J(\theta) = -\frac{1}{m}\sum_{i=1}^{m}[-y^{(i)}log(h_\theta(x^{(i)}))-(1-y^{(i)})log(1-h_\theta(x^{(i)}))] J(θ)=m1i=1m[y(i)log(hθ(x(i)))(1y(i))log(1hθ(x(i)))]

如果我们用矩阵表示的话,代价函数可以化简为:
J ( θ ) = 1 m ⋅ ( − y T l o g ( h ) − ( 1 − y ) T l o g ( 1 − h ) ) , h = g ( X θ ) J(\theta) = \frac{1}{m}\cdot(-y^Tlog(h)-(1-y)^Tlog(1-h)), h=g(X\theta) J(θ)=m1(yTlog(h)(1y)Tlog(1h))h=g(Xθ)

3.4 梯度下降法

现在我们知道了模型,知道了代价函数,接下来该最优化参数了。我们同样使用梯度下降法,则每一次迭代得到的新参数为(实际求导和化简的过程省略):
θ j : = θ j − α m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \theta_j := \theta_j-\frac{\alpha}{m}\sum_{i=1}^m(h_\theta(x^{(i)})-y^{(i)})x_j^{(i)} θj:=θjmαi=1m(hθ(x(i))y(i))xj(i)

看上去和我们在线性回归中得到的学习算法一致,但线性回归中的模型 h ( x ) = θ T x h(x)=\theta^Tx h(x)=θTx,而这里的模型为 h θ ( x ) = 1 1 + e − θ T x h_\theta(x) = \frac{1}{1+e^{-\theta^Tx}} hθ(x)=1+eθTx1,所以其实是不一样的。

如果用矩阵表示的话,上面的学习算法可以化简为:
θ : = θ − α m X T ( g ( X θ ) − y ⃗ ) ) \theta := \theta - \frac{\alpha}{m} X^T(g(X\theta)-\vec{y})) θ:=θmαXT(g(Xθ)y ))

3.5 代码实现

import numpy as np
from matplotlib import pyplot as plt
import csv
from sklearn.linear_model import LogisticRegression


def legend_without_duplicate_labels(ax):
    handles, labels = ax.get_legend_handles_labels()
    unique = [(h, l) for i, (h, l) in enumerate(zip(handles, labels)) if l not in labels[:i]]
    ax.legend(*zip(*unique))


def plotData(X, y, m):
    pos = np.argwhere(y==1)
    neg = np.argwhere(y==0)
    fig, ax = plt.subplots(facecolor="w")
    ax.plot(X[pos, 0], X[pos, 1], '+', color='g', label="Admitted")
    ax.plot(X[neg, 0], X[neg, 1], 'o',color='r', label="Not Admitted")
    plt.xlabel('Exam 1 Score')
    plt.ylabel('Exam 2 Score')
    legend_without_duplicate_labels(ax)
    # plt.show()

def gradientDescent(X,y,theta,alpha,iterations):
    m = len(y)
    J_history = []

    for iter in range(1,iterations+1):
        J,grad = computeCost(X,y,theta)
        theta = theta-grad*alpha
        J_history.append([iter,J])
    return theta,J_history

def featureNormalize(X)->[]:
    mu = np.mean(X,axis=0)
    sigma = np.std(X,axis=0)
    X = (X-mu)/sigma
    return [X,mu,sigma]

def computeCost(X,y,theta):
    m = len(y)
    J = sum(np.log(sigmoid(X.dot(theta))).T.dot(-1 * y) - np.log(1 - sigmoid(X.dot(theta))).T.dot(1 - y)) / m
    grad = X.T.dot(sigmoid(X.dot(theta))-y)/m
    return J,grad

def sigmoid(z):
    g = 1 / (1+np.exp(-z))
    return g

# def computeCost_Reg(X,y,theta,la):
#     m = len(y)
#     J = sum(np.log(sigmoid(X.dot(theta))).T.dot(-1 * y) - np.log(1 - sigmoid(X.dot(theta))).T.dot(1 - y)) / m
#     J = J + sum(np.square(theta[1:]))*la/(2*m)
#     grad = X.T.dot(sigmoid(X.dot(theta))-y)/m
#     temp = theta*la/m
#     temp[0,0] = 0
#     grad = grad+temp
#     return J,grad


def predict_norm(X,mu,sigma,theta):
    X = (X - mu) / sigma
    m, n = X.shape
    X = np.hstack((np.ones((m, 1)), X))
    return (sigmoid(X.dot(theta))>=0.5)


def predict(X,theta):
    m = len(X)
    X = np.hstack((np.ones((m, 1)), X))
    return (sigmoid(X.dot(theta))>=0.5)

def main():
    data = []
    with open('data1.txt') as f:
        csv_reader = csv.reader(f,delimiter=',')
        for row in csv_reader:
            data.append(row)
    f.close()

    data = np.mat(data,dtype='float32')
    m,n = data.shape
    X = data[:,0:n-1]

    y = np.mat(data[:,n-1],dtype='int32')

    plotData(X,y,m)

    test_X = X
    X,mu,sigma = featureNormalize(X)

    print(sigmoid(0))

    X = np.hstack((np.ones((m, 1)), X))
    theta = np.zeros((n, 1))

    # cost, grad = computeCost(X, y, theta)
    # print(cost)
    # print(grad)

    test_theta = np.mat([[-25.1613],[0.2062],[0.2015]])
    # cost,grad = computeCost(X,y,test_theta)
    # print(cost)
    # print(grad)

    alpha = 0.3
    iterations = 10000
    theta,history = gradientDescent(X,y,theta,alpha,iterations)
    print(theta)

    print(predict_norm(np.mat([45, 85]), mu, sigma, theta))
    print(predict_norm(np.mat([55, 75]), mu, sigma, theta))
    p = predict_norm(test_X,mu,sigma,theta)
    ans = p-y
    correct = np.argwhere(ans==0)
    print(len(correct)/m*100)

    p2 = predict(test_X,test_theta)
    ans2 = p2 - y
    correct2 = np.argwhere(ans2 == 0)
    print(len(correct2) / m * 100)

    clf = LogisticRegression()
    clf.fit(test_X,y)
    print(clf.score(test_X,y))

    print(computeCost_Reg(X,y,theta,0.1))

if __name__ == '__main__':
    main()

4. 多元分类

上面我们提到了二分类问题,那么对于多分类问题(比如,阿拉伯数字识别),我们可以将其拆分成多个二分类问题。

假设我们有三种类,下面是我们的样本,绿色表示 I I I类,红色表示 I I II II类,黑色表示 I I I III III类。那么我们可以建立3套二分类模型,分别识别“是否是 I I I类”,“是否是 I I II II类”,以及“是否是 I I I III III类”三个问题。

对于 I I I类,我们设模型为 h θ ( 1 ) ( x ) h_\theta^{(1)}(x) hθ(1)(x),这个模型输出的结果为:在特征x和参数 θ \theta θ下,y=1的概率,剩下两类都会被认为是y=0,我们只关心是否是 I I I类。因此,从 I I I类的视角来看,上面的样本图应该如下所示,青色代表无关样本,绿色代表: I I I类样本,红色虚线表示决定边界。

对于 I I II II类,我们设模型为 h θ ( 2 ) ( x ) h_\theta^{(2)}(x) hθ(2)(x),这个模型输出的结果为:在特征x和参数 θ \theta θ下,y=1的概率,剩下两类都会被认为是y=0,我们只关心是否是 I I II II类。因此,从 I I II II类的视角来看,上面的样本图应该如下所示,青色代表无关样本,红色代表: I I II II类样本,红色虚线表示决定边界。

对于 I I I III III类,我们设模型为 h θ ( 3 ) ( x ) h_\theta^{(3)}(x) hθ(3)(x),这个模型输出的结果为:在特征x和参数 θ \theta θ下,y=1的概率,剩下两类都会被认为是y=0,我们只关心是否是 I I I III III类。因此,从 I I I III III类的视角来看,上面的样本图应该如下所示,青色代表负样本,黑色代表: I I I III III类样本,红色虚线表示决定边界。

在分别最优化模型参数之后,对于一个待测样本x,我们分别用三个模型去计算,得到的就是x是 I I I类, I I II II类, I I I III III类的概率,然后去概率最大的,就是我们的预测结果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值