python机器学习——朴素贝叶斯算法笔记详细记录

朴素贝叶斯算法

本篇主要介绍朴素贝叶斯算法及应用案例。

以Tatinic数据集为例进行应用,同时介绍数据处理,数据集的划分,算法效果评估等内容。

一、简介

朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法 。

最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Bayesian Model,NBM)。

和决策树模型相比,朴素贝叶斯分类器(Naive Bayes Classifier 或 NBC)发源于古典数学理论,有着坚实的数学基础,以及稳定的分类效率。同时,NBC模型所需估计的参数很少,对缺失数据不太敏感,算法也比较简单。

朴素贝叶斯算法(Naive Bayesian algorithm) 是应用最为广泛的分类算法之一。

朴素贝叶斯方法是在贝叶斯算法的基础上进行了相应的简化,即假定给定目标值时属性之间相互条件独立。

也就是说没有哪个属性变量对于决策结果来说占有着较大的比重,也没有哪个属性变量对于决策结果占有着较小的比重。

虽然这个简化方式在一定程度上降低了贝叶斯分类算法的分类效果,但是在实际的应用场景中,极大地简化了贝叶斯方法的复杂性。

二、原理

设有样本数据集D = {d1 ,d2,···,dn },对应样本集的特征属性集为X={x1,x2,···,xd},类变量为Y={y1,y2,···,ym},即D可以分成ym类别。其中x1,x2,···,xd相互随机独立,则Y的先验概率Pprior = P(Y),Y的后验概率Ppost = P(Y|X),则由朴素贝叶斯算法可得,后验概率可以由先验概率Pprior = P(Y)、证据P(X)、类条件概率P(X|Y)计算出:
P ( X ∣ Y ) = P ( Y ) P ( X ∣ Y ) P ( X ) P(X|Y)=\frac{P(Y)P(X|Y)}{P(X)} P(XY)=P(X)P(Y)P(XY)

朴素贝叶斯基于各特征相互独立,在给定类别为y的情况下,上式可以进一步表示为下式:
P ( X ∣ Y = y ) = ∏ i = 1 d P ( x i ∣ Y = y ) P(X|Y=y)=\prod^{d}_{i=1}P(x_i|Y=y) P(XY=y)=i=1dPxiY=y
由以上两式可以算出后验概率为:
P p o s t = P ( Y ∣ X ) = P ( y ) ∏ i = 1 d P ( x i ∣ Y ) P ( X ) P_{post}=P(Y|X)=\frac{P(y)\prod^{d}_{i=1}P(x_i|Y)}{P(X)} Ppost=P(YX)=P(X)P(y)i=1dP(xiY)
由于P(X)的大小是固定不变的,因此在比较后验概率时,只比较上式的分子部分即可。

因此可以得到一个样本数据属于类别yi的朴素贝叶斯计算公式如下:
P ( y i ∣ x 1 , x 2 , ⋅ ⋅ ⋅ , x d ) = p ( y i ) ∏ j = 1 d P ( x j ∣ y i ) ∏ j = 1 d P ( x j ) P(y_i|x_1,x_2,···,x_d)=\frac{p(y_i)\prod^{d}_{j=1}P(x_j|y_i)}{\prod^{d}_{j=1}P(x_j)} P(yix1,x2,⋅⋅⋅,xd)=j=1dP(xj)p(yi)j=1dP(xjyi)
 注意,一般只比较分子部分,这一点在后面会有一个在Titanic上应用的小讨论

三、原理分析(实例)

这里用一个实际案例来说明比较抽象的公式。

以瓜树的案例来计算说明:

目标:对下例进行分类

计算过程:

那么,我自己的理解就是:

​ 先在训练集中计算标签为0和1的先验概率,再分别计算每个特征在标签为0和1的条件概率。

​ 预测过程中,将预测数据每个特征在标签为1时的条件概率之积与预测为1的概率直接相乘,记为P(1)

​ 再将预测数据每个特征在标签为0时的条件概率之积与预测为0的概率直接相乘,记作P(0)

​ 比较P(1)与P(0),哪个大就预测数据的标签为0或1.

四、在Titanic数据集上的应用

1.数据集介绍

数据有如下特征:

继续观察每个特征下的具体情况:

我们会发现:

Age,Cabin,Embarked存在空值,需要进行处理

继续看整体生存状况

再综合我们的常识可以得到结论:

女性的存活率高于男性

Pclass1,2,3的生存率依次降低,这有可能与不同层的乘船人的社会地位,富裕程度有关

年龄上来说,小孩先走,存活率较高

Fare分布差异很大。

Parch 代表同船的父母或子女,SipSp代表同船的兄弟姐妹,这都是两个表现亲人的关系,显然这类因素会对存活率产生影响

那么结合上面的分析的,可以做出如下处理:

对于不重要的特征舍弃掉,简化后面的计算,同时将SibSp和Parch两项合并。

数据中很多为连续型和字符型数据,我们需要进行数值化,离散化处理。

年龄中位数28,缺失值补充为28。并且以25和31为界限分为三类

sibsp&parch按照有无分为两类

生还共计342人。其中全体票价和生还票价均值均约为32。

​ 生还者票价高于32的126人

死亡共计549人。其中票价低于32的464人

​ 票价低于32共计680人,死亡率0.68

票价低于64的共计773人,死亡512人 选择以64为分界点

附上此部分分析代码:

# 以下是最初的数据分析
    # train_set, category = loadDataset('titanic.csv')
    # print(category)
    # print(train_set)
    # k = [0, 0, 0, 0, 0, 0]
    # min = train_set[0][3]
    # max = 0
    # age = []
    # for data in train_set:
    #     for i in range(len(category)):
    #         if data[i] is None:
    #             k[i] += 1
    #     if data[3] is not None:
    #         if data[3] < min:
    #             min = data[3]
    #         if data[3] > max:
    #             max = data[3]
    #         age.append(data[3])
    #
    # age = sorted(age)
    #
    # print(category)
    # print(k)
    # print(min)
    # print(max)
    # print(age)
    # print((len(train_set)-k[3])/2)  #357
    # print(age[int((len(train_set)-k[3])/2)])    # 28
    # print(len(age)/3)
    # print(age[297])     # 25
    # print(age[297*2])   # 31
    # for data in train_set:
    #     if data[3] is None:
    #         data[3] = 28
    # print(train_set)
    # s = 0
    # for data in train_set:
    #     if data[-1] < 64:
    #         s += 1
    #
    # print(s)
    # 以上是最初的数据分析

