人工智能6.2 -- 机器学习算法篇(二)决策树和随机森林(含实践)

python,大数据,机器学习,深度学习,计算机视觉

六、机器学习算法篇(二)决策树和随机森林(含实践)

前言

决策树的思想简单地说就是:if-else, if -else,并且关心的是多个条件判断时,用哪个条件先做if判断更好?
1970年代,一个叫昆兰的人找到了用信息论中的熵来度量决策树的决策选择过程。
下面说决策树之前先了解一下熵的概念。

信息量 和 信息熵

对于和本博主一样的通信专业 或学过信息论的小伙伴,对这概念应该很熟悉,来回顾一下。

  1. 信息量
    一个信息的所含的信息量 f 与信息发生率 p 成反比,事件发生的概率越小所含的信息越大(如太阳从西边出来啦!=>奇迹呀!重要信息,信息量大),发生概率越大则信息量越小(明天太阳从东方升起。=>这是废话,谁都知道,信息量小)。所以,香农给出了信息量 f 的定义:
    对于某个符号,它的概率为p时,用这个公式衡量它的信息量:
    在这里插入图片描述
  2. 信息熵
    信源的平均信息量,称为信息熵,用H表示:
    在这里插入图片描述
    信息熵是信息论中用于度量信息量的一个概念。
    注意:
  • (1)当式子中对数的底数为2时,信息熵的单位为bit
  • (2)一个系统越是有序,信息熵就越低;越混乱则信息熵就越高。
    在这里插入图片描述
    熵越小,样本越纯。熵小意味着发生概率要么接近于0,要么接近于1,远离0.5,发生概率若为0.5则分类的不是很清楚,若以此为分类条件则样本不纯。
    比方说:孩子不爱吃饭,是否生病?采样m个统计,孩子不吃饭生病的占比0.5,不生病的也占比0.5(可能就是挑食),若用这个分类,把m个样本分类,说哪些生病了哪些没生病,显示误差大,分类的纯度低。若问:孩子高烧38度,是否是感冒?采样统计,生病的占比0.9,不生病的占比0.1(可能中暑不是感冒,可能是大病不是普通的小感冒),若以此分类误差就小则分类的纯度高。

Gini系数

Gini系数怎么来的?上面写过熵的定义:
在这里插入图片描述
当log底数为 e时,
为了方便计算,将ln pi 替换为 l - pi 。因为由泰勒展开式,可近似地 ln pi = l - pi ,代入上式,即可得到Gini系数,如下:
在这里插入图片描述还可以这样理解:
在这里插入图片描述

例. Gini系数
import numpy as np
import matplotlib.pyplot as plt

if __name__ == "__main__":
    p = np.arange(0.001, 1, 0.001, dtype=np.float)
    gini = 2*p*(1-p)
    h = -(p*np.log2(p) + (1-p)*np.log2(1-p))/2
    err = 1 - np.max(np.vstack((p, 1-p)), 0)
    plt.plot(p, h, 'b-', linewidth=2, label='Entropy')
    plt.plot(p, gini, 'r-', linewidth=2, label='Gini')
    plt.plot(p, err, 'g-', linewidth=2, label='Error')
    plt.grid(True)
    plt.legend(loc='upper left')
    plt.show()

在这里插入图片描述

决策树(Decision Tree) 和 随机森林(Random Forest)

决策树,建立了一个决策树就会建立多个决策树,多个树就形成了随机森林。
决策树好处:训练速度快,可以做集成从而得到更优的决策树。决策树的应用场景很多。如根据人身体状况的一系列特征因素判断病人是否生病。总之,决策树 ------ 是?/否?

  • (1)决策树用于判断人的兴趣爱好:

在这里插入图片描述
像上面那个tree1,我们还可以再做一个树tree2,生成随机森林:
在这里插入图片描述

  • (2)决策树用于信用贷款的场景:

在这里插入图片描述

  • (3)决策树用于样本数据的分类:

