分类任务一直都是机器学习的基础任务,已经被广泛应用在新闻分类、情感分类、主题分类、图片分类、视频分类等领域。

机器学习分类通过训练集进行学习,建立一个从输入空间 X 到输出空间 Y(离散值)的映射。按输出类别(标签)不同,可以分为二元分类(Binary Classification)、多元分类(Multi-Class Classification)。本文以二元分类为例,介绍一下机器学习在分类问题中的应用。

1 分类问题的模型表达

分类问题的模型表达与回归问题基本相同,区别仅在于输出值 Y 为离散值。通常,我们使用机器学习二元分类算法 二元归类_深度学习表示输入变量;机器学习二元分类算法 二元归类_逻辑回归_02表示输出或目标变量;机器学习二元分类算法 二元归类_机器学习_03表示训练集。我们还将使用 X 表示输入值的值域,使用 Y 表示输出值的类别。

监督学习就是给定一个训练集,学习一个函数机器学习二元分类算法 二元归类_回归_04,这样机器学习二元分类算法 二元归类_机器学习二元分类算法_05机器学习二元分类算法 二元归类_机器学习二元分类算法_06的相应值的"良好"预测因子。由于历史原因,机器学习二元分类算法 二元归类_机器学习二元分类算法_05被称为假设函数。

机器学习二元分类算法 二元归类_回归_08

2 假设函数

在分类问题中,要预测的变量 机器学习二元分类算法 二元归类_机器学习二元分类算法_06 是离散的值。分类问题的例子有:判断一封电子邮件是否是垃圾邮件;判断一次金融交易是否是欺诈;区别一个肿瘤是恶性的还是良性的。这些分类也都被称为二元分类。一般来说,我们将因变量 (dependent variable) 可能属于的两个类分别称为负向类(negative class)和正向类 (positive class) ,则因变量机器学习二元分类算法 二元归类_机器学习二元分类算法_10

线性回归模型只能预测连续的值,然而对于分类问题,只需要输出0或1。解决这个问题的一种方法是使用 logistic sigmoid 函数将线性函数的输出压缩进区间 (0, 1) 。该值可以解释为概率:

机器学习二元分类算法 二元归类_逻辑回归_11

这个方法被称为逻辑回归 (Logistic Regression) 。

逻辑回归模型的假设函数如下,其作用是,对于给定的输入变量,根据选择的参数计算输出变量等于1的可能性:

机器学习二元分类算法 二元归类_逻辑回归_12

其中,机器学习二元分类算法 二元归类_深度学习_13 代表特征向量,机器学习二元分类算法 二元归类_逻辑回归_14 代表逻辑函数(logistic function)。logistic function是一个常用的逻辑函数,也被称为Sigmoid function,公式为:

机器学习二元分类算法 二元归类_机器学习_15

例如,如果对于给定的机器学习二元分类算法 二元归类_逻辑回归_16,通过已经确定的参数计算得出机器学习二元分类算法 二元归类_深度学习_17,则表示有70%的几率机器学习二元分类算法 二元归类_机器学习二元分类算法_06为正向类,相应地机器学习二元分类算法 二元归类_机器学习二元分类算法_06为负向类的几率为1-0.7=0.3。

在逻辑回归中:

  • 机器学习二元分类算法 二元归类_回归_20时,预测 机器学习二元分类算法 二元归类_机器学习二元分类算法_21
  • 机器学习二元分类算法 二元归类_回归_22时,预测 机器学习二元分类算法 二元归类_深度学习_23

机器学习二元分类算法 二元归类_机器学习_24

  • 机器学习二元分类算法 二元归类_机器学习_25 时,预测 机器学习二元分类算法 二元归类_机器学习二元分类算法_21
  • 机器学习二元分类算法 二元归类_机器学习二元分类算法_27 时,预测 机器学习二元分类算法 二元归类_深度学习_23

假设函数和Sigmoid函数的代码表示如下:

import numpy as np

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

def hypothesis_fun(x, theta):
    return sigmoid(theta @ x)

Sigmoid函数的图像如下:

import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 100)
plt.ylim(0, 1)
plt.vlines(x=0, ymin=0, ymax=1, colors='k')
plt.plot(x, sigmoid(x));

机器学习二元分类算法 二元归类_回归_29

3 损失函数

对于线性回归模型,我们定义的损失函数是所有模型误差的平方和。理论上来说,我们也可以对逻辑回归模型沿用这个定义。但是问题在于,当我们将二元分类的假设函数机器学习二元分类算法 二元归类_逻辑回归_30带入到均方误差函数时,我们得到的损失函数将是一个非凸函数(non-convexfunction)。

