感知机与逻辑斯蒂回归的Python实现

感知机与逻辑斯蒂回归的Python实现

实验目的

深入理解感知机与logistic回归的统计数学原理,掌握其算法过程,提高代码能力

实验准备

使用到的第三方库有:

numpy——用于记录样本数据的张量,方便运算

csv——用于导入csv格式的数据

matplotlib.pyplot、Ipython.display——用于为二维特征数据画图

下图为数据样式
在这里插入图片描述

实验结果

感知机

二维特征数据

样本的两个特征值和真实类别由正态分布函数随机生成

  • 学习率0.0001,训练轮数2000。以下为每500轮训练后的权重变化与分类面的位置变化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

最终分类结果如下,预测准确率为83.333%(误分类了一个样本)

在这里插入图片描述

  • 如下二图,由于生成的数据具有随机性,在固定的学习率和训练轮数下,预测准确率往往不固定,可能是100%、50%、66.67%等等,依赖于样本

在这里插入图片描述
在这里插入图片描述

  • 实际上,学习率和训练轮数的设置也会影响训练准确率和预测准确率,但这里只有6个样本,这个问题不是很突出,因此放在cancer数据集中讨论
cancer数据集

由于是给定的数据集,消除了随机性,这里按照试验要求将整个数据集取前4/5作为训练集,后1/5作为预测集。数据共569个样本,30个特征维度,二分类

  • 学习率0.0001,训练轮数2000。以下为每500轮训练后的第一个权重和偏置的变化
the epoch: 1
w[0], b of this epoch: [-4.39230000e-03][-0.0083]
the epoch: 501
w[0], b of this epoch: [-1.12009642e+01][-4.1583]
the epoch: 1001
w[0], b of this epoch: [-1.40914858e+01][-8.3083]
the epoch: 1501
w[0], b of this epoch: [-1.37060803e+01][-12.4583]

最终的预测准确率为92.04%

  • 学习率0.0001,训练轮数1000
The accuary of prediction: 82.30088495575221%
  • 学习率0.0001,训练轮数1000
The accuary of prediction: 86.72566371681415%
  • 学习率0.01,训练轮数2000
The accuary of prediction: 92.03539823008849%

由上可见,在这个感知机的实现中,训练轮数对固定样本的分类训练影响最大,学习率开始可能影响较大,但对最终结果影响不大

逻辑斯蒂回归

二维特征数据

这里的样本生成方法与感知机一样,不再赘述,详见代码分析一节

  • 学习率0.0001,训练轮数2000。以下为每500轮训练后的权重变化与分类面的位置变化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最终分类结果如下,预测准确率为50%(误分类了三个样本)

在这里插入图片描述

  • 如下二图,由于生成的数据具有随机性,在固定的学习率和训练轮数下,预测准确率往往不固定,可能是100%、50%、66.67%等等,依赖于样本

在这里插入图片描述
在这里插入图片描述

  • 学习率0.0001,训练轮数2000

在这里插入图片描述

  • 学习率0.00001,训练轮数2000

在这里插入图片描述
经测试,在固定2000轮训练下,学习率为0.0001时对数据的分类拟合较好,可以达到100%,而学习率更大或更小时,由于数据的随机性,准确率常在66.67%或83.33%,甚至50%,由此可见学习率对于模型训练也有不小影响

cancer数据集

由于是给定的数据集,消除了随机性,这里按照试验要求将整个数据集取前4/5作为训练集,后1/5作为预测集。数据共569个样本,30个特征维度,二分类

  • 学习率0.001,训练轮数2000。以下为每500轮训练后的第一个权重和偏置的变化
the epoch: 1
w[0], b of this epoch: [-2.19615000e-02][-0.0415]
the epoch: 501
w[0], b of this epoch: [-1.11730504e+02][-14.50546454]
the epoch: 1001
w[0], b of this epoch: [-1.39643444e+02][-18.37636951]
the epoch: 1501
w[0], b of this epoch: [-1.55486049e+02][-20.38613226]

最终的预测准确率为91.15%

  • 学习率0.0001,训练轮数2000
The accuary of prediction: 89.38053097345133%
  • 学习率0.001,训练轮数1000
The accuary of prediction: 82.30088495575221%
  • 学习率0.001,训练轮数3000
The accuary of prediction: 82.30088495575221%

由上可见,在逻辑斯蒂回归的实现中,学习率和训练轮数对固定样本的分类训练都有不小的影响

代码分析

感知机

import numpy as np
import csv
import matplotlib.pyplot as plt
from IPython import display