2.导入相关库

import csv
import numpy as np
import matplotlib.pyplot as plt

3.读入数据和数据处理

此部分函数实现以下内容:

  1. 读入数据集
  2. 删除分析中的不重要特征
  3. 转换数据格式为float
  4. 数据离散化:

性别以0,1区分 #此处其实更好的方法是one—hot处理,读者可以自行研究

年龄以25,31(60)为界限进行离散 #此处再优化分析时会在此说明

sibsp和parcg加和后以2为界限,离散化

fare以64为界限离散化

def loadDataset(filename):
    with open(filename, 'r') as f:
        lines = csv.reader(f)
        data_set = list(lines)
    if filename != 'titanic.csv':
        for i in range(len(data_set)):
            del(data_set[i][0])
    # 整理数据
    for i in range(len(data_set)):
        del(data_set[i][0])
        del(data_set[i][2])
        data_set[i][4] += data_set[i][5]
        del(data_set[i][5])
        del(data_set[i][5])
        del(data_set[i][6])
        del(data_set[i][-1])

    category = data_set[0]

    del (data_set[0])
    # 转换数据格式
    for data in data_set:
        data[0] = int(data[0])
        data[1] = int(data[1])
        if data[3] != '':
            data[3] = float(data[3])
        else:
            data[3] = None
        data[4] = float(data[4])
        data[5] = float(data[5])
    # 补全缺失值 转换记录方式 分类
    for data in data_set:
        if data[3] is None:
            data[3] = 28
        # male : 1, female : 0
        if data[2] == 'male':
            data[2] = 1
        else:
            data[2] = 0
        # age <25 为0, 25<=age<31为1,age>=31为2
        if data[3] < 25:
            data[3] = 0
        elif data[3] >= 21 and data[3] < 60: # 但是测试得60分界准确率最高???!!!
            data[3] = 1
        else:
            data[3] = 2
        # sibsp&parcg以2为界限,小于为0,大于为1
        if data[4] < 2:
            data[4] = 0
        else:
            data[4] = 1
        # fare以64为界限
        if data[-1] < 64:
            data[-1] = 0
        else:
            data[-1] = 1


    return data_set, category

这一部分读入数据要根据自己手中的数据来写,无法直接应用

4.训练集和测试集的划分

常⽤有hold-out,cross validation,bootstrapping⽅法

hold—out

Hold-Out 是基本的划分方法,字面意思就是“留出来一部分”,即将数据集直接按照一定比例划分。

例如常用的 “2/8” 与 “3/7”,含义为训练集 80% 或 70%,相应的测试集占 20% 或 30%.

Hold-Out 的缺点较为明显,即在验证集上计算的出来的 最后评估指标与原始数据的顺序有很大关系。

import random
import csv
import pandas as pd

def loadDataset(filename, split, trainingSet = [], testSet = []):
     with open(filename, 'r') as f:
         lines = csv.reader(f)
         dataset = list(lines)
         for x in range(len(dataset)-1):
             if random.random() < split:  #将数据集随机划分
                 trainingSet.append(dataset[x])
             else:
                 testSet.append(dataset[x])


if __name__ == "__main__":
    train = []
    test = []
    loadDataset('', 0.7, train, test)
    print(train)
    print(test)

    train2 = pd.DataFrame(data=train)
    train2.to_csv('')

    test2 = pd.DataFrame(data=test)
    test2.to_csv('')
cross validation
简单交叉验证

将原始数据随机分为两组,一组做为训练集,一组做为验证集,利用训练集训练分类器,然后利用验证集验证模型,记录最后的分类准确率为此分类器的性能指标。

好处:处理简单,只需随机把原始数据分为两组即可

坏处:没有达到交叉的思想,由于是随机的将原始数据分组,所以最后验证集分类准确率的高低与原始数据的分组有很大的关系,得到的结果并不具有说服性。

2-折交叉验证(2-fold Cross Validation,记为2-CV)

做法是将数据集分成两个相等大小的子集,进行两次的分类器训练。在第一次中,一个子集作为训练集,另一个便作为测试集;在第二次训练中,则将训练集与测试集对换,

其并不常用:

主要原因是训练集样本数太少,不足以代表母体样本的分布,导致te测试阶段辨识率容易出现明显落差。

K-折交叉验证(K-fold Cross Validation,记为K-CV)

将原始数据分成K组(一般是均分),将每个子集数据分别做一次验证集,其余的K-1组子集数据作为训练集,这样会得到K个模型,用这K个模型最终的验证集的分类准确率的平均数作为此K-CV下分类器的性能指标。K一般大于等于2,实际操作时一般从3开始取,只有在原始数据集合数据量小的时候才会尝试取2。

应用最多,K-CV可以有效的避免过拟合与欠拟合的发生,最后得到的结果也比较具有说服性。

常见为十折交叉验证

  • 将数据集分成十份,轮流将其中9份作为训练数据,1份作为测试数据,进行试验。每次试验都会得出相应的正确率。

  • 10次的结果的正确率的平均值作为对算法精度的估计,一般还需要进行多次10折交叉验证(例如10次10折交叉验证),再求其均值,作为对算法准确性的估计

import csv
import pandas as pd

with open('titanic.csv', 'r') as f:
    lines = csv.reader(f)
    data_set = list(lines)
del(data_set[0])
length = len(data_set)//10
print(length)
data = []
for i in range(10):
    data.append(data_set[i*length:(i+1)*length])
data.append(data_set[10*length:])

print(data_set)
print(data)
bootstrapping(自助法)

最常用的一种Bootstrap自助法,假设给定的数据集包含d个样本。该数据集有放回地抽样m次,产生m个样本的训练集。这样原数据样本中的某些样本很可能在该样本集中出现多次。没有进入该训练集的样本最终形成检验集(测试集)。 显然每个样本被选中的概率是1/m,因此未被选中的概率就是(1-1/m),这样一个样本在训练集中没出现的概率就是m次都未被选中的概率,即(1-1/m)m。当m趋于无穷大时,这一概率就将趋近于e^-1=0.368,所以留在训练集中的样本大概就占原来数据集的63.2%。

