机器学习——逻辑回归算法(Logistic Regression)思想及算法实现

1.分类原理

1.1 逻辑回归

逻辑回归算法是一个分类算法,其性质是它的输出值永远在0到1之间,是目前最流行、最广泛使用的一直学习算法。

1.2 Sigmoid函数

根据线性回归模型我们只能预测连续的值,然而对于分类问题,我们需要输出0或1,此时引入逻辑回归模型,模型核心为Sigmoid函数,公式为:$g(z)=\frac{1}{1+e^{-z}}$

其中:$z=-\Theta^{T} X$

该函数图像为:

$h_{\Theta}(x)$的作用是对于给定的输入变量,根据选择的参数计算出输出变量=1的可能性。

1.3 代价函数

在逻辑回归中可以判定当$h_{\Theta}(x)$大于等于0.5时,预测y=1。

当$h_{\Theta}(x)$小于0.5时,预测y=0。

逻辑回归中使用最大似然法来拟合参数:

训练样本的特征向量为:

其中:

构建代价函数为:

使用梯度下降算法求使得代价函数最小的参数:

2.算法实现

2.1 Sigmoid函数python实现

1
2
3
4
5
import numpy as np

#定义sigmoid函数
def sigmoid(z):
return 1/(1+np.exp(-z))

2.2 梯度下降算法python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def gradAscent(dataMatIn, classLabels):
dataMatrix = mat(dataMatIn) # 转换为矩阵
labelMat = mat(classLabels).transpose() # 转换为矩阵

m, n = shape(dataMatrix)
alpha = 0.001 # 学习率
maxCycles = 505 # 迭代次数
weights = ones((n, 1))

for k in range(maxCycles): # 迭代拟合参数
h = sigmoid(dataMatrix * weights) # 求h
error = (h - labelMat) # 求误差量
weights=weights-alpha*dataMatrix.transpose()*error/len(dataMatrix)
return weights

3.数据集说明

参考数据集网址: http://archive.ics.uci.edu/ml/

本次机器学习逻辑回归算法实现分类使用的是经典数据集 Iris Data(鸢尾花数据集),数据集包含3个类(Iris Setosa、Iris Versicolour、Iris Virginica),每个类有50个实例,其中每个类都涉及一种鸢尾花品种。预测的属性为鸢尾花植物的类别。

此次算法实现中删掉了其中一个鸢尾花种类,算法引入的数据集只做鸢尾花种类的二分类,并且分别将鸢尾花种类数据集按 8:2 的比例分为训练数据集和测试数据集。

鸢尾花数据集的属性信息:

  • 萼片长度(厘米)
  • 萼片宽度(厘米)
  • 花瓣长度(厘米)
  • 花瓣宽度(厘米)

逻辑回归分类算法的模型训练过程使用鸢尾花的全部四个特征,将鸢尾花样本可视化及分类可视化过程选取了鸢尾花的前两个属性(萼片长度、萼片宽度)。

4.程序说明

4.1 载入数据集

将源数据集载入算法,并初始处理样本,将鸢尾花种类分为正向类和负向类。函数返回鸢尾花的特征向量和对应的鸢尾花种类向量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"""载入数据集"""
from numpy import *

path="iris_train.data"

def loadDataSet():
"""读取数据"""
x_data=[]
y_data=[]
with open(path) as f_obj:
for line in f_obj.readlines():
lineArr=line.strip().split(',')
lineArr.insert(0,1.0)
if (lineArr[-1]=='Iris-setosa'):
y_data.append(0)
elif(lineArr[-1]=='Iris-versicolor'):
y_data.append(1)
x_data.append(list(map(float, lineArr[:-1])))
return x_data,y_data

4.2 分类可视化

4.2.1 鸢尾花数据集二分类

为了直观理解分类结果,便于可视化,利用matplotlib库的pyplot函数,按照鸢尾花数据特征中的萼片长度、萼片宽度属性绘制训练数据集的散点图,并绘制判定边界。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def plotBestFit(weights):
dataMat, labelMat = loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1,xcord2 = [],[]
ycord1,ycord2 = [],[]
for i in range(n):
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i, 1])
ycord1.append(dataArr[i, 2])
else:
xcord2.append(dataArr[i, 1])
ycord2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = arange(3.0, 8.0, 0.1)
y = (-(float)(weights[0][0]) - (float)(weights[1][0]) * x) / (float)(weights[2][0])
ax.plot(x, y)
plt.title('Iris data set classification',fontsize=24)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.show()

4.2 分类结果

4.3 鸢尾花测试集预测

4.3.1 预测函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def predictClass():
x,y=[],[]
dataMat = loadDataSet()
print(len(mat(dataMat)))
for i in range(len(mat(dataMat))):
# 梯度下降算法拟合的最优参数
weights= [0.75401377,-0.2401068,0.15175685,0.64057407,0.93906922]
z=mat(weights[:-2])*mat(dataMat[i][:-2]).transpose()
x.append(z)
h=sigmoid(mat(weights[:-2])*mat(dataMat[i][:-2]).transpose())
y.append(h)
return x,y

def scatterClass():
x,y=predictClass()
plt.scatter(x, y, c='red',s=40)
# 设置坐标轴的取值范围
plt.axis([-1, 1, 0, 1])
plt.show()

