Python实现鸢尾花数据集分类问题——使用LogisticRegression分类器

. 逻辑回归   

逻辑回归(Logistic Regression)是用于处理因变量为分类变量的回归问题,常见的是二分类或二项分布问题,也可以处理多分类问题,它实际上是属于一种分类方法。  

概率p与因变量往往是非线性的,为了解决该类问题,我们引入了logit变换,使得logit(p)与自变量之  间存在线性相关的关系,逻辑回归模型定义如下:  

 1 #Sigmoid曲线:  
 2 import matplotlib.pyplot as plt  
 3 import numpy as np  
 4   
 5 def Sigmoid(x):  
 6     return 1.0 / (1.0 + np.exp(-x))  
 7   
 8 x= np.arange(-10, 10, 0.1)  
 9 h = Sigmoid(x)            #Sigmoid函数  
10 plt.plot(x, h)  
11 plt.axvline(0.0, color='k')   #坐标轴上加一条竖直的线(0位置)  
12 plt.axhspan(0.0, 1.0, facecolor='1.0', alpha=1.0, ls='dotted')    
13 plt.axhline(y=0.5, ls='dotted', color='k')  #在y=0.5的地方加上黑色虚线  
14 plt.yticks([0.0,  0.5, 1.0])  #y轴标度  
15 plt.ylim(-0.1, 1.1)       #y轴范围  
16 plt.show()    

 二、鸢尾花分类问题的思路分析

(1)选择使用LogisticRegression分类器,由于Iris数据集涉及到3个目标分类问题,而逻辑回归模型是二分类模型,用于二分类问题。因此,可以将其推广为多项逻辑回归模型multi-nominal logistic regression model),用于多分类。

2)根据多项逻辑回归模型,编写代码,输入数据集,训练得到相应参数并作出预测。

3)对预测出的数据的分类结果和原始数据进行可视化展示。

三、多项逻辑回归模型的原理及推导过程

 假设类别 Y 的取值集合为 {1,2,...,K},那么多项逻辑回归模型是:

 

其似然函数为:

 

其中, 为模型在输入样本时,将其判为类别k 的概率;

起到指示函数的作用,当K 等于样本的标签类别时为1,其余均为0

  对似然函数取对数,然后取负,得到(简记为:),最终要训练出的模型参数要使得的值取得最小。

的推导过程如下:

 

考虑到过拟合的发生,对加上一个正则项:

可以写成:

关于求梯度,得到:

在上式中,第一项可以看成是类别k的后验期望值,第二项 视为类别k 的先验期望值,第三项是正则化项,用于缓解过拟合。

  接下来使用梯度下降法对参数 进行修正更新即可:

                       

四、实现步骤

4.1 读入数据文件