# 给二维特征数据画图
def plot2d(sample, idx_right, idx_false, w, b):
    # 新建画布
    display.set_matplotlib_formats('svg')
    plt.rcParams['figure.figsize'] = (8, 4)
    # 利用分类索引画散点,对绿错红,0.4透明度
    plt.scatter(sample[idx_right, 0], sample[idx_right, 1], c='green', alpha=0.4)
    plt.scatter(sample[idx_false, 0], sample[idx_false, 1], c='red', alpha=0.4)
    # 利用样本数据的两个最值寻找分类线上的端点
    x0min, x0max = sample[:, 0].min(), sample[:, 0].max()
    x1min = -(b+x0min*w[0, 0])/w[1, 0]
    x1max = -(b+x0max*w[0, 0])/w[1, 0]
    # 画线
    plt.plot([x0min, x0max], [x1min, x1max], 'yv-', label='line', linewidth=1)
    plt.xlabel('x1')  # 打标签
    plt.ylabel('x2')
    plt.show() 


# 加载数据,数据样式参看实验准备一节
def load_data(data_path, train_rate=0.8):
    with open(data_path) as f:
        reader = csv.reader(f)
        ans = np.array([list for list in reader])
        ans = np.delete(ans, 0, axis=0)      # 删表头
        ans = np.delete(ans, 0, axis=1)      # 删id
        label = ans[:, 0].reshape(-1, 1)     # 拿第二列的分类值
        ans = np.delete(ans, 0, axis=1)      # 删分类值
        ans = ans.astype(np.float)
        label[label == 'M'] = 1              # 转为数值
        label[label == 'B'] = -1
        label = label.astype(np.int)
        rate = int(ans.shape[0]*train_rate)  # 按预设比例划分数据集
        return ans[0:rate, :], label[0:rate, :], ans[rate:-1, :], label[rate:-1, :]


# 计算准确率并绘制图形
def accuary(pre, label, data, w, b):
    res = np.multiply(pre, label) > 0  
    # 对pre和label按位相乘,1*1或-1*-1或1*-1,同号结果大于零即预测成功
    idx_right = np.argwhere(res.squeeze() == True)   
    # 取预测成功者索引,即>0为True的
    idx_false = np.argwhere(res.squeeze() == False)  # 取失败者索引
    # 若采用二维特征数据需要画图则取消注释,高维数据使用会出错
    # plot2d(data, idx_right.reshape(1, -1).squeeze(), idx_false.reshape(1, -1).squeeze(), w, b)
    return len(idx_right) / len(label) * 100         # 返回准确率


# 返回(样本数*1)的1/-1预测分类矩阵
def predict(w, x, b):
    res = np.sign(np.dot(x, w) + b)  # 线性运算后取符号
    res[res == 0] = -1               # 在分类线上者(即为0者)归为-1类
    return res                       # 返回预测类的矩阵


# 训练,迭代参数
def train(w, x, b, y, lr, epoch):
    for i in range(epoch):
        print("the epoch: {}".format(i+1))
        y_pre = np.dot(x, w) + b                        # 线性运算
        res = np.multiply(y_pre, y) > 0                 # 按位乘
        idx_right = np.argwhere(res.squeeze() == True)  # 取同号之积
        temp = x.copy()                                 
        # 不能写temp = x,否则会引用传递改变x!!!!!
        temp[idx_right.squeeze()] = 0  
        # 正确预测则不参与权值迭代,其特征值暂时归零
        # 迭代参数,最小化损失函数
        w += lr*np.dot(temp.transpose(), y)             # lr*x*y累和
        b += lr*y.sum()                                 # lr*y
        # if i % 500 == 0:
        #    print("w, b of this epoch: \n{}\n{}".format(w, b))
        #    temp_accuary = accuary(y_pre, y, x, w, b)
        #    print("the accuary of epoch: {}%".format(temp_accuary))
        # if (temp_accuary > train_accuary) and (lr > 0.01):
        #    lr -= 0.01


if __name__ == '__main__':
    # 加载cancer数据集
    data_path = 'data.csv'
    train_d, train_l, pre_d, pre_l = load_data(data_path, train_rate=0.8)

    # 随机生成二维特征样本,使用该样本需要把上一行注释掉
    '''train_d = np.random.randn(6, 2)
    train_l = np.sign(np.random.randn(6, 1))
    train_l[train_l == 0] = -1
    pre_d = train_d
    pre_l = train_l'''

    # 初始化权重、偏置,以及超参数
    w = np.zeros((train_d.shape[1], 1), dtype=float)
    b = np.zeros(1, dtype=float)
    lr = 0.0001   # 学习率
    epoch = 2000  # 训练轮数

    # 训练并预测
    train(w, train_d, b, train_l, lr, epoch)
    p = predict(w, pre_d, b)
    print("The accuary of prediction: {}%".format(accuary(p, pre_l, pre_d, w, b)))


