且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

逻辑回归实现

更新时间:2022-08-12 16:11:54

逻辑回归算法的Python实现

代码

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
# Get data as DataFrame Object
df = pd.read_csv('/Users/air/Macro/MegaChen/Study/ProgramOfStudy/MachineLearning/Data/梯度下降/data/LogiReg_data.txt', names=['Exam1', 'Exam2', 'Result'])
df.insert(0, 'Ones', 1)
matrix = df.as_matrix()

# 发现是监督学习的分类学习, 一般采用的是sigmoid函数构造最大似然函数从而得出损失函数, 为什么在这里不强调目标函数, 因为目标函数在目前
# 学到知识范围中, 就是一个t = theta0 * 1 + theta1 * x 1 + ... + thetan * xn == theta.T * x, 就是我们的theta矩阵的转置乘以
# 我们的训练集

# 此为原始的sigmoid函数, 后面的model函数仅仅是将这里的x换元成theta0 * 1 + theta1 * x 1 + ... + thetan * xn == theta.T * x
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# 对原始的sigmoid函数的换元封装, 用于为之后求似然函数作铺垫
def model(x, theta_set):
    return sigmoid(np.dot(x, theta_set.T))


# x_set是一个二维矩阵, len函数是求出它的行数, 也就是一维的元素的个数

# 与数学中的函数有一些不一样, 这里作为参数的theta_set虽然是自变量, 但是在计算机中是不允许的, 所以这里的thata_set也要是一个具体的值
# 而x_set和y_set是函数的参数, 他们也是某种意义上的“自变量”, 因为他们的值也在随着我们的数据改变

def cost(x_set, y_set, theta_set):
    left_side = np.multiply(-y_set, np.log(model(x_set, theta_set)))
    right_side = np.multiply(1 - y_set, np.log(1 - model(x_set, theta_set)))
    return sum(left_side - right_side) / len(x_set)



# 求出了cost损失函数, 接下来对该cost函数进行梯度下降求解出每一个theta的梯度, 并一一对应的保存到一个list中
def gradient(x_set, y_set, theta_set):
    grad = np.zeros(theta_set.shape)
    error = (model(x_set, theta_set) - y_set).ravel() # 将(100,1)的二维矩阵转为一维, 为了term = np.multiply(error, x_set[:, i])中能够计算
    # error = (model(X, theta)- y).ravel()
    for i in range(len(theta_set.ravel())):
        term = np.multiply(error, x_set[:, i])
        grad[0, i] = sum(term) / len(x_set)
    return grad

# 得出了梯度, 我们只需要假定一个起始的α学习率值, 进行一个梯度下降即可
def descent(data, rate, theta_set):
    np.random.shuffle(data)
    x_set = data[:, 0:3]
    print(x_set.shape)
    y_set = data[:, 3:]
    print(y_set.shape)
    # 计算原始的损失函数, 这个时候的theta_set应该都全为0
    costs = [cost(x_set, y_set, theta_set)]
    times = 0
    while True:
        times += 1
        grad = gradient(x_set, y_set, theta_set)
        # 更新了我们的参数, 这里就是计算机的学习过程:-)
        theta_set = theta_set - rate * grad
        print(theta_set)
        print(theta_set)
        print(theta_set)
        costs.append(cost(x_set, y_set, theta_set))
        # 什么时候结束
        if times > 5000:
            return theta_set, costs

        
theta_set, costs = descent(matrix, 0.0001, np.zeros((1,3)))
plt.figure(figsize=(12,4))
plt.plot(range(len(costs)), costs, c='r')
plt.show()
print(theta_set)

小结

  • 一般程序的开头就是调用梯度下降函数, 在该函数中调用我们之前定义好的sigmoid, model等函数, 该函数的返回结果就是我们需要的theta参数
  • 对于array([1,2,3])这样矩阵, 它默认是一个列向量, 但是我们希望将他当做一个行向量来使用, 那就使用切片, 保留:, 如a[3:], 如果总共4列的话, 这样就得到了最后一列的行向量, 因为(3,)其实是一个list, 而(3,1)才是矩阵的, 矩阵最起码是二维的
  • 虽然我们的数学公式中的自变量都是一个数, 但是在Python的应用中, 我们都是传入矩阵的, 也就是将我们的数据一并以矩阵的形式传入到函数中, 这样全部的数据一下子就计算出来了
  • 列向量一定是二维的, 而行向量应该是一维的
  • ravel()函数会降维
  • 在强调一下, numpy中的zeros, ones返回的都是二维的矩阵, 不可能出现(3,), 除非a = np.array([1,2,3])

大致步骤

  1. 判断模型, 以逻辑回归为例
  2. 使用sigmoid函数
  3. h(x) = ...
  4. 用model函数表示h(x), 对sigmoid函数进行封装
  5. 定义cost函数, 需用推出数学公式, 在该函数中调用model函数
  6. 使定义gradient函数, 返回每一个参数对应的gradient的值组成的list, 注意: 该gradient函数不会调用cost函数, 因为一个是梯度(导数), 另一个是cost的函数值
  7. 定义主要的梯度下降函数descent, 返回的就是我们需要的theta_set, 在该函数中调用cost和gradient函数, 其中cost函数的值放在一个costs数据中, 用于画出函数图形和比较两次cost函数值的差, 如果小于我们规定的一个值, 就return退出函数, 而gradient就是为了一个这样的函数, theta = theta - alpha * gradient, 得出我们下一个theta值