机器学习:Python实现纯代码逻辑回归算法

之前写了一篇逻辑回归的理论知识,写得还算详尽,包含了公式的详细推导过程。这篇文章将结合之前的理论推导,通过Python代码实现逻辑回归算法,并用来预测鸢尾花种类。由于这篇文章是对照着之前的理论文章进行讲解的,所以最好先看前一篇理论文章,再看这篇实践文章。理论的文章链接为:https://blog.csdn.net/opp003/article/details/8478449
上一篇的文章目标是得到权重矩阵的计算公式,也就是上篇文章中的最后一个公式,式(33)。有了权重矩阵的计算公式,就可以计算出权重矩阵,这样,结合假设函数,也就是sigmoid函数,就可以进行预测了。下面我们来看代码。

1.sigmoid函数


sigmoid函数就是我们之前提到的假设函数h(x),也就是前一篇文章中的式(26)。

\large \dpi{150} h_{\theta }(x) = \frac{1}{1+e^{-z}}

它的实现代码如下:

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

2.梯度下降法

梯度下降法的理论知识已在上一篇文章中详细讲解了,这里不再赘述。我们来看核心部分,即权重矩阵的代码如何实现。权重矩阵的计算公式为上篇文章中的式(33)。

\large \theta_{j+1} = \theta _{j} - \frac{\eta }{m} \sum_{i=1}^{m} (h_{\theta }(x^{i}) - y^{i})x^{i}_{j}

这个核心计算公式的代码实现如下:

weights = weights - alpha * X.transpose() * (h - y) 


其中weights就是我们要计算的权重矩阵,也就是数学公式里的 \large \theta 。alpha为学习率,也就是公式里的 \large \eta 。而代码中的 \large h - y,也就是公式中的误差计算部分。

梯度下降法的详细代码如下:

扫描二维码关注公众号,回复: 4596912 查看本文章
def gradDescent(X,y):
    m,n = X.shape
    #将数据矩阵化
    X = np.mat(X)
    y = np.mat(y)
    y = y.transpose()
    #学习率设为0.01
    alpha = 0.01
    #进行500次训练,迭代更新权重矩阵
    n_estimate = 500
    #初始化一个值都为1的权重矩阵
    weights = np.ones((n,1))
    for k in range(n_estimate):
        h = sigmoid(X * weights)
        error = h - y
        weights = weights - alpha * X.transpose() * error
    return weights


3.完整代码

# -*- coding: utf-8 -*-
# @Time    : 2018/12/10 下午6:03
# @Author  : yangchen
# @FileName: HandwritingLR.py
# @Software: PyCharm
# @Blog    :https://blog.csdn.net/opp003/article

import pandas as pd
import numpy as np

'''
假设函数
h(z) = 1 / (1 + e(-z))
z = theta * x
'''
def sigmoid(z):
    return 1 / (1 + np.exp(-z))


'''
梯度下降
X:训练集
y:训练集标签
alpha:梯度下降学习率
n_estimate:训练次数
'''
def gradDescent(X,y):
    m,n = X.shape
    #将数据矩阵化
    X = np.mat(X)
    y = np.mat(y)
    y = y.transpose()
    #学习率设为0.01
    alpha = 0.01
    #进行500次训练,迭代更新权重矩阵
    n_estimate = 500
    #初始化一个值都为1的权重矩阵
    weights = np.ones((n,1))
    for k in range(n_estimate):
        h = sigmoid(X * weights)
        error = h - y
        weights = weights - alpha * X.transpose() * error
    return weights


'''
加载数据
'''
def loadData():
    df = pd.read_csv('iris.csv')
    #数据乱序
    df = df.sample(frac=1)
    #按照8:2的比例分隔数据,分别用来训练和测试
    train_data = df[0:round(len(df)*0.8)]
    test_data = df[round(len(df)*0.8):]
    y_train = train_data['LABEL']
    X_train = train_data.drop('LABEL',axis=1)
    y_test = test_data['LABEL']
    X_test = test_data.drop('LABEL', axis=1)
    #矩阵化测试集
    X_test = np.mat(X_test)
    #数组化测试集的label
    y_test = y_test.tolist()

    return X_train,y_train,X_test,y_test


if __name__ == '__main__':
    X_train, y_train, X_test, y_test = loadData()
    weights = gradDescent(X_train, y_train)
    #别忘了要乘上sigmoid函数
    X_predict = sigmoid(X_test * weights)
    #将数据从二维矩阵转为数组
    X_predict = X_predict.tolist()
    for i in range(len(X_predict)):
        x = X_predict[i]
        y = y_test[i]
        prob_of_one = x[0]
        if prob_of_one >= 0.5:
            print('predict: 1 , ' + 'actual: ' + str(y))
        else:
            print('predict: 0 , ' + 'actual: ' + str(y))

最后的main函数里有几行代码要解释下:

X_predict = sigmoid(X_test * weights)

这里,X_test * weights 计算的是线性部分,即理论篇中的z = \theta ^{T}x,而假设函数是

\large \dpi{150} h_{\theta }(x) = \frac{1}{1+e^{-z}}    ,

所以计算完 X_test * weights 后,要将它放入 sigmoid 函数,从而得到一个结果,这个结果是预测为正样本,即 label 为 1 的概率,别忘了,逻辑回归最原始的结果是概率。我们通过0.5来划分它是1还是0。 