例如:人工样本为1,2,3;只有三个样本,则可以从随机变量X,分布为P(X=k)=1/3, k=1,2,3; 这样的经验分布中用计算机根据上述分布自动产生样本,如产生5个样本:1 2 3 2 1;也可以是:3 3 2 1 1

自助法在数据集较小、难以有效划分训练集测试集时很有用;此外,自助法能从初始数据集中产生多个不同的训练集,这对集成学习等方法有很大的好处。然而,自助法产生的数据集改变了初始数据集的分布,这会引入估计偏差。因此,在初始数据量足够时,留出法和交叉验证法更常用一些。

#自助法
import numpy as np
#任意设置一个数据集
X = [1, 4, 3, 23, 4, 6, 7, 8, 9, 45, 67, 89, 34, 54, 76, 98, 43, 52]

#通过产生的随机数获得抽取样本的序号
bootstrapping = []
for i in range(len(X)):
    bootstrapping.append(np.floor(np.random.random()*len(X)))
#通过序号获得原始数据集中的数据
D_1 = []
for i in range(len(X)):
    D_1.append(X[int(bootstrapping[i])])


5.朴素贝叶斯计算

这部分没什么好说的,按照公式计算就好

class NaiveBayes:
    def __init__(self):
        pass

    def train(self, data):
        length = len(data)
        p = []
        for i in range(len(data[0])):
            p.append([])
        # 计算先验概率
        sur = 0
        for i in data:
            if i[0] == 1:
                sur += 1

        death = length - sur
        p1 = sur/length
        p0 = 1 - p1
        p[0].append(p1)
        p[0].append(p0)

        print(p)

        # 计算pclass的条件概率
        a1 = 0
        a0 = 0
        b1 = 0
        b0 = 0
        c1 = 0
        c0 = 0
        for i in data:
            if i[0] == 1 and i[1] == 1:
                a1 += 1
            elif i[0] == 0 and i[1] == 1:
                a0 += 1
            elif i[0] == 1 and i[1] == 2:
                b1 += 1
            elif i[0] == 0 and i[1] == 2:
                b0 += 1
            elif i[0] == 1 and i[1] == 3:
                c1 += 1
            elif i[0] == 0 and i[1] == 3:
                c0 += 1
        p[1].append(a1 / sur)
        p[1].append(a0 / death)
        p[1].append(b1 / sur)
        p[1].append(b0 / death)
        p[1].append(c1 / sur)
        p[1].append(c0 / death)

        # 计算sex的条件概率
        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[2] == 1:
                m1 += 1
            elif i[0] == 0 and i[2] == 1:
                m0 += 1
            elif i[0] == 1 and i[2] == 0:
                f1 += 1
            elif i[0] == 0 and i[2] == 0:
                f0 += 1
        p[2].append(m1 / sur)
        p[2].append(m0 / death)
        p[2].append(f1 / sur)
        p[2].append(f0 / death)

        # 计算age的条件概率
        a1 = 0
        a0 = 0
        b1 = 0
        b0 = 0
        c1 = 0
        c0 = 0
        for i in data:
            if i[0] == 1 and i[3] == 1:
                a1 += 1
            elif i[0] == 0 and i[3] == 1:
                a0 += 1
            elif i[0] == 1 and i[3] == 2:
                b1 += 1
            elif i[0] == 0 and i[3] == 2:
                b0 += 1
            elif i[0] == 1 and i[3] == 3:
                c1 += 1
            elif i[0] == 0 and i[3] == 3:
                c0 += 1
        p[3].append(a1 / sur)
        p[3].append(a0 / death)
        p[3].append(b1 / sur)
        p[3].append(b0 / death)
        p[3].append(c1 / sur)
        p[3].append(c0 / death)

        # 计算sibsp&parch条件概率
        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[4] == 1:
                m1 += 1
            elif i[0] == 0 and i[4] == 1:
                m0 += 1
            elif i[0] == 1 and i[4] == 0:
                f1 += 1
            elif i[0] == 0 and i[4] == 0:
                f0 += 1
        p[4].append(m1 / sur)
        p[4].append(m0 / death)
        p[4].append(f1 / sur)
        p[4].append(f0 / death)

        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[5] == 1:
                m1 += 1
            elif i[0] == 0 and i[5] == 1:
                m0 += 1
            elif i[0] == 1 and i[5] == 0:
                f1 += 1
            elif i[0] == 0 and i[5] == 0:
                f0 += 1
        p[5].append(m1 / sur)
        p[5].append(m0 / death)
        p[5].append(f1 / sur)
        p[5].append(f0 / death)

        return p

    def predict(self, t_data, p):
        result = []
        pp = []
        for data in t_data:
            p1 = p[0][0]
            p0 = p[0][1]
            # pclass
            if data[1] == 1:
                p1 *= p[1][0]
                p0 *= p[1][1]
            elif data[1] == 2:
                p1 *= p[1][2]
                p0 *= p[1][3]
            elif data[1] == 3:
                p1 *= p[1][4]
                p0 *= p[1][5]
            # sex
            if data[2] == 1:
                p1 *= p[2][0]
                p0 *= p[2][1]
            else:
                p1 *= p[2][2]
                p0 *= p[2][3]

            # age
            if data[3] == 0:
                p1 *= p[3][0]
                p0 *= p[3][1]
            elif data[3] == 1:
                p1 *= p[3][2]
                p0 *= p[3][3]
            else:
                p1 *= p[3][4]
                p0 *= p[3][5]

            #sibsp&parch
            if data[4] == 1:
                p1 *= p[4][0]
                p0 *= p[4][1]
            else:
                p1 *= p[4][2]
                p0 *= p[4][3]

            # fare
            if data[5] == 1:
                p1 *= p[5][0]
                p0 *= p[5][1]
            else:
                p1 *= p[5][2]
                p0 *= p[5][3]

            pp.append(p1)

            if p1 >= p0:
                result.append(1)
            else:
                result.append(0)
        return pp, result