在这里插入图片描述
以右上角的分法为例,讲解信息熵:
切法(1)若竖着切,分成左右,
在这里插入图片描述
则左半,随机抓一个点,是红色样本点、绿色样本点的概率:
在这里插入图片描述
切法(2)若横着切,分成上下,(可预测成上半是绿色,下半是红色,忽略现在阴影)
在这里插入图片描述
则上半,随机抓一个点,是红色样本点、绿色样本点的概率:
在这里插入图片描述
由熵的定义,可看出切法(1)的信息熵小,系统更有序;切法(2)的熵值大,更混乱。

  • (4)决策树用于拟合

如下,有100个样本点(x1, y1), (x2, y2), … , (x100, y100),形成一条函数曲线。
在这里插入图片描述
然后将按照下面设计的深度为1的决策树其分成两部分,x<5的点为一部分,x>5的点为一部分。
在这里插入图片描述在这里插入图片描述
对分成的两部分分别求均值。假设x<5的样本点的均值为1.8,x>5的样本点的均值为4.6。
我们设计模型:凡是x<5的样本点对应的y值都预测为1.8(y的估计值),凡是x>5的样本点对应的y值都预测为4.6,所以我们设计的预测模型如下图红线:
在这里插入图片描述
假设再细分,x<5的部分再分为x<3 和 x>3(即3<x<5),不断地细分,决策树的深度也越来越大,从而有了下面的预测模型:
在这里插入图片描述

实际中,效果如下:
在这里插入图片描述
思考:上面的拟合线(红线)为什么有的是斜着上升的而不是竖直上升的呢?
因为对模型的样本点分类时,x<5,

注意:设计预测模型时,决策树的深度并不是越大越好,要防止过拟合!

实战

典例(重点必会):二次函数决策树
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor

if __name__ == "__main__":

    # 准备训练数据
    N = 100
    x = np.random.rand(N) * 6 -3
    x.sort()
    y = x*x
    x = x.reshape(-1, 1) # 转成1列。reshape(m, n)转成m行n列,-1表自动计算。如reshape(m, -1)表转成m行,reshape(-1, m)表转成1列

    mpl.rcParams['font.sans-serif'] = ['SimHei']
    mpl.rcParams['axes.unicode_minus'] = False

    # 决策树深度及其曲线颜色
    depth = [2, 4, 6, 8, 10]
    color = 'rgbmy'

    # 实际值
    plt.figure(facecolor='w') # 显示面板是白色背景
    plt.plot(x, y, 'ro', ms = 5, mec='k', label='实际值') # 样本点:ro红色圆点,ms大小size为5,mec点外圈颜色color为k黑色black

    # 准备测试数据
    x_test = np.linspace(-3, 3, 50).reshape(-1, 1)

    # 构建决策树
    dtr = DecisionTreeRegressor() # dtr = decision tree
    # 循环不同深度情况下决策树的模型,并用之测试数据的输出
    for d, c in zip(depth, color):
#   训练:
        # 设置最大深度(预剪枝)
        dtr.set_params(max_depth=d)
        # 训练决策树
        dtr.fit(x, y)
#   测试:
        # 用模型去预测
        y_hat = dtr.predict(x_test) # predict v.预测
        # 画出模型得到的曲线
        plt.plot(x_test, y_hat, '-', color=c, linewidth=2, markeredgecolor='k', label='Depth=%d' % d)

    # 一些画图的基本参数
    plt.legend(loc='upper center', fontsize=12)
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.grid(b=True, ls=':', color='#606060')
    plt.title('二次函数决策树', fontsize=15)
    plt.tight_layout(2)

    plt.show()

运行结果:
在这里插入图片描述

实战1. 鸢尾花数据的决策树分类
#!/usr/bin/python
# -*- coding:utf-8 -*-

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier # 决策树分类器
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline

# 花萼长度、花萼宽度,花瓣长度,花瓣宽度
iris_feature_E = 'sepal length', 'sepal width', 'petal length', 'petal width'
iris_feature = u'花萼长度', u'花萼宽度', u'花瓣长度', u'花瓣宽度'
iris_class = 'Iris-setosa', 'Iris-versicolor', 'Iris-virginica'