还要说明的一点是,我们是通过二维矩阵来计算的。所以得到的结果虽然是一个样本一个对应值,但结果值也是存放在二维矩阵中。所以需要通过 numpy 的 tolist 方法先转为二维数组,再通过取 [0] 来得到值。

最后的运行结果如下:

predict: 1 , actual: 1
predict: 0 , actual: 0
predict: 0 , actual: 0
predict: 0 , actual: 0
predict: 0 , actual: 0
predict: 1 , actual: 1
predict: 1 , actual: 1
predict: 0 , actual: 0
predict: 0 , actual: 0
predict: 0 , actual: 0
predict: 1 , actual: 1
predict: 1 , actual: 1
predict: 0 , actual: 0
predict: 1 , actual: 1
predict: 1 , actual: 0
predict: 1 , actual: 1
predict: 1 , actual: 1
predict: 1 , actual: 1
predict: 1 , actual: 1
predict: 0 , actual: 0

我们看预测结果和真实结果的对比,20条测试集里,只有一条预测错了。准确率达到了95%。

文中用到的数据集是鸢尾花的部分数据集,1和0分别代表两种不同的花种。数据集如下:

col_1,col_2,col_3,col_4,LABEL
7,3.2,4.7,1.4,1
6.4,3.2,4.5,1.5,1
6.9,3.1,4.9,1.5,1
5.5,2.3,4,1.3,1
6.5,2.8,4.6,1.5,1
5.7,2.8,4.5,1.3,1
6.3,3.3,4.7,1.6,1
4.9,2.4,3.3,1,1
6.6,2.9,4.6,1.3,1
5.2,2.7,3.9,1.4,1
5,2,3.5,1,1
5.9,3,4.2,1.5,1
6,2.2,4,1,1
6.1,2.9,4.7,1.4,1
5.6,2.9,3.6,1.3,1
6.7,3.1,4.4,1.4,1
5.6,3,4.5,1.5,1
5.8,2.7,4.1,1,1
6.2,2.2,4.5,1.5,1
5.6,2.5,3.9,1.1,1
5.9,3.2,4.8,1.8,1
6.1,2.8,4,1.3,1
6.3,2.5,4.9,1.5,1
6.1,2.8,4.7,1.2,1
6.4,2.9,4.3,1.3,1
6.6,3,4.4,1.4,1
6.8,2.8,4.8,1.4,1
6.7,3,5,1.7,1
6,2.9,4.5,1.5,1
5.7,2.6,3.5,1,1
5.5,2.4,3.8,1.1,1
5.5,2.4,3.7,1,1
5.8,2.7,3.9,1.2,1
6,2.7,5.1,1.6,1
5.4,3,4.5,1.5,1
6,3.4,4.5,1.6,1
6.7,3.1,4.7,1.5,1
6.3,2.3,4.4,1.3,1
5.6,3,4.1,1.3,1
5.5,2.5,4,1.3,1
5.5,2.6,4.4,1.2,1
6.1,3,4.6,1.4,1
5.8,2.6,4,1.2,1
5,2.3,3.3,1,1
5.6,2.7,4.2,1.3,1
5.7,3,4.2,1.2,1
5.7,2.9,4.2,1.3,1
6.2,2.9,4.3,1.3,1
5.1,2.5,3,1.1,1
5.7,2.8,4.1,1.3,1
6.3,3.3,6,2.5,0
5.8,2.7,5.1,1.9,0
7.1,3,5.9,2.1,0
6.3,2.9,5.6,1.8,0
6.5,3,5.8,2.2,0
7.6,3,6.6,2.1,0
4.9,2.5,4.5,1.7,0
7.3,2.9,6.3,1.8,0
6.7,2.5,5.8,1.8,0
7.2,3.6,6.1,2.5,0
6.5,3.2,5.1,2,0
6.4,2.7,5.3,1.9,0
6.8,3,5.5,2.1,0
5.7,2.5,5,2,0
5.8,2.8,5.1,2.4,0
6.4,3.2,5.3,2.3,0
6.5,3,5.5,1.8,0
7.7,3.8,6.7,2.2,0
7.7,2.6,6.9,2.3,0
6,2.2,5,1.5,0
6.9,3.2,5.7,2.3,0
5.6,2.8,4.9,2,0
7.7,2.8,6.7,2,0
6.3,2.7,4.9,1.8,0
6.7,3.3,5.7,2.1,0
7.2,3.2,6,1.8,0
6.2,2.8,4.8,1.8,0
6.1,3,4.9,1.8,0
6.4,2.8,5.6,2.1,0
7.2,3,5.8,1.6,0
7.4,2.8,6.1,1.9,0
7.9,3.8,6.4,2,0
6.4,2.8,5.6,2.2,0
6.3,2.8,5.1,1.5,0
6.1,2.6,5.6,1.4,0
7.7,3,6.1,2.3,0
6.3,3.4,5.6,2.4,0
6.4,3.1,5.5,1.8,0
6,3,4.8,1.8,0
6.9,3.1,5.4,2.1,0
6.7,3.1,5.6,2.4,0
6.9,3.1,5.1,2.3,0
5.8,2.7,5.1,1.9,0
6.8,3.2,5.9,2.3,0
6.7,3.3,5.7,2.5,0
6.7,3,5.2,2.3,0
6.3,2.5,5,1.9,0
6.5,3,5.2,2,0
6.2,3.4,5.4,2.3,0
5.9,3,5.1,1.8,0

猜你喜欢

转载自blog.csdn.net/opp003/article/details/84944253