分类问题

问题描述

机器学习中有监督学习主要分为回归问题和分类问题,回归问题希望预测的结果是连续的,为什么不能用之前线性回归的方法来处理分类问题呢?答案其实是可以,不过我们在计算损失函数时需要对预测值远远偏离真实值的对象进行打击。理由也很简单,因为这些过大的偏差会使我们的回归结果向减小这类偏差的方向移动,也就是说距离分类边界太原的数据点会迫使分类边界向其方向移动,如图所示:

rr6XA.png

分类问题所预测的结果是离散的“类别”。这是输入变量可以是离散的也可以是连续的,监督学习从数据中学习一个分类模型或分类决策函数就是分类器,分类器根据输入变量对输出进行预测,即为分类,

分类问题太好理解了,生活中处处是分类,在此不再赘述。

分类问题中最简单的自然就是:二分类went

二分类问题

说起二分类问题,第一个想到的自然就是Logistic(对数几率),首先了解以下什么是Logistic分布:

Logistic分布

服从Logistic分布是指X的分布函数和概率密度函数服从:
$$
F(x)=P(X\leq x)=\frac{1}{1+e^{-(x-\mu)\gamma}}
$$

$$
f(x)=\frac{e^{-(x-\mu)\gamma}}{\gamma(1+e^{-(x-\mu)\gamma})^2}
$$

其中$\mu$影响 中心对称点位置 ,$\gamma$越小中心点附近的增长速度越开。而Sigmoid函数就是$\mu =0,\gamma=1$的一个特例:

r2QAF.png

对数几率回归

对于二分类Logistic问题,其目标是希望找到一个区分度足够好的决策边界,能够将两类很好的分开。

​ 假设输入数据的特征向量$x\in R^n$,那么剧场边界可以表示为$\sum_{i=1}^n w_ix_i+b=0$,假设预测样本的$x_0$,使得$h(x_0)=\sum_{i=1}^n w_ix_i+b>0$,则判断其为1类,反之若$h(x_0)=\sum_{i=1}^n w_ix_i+b<0$,判断其为0类。而Logistic回归要跟进一步,利用比较概率值来判断类别。

将sigmoid函数应用于分类问题:
$$
y=\frac{1}{1+e^{({\bf w^Tx}+b)}}
$$
若将y视为样本x为正例的可能性$p(y=1\vert x)$,则1-y为样本为反例的可能性$p(y=0\vert x)$,用概率更改上述公式为:

显然:
$$
p(y=1\vert x)=\frac{e^{w^Tx+b}}{1+e^{w^Tx+b}}
$$

$$
p(y=0\vert x)=\frac{1}{1+e^{w^Tx+b}}
$$

我们给出一个定义,几率:只一个事件发生的概率与不发生概率的比值,那某事件发生的对数几率就为:
$$
\ln it §=\ln \frac{p}{1-p}
$$

$$
\ln\frac{p(y=1\vert x)}{p(y=0\vert x)}=w^Tx+b
$$
观察上式等号右边项,只想道一声别来无恙,这不就是线性回归的模型吗?那不就是把线性回归的推到再推一遍呐,不过不一样的是我们此时不需要回归,而是分类,也就是说我们不需要其求出来的Loss最小,而是要似然概率最大。

什么?你不知道什么是似然概率?那您自己百度吧。简单的说就是根据结果推导分布。

给定数据集${(x_i,y_i)}_{i=1}^m$,对于每一对数据,预测正确的概率为:
$$
p(y_i\vert x_i;w,b)=y_ip(\hat y_i=1\vert x;w,b)+(1-y_i)p(\hat y_i=0\vert x;w,b)
$$
其中 $y_i$分为0,1两类.

假设其中有n个分类为1,则似然函数为:

$$
\prod _{i=1}^m[p(\hat y_i=1\vert x;w,b)]^n[p(\hat y_i=1\vert x;w,b)]^{m-n}
$$