6.模型评估1

此处评估标准选取ROC_AUC

先给出混淆矩阵:

ROC 图像:

纵轴为 TPR 真正例率,预测为正且实际为正的样本占所有正例样本的比例。

横轴为 FPR 假正例率,预测为正但实际为负的样本占所有负例样本的比例。
T P R = T P T P + F N TPR = \frac{TP}{TP+FN} TPR=TP+FNTP

F P R = F P T N + F P FPR = \frac{FP}{TN+FP} FPR=TN+FPFP

ROC图像分析
  • 第一个点,(0,1),即 FPR=0, TPR=1,这意味着 FN(false negative)=0,并且FP(false positive)=0。这意味着分类器很完美,因为它将所有的样本都正确分类。
  • 第二个点,(1,0),即 FPR=1,TPR=0,这个分类器是最糟糕的,因为它成功避开了所有的正确答案。
  • 第三个点,(0,0),即 FPR=TPR=0,即 FP(false positive)=TP(true positive)=0,此时分类器将所有的样本都预测为负样本(negative)。
  • 第四个点(1,1),分类器将所有的样本都预测为正样本。
  • 对角线上的点表示分类器将一半的样本猜测为正样本,另外一半的样本猜测为负样本。

因此,ROC 曲线越接近左上角,分类器的性能越好。

怎么画ROC曲线

  • 按 Score 从大到小排列
  • 依次将每个 Score 设定为阈值,然后这 20 个样本的标签会变化,当它的 score 大于或等于当前阈值时,则为正样本,否则为负样本。
  • 这样对每个阈值,可以计算一组 FPR 和 TPR,此例一共可以得到 20 组。
  • 当阈值设置为 1 和 0 时, 可以得到 ROC 曲线上的 (0,0) 和 (1,1) 两个点。
怎么计算AUC

A U C = 1 2 ∑ i = 1 m − 1 ( x i + 1 − x i ) ⋅ ( y i + y i + 1 ) AUC = \frac{1}{2}\sum^{m-1}_{i=1}(x_{i+1}-x_i)·(y_i+y_{i+1}) AUC=21i=1m1(xi+1xi)(yi+yi+1)

def roc_auc(p, test, result):
    tp = 0
    tn = 0
    for i in test:
        if i[0] == 1:
            tp += 1
        else:
            tn += 1

    for i in range(len(test)):
        test[i] = test[i][0]
        p[i] = [p[i]]
        p[i].append(test[i])
        p[i].append(result[i])

    p = sorted(p, reverse=True)
    print(p)
    tpr = [0]
    fpr = [0]
    length = len(p)

    for i in range(1, length):
        if p[i][1] == 1:
            fpr.append(fpr[i-1])
            tpr.append(tpr[i-1]+1/tp)

        elif p[i][1] == 0:
            fpr.append(fpr[i-1]+1/tn)
            tpr.append(tpr[i-1])

    tpr.append(1)
    fpr.append(1)

    print(fpr)
    print(tpr)

    auc = 0
    for i in range(len(fpr)-1):
        auc += (fpr[i+1]-fpr[i])*(tpr[i]+tpr[i+1])
    auc = auc/2

    print(auc)

    fpr = np.array(fpr)
    tpr = np.array(tpr)

    plt.title("ROC curve of %s (AUC = %.4f)" % ('svm', auc))
    plt.xlabel("False Positive Rate")
    plt.ylabel("True Positive Rate")

    plt.plot(fpr, tpr)
    plt.show()

7.模型评估2——F1_score

F1分数(F1 Score)中,是统计学中用来衡量二分类模型精确度的一种指标。它同时兼顾了分类模型的精确率和召回率。F1分数可以看作是模型精确率和召回率的一种调和平均,它的最大值是1,最小值是0。
F 1 = 2 ∗ P ∗ R P + R = 2 ∗ T P 样例总数 + T P − T N F1 = \frac{2*P*R}{P+R}=\frac{2*TP}{样例总数+TP-TN} F1=P+R2PR=样例总数+TPTN2TP

 tp = 0
    tn = 0
    for i in range(len(result)):
        if test[i][0] == result[i] == 1:
            tp += 1
        if test[i][0] == result[i] == 0:
            tn += 1

    print(tp)
    print(tn)

    f1 = (2*tp)/(len(test)+tp-tn)
    print(f1)

    roc_auc(pp, test, result)

8.评估结果

准确率:74.5%

F1 : 0.678

9.完整代码

import csv
import numpy as np
import matplotlib.pyplot as plt

# 0PassengerId:乘客的ID                               不重要
# 1Survived:乘客是否获救,Key:0=没获救,1=已获救
# 2Pclass:乘客船舱等级(1/2/3三个等级舱位)
# 3Name:乘客姓名                                       不重要
# 4Sex:性别
# 5Age:年龄
# 6SibSp:乘客在船上的兄弟姐妹/配偶数量
# 7Parch:乘客在船上的父母/孩子数量
# 8Ticket:船票号                                         不重要
# 9Fare:船票价
# 10Cabin:客舱号码                                        不重要
# 11Embarked:登船的港口                                   不重要

# 数据分析得: 年龄中位数28,缺失值补充为28。并且以25和31为界限分为三类
#             sibsp&parch按照有无分为两类
#            生还共计342人。其中全体票价和生还票价均值均约为32。
#               生还者票价高于32的126人
#            死亡共计549人。其中票价低于32的464人
#           票价低于32共计680人,死亡率0.68
#           票价低于64的共计773人,死亡512人  选择以64为分界点