4.3.2 预测结果

测试可视化过程只选取了梯度下降法得到的前三个最优权值,以此计算预测结果(sigmoid函数值)。

鸢尾花测试数据集为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
5.1,3.5,1.4,0.2,Iris-setosa
4.9,3.0,1.4,0.2,Iris-setosa
4.7,3.2,1.3,0.2,Iris-setosa
4.6,3.1,1.5,0.2,Iris-setosa
5.0,3.6,1.4,0.2,Iris-setosa
5.4,3.9,1.7,0.4,Iris-setosa
4.6,3.4,1.4,0.3,Iris-setosa
5.0,3.4,1.5,0.2,Iris-setosa
4.4,2.9,1.4,0.2,Iris-setosa
4.9,3.1,1.5,0.1,Iris-setosa
7.0,3.2,4.7,1.4,Iris-versicolor
6.4,3.2,4.5,1.5,Iris-versicolor
6.9,3.1,4.9,1.5,Iris-versicolor
5.5,2.3,4.0,1.3,Iris-versicolor
6.5,2.8,4.6,1.5,Iris-versicolor
5.7,2.8,4.5,1.3,Iris-versicolor
6.3,3.3,4.7,1.6,Iris-versicolor
4.9,2.4,3.3,1.0,Iris-versicolor
6.6,2.9,4.6,1.3,Iris-versicolor
5.2,2.7,3.9,1.4,Iris-versicolor

由模型测试图可看出测试结果可辨。

5.附录

load_data.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"""载入数据集"""
from numpy import *
path="iris_train.data"
def loadDataSet():
"""读取数据"""
x_data=[]
y_data=[]
with open(path) as f_obj:
for line in f_obj.readlines():
lineArr=line.strip().split(',')
lineArr.insert(0,1.0)
if (lineArr[-1]=='Iris-setosa'):
y_data.append(0)
elif(lineArr[-1]=='Iris-versicolor'):
y_data.append(1)
x_data.append(list(map(float, lineArr[:-1])))
return x_data,y_data

sigmoid.py

1
2
3
4
import numpy as np
#定义sigmoid函数
def sigmoid(z):
return 1/(1+np.exp(-z))

gradascent.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import matplotlib.pyplot as plt
from numpy import *

from sigmoid import sigmoid
from load_data import loadDataSet

def gradAscent(dataMatIn, classLabels):
dataMatrix = mat(dataMatIn) # 转换为矩阵
labelMat = mat(classLabels).transpose() # 转换为矩阵

m, n = shape(dataMatrix)
alpha = 0.001 # 学习率
maxCycles = 505 # 迭代次数
weights = ones((n, 1))

for k in range(maxCycles): # 迭代拟合参数
h = sigmoid(dataMatrix * weights) # 求h
error = (h - labelMat) # 求误差量
weights=weights-alpha*dataMatrix.transpose()*error/len(dataMatrix)
return weights

def GetResult():
dataMat, labelMat = loadDataSet()
weights = gradAscent(dataMat, labelMat)
print(weights)
plotBestFit(weights)

def plotBestFit(weights):
dataMat, labelMat = loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1,xcord2 = [],[]
ycord1,ycord2 = [],[]
for i in range(n):
if int(labelMat[i]) == 1:
xcord1.append(dataArr[i, 1])
ycord1.append(dataArr[i, 2])
else:
xcord2.append(dataArr[i, 1])
ycord2.append(dataArr[i, 2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = arange(3.0, 8.0, 0.1)
y = (-(float)(weights[0][0]) - (float)(weights[1][0]) * x) / (float)(weights[2][0])
ax.plot(x, y)
plt.title('Iris data set classification',fontsize=24)
plt.xlabel('Sepal length')
plt.ylabel('Sepal width')
plt.show()


if __name__ == '__main__':
GetResult()

predice_classfication.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from sigmoid import sigmoid

from numpy import *
import matplotlib.pyplot as plt

path="iris_test.data"

def loadDataSet():
"""读取数据"""
x_data=[]
with open(path) as f_obj:
for line in f_obj.readlines():
lineArr=line.strip().split(',')
lineArr.insert(0,1.0)
x_data.append(list(map(float, lineArr[:-1])))
return x_data

def predictClass():
x,y=[],[]
dataMat = loadDataSet()
print(len(mat(dataMat)))
for i in range(len(mat(dataMat))):
weights= [0.75401377,-0.2401068,0.15175685,0.64057407,0.93906922]
print('z',mat(weights[:-2])*mat(dataMat[i][:-2]).transpose())
z=mat(weights[:-2])*mat(dataMat[i][:-2]).transpose()
x.append(z)
h=sigmoid(mat(weights[:-2])*mat(dataMat[i][:-2]).transpose())
y.append(h)
print('h',h)
return x,y

def scatterClass():
x,y=predictClass()
plt.scatter(x, y, c='red',s=40)
# 设置坐标轴的取值范围
plt.axis([-0.75, 0.75, 0.2, 0.8])
plt.title('Iris data set prediction',fontsize=24)
plt.xlabel('Z')
plt.ylabel('Sigmoid')
plt.show()

if __name__=='__main__':
predictClass()
scatterClass()
Author: wnxy
Link: https://wnxy.xyz/2020/10/11/Machine-learning-logistic-regression-algorithm/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.