这意味着我们的损失函数有许多局部最小值,这将影响梯度下降算法寻找全局最小值。

因此,我们重新定义逻辑回归的损失函数为:

机器学习二元分类算法 二元归类_机器学习二元分类算法_31

其中,

机器学习二元分类算法 二元归类_机器学习_32

这样构建的机器学习二元分类算法 二元归类_逻辑回归_33函数的特点是:当实际的 机器学习二元分类算法 二元归类_机器学习_34机器学习二元分类算法 二元归类_机器学习二元分类算法_35也为 1 时误差为 0,当 机器学习二元分类算法 二元归类_机器学习_34机器学习二元分类算法 二元归类_机器学习二元分类算法_35不为1时误差随着机器学习二元分类算法 二元归类_机器学习二元分类算法_35变小而变大;当实际的 机器学习二元分类算法 二元归类_回归_39机器学习二元分类算法 二元归类_机器学习二元分类算法_35也为 0 时代价为 0,当机器学习二元分类算法 二元归类_回归_39机器学习二元分类算法 二元归类_机器学习二元分类算法_35不为0时误差随着 机器学习二元分类算法 二元归类_机器学习二元分类算法_35的变大而变大。

进而将构建的 机器学习二元分类算法 二元归类_逻辑回归_33简化如下:

机器学习二元分类算法 二元归类_机器学习_45

带入损失函数得到:

机器学习二元分类算法 二元归类_机器学习_46

这时,损失函数机器学习二元分类算法 二元归类_机器学习_47会是一个凸函数,并且没有局部最优值。损失函数的代码表示如下:

def loss_fun(x, y, theta):
    y_p = hypothesis_fun(x, theta)
    first = y * np.log(y_p)
    second = (1 - y) * np.log(1 - y_p)
    return -np.sum(first + second)/(y.size)

4 梯度优化

在得到这样一个损失函数以后,我们便可以用梯度下降算法来求得能使损失函数最小的参数了。算法为:

机器学习二元分类算法 二元归类_机器学习_48

求导后得到:

机器学习二元分类算法 二元归类_机器学习_49

具体的推导过程如下,考虑到:

机器学习二元分类算法 二元归类_机器学习_50

则:

机器学习二元分类算法 二元归类_深度学习_51

所以:

$$
\begin{aligned}
\frac{\partial }{\partial {\theta_{j}}}J\left( \theta \right) &=\frac{\partial }{\partial {\theta_{j}}}[-\frac{1}{m}\sum\limits_{i=1}{m}{[-{{y}{(i)}}\log \left( 1+{{e}{-{\theta{T}}{{x}^{(i)}}}} \right)-\left( 1-{{y}^{(i)}} \right)\log \left( 1+{{e}{{\theta{T}}{{x}^{(i)}}}} \right)]}]

\newline &=-\frac{1}{m}\sum\limits_{i=1}{m}{[-{{y}{(i)}}\frac{-x_{j}{(i)}{{e}{-{\theta{T}}{{x}{(i)}}}}}{1+{{e}{-{\theta{T}}{{x}^{(i)}}}}}-\left( 1-{{y}^{(i)}} \right)\frac{x_j{(i)}{{e}{{\thetaT}{{x}{(i)}}}}}{1+{{e}{{\thetaT}{{x}^{(i)}}}}}}]

\newline &=-\frac{1}{m}\sum\limits_{i=1}{m}{[{y}{(i)}}\frac{x_j{(i)}}{1+{{e}{{\thetaT}{{x}{(i)}}}}}-\left( 1-{{y}^{(i)}} \right)\frac{x_j{(i)}{{e}{{\thetaT}{{x}{(i)}}}}}{1+{{e}{{\thetaT}{{x}^{(i)}}}}}]

\newline &=-\frac{1}{m}\sum\limits_{i=1}{m}{\frac{{{y}{(i)}}x_j{(i)}-x_j{(i)}{{e}{{\thetaT}{{x}{(i)}}}}+{{y}{(i)}}x_j{(i)}{{e}{{\thetaT}{{x}{(i)}}}}}{1+{{e}{{\thetaT}{{x}^{(i)}}}}}}

\newline &=-\frac{1}{m}\sum\limits_{i=1}{m}{\frac{{{y}{(i)}}\left( 1\text{+}{{e}{{\thetaT}{{x}^{(i)}}}} \right)-{{e}{{\thetaT}{{x}{(i)}}}}}{1+{{e}{{\thetaT}{{x}{(i)}}}}}x_j^{(i)}}