if __name__ == "__main__":
    mpl.rcParams['font.sans-serif'] = [u'SimHei']
    mpl.rcParams['axes.unicode_minus'] = False

    path = '..\\8.Regression\\iris.data'  # 数据文件路径
    # 重点:因为这个文件内容没有头标题,所以标记,否则会少读一行。默认是有标题从第二行开始读数据。
    data = pd.read_csv(path, header=None)
    x = data[range(4)]
    y = pd.Categorical(data[4]).codes # 将DataFrame格式的转化成正常类型的数,方便使用。
    # 为了可视化,仅使用前两列特征
    x = x.iloc[:, :2] # iloc=index localtion, 前两列[... : ... , ... : 2]
    x_train, x_test, y_train, y_test = train_test_split(x, y, train_size=0.7, random_state=1)
    print(y_test.shape)

    # 决策树参数估计。DecisionTreeClassifier()参数说明:
    # min_samples_split = 10:如果该结点包含的样本数目大于10,则(有可能)对其分支
    # min_samples_leaf = 10:若将某结点分支后,得到的每个子结点样本数目都大于10,则完成分支;否则,不进行分支
    model = DecisionTreeClassifier(criterion='entropy') # DecisionTreeClassifier决策树分类器,默认的分类标准criterion='gini'基尼系数,这里用entropy熵作为分类标准。二者差别不太明显
    model.fit(x_train, y_train)
    # 测试模型。测试数据
    y_test_hat = model.predict(x_test)      # y的估计值(y^),读作y hat

    # 输出
    with open('iris.dot', 'w') as f:
        tree.export_graphviz(model, out_file=f)

    # 画图
    N, M = 50, 50  # 横纵各采样多少个值
    x1_min, x2_min = x.min()
    x1_max, x2_max = x.max()
    t1 = np.linspace(x1_min, x1_max, N)
    t2 = np.linspace(x2_min, x2_max, M)
    x1, x2 = np.meshgrid(t1, t2)  # 生成网格采样点
    x_show = np.stack((x1.flat, x2.flat), axis=1)  # 测试点
    print(x_show.shape)

    # # 无意义,只是为了凑另外两个维度
    # # 打开该注释前,确保注释掉x = x[:, :2]
    # x3 = np.ones(x1.size) * np.average(x[:, 2])
    # x4 = np.ones(x1.size) * np.average(x[:, 3])
    # x_test = np.stack((x1.flat, x2.flat, x3, x4), axis=1)  # 测试点

    cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF']) # 背景色
    cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b']) # 样本点的颜色
    y_show_hat = model.predict(x_show)  # 预测值
    print(y_show_hat.shape)
    print(y_show_hat)
    y_show_hat = y_show_hat.reshape(x1.shape)  # 使之与输入的形状相同
    print(y_show_hat)
    plt.figure(facecolor='w')
    plt.pcolormesh(x1, x2, y_show_hat, cmap=cm_light)  # 预测值的显示
    plt.scatter(x_test[0], x_test[1], c=y_test.ravel(), edgecolors='k', s=150, zorder=10, cmap=cm_dark, marker='*')  # 测试数据
    plt.scatter(x[0], x[1], c=y.ravel(), edgecolors='k', s=40, cmap=cm_dark)  # 全部数据
    plt.xlabel(iris_feature[0], fontsize=15)
    plt.ylabel(iris_feature[1], fontsize=15)
    plt.xlim(x1_min, x1_max)
    plt.ylim(x2_min, x2_max)
    plt.grid(True)
    plt.title(u'鸢尾花数据的决策树分类', fontsize=17)
    plt.show()

    # 训练集上的预测结果
    y_test = y_test.reshape(-1)
    print(y_test_hat)
    print(y_test)
    result = (y_test_hat == y_test)   # True则预测正确,False则预测错误
    acc = np.mean(result)
    print('准确度: %.2f%%' % (100 * acc))

    # 过拟合:错误率
    depth = np.arange(1, 15)
    err_list = []
    for d in depth:
        clf = DecisionTreeClassifier(criterion='entropy', max_depth=d)
        clf.fit(x_train, y_train)
        y_test_hat = clf.predict(x_test)  # 测试数据
        result = (y_test_hat == y_test)  # True则预测正确,False则预测错误
        if d == 1:
            print(result)
        err = 1 - np.mean(result)
        err_list.append(err)
        # print d, ' 准确度: %.2f%%' % (100 * err)
        print(d, ' 错误率: %.2f%%' % (100 * err))
    plt.figure(facecolor='w') # 面板背景色白色
    plt.plot(depth, err_list, 'ro-', lw=2) # ro-红色圆点 为样本点
    plt.xlabel(u'决策树深度', fontsize=15)
    plt.ylabel(u'错误率', fontsize=15)
    plt.title(u'决策树深度与过拟合', fontsize=17)
    plt.grid(True)
    plt.show()

