双类别逻辑回归

假设某班学生的两门考试成绩(exam1 score, exam2 score)与最终评价是否合格(passed)的数据如下(部分数据):

链接:https://pan.baidu.com/s/1-vDe2IJKARdpnmnVOD8a2w?pwd=6688 
提取码:6688

根据上面的训练数据,如果再提供一组新的分数(例如:65,58),则该学生是否通过呢?

''' 查看成绩数据点的分布图 '''

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

def initPlot():
    plt.figure()
    plt.title('Exam Scores for Final Pass')
    plt.xlabel('Exam score 1')
    plt.ylabel('Exam score 2')
    plt.axis([30, 100, 30, 100])
    return plt

trainData = np.loadtxt(open('exam_score.csv', 'r'), delimiter=",",skiprows=1)

plt = initPlot()
score1ForPassed = trainData[trainData[:, 2] == 1, 0]    # 从trainData中获取下标索引第2列(passed)值为1的所有行的第0列元素
score2ForPassed = trainData[trainData[:, 2] == 1, 1]
score1ForUnpassed = trainData[trainData[:, 2] == 0, 0]
score2ForUnpassed = trainData[trainData[:, 2] == 0, 1]
plt.plot(score1ForPassed,score2ForPassed,'r+')
plt.plot(score1ForUnpassed,score2ForUnpassed,'ko')
plt.show()

水平方向为Exam 1 Score,垂直方向为Exam 2 Score。红色+点表示Passed=1,黑色圆圈点表示Passed=0。可以观察到,所有数据点较为明显的分成两个类别(通过或不通过)。

线性回归主要都是针对训练数据和计算结果均为数值的情形。而在本例中,结果不是数值而是某种分类:考试成绩通过或不通过。在这种情况下,可以将每个类别作为一个数值结果,然后通过模型计算自变量与该分类数值结果之间的关系。逻辑回归则提供了这类问题的解决办法。

使用LogisticRegression

  • 设置逻辑回归算法的某些属性

  • model = LogisticRegression(solver=‘lbfgs’)
    solver–优化器:使用lbfgs算法来执行回归计算。默认使用liblinear。注意,这两种算法的结果并不相同

  • 执行计算
    model.fit(X, y)

  • 执行预测
    model.predict(newX)
    返回值是newX矩阵中每行数据所对应的结果。如果是1,则表示passed;如果是0,则表示unpassed 获得模型参数值
    theta0 = model.intercept_[0]
    theta1 = model.coef_[0,0]
    theta2 = model.coef_[0,1]

  • 决策边界线 决策边界线可视为两种类别数据点的分界线。在该分界线的一侧,所有数据点都被归为passed类(1),另一侧的所有数据点都被归为unpassed类(0) 对于本例来说,决策边界线是一条直线(在案例2中进行了说明) theta0theta1theta2定义了决策边界线直线:其中,表示横坐标(Exam Score 1),表示纵坐标(Exam Score 2),表示该直线的截距

''' 使用LogisticRegression进行逻辑回归 '''

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression

trainData = np.loadtxt(open('exam_score.csv', 'r'), delimiter=",",skiprows=1)
xTrain = trainData[:,[0,1]]          # 无需追加Intercept Item列
yTrain = trainData[:,2]

# print(trainData)
# print(xTrain)

model = LogisticRegression(solver='lbfgs')   # 使用lbfgs算法。默认是liblinear算法
model.fit(xTrain, yTrain)
newScores = np.array([[58, 67],[90, 90],[35, 38],[55, 56]])
print("预测结果:")
print(model.predict(newScores))
# 获取theta计算结果
print(model.intercept_)
print(model.coef_)
theta = np.array([model.intercept_[0], model.coef_[0,0], model.coef_[0,1]])

def initPlot():
    plt.figure()
    plt.title('Exam Scores for Final Pass')
    plt.xlabel('Exam score 1')
    plt.ylabel('Exam score 2')
    plt.axis([30, 100, 30, 100])
    return plt

