逻辑回归分类鸢尾花和红酒等级
逻辑回归分类鸢尾花和红酒等级
- 源代码以及训练数据和测试数据已上传:https://download.****.net/download/j__max/10816259
一、实验准备
1、实验内容和目的
-
使用逻辑回归算法来对鸢尾花进行分类;同时,OJ上还给出了另外一组较强的测试数据,要求对红酒进行等级的分类
-
数据集包括训练数据train.txt和测试数据test.txt;测试数据中,每个样本包括特定的几个特征参数,最后是一个类别标签,而测试数据中的样本则只包括了特征参数
2、实验原理
-
逻辑回归是一个分类算法,它可以处理二元分类以及多元分类。虽然它的名字里面有“回归”两个字,却不是一个回归算法。个人认为,如此命名的原因在于,虽然逻辑回归是分类模型,但是它的原理残留着回归模型的影子
-
之前学习了线性回归,我们知道,线性回归的模型是求出输出特征向量和输入样本矩阵之间的线性关系系数,其满足。此时我们的是连续的,所以它是一个回归模型。那如果我们想要是离散的,该怎么办呢?一个办法就是,我们对于这个再做一次函数转换,变为。如果我们另的值在某个实数区间的时候为类别A,在另一个实数区间的时候为类别B,以此类推,就得到了一个分类模型
2.1 基于Logistic回归和Sigmoid函数的分类
-
根据上面对逻辑回归的综述,我们需要一个函数,它能够接受所有的输入然后预测出类别。例如,在两个类的情况下,上述函数输出0或者1。有一个函数刚好满足这个性质,它就是Sigmoid函数。Sigmoid函数具体的计算公式如下:
-
图5-1给出了Sigmoid函数在不同坐标尺度下的两条曲线图。当为0时,Sigmoid函数值为0.5。随着的增大,对应的Sigmoid值将逼近于1;而随着的减小,Sigmoid值将逼近于0。如果横坐标刻度足够大(图5-1下图),Sigmoid函数看起来很像一个阶跃函数
-
因此,为了实现Logistic回归分类器,我们可以在每个特征上都乘以一个回归系数,然后把所有的结果值相加,将这个总和代入Sigmoid函数中,进而得到一个范围在0~1之间的数值。任何大于0.5的数据被分为1类,小于0.5即被分为0类。
-
确定了分类器的函数形式之后,还剩下一个问题要解决:最佳回归系数是多少?如何确定它们的大小?
2.2 基于最优化方法的最佳回归系数确定
-
Sigmoid函数的输入记为,由下面公式得出:
-
如果采用向量的写法,上述公式可以写成,它表示将这两个数值向量对应元素相乘然后全部加起来即得到值。其中的向量是分类器的输入数据,向量也就是我们要找到的最佳参数(系数),从而使得分类器尽可能的精确。为了寻找该最佳参数,就需要用到最优化理论的一些知识
-
梯度上升法是最优化算法的一种,它的基本思想是:要找到某函数的最大值,最好的方法是沿着该函数的梯度方向探寻。如下图所示,梯度上升算法到达每个点后都会重新估计移动的方向。从开始,计算完该点的梯度,函数就根据梯度移动到下一个点。在点,梯度再次被重新计算,并沿新的梯度方向移动到。如此循环迭代,直到满足停止条件
二、进行实验
-
OJ上给出了两组测试数据,测试结果如下:
1、算法思路
-
整体的算法思路就是使用随机梯度上升算法来计算样本特征的权重值,然后在该权重值的基础上使用Sigmoid函数来对测试样本进行分类
-
不过有一点特殊的地方,鸢尾花有三个类别,也就是需要进行三分类。这就需要在二分类的基础上有所修改,我想到的方法就是:针对三种鸢尾花类别,分三次处理训练数据,之后得到三组权重值,最后用这三组权重值结合测试样本的特征值进行计算,通过比较函数值来分类
2、算法步骤
-
(1) 处理训练数据,得到特征参数集和类别标签集
-
(2) 使用训练数据进行训练,得到三组权重值
-
(3) 处理测试数据,取出样本的特征值
-
(4) 使用Sigmoid函数,在权重值的基础上计算测试样本的函数值
-
(5) 通过比较函数值进行分类
3、代码实现
- 具体的功能实现在代码中的注释均进行了详细说明
#!/usr/bin/python
# -*- coding utf-8 -*-
# Project: Logistic
# Author: jiangnan
# Mail: [email protected]
# Date: 2018/11/14
import numpy as np
def loadTrainDataSet(feature_count, type):
"""
函数说明:
加载和处理训练数据,分离出每个样本的特征参数和类别标签
:param
feature_count: 样本的特征个数
:param
type: 指定本次处理训练数据所针对的鸢尾花类别
0、1、2分别对应Iris-setosa、Iris-versicolor、Iris-virginica
:return:
"""
dataMat = []
labelMat = []
fr = open("data/train.txt")
for line in fr.readlines(): # 逐行处理数据
lineArr = line.strip().split(',')
currentArr = []
[currentArr.append(float(x)) for x in lineArr[ :feature_count]] # 取出每个样本的特征参数
currentArr.append(1.0) # 将X0的值设定为1.0
dataMat += [currentArr] # 将每个样本的特征参数加入结果矩阵
# 接下来结合样本的所属类别和type参数的值进行判断
# 将所针对的鸢尾花类别标签置为1
# 其余不满足条件的置为0
if (lineArr[4] == 'Iris-setosa' and type == 0):
labelMat.append(1)
elif (lineArr[4] == 'Iris-versicolor' and type == 1):
labelMat.append(1)
if (lineArr[4] == 'Iris-virginica' and type == 2):
labelMat.append(1)
else:
labelMat.append(0)
# 返回特征参数集和类别标签集
return dataMat, labelMat
def sigmoid(inX):
"""
函数说明:
Sigmoid函数,用来进行类别判断
:param
inX: 特征参数
:return:
返回该特征参数下所对应的函数值
"""
return 1.0 / (1 + np.exp(-inX))
def LogisticRegression(dataMat, labelMat, numIter = 1000):
"""
函数说明:
使用随机梯度上升算法来计算样本特征的权重
:param
dataMat: 训练数据的特征参数集
:param
labelMat: 训练数据的类别标签集
:param
numIter: 迭代次数
:return:
返回样本特征的权重值
"""
dataMat = np.array(dataMat)
m, n = np.shape(dataMat)
weights = np.ones(n) # 权重矩阵初始化为1
for i in range(numIter):
dataIndex = list(range(m))
alpha = 0.001
for j in range(m):
# 随机取得一个下标值
# 然后更新对应的回归系数值
randIndex = int(np.random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMat[dataIndex[randIndex]] * weights))
error = labelMat[dataIndex[randIndex]] - h
weights = weights + alpha * error * dataMat[dataIndex[randIndex]]
del(dataIndex[randIndex])
return weights # 返回最后的权重值
def classify(inX, weights):
"""
函数说明:
调用Sigmoid函数,计算样本在该权重值下的函数值
:param
inX: 样本的特征参数
:param
weights: 权重值
:return:
返回Sigmoid函数值
"""
inX = np.array(inX)
prob = sigmoid(sum(inX * weights))
return prob
def solve():
"""
函数说明:
综合调用上述函数进行分类
"""
# 第一次处理训练数据
# 针对属于Iris-setosa类别的鸢尾花
# 计算并输出样本特征的权重值
dataMat_0, labelMat_0 = loadTrainDataSet(4, 0)
weights_0 = LogisticRegression(dataMat_0, labelMat_0)
print(weights_0)
# 第二次处理训练数据
# 针对属于Iris-versicolor类别的鸢尾花
# 计算并输出样本特征的权重值
dataMat_1, labelMat_1 = loadTrainDataSet(4, 1)
weights_1 = LogisticRegression(dataMat_1, labelMat_1)
print(weights_1)
# 第三次处理训练数据
# 针对属于Iris-virginica类别的鸢尾花
# 计算并输出样本特征的权重值
dataMat_2, labelMat_2 = loadTrainDataSet(4, 2)
weights_2 = LogisticRegression(dataMat_2, labelMat_2)
print(weights_2)
# 对测试数据中的样本进行分类
fr = open("data/test.txt")
for line in fr.readlines():
# 提取测试样本的特征参数
lineArr = line.strip().split(',')
currentArr = []
[currentArr.append(float(x)) for x in lineArr[:4]]
currentArr.append(1.0)
# 分别使用三组权重值进行计算
# 得到对应的的三个函数值
prob_0 = classify(currentArr, weights_0)
prob_1 = classify(currentArr, weights_1)
prob_2 = classify(currentArr, weights_2)
# 比较三个函数值
# 找到使得函数值最大的那组权重值
# 将样本分类为该组权重值对应的类别
if(prob_0 > prob_1 and prob_0 > prob_2):
print('Iris-setosa')
elif(prob_1 > prob_0 and prob_1 > prob_2):
print('Iris-versicolor')
else:
print('Iris-virginica')
fr.close()
if __name__ == '__main__':
solve()
4、总结
-
逻辑回归的优缺点
-
优点:计算代价不高,易于理解和实现
-
缺点:容易欠拟合,分类精度可能不高
-
附录
- OJ上多给出了一组测试数据,要求对红酒的等级进行分类,和鸢尾花相比,只是样本的特征个数不同,因此只需要在上述代码的基础上进行小修改即可
#!/usr/bin/python
# -*- coding utf-8 -*-
# Project: Logistic
# Author: jiangnan
# Mail: [email protected]
# Date: 2018/11/14
import numpy as np
def loadTrainDataSet(feature_count, type):
dataMat = []
labelMat = []
fr = open("train.txt")
for line in fr.readlines():
lineArr = line.strip().split(',')
currentArr = []
[currentArr.append(float(x)) for x in lineArr[ :feature_count]]
currentArr.append(1.0)
dataMat += [currentArr]
if (lineArr[13] == '1' and type == 0):
labelMat.append(1)
elif (lineArr[13] == '2' and type == 1):
labelMat.append(1)
if (lineArr[13] == '3' and type == 2):
labelMat.append(1)
else:
labelMat.append(0)
return dataMat, labelMat
def sigmoid(inX):
return 1.0 / (1 + np.exp(-inX))
def LogisticRegression(dataMat, labelMat, numIter = 2000):
dataMat = np.array(dataMat)
m, n = np.shape(dataMat)
weights = np.ones(n)
for i in range(numIter):
dataIndex = list(range(m))
alpha = 0.001
for j in range(m):
randIndex = int(np.random.uniform(0, len(dataIndex)))
h = sigmoid(sum(dataMat[dataIndex[randIndex]] * weights))
error = labelMat[dataIndex[randIndex]] - h
weights = weights + alpha * error * dataMat[dataIndex[randIndex]]
del(dataIndex[randIndex])
return weights
def classify(inX, weights):
inX = np.array(inX)
prob = sigmoid(sum(inX * weights))
return prob
def solve():
dataMat_0, labelMat_0 = loadTrainDataSet(13, 0) # 修改了特征参数的个数
weights_0 = LogisticRegression(dataMat_0, labelMat_0)
print(weights_0)
dataMat_1, labelMat_1 = loadTrainDataSet(13, 1)
weights_1 = LogisticRegression(dataMat_1, labelMat_1)
print(weights_1)
dataMat_2, labelMat_2 = loadTrainDataSet(13, 2)
weights_2 = LogisticRegression(dataMat_2, labelMat_2)
print(weights_2)
fr = open("test.txt")
for line in fr.readlines():
lineArr = line.strip().split(',')
currentArr = []
[currentArr.append(float(x)) for x in lineArr[:13]]
currentArr.append(1.0)
prob_0 = classify(currentArr, weights_0)
prob_1 = classify(currentArr, weights_1)
prob_2 = classify(currentArr, weights_2)
if(prob_0 > prob_1 and prob_0 > prob_2):
print(1)
elif(prob_1 > prob_0 and prob_1 > prob_2):
print(2)
else:
print(3)
if __name__ == '__main__':
solve()