运行结果:

C:\Python\Anaconda3\python.exe C:/AI/AnacondaProject/10.RandomForest/10.1.Iris_DecisionTree.py
(45,)
(2500, 2)
(2500,)
[0 0 0 ... 2 2 2]
[[0 0 0 ... 1 1 1]
 [0 0 0 ... 1 1 1]
 [0 0 0 ... 1 1 1]
 ...
 [0 0 0 ... 2 2 2]
 [0 0 0 ... 2 2 2]
 [0 0 0 ... 2 2 2]]
[0 2 2 0 2 2 1 0 0 2 2 0 1 2 1 0 2 1 0 0 1 0 2 0 2 1 0 0 1 1 2 2 2 2 1 0 1
 0 2 1 2 0 1 1 1]
[0 1 1 0 2 1 2 0 0 2 1 0 2 1 1 0 1 1 0 0 1 1 1 0 2 1 0 0 1 2 1 2 1 2 2 0 1
 0 1 2 2 0 2 2 1]
准确度: 60.00%
[False False False  True  True False  True  True  True  True False  True
  True False False  True False False  True  True False False False  True
  True False False  True False  True False  True False  True  True  True
 False  True False  True  True  True  True  True False]
1  错误率: 44.44%
2  错误率: 40.00%
3  错误率: 20.00%
4  错误率: 24.44%
5  错误率: 24.44%
6  错误率: 28.89%
7  错误率: 35.56%
8  错误率: 37.78%
9  错误率: 35.56%
10  错误率: 40.00%
11  错误率: 37.78%
12  错误率: 35.56%
13  错误率: 37.78%
14  错误率: 37.78%

Process finished with exit code 0

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

实战2. 决策树对鸢尾花数据的两特征组合的分类结果
#!/usr/bin/python
# -*- coding:utf-8 -*-

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier


# 'sepal length', 'sepal width', 'petal length', 'petal width'
iris_feature = u'花萼长度', u'花萼宽度', u'花瓣长度', u'花瓣宽度'

if __name__ == "__main__":
    mpl.rcParams['font.sans-serif'] = [u'SimHei']  # 黑体 FangSong/KaiTi
    mpl.rcParams['axes.unicode_minus'] = False

    path = '..\\8.Regression\\iris.data'  # 数据文件路径
    data = pd.read_csv(path, header=None)
    x_prime = data[range(4)]
    y = pd.Categorical(data[4]).codes

    feature_pairs = [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]
    plt.figure(figsize=(10, 9), facecolor='#FFFFFF')
    for i, pair in enumerate(feature_pairs):
        # 准备数据
        x = x_prime[pair]

        # 决策树学习
        clf = DecisionTreeClassifier(criterion='entropy', min_samples_leaf=3)
        clf.fit(x, y)

        # 画图
        N, M = 500, 500  # 横纵各采样多少个值
        x1_min, x2_min = x.min()
        x1_max, x2_max = x.max()
        t1 = np.linspace(x1_min, x1_max, N)
        t2 = np.linspace(x2_min, x2_max, M)
        x1, x2 = np.meshgrid(t1, t2)  # 生成网格采样点
        x_test = np.stack((x1.flat, x2.flat), axis=1)  # 测试点

        # 训练集上的预测结果
        y_hat = clf.predict(x)
        y = y.reshape(-1)
        c = np.count_nonzero(y_hat == y)    # 统计预测正确的个数
        print('特征:  ', iris_feature[pair[0]], ' + ', iris_feature[pair[1]],)
        print('\t预测正确数目:', c,)
        print('\t准确率: %.2f%%' % (100 * float(c) / float(len(y))))

        # 显示
        cm_light = mpl.colors.ListedColormap(['#A0FFA0', '#FFA0A0', '#A0A0FF'])
        cm_dark = mpl.colors.ListedColormap(['g', 'r', 'b'])
        y_hat = clf.predict(x_test)  # 预测值
        y_hat = y_hat.reshape(x1.shape)  # 使之与输入的形状相同
        plt.subplot(2, 3, i+1)
        plt.pcolormesh(x1, x2, y_hat, cmap=cm_light)  # 预测值
        plt.scatter(x[pair[0]], x[pair[1]], c=y, edgecolors='k', cmap=cm_dark)  # 样本
        plt.xlabel(iris_feature[pair[0]], fontsize=14)
        plt.ylabel(iris_feature[pair[1]], fontsize=14)
        plt.xlim(x1_min, x1_max)
        plt.ylim(x2_min, x2_max)
        plt.grid()
    plt.suptitle(u'决策树对鸢尾花数据的两特征组合的分类结果', fontsize=18)
    plt.tight_layout(2)
    plt.subplots_adjust(top=0.92)
    plt.show()

