深度学习 100 题(转)

深度学习 100 题(转)

https://github.com/wizardforcel/data-science-notebook/tree/master/dl/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0_100_%E9%A2%98

来源:BAT面试1000题

1、梯度下降算法的正确步骤是什么?

a.计算预测值和真实值之间的误差

b.重复迭代,直至得到网络权重的最佳值

c.把输入传入网络,得到输出值

d.用随机值初始化权重和偏差

e.对每一个产生误差的神经元,调整相应的(权重)值以减小误差

A.abcde B.edcba C.cbaed D.dcaeb

解析:正确答案D,考查知识点-深度学习。

2、已知:

- 大脑是有很多个叫做神经元的东西构成,神经网络是对大脑的简单的数学表达。

- 每一个神经元都有输入、处理函数和输出。

- 神经元组合起来形成了网络,可以拟合任何函数。

- 为了得到最佳的神经网络,我们用梯度下降方法不断更新模型

给定上述关于神经网络的描述,什么情况下神经网络模型被称为深度学习模型?

A.加入更多层,使神经网络的深度增加

B.有维度更高的数据

C.当这是一个图形识别的问题时

D.以上都不正确

解析:正确答案A,更多层意味着网络更深。没有严格的定义多少层的模型才叫深度模型,目前如果有超过2层的隐层,那么也可以及叫做深度模型。

3、训练CNN时,可以对输入进行旋转、平移、缩放等预处理提高模型泛化能力。这么说是对,还是不对?

A.对 B.不对

解析:对。如寒sir所说,训练CNN时,可以进行这些操作。当然也不一定是必须的,只是data augmentation扩充数据后,模型有更多数据训练,泛化能力可能会变强。

4、下面哪项操作能实现跟神经网络中Dropout的类似效果?

A.Boosting B.Bagging C.Stacking D.Mapping

解析:正确答案B。Dropout可以认为是一种极端的Bagging,每一个模型都在单独的数据上训练,同时,通过和其他模型对应参数的共享,从而实现模型参数的高度正则化。

5、下列哪一项在神经网络中引入了非线性?

A.随机梯度下降

B.修正线性单元(ReLU)

C.卷积函数

D.以上都不正确

解析:正确答案B。修正线性单元是非线性的激活函数。

6、CNN的卷积核是单层的还是多层的?

解析:

一般而言,深度卷积网络是一层又一层的。层的本质是特征图, 存贮输入数据或其中间表示值。一组卷积核则是联系前后两层的网络参数表达体, 训练的目标就是每个卷积核的权重参数组。

描述网络模型中某层的厚度,通常用名词通道channel数或者特征图feature map数。不过人们更习惯把作为数据输入的前层的厚度称之为通道数(比如RGB三色图层称为输入通道数为3),把作为卷积输出的后层的厚度称之为特征图数。

卷积核(filter)一般是3D多层的,除了面积参数, 比如3x3之外, 还有厚度参数H(2D的视为厚度1). 还有一个属性是卷积核的个数N。

卷积核的厚度H, 一般等于前层厚度M(输入通道数或feature map数). 特殊情况M > H。

卷积核的个数N, 一般等于后层厚度(后层feature maps数,因为相等所以也用N表示)。

卷积核通常从属于后层,为后层提供了各种查看前层特征的视角,这个视角是自动形成的。

卷积核厚度等于1时为2D卷积,对应平面点相乘然后把结果加起来,相当于点积运算;

卷积核厚度大于1时为3D卷积,每片分别平面点求卷积,然后把每片结果加起来,作为3D卷积结果;1x1卷积属于3D卷积的一个特例,有厚度无面积, 直接把每片单个点乘以权重再相加。

归纳之,卷积的意思就是把一个区域,不管是一维线段,二维方阵,还是三维长方块,全部按照卷积核的维度形状,对应逐点相乘再求和,浓缩成一个标量值也就是降到零维度,作为下一层的一个feature map的一个点的值!

可以比喻一群渔夫坐一个渔船撒网打鱼,鱼塘是多层水域,每层鱼儿不同。

船每次移位一个stride到一个地方,每个渔夫撒一网,得到收获,然后换一个距离stride再撒,如此重复直到遍历鱼塘。