逻辑斯蒂回归

该实现大部分与感知机相同,重复之处不再注释

import numpy as np
import csv
import matplotlib.pyplot as plt
from IPython import display


def plot2d(sample, idx_right, idx_false, w, b):
    display.set_matplotlib_formats('svg')
    plt.rcParams['figure.figsize'] = (8, 4)

    plt.scatter(sample[idx_right, 0], sample[idx_right, 1], c='green', alpha=0.4)
    plt.scatter(sample[idx_false, 0], sample[idx_false, 1], c='red', alpha=0.4)

    x0min, x0max = sample[:, 0].min(), sample[:, 0].max()
    x1min = -(b+x0min*w[0, 0])/w[1, 0]
    x1max = -(b+x0max*w[0, 0])/w[1, 0]
    plt.plot([x0min, x0max], [x1min, x1max], 'yv-', label='line', linewidth=1)
    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.show()


def load_data(data_path, train_rate=0.8):
    with open(data_path) as f:
        reader = csv.reader(f)
        ans = np.array([list for list in reader])
        ans = np.delete(ans, 0, axis=0)
        ans = np.delete(ans, 0, axis=1)
        label = ans[:, 0].reshape(-1, 1)
        ans = np.delete(ans, 0, axis=1)
        ans = ans.astype(np.float64)
        label[label == 'M'] = 1
        label[label == 'B'] = 0  # 此处类值为0/1,切勿按感知机代码加载数据
        label = label.astype(np.int)
        rate = int(ans.shape[0] * train_rate)
        return ans[0:rate, :], label[0:rate, :], ans[rate:-1, :], label[rate:-1, :]


def accuary(pre, label, data, w, b):
    res = np.logical_xor(pre, label)
    # 此处由于类别值为0/1,采取异或来观测分类正确与否,
    # 正确则相同返回False,错误则相异返回True
    idx_right = np.argwhere(res.squeeze() == False)
    idx_false = np.argwhere(res.squeeze() == True)
    # 要画图就取消注释
    # plot2d(data, idx_right.reshape(1, -1).squeeze(), idx_false.reshape(1, -1).squeeze(), w, b)
    return len(idx_right) / len(label) * 100


def predict(w, x, b):
    linear = np.dot(x, w) + b
    inlinear = 1.0 / (1.0 + np.exp(linear))  # logistic变换
    inlinear = 1.0 - inlinear
    idx = np.argwhere(inlinear.squeeze() > 0.5)
    res = np.zeros(linear.shape)
    res[idx] = 1
    return res


def train(w, x, b, y, lr, epoch):
    for i in range(epoch):
        print("the epoch: {}".format(i+1))
        linear = np.dot(x, w) + b
        inlinear = 1.0 / (1.0 + np.exp(linear))
        # print(inlinear)
        inlinear = 1.0 - inlinear
	    # 为下面算准确率准备
        idx = np.argwhere(inlinear.reshape(1, -1).squeeze() > 0.5)
        res = np.zeros(inlinear.shape)
        res[idx] = 1
	    # 迭代参数,最大化似然函数
        w += lr * np.dot(x.transpose(), (y-inlinear))
        b += lr * (y-inlinear).sum()
        # if i % 500 == 0:
        #    print("w, b of this epoch: \n{}\n{}".format(w, b))
        #    print("the accuary: {}%".format(accuary(res, y, x, w, b)))


if __name__ == '__main__':
    # 加载cancer数据集
    data_path = 'data.csv'
    train_d, train_l, pre_d, pre_l = load_data(data_path, train_rate=0.8)

    # 随机生成二维特征样本,若使用则注释掉上一行
    '''train_d = np.random.randn(6, 2)
    train_l = np.sign(np.random.randn(6, 1))
    train_l[train_l < 0] = 0
    pre_d = train_d
    pre_l = train_l'''

    # 初始化权重、偏置,以及超参数
    w = np.zeros((train_d.shape[1], 1), dtype=np.float)
    b = np.zeros(1, dtype=np.float)
    lr = 0.001
    epoch = 3000

    # 训练并预测
    train(w, train_d, b, train_l, lr, epoch)
    p = predict(w, pre_d, b)
    print("The accuary of prediction: {}%".format(accuary(p, pre_l, pre_d, w, b)))


另附

卑微带学生第一次写博客,贴上了写的还算美观的一次课程作业报告,希望讨个开门红吧!
以后多多学习记录!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值