def loadDataset(filename):
    with open(filename, 'r') as f:
        lines = csv.reader(f)
        data_set = list(lines)
    if filename != 'titanic.csv':
        for i in range(len(data_set)):
            del(data_set[i][0])
    # 整理数据
    for i in range(len(data_set)):
        del(data_set[i][0])
        del(data_set[i][2])
        data_set[i][4] += data_set[i][5]
        del(data_set[i][5])
        del(data_set[i][5])
        del(data_set[i][6])
        del(data_set[i][-1])

    category = data_set[0]

    del (data_set[0])
    # 转换数据格式
    for data in data_set:
        data[0] = int(data[0])
        data[1] = int(data[1])
        if data[3] != '':
            data[3] = float(data[3])
        else:
            data[3] = None
        data[4] = float(data[4])
        data[5] = float(data[5])
    # 补全缺失值 转换记录方式 分类
    for data in data_set:
        if data[3] is None:
            data[3] = 28
        # male : 1, female : 0
        if data[2] == 'male':
            data[2] = 1
        else:
            data[2] = 0
        # age <25 为0, 25<=age<31为1,age>=31为2
        if data[3] < 25:
            data[3] = 0
        elif data[3] >= 21 and data[3] < 60: # 但是测试得60分界准确率最高???!!!
            data[3] = 1
        else:
            data[3] = 2
        # sibsp&parcg以2为界限,小于为0,大于为1
        if data[4] < 2:
            data[4] = 0
        else:
            data[4] = 1
        # fare以64为界限
        if data[-1] < 64:
            data[-1] = 0
        else:
            data[-1] = 1


    return data_set, category


class NaiveBayes:
    def __init__(self):
        pass

    def train(self, data):
        length = len(data)
        p = []
        for i in range(len(data[0])):
            p.append([])
        # 计算先验概率
        sur = 0
        for i in data:
            if i[0] == 1:
                sur += 1

        death = length - sur
        p1 = sur/length
        p0 = 1 - p1
        p[0].append(p1)
        p[0].append(p0)

        print(p)

        # 计算pclass的条件概率
        a1 = 0
        a0 = 0
        b1 = 0
        b0 = 0
        c1 = 0
        c0 = 0
        for i in data:
            if i[0] == 1 and i[1] == 1:
                a1 += 1
            elif i[0] == 0 and i[1] == 1:
                a0 += 1
            elif i[0] == 1 and i[1] == 2:
                b1 += 1
            elif i[0] == 0 and i[1] == 2:
                b0 += 1
            elif i[0] == 1 and i[1] == 3:
                c1 += 1
            elif i[0] == 0 and i[1] == 3:
                c0 += 1
        p[1].append(a1 / sur)
        p[1].append(a0 / death)
        p[1].append(b1 / sur)
        p[1].append(b0 / death)
        p[1].append(c1 / sur)
        p[1].append(c0 / death)

        # 计算sex的条件概率
        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[2] == 1:
                m1 += 1
            elif i[0] == 0 and i[2] == 1:
                m0 += 1
            elif i[0] == 1 and i[2] == 0:
                f1 += 1
            elif i[0] == 0 and i[2] == 0:
                f0 += 1
        p[2].append(m1 / sur)
        p[2].append(m0 / death)
        p[2].append(f1 / sur)
        p[2].append(f0 / death)

        # 计算age的条件概率
        a1 = 0
        a0 = 0
        b1 = 0
        b0 = 0
        c1 = 0
        c0 = 0
        for i in data:
            if i[0] == 1 and i[3] == 1:
                a1 += 1
            elif i[0] == 0 and i[3] == 1:
                a0 += 1
            elif i[0] == 1 and i[3] == 2:
                b1 += 1
            elif i[0] == 0 and i[3] == 2:
                b0 += 1
            elif i[0] == 1 and i[3] == 3:
                c1 += 1
            elif i[0] == 0 and i[3] == 3:
                c0 += 1
        p[3].append(a1 / sur)
        p[3].append(a0 / death)
        p[3].append(b1 / sur)
        p[3].append(b0 / death)
        p[3].append(c1 / sur)
        p[3].append(c0 / death)

        # 计算sibsp&parch条件概率
        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[4] == 1:
                m1 += 1
            elif i[0] == 0 and i[4] == 1:
                m0 += 1
            elif i[0] == 1 and i[4] == 0:
                f1 += 1
            elif i[0] == 0 and i[4] == 0:
                f0 += 1
        p[4].append(m1 / sur)
        p[4].append(m0 / death)
        p[4].append(f1 / sur)
        p[4].append(f0 / death)

        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[5] == 1:
                m1 += 1
            elif i[0] == 0 and i[5] == 1:
                m0 += 1
            elif i[0] == 1 and i[5] == 0:
                f1 += 1
            elif i[0] == 0 and i[5] == 0:
                f0 += 1
        p[5].append(m1 / sur)
        p[5].append(m0 / death)
        p[5].append(f1 / sur)
        p[5].append(f0 / death)

        return p

    def predict(self, t_data, p):
        result = []
        pp = []
        for data in t_data:
            p1 = p[0][0]
            p0 = p[0][1]
            # pclass
            if data[1] == 1:
                p1 *= p[1][0]
                p0 *= p[1][1]
            elif data[1] == 2:
                p1 *= p[1][2]
                p0 *= p[1][3]
            elif data[1] == 3:
                p1 *= p[1][4]
                p0 *= p[1][5]
            # sex
            if data[2] == 1:
                p1 *= p[2][0]
                p0 *= p[2][1]
            else:
                p1 *= p[2][2]
                p0 *= p[2][3]

            # age
            if data[3] == 0:
                p1 *= p[3][0]
                p0 *= p[3][1]
            elif data[3] == 1:
                p1 *= p[3][2]
                p0 *= p[3][3]
            else:
                p1 *= p[3][4]
                p0 *= p[3][5]

            #sibsp&parch
            if data[4] == 1:
                p1 *= p[4][0]
                p0 *= p[4][1]
            else:
                p1 *= p[4][2]
                p0 *= p[4][3]

            # fare
            if data[5] == 1:
                p1 *= p[5][0]
                p0 *= p[5][1]
            else:
                p1 *= p[5][2]
                p0 *= p[5][3]

            pp.append(p1)

            if p1 >= p0:
                result.append(1)
            else:
                result.append(0)
        return pp, result


