作业一 ·感知机实现二分类

感知机

一、感知机的基本原理

1.1 感知机学习的数学模型

感知机是根据输入实例的特征向量 x x x对其进行二类分类的线性分类模型,针对训练集数据:
T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } T = \{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\} T={(x1,y1),(x2,y2),,(xN,yN)}
其中, x i ∈ R n , y i ∈ { − 1 , 1 } , i = 1 , 2 , … , N x_i \in R^n,y_i \in \{-1,1\},i=1,2,\dots,N xiRn,yi{1,1},i=1,2,,N,则有感知机模型函数:

f ( x ) = sign ⁡ ( w ⋅ x + b ) f(x)=\operatorname{sign}(w \cdot x+b) f(x)=sign(wx+b)

感知机模型对应于输入空间(特征空间)中的分离超平面 w ⋅ x + b = 0 w \cdot x+b=0 wx+b=0。其中,w是一个 n n n维向量,与 x x x维度相同, b b bd为一常数。

1.2 感知机的损失函数

感知机学习的策略是极小化损失函数:
min ⁡ w , b L ( w , b ) = − ∑ x i ∈ M y i ( w ⋅ x i + b ) \min _{w, b} L(w, b)=-\sum_{x_{i} \in M} y_{i}\left(w \cdot x_{i}+b\right) w,bminL(w,b)=xiMyi(wxi+b)

M为误分类点的集合,损失函数对应于误分类点到分离超平面的总距离。

感知机学习算法是误分类驱动的,具体是采用随机梯度下降法。首先,任意选取一个超平面 w 0 , b 0 w_0,b_0 w0,b0,然后用梯度下降法不断极小化目标函数。极小化过程中不是一次使得集合 M M M中所有的误分类点的梯度下降,而是一次随机选取一个误分类点进行梯度下降。

对损失函数求梯度,有:
∇ w L ( w , b ) = − ∑ x i ∈ M y i x i ∇ b L ( w , b ) = − ∑ x i ∈ M y i \nabla_wL(w,b) = -\sum_{x_i \in M}y_ix_i\\ \nabla_bL(w,b) = -\sum_{x_i \in M}y_i wL(w,b)=xiMyixibL(w,b)=xiMyi
随机选取一个误分类点 ( x i , y i ) (x_i,y_i) (xi,yi),对 w , b w,b w,b进行更新

w ← w + η y i x i b ← b + η y i w \leftarrow w + \eta y_ix_i\\ b \leftarrow b + \eta y_i ww+ηyixibb+ηyi

式中, η ( 0 ≤ η ≤ 1 ) \eta (0 \le \eta \le 1) η(0η1)是学习率。这样通过迭代可以使得损失函数 L ( w , b ) L(w,b) L(w,b)不断减少,直至为0

感知机学习算法是基于随机梯度下降法的对损失函数的最优化算法,有原始形式和对偶形式。

1.3 感知机学习算法的原始形式

原始形式中,首先任意选取一个超平面,然后用梯度下降法不断极小化目标函数。在这个过程中一次随机选取一个误分类点使其梯度下降。整个算法流程如下:

针对训练集数据: T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } T = \{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\} T={(x1,y1),(x2,y2),,(xN,yN)}
其中, x i ∈ R n , y i ∈ { − 1 , 1 } , i = 1 , 2 , … , N x_i \in R^n,y_i \in \{-1,1\},i=1,2,\dots,N xiRn,yi{1,1},i=1,2,,N

有感知机模型函数: f ( x ) = sign ⁡ ( w ⋅ x + b ) f(x)=\operatorname{sign}(w \cdot x+b) f(x)=sign(wx+b)

  1. 选取初始值 w 0 , b 0 w_0,b_0 w0,b0(一般都取0作为初始值)
  2. 在训练集中选取数据 ( x i , y i ) (x_i,y_i) (xi,yi)
  3. 如果 y i ( w ⋅ x i + b ) ≤ 0 y_i(w \cdot x_i + b) \le 0 yi(wxi+b)0
    w ← w + η y i x i b ← b + η y i w \leftarrow w + \eta y_ix_i\\ b \leftarrow b + \eta y_i ww+ηyixibb+ηyi
  4. 转至步骤2,直至训练集中没有误分类点