运行结果:

C:\Python\Anaconda3\python.exe C:/AI/AnacondaProject/10.RandomForest/10.2.Iris_DecisionTree_Enum.py
特征:   花萼长度  +  花萼宽度
	预测正确数目: 123
	准确率: 82.00%
特征:   花萼长度  +  花瓣长度
	预测正确数目: 145
	准确率: 96.67%
特征:   花萼长度  +  花瓣宽度
	预测正确数目: 144
	准确率: 96.00%
特征:   花萼宽度  +  花瓣长度
	预测正确数目: 143
	准确率: 95.33%
特征:   花萼宽度  +  花瓣宽度
	预测正确数目: 145
	准确率: 96.67%
特征:   花瓣长度  +  花瓣宽度
	预测正确数目: 147
	准确率: 98.00%

Process finished with exit code 0

在这里插入图片描述

实战3. 不同深度的决策树比较
#!/usr/bin/python
# -*- coding:utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor


if __name__ == "__main__":
    N = 100
    x = np.random.rand(N) * 6 - 3     # [-3,3)
    x.sort()
    y = np.sin(x) + np.random.randn(N) * 0.05
    print(y)
    x = x.reshape(-1, 1)  # 转置后,得到N个样本,每个样本都是1维的
    print(x)

    dt = DecisionTreeRegressor(criterion='mse', max_depth=9)
    dt.fit(x, y)
    x_test = np.linspace(-3, 3, 50).reshape(-1, 1)
    y_hat = dt.predict(x_test)
    plt.plot(x, y, 'ro', ms=2, label='Actual') # 实际:红色点,ms控制点的大小size
    plt.plot(x_test, y_hat, 'g-', linewidth=2, label='Predict') # 预测:绿色线
    plt.legend(loc='upper left')
    plt.grid()
    plt.show()

    # 比较决策树的深度影响
    depth = [2, 4, 6, 8, 10]
    clr = 'rgbmy'
    dtr = DecisionTreeRegressor(criterion='mse')
    plt.plot(x, y, 'ko', ms=6, label='Actual')
    x_test = np.linspace(-3, 3, 50).reshape(-1, 1)
    for d, c in zip(depth, clr):
        dtr.set_params(max_depth=d)
        dtr.fit(x, y)
        y_hat = dtr.predict(x_test)
        plt.plot(x_test, y_hat, '-', color=c, linewidth=2, label='Depth=%d' % d)
    plt.legend(loc='upper left')
    plt.grid(b=True)
    plt.show()

运行结果:

C:\Python\Anaconda3\python.exe C:/AI/AnacondaProject/10.RandomForest/10.3.DecisionTreeRegressor.py
[-0.27497777 -0.39866815 -0.64748857 -0.9128954  -0.78432897 -0.84038777
 -0.82577223 -0.95190435 -1.01756317 -0.98509855 -1.00065722 -0.99928047
 -1.07139114 -1.06784447 -1.042834   -1.0608263  -1.05213915 -0.93913138
 -0.98112229 -0.98460773 -0.9555358  -1.03465139 -0.88633535 -0.95783224
 -0.98863757 -0.91670748 -0.87833522 -0.79367493 -0.81166087 -0.7300355
 -0.71037816 -0.69674226 -0.7124849  -0.54716052 -0.57468564 -0.62216022
 -0.44525448 -0.40682577 -0.40560921 -0.43823563 -0.28329833 -0.23163354
 -0.18659963 -0.16060826 -0.11101602 -0.09066416  0.06509584  0.16406084
  0.19512072  0.14745489  0.35553146  0.39626206  0.36260649  0.4180749
  0.44613007  0.42589551  0.53252183  0.60937969  0.69920334  0.73875954
  0.72482714  0.7948256   0.8167109   0.84773602  0.78976302  0.9229774
  0.88793966  0.90497241  0.95064763  0.89482579  0.82430182  0.8416387
  0.92238887  0.99963673  1.00531621  0.99279825  1.01407016  0.9153773
  1.12029327  0.88317005  1.0082525   0.94889197  0.94999445  0.95270944
  0.88698381  0.92040994  0.85282117  0.78993407  0.67662416  0.75842476
  0.72349198  0.58429482  0.60301675  0.52202472  0.54467787  0.42091256
  0.44858715  0.45242071  0.19375439  0.23811981]
[[-2.86978022]
 [-2.78827194]
 [-2.47164969]
 [-2.24111077]
 [-2.20815034]
 [-2.20290187]
 [-2.06961306]
 [-2.06912637]
 [-1.92262272]
 [-1.82399321]
 [-1.71543685]
 [-1.6777186 ]
 [-1.64142245]
 [-1.4896309 ]
 [-1.46618904]
 [-1.45628588]
 [-1.45305959]
 [-1.32133088]
 [-1.32080154]
 [-1.30935308]
 [-1.29126741]
 [-1.27737413]
 [-1.20986465]
 [-1.12273528]
 [-1.10458377]
 [-1.08238499]
 [-1.07552708]
 [-1.04893242]
 [-0.95635723]
 [-0.84169437]
 [-0.78729994]
 [-0.78226779]
 [-0.73656101]
 [-0.62277393]
 [-0.6121653 ]
 [-0.5202867 ]
 [-0.47385285]
 [-0.43823151]
 [-0.43321273]
 [-0.38149354]
 [-0.23006213]
 [-0.18396616]
 [-0.17916745]
 [-0.1749263 ]
 [-0.17091459]
 [-0.07523462]
 [ 0.13969686]
 [ 0.19233009]
 [ 0.24482859]
 [ 0.25798492]
 [ 0.3464307 ]
 [ 0.41431783]
 [ 0.42820749]
 [ 0.4303701 ]
 [ 0.46586533]
 [ 0.53716404]
 [ 0.61683432]
 [ 0.63562271]
 [ 0.73059562]
 [ 0.75608801]
 [ 0.80165918]
 [ 0.83259093]
 [ 0.93132083]
 [ 0.96538883]
 [ 1.00536365]
 [ 1.01741339]
 [ 1.05577794]
 [ 1.1259512 ]
 [ 1.14009872]
 [ 1.17886023]
 [ 1.1868475 ]
 [ 1.1957344 ]
 [ 1.21799977]
 [ 1.40219919]
 [ 1.49229149]
 [ 1.50306054]
 [ 1.58300576]
 [ 1.6110291 ]
 [ 1.72605891]
 [ 1.76478901]
 [ 1.76981066]
 [ 1.81422539]
 [ 1.82758535]
 [ 1.83341238]
 [ 1.84276767]
 [ 1.96376659]
 [ 2.16258092]
 [ 2.27454853]
 [ 2.33261315]
 [ 2.3523572 ]
 [ 2.36417343]
 [ 2.47828994]
 [ 2.49392901]
 [ 2.50292793]
 [ 2.58822646]
 [ 2.5887438 ]
 [ 2.6393389 ]
 [ 2.6839569 ]
 [ 2.87224916]
 [ 2.92479839]]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值