plt = initPlot()
score1ForPassed = trainData[trainData[:,2] == 1, 0]
score2ForPassed = trainData[trainData[:,2] == 1, 1]
score1ForUnpassed = trainData[trainData[:,2] == 0, 0]
score2ForUnpassed = trainData[trainData[:,2] == 0, 1]
plt.plot(score1ForPassed,score2ForPassed,'r+')
plt.plot(score1ForUnpassed,score2ForUnpassed,'ko')
boundaryX = np.array([30, 50, 70, 90, 100])                 # 绘制决策边界线
boundaryY = -(theta[1] * boundaryX + theta[0]) / theta[2]   # 根据决策边界线的直线公式和x值,计算对应的y值
plt.plot(boundaryX, boundaryY, 'b-')
plt.show()

上图做出了决策边界线,该线左下方的数据点,被认为分到第0类(考试不通过),该线右上方的数据点,被认为分到第1类(考试通过)决策边界线不一定能正好把所有有本都正确的分类,除非采用更高阶的模型(而不是本例中的线性模型)。但是高阶模型也可能产生过拟合。

基于成本函数和梯度下降算法的实现

  • 判别式/预测函数

  • x代表一个(行)样本数据,该样本数据共有n个变量(维度)

  • 为Intercept Item,一般设置为1

  • g称为sigmoid激活函数,其定义为

  • 当z=0时,g(z)的值为0.5。低于0.5的g(z)可以认为预测为false,高于0.5的预测为true。

  • 激活函数相当于起了这样的作用:将一个连续的数值量,基于设定的阈值转变成离散的分类结果

  • 成本函数

  • 使用矩阵运算表达如下:

  • 梯度计算:

  • 计算决策边界线(Decision Boundary):
    决策边界线上所有的点,其预测出来的y值正好为0.5,即:当n=2时有: ,该边界线是一条直线

  • 程序编写注意事项:

  • 仔细思考成本函数和梯度计算的矩阵运算实现

  • 先对数据进行归一化处理,有助于计算收敛

  • 训练数据和测试数据需要手动添加Intercept Item列。先进行归一化,再手动添加Intercept Item

  • 测试数据也需要进行相应的归一化处理,才能预测。

  • 绘制边界线时,边界线上的点横坐标数据也需要先归一处理,然后求出归一化的纵坐标,最后再回算出纵坐标的正常值

  • 一般情况下,测试数据、边界线数据在进行归一化处理时,都应该采用训练数据中计算出来的列平均值,而不是它们自身的平均值

''' 使用梯度下降算法进行逻辑回归 '''

%matplotlib inline 
import numpy as np
import matplotlib.pyplot as plt
import bgd_resolver

def normalizeData(X, column_mean, column_std):       # 归一化
    return (X - column_mean) / column_std

def sigmoid(z):                                      #
    return 1. / (1 + np.exp(-z))

def costFn(theta, X, y):                             # 成本函数
    temp = sigmoid(X.dot(theta))
    cost = -y.dot(np.log(temp)) - (1 - y).dot(np.log(1 - temp))
    return cost / len(X)

def gradientFn(theta, X, y):                         # 梯度下降
    return xTrain.T.dot(sigmoid(xTrain.dot(theta)) - yTrain) / len(X)

def initPlot():                                      # 绘图函数
    plt.figure()
    plt.title('Exam Scores for Final Pass')
    plt.xlabel('Exam score 1')
    plt.ylabel('Exam score 2')
    plt.axis([30, 100, 30, 100])
    return plt


trainData = np.loadtxt(open('exam_score.csv', 'r'), delimiter=",",skiprows=1)
xTrain = trainData[:, [0, 1]]
# 计算训练数据每列平均值和每列的标准差
xTrain_column_mean = xTrain.mean(axis=0)
xTrain_column_std = xTrain.std(axis=0)
xTrain = normalizeData(xTrain, xTrain_column_mean, xTrain_column_std)      # 如果不进行归一化处理,计算过程中可能产生溢出(但似乎仍可以收敛)
x0 = np.ones(len(xTrain))
xTrain = np.c_[x0, xTrain]          # 需手动追加Intercept Item列
yTrain = trainData[:,2]             # 取出因变量

np.random.seed(0)
init_theta = np.random.random(3)    # 随机初始化theta
theta = bgd_resolver.batch_gradient_descent(costFn, gradientFn, init_theta, xTrain, yTrain, 0.005, 0.00001)

# 预测若干数据,也需要先归一化,使用之前训练数据的mean和std
newScores = np.array([[58, 67], [90, 90], [35, 38], [55, 56]]) 
newScores = normalizeData(newScores, xTrain_column_mean, xTrain_column_std)
x0 = np.ones(len(newScores))
newScores = np.c_[x0, newScores]  # 注意要添加Intercept Item
print("预测结果:")
print(sigmoid(newScores.dot(theta)))