1.4 感知机学习算法的对偶形式

对偶性的基本思想是希望将 w , b w,b w,b表示为实例 x i , y i x_i,y_i xiyi线性组合的形式。已知在感知机原始形式中,对误分类点 ( x i , y i ) (x_i,y_i) (xi,yi)参数迭代形式:
w ← w + η y i x i b ← b + η y i w \leftarrow w + \eta y_ix_i\\ b \leftarrow b + \eta y_i ww+ηyixibb+ηyi
现在假设初始值 w 0 , b 0 w_0,b_0 w0,b0均为0,则可修改迭代公式为:
w ← η y i x i b ← η y i w \leftarrow \eta y_ix_i\\ b \leftarrow \eta y_i wηyixibηyi
则可知,第n次迭代时, w , b w,b w,b关于 ( x i , y i ) (x_i,y_i) (xi,yi)的增量分别是 n ⋅ η y i x i n \cdot \eta y_ix_i nηyixi n ⋅ η y i n \cdot \eta y_i nηyi

假设 α i = n ⋅ η \alpha_i=n \cdot \eta αi=nη,得增量表达式: α i y i x i \alpha_i y_ix_i αiyixi α i y i \alpha_i y_i αiyi因此最后学习到得 w , b w,b w,b可以用下式表达:

w = ∑ i = 1 N α i y i x i b = ∑ i = 1 N α i y i w = \sum_{i=1}^N \alpha_iy_ix_i\\ b = \sum_{i=1}^N \alpha_iy_i w=i=1Nαiyixib=i=1Nαiyi
其中,N时迭代次数。对于每个实例点,如果其使得参数更新的次数越多,意味着其离超平面越近,即其越难被分类。

整个算法流程如下:

针对训练集数据: T = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x N , y N ) } T = \{(x_1,y_1),(x_2,y_2),\dots,(x_N,y_N)\} T={(x1,y1),(x2,y2),,(xN,yN)}
其中, x i ∈ R n , y i ∈ { − 1 , 1 } , i = 1 , 2 , … , N x_i \in R^n,y_i \in \{-1,1\},i=1,2,\dots,N xiRn,yi{1,1},i=1,2,,N

有感知机模型函数: f ( x ) = sign ⁡ ( ∑ i = 1 N α i y i x i ⋅ x + b ) f(x)=\operatorname{sign}( \sum_{i=1}^N \alpha_iy_ix_i \cdot x+b) f(x)=sign(i=1Nαiyixix+b)

  1. 选取初始值 α 0 , b 0 \alpha_0,b_0 α0,b0(一般都取0作为初始值)
  2. 在训练集中选取数据 ( x i , y i ) (x_i,y_i) (xi,yi)
  3. 如果 y i ( ∑ j = 1 N α j y j x j ⋅ x i + b ) ≤ 0 y_i(\sum_{j=1}^N \alpha_jy_jx_j \cdot x_i+b) \le 0 yi(j=1Nαjyjxjxi+b)0
    α j ← α j + η b ← b + η y i \alpha_j \leftarrow \alpha_j + \eta\\ b \leftarrow b + \eta y_i αjαj+ηbb+ηyi
  4. 转至步骤2,直至训练集中没有误分类点

在本算法中,训练集实例仅以内积的形式出现。为了方便,可以预先将训练集中实例间的内积计算出来并以矩阵的形式存储,这就是所谓的Gram矩阵

G = ∥ x i ⋅ x j ∥ N × N G = \|x_i \cdot x_j \|_{N \times N} G=xixjN×N

1.5 感知机学习算法的收敛性

当训练数据集线性可分时,感知机学习算法是收敛的。感知机算法在训练数据集上的误分类次数 k k k满足不等式:

k ⩽ ( R γ ) 2 k \leqslant\left(\frac{R}{\gamma}\right)^{2} k(γR)2

当训练数据集线性可分时,感知机学习算法存在无穷多个解,其解由于不同的初值或不同的迭代顺序而可能有所不同。

二、应用实例

计划需要完成四种分类问题:

  1. 使用感知机原始形式解决二维分类问题(可视化)
  2. 使用感知机对偶形式解决二维分类问题(可视化)
  3. 使用感知机原始形式算法解决四维分类问题
  4. 使用感知机对偶形式解决四维分类问题

2.1 使用感知机原始形式解决二维分类问题

# 导入所需库
import csv
import numpy as np
from matplotlib import pyplot as plt
2.1.1 Iris数据提取、抽取、分割和可视化
# 样本数据的抽取
with open('iris.data') as csv_file:
    data = list(csv.reader(csv_file, delimiter=','))

label_map = {
    'Iris-setosa': -1,
    'Iris-versicolor': 1,
}
# 感知机解决二分类问题的标签一定为1或-1(因为sign函数),别为正实例点和负实例点,

# 抽取样本
X = np.array([[float(x) for x in s[:-1]] for s in data[:100]], np.float32) # X是一个四维数据,此处我们只去其两维
Y = np.array([[label_map[s[-1]]] for s in data[:100]], np.float32) # 

# 样本可视化
plt.scatter(X[:50, 0], X[:50, 1], label='Iris-setosa') # 前50个数据点为1类
plt.scatter(X[50:, 0], X[50:, 1], label='Iris-versicolor') # 后50个数据点为1类
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()

# 分割数据集

# 将数据集按照8:2划分为训练集和测试集
train_idx = np.random.choice(100, 80, replace=False)

test_idx = np.array(list(set(range(100)) - set(train_idx)))

# train-训练集 test-测试集
X_train, Y_train = X[train_idx], Y[train_idx]
X_test, Y_test = X[test_idx], Y[test_idx]

请添加图片描述

2.1.2 训练
# 感知机学习类(原始形式)

