机器学习实战:逻辑回归+梯度下降

使用梯度下降的方法进行逻辑回归实战:

问题说明:
这里将建立一个逻辑回归模型来预测一个学生是否被大学录取。
假设你是一个大学的管理员,你想根据两次考试的结果来决定每个申请人的录取机会,你有以前的申请人的历史数据。可以用历史数据作为逻辑回归的训练集。对于每一个样本,有两次考试的申请人的成绩和录取决定。建立一个分类模型,根据考试成绩估计入学概率。

数据链接:
链接:https://pan.baidu.com/s/1-pjwe1ogk30WpzN4Qg1NZA 密码:wqmt

完整代码实现如下:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

pdData = pd.read_csv('/Users/hxx/Downloads/LogiReg_data.txt', header=None, names=['Exam1', 'Exam2', 'Admitted'], engine='python')
#header: 指定第几行作为列名(忽略注解行),如果没有指定列名,默认header=0; 如果指定了列名header=None
#names 指定列名,如果文件中不包含header的行,应该显性表示header=None。这边列名为'Exam1', 'Exam2', 'Admitted'三列
print(pdData.head())#head()函数默认读取前五行
print(pdData.shape)#数据文件1003列
positive = pdData[pdData['Admitted']==1]
negative = pdData[pdData['Admitted']==0]
plt.figure(figsize=(10,5))#设置画布
plt.scatter(positive['Exam1'], positive['Exam2'], c='b', marker='o', label='Admitted')#绘制散点图positive的点,Exam1和Exam2组成一个点
plt.scatter(negative['Exam1'], negative['Exam2'], c='r', marker='x', label='Not Admitted')#绘制散点图negative的点
plt.legend()# 添加图例(也就是图中右上角positive和negative的解释)
plt.xlabel('Exam1 Score')#添加x轴标签
plt.ylabel('Exam2 Score')#添加y轴标签
plt.show()

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


nums = np.arange(-10,10,step=1)#随机取-1010之间数值步长为1
plt.figure(figsize=(12,4))#设置画布
plt.plot(nums,sigmoid(nums),c='r')#sigmoid函数画图展示,nums表示x,sigmoid(nums)表示的y,x和y确定一点组成sigmoid函数图像
plt.show()

#定义模型h(x)
def model(X,theta):
    return sigmoid(np.dot(X,theta.T))#将theta转置与x相乘,在代入sigmoid函数中得到模型。X100*3列,theta是一行三列

#取数据
pdData.insert(0,'ones',1) #给数据添加一列,列名为ones,在第0列添加,数值为1,也就相当于x_0的系数theta_0
orig_data = pdData .as_matrix() # 将数据转变成矩阵形式
cols = orig_data .shape[1]#shape[0]就是读取矩阵第一维度的长度,相当于行数;shape[1]就是读取矩阵第二维度的长度,相当于列数,shape[1]=4
X = orig_data[:,0:cols-1]#逗号前冒号表示所有值,0:cols-1表示取第0列到(clos-1)列不包括cols-1这一列
y = orig_data[:,cols-1:cols]
theta = np.zeros([1,3])#这边设置theta值均为0,一行三列和x的一行三列相对应
print(X)
print(y)
print(theta)

#定义损失函数
#这边就是逻辑回归中的损失函数,逻辑回归详情可以参考:https://blog.csdn.net/hxx123520/article/details/104313032第四部分详细说明
def cost(X,y,theta):#对应于J(theta)
    left = np.multiply(-y,np.log(model(X,theta)))#np.multiply对应位置相乘
    right = np.multiply(1-y,np.log(1-model(X,theta)))
    return np.sum(left-right)/(len(X))
print(cost(X,y,theta))

#根据梯度计算公式计算梯度
#也就是损失函数对theta求偏导,得到的就是梯度
def gradient(X,y,theta):
    grad = np.zeros(theta.shape)  # 定义初始化梯度,一行三列
    #print(grad.shape)
    error = (model(X, theta) - y).ravel()# 一行一百列。error=(预测y-真实值y)
    #print(error)
    # print(len(theta .ravel()))#3
    for j in range(len(theta.ravel())):
        term = np.multiply(error, X[:, j])#x[:,j] 取第j列的所有x样本
        grad[0, j] = np.sum(term) / len(X)#这边的grad就是一行三列。grad[0,0]表示第0行第0列,for循环不断更新j,最终就是[02]表示02列
        #print(grad)
    return grad