\newline &=-\frac{1}{m}\sum\limits_{i=1}{m}{({{y}{(i)}}-\frac{{{e}{{\thetaT}{{x}{(i)}}}}}{1+{{e}{{\thetaT}{{x}{(i)}}}}})x_j^{(i)}}

\newline &=-\frac{1}{m}\sum\limits_{i=1}{m}{({{y}{(i)}}-\frac{1}{1+{{e}{-{\thetaT}{{x}{(i)}}}}})x_j{(i)}}

\newline &=-\frac{1}{m}\sum\limits_{i=1}{m}{[{{y}{(i)}}-{h_\theta}\left( {{x}^{(i)}} \right)]x_j^{(i)}}

\newline &=\frac{1}{m}\sum\limits_{i=1}^{m}{[{h_\theta}\left( {{x}^{(i)}} \right)-{{y}{(i)}}]x_j{(i)}}
\end{aligned}
$$

线性回归中,我们能够通过求解正规方程以找到最佳权重。相比而言,逻辑回归会更困难些。其最佳权重没有闭解,只能通过梯度下降算法最小化损失函数来搜索。具体代码如下

def gradient_decent(x, y, theta, learning_rate):
    gradient = x @ (hypothesis_fun(x, theta) - y)/ y.size
    return theta - learning_rate * gradient

5 测试数据集

关注公众号,回复20230310下载测试数据。测试数据共三列,前两列为自变量,最后一列为因变量。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
path = 'class.data'
data = pd.read_csv(path)
data.head()

X

Y

Admitted

0

34.623660

78.024693

0

1

30.286711

43.894998

0

2

35.847409

72.902198

0

3

60.182599

86.308552

1

4

79.032736

75.344376

1

x = data[['X', 'Y']].to_numpy()
x = x.transpose()
y = data['Admitted'].to_numpy()
x.shape, y.shape

((2, 100), (100,))

positive = data[data['Admitted'].isin([1])]
negative = data[data['Admitted'].isin([0])]

fig, ax = plt.subplots(figsize=(12, 8))
ax.scatter(positive['X'], positive['Y'], s=50, 
           c='b', marker='o', label='Admitted')
ax.scatter(negative['X'], negative['Y'], s=50, 
           c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
plt.show()

机器学习二元分类算法 二元归类_逻辑回归_52

6 模型训练与预测

二元分类和多元线性回归相同,在正式训练之前需要对输入变量进行了无量纲化。此外,在输入变量x矩阵的第一行前插入全是1的一行,用于表示偏置项。

x2 = feature_scale(x)
x2 = np.vstack((np.ones(x2.shape[1]), x2))

parameters = np.random.rand(x2.shape[0])
learning_rate = 0.1
losses = []

batch_size = 70
epoch_size = 1000

for epoch in range(epoch_size):
    for i in range(y.size//batch_size+1):
        random_samples = np.random.choice(x2.shape[1], batch_size)
        parameters = gradient_decent(x2[:, random_samples], 
                              y[random_samples], 
                              parameters, 
                              learning_rate)
        losses.append(loss_fun(x2, y, parameters))
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 9))
ax1.plot(losses)
ax1.set_xlabel('Iteration number')
ax1.set_ylabel('Loss')

predict_y = hypothesis_fun(x2, parameters)
predict_y[predict_y>=0.5] = 1
predict_y[predict_y<1] = 0
ax2.scatter(x[0][predict_y!=y], x[1][predict_y!=y], s=80, 
            c='r', marker='o', label='Wrong', alpha=1)
ax2.scatter(x[0][predict_y==1], x[1][predict_y==1], s=50, 
            c='b', marker='*', label='Admitted')
ax2.scatter(x[0][predict_y==0], x[1][predict_y==0], s=50, 
            c='b', marker='x', label='Not Admitted')
ax2.legend()
ax2.set_xlabel('X')
ax2.set_ylabel('Y')
plt.show();

机器学习二元分类算法 二元归类_机器学习二元分类算法_53

此外,和非线性回归的原理类似,在进行二元分类时也可以对输入变量进行变换,以使假设函数更好的逼近预测值。而这种变换同样不需要对模型进行任何改变,只需要改变一下输入即可。如,将输入变为如下形式:

times = 3
x2 = x.copy()
for i in range(2, times+1):
    x2 = np.vstack((x2, x2**i))
x2 = feature_scale(x2)
x2 = np.vstack((np.ones(x2.shape[1]), x2))

此时预测结果如下,可见预测结果更为准确。但需要注意的是,变换之后由于自变量数量增加需要更多的训练次数(epoch):

机器学习二元分类算法 二元归类_机器学习_54