这里需要注意的是,datas中取前两列作为特征(为了后期的可视化画图更加直观,故只取前两列特征值向量进行训练

 1 attributes=['SepalLength','SepalWidth','PetalLength','PetalWidth'] #鸢尾花的四个属性名
 2 
 3     datas=[]
 4     labels=[]
 5 
 6     # with open('IRIS_dataset.txt','r') as f:
 7     #     for line in f:
 8     #         linedata=line.split(',')
 9     #         datas.append(linedata[:-1]) #前4列是4个属性的值
10     #         labels.append(linedata[-1].replace('
','')) #最后一列是类别
11 
12     #读入数据集的数据:
13     data_file=open('IRIS_dataset.txt','r')
14     for line in data_file.readlines():
15         # print(line)
16         linedata = line.split(',')
17         # datas.append(linedata[:-1])  # 前4列是4个属性的值(误判的样本的个数为:7
18         datas.append(linedata[:-3])  # 前2列是2个属性的值(误判的样本的个数为:30
19         labels.append(linedata[-1].replace('
', ''))  # 最后一列是类别
20 
21     datas=np.array(datas)
22     datas=datas.astype(float) #将二维的字符串数组转换成浮点数数组
23     labels=np.array(labels)
24     kinds=list(set(labels)) #3个类别的名字列表

4.2 编写代码实现LogisticRegression算法

 1 # LogisticRegression算法,训练数据,传入参数为数据集(包括特征数据及标签数据),结果返回训练得到的参数 W
 2 def LogRegressionAlgorithm(datas,labels):
 3     kinds = list(set(labels))  # 3个类别的名字列表
 4     means=datas.mean(axis=0) #各个属性的均值
 5     stds=datas.std(axis=0) #各个属性的标准差
 6     N,M= datas.shape[0],datas.shape[1]+1  #N是样本数,M是参数向量的维
 7     K=3 #k=3是类别数
 8 
 9     data=np.ones((N,M))
10     data[:,1:]=(datas-means)/stds #对原始数据进行标准差归一化
11 
12     W=np.zeros((K-1,M))  #存储参数矩阵
13     priorEs=np.array([1.0/N*np.sum(data[labels==kinds[i]],axis=0) for i in range(K-1)]) #各个属性的先验期望值
14 
15     liklist=[]
16     for it in range(1000):
17         lik=0 #当前的对数似然函数值
18         for k in range(K-1): #似然函数值的第一部分
19             lik -= np.sum(np.dot(W[k],data[labels==kinds[k]].transpose()))
20         lik +=1.0/N *np.sum(np.log(np.sum(np.exp(np.dot(W,data.transpose())),axis=0)+1)) #似然函数的第二部分
21         liklist.append(lik)
22 
23         wx=np.exp(np.dot(W,data.transpose()))
24         probs=np.divide(wx,1+np.sum(wx,axis=0).transpose()) # K-1 *N的矩阵
25         posteriorEs=1.0/N*np.dot(probs,data) #各个属性的后验期望值
26         gradients=posteriorEs - priorEs +1.0/100 *W #梯度,最后一项是高斯项,防止过拟合
27         W -= gradients #对参数进行修正
28     print("输出W为:",W)
29     return

4.3 编写predict_fun()预测函数

根据训练得到的参数W和数据集,进行预测。输入参数为数据集和由LogisticRegression算法得到的参数W,返回值为预测的值。

 1 #根据训练得到的参数W和数据集,进行预测。输入参数为数据集和由LogisticRegression算法得到的参数W,返回值为预测的值
 2 def predict_fun(datas,W):
 3     N, M = datas.shape[0], datas.shape[1] + 1  # N是样本数,M是参数向量的维
 4     K = 3  # k=3是类别数
 5     data = np.ones((N, M))
 6     means = datas.mean(axis=0)  # 各个属性的均值
 7     stds = datas.std(axis=0)  # 各个属性的标准差
 8     data[:, 1:] = (datas - means) / stds  # 对原始数据进行标准差归一化
 9 
10     # probM每行三个元素,分别表示data中对应样本被判给三个类别的概率
11     probM = np.ones((N, K))
12     print("data.shape:", data.shape)
13     print("datas.shape:", datas.shape)
14     print("W.shape:", W.shape)
15     print("probM.shape:", probM.shape)
16     probM[:, :-1] = np.exp(np.dot(data, W.transpose()))
17     probM /= np.array([np.sum(probM, axis=1)]).transpose()  # 得到概率
18 
19     predict = np.argmax(probM, axis=1).astype(int)  # 取最大概率对应的类别
20     print("输出predict为:", predict)
21     return predict

 4.4 绘制图像

 

1)确定坐标轴范围,x,y轴分别表示两个特征

 1 # 1.确定坐标轴范围,x,y轴分别表示两个特征
 2     x1_min, x1_max = datas[:, 0].min(), datas[:, 0].max()  # 第0列的范围
 3     x2_min, x2_max = datas[:, 1].min(), datas[:, 1].max()  # 第1列的范围
 4     x1, x2 = np.mgrid[x1_min:x1_max:150j, x2_min:x2_max:150j]  # 生成网格采样点,横轴为属性x1,纵轴为属性x2
 5     grid_test = np.stack((x1.flat, x2.flat), axis=1)  # 测试点
 6     #.flat 函数将两个矩阵都变成两个一维数组,调用stack函数组合成一个二维数组
 7     print("grid_test = 
", grid_test)
 8 
 9     grid_hat = predict_fun(grid_test,W)  # 预测分类值
10     grid_hat = grid_hat.reshape(x1.shape)  # 使之与输入的形状相同
11     #grid_hat本来是一唯的,调用reshape()函数修改形状,将其grid_hat转换为两个特征(长度和宽度)
12     print("grid_hat = 
", grid_hat)
13     print("grid_hat.shape: = 
", grid_hat.shape) # (150, 150)

(2)指定默认字体

1 # 2.指定默认字体
2     mpl.rcParams['font.sans-serif'] = [u'SimHei']
3     mpl.rcParams['axes.unicode_minus'] = False

(3)绘制图像

 1 # 3.绘制图像
 2     cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])
 3     cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b'])
 4 
 5     alpha = 0.5
 6 
 7     plt.pcolormesh(x1, x2, grid_hat, cmap=plt.cm.Paired)  # 预测值的显示
 8     # 调用pcolormesh()函数将x1、x2两个网格矩阵和对应的预测结果grid_hat绘制在图片上
 9     # 可以发现输出为三个颜色区块,分布表示分类的三类区域。cmap=plt.cm.Paired/cmap=cm_light表示绘图样式选择Paired主题
10     # plt.scatter(datas[:, 0], datas[:, 1], c=labels, edgecolors='k', s=50, cmap=cm_dark)  # 样本
11     plt.plot(datas[:, 0], datas[:, 1], 'o', alpha=alpha, color='blue', markeredgecolor='k')
12     ##绘制散点图
13     plt.scatter(datas[:, 0], datas[:, 1], s=120, facecolors='none', zorder=10)  # 圈中测试集样本
14     plt.xlabel(u'花萼长度', fontsize=13)  #X轴标签
15     plt.ylabel(u'花萼宽度', fontsize=13)  #Y轴标签
16     plt.xlim(x1_min, x1_max) # x 轴范围
17     plt.ylim(x2_min, x2_max) # y 轴范围
18     plt.title(u'鸢尾花LogisticRegression二特征分类', fontsize=15)
19     # plt.legend(loc=2)  # 左上角绘制图标
20     # plt.grid()
21     plt.show()

五、 实验结果

(1)运行程序输出的参数:

使用二个特征:

输出W为: [[-0.41462351  1.26263398  0.26536423]

           [-1.07260354 -2.44478672  1.96448439]]

输出predict为: [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1  1 1 1 1 2 1 1 1 1 1 1 1 1 0 0 0 2 0 2 0 2 0 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2  0 0 0 0 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 2 2 0 2 2 0 2 0 0 0 0 2 0 0 0 0  0 0 2 2 0 0 0 0 2 0 2 0 0 0 0 2 2 0 0 0 0 0 0 2 0 0 0 2 0 0 0 2 0 0 0 2 0  0 2]

误判的样本的个数为:28

 

使用四个特征:

输出W为:

 [[-0.09363942 -1.41359443  1.17376524 -2.3116611  -2.20018596]

 [ 1.44071982 -0.05960463 -0.31391519 -0.87589944 -1.83255315]]

输出predict为: [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0  0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1  1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 1 2 2 2 2  2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2  2 2]

误判的样本的个数为:8

 

 

(2)数据可视化结果如下:

 

 

 

 

六、结果分析与比较

  由以上实验结果可以看出,使用了二特征的误判的样本个数为28(样本总数为150),而使用了四个特征的训练结果,误判的样本个数为8,在一定程度上可以解释使用的特征数过少的话,会导致欠拟合的情况发生。

  为了后期的可视化画图更加直观,故只取前两列特征值向量进行训练。结果展示如上图所示。

 

 

完整实现代码详见:【GitHub

 

【Reference】

1、Logistic回归多分类之鸢尾花

2、https://blog.csdn.net/BTUJACK/article/details/79761461

3、https://blog.csdn.net/eastmount/article/details/77920470

原文地址:https://www.cnblogs.com/shenxiaolin/p/8857158.html