首页 > 编程语言 > 详细

Python 实现 - 梯度下降求解逻辑回归

时间:2019-08-29 21:12:56      阅读:270      评论:0      收藏:0      [点我收藏+]

Python 实现 - 梯度下降求解逻辑回归

相关模块 - 三大件

#三大件
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

数据集 - 大学录取

我们将建立一个逻辑回归模型来预测一个学生是否被大学录取。

假设你是一个大学系的管理员,你想根据两次考试的结果来决定每个申请人的录取机会。

你有以前的申请人的历史数据,你可以用它作为逻辑回归的训练集。

对于每一个培训例子,你有两个考试的申请人的分数和录取决定。

为了做到这一点,我们将建立一个分类模型,根据考试成绩估计入学概率。

import os
path = data + os.sep + LogiReg_data.txt
pdData = pd.read_csv(path, header=None, names=[Exam 1, Exam 2, Admitted])
pdData.head()
pdData.shape   #(100, 3) 数据维度 100 行, 3列

技术分享图片

图形展示 - 散点图

positive = pdData[pdData[Admitted] == 1] # returns the subset of rows such Admitted = 1, i.e. the set of *positive* examples
negative = pdData[pdData[Admitted] == 0] # returns the subset of rows such Admitted = 0, i.e. the set of *negative* examples

fig, ax = plt.subplots(figsize=(10,5))
ax.scatter(positive[Exam 1], positive[Exam 2], s=30, c=b, marker=o, label=Admitted)
ax.scatter(negative[Exam 1], negative[Exam 2], s=30, c=r, marker=x, label=Not Admitted)
ax.legend() # 展示图形标识
ax.set_xlabel(Exam 1 Score) # x 标识
ax.set_ylabel(Exam 2 Score) # y 标识

 技术分享图片

逻辑回归 - 实现算法

? 目标

 ? 建立分类器 

 ? 求解三个参数 技术分享图片 - 分别表示 成绩一, 成绩二以及偏置项的参数 

? 阈值

  ? 设定阈值来标识分类的结果, 此处设置为 0.5 

  ? > 0.5  标识通过, < 0.5 标识未通过

? 所需模块

  ? sigmoid : 映射到概率的函数

  ? model : 返回预测结果值

  ? cost : 根据参数计算损失

  ? gradient : 计算每个参数的梯度方向

  ? descent : 进行参数更新

  ? accuracy: 计算精度

sigmoid 函数实现

  ?  公式    技术分享图片

  ?  Python 实现     def sigmoid(z): return 1 / (1 + np.exp(-z)) 

  ? 画图展示 

nums = np.arange(-10, 10, step=1) #creates a vector containing 20 equally spaced values from -10 to 10
fig, ax = plt.subplots(figsize=(12,4))
ax.plot(nums, sigmoid(nums), r)

 技术分享图片

  ? 值域 

    技术分享图片

?  model 函数 - 预测函数实现 

  ? 数学公式     技术分享图片

  ? Python 实现 

def model(X, theta):
    
    return sigmoid(np.dot(X, theta.T))

?  构造数据集

技术分享图片

 ? 加一行 -  数据集要进行偏置项的处理 ( 加一列全1的数据 )

pdData.insert(0, Ones, 1) # 偏置项参数的处理


# set X (training data/训练数据) and y (target variable/变量参数)
orig_data = pdData.as_matrix() # 转换为 数组形式
cols = orig_data.shape[1]
X = orig_data[:,0:cols-1]
y = orig_data[:,cols-1:cols]

# convert to numpy arrays and initalize the parameter array theta
#X = np.matrix(X.values)
#y = np.matrix(data.iloc[:,3:4].values) #np.array(y.values)
theta = np.zeros([1, 3])  # 参数占位, 先用 0 填充

技术分享图片

技术分享图片技术分享图片

 技术分享图片技术分享图片

?  损失函数

  ? 数学公式 

  技术分享图片

  将对数似然函数去负号 -   技术分享图片

  求平均损失 -   技术分享图片

  这块如果忘了怎么回事可以在这里进行查阅 点击这里跳转公式篇

   ?  Python 实现 

def cost(X, y, theta):
    left = np.multiply(-y, np.log(model(X, theta)))
    right = np.multiply(1 - y, np.log(1 - model(X, theta))) 
    return np.sum(left - right) / (len(X))

  X, y  表示两个特征值, 传入  theta  表示参数 技术分享图片 

 np.multiply  乘法计算, 将式子分为左右查分

   ?  计算 cost 值

技术分享图片

?  计算梯度

   ?  数学公式 - 进行求导计算后, 将最终结果转换为 python 实现

技术分享图片

    技术分享图片

    ?  Python 实现

def gradient(X, y, theta):
    grad = np.zeros(theta.shape) # 因为会有三个结果, 因此这里进行预先的占位
    error = (model(X, theta)- y).ravel() # h0(xi)-yi 象形... 这里吧前面的m分之一的那个负号移到了这里来去除负号
    for j in range(len(theta.ravel())): # 求三次
        term = np.multiply(error, X[:,j]) # : 表示所有的行, j 表示第 j 列
        grad[0, j] = np.sum(term) / len(X)
    
    return grad

     这里有三个 技术分享图片 , 那么应该也是计算出来三个值, 因此这里进行了三次的循环

  每次的循环针对要处理的那一列进行操作

?  停止策略

