1 引言
Logistic 是在回归的基础上进行分类(二分类),试想在二维平面上,是不是可以用一条直线把数据点分开呢(如果这条直线存在的话)?假如直线存在,在这条直线上的点为0,这条直线两侧的点,对于这条直线一侧大于0,一侧小于0,这就是进行了分类,现在呢,我们引入 sigmoid 函数,用来进一步转化为概率问题,sigmoid 函数如下所示
2 Sample Code
2.1 部分数据集如下,共计 100行3列,前两列为feature,最后一列为label
import numpy as np
# 从文本中读取数据集
def loadDataSet():
x = []
y = []
fr = open(r'E:\tensorflow\Ch05\testSet.txt')
for line in fr.readlines():
# line.strip() 截取掉所有回车字符(即每行最后一个字符)
# split(str="") str - 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等分隔
line_x = line.strip().split()
# 此处第一列的 1,即为biases
x.append([1.0, float(line_x[0]), float(line_x[1])])
y.append([int(line_x[2])])
return np.mat(x),np.mat(y)
# 测试 loadDataSet
x,y = loadDataSet()
print(x.shape) # (100,3)
print(y.shape) # (100,1)
# 读进数据集之后,现在让我们来以图像化显示一下数据集
def plotFigure():
x, y = loadDataSet()
xarr = np.array(x)
n = np.shape(x)[0]
x1 = []; y1 = []
x2 = []; y2 = []
for i in np.arange(n):
if int(y[i]) == 1:
x1.append(xarr[i,1]); y1.append(xarr[i,2])
else:
x2.append(xarr[i,1]); y2.append(xarr[i,2])
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# scatter 画出散点图
ax.scatter(x1, y1, s = 30, c = 'r', marker = 's')
ax.scatter(x2, y2, s = 30, c = 'g')
plt.show()
# 画图
plotFigure()
- 数据集以图像化显示如上所示,现在我们就要来对它进行分类了(二分类问题)。也就是找到一条直线来使不同类别数据分开。
2.2 辅助函数
# 定义 sigmoid 函数
def sigmoid(x):
return 1.0 / (1 + np.exp(- x))
print(sigmoid(0)) # 输出 0.5 验证通过
# 画出决策边界
# 用此函数来画出三种梯度上升算法的分类直线
import matplotlib.pyplot as plt
def plotBestFit(weights):
x, y = loadDataSet()
xarr = np.array(x)
n = np.shape(x)[0]
x1 = []; y1 = []
x2 = []; y2 = []
for i in np.arange(n):
if int(y[i]) == 1:
x1.append(xarr[i,1]); y1.append(xarr[i,2])
else:
x2.append(xarr[i,1]); y2.append(xarr[i,2])
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
# scatter 画出散点图
ax.scatter(x1, y1, s = 30, c = 'r', marker = 's')
ax.scatter(x2, y2, s = 30, c = 'g')
# 画出Logistic 分类直线
a = np.arange(-3.0, 3.0, 0.1) # (60,)
# 由分类直线 weights[0] + weights[1] * a + weights[2] * b = 0 易得下式
b = (-weights[0] - weights[1] * a) / weights[2]
# print(b.shape) # (1, 60)
# print(b.T.shape) # (60, 1)
ax.plot(a, b.T)
plt.title('BestFit')
plt.xlabel('x1')
plt.ylabel('x2')
plt.show()
2.3 梯度上升算法
本文所使用的优化方法为 梯度上升算法,又逐步优化实现了 批量梯度上升算法,随机梯度上升算法,改进的随机梯度上升算法,并比较了三种优化方法的优越性。
2.3.1 批量梯度上升算法
# 批量梯度上升优化算法
def gradAscent(x, y):
m, n = np.shape(x) # 100 3
alpha = 0.001
maxCycle = 500
weights = np.ones((n, 1)) # 3行1列
for k in np.arange(maxCycle):
h = sigmoid(x * weights)
error = y - h
weights = weights + alpha * x.T * error # 详细推导过程见参考
return weights # (3, 1)
x,y = loadDataSet()
weights = gradAscent(x, y)
print(weights)
# [[ 4.12414349]
# [ 0.48007329]
# [-0.6168482 ]]
plotBestFit(weights)
2.3.2 随机梯度上升算法
# 随机梯度上升算法
def randgradAscent(x, y):
m, n = np.shape(x) # 100 3
alpha = 0.01
weights = np.ones(n) #(3,) [1. 1. 1.]
for i in np.arange(m):
h = sigmoid(np.sum(x[i] * weights))
error = y[i] - h
weights = weights + alpha * error * x[i]
return (np.mat(weights)).T
x,y = loadDataSet()
weights = randgradAscent(np.array(x), np.array(y))
print(weights)
# [[ 1.01702007]
# [ 0.85914348]
# [-0.36579921]]
# 画出决策边界
plotBestFit(weights)
2.3.3 改进的随机梯度上升算法
# 改进的随机梯度上升算法
def randgradAscent1(x, y, cycle = 150):
m, n = np.shape(x) # 100 3
weights = np.ones(n) # (3,) [1. 1. 1.]
# 循环 150 次
for j in np.arange(cycle):
dataindex = np.arange(m) # 100
# 在 150 次之内,每一次又循环 100 次
for i in np.arange(m):
# 定义学习率,随着 i,j 的增大,学习率越来越小,0.01保证学习率永远不为0
alpha = 4 / (1.0 + j + i) + 0.01
# 从 0 - len(dataindex) 中随机取出一个数
randindex = int(np.random.uniform(0, len(dataindex)))
# 预测类别的概率
h = sigmoid(np.sum(x[randindex] * weights))
# 误差
error = y[randindex] - h
# 梯度上升更新权重
weights = weights + alpha * error * x[randindex]
# dataindex 原来为 array ,转化为 list
dataindex = dataindex.tolist()
# 删除已取出的数
del(dataindex[randindex])
# dataindex 再次转化为 array
dataindex = np.array(dataindex)
# 注意,此处开始创建 weights 的shape为 (3,) ,现在把weight转化为matrix类型,再者转置,化为 (3, 1)
return np.mat(weights).T
x,y = loadDataSet()
weights = randgradAscent1(np.array(x), np.array(y))
print(weights)
# [[14.7744613 ]
# [ 0.93062723]
# [-2.03935598]]
# 画出决策边界
plotBestFit(weights)
3 实战: 从疝气病症预测病马的死亡率
# 预测类别
def classifyVector(x, weights):
prob = sigmoid(np.sum(x * weights))
if prob > 0.5:
return 1.0
else:
return 0.0
# 读取文件
def colicTest():
# 读取训练集
frTrain = open(r'E:\tensorflow\Ch05\horseColicTraining.txt')
# 读取测试集
frTest = open(r'E:\tensorflow\Ch05\horseColicTest.txt')
# 定义存放 feature 和 label 的容器
trainingSet = []; trainingLabels = []
# 一次取文本中的一行
# 一行共计 22 列,前 21 列为 feature, 最后一列为 label
for line in frTrain.readlines():
currLine = line.strip().split('\t')
lineArr = []
for i in np.arange(21):
lineArr.append(float(currLine[i]))
trainingSet.append(lineArr)
trainingLabels.append(float(currLine[21]))
# 调用改进的随机梯度上升算法计算权重
trainWeights = randgradAscent1(np.array(trainingSet), np.array(trainingLabels), 500)
# 测试
# 一行共计 22 列,前 21 列为 feature, 最后一列为 label
# 测试错误计数
errorCount = 0
# 测试集总共样本数
numTestVec = 0.0
# 一次取出一行
for line in frTest.readlines():
# 测试集样本计数
numTestVec += 1.0
currLine = line.strip().split('\t')
lineArr = []
for i in np.arange(21):
lineArr.append(float(currLine[i]))
# 若预测和真实类别不符合,则错误计数加 1
if int(classifyVector(np.array(lineArr), trainWeights) != int(currLine[21])):
errorCount += 1
# 计数这一次的错误率
errorRate = (float(errorCount) / numTestVec)
print("the error rate of this test is: %f" % errorRate )
return errorRate
# 主函数
def multiTest():
# 共计测试 10 次,即测试集读取10次,每次算出错误率,最后算出平均错误率
numTests = 10
# 10 次中,每一次的错误率
errorSum = 0.0
for k in np.arange(numTests):
errorSum += colicTest()
print("after %d iterations the average error rate is: %f" %(numTests, errorSum / float(numTests)))
# 开始运行,进行分类
multiTest()
参考
- Logistic回归
- GitHub源码