本文原作者:罗齐,经授权后发布。
“Feature engineering is the process of using domain knowledge to extract features from raw data via data mining techniques.” (Wikipedia)
在机器学习的各个子领域中,特征工程都扮演着重要的角色。业界有这么一句话,数据和特征决定了机器学习算法的上限,而模型、算法的选择和优化只是在不断逼近这个上限。通过运用领域知识,在原始数据集中构造解释变量,有利于机器学习模型预测目标。传统的特征工程方法是运用与问题相关的领域专业知识,从数据集中提取特征。我们称之为手动特征工程,手动特征工程的缺点在于繁琐耗时,对于不同的问题需要重新开始,过于依赖人的经验与判断。因此,研发自动特征工程就尤为重要。
特征工程又包含了数据预处理、特征提取、特征增强(特征选择,特征降维,特征编码)等子问题。到目前为止,还没有一种办法能够像pipeline一样自动化地完成这一系列的操作,但是业界学界提出了各种方法来辅助工程师减少在特征工程上的耗时,提高特征的效果。
文献8将自动特征工程分为三个方向:显式(如显式特征叉乘,笛卡尔积),半显式(如GBDT)与隐式(如FM,DL)。本文就循着这三个方向简要介绍自动特征工程一些进展及其在推荐领域的应用。
显式特征组合也就是算法输出会明确指定哪些特征组合起来(如笛卡尔积)。整体研究思路是构架并搜索特征空间,也有使用正则化加贪心方法。显式特征组合的优势在于可解释性,能够给工程人员或产品人员对业务更加清晰的洞察,可以发现哪些特征是有潜在关系的。而且,通过显式特征组合的方法产生的特征集,可以增强所有其他机器学习算法,成为训练的基础。
Deep Feature Synthesis(DFS)目前在自动特征构建领域是做的比较好的显式特征组合的方法。DFS主要处理关系型数据,能够从中自动生成特征。本质上该算法遵循数据中基本字段的关系链路,然后沿该路径依次应用数学函数以创建最终特征。很多业务数据都是以关系型数据的形式存在,以电商数据为例,可以抽象出用户、商品、订单三类基本实体。实体会带有属性(即字段或特征),实体之间会存在关联(以某个字段为键)。
人工特征工程的过程就是针对业务中的实体的属性,经过一些运算得到的一些特征。然后根据关联关系,将与之关联的实体的特征也关联进来,经过一些运算得到两个相关联的实体的特征。以电商数据为例,用户本身带有年龄、性别、城市、职业等属性,对这些属性做一些运算(比如离散化、分桶)得到用户特征。之后,对用户关联的订单实体,也能够提取一些订单级别的特征,比如该用户的购买力、购买频次、上次购买时间等。进而,循着订单中的商品外键,可以连接到商品实体,得到商品级别的特征,比如用户购买商品的喜好、购买某些产品的频次。这个过程就是沿着关联路径,最后全部关联到uid上,得到用户特征。同理,也可以使用这种方式构建商品特征。
DFS将特征分为两种:
之后,就能根据父子关系计算rfeat和dfeat,把rfeat和dfeat合并到parent表中,计算parent的efeat。递归实现这些操作,也就是自底向上累积计算特征。伪代码如下:
DFS算法在应用上已经有比较成熟的实现,Featuretools库。方便调用者定义实体关系,并且提供非常多的机缘,Featuretools将数据的转换和聚合函数称为基元(primitives)。自动化特征工程让新手可以从一组相关数据表中创建数千个相关特征。我们只需要知道我们表的基本结构以及它们之间的关系,我们在称为实体集的单个数据结构中跟踪它们。通过调用方法即可在一次函数调用中构建数千个特征。featuretools特征构建都是通过人工设计的基元实现的,相当一部分是具有可解释性的特征。在对类似的业务进行迁移时,也可以人工构建基元,构建特征处理方式,以不断减少重复性劳动。
以Featuretools提供的贷款数据集为例,对自动和手动进行特征工程的耗时、特征数量和性能之间的比较:
使用featuretools实际上也有缺点:
这个算法来自KDD2019《AutoCross: Automatic Feature Crossing for Tabular Data in Real-World
Applications》。主要思路是先生成一部分二阶组合特征,然后用效果好的二阶组合特征去衍生三阶组合特征,并非生成所有的三阶组合特征。相当于一种贪心的搜索方法。如下图简单阐述了Beam Search的思路
显式特征组合的方法也就是算法输出会明确指定哪些特征组合起来。主要是基于贪心和搜索方法:比如上述的Beam Search,基于贪心的启发式搜索(前向/后向搜索等);随机搜索(模拟退火、遗传算法等)。还有一些方法比如Lasso正则化,能够在特征空间中进行权重截断,形成组合候选。
显式特征组合的有点主要是可解释性,能够提供对数据一些深度的业务理解。另外一方面就是可迁移性,得到的一套特征组合可以使用不同的机器学习算法。
半显式的组合主要基于的是树模型。叶子节点的每一个分支并不是一种显式、直接的特征组合,而是这些特征在特定取值区间的组合。所以从结果上来说我们做到了特征组合,有一定可解释性,但是同样也没有办法直接看特征相关性或者特征之间组合关系,所以称之为半显式。实际上,半显式特征组合也是一种层次贪心的算法。
GBDT 是AdaBoost 家族的成员,它将利用前一轮迭代的误差来更新训练集的权重,校正前一轮迭代被错误分类的样本,下一轮迭代会将重心放在上一轮分错的样本上(或者说残差)。
LR 模型中的特征交叉很关键,但又无法直接通过特征笛卡儿积解决,只能依靠人工经验。这种方法耗时耗力的同时并不一定会带来效果的提升。如何自动发现有效的特征和特征交叉,弥补人工经验的不足,避免大量人工特征工作?第一种解决方案就是FM,但是FM特征交叉的局限性就是自动交叉的阶数有限,通常只有二阶。第二种解决方案是,Facebook 于 2014 年在论文Practical Lessons from Predicting Clicks on Ads at Facebook中介绍了通过GBDT进行特征交叉选择。GBDT+LR可以有多种多样的网络结构,主要根据其所处理的特征类型来区分。
Facebook采用的GBDT+LR融合方式是:用已有的特征训练GBDT 模型,然后利用GBDT 模型学习到的树来构造新特征,最后把这些新特征加入到原有特征中一起训练模型。其构造的新特征向量 中每个元素对应GBDT 模型中树的某个叶节点。当一个样本点通过某棵树最终落在这棵树的 一个叶节点上时,那么在新特征向量中这个叶节点对应的元素值为 1,而这棵树的其他叶节点 对应的元素值为 0。新特征向量的长度等于 GBDT 模型里所有树包含的叶节点数之和。这里树的每条路径,是通过最小化均方差等方法最终分割出来的有区分性路径,根据该路径得到的特征、特征交叉都相对有区分性,其效果理论上不会亚于采用人工经验的处理方式。
Facebook 的论文中有个例子,图中 Tree1、Tree2 为通过GBDT模型学出来的两棵树。x 为一条输入样本,遍历两棵树后,x 样本分别落到两棵树的叶节点上,左树 Tree1 有 3 个叶节点,右树 Tree2 有两个叶节点,最终的特征即五维的向量。对于输入 x,假设其落在左树 Tree1 的第一个节点,则编码1,0,0;若落在右树 Tree2 的第二个节点,则编码0,1。所以,整体的编码为1,0,0,0,1。将这类编码作为特征,输入到线性分类模型(LR或FM)中进行分类。
GBDT 模型能够学习高阶(树的深度越深,其特征的层级就越高)非线性特征交叉,对应树的一条路径(用叶节点来表示)就是一组高阶特征交叉。通常把一些连续值特征、值空间不大的离散特征都丢给 GBDT 模型;空间很大的 ID 特征(比如商品 ID)则留在 LR 模型中进行训练。这既能做高阶特征交叉,又能利用线性模型易于处理大规模稀疏数据的优势。
随机森林就是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树。从直观角度来解释,每棵决策树都是一个分类器(假设现在针对的是分类问题),因此,对于一个输入样本,N棵树会有N个分类结果。而随机森林集成了所有的分类投票结果,将投票次数最多的类别指定为最终的输出,这就是一种最简单的 Bagging 思想。
单棵决策树的每一条路径都表示一个高阶特征交叉,其中该高阶特征交叉的特征对于Label 来说是显著的。这里我们只需要通过控制树的数量来控制交叉组合的数量,通过控制树的深度来控制交叉的阶数,以及通过控制节点的最小样本数来防止过拟合特征的产生。
基于树模型在特征交叉与选择中的优越性,可以设计GBDT和随机森林组合。其中,设计随机森林为鼓励模型多样性。
在设计过程中,每一个GBDT和随机森林进行特征交叉和选择的目的不一样,比如:
其中,随机森林比较随意,目的只是鼓励多样性,可以每次随机抽取特征进行构建。另外,有些特征不适合进行特征交叉,可将这些特征单独拿出来,放在最后的LR层。
半显式特征组合主要是森林类、树类算法。它们的主要特点事相对神经网络来说容易理解,相对鲁棒。但是同样的,对离散的特征非常难处理,传统上训练一棵 m 个特征 n 个训练数据 k 层深 t 棵树的模型需要 O(mntk) 的时间,即使对系数特征进行优化,也很难降低特征分裂中分桶上的空间与传输消耗。为了解决这个问题,有不少研究尝试通过Embedding,Ensemble、Stacking,General Boosting来解决,使树模型可以在大规模特征维度的情况下可以进行特征组合。
因子分解机(Factorization Machine, FM)是一种基于矩阵分解的机器学习算法。传统的逻辑回归等相关变种模型均认为特征是相互独立的,但是在实际很多情况下特征之间的依赖关系却是不可忽视的,因此需要进行特征交叉。在大多数业务场景下,类别特征经过OneHot后会变得相当稀疏,再进一步特征交叉的话,特征空间就会变得很大,FM就可用于解决特征交叉下数据稀疏的问题。
FM引入多项式回归模型来加入特征间的关联性,通常对线性模型增加一个二阶多项式,其多项式模型的公式变为
上述多项式模型的二阶特征的参数共有n(n−1)/2n(n-1)/2n(n−1)/2种,且任意参数间相互独立,并且在进行参数估计时发现,对于这些二次项的参数,都需要大量非零样本来进行求解,但是很多时候特征空间是相当稀疏的,这种情况下参数的估计变得相当不准确。为了解决这个问题,引入矩阵分解的思路,对交叉项的系数矩阵进行了如下分解:
思路是:由于特征之间不是相互独立的,因此可以使用一个隐因子来串联。在推荐算法中,类似的想法是为将一个评分矩阵分解为user矩阵和item矩阵,每个user和item都可以用一个隐向量来表示。如图将一个user表示成一个二维向量,同时把一个item表示为一个二维向量,两个向量的点乘就是每个user对每个item的评分矩阵。
FM的核心思想也类似,将所有二次项系数组成一个对称矩阵W,W可被分解为
V的第J列即第J维特征的隐向量。化简W矩阵,
那么,FM公式变为:
在FM模型中,每一个特征会对应一个隐变量。在FFM模型中,将特征分为多个域(Field),每一维特征都归属一个特定的域,域和特征是一对多的关系。如下表,可以把Onehot之后的特征北京、上海、深圳划分为同一个域。
field | field1年龄 | field2城市 | field2城市 | field2城市 | field3性别 | field3性别 |
---|---|---|---|---|---|---|
feature | x1年龄 | x2北京 | x3上海 | x4深圳 | x5男 | x6女 |
用户1 | 23 | 1 | 0 | 0 | 1 | 0 |
用户2 | 31 | 0 | 0 | 1 | 0 | 1 |
每个特征对应每个域分别有一个因变量。因变量viv_ivi不仅与xix_ixi有关,还跟与xix_ixi相乘的xjx_jxj所属的域有关系,即viv_ivi扩展成了一个二维向量vf∗kv_{f*k}vf∗k,fff是域的总个数,kkk是隐向量长度。假设样本有nnn个特征,FFM中的二次项中每个特征都有n∗fn*fn∗f个隐向量,而FM中所有特征的隐向量只有一个。FM可以看做是FFM的特例,所有特征都归属到一个域中。只保留二次项,FFM的方程如下:
FM和FFM通过枚举所有的二阶特征组合,用低维空间中的内积去代表两个特征的组合,取得了不错的效果。FM的结果可以看成是低阶(二阶)特征提取,在下文的深度模型部分可以看到,FM的输出可以作为深度神经网络的输入。
在传统机器学习模型构建过程中,经常使用one hot encoding对离散特征,特别是id类特征进行编码,但由于one hot encoding的维度等于物体的总数,比如item特征one hot encoding的维度就至少是千万量级的。这样的编码方式对于商品来说是极端稀疏的,甚至用multi hot encoding对用户浏览历史的编码也会是一个非常稀疏的向量。而深度学习的特点以及工程方面的原因使其不利于稀疏特征向量的处理。所以这个时候embedding就派上用场了,它能够用低维向量对物体进行编码还能保留其含义。这里大概介绍几种推荐系统业界常用的embedding方法。
第一种是阿里巴巴淘宝的策略,来自KDD2018《Billion-scale Commodity Embedding for E-commerce Recommendation in Alibaba》。这篇文章使用了graph Embedding方法中的Deep Walk算法作为基础对目标商品进行建模。作者认为每一个被点击的商品就是图中的每一个点,任意两个相邻被点击的商品,比方说对于用户u1u_1_u_1来说就是D和A,A和B来说他们之间就会有一条边,同时每一条边都是包含权值的,点的权值就是两个点的共现次数,最终形成的Graph如下图(左二)所示。接着在下图中使用随机游走算法生成一系列的商品序列,然后运用skip-gram算法生成每个商品的Embedding的向量表征形式。
第二种是Airbnb针对民宿租赁场景提出的一个模型,来自KDD2018《Real-time Personalization using Embeddings for Search Ranking at Airbnb》,目标是对user和host listing进行Embedding,从而完成host listing的相似度匹配工作。从本质上来说,这篇paper提出的几个idea都是对skip-gram模型进行微小的修改,从而适配Airbnb适用的目标场景。
feature embedding在推荐系统中起着非常重要的作用。把它归为自动隐式特征工程不知是否合适。
这是参考的华为诺亚方舟实验室2019年WWW会议的论文《Feature Generation by Convolutional Neural Network for Click-Through Rate Prediction》。
目前各个公司在推荐领域使用的都是以神经网络为基础的深度学习模型,但是推荐领域普遍存在着非常严重的数据稀疏问题,特别是涉及到特征交叉部分的时候这样的现象尤为明显,因此直接在输入层输入独立的特征,把挖掘特征之间交互信息的任务完全交给后续的多层全连接网络显然是远远不够的。
所以各个公司在真正实践的时候,往往由领域专家从业务本身入手,人工的在输入层加入有意义的交叉特征往往是对最终模型效果的提升有着较大的帮助。但是很多推荐场景业务非常的复杂,涉及到的特征组合更是数不胜数,这给做特征的领域专家带来了很大的工作量。基于这样的背景,论文作者同时结合了在图像领域抽取局部信息特征的卷积结构(CNN)提出了一种叫做Feature generation的结构,自动的从原始特征中进行特征的组合,将组合后的特征和原始特征进行拼接feed到后续的深度网络结构中,并证明了模型的有效性。
如下图所示是Wide&Deep和加入了特征生成网络FGCNN的对比,原始特征在喂到神经网络之前先通过FGCNN生成新的隐式特征,与原始特征拼接后再喂到神经网络。
下图是特征生成的网络结构:
论文作者最终通过大量的实验,证明了这种特征生成的有效性。特别强调一点是,作者专门通过打乱特征的输入顺序,观测到模型最终的预测效果是稳定而提升的,从而证明了recombination操作的对应全局特征交互信息提取的有效性和稳定性。
值得注意的是,喂到特征生成网络的数据实际上也是embedding,所以总的来看感觉其实还是在模型上做文章。
隐式特征构造的算法在对原始的特征进行高阶的重新组合交叉,捕捉其中的pattern输出。和神经网络一样,其实已经超出了人能解释的范畴,对外呈现一个黑盒的状态。但实际上这些算法也可以称之为特征,就像在迁移学习中,通过预先训练好的CNN模型提取特征喂到另一个分类模型中一样。
特征选择方法有很多,一般分为三类:第一类过滤法比较简单,它按照特征的发散性或者相关性指标对各个特征进行评分,设定评分阈值或者待选择阈值的个数,选择合适特征。比如方差法、相关系数、假设检验。第二类是包装法,根据目标函数,通常是预测效果评分,每次选择部分特征,或者排除部分特征。第三类嵌入法则稍微复杂一点,它先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据权值系数从大到小来选择特征。
自动化特征选择主要是人为设定一个特征个数的阈值,通过特征选择的方法,来筛选出满足阈值的特征。
这种特征选择的方式来源于微软2019年的论文Feature Gradients: Scalable Feature Selection via Discrete Relaxation,这是一种基于梯度搜索算法的特征选择。
这个算法目前已经在微软的自动化机器学习支持库nni中得到了开源实现,可以直接调用。
最常用的方法是L1和L2正则化(主要是L1,L1正则化自带有稀疏特性)。
正则化惩罚项越大,那么模型的系数就会越小。当正则化惩罚项大到一定的程度的时候,部分特征系数会变成0,当正则化惩罚项继续增大到一定程度时,所有的特征系数都会趋于0. 但是我们会发现一部分特征系数会更容易先变成0,这部分系数就是可以筛掉的。也就是说,我们选择特征系数较大的特征。
虽说深度学习模型能够通过训练对不同的特征起到”优胜略汰“的作用,给不同特征赋予不同权重。但是,如果在喂到深度学习模型之前做好特征选择的工作,只需更少的数据量即可得到同样性能的模型,从系统的角度看,特征选择对机器学习执行性能的优化也有重大意义。
之前介绍的因子分解机通过对特征的隐变量进行内积操作来提取特征组合,一般只用到了二阶特征交叉。随着阶数的提高,模型学习的成本越大。实际上,高阶的交叉特征和低阶的交叉特征都非常重要,同时学习两种交叉特征的性能比只考虑其中一种性能好。为了构建高阶的交叉特征,很自然地就想到通过深度神经网络DNN来解决。
对于离散特征的处理,我们一般是将特征转为Onehot形式。但是,将OneHot类型的特征输入到DNN中,却会导致参数过多。针对NN难以处理离散特征的问题,可以通过Embedding的技术进行解决(这块其实在前面已经讲过,但是在这里权当抛砖引玉)。Embedding最早在NN上的应用是在NLP问题上,通过将每个单词映射到一个低维空间,再使用concat、卷积、池化等方式形成等长的底层输入,最后使用神经网络进行训练。或者可以采用类似FFM中的思想,将特征分为不同的域,对同一个域里的稀疏Onehot向量映射到一个稠密向量。如下图,将年龄的长度为5的onehot向量与两个隐含层节点相连,城市的长度为600的onehot向量与两个隐含层节点相连,可减少输入层与隐含层之间的参数:
为了让不同域的特征进行交叉组合,在网络上再加两层的全连接层,让稠密向量进行交叉,那么高阶特征的交叉就出来了。如下图,增加了两层隐含层进行高阶特征交叉:
为了让连续特征与离散特征进行特征交叉组合,在网络的每一层可以增加连续特征。该网络就是FNN,Wide&Deep模型的Deep模型与该网络也是相同的结构。
通过深层网络结构,解决了DNN的输入问题,但是这里仍存在问题,低阶特征交叉和高阶特征交叉隐含地体现在隐含层,而我们希望把低阶特征交叉单独建模,然后融合高阶特征交叉。比如我们可以通过FM模型把低阶特征进行单独建模。
之后把低阶特征交叉与DNN模型进行整合。二者的融合有两种方式:串行结构或并行结构。并行结构是在输出层对高阶部分和低阶部分进行连接。典型代表有:Wide&Deep、DeepFM、DCN和DIN。
串行结构在输入层分为两部分,输入层的第一部分就是对离散特征进行Dense转换,第二部分就是连续特征。然后这两部分的连接作为FM的输入,再把FM层作为DNN层的输入,或直接作为DNN层的输入。串行结构网络的典型代表:PNN、NFM和AFM。串行结构如下图所示。
这是使用深度神经网络来进行特征交叉的基础,对特征组合、特征交叉还有许多可以优化的地方。
比如使用Product-based Neural Network(PNN)提出了一种特征交叉层的思想,以解决交叉特征表达不充分的问题。在推荐系统中,特征之间的关系更多的是一种“And”的关系而非“Add”的关系。在特征交叉层中,一部分是embedding层的线性部分,另一部分是embedding层的特征交叉部分。
在特征工程中,也存在像推荐系统与搜索一样的“老虎机问题”,要同时满足记忆(Memorization)和泛化(Generalization)的要求。利用记忆(Memorization)方法可以广泛地学到特征的共现率,并利用这种关系开采(Exploit)历史数据中的相关性(Correlation)。利用泛化(Generalization) 方法则基于相关性的转移,探索(Explore)在之前很少或从未出现过的新的交叉特征。
Google 提出的Wide & Deep 学习模型,使得在同一个模型中达到记忆和泛化。
在Wide & Deep 模型中包括两部分,分别为Wide 模型和 Deep 模型。其中wide部分用于记忆,deep部分用于泛化。记忆即从历史数据中发现 item或者特征之间的相关性,泛化则是探索并发现在历史数据中很少或者没有出现过 的新的交叉特征。达到了对特征组合非常高效的exploit和explore。
Wide 模型的输入特征包括原始的输入特征和转换后的特征。一个最重要的转换是特征交叉转换(Cross-Product Transformation)。它定义为:
其中ckic_{ki}cki为一个boolean变量,如果第iii个特征是第kkk个变换ϕk\phi_kϕk的一部分,那么为1;否则为0。对于二值特征,一个特征交叉转换(如 AND(性别=男, 语言=英语))只能当组成特征(“性别=男”和“语言=英语”)都为 1 时才会为 1,否则为 0。在模型中,模型对这两个特征进行向量积叉乘,得到一个矩阵。wide模型可以对这些0-1的“特例”进行记忆,但对训练集中没有出现的特征对,wide就不会有任何效果。下图是一个例子,wide对某个query和item起到正向作用。
Deep模型是一个前向神经网络。先把稀疏的、高维的类别特征转换成一个低维的、稠密的、实数值的Embedding 向量,来探索过去从未或很少出现的新的特征交叉。将这些高维稀疏的类别特征(如人口学特征和设备类别)映射为低维稠密的向量后,与其他连续特征(用户年龄、应用安装数等)拼接在一起, 输入MLP 中,最后输入至逻辑输出单元。下图是一个例子,Deep模型可以学习一个低维的embedding来学习两类字符上并不相关的item,如burger和chicken fried rice。
Wide & Deep 模型中,由于Wide 部分是一个 LR模型,因此仍然需要大量的人工特征工程工作。但Wide & Deep 模型给整个学术界和工业界提供了一种框架思想。基于这种思想,华为诺亚方舟团队结合FM的特征交叉功能,将Wide & Deep 模型的 LR 部分替换成 FM 来避免人工特征工程工作。
我们的目标是同时学到低阶特征交叉和高阶特征交叉。DeepFM模型包含两个组件:FM组件和 Deep 组件,它们共享 相同的输入。
DeepFM 模型的特点如下:
深度学习与众不同的特性之一,在于其能够对原始特征进行更高层次的抽象和提取,进而生成区分度更高、相关性更好的特征集合,因此深度学习算法还经常被叫作“自动特征提取算法”。深度神经网络通过神经元的层次化的相互连接,构建的特征组合远超手动特征工程的效果。但是深度神经网络高维离散特征的变量处理非常复杂,同时缺乏可解释性,过于黑盒化,这样学习出来的特征组合难以迁移使用,也很难给人明确的信息反馈。
对大规模离散特征的embedding技术,其中的代表是FM,DeepFM,FNN等,下图摘取自DeepFM的论文,展示了这些算法的异同。这些技术尝试在高维的特征中对特征之间的关系进行推理,同时记忆精细化的特征。
自动化特征工程解决了以往手动特征构造存在的问题。自动地从源数据提取特征,从低阶到高阶的交叉、组合,尝试推理出数据与标签之间的联系。但是,到目前为止,通过模型或者算法来构建特征空间,是不能完全取代人的工作的。我们还是要借助对业务的理解与经验,尝试从源数据中提取出具有场景限制性、问题限制性的特征。因为特征工程不仅与算法相关,与实际问题更是强相关的。不管黑猫白猫,只要能抓到老鼠就是好猫,在模型侧与特征工程侧,如何更好的搭配,只要能尽量降低工程成本的同时达到更优的业务目标,就是工程上最优的解决方案。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。