STOP_ITER = 0 # 根据迭代次数, 达到迭代次数及停止
STOP_COST = 1 # 根据损失, 损失差异较小及停止
STOP_GRAD = 2 # 根据梯度, 梯度变化较小则停止

def stopCriterion(type, value, threshold):
    #设定三种不同的停止策略
    if type == STOP_ITER:        return value > threshold
    elif type == STOP_COST:      return abs(value[-1]-value[-2]) < threshold
    elif type == STOP_GRAD:      return np.linalg.norm(value) < threshold

 

?  洗牌

import numpy.random
#洗牌
def shuffleData(data):
    np.random.shuffle(data)
    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 # batch
    X, y = shuffleData(data)
    grad = np.zeros(theta.shape) # 计算的梯度
    costs = [cost(X, y, theta)] # 损失值

    
    while True:
        grad = gradient(X[k:k+batchSize], y[k:k+batchSize], theta) 
        k += batchSize #取batch数量个数据
        if k >= n: 
            k = 0 
            X, y = shuffleData(data) #重新洗牌
        theta = theta - alpha*grad # 参数更新
        costs.append(cost(X, y, theta)) # 计算新的损失
        i += 1 

        if stopType == STOP_ITER:       value = i
        elif stopType == STOP_COST:     value = costs
        elif stopType == STOP_GRAD:     value = grad
        if stopCriterion(stopType, value, thresh): break
    
    return theta, i-1, costs, grad, time.time() - init_time

    ?  参数详解

  •  data  -  数据
  •  theta  - 参数
  •  batchSize  这里的指定及选择下降方式
    • 如果指定为 1 则表示随机, 指定为总样本数则表示批量, 指定 1-总数 之间则表示小批量
  • stopType  - 停止策略
    • 迭代次数 - 达到指定次数停止
    • 损失 - 损失较小停止
    • 梯度 - 梯度较小停止
  • thresh  -  策略对应的阈值,
    • 比如选择 迭代次数停止策略, 则此值表示迭代次数的上限, 
  • alpha  - 学习率 ( 就那个 0.01 ~ 0.0....1 的那个)

?  辅助工具函数

def runExpe(data, theta, batchSize, stopType, thresh, alpha):
    #import pdb; pdb.set_trace();
    theta, iter, costs, grad, dur = descent(data, theta, batchSize, stopType, thresh, alpha)  # 这是最核心的代码

name
= "Original" if (data[:,1]>2).sum() > 1 else "Scaled" name += " data - learning rate: {} - ".format(alpha) # 根据传入参数选择下降方式 if batchSize==n: strDescType = "Gradient" # 批量 elif batchSize==1: strDescType = "Stochastic" # 随机 else: strDescType = "Mini-batch ({})".format(batchSize) # 小批量 name += strDescType + " descent - Stop: " # 根据 停止方式进行选择处理生成 name if stopType == STOP_ITER: strStop = "{} iterations".format(thresh) elif stopType == STOP_COST: strStop = "costs change < {}".format(thresh) else: strStop = "gradient norm < {}".format(thresh) name += strStop print ("***{}\nTheta: {} - Iter: {} - Last cost: {:03.2f} - Duration: {:03.2f}s".format( name, theta, iter, costs[-1], dur)) # 画图 fig, ax = plt.subplots(figsize=(12,4)) ax.plot(np.arange(len(costs)), costs, r) ax.set_xlabel(Iterations) ax.set_ylabel(Cost) ax.set_title(name.upper() + - Error vs. Iteration) return theta

此函数进行画图的一些名字的显示以及图形进行的简单的功能封装函数

可以理解为一个工具函数, 自动根据参数选择画图的某些调节比如标题等

逻辑回归 - 算法计算

?  批量下降 - 迭代次数停止策略

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

    ?  参数详解

  • 这里 设置 100 为样本的总数据量, 因此这是个批量下降方式的计算
  • 停止策略是基于迭代次数进行判断
  •  thresh  5000 表示迭代次数
  • 学习率的设置为 0.00001 非常的小,  

    ?  测试结果

  技术分享图片

    ?  结果详解

  1.34s 完成了全部的 5000次 迭代, 最终的结果的为 0.63 左右

?  批量下降 - 损失值停止策略

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

    ?  参数详解

  • 这里 依旧设置 100 为样本的总数据量, 依旧为批量下降方式的计算
  • 停止策略是基于损失值进行判断
  •  thresh  设置为如果损失小于 0.000001 的时候则停止
  • 学习率的设置为 0.00001 非常的小

    ?  测试结果

 技术分享图片

    ?  结果详解

  可见测试用了 32.02s才完成, 达到损失值这么小的时候用了 100000次以上的迭代次数, 但是结果也达到了 0.4 以下

  比上一轮的结果要优秀很多

?  批量下降 - 梯度值停止策略

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

    ?  参数详解

  • 这里 依旧设置 100 为样本的总数据量, 依旧为批量下降方式的计算
  • 停止策略是基于梯度值变化进行判断
  •  thresh  设置 0.05 表示梯度变化 
  • 学习率的设置为 0.00001 非常的小

    ?  测试结果

 技术分享图片

    ?  结果详解

  可见测试用了12.18s 才完成, 达到损失值这么小的时候用了40000 次以上的迭代次数, 但是结果也达到了 0.5 以下

  由迭代次数也可以看出比不过 12w 次的结果, 因此最终的收敛在 0.50 左右无法比过 0.4 的上轮结果

 

Python 实现 - 梯度下降求解逻辑回归

原文:https://www.cnblogs.com/shijieli/p/11431835.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!