class PerceptionMethod(object):  # 定义 感知机学习类
    def __init__(self, X, Y, eta):  # 类中参数是 X,Y(X,Y)均为numpy数组,eta,eta是学习率
        if X.shape[0] != Y.shape[0]:  # 要求X,Y中的数目一样,即一个x对应一个y,否则返回错误
            raise ValueError('Error,X and Y must be same when axis=0 ')
        else:  # 在类中储存参数
            self.X = X
            self.Y = Y
            self.eta = eta

    def ini_Per(self):  # 感知机的原始形式
        weight = np.zeros(self.X.shape[1])  # 初始化weight,b,np.zeros(size)表示生成0矩阵,weight的数据类型是array
        b = 0
        number = 0  # 记录训练次数
        mistake = True  # mistake是变量用来说明分类是否有错误
        while mistake is True:  # 当有错时
            mistake = False  # 开始下一轮纠错前需要将mistake变为true,一来判断这一轮是否有错误
            for index in range(self.X.shape[0]):  # 循环开始
                if self.Y[index] * (weight @ self.X[index] + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
                    weight += self.eta * self.Y[index] * self.X[index]  # 进行更新weight,b
                    b += self.eta * self.Y[index]
                    number += 1
                    mistake = True  # 此轮检查出错误,表明mistake为true,进行下列一轮
                    break  # 找出第一个错误后调出循环
        return weight, b  # 返回值

# 测试验证函数

def test(weight,b,X,Y):
    num = 0 # 错误分类个数
    for index in range(X.shape[0]):  # 循环开始
        if Y[index] * (weight @ X[index] + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
            num += 1
    return 1- num/(X.shape[0])

# 只使用训练集的两个维度

X_train_2 = X_train[:,0:2]
X_test_2 = X_test[:,0:2]

# 训练

PER = PerceptionMethod(X_train_2, Y_train, 1) # 类初始化
weight,b = PER.ini_Per()

# 训练集可视化

plt.scatter(X[:50, 0], X [:50, 1], label='Iris-setosa') # 前50个数据点为1类
plt.scatter(X[50:, 0], X [50:, 1], label='Iris-versicolor') # 后50个数据点为1类
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()

# 可视化分类平面
x = np.linspace(3, 8, 100)
y = -(weight[0]*x + b)/weight[1]
plt.plot(x, y)

请添加图片描述

2.1.3 测试
# 测试集验证

acc = test(weight,b,X_test_2,Y_test)
print('Test accuracy is', acc)
Test accuracy is 1.0

2.2 使用感知机对偶形式解决二维分类问题

2.2.1 Iris数据提取、抽取、分割和可视化

在上文已经完成

2.2.2 训练
# 感知机学习类(对偶形式)

import numpy as np
from matplotlib import pyplot as plt
class PerceptionMethod(object):  # 定义 感知机学习类
    def __init__(self, X, Y, eta):  # 类中参数是 X,Y(X,Y)均为numpy数组,eta,eta是学习率
        if X.shape[0] != Y.shape[0]:  # 要求X,Y中的数目一样,即一个x对应一个y,否则返回错误
            raise ValueError('Error,X and Y must be same when axis=0 ')
        else:  # 在类中储存参数
            self.X = X
            self.Y = Y
            self.eta = eta
            G = np.zeros((X.shape[0],X.shape[0]))
            for i in range(X.shape[0]):
                for j in range(X.shape[0]):
                    G[i,j] = X[i] @ X[j]
            self.G = G
    def ini_Per(self):  # 感知机的原始形式
        a = np.zeros(self.X.shape[0])  # 初始化weight,b,np.zeros(size)表示生成0矩阵,weight的数据类型是array
        b = 0
        number = 0  # 记录训练次数
        mistake = True  # mistake是变量用来说明分类是否有错误
        while mistake is True:  # 当有错时
            mistake = False  # 开始下一轮纠错前需要将mistake变为true,一来判断这一轮是否有错误
            for index in range(self.X.shape[0]):  # 循环开始
                tmp = 0
                for j in range(self.X.shape[0]):
                    tmp += a[j]*self.Y[j]*self.G[index,j]
                if self.Y[index] * (tmp + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
                    a[index] += self.eta   # 进行更新weight,b
                    b += self.eta * self.Y[index]
                    number += 1
                    mistake = True  # 此轮检查出错误,表明mistake为true,进行下列一轮
                    break  # 找出第一个错误后调出循环
        return a, b  # 返回值

# 测试验证函数

def test(weight,b,X,Y):
    num = 0 # 错误分类个数
    for index in range(X.shape[0]):  # 循环开始
        if Y[index] * (weight @ X[index] + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
            num += 1
    return 1- num/(X.shape[0])

# 只使用训练集的两个维度

X_train_2 = X_train[:,0:2]
X_test_2 = X_test[:,0:2]

# 训练

PER = PerceptionMethod(X_train_2, Y_train, 1) # 类初始化
a,b = PER.ini_Per()
weight = 0
for i in range(X_train_2.shape[0]):  # 循环开始
    weight += a[i]*X_train_2[i]*Y_train[i]

# 训练集可视化

plt.scatter(X[:50, 0], X [:50, 1], label='Iris-setosa') # 前50个数据点为1类
plt.scatter(X[50:, 0], X [50:, 1], label='Iris-versicolor') # 后50个数据点为1类
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()

# 可视化分类平面
x = np.linspace(3, 8, 100)
y = -(weight[0]*x + b)/weight[1]
plt.plot(x, y)

请添加图片描述

2.2.3 测试集验证
acc = test(weight,b,X_test_2,Y_test)
print('Test accuracy is', acc)
Test accuracy is 1.0

2.3 使用感知机原始形式解决四维分类问题

2.3.1 Iris数据提取、抽取、分割和可视化

在上文已经完成

2.3.2 训练
# 感知机学习类(原始形式)

class PerceptionMethod(object):  # 定义 感知机学习类
    def __init__(self, X, Y, eta):  # 类中参数是 X,Y(X,Y)均为numpy数组,eta,eta是学习率
        if X.shape[0] != Y.shape[0]:  # 要求X,Y中的数目一样,即一个x对应一个y,否则返回错误
            raise ValueError('Error,X and Y must be same when axis=0 ')
        else:  # 在类中储存参数
            self.X = X
            self.Y = Y
            self.eta = eta

    def ini_Per(self):  # 感知机的原始形式
        weight = np.zeros(self.X.shape[1])  # 初始化weight,b,np.zeros(size)表示生成0矩阵,weight的数据类型是array
        b = 0
        number = 0  # 记录训练次数
        mistake = True  # mistake是变量用来说明分类是否有错误
        while mistake is True:  # 当有错时
            mistake = False  # 开始下一轮纠错前需要将mistake变为true,一来判断这一轮是否有错误
            for index in range(self.X.shape[0]):  # 循环开始
                if self.Y[index] * (weight @ self.X[index] + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
                    weight += self.eta * self.Y[index] * self.X[index]  # 进行更新weight,b
                    b += self.eta * self.Y[index]
                    number += 1
                    mistake = True  # 此轮检查出错误,表明mistake为true,进行下列一轮
                    break  # 找出第一个错误后调出循环
        return weight, b  # 返回值

# 测试验证函数

def test(weight,b,X,Y):
    num = 0 # 错误分类个数
    for index in range(X.shape[0]):  # 循环开始
        if Y[index] * (weight @ X[index] + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
            num += 1
    return 1- num/(X.shape[0])

# 使用训练集的四个维度

# 训练

PER = PerceptionMethod(X_train, Y_train, 1) # 类初始化
weight,b = PER.ini_Per()
2.3.3 测试
acc = test(weight,b,X_test,Y_test)
print('Test accuracy is', acc)
Test accuracy is 1.0

2.4 使用感知机对偶形式解决四维分类问题

2.4.1 Iris数据提取、抽取、分割和可视化

在上文已经完成

2.4.2 训练
# 感知机学习类(对偶形式)

import numpy as np
from matplotlib import pyplot as plt
class PerceptionMethod(object):  # 定义 感知机学习类
    def __init__(self, X, Y, eta):  # 类中参数是 X,Y(X,Y)均为numpy数组,eta,eta是学习率
        if X.shape[0] != Y.shape[0]:  # 要求X,Y中的数目一样,即一个x对应一个y,否则返回错误
            raise ValueError('Error,X and Y must be same when axis=0 ')
        else:  # 在类中储存参数
            self.X = X
            self.Y = Y
            self.eta = eta
            G = np.zeros((X.shape[0],X.shape[0]))
            for i in range(X.shape[0]):
                for j in range(X.shape[0]):
                    G[i,j] = X[i] @ X[j]
            self.G = G
    def ini_Per(self):  # 感知机的原始形式
        a = np.zeros(self.X.shape[0])  # 初始化weight,b,np.zeros(size)表示生成0矩阵,weight的数据类型是array
        b = 0
        number = 0  # 记录训练次数
        mistake = True  # mistake是变量用来说明分类是否有错误
        while mistake is True:  # 当有错时
            mistake = False  # 开始下一轮纠错前需要将mistake变为true,一来判断这一轮是否有错误
            for index in range(self.X.shape[0]):  # 循环开始
                tmp = 0
                for j in range(self.X.shape[0]):
                    tmp += a[j]*self.Y[j]*self.G[index,j]
                if self.Y[index] * (tmp + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
                    a[index] += self.eta   # 进行更新weight,b
                    b += self.eta * self.Y[index]
                    number += 1
                    mistake = True  # 此轮检查出错误,表明mistake为true,进行下列一轮
                    break  # 找出第一个错误后调出循环
        return a, b  # 返回值

# 测试验证函数

def test(weight,b,X,Y):
    num = 0 # 错误分类个数
    for index in range(X.shape[0]):  # 循环开始
        if Y[index] * (weight @ X[index] + b) <= 0:  # 错误判断条件 这里@表示的是矩阵运算的乘法
            num += 1
    return 1- num/(X.shape[0])

# 使用训练集的四个维度

# 训练

PER = PerceptionMethod(X_train, Y_train, 1) # 类初始化
a,b = PER.ini_Per()
weight = 0
for i in range(X_train.shape[0]):  # 循环开始
    weight += a[i]*X_train[i]*Y_train[i]
print(weight)

[-1.3999987 -5.1000004  8.         4.1000004]
2.4.3 测试
acc = test(weight,b,X_test,Y_test)
print('Test accuracy is', acc)
Test accuracy is 1.0
  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
机器学习是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。它专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。机器学习是人工智能的核心,是使计算机具有智能的根本途径。 机器学习的发展历程可以追溯到20世纪50年代,当时Arthur Samuel在IBM开发了第一个自我学习程序,一个西洋棋程序,这标志着机器学习的起步。随后,Frank Rosenblatt发明了第一个人工神经网络模型——感知机。在接下来的几十年里,机器学习领域取得了许多重要的进展,包括最近邻算法、决策树、随机森林、深度学习等算法和技术的发展。 机器学习有着广泛的应用场景,如自然语言处理、物体识别和智能驾驶、市场营销和个性化推荐等。通过分析大量的数据,机器学习可以帮助我们更好地理解和解决各种复杂的问题。例如,在自然语言处理领域,机器学习技术可以实现机器翻译、语音识别、文本分类和情感分析等功能;在物体识别和智能驾驶领域,机器学习可以通过训练模型来识别图像和视频中的物体,并实现智能驾驶等功能;在市场营销领域,机器学习可以帮助企业分析用户的购买行为和偏好,提供个性化的产品推荐和定制化的营销策略。 总的来说,机器学习是一个快速发展且充满潜力的领域,它正在不断地改变我们的生活和工作方式。随着技术的不断进步和应用场景的不断扩展,相信机器学习将会在未来发挥更加重要的作用。
以下是一个简单的多层感知机实现二分类的代码示例,使用 PyTorch 框架: ```python import torch import torch.nn as nn import torch.optim as optim # 定义多层感知机模型 class MLP(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(MLP, self).__init__() self.fc1 = nn.Linear(input_size, hidden_size) self.relu = nn.ReLU() self.fc2 = nn.Linear(hidden_size, output_size) self.sigmoid = nn.Sigmoid() def forward(self, x): x = self.fc1(x) x = self.relu(x) x = self.fc2(x) x = self.sigmoid(x) return x # 准备训练数据 x_train = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]) y_train = torch.tensor([[0.0], [1.0], [1.0], [0.0]]) # 定义模型参数 input_size = 2 hidden_size = 4 output_size = 1 lr = 0.1 epochs = 1000 # 初始化模型和优化器 model = MLP(input_size, hidden_size, output_size) optimizer = optim.SGD(model.parameters(), lr=lr) # 训练模型 for epoch in range(epochs): # 前向传播 y_pred = model(x_train) # 计算损失 loss_fn = nn.MSELoss() loss = loss_fn(y_pred, y_train) # 反向传播 optimizer.zero_grad() loss.backward() optimizer.step() # 每 100 步输出一次损失 if epoch % 100 == 0: print("Epoch {}, Loss {}".format(epoch, loss.item())) # 测试模型 x_test = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]]) y_test = model(x_test) print(y_test) ``` 上述代码实现了一个包含一个隐藏层的多层感知机模型,使用均方误差作为损失函数,使用随机梯度下降算法进行优化。训练完毕后,对模型进行测试并输出预测结果。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值