A渔夫盯着鱼的品种,遍历鱼塘后该渔夫描绘了鱼塘的鱼品种分布;

B渔夫盯着鱼的重量,遍历鱼塘后该渔夫描绘了鱼塘的鱼重量分布;

还有N-2个渔夫,各自兴趣各干各的;

最后得到N个特征图,描述了鱼塘的一切!

2D卷积表示渔夫的网就是带一圈浮标的渔网,只打上面一层水体的鱼;

3D卷积表示渔夫的网是多层嵌套的渔网,上中下层水体的鱼儿都跑不掉;

1x1卷积可以视为每次移位stride,甩钩钓鱼代替了撒网;

下面解释一下特殊情况的 M > H:

实际上,除了输入数据的通道数比较少之外,中间层的feature map数很多,这样中间层算卷积会累死计算机(鱼塘太深,每层鱼都打,需要的鱼网太重了)。所以很多深度卷积网络把全部通道/特征图划分一下,每个卷积核只看其中一部分(渔夫A的渔网只打捞深水段,渔夫B的渔网只打捞浅水段)。这样整个深度网络架构是横向开始分道扬镳了,到最后才又融合。这样看来,很多网络模型的架构不完全是突发奇想,而是是被参数计算量逼得。特别是现在需要在移动设备上进行AI应用计算(也叫推断), 模型参数规模必须更小, 所以出现很多减少握手规模的卷积形式, 现在主流网络架构大都如此。

7、什么是卷积?

解析:

对图像(不同的数据窗口数据)和滤波矩阵(一组固定的权重:因为每个神经元的多个权重固定,所以又可以看做一个恒定的滤波器filter)做内积(逐个元素相乘再求和)的操作就是所谓的『卷积』操作,也是卷积神经网络的名字来源。

非严格意义上来讲,下图中红框框起来的部分便可以理解为一个滤波器,即带着一组固定权重的神经元。多个滤波器叠加便成了卷积层。

OK,举个具体的例子。比如下图中,图中左边部分是原始输入数据,图中中间部分是滤波器filter,图中右边是输出的新的二维数据。

中间滤波器filter与数据窗口做内积,其具体计算过程则是:4*0 + 0*0 + 0*0 + 0*0 + 0*1 + 0*1 + 0*0 + 0*1 + -4*2 = -8

8、什么是CNN的池化pool层?

解析:

池化,简言之,即取区域平均或最大,如下图所示(图引自cs231n)

上图所展示的是取区域最大,即上图左边部分中 左上角2x2的矩阵中6最大,右上角2x2的矩阵中8最大,左下角2x2的矩阵中3最大,右下角2x2的矩阵中4最大,所以得到上图右边部分的结果:6 8 3 4。

9、简述下什么是生成对抗网络。

解析:

GAN之所以是对抗的,是因为GAN的内部是竞争关系,一方叫generator,它的主要工作是生成图片,并且尽量使得其看上去是来自于训练样本的。另一方是discriminator,其目标是判断输入图片是否属于真实训练样本。

更直白的讲,将generator想象成假币制造商,而discriminator是警察。generator目的是尽可能把假币造的跟真的一样,从而能够骗过discriminator,即生成样本并使它看上去好像来自于真实训练样本一样。

如下图中的左右两个场景:

