逻辑回归 — 手写代码(附详细分析)

逻辑回归(Logistic regression)


三种梯度下降策略:
  • 批量梯度下降:容易得到最优解,但是由于每次考虑所有样本,速度很慢
  • 随机梯度下降:每次找一个样本,迭代速度快,但不一定每次都朝着收敛的方向
  • 小批量梯度下降:每次更新选择一小部分数据来算,实用

对于 逻辑回归 不了解的,可以看看我写的这篇文章,或许可以帮助到你。URL: 逻辑回归算法-推导学习详细


案例:
  • 根据学生的两门课成绩,决定学生是否被录取
  • 部分测试数据
Exam 1Exam 2Admitted
34.6278.240
30.2843.890
35.8472.900
60.1886.301
79.0375.341

代码:
import os
import time
import numpy as np
import pandas as pd

STOP_ITER = 0
STOP_COST = 1
STOP_GRAD = 2


def stopCriterion(type, value, threshold):  # 迭代次数  thresh=5000
    """
    设定三种不同的停止策略
    :param type: 停止策略类型
    :param value:
    :param threshold:  阈值
    :return:bool值
    """

    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


def get_data(Input_path):
    """
    读取数据
    :param Input_path: 数据所在的目录
    :return: pd_data 把数据转成pandas返回,列名 ['Exam 1', 'Exam 2', 'Admitted']
    """
    Input_path += os.sep  # os.sep  打印'\\'  os.sep根据你所处的平台,自动采用相应的分隔符号
    file_name_list = os.listdir(Input_path)  # 读取目录下的所有文件
    print(file_name_list)
    for file_name in file_name_list:
        path = Input_path + file_name
        break
    print(path)
    pd_data = pd.read_csv(open(path), header=None, names=['Exam 1', 'Exam 2', 'Admitted'])  # 由于数据是没有列名的,所以自己指定
    # positive = pd_data[pd_data['Admitted'] == 1]
    # negative = pd_data[pd_data['Admitted'] == 0]
    pd_data.insert(loc=0, column='Ones', value=1)  # 新插入一列配合 θ0
    orig_data = pd_data.as_matrix()
    # orig_data = pd_data.values()
    # print("orig_data.shape: {}".format(orig_data.shape))  # (5, 4)
    # print("orig_data.shape[1]: {}".format(orig_data.shape[1]))  # 4

    return orig_data


def shuffleData(data):
    """
    洗牌
    :param data:原始训练数据
    :return: X, y   原始训练数据(train set x)
    """

    np.random.shuffle(data)
    cols = data.shape[1]
    X = data[:, 0:cols - 1]
    y = data[:, cols - 1:]
    return X, y


def sigmoid(z):
    """
    sigmoid 函数
    :param z: 输入值
    :return: 1 / (1 + np.exp(-z)) 计算后的结果  原始训练数据(train set y)
    """
    sigmoid_reslut = 1 / (1 + np.exp(-z))
    print("***************************** sigmoid(z) **********************************")
    # print("1 / (1 + np.exp(-z))计算结果为:{}".format(sigmoid_reslut))
    return sigmoid_reslut


def model(X, theta):
    """
    h(θ) = θT * x 的计算模块
    :param X: 原始训练数据(train set x)
    :param theta: θ值
    :return:  sigmoid(np.dot(X, theta.T))    即 h(θ) 的计算结果,把计算结果映射到sigmoid函数里
    """
    print("***************************** model(X, theta) h(θ) **********************************")
    # print("X   为   :{}".format(X))
    # print("np.dot(X, theta.T)计算结果为:{}".format(np.dot(X, theta.T)))
    model_reslut = sigmoid(np.dot(X, theta.T))

    return model_reslut


def cost(X, y, theta):
    """
    损失函数
    D(hθ(x),y)=−ylog(hθ(x))−(1−y)log(1−hθ(x))
    J(θ)=1/n(D(∑hθ(xi),yi))  i=1,2,3....n
    :param X: 原始训练数据(train set x)
    :param y: 原始训练数据(train set y)
    :param theta: θ值
    :return: J(θ)
    """
    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))