#numpy中的ravel()、flatten()、squeeze()都有将多维数组转换为一维数组的功能,区别:
#ravel():如果没有必要,不会产生源数据的副本。这边的error就是通过ravel函数将一百行一列拉成一行100列
#flatten():返回源数据的副本
#squeeze():只能对维数为1的维度降维

#三种梯度下降方法与三种停止策略

#三种停止策略
STOP_ITER=0 #根据指定的迭代次数来停止,更新一次参数相当于一次迭代
STOP_COST=1 #根据损失,每次迭代看一下迭代之前和之后的目标函数(损失值)的变化,没啥变化的话,可以停止
STOP_GRAD=2 #根据梯度,梯度变化贼小贼小

#设定三种不同的停止策略
def stopCriterion(type,value,threshold):
    if type == STOP_ITER: return value > threshold #threshold指的是指定的迭代次数
    elif type ==STOP_COST: return  abs(value[-1]-value[-2]) < threshold#threshold指的损失函数的阈值,如果小于这个值就停止
    elif type == STOP_GRAD: return np.linalg.norm(value) < threshold#threshold指的梯度值,梯度小于这个值停止。np.linalg.norm()表示范数,首先需要注意的是范数是对向量(或者矩阵)的度量,是一个标量(scalar)

#对数据进行洗牌,使模型的泛化能力更强
def shuffleData(data):
    np.random.shuffle(data)#shuffle() 方法将序列的所有元素随机排序。
    cols=data. shape[1]
    X = data[:,0:cols-1]
    y = data[:,cols-1:]
    return X,y

import time
def descent(data,theta,batchsize,stoptype,thresh,alpha):
    init_time = time.time() #比较不同梯度下降方法的运行速度
    i=0 #迭代次数
    k=0 #batchsize初始值
    X,y=shuffleData(data)#shuffleData函数对数据重新洗牌作用
    grad = np.zeros(theta.shape)#计算梯度
    costs= [cost(X,y,theta)]#计算损失

    while True:
        grad=gradient(X[k:k+batchsize],y[k:k+batchsize],theta)#grad的输出梯度结果为一行三列,
        k+=batchsize #取样本数据
        if k>= n :
            k=0
            X,y=shuffleData(data)
        theta=theta-alpha * grad#theta是一行三列,减去alpha*grad的一行三列,也就是更新后的theta
        costs.append(cost(X,y,theta))#将损失值添加到costs中。append() 方法向列表的尾部添加一个新的元素。
        i+=1;

        if stoptype==STOP_ITER: value=i
        if stoptype==STOP_COST:value=costs
        if stoptype==STOP_GRAD:value=grad

        if stopCriterion(stoptype,value,thresh):break

    return theta,i-1,costs,grad,time.time()-init_time

def runExpe(data,theta,batchsize,stoptype,thresh,alpha):
    theta, iter, costs, grad, dur = descent(data,theta,batchsize,stoptype,thresh,alpha)
    if (data[:,1]>2).sum()>1:
        name="Original"
    else:
        name="Scaled"

    name=name + " data - learning rate: {} -".format(alpha)

    if batchsize==n:
        strDescType = "Gradient" #batchsize等于n是全局随机梯度下降
    elif batchsize==1:
        strDescType = "Stochastic" #batchsize等于1是随机梯度下降
    else:
        strDescType = "Mini-batch({})".format(batchsize)#小批量梯度下降

    name = name + strDescType + " descent - stop :"

    if stoptype==STOP_ITER:
        strstop = "{} iterations".format(thresh)
    elif stoptype==STOP_COST:
        strstop = "cost change < {}".format(thresh)
    else:
        strstop = "gradient norm < {}".format(thresh)

    name = name + strstop
    print("*{}\nTheta: {} - Iter: {} - Last cost: {:03.2f} - Duration: {:03.2f}s ".format(name,theta,iter,costs[-1],dur))

    p=plt.subplots(figsize=(12,4))
    plt.plot(np.arange(len(costs)),costs,'r')
    plt.xlabel('Iterations')
    plt.ylabel('costs')
    plt.title(name.upper() + ' - Error vs. Iteration')# upper() 方法将字符串中的小写字母转为大写字母
    plt.show()
    return theta