def roc_auc(p, test, result):
    tp = 0
    tn = 0
    for i in test:
        if i[0] == 1:
            tp += 1
        else:
            tn += 1

    for i in range(len(test)):
        test[i] = test[i][0]
        p[i] = [p[i]]
        p[i].append(test[i])
        p[i].append(result[i])

    p = sorted(p, reverse=True)
    print(p)
    tpr = [0]
    fpr = [0]
    length = len(p)

    for i in range(1, length):
        if p[i][1] == 1:
            fpr.append(fpr[i-1])
            tpr.append(tpr[i-1]+1/tp)

        elif p[i][1] == 0:
            fpr.append(fpr[i-1]+1/tn)
            tpr.append(tpr[i-1])

    tpr.append(1)
    fpr.append(1)

    print(fpr)
    print(tpr)

    auc = 0
    for i in range(len(fpr)-1):
        auc += (fpr[i+1]-fpr[i])*(tpr[i]+tpr[i+1])
    auc = auc/2

    print(auc)

    fpr = np.array(fpr)
    tpr = np.array(tpr)

    plt.title("ROC curve of %s (AUC = %.4f)" % ('svm', auc))
    plt.xlabel("False Positive Rate")
    plt.ylabel("True Positive Rate")

    plt.plot(fpr, tpr)
    plt.show()




if __name__ == "__main__":
    # 以下是最初的数据分析
    # train_set, category = loadDataset('titanic.csv')
    # print(category)
    # print(train_set)
    # k = [0, 0, 0, 0, 0, 0]
    # min = train_set[0][3]
    # max = 0
    # age = []
    # for data in train_set:
    #     for i in range(len(category)):
    #         if data[i] is None:
    #             k[i] += 1
    #     if data[3] is not None:
    #         if data[3] < min:
    #             min = data[3]
    #         if data[3] > max:
    #             max = data[3]
    #         age.append(data[3])
    #
    # age = sorted(age)
    #
    # print(category)
    # print(k)
    # print(min)
    # print(max)
    # print(age)
    # print((len(train_set)-k[3])/2)  #357
    # print(age[int((len(train_set)-k[3])/2)])    # 28
    # print(len(age)/3)
    # print(age[297])     # 25
    # print(age[297*2])   # 31
    # for data in train_set:
    #     if data[3] is None:
    #         data[3] = 28
    # print(train_set)
    # s = 0
    # for data in train_set:
    #     if data[-1] < 64:
    #         s += 1
    #
    # print(s)
    # 以上是最初的数据分析





    train_set, category = loadDataset('titanic_train.csv')
    test_set, category = loadDataset('titanic_test.csv')

    print(category)
    print(train_set)

    bayes = NaiveBayes()

    p = bayes.train(train_set)
    print(p)

    pp, result = bayes.predict(test_set, p)
    print(result)
    count = 0
    for i in range(len(result)):
        if test_set[i][0] == result[i]:
            count += 1

    accuracy = count/len(test_set)
    print(accuracy)



    # # 十折交叉验证
    # data_set, category = loadDataset('titanic.csv')
    #
    # length = len(data_set) // 10
    # print(length)
    # data = []
    # for i in range(10):
    #     data.append(data_set[i * length:(i + 1) * length])
    # data.append(data_set[10 * length:])
    #
    # sum = 0
    #
    # for i in range(9):
    #     p = bayes.train(data[i])
    #     print(p)
    #
    #     pp, result = bayes.predict(data[9], p)
    #     print(result)
    #     count = 0
    #     for i in range(len(result)):
    #         if test_set[i][0] == result[i]:
    #             count += 1
    #     sum += count/len(data[9])
    #
    # print(sum/10)


    # 自助法
    data_set, category = loadDataset('titanic.csv')
    bootstrapping = []
    for i in range(len(data_set)):
         bootstrapping.append(np.floor(np.random.random()*len(data_set)))

    test = []
    for i in range(len(data_set)):
         test.append(data_set[int(bootstrapping[i])])

    p = bayes.train(data_set)
    print(p)

    pp, result = bayes.predict(test, p)
    print(result)
    count = 0
    for i in range(len(result)):
        if test[i][0] == result[i]:
            count += 1

    accuracy = count / len(test)
    print(accuracy)

    #基于自助法计算F1-score
    tp = 0
    tn = 0
    for i in range(len(result)):
        if test[i][0] == result[i] == 1:
            tp += 1
        if test[i][0] == result[i] == 0:
            tn += 1

    print(tp)
    print(tn)

    f1 = (2*tp)/(len(test)+tp-tn)
    print(f1)

    roc_auc(pp, test, result)


五、模型优化与提升

  1. 性别数值化更好的方法是one—hot处理
  2. 年龄离散化的标准。后续再测试的过程中发现,年龄的离散化标准对模型的准确率影响极大,这一点可以在探索后进行优化进而提升模型效果
  3. 对于贝叶斯的概率计算时,原方法是只计算分子进行比较。但是我们可以换一种方法,计算完整的概率值,然后与0.5进行比较,然后分类。这种方法再结合更加合适的年龄划分标准可以是本地的准确率达到90%以上,在kaggle上也可以达到80%以上

这几种优化思路提供给读者自行探索尝试。

import csv
import numpy as np
import matplotlib.pyplot as plt

# 0PassengerId:乘客的ID                               不重要
# 1Survived:乘客是否获救,Key:0=没获救,1=已获救
# 2Pclass:乘客船舱等级(1/2/3三个等级舱位)
# 3Name:乘客姓名                                       不重要
# 4Sex:性别
# 5Age:年龄
# 6SibSp:乘客在船上的兄弟姐妹/配偶数量
# 7Parch:乘客在船上的父母/孩子数量
# 8Ticket:船票号                                         不重要
# 9Fare:船票价
# 10Cabin:客舱号码                                        不重要
# 11Embarked:登船的港口                                   不重要

# 数据分析得: 年龄中位数28,缺失值补充为28。并且以25和31为界限分为三类
#             sibsp&parch按照有无分为两类
#            生还共计342人。其中全体票价和生还票价均值均约为32。
#               生还者票价高于32的126人
#            死亡共计549人。其中票价低于32的464人
#           票价低于32共计680人,死亡率0.68
#           票价低于64的共计773人,死亡512人  选择以64为分界点