def gradient(X, y, theta, i):
    """
    计算梯度
    对J(θ)求偏导
    :param X: 原始训练数据(train set x)
    :param y: 原始训练数据(train set y)
    :param theta: θ值
    :return:  grad 梯度值
    """
    grad = np.zeros(theta.shape)
    print("第{}次初始grad:{}".format(i, grad))
    error = (model(X, theta) - y).ravel()
    for j in range(len(theta.ravel())):  # for each parmeter   theta.ravel() --》array([0., 0., 0.])
        term = np.multiply(error, X[:, j])  # 因为原始数据我们加了一列1,而且是三个theta , 所以  X[:, j]  是取 X 的三列不同的数据
        grad[0, j] = np.sum(term) / len(X)
    print("第{}次更新grad:{}".format(i, grad))
    return grad


def descent(data, theta, batchSize, stopType, thresh, alpha):
    """
     梯度下降求解
    :param data: 原始训练数据
    :param theta: θ值    array([[0., 0., 0.]])
    :param batchSize: batch数量个数据   batch = n = 100
    :param stopType: 停止策略类型  STOP_ITER = 0  STOP_COST = 1   STOP_GRAD = 2
    :param thresh: 迭代次数      5000
    :param alpha: 学习率    0.000001
    :return: theta, i-1, costs, grad, time.time() - init_time
    """

    init_time = time.time()  # 返回当前时间的时间戳
    i = 0  # 迭代次数
    k = 0  #
    X, y = shuffleData(data)  # 对数据进行 “洗牌”
    # grad = np.zeros(theta.shape)  # 计算的梯度
    costs = [cost(X, y, theta)]  # 损失值  costs:[0.6931471805599453]
    print("初始计算时的 costs :{}".format(costs))
    print("初始计算时的 theta :{}".format(theta))
    while True:
        print("***************************************************************".format(i))
        print("************************第 {} 次循环计算************************".format(i))
        grad = gradient(X[k:k + batchSize], y[k:k + batchSize], theta, i)
        k += batchSize  # 取batch数量个数据
        if k >= n:
            k = 0
            X, y = shuffleData(data)  # 数据重新“洗牌”
        theta = theta - alpha * grad  # θ参数更新
        costs.append(cost(X, y, theta))  # 计算新的损失
        print("第 {} 次计算的 costs :{}".format(i, cost(X, y, theta)))
        print("第 {} 次计算的 theta :{}".format(i, theta))
        i += 1

        if stopType == STOP_ITER:  # STOP_ITER = 0
            value = i
        elif stopType == STOP_COST:  # STOP_COST = 1
            value = costs
        elif stopType == STOP_GRAD:  # STOP_GRAD = 2
            value = grad

        # stopCriterion() 返回的是bool值,满足相应的条件就循环停止计算
        if stopCriterion(stopType, value, thresh):  # 迭代次数  thresh=5000
            break

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


def run(data, theta, batchSize, stopType, thresh, alpha):
    """
    控制程序
    :param data: 原始训练数据
    :param theta: θ值    array([[0., 0., 0.]])
    :param batchSize: batch数量个数据 batch = n = 100
    :param stopType: 停止策略类型
    :param thresh: 迭代次数  thresh=5000
    :param alpha: 学习率
    :return: 学习好的θ值    array([[0., 0., 0.]])
    """

    theta, iter, costs, grad, dur = descent(data, theta, batchSize, stopType, thresh, alpha)
    # theta : θ值    array([[0., 0., 0.]])
    # iter : 迭代次数
    # costs : 每一次的损失值 是一个列表   可视化用
    # grad :梯度值 也就是对 J(θ) 求偏导的值
    # dur : 消耗的时间

    return theta


if __name__ == '__main__':
    Input_path = r"D:\Py_Machine_Learn\data\py_charm"
    orig_data = get_data(Input_path)

    n = 100  # batch数量个数据
    theta = np.zeros([1, 3])

    # 设定迭代次数停止策略
    run(orig_data, theta, n, STOP_ITER, thresh=5, alpha=0.000001)  # 基于所有的样本  这里thresh表示迭代次数

    # 根据损失值停止
    run(orig_data, theta, n, STOP_COST, thresh=0.000001, alpha=0.001)  # 这里thresh表示损失值

    # 根据梯度变化停止
    run(orig_data, theta, n, STOP_GRAD, thresh=0.05, alpha=0.001)  # 这里thresh表示梯度阈值

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值