n=100;#梯度计算时选取多少个样本 基于所有的样本
#对比不同的停止策略
runExpe(orig_data, theta, n, STOP_ITER, thresh=5000, alpha=0.000001) #迭代5000次,即停止策略基于迭代次数

# 不指定迭代次数,停止策略基于损失函数
runExpe(orig_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001)
# 停止策略基于梯度值
runExpe(orig_data, theta, n, STOP_GRAD, thresh=0.05, alpha=0.001)

# 对比不同的梯度下降方法
runExpe(orig_data, theta, 1, STOP_ITER, thresh=5000, alpha=0.001)  # 随机梯度下降,浮动大,稳定性差
runExpe(orig_data, theta, 1, STOP_ITER, thresh=15000, alpha=0.000002)

runExpe(orig_data, theta, 16, STOP_ITER, thresh=15000, alpha=0.001)  # 小批量梯度下降

#数据标准化问题,当数据发生浮动,先在数据层面上处理,先处理数据,再处理模型
from sklearn import preprocessing as pp

scaled_data = orig_data.copy()
scaled_data[:, 1:3] = pp.scale(orig_data[:, 1:3])#scale 零均值单位方差,将数据转化为标准正态分布

runExpe(scaled_data, theta, n, STOP_ITER, thresh=5000, alpha=0.001)  # 0.38
runExpe(scaled_data, theta, n, STOP_GRAD, thresh=0.02, alpha=0.001)  # 0.22
runExpe(scaled_data, theta, 1, STOP_GRAD, thresh=0.002 / 5, alpha=0.001)  # 0.22
theta = runExpe(scaled_data, theta, 16, STOP_GRAD, thresh=0.002 * 2, alpha=0.001)

#以下——得到精度
def predict(X,theta):
    return [1 if x >=0.5 else 0 for x in model(X,theta)]#设定阈值为0.5,大于0.5就可以入学

scaled_X = scaled_data[:,:3]
y = scaled_data[:,3]
prediction = predict(scaled_X,theta)
correct = [1 if ((a==1 and b==1) or (a==0 and b==0)) else 0 for (a,b) in zip(prediction,y)]#a对应的prediction值,b对应的真实值y
accuracy = (sum(map(int,correct)) % len(correct))#map() 会根据提供的函数对指定序列做映射。这边将correct映射为整数型在进行求和
print("accuracy {0}%".format(accuracy))

2.接下来详细的介绍一下不同的停止策略在上面代码中已经给出

2.1 批量梯度下降为例(batchsize=n=100)

停止条件为迭代次数5000

runExpe(orig_data, theta, n, STOP_ITER, thresh=5000, alpha=0.000001)

返回

*Original data - learning rate: 1e-06 -Gradient descent - stop :5000 iterations
Theta: [[-0.00027127  0.00705232  0.00376711]] - Iter: 5000 - Last cost: 0.63 - Duration: 0.95s 

在这里插入图片描述看似损失值已经稳定在最低点0.63

停止条件为损失值

设定阈值为0.000001,需要迭代110000次左右

runExpe(orig_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001)

返回

*Original data - learning rate: 0.001 -Gradient descent - stop :cost change < 1e-06
Theta: [[-5.13364014  0.04771429  0.04072397]] - Iter: 109901 - Last cost: 0.38 - Duration: 20.22s

在这里插入图片描述损失值最低为0.38,似乎还可以进一步收敛

停止条件为梯度大小

设定阈值0.05,需要迭代40000次左右

runExpe(orig_data, theta, n, STOP_GRAD, thresh=0.05, alpha=0.001)

返回

*Original data - learning rate: 0.001 -Gradient descent - stop :gradient norm < 0.05
Theta: [[-2.37033409  0.02721692  0.01899456]] - Iter: 40045 - Last cost: 0.49 - Duration: 7.67s 