def loadDataset(filename):
    with open(filename, 'r') as f:
        lines = csv.reader(f)
        data_set = list(lines)
    if filename != 'data/data43004/titanic.csv':
        for i in range(len(data_set)):
            del(data_set[i][0])
    # 整理数据
    for i in range(len(data_set)):
        del(data_set[i][0])
        del(data_set[i][2])
        data_set[i][4] += data_set[i][5]
        del(data_set[i][5])
        del(data_set[i][5])
        del(data_set[i][6])
        del(data_set[i][-1])

    category = data_set[0]

    del (data_set[0])
    # 转换数据格式
    for data in data_set:
        data[0] = int(data[0])
        data[1] = int(data[1])
        if data[3] != '':
            data[3] = float(data[3])
        else:
            data[3] = None
        data[4] = float(data[4])
        data[5] = float(data[5])
    # 补全缺失值 转换记录方式 分类
    for data in data_set:
        if data[3] is None:
            data[3] = 28
        # male : 1, female : 0
        if data[2] == 'male':
            data[2] = 1
        else:
            data[2] = 0
        # age <25 为0, 25<=age<31为1,age>=31为2
        if data[3] < 25:
            data[3] = 0
        elif data[3] >= 21 and data[3] < 60: # 但是测试得60分界准确率最高???!!!
            data[3] = 1
        else:
            data[3] = 2
        # sibsp&parcg以2为界限,小于为0,大于为1
        if data[4] < 2:
            data[4] = 0
        else:
            data[4] = 1
        # fare以64为界限
        if data[-1] < 64:
            data[-1] = 0
        else:
            data[-1] = 1


    return data_set, category


class NaiveBayes:
    def __init__(self):
        pass

    def train(self, data):
        length = len(data)
        p = []
        for i in range(len(data[0])):
            p.append([])
        # 计算先验概率
        sur = 0
        for i in data:
            if i[0] == 1:
                sur += 1

        death = length - sur
        p1 = sur/length
        p0 = 1 - p1
        p[0].append(p1)
        p[0].append(p0)

        print(p)

        # 计算pclass的条件概率
        a1 = 0
        a0 = 0
        b1 = 0
        b0 = 0
        c1 = 0
        c0 = 0
        for i in data:
            if i[0] == 1 and i[1] == 1:
                a1 += 1
            elif i[0] == 0 and i[1] == 1:
                a0 += 1
            elif i[0] == 1 and i[1] == 2:
                b1 += 1
            elif i[0] == 0 and i[1] == 2:
                b0 += 1
            elif i[0] == 1 and i[1] == 3:
                c1 += 1
            elif i[0] == 0 and i[1] == 3:
                c0 += 1
        p[1].append(a1 / sur)
        p[1].append(a0 / death)
        p[1].append(b1 / sur)
        p[1].append(b0 / death)
        p[1].append(c1 / sur)
        p[1].append(c0 / death)

        # 计算sex的条件概率
        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[2] == 1:
                m1 += 1
            elif i[0] == 0 and i[2] == 1:
                m0 += 1
            elif i[0] == 1 and i[2] == 0:
                f1 += 1
            elif i[0] == 0 and i[2] == 0:
                f0 += 1
        p[2].append(m1 / sur)
        p[2].append(m0 / death)
        p[2].append(f1 / sur)
        p[2].append(f0 / death)

        # 计算age的条件概率
        a1 = 0
        a0 = 0
        b1 = 0
        b0 = 0
        c1 = 0
        c0 = 0
        for i in data:
            if i[0] == 1 and i[3] == 1:
                a1 += 1
            elif i[0] == 0 and i[3] == 1:
                a0 += 1
            elif i[0] == 1 and i[3] == 2:
                b1 += 1
            elif i[0] == 0 and i[3] == 2:
                b0 += 1
            elif i[0] == 1 and i[3] == 3:
                c1 += 1
            elif i[0] == 0 and i[3] == 3:
                c0 += 1
        p[3].append(a1 / sur)
        p[3].append(a0 / death)
        p[3].append(b1 / sur)
        p[3].append(b0 / death)
        p[3].append(c1 / sur)
        p[3].append(c0 / death)

        # 计算sibsp&parch条件概率
        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[4] == 1:
                m1 += 1
            elif i[0] == 0 and i[4] == 1:
                m0 += 1
            elif i[0] == 1 and i[4] == 0:
                f1 += 1
            elif i[0] == 0 and i[4] == 0:
                f0 += 1
        p[4].append(m1 / sur)
        p[4].append(m0 / death)
        p[4].append(f1 / sur)
        p[4].append(f0 / death)

        m1 = 0
        m0 = 0
        f1 = 0
        f0 = 0
        for i in data:
            if i[0] == 1 and i[5] == 1:
                m1 += 1
            elif i[0] == 0 and i[5] == 1:
                m0 += 1
            elif i[0] == 1 and i[5] == 0:
                f1 += 1
            elif i[0] == 0 and i[5] == 0:
                f0 += 1
        p[5].append(m1 / sur)
        p[5].append(m0 / death)
        p[5].append(f1 / sur)
        p[5].append(f0 / death)

        return p

    def predict(self, t_data, p):
        result = []
        pp = []
        for data in t_data:
            p1 = p[0][0]
            p0 = p[0][1]
            # pclass
            if data[1] == 1:
                p1 *= p[1][0]
                p0 *= p[1][1]
            elif data[1] == 2:
                p1 *= p[1][2]
                p0 *= p[1][3]
            elif data[1] == 3:
                p1 *= p[1][4]
                p0 *= p[1][5]
            # sex
            if data[2] == 1:
                p1 *= p[2][0]
                p0 *= p[2][1]
            else:
                p1 *= p[2][2]
                p0 *= p[2][3]

            # age
            if data[3] == 0:
                p1 *= p[3][0]
                p0 *= p[3][1]
            elif data[3] == 1:
                p1 *= p[3][2]
                p0 *= p[3][3]
            else:
                p1 *= p[3][4]
                p0 *= p[3][5]

            #sibsp&parch
            if data[4] == 1:
                p1 *= p[4][0]
                p0 *= p[4][1]
            else:
                p1 *= p[4][2]
                p0 *= p[4][3]

            # fare
            if data[5] == 1:
                p1 *= p[5][0]
                p0 *= p[5][1]
            else:
                p1 *= p[5][2]
                p0 *= p[5][3]

            pp.append(p1)

            if p1 >= p0:
                result.append(1)
            else:
                result.append(0)
        return pp, result