二项分布嘛,我们找到合适的参数$w,b$使其最大,乘法肯定不好计算,所以我们使用对数简化为加法,即最大化似然对数:
$$
l(w,b)=\sum_{i=1}^m\ln p(y_i\vert x_i;w,b)
$$
上式等价于:
$$
\begin{aligned}
l(w,b)&=\sum_{i=1}^mp(y_i\vert x_i;w,b)\\
&=\sum_{i=1}^m\ln \frac{y_i(e^{w^Tx_i+b})+(1-y_i)}{1+e^{w^T x_i+b}}\\
&=\sum_{i=1}^m[ln(y_ie^{w^T x_i+b}+(1-y_i))-\ln(1+e^{w^T x_i+b})]\\
&=\sum_{i=1}^m(y_i(w^Tx_i+b)-\ln (1+e^{w^T x_i+b} ))
\end{aligned}
$$

最简单就是使用梯度下降法求解上述最大化问题:
$$
\begin{aligned}
\frac{\partial L(w)}{\partial w}&=\sum_{i=1}^my_ix_i-\sum_{i=1}^m\frac{e^{w^Tx_i+b}}{1+e^{w^Tx_i+b}}x_i\\
&=\sum_{i=1}^m(y_i-p(y_i=1\vert x_i))x_i
\end{aligned}
$$

$$
\begin{aligned}
\frac{\partial L(w)}{\partial b}&=\sum_{i=1}^my_ix_i-\sum_{i=1}^m\frac{e^{w^Tx_i+b}}{1+e^{w^Tx_i+b}}\\
&=\sum_{i=1}^m(y_i-p(y_i=1\vert x_i))
\end{aligned}
$$

注意上述公式中$w,x_i$都为列向量,类似多元线性回归中的情形。

鸢尾花数据集的二分类实现:

rhaiq.png

输入x特征向量为4维,通过PCA降维到二维,二分类

import numpy as np
import pandas as pd
import torch
from torch import nn
import os,sys
import matplotlib.pylab as plt
os.chdir(sys.path[0])
def pca(X,k):#k is the components you want
#mean of each feature
n_samples, n_features = X.shape
mean=np.array([np.mean(X[:,i]) for i in range(n_features)])

norm_X=X-mean #去中心化

scatter_matrix=np.dot(np.transpose(norm_X),norm_X)#协方差矩阵

eig_val, eig_vec = np.linalg.eig(scatter_matrix)#协方差矩阵的特征向量和特征值
eig_pairs = [(np.abs(eig_val[i]), eig_vec[:,i]) for i in range(n_features)]

eig_pairs.sort(reverse=True)#由大到小排序

feature=np.array([ele[1] for ele in eig_pairs[:k]])#留下k个主要特征

data=np.dot(norm_X,np.transpose(feature))
return data



#数据读取
label_set=lambda x: 1 if x=='"setosa"' else 0
with open('./iris/iris.txt') as f:
data_list=f.readlines()
data_list=[i.split('\n')[0]for i in data_list[1:]]
data_list=[i.split(' ') for i in data_list]
data=[[float(i[1]),float(i[2]),float(i[3]),float(i[4]),label_set(i[5])] for i in data_list]

x_data=[[i[0],i[1],i[2],i[3]]for i in data]
y_data=[[i[4]] for i in data]
x_data=pca(np.array(x_data),2)
x_data=torch.from_numpy(x_data).float()
y_data=torch.from_numpy(np.array(y_data)).float()
#模型定义
class Logistic_Regreession(nn.Module):
def __init__(self) -> None:
super(Logistic_Regreession,self).__init__()
self.LR=nn.Linear(2,1)
self.sm=nn.Sigmoid()
def forward(self,x):
y=self.LR(x)
output=self.sm(y)
return output

if torch.cuda.is_available():
x_data=x_data.cuda()
y_data=y_data.cuda()
model=Logistic_Regreession().cuda()
else:
model=Logistic_Regreession()
criterion=nn.MSELoss()
optimizer=torch.optim.SGD(model.parameters(),lr=1e-3,weight_decay=0.05,momentum=0.78)

epochs=2000
for epoch in range(epochs):
output=model(x_data)
mask=output.ge(0.5).float()