在这里插入图片描述损失值最小为0.49,似乎还可以进一步收敛
综上,基于批量梯度下降方法,上述三种停止条件得到的损失函数值为0.63、0.38和0.49,迭代次数分别为5000次、110000次和40000次,迭代次数越多,损失值越小

3.对比不同的梯度下降方法

停止策略为迭代次数

3.1 随机梯度下降

runExpe(orig_data, theta, 1, STOP_ITER, thresh=5000, alpha=0.001)

返回

*Original data - learning rate: 0.001 -Stochastic descent - stop :5000 iterations
Theta: [[-0.38740891  0.09055956 -0.06431339]] - Iter: 5000 - Last cost: 0.91 - Duration: 0.31s 

在这里插入图片描述波动非常大,迭代过程不稳定,这也是随机梯度下降的主要缺点
尝试降低学习率为0.000002,增加迭代次数为15000

runExpe(orig_data, theta, 1, STOP_ITER, thresh=15000, alpha=0.000002)

返回

*Original data - learning rate: 2e-06 -Stochastic descent - stop :15000 iterations
Theta: [[-0.00202428  0.00981444  0.00076997]] - Iter: 15000 - Last cost: 0.63 - Duration: 0.93s 

在这里插入图片描述效果要好一些,损失值似乎稳定在0.63,根据上面的结果可知,0.63不算是一个特别合理的值

3.2 小批量梯度下降

#取样本为16
runExpe(orig_data, theta, 16, STOP_ITER, thresh=15000, alpha=0.001)

返回

*Original data - learning rate: 0.001 -Mini-batch(16) descent - stop :15000 iterations
Theta: [[-1.03955915  0.01512354  0.00209486]] - Iter: 15000 - Last cost: 0.60 - Duration: 1.18s 

在这里插入图片描述上下波动,迭代过程不稳定

对于一些数据,降低学习率之后没有效果,迭代过程依旧不稳定
因此,可能不是模型本身的问题,而是数据本身的问题,尝试着对数据做一些变换,此处对数据进行标准化,用标准化后的数据求解

4.数据标准化

数据标准化问题,当数据发生浮动,先在数据层面上处理,先处理数据,再处理模型

from sklearn import preprocessing as pp

scaled_data = orig_data.copy()
scaled_data[:, 1:3] = pp.scale(orig_data[:, 1:3])#scale 零均值单位方差,将数据转化为标准正态分布

接下来再来看看经过标准化处理后的数据得到的损失值图

runExpe(scaled_data, theta, n, STOP_ITER, thresh=5000, alpha=0.001)  # 0.38

返回

*Scaled data - learning rate: 0.001 -Gradient descent - stop :5000 iterations
Theta: [[0.3080807  0.86494967 0.77367651]] - Iter: 5000 - Last cost: 0.38 - Duration: 0.98s 

在这里插入图片描述

runExpe(scaled_data, theta, n, STOP_GRAD, thresh=0.02, alpha=0.001)  # 0.22

返回

*Scaled data - learning rate: 0.001 -Gradient descent - stop :gradient norm < 0.02
Theta: [[1.0707921  2.63030842 2.41079787]] - Iter: 59422 - Last cost: 0.22 - Duration: 12.18s 

在这里插入图片描述

runExpe(scaled_data, theta, 1, STOP_GRAD, thresh=0.002 / 5, alpha=0.001)  # 0.22

返回

*Scaled data - learning rate: 0.001 -Stochastic descent - stop :gradient norm < 0.0004
Theta: [[1.1486333  2.79230152 2.56637779]] - Iter: 72596 - Last cost: 0.22 - Duration: 5.63s 

在这里插入图片描述

runExpe(scaled_data, theta, 16, STOP_GRAD, thresh=0.002 * 2, alpha=0.001)

返回

*Scaled data - learning rate: 0.001 -Mini-batch(16) descent - stop :gradient norm < 0.004
Theta: [[1.09946538 2.6858268  2.46623512]] - Iter: 63755 - Last cost: 0.22 - Duration: 6.40s 

在这里插入图片描述可以发现,经过标准化处理的数据得到的损失值得到明显的改善。

发布了6 篇原创文章 · 获赞 22 · 访问量 4674

猜你喜欢

转载自blog.csdn.net/hxx123520/article/details/104688711