def roc_auc(p, test, result):
    tp = 0
    tn = 0
    for i in test:
        if i[0] == 1:
            tp += 1
        else:
            tn += 1

    for i in range(len(test)):
        test[i] = test[i][0]
        p[i] = [p[i]]
        p[i].append(test[i])
        p[i].append(result[i])

    p = sorted(p, reverse=True)
    print(p)
    tpr = [0]
    fpr = [0]
    length = len(p)

    for i in range(1, length):
        if p[i][1] == 1:
            fpr.append(fpr[i-1])
            tpr.append(tpr[i-1]+1/tp)

        elif p[i][1] == 0:
            fpr.append(fpr[i-1]+1/tn)
            tpr.append(tpr[i-1])

    tpr.append(1)
    fpr.append(1)

    print(fpr)
    print(tpr)

    auc = 0
    for i in range(len(fpr)-1):
        auc += (fpr[i+1]-fpr[i])*(tpr[i]+tpr[i+1])
    auc = auc/2

    print(auc)

    fpr = np.array(fpr)
    tpr = np.array(tpr)

    plt.title("ROC curve of %s (AUC = %.4f)" % ('svm', auc))
    plt.xlabel("False Positive Rate")
    plt.ylabel("True Positive Rate")

    plt.plot(fpr, tpr)
    plt.show()




if __name__ == "__main__":
    # 以下是最初的数据分析
    # train_set, category = loadDataset('titanic.csv')
    # print(category)
    # print(train_set)
    # k = [0, 0, 0, 0, 0, 0]
    # min = train_set[0][3]
    # max = 0
    # age = []
    # for data in train_set:
    #     for i in range(len(category)):
    #         if data[i] is None:
    #             k[i] += 1
    #     if data[3] is not None:
    #         if data[3] < min:
    #             min = data[3]
    #         if data[3] > max:
    #             max = data[3]
    #         age.append(data[3])
    #
    # age = sorted(age)
    #
    # print(category)
    # print(k)
    # print(min)
    # print(max)
    # print(age)
    # print((len(train_set)-k[3])/2)  #357
    # print(age[int((len(train_set)-k[3])/2)])    # 28
    # print(len(age)/3)
    # print(age[297])     # 25
    # print(age[297*2])   # 31
    # for data in train_set:
    #     if data[3] is None:
    #         data[3] = 28
    # print(train_set)
    # s = 0
    # for data in train_set:
    #     if data[-1] < 64:
    #         s += 1
    #
    # print(s)
    # 以上是最初的数据分析





    train_set, category = loadDataset('data/data43004/titanic_train.csv')
    test_set, category = loadDataset('data/data43004/titanic_test.csv')

    print(category)
    print(train_set)

    bayes = NaiveBayes()

    p = bayes.train(train_set)
    print(p)

    pp, result = bayes.predict(test_set, p)
    print(result)
    count = 0
    for i in range(len(result)):
        if test_set[i][0] == result[i]:
            count += 1

    accuracy = count/len(test_set)
    print(accuracy)



    # # 十折交叉验证
    # data_set, category = loadDataset('titanic.csv')
    #
    # length = len(data_set) // 10
    # print(length)
    # data = []
    # for i in range(10):
    #     data.append(data_set[i * length:(i + 1) * length])
    # data.append(data_set[10 * length:])
    #
    # sum = 0
    #
    # for i in range(9):
    #     p = bayes.train(data[i])
    #     print(p)
    #
    #     pp, result = bayes.predict(data[9], p)
    #     print(result)
    #     count = 0
    #     for i in range(len(result)):
    #         if test_set[i][0] == result[i]:
    #             count += 1
    #     sum += count/len(data[9])
    #
    # print(sum/10)


    # 自助法
    data_set, category = loadDataset('data/data43004/titanic.csv')
    bootstrapping = []
    for i in range(len(data_set)):
         bootstrapping.append(np.floor(np.random.random()*len(data_set)))

    test = []
    for i in range(len(data_set)):
         test.append(data_set[int(bootstrapping[i])])

    p = bayes.train(data_set)
    print(p)

    pp, result = bayes.predict(test, p)
    print(result)
    count = 0
    for i in range(len(result)):
        if test[i][0] == result[i]:
            count += 1

    accuracy = count / len(test)
    print(accuracy)

    #基于自助法计算F1-score
    tp = 0
    tn = 0
    for i in range(len(result)):
        if test[i][0] == result[i] == 1:
            tp += 1
        if test[i][0] == result[i] == 0:
            tn += 1

    print(tp)
    print(tn)

    f1 = (2*tp)/(len(test)+tp-tn)
    print(f1)

    roc_auc(pp, test, result)


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
朴素贝叶斯算法是一种基于概率统计的分类算法,它的核心思想是利用贝叶斯定理计算出每个类别的后验概率,然后选择具有最大后验概率的类别作为分类结果。在这个过程中,朴素贝叶斯假设每个特征之间相互独立,也就是说,每个特征对于分类的贡献是独立的。 在Python中,我们可以使用scikit-learn库来实现朴素贝叶斯算法。具体步骤如下: 1.准备数据:将数据集划分为训练集和测试集。 2.训练模型:使用训练集来训练朴素贝叶斯模型。 3.测试模型:使用测试集来测试模型的准确率。 下面是一个简单的朴素贝叶斯分类器的实现示例: ``` from sklearn.naive_bayes import GaussianNB from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split # 加载数据集 iris = load_iris() # 划分训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3, random_state=0) # 创建朴素贝叶斯分类器对象 gnb = GaussianNB() # 训练模型 gnb.fit(X_train, y_train) # 预测测试集的结果 y_pred = gnb.predict(X_test) # 计算准确率 accuracy = sum(y_pred == y_test) / len(y_test) print("准确率:", accuracy) ``` 这里使用了iris数据集作为示例数据集进行分类。首先,我们使用`train_test_split`函数将数据集划分为训练集和测试集,然后创建一个`GaussianNB`对象,训练模型并使用测试集来测试模型的准确率。最后,我们计算出准确率并将其打印出来。 完整的代码可以在以下链接中找到: https://github.com/Light-City/NaiveBayesClassifier-Python/blob/master/NaiveBayesClassifier.py

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心无旁骛~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值