correct=(mask==y_data).sum()
acc=correct/y_data.shape[0]
loss=criterion(output,y_data)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch%10==0:
print("epoch:{},loss:{:.6f},acc:{:.4f}".format(epoch,loss,acc))
x=x_data.numpy()[:,0]
y=x_data.numpy()[:,1]
x1=[]
y1=[]
y2=[]
x2=[]
for i in range(len(x)):
if y_data[i]==0:
x1.append(x[i])
y1.append(y[i])
else:
x2.append(x[i])
y2.append(y[i])
w=model.LR.weight[0]
w0=w[0].detach().numpy()
w1=w[1].detach().numpy()
b=model.LR.bias.data[0].numpy()
plot_x=np.arange(-2,3,0.01)
plot_y=(-w0*plot_x+b)/w1
plt.scatter(x1,y1,color='red')
plt.scatter(x2,y2,color='blue')
plt.plot(plot_x,plot_y)
plt.show()

rh3ce.png

rz92Q.png

多分类问题

多分类问题一般都是将其简化为多个二分类问题使其得以解决:

主要做法有以下三类:

一对一(OvO)

one vs one:

给定多分类$y_i \in {C_1,C_2,…C_N}$,n分类问题,OVO将这N个类别两两配对,从而产生$\frac{N(N-1)}{2}$个二分类任务,然后对某一输入做预测时,这$\frac{N(N-1)}{2}$个分类器分别各自作出分类,最终将被预测得最多的类别作为分类结果。参考如下图

一对其余(OvR)

One VS Rest:

每次将一个类作为正例,其他所有类作为反例来训练N个分类器,在测试时若只有一个分类器作为正类,则将其作为最终分类。

rhFtS.png

多对多(MvM)

Many VS Many :

每次将若干个类作为正类,若干个其他类作为反类。MVM的正反类构造必须有 特殊的设计,不能随意选取,常用技术为:纠错输出码’

纠错输出码(Error Correcting Output Codes)

ECOC将编码思想引入类别拆分:

  • 编码:对N个类别做M次划分,每次划分将一部分分类化为正类,一部分化为反类,从而形成一个二分类训练集;这样一共产生M个训练集,可以训练出M个分类器
  • M个分类器分别对测试样本进行预测,这些预测标记组成一个编码,将这个预测编码与每个类别各自的编码进行比较,返回其中距离最小的作为最终预测结果

类别划分通过"编码矩阵"完成,常用编码矩阵有二元码和三元码,前者将类别分别指定为正类和反类,后者除了正反类之外,还可以指定停用类。

rhBZy.png

纠错输出码有一定的纠错能力,比如图(a)中,正确的预测编码为(-1,+1,+1,-1,+1),但$f_2$分类器出错导致其编码为(-1,-1,+1,-1,+1),但基于该编码仍然正确分类为$C_3$

类别不平衡问题

以上各个分开类的实现前提都是 我的数据集中各类数据基本相当,若是出现类别不平衡情况:比如二分类问题中,数据集有998个正例却只有2个反例,这样的数据所训练的分类器没有任何价值,因为它会将所有测试数据预测为正例。

有办法解决这个问题吗?当然有,记得我们在分类器sigmoid函数输出之后,将其与0.5比较,大于0.5的判为正例,小于0.5的判为反例。为何一定是0.5呢?因为我们假设正反例可能性相同,即分类器比率决策规则为:
$$
若\frac{y}{1-y}>1,则其为正例
$$
那既然数据集中正反例观测概率不为0.5,那我们何不改变此规则呢?

若数据集中正例的个数为$m^+$,反例数为$m^-$,则决策规则更改为:
$$
若\frac{y}{1-y}>\frac{m^+}{m^-},则其为正例
$$
等价于:
$$
若\frac{y’}{1-y’}=\frac{y}{1-y}\times \frac{m^-}{m^+}>1,则其为正例
$$
这就是所谓的——再放缩

该方法的思想虽然简单,但是在实现过程中不一定有效,因为我们所得的正反例比率仅仅是通过训练集统计而得,虽然我们假设“训练集是样本总体的无偏采样”,但实际却往往并不成立,也就是说,我们未必能基于训练集观测几率来推测出样本总体的真实几率。

所以实际中主要有三种办法:

  • 欠采样:除去一些反例或正例使得正反例数目接近
  • 过采样:增加一些正例或者反例使得正反例数目接近
  • 阈值移动:就是在决策时采用上文所述的再缩放方法