更多请参见此课程:《生成对抗网络班》(链接:https://www.julyedu.com/course/getDetail/83

10、学梵高作画的原理是什么?

解析:

这里有篇如何做梵高风格画的实验教程《教你从头到尾利用DL学梵高作画:GTX 1070 cuda 8.0 tensorflow gpu版》(链接:http://blog.csdn.net/v_july_v/article/details/52658965),至于其原理请看这个视频:NeuralStyle艺术化图片(学梵高作画背后的原理)(链接:http://www.julyedu.com/video/play/42/523)。

11、请简要介绍下tensorflow的计算图。

解析:

Tensorflow是一个通过计算图的形式来表述计算的编程系统,计算图也叫数据流图,可以把计算图看做是一种有向图,Tensorflow中的每一个节点都是计算图上的一个Tensor, 也就是张量,而节点之间的边描述了计算之间的依赖关系(定义时)和数学操作(运算时)。

如下两图表示:

a=x*y; b=a+z; c=tf.reduce_sum(b);

12、你有哪些deep learning(rnn、cnn)调参的经验?

解析:

一、参数初始化

下面几种方式,随便选一个,结果基本都差不多。但是一定要做。否则可能会减慢收敛速度,影响收敛结果,甚至造成Nan等一系列问题。

下面的n_in为网络的输入大小,n_out为网络的输出大小,n为n_in或(n_in+n_out)*0.5

Xavier初始法论文:

http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf

He初始化论文:

https://arxiv.org/abs/1502.01852

uniform均匀分布初始化:

w = np.random.uniform(low=-scale, high=scale, size=[n_in,n_out])

Xavier初始法,适用于普通激活函数(tanh,sigmoid):scale = np.sqrt(3/n)

He初始化,适用于ReLU:scale = np.sqrt(6/n)

normal高斯分布初始化:w = np.random.randn(n_in,n_out) * stdev # stdev为高斯分布的标准差,均值设为0

Xavier初始法,适用于普通激活函数 (tanh,sigmoid):stdev = np.sqrt(n)

He初始化,适用于ReLU:stdev = np.sqrt(2/n)

svd初始化:对RNN有比较好的效果。

二、数据预处理方式

zero-center ,这个挺常用的.X -= np.mean(X, axis = 0) # zero-centerX /= np.std(X, axis = 0) # normalize

PCA whitening,这个用的比较少.

三、训练技巧

要做梯度归一化,即算出来的梯度除以minibatch size

clip c(梯度裁剪): 限制最大梯度,其实是value = sqrt(w1^2+w2^2….),如果value超过了阈值,就算一个衰减系系数,让value的值等于阈值: 5,10,15

dropout对小数据防止过拟合有很好的效果,值一般设为0.5,小数据上dropout+sgd在我的大部分实验中,效果提升都非常明显.因此可能的话,建议一定要尝试一下。 dropout的位置比较有讲究, 对于RNN,建议放到输入->RNN与RNN->输出的位置.关于RNN如何用dropout,可以参考这篇论文:http://arxiv.org/abs/1409.2329

adam,adadelta等,在小数据上,我这里实验的效果不如sgd, sgd收敛速度会慢一些,但是最终收敛后的结果,一般都比较好。如果使用sgd的话,可以选择从1.0或者0.1的学习率开始,隔一段时间,在验证集上检查一下,如果cost没有下降,就对学习率减半. 我看过很多论文都这么搞,我自己实验的结果也很好. 当然,也可以先用ada系列先跑,最后快收敛的时候,更换成sgd继续训练.同样也会有提升.据说adadelta一般在分类问题上效果比较好,adam在生成问题上效果比较好。

除了gate之类的地方,需要把输出限制成0-1之外,尽量不要用sigmoid,可以用tanh或者relu之类的激活函数.1. sigmoid函数在-4到4的区间里,才有较大的梯度。之外的区间,梯度接近0,很容易造成梯度消失问题。2. 输入0均值,sigmoid函数的输出不是0均值的。

rnn的dim和embdding size,一般从128上下开始调整. batch size,一般从128左右开始调整.batch size合适最重要,并不是越大越好。

word2vec初始化,在小数据上,不仅可以有效提高收敛速度,也可以可以提高结果。

四、尽量对数据做shuffle

LSTM 的forget gate的bias,用1.0或者更大的值做初始化,可以取得更好的结果,来自这篇论文:http://jmlr.org/proceedings/papers/v37/jozefowicz15.pdf, 我这里实验设成1.0,可以提高收敛速度.实际使用中,不同的任务,可能需要尝试不同的值.

Batch Normalization据说可以提升效果,不过我没有尝试过,建议作为最后提升模型的手段,参考论文:Accelerating Deep Network Training by Reducing Internal Covariate Shift

如果你的模型包含全连接层(MLP),并且输入和输出大小一样,可以考虑将MLP替换成Highway Network,我尝试对结果有一点提升,建议作为最后提升模型的手段,原理很简单,就是给输出加了一个gate来控制信息的流动,详细介绍请参考论文:http://arxiv.org/abs/1505.00387

来自@张馨宇的技巧:一轮加正则,一轮不加正则,反复进行。

五、Ensemble

Ensemble是论文刷结果的终极核武器,深度学习中一般有以下几种方式

同样的参数,不同的初始化方式

不同的参数,通过cross-validation,选取最好的几组

同样的参数,模型训练的不同阶段,即不同迭代次数的模型。

不同的模型,进行线性融合. 例如RNN和传统模型。

13、CNN最成功的应用是在CV,那为什么NLP和Speech的很多问题也可以用CNN解出来?为什么AlphaGo里也用了CNN?这几个不相关的问题的相似性在哪里?CNN通过什么手段抓住了这个共性?

解析:

Deep Learning -Yann LeCun, Yoshua Bengio & Geoffrey Hinton

Learn TensorFlow and deep learning, without a Ph.D.

The Unreasonable Effectiveness of Deep Learning -LeCun 16 NIPS Keynote

以上几个不相关问题的相关性在于,都存在局部与整体的关系,由低层次的特征经过组合,组成高层次的特征,并且得到不同特征之间的空间相关性。如下图:低层次的直线/曲线等特征,组合成为不同的形状,最后得到汽车的表示。

CNN抓住此共性的手段主要有四个:局部连接/权值共享/池化操作/多层次结构。

局部连接使网络可以提取数据的局部特征;权值共享大大降低了网络的训练难度,一个Filter只提取一个特征,在整个图片(或者语音/文本) 中进行卷积;池化操作与多层次结构一起,实现了数据的降维,将低层次的局部特征组合成为较高层次的特征,从而对整个图片进行表示。如下图:

上图中,如果每一个点的处理使用相同的Filter,则为全卷积,如果使用不同的Filter,则为Local-Conv。

14、LSTM结构推导,为什么比RNN好?

解析:

推导forget gate,input gate,cell state, hidden information等的变化;因为LSTM有进有出且当前的cell informaton是通过input gate控制之后叠加的,RNN是叠乘,因此LSTM可以防止梯度消失或者爆炸。

15、Sigmoid、Tanh、ReLu这三个激活函数有什么缺点或不足,有没改进的激活函数。

解析:

sigmoid、Tanh、ReLU的缺点在121问题中已有说明,为了解决ReLU的dead cell的情况,发明了Leaky Relu, 即在输入小于0时不让输出为0,而是乘以一个较小的系数,从而保证有导数存在。同样的目的,还有一个ELU,函数示意图如下。

还有一个激活函数是Maxout,即使用两套w,b参数,输出较大值。本质上Maxout可以看做Relu的泛化版本,因为如果一套w,b全都是0的话,那么就是普通的ReLU。Maxout可以克服Relu的缺点,但是参数数目翻倍。

资料来源:

1.寒小阳&AntZ,张雨石博客等;

2.萧瑟,

https://www.zhihu.com/question/41631631/answer/94816420

3.许韩,

https://zhuanlan.zhihu.com/p/25005808

4.《 CNN笔记:通俗理解卷积神经网络》,http://blog.csdn.net/v_july_v/article/details/51812459

5.我愛大泡泡,

http://blog.csdn.net/woaidapaopao/article/details/77806273

16、为什么引入非线性激励函数?

解析:

第一,对于神经网络来说,网络的每一层相当于f(wx+b)=f(w'x),对于线性函数,其实相当于f(x)=x,那么在线性激活函数下,每一层相当于用一个矩阵去乘以x,那么多层就是反复的用矩阵去乘以输入。根据矩阵的乘法法则,多个矩阵相乘得到一个大矩阵。所以线性激励函数下,多层网络与一层网络相当。比如,两层的网络f(W1*f(W2x))=W1W2x=Wx。

第二,非线性变换是深度学习有效的原因之一。原因在于非线性相当于对空间进行变换,变换完成后相当于对问题空间进行简化,原来线性不可解的问题现在变得可以解了。

下图可以很形象的解释这个问题,左图用一根线是无法划分的。经过一系列变换后,就变成线性可解的问题了。

如果不用激励函数(其实相当于激励函数是f(x) = x),在这种情况下你每一层输出都是上层输入的线性函数,很容易验证,无论你神经网络有多少层,输出都是输入的线性组合,与没有隐藏层效果相当,这种情况就是最原始的感知机(Perceptron)了。

正因为上面的原因,我们决定引入非线性函数作为激励函数,这样深层神经网络就有意义了(不再是输入的线性组合,可以逼近任意函数)。最早的想法是sigmoid函数或者tanh函数,输出有界,很容易充当下一层输入(以及一些人的生物解释)。

17、请问人工神经网络中为什么ReLu要好过于tanh和sigmoid function?

解析:

先看sigmoid、tanh和RelU的函数图:

第一,采用sigmoid等函数,算激活函数时(指数运算),计算量大,反向传播求误差梯度时,求导涉及除法和指数运算,计算量相对大,而采用Relu激活函数,整个过程的计算量节省很多。

第二,对于深层网络,sigmoid函数反向传播时,很容易就会出现梯度消失的情况(在sigmoid接近饱和区时,变换太缓慢,导数趋于0,这种情况会造成信息丢失),这种现象称为饱和,从而无法完成深层网络的训练。而ReLU就不会有饱和倾向,不会有特别小的梯度出现。

第三,Relu会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生(以及一些人的生物解释balabala)。当然现在也有一些对relu的改进,比如prelu,random relu等,在不同的数据集上会有一些训练速度上或者准确率上的改进,具体的大家可以找相关的paper看。

多加一句,现在主流的做法,会多做一步batch normalization,尽可能保证每一层网络的输入具有相同的分布[1]。而最新的paper[2],他们在加入bypass connection之后,发现改变batch normalization的位置会有更好的效果。大家有兴趣可以看下。

[1] Ioffe S, Szegedy C. Batch normalization: Accelerating deep network training by reducing internal covariate shift[J]. arXiv preprint arXiv:1502.03167, 2015.

[2] He, Kaiming, et al. "Identity Mappings in Deep Residual Networks." arXiv preprint arXiv:1603.05027 (2016).

18、为什么LSTM模型中既存在sigmoid又存在tanh两种激活函数,而不是选择统一一种sigmoid或者tanh?这样做的目的是什么?

解析:

sigmoid 用在了各种gate上,产生0~1之间的值,这个一般只有sigmoid最直接了。

tanh 用在了状态和输出上,是对数据的处理,这个用其他激活函数或许也可以。

二者目的不一样

另可参见A Critical Review of Recurrent Neural Networks for Sequence Learning的section4.1,说了那两个tanh都可以替换成别的。

19、如何解决RNN梯度爆炸和弥散的问题?

解析:

为了解决梯度爆炸问题,Thomas Mikolov首先提出了一个简单的启发性的解决方案,就是当梯度大于一定阈值的的时候,将它截断为一个较小的数。具体如算法1所述:

算法:当梯度爆炸时截断梯度(伪代码)

下图可视化了梯度截断的效果。它展示了一个小的rnn(其中W为权值矩阵,b为bias项)的决策面。这个模型是一个一小段时间的rnn单元组成;实心箭头表明每步梯度下降的训练过程。当梯度下降过程中,模型的目标函数取得了较高的误差时,梯度将被送到远离决策面的位置。截断模型产生了一个虚线,它将误差梯度拉回到离原始梯度接近的位置。

梯度爆炸,梯度截断可视化

为了解决梯度弥散的问题,我们介绍了两种方法。第一种方法是将随机初始化

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是每一小对应的Python代码: 1. 读取拼音数据(无声调): ```python with open('/kaggle/input/pyinin/pinyn.txt', 'r', encoding='utf-8') as f: pinyin_data = f.read().split() ``` 2. 定义数据集,采用字符模型,每个样本采用one-hot编码: ```python import numpy as np # 构建字符集 vocab = sorted(set(''.join(pinyin_data))) vocab_size = len(vocab) # 创建字符到索引的映射 char_to_idx = {char: idx for idx, char in enumerate(vocab)} idx_to_char = np.array(vocab) # 将拼音数据换为one-hot编码 def one_hot_encode(text, vocab_size, char_to_idx): num_samples = len(text) max_len = max([len(txt) for txt in text]) input_data = np.zeros((num_samples, max_len, vocab_size)) for i in range(num_samples): for j in range(len(text[i])): input_data[i, j, char_to_idx[text[i][j]]] = 1 return input_data input_data = one_hot_encode(pinyin_data, vocab_size, char_to_idx) ``` 3. 实现序列的随机采样和序列的顺序划分: ```python # 随机采样 def random_sample(batch_size, seq_len): # 随机选择一个起始位置 idx = np.random.randint(0, input_data.shape[0] - seq_len) input_seq = input_data[idx:idx+seq_len] target_seq = np.copy(input_seq) target_seq[:-1] = input_seq[1:] return input_seq, target_seq # 序列的顺序划分 def seq_partition(batch_size, seq_len): num_batches = input_data.shape[0] // (batch_size * seq_len) data = input_data[:num_batches * batch_size * seq_len] data = data.reshape(batch_size, num_batches * seq_len, vocab_size) data = np.transpose(data, axes=[1, 0, 2]) input_seq = data[:-1] target_seq = data[1:] return input_seq, target_seq ``` 4. 标签Y与X同形状,但时间超前1: ```python target_seq = np.copy(input_seq) target_seq[:-1] = input_seq[1:] ``` 5. 准备数据,一次梯度更新使用的数据形状为:(时间步,Batch,类别数): ```python def get_data(batch_size, seq_len, use_random_sample=True): if use_random_sample: X, Y = random_sample(batch_size, seq_len) else: X, Y = seq_partition(batch_size, seq_len) return X.transpose(1, 0, 2), Y.transpose(1, 0, 2) ``` 6. 实现基本循环神经网络模型: ```python import torch import torch.nn as nn class RNNModel(nn.Module): def __init__(self, vocab_size, hidden_size, num_layers=1, rnn_type='rnn'): super().__init__() self.rnn_type = rnn_type self.hidden_size = hidden_size self.num_layers = num_layers self.embedding = nn.Embedding(vocab_size, hidden_size) if rnn_type == 'rnn': self.rnn = nn.RNN(hidden_size, hidden_size, num_layers) elif rnn_type == 'gru': self.rnn = nn.GRU(hidden_size, hidden_size, num_layers) self.fc = nn.Linear(hidden_size, vocab_size) def forward(self, input_seq, hidden=None): seq_len, batch_size, _ = input_seq.size() if hidden is None: hidden = self.init_hidden(batch_size) embed = self.embedding(input_seq) output, hidden = self.rnn(embed, hidden) output = output.view(seq_len * batch_size, self.hidden_size) output = self.fc(output) return output.view(seq_len, batch_size, -1), hidden def init_hidden(self, batch_size): if self.rnn_type == 'gru': return torch.zeros(self.num_layers, batch_size, self.hidden_size) else: return torch.zeros(self.num_layers, batch_size, self.hidden_size) ``` 7. 训练,损失函数为平均交叉熵: ```python def train(model, optimizer, criterion, num_epochs, batch_size, seq_len): model.train() for epoch in range(num_epochs): hidden = None for i in range(0, input_data.shape[0] - seq_len, seq_len): X, Y = get_data(batch_size, seq_len, use_random_sample=False) X = torch.tensor(X, dtype=torch.long) Y = torch.tensor(Y, dtype=torch.long) optimizer.zero_grad() output, hidden = model(X, hidden) hidden.detach_() loss = criterion(output.view(-1, vocab_size), Y.view(-1)) loss.backward() nn.utils.clip_grad_norm_(model.parameters(), 0.5) optimizer.step() if epoch % 10 == 0: print(f'Epoch {epoch}, Loss: {loss.item():.4f}') ``` 8. 预测,给定一个前缀,进行单步预测和K步预测: ```python def predict(model, init_text, predict_len): hidden = None input_seq = torch.tensor([[char_to_idx[c] for c in init_text]], dtype=torch.long) output_text = init_text for i in range(predict_len): output, hidden = model(input_seq, hidden) output = output[-1].squeeze(0) output_dist = nn.functional.softmax(output, dim=0) top_k_prob, top_k_idx = output_dist.topk(k=5) top_k_prob = top_k_prob.detach().numpy() top_k_idx = top_k_idx.detach().numpy() for j in range(top_k_idx.shape[0]): next_char = idx_to_char[top_k_idx[j]] prob = top_k_prob[j] output_text += next_char input_seq = torch.tensor([[char_to_idx[c] for c in output_text]], dtype=torch.long) return output_text ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值