plt = initPlot()
score1ForPassed = trainData[trainData[:,2] == 1, 0]    
score2ForPassed = trainData[trainData[:,2] == 1, 1]
score1ForUnpassed = trainData[trainData[:,2] == 0, 0]
score2ForUnpassed = trainData[trainData[:,2] == 0, 1]
plt.plot(score1ForPassed,score2ForPassed,'r+')
plt.plot(score1ForUnpassed,score2ForUnpassed,'ko')

# 绘制决策边界线
boundaryX = np.array([30, 50, 70, 90, 100])
# 因为之前进行了归一化,因此边界线上点的x坐标也需要先归一化。x坐标对应的列索引是0
normalizedBoundaryX = (boundaryX - xTrain_column_mean[0]) / xTrain_column_std[0]
# 下面计算出来的边界线上的y坐标normalizedBoundaryY是经过归一化处理的坐标 
normalizedBoundaryY = -(theta[1] * normalizedBoundaryX + theta[0] ) / theta[2]
# boundaryY才是将归一化坐标还原成正常坐标。y坐标对应的列索引是1
boundaryY = xTrain_column_std[1] * normalizedBoundaryY + xTrain_column_mean[1]

plt.plot(boundaryX, boundaryY, 'b-')
plt.show()

基于scipy.optimize优化运算库的实现

  • 使用minimize库函数

  • 需要提供jac参数,并将其设置为梯度计算函数

  • scipy.optimize库中提供的算法会比我们自己实现的算法更高效、灵活、全面

  • 本例中没有对数据进行归一处理,因此导致minimize方法执行过程中溢出(尽管可能也能收敛)。请自行添加归一化处理功能

''' 使用minimize来优化逻辑回归求解 '''

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize as opt

# 定义全局变量
trainData = np.loadtxt(open('exam_score.csv', 'r'), delimiter=",",skiprows=1)
xTrain = trainData[:,[0, 1]]
x0 = np.ones(len(xTrain))
xTrain = np.c_[x0, xTrain]
yTrain = trainData[:,2]

def sigmoid(z):
    return 1. / (1 + np.exp(-z))

# Cost Function以theta为参数
def costFn(theta, X, y):
    temp = sigmoid(xTrain.dot(theta))
    cost = -yTrain.dot(np.log(temp)) - (1 - yTrain).dot(np.log(1 - temp))
    return cost / len(X)

# Gradient Function以theta为参数
def gradientFn(theta, X, y):
    return xTrain.T.dot(sigmoid(xTrain.dot(theta)) - yTrain) / len(X)

np.random.seed(0)
# 随机初始化theta,计算过程中可能产生溢出。
# 可以尝试将init_theta乘以0.01,这样可以防止计算溢出
init_theta = np.random.random(xTrain.shape[1])      
result = opt.minimize(costFn, init_theta, args=(xTrain, yTrain), method='BFGS', jac=gradientFn, options={'disp': True})
theta = result.x            # 最小化Cost时的theta

# 预测若干数据
newScores = np.array([[1, 58, 67],[1, 90,90],[1, 35,38],[1, 55,56]])  # 注意要添加Intercept Item
print("预测结果:")
print(sigmoid(newScores.dot(theta)))

def initPlot():
    plt.figure()
    plt.title('Exam Scores for Final Pass')
    plt.xlabel('Exam score 1')
    plt.ylabel('Exam score 2')
    plt.axis([30, 100, 30, 100])
    return plt

plt = initPlot()
score1ForPassed = trainData[trainData[:,2] == 1, 0]    
score2ForPassed = trainData[trainData[:,2] == 1, 1]
score1ForUnpassed = trainData[trainData[:,2] == 0, 0]
score2ForUnpassed = trainData[trainData[:,2] == 0, 1]
plt.plot(score1ForPassed,score2ForPassed,'r+')
plt.plot(score1ForUnpassed,score2ForUnpassed,'ko')
boundaryX = np.array([30, 50, 70, 90, 100])                     # 绘制决策边界线
boundaryY = -(theta[1] * boundaryX + theta[0]) / theta[2]
plt.plot(boundaryX,boundaryY, 'b-')
plt.show()

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伪_装

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值