前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习TensorFlow中有关特征工程的API

学习TensorFlow中有关特征工程的API

作者头像
代码医生工作室
发布2019-07-19 16:59:23
5.5K0
发布2019-07-19 16:59:23
举报
文章被收录于专栏:相约机器人相约机器人

用TensorFlow框架搭建神经网络已经是大众所知的事情。今天我们来聊一聊如何用TensorFlow 对数据进行特征工程处理。

在TensorFlow中还有一些不被大家熟知的数据处理API。这些API与TensorFlow框架结合紧密,使用方便。用这些API做数据前期的特征处理,可以提高效率。

一、接口介绍

TensorFlow使用特征列接口来进行数据特征工程的处理。框架中一共包含有两个特征列接口:特征列接口和序列特征列接口。

1.了解特征列接口

特征列(tf.feature_column)接口是TensorFlow中专门用于处理特征工程的高级API。用tf.feature_column接口可以很方便地对输入数据进行特征转化。

特征列就像是原始数据与估算器之间的中介,它可以将输入数据转化成需要的特征样式,以便传入模型进行训练。

2.了解序列特征列接口

序列特征列接口(tf.contrib.feature_column.sequence_feature_column)是TensorFlow中专门用于处理序列特征工程的高级API。它是在tf.feature_column接口之上的又一次封装。该API目前还在contrib模块中,未来有可能被移植到主版本中。

在序列任务中,使用序列特征列接口(sequence_feature_column)会大大减少程序的开发量。

在序列特征列接口中一共包含以下几个函数。

  • sequence_input_layer:构建序列数据的输入层。
  • sequence_categorical_column_with_hash_bucket:将序列数据转化成离散分类特征列。
  • sequence_categorical_column_with_identity:将序列数据转化成ID特征列。
  • sequence_categorical_column_with_vocabulary_file:将序列数据根据词汇表文件转化成特征列。
  • sequence_categorical_column_with_vocabulary_list:将序列数据根据词汇表列表转化成特征列。
  • sequence_numeric_column:将序列数据转化成连续值特征列。

提示:

在TensorFlow 2.x版本中没有contrib模块。所以sequence_feature_column可以TensorFlow 1.x版本的使用。

二、实例演示

TensorFlow的特征列接口几乎可以涵盖于特征工程相关的所有转化操作,使用起来非常容易,下面通过介个例子来演示一下:

1.代码实现:用feature_column模块处理连续值特征列

连续值类型是TensorFlow中最简单、最常见的特征列数据类型。本实例通过4个小例子演示连续值特征列常见的使用方法。

1.显示一个连续值特征列

编写代码定义函数test_one_column。在test_one_column函数中具体完成了以下步骤:

(1)定义一个特征列。

(2)将带输入的样本数据封装成字典类型的对象。

(3)将特征列与样本数据一起传入tf.feature_column.input_layer函数,生成张量。

(4)建立会话,输出张量结果。

在第(3)步中用feature_column接口的input_layer函数生成张量。input_layer函数生成的张量相当于一个输入层,用于往模型中传入具体数据。input_layer函数的作用与占位符定义函数tf.placeholder的作用类似,都用来建立数据与模型之间的连接。

通过这几个步骤便可以将特征列的内容完全显示出来。具体代码如下:

代码7-3 用feature_column模块处理连续值特征列

因为在创建特征列price时只提供了名称“price”(见代码第6行),所以在创建字典features时,其内部的key必须也是“price”(见代码第8行)。

定义好函数test_one_column之后,便可以直接调用它(见代码第15行)。整个代码运行之后,显示以下结果:

[[1.]

[5.]]

结果中的数组来自于代码第8行字典对象features的value值。在第8行代码中,将值为[[1.],[5.]]的数据传入了字典features中。

在字典对象features中,关键字key的值是“price”,它所对应的值value可以是任意的一个数值。在模型训练时,这些值就是“price”属性所对应的具体数据。

2.通过占位符输入特征列

将占位符传入字典对象的值value中,实现特征列的输入过程。具体代码如下:

代码7-3 用feature_column模块处理连续值特征列(续)

在代码第19行,生成了带有占位符的字典对象features。

代码第23~25行,在会话中以注入机制传入数值[[1.], [5.]],生成转换后的具体列值。

整个代码运行之后,输出以下结果:

[[1.]

[5.]]

3.支持多维数据的特征列

在创建特征列时,还可以让一个特征列对应的数据有多维,即在定义特征列时为其指定形状。

提示:

特征列中的形状是指单条数据的形状,并非整个数据的形状。

具体代码如下:

代码7-3 用feature_column模块处理连续值特征列(续)

在代码第31行,在创建price特征列时,指定了形状为[1,2],即1行2列。

接着用两种方法向price特征列注入数据(见代码第32、33行)

  • 在代码第32行,创建字典features,传入了一个形状为[2,1,2]的三维数组。这个三维数组中的第一维是数据的条数(2条);第二维与第三维要与price指定的形状[1,2]一致。
  • 在代码第33行,创建字典features1,传入了一个形状为[2,2]的二维数组。该二维数组中的第一维是数据的条数(2条);第二维代表每条数据的列数(每条数据有2列)。

在代码第34、35行中,都用tf.feature_column模块的input_layer方法将字典features与features1注入特征列price中,并得到了张量net与net1。

代码运行后,张量net与net1的输出结果如下:

[[1. 2.] [5. 6.]]

[[3. 4.] [7. 8.]]

结果输出了两行数据,每一行都是一个形状为[2,2]的数组。这两个数组分别是字典features、features1经过特征列输出的结果。

提示:

代码第30行的作用是将图重置。该操作可以将当前图中的所有变量删除。这种做法可以避免在Spyder编译器下多次运行图时产生数据残留问题。

4.带有默认顺序的多个特征列

如果要创建的特征列有多个,则系统默认会按照每个列的名称由小到大进行排序,然后将数据按照约束的顺序输入模型。具体代码如下:

代码7-3 用feature_column模块处理连续值特征列(续)

在上面代码中,实现了以下操作。

(1)定义了3个特征列(见代码第42、43、44行)。

(2)定义了一个字典features,用于具体输入(见代码第46行)。

(3)用input_layer方法创建输入层张量(见代码第53行)。

(4)建立会话(session),输出输入层结果(见代码第55行)。

将程序运行后,输出以下结果:

[[1. 3. 4.]]

输出的结果为[[1. 3. 4.]]所对应的列,顺序为price_a、price_b、price_c。而input_layer中的列顺序为price_c、price_a、price_b(见代码第53行),二者并不一样。这表示,输入层的顺序是按照列的名称排序的,与input_layer中传入的顺序无关。

提示:

将input_layer中传入的顺序当作输入层的列顺序,这是一个非常容易犯的错误。

输入层的列顺序只与列的名称和类型有关(7.4.3小节“5. 多特征列的顺序”中还会讲到列顺序与列类型的关系),与传入input_layer中的顺序无关。

2.代码实现:将连续值特征列转化成离散值特征列

下面将连续值特征列转化成离散值特征列。

1.将连续值特征按照数值大小分类

用tf.feature_column.bucketized_column函数将连续值按照指定的阈值进行分段,从而将连续值映射到离散值上。具体代码如下:

代码7-4 将连续值特征列转化成离散值特征列

代码运行后,输出以下结果:

[[2. 1. 0. 0.]

[6. 0. 0. 1.]]

输出的结果中有两条数据,每条数据有4个元素:

  • 第1个元素为price列的具体数值。
  • 后面3个元素为price_bucketized列的具体数值。

从结果中可以看到,tf.feature_column.bucketized_column函数将连续值price按照3段来划分(小于3、3~5之间、大于5),并将它们生成one-hot编码。

2.将整数值直接映射到one-hot编码

如果连续值特征列的数据是整数,则还可以直接用tf.feature_column. categorical_column_with_identity函数将其映射成one-hot编码。

函数tf.feature_column.categorical_column_with_identity的参数和返回值解读如下。

  • 需要传入两个必填的参数:列名称(key)、类的总数(num_buckets)。其中,num_buckets的值一定要大于key列中所有数据的最大值。
  • 返回值:为_IdentityCategoricalColumn对象。该对象是使用稀疏矩阵的方式存放转化后的数据。如果要将该返回值作为输入层传入后续的网络,则需要用indicator_column函数将其转化为稠密矩阵。

具体代码如下:

代码7-4 将连续值特征列转化成离散值特征列(续)

代码运行后,输出以下结果:

[[2. 0. 0. 1. 0. 0. 0.]

[4. 0. 0. 0. 0. 1. 0.]]

结果输出了两行信息。每行的第1列为连续值price列内容,后面6列为one-hot编码。

因为在代码第23行,将price列转化为one-hot时传入的参数是6,代表分成6类。所以在输出结果中,one-hot编码为6列。

3.代码实现:将离散文本特征列转化为one-hot与词向量

离散型文本数据存在多种组合形式,所以无法直接将其转化成离散向量(例如,名字属性可以是任意字符串,但无法统计总类别个数)。

处理离散型文本数据需要额外的一套方法。下面具体介绍。

1.将离散文本按照指定范围散列的方法

将离散文本特征列转化为离散特征列,与将连续值特征列转化为离散特征列的方法相似,可以将离散文本分段。只不过分段的方式不是比较数值的大小,而是用hash算法进行散列。

用tf.feature_column.categorical_column_with_hash_bucket方法可以将离散文本特征按照hash算法进行散列,并将其散列结果转化成为离散值。

该方法会返回一个_HashedCategoricalColumn类型的张量。该张量属于稀疏矩阵类型,不能直接输入tf.feature_column.input_layer函数中进行结果输出,只能用稀疏矩阵的输入方法来运行结果。

具体代码如下:

代码7-5 将离散文本特征列转化为one-hot编码与词向量

本段代码运行后,会按以下步骤执行:

(1)将输入的['a']、['x']使用hash算法进行散列。

(2)设置散列参数hash_bucket_size的值为5。

(3)将第(1)步生成的结果按照参数hash_bucket_size进行散列。

(4)输出最终得到的离散值(0~4之间的整数)。

上面的代码运行后,输出以下结果:

稀疏矩阵:

SparseTensorValue(indices=array([[0, 0],

[1, 0]], dtype=int64), values=array([4, 0], dtype=int64), dense_shape=array([2, 1], dtype=int64))

稠密矩阵:

[[4]

[0]]

从最终的输出结果可以看出,程序将字符a转化为数值4;将字符b转化为数值0。

将离散文本转化成特征值后,就可以传入模型,并参与训练了。

提示:

有关稀疏矩阵的更多介绍可以参考《深度学习之TensorFlow——入门、原理与进阶实战》一书中的9.4.17小节。

2.将离散文本按照指定词表与指定范围混合散列

除用hash算法对离散文本数据进行散列外,还可以用词表的方法将离散文本数据进行散列。

用tf.feature_column.categorical_column_with_vocabulary_list方法可以将离散文本数据按照指定的词表进行散列。该方法不仅可以将离散文本数据用词表来散列,还可以与hash算法混合散列。其返回的值也是稀疏矩阵类型。同样不能将返回的值直接传入tf.feature_column.input_layer函数中,只能用“1. 将离散文本按照指定范围散列”中的方法将其显示结果。

具体代码如下:

代码7-5 将离散文本特征列转化为one-hot编码与词向量(续)

代码第29、30行向tf.feature_column.categorical_column_with_vocabulary_list方法传入了3个参数,具体意义如下所示。

  • name:代表列的名称,这里的列名就是name。
  • vocabulary_list:代表词表,其中词表里的个数就是总的类别数。这里分为3类('anna','gary','bob'),对应的类别为(0,1,2)。
  • num_oov_buckets:代表额外的值的散列。如果name列中的数值不在词表的分类中,则会用hash算法对其进行散列分类。这里的值为2,表示在词表现有的3类基础上再增加两个散列类。不在词表中的name有可能被散列成3或4。

提示:

tf.feature_column.categorical_column_with_vocabulary_list方法还有第4个参数:default_value,该参数默认值为1。

如果在调用tf.feature_column.categorical_column_with_vocabulary_list方法时没有传入num_oov_buckets参数,则程序将只按照词表进行分类。

在按照词表进行分类的过程中,如果name中的值在词表中找不到匹配项,则会用参数default_value来代替。

第33、38行代码,用_LazyBuilder函数构建程序的输入部分。该函数可以同时支持值为稠密矩阵和稀疏矩阵的字典对象。

运行代码,输出以下结果:

稀疏矩阵:

SparseTensorValue(indices=array([[0, 0],

[1, 0],

[2, 0]], dtype=int64), values=array([0, 1, 4], dtype=int64), dense_shape=array([3, 1], dtype=int64))

稀疏矩阵2:

SparseTensorValue(indices=array([[0, 0],

[1, 0],

[2, 0]], dtype=int64), values=array([0, 1, 4], dtype=int64), dense_shape=array([3, 1], dtype=int64))

稠密矩阵:

[[0]

[1]

[4]]

结果显示了3个矩阵:前两个是稀疏矩阵,最后一个为稠密矩阵。这3个矩阵的值是一样的。具体解读如下。

  • 从前两个稀疏矩阵可以看出:在传入原始数据的环节中,字典中的value值可以是稠密矩阵或稀疏矩阵。
  • 从第3个稠密矩阵中可以看出:输入数据name列中的3个名字('anna','gary', 'alsa')被转化成了(0,1,4)3个值。其中,0与1是来自于词表的分类,4是来自于hash算法的散列结果。

提示:

在使用词表时要引入lookup_ops模块,并且,在会话中要用lookup_ops.tables_initializer()对其进行初始化,否则程序会报错。

3.将离散文本特征列转化为one-hot编码

在实际应用中,将离散文本进行散列之后,有时还需要对散列后的结果进行二次转化。下面就来看一个将散列值转化成one-hot编码的例子。

代码7-5 将离散文本特征列转化为one-hot编码与词向量(续)

代码运行后,输出以下结果:

[[0. 0. 0. 0. 1.]

[1. 0. 0. 0. 0.]]

结果中输出了两条数据,分别代表字符“a”“x”在散列后的one-hot编码。

4.将离散文本特征列转化为词嵌入向量

词嵌入可以理解为one-hot编码的升级版。它使用多维向量更好地描述词与词之间的关系。下面就来使用代码实现词嵌入的转化。

代码7-5 将离散文本特征列转化为one-hot编码与词向量(续)

在词嵌入转化过程中,具体步骤如下:

(1)将传入的字符“a”与“x”转化为0~4之间的整数。

(2)将该整数转化为词嵌入列。

代码第91行,将数据字典features、词嵌入列embedding_col、列变量对象cols_to_vars一起传入输入层input_layer函数中,得到最终的转化结果net。

代码运行后,输出以下结果:

[[ 0.08975066 0.34540504 0.85922384]

[-0.22819372 -0.34707746 -0.76360196]]

从结果中可以看到,每个整数都被转化为3个词嵌入向量。这是因为,在调用tf.feature_column.embedding_column函数时传入的维度dimension是3(见代码第83行)。

提示:

在使用词嵌入时,系统内部会自动定义指定个数的张量作为学习参数,所以运行之前一定要对全局张量进行初始化(见代码第94行)。本实例显示的值,就是系统内部定义的张量被初始化后的结果。

另外,还可以参照《深度学习之TensorFlow工程化项目实战》一书7.5节的方式为词向量设置一个初始值。通过具体的数值可以更直观地查看词嵌入的输出内容。

5.多特征列的顺序

在大多数情况下,会将转化好的特征列统一放到input_layer函数中制作成一个输入样本。

input_layer函数支持的输入类型有以下4种:

  • numeric_column特征列。
  • bucketized_column特征列。
  • indicator_column特征列。
  • embedding_column特征列。

如果要将7.4.3小节中的hash值或词表散列的值传入input_layer函数中,则需要先将其转化成indicator_column类型或embedding_column类型。

当多个类型的特征列放在一起时,系统会按照特征列的名字进行排序。

具体代码如下:

代码7-5 将离散文本特征列转化为one-hot编码与词向量(续)

上面代码中构建了3个输入的特征列:

  • numeric_column列。
  • embedding_column列。
  • indicator_column列。

其中,embedding_column列与indicator_column列由categorical_column_with_hash_bucket方法列转化而来(见代码第104、106行)。

代码运行后输出以下结果:

asparse_feature_indicator

asparse_feature_embedding

numeric_col

[[-1.0505784 -0.4121129 -0.85744965 0. 0. 0. 0. 1. 3.]

[-0.2486877 0.5705532 0.32346958 1. 0. 0. 0. 0. 6.]]

输出结果的前3行分别是one_hot_col列、embedding_col列与numeric_col列的名称。

输出结果的最后两行是输入层input_layer所输出的多列数据。从结果中可以看出,一共有两条数据,每条数据有9列。这9列数据可以分为以下3个部分。

  • 第1部分是embedding_col列的数据内容(见输出结果的前3列)。
  • 第2部分是one_hot_col列的数据内容(见输出结果的第4~8列)。
  • 第3部分是numeric_col列的数据内容(见输出结果的最后一列)。

这个三个部分的排列顺序与其名字的字符串排列顺序是完全一致的(名字的字符串排列顺序为 asparse_feature_embedding、asparse_feature_indicator、numeric_col)。

4.代码实现:根据特征列生成交叉列

在《深度学习之TensorFlow工程化项目实战》一书7.2节中用tf.feature_column.crossed_column函数将多个单列特征混合起来生成交叉列,并将交叉列作为新的样本特征,与原始的样本数据一起输入模型进行计算。

本小节将详细介绍交叉列的计算方式,以及函数tf.feature_column.crossed_column的使用方法。

具体代码如下:

代码7-6 根据特征列生成交叉列

代码第5行用tf.feature_column.crossed_column函数将特征列b和c混合在一起,生成交叉列。该函数有以下两个必填参数。

  • key:要进行交叉计算的列。以列表形式传入(代码中是[b,ꞌcꞌ])。
  • hash_bucket_size:要散列的数值范围(代码中是5)。表示将特征列交叉合并后,经过hash算法计算并散列成0~4之间的整数。

提示:

tf.feature_column.crossed_column函数的输入参数key是一个列表类型。该列表的元素可以是指定的列名称(字符串形式),也可以是具体的特征列对象(张量形式)。

如果传入的是特征列对象,则还要考虑特征列类型的问题。因为tf.feature_column.crossed_column函数不支持对numeric_column类型的特征列做交叉运算,所以,如果要对numeric_column类型的列做交叉运算,则需要用bucketized_column函数或categorical_column_with_identity函数将numeric_column类型转化后才能使用。

代码运行后,输出以下结果:

SparseTensorValue(indices=array([[0, 0],

[0, 1],

[1, 0],

[1, 1],

[1, 2],

[1, 3]], dtype=int64), values=array([3, 1, 3, 1, 0, 4], dtype=int64), dense_shape=array([2, 4], dtype=int64))

[[ 3 1 -1 -1] [ 3 1 0 4]]

程序运行后,交叉矩阵会将以下两矩阵进行交叉合并。具体计算方法见式(7.1):

式(7.1)中,size就是传入crossed_column函数的参数hash_bucket_size,其值为5,表示输出的结果都在0~4之间。

在生成的稀疏矩阵中,[0,2]与[0,3]这两个位置没有值,所以在将其转成稠密矩阵时需要为其加两个默认值“1”。于是在输出结果的最后1行,显示了稠密矩阵的内容[[ 3 1 -1 -1] [ 3 1 0 4]]。该内容中用两个“1”进行补位。

三、在自然语言场景下的实例演示

下面以一个NLP数据的预处理场景为例,演示一下sequence_feature_column接口的具体使用。

1.代码实现:构建模拟数据

假设有一个字典,里面只有3个词,其向量分别为0、1、2。

用稀疏矩阵模拟两个具有序列特征的数据a和b。每个数据有两个样本:模拟数据a的内容是[2][0,1]。模拟数据b的内容是[1][2,0]。

具体代码如下:

代码7-7 序列特征工程

代码第5、10行分别用tf.SparseTensor函数创建两个稀疏矩阵类型的模拟数据。

2.代码实现:构建词嵌入初始值

词嵌入过程将字典中的词向量应用到多维数组中。在代码中,定义两套用于映射词向量的多维数组(embedding_values_a与embedding_values_b),并对其进行初始化。

提示:

在实际使用中,对多维数组初始化的值,会被定义成1~1之间的浮点数。这里都将其初始化成较大的值,是为了在测试时让显示效果更加明显。

具体代码如下:

代码7-7 序列特征工程(续)

3.代码实现:构建词嵌入特征列与共享特征列

使用函数sequence_categorical_column_with_identity可以创建带有序列特征的离散列。该离散列会将词向量进行词嵌入转化,并将转化后的结果进行离散处理。

使用函数shared_embedding_columns可以创建共享列。共享列可以使多个词向量共享一个多维数组进行词嵌入转化。具体代码如下:

代码7-7 序列特征工程(续)

4.代码实现:构建序列特征列的输入层

用函数tf.contrib.feature_column.sequence_input_layer构建序列特征列的输入层。该函数返回两个张量:

  • 输入的具体数据。
  • 序列的长度。

具体代码如下:

代码7-7 序列特征工程(续)

代码第52行,用sequence_input_layer函数生成了输入层input_layer张量。该张量中的内容是按以下步骤产生的。

(1)定义原始词向量。

  • 模拟数据a的内容是[2][0,1]。
  • 模拟数据b的内容是[1][2,0]。

(2)定义词嵌入的初始值。

  • embedding_values_a的内容是:[ (1., 2.),(3., 4.),(5., 6.) ]。
  • embedding_values_b的内容是:[(11., 12., 13.), (14., 15., 16.), (17., 18., 19.) ]。

(3)将词向量中的值作为索引,去第(2)步的数组中取值,完成词嵌入的转化。

  • 特征列embedding_column_a:将模拟数据a经过embedding_values_a转化后得到 [[5.,6.],[0,0]][[1.,2.],[3.,4.]]。
  • 特征列embedding_column_b:将模拟数据b经过embedding_values_b转化后得到[[14., 15., 16.],[0,0,0]][[ 17., 18., 19.],[ 11., 12., 13.]]。

提示:

sequence_feature_column接口在转化词嵌入时,可以对数据进行自动对齐和补0操作。在使用时,可以直接将其输出结果输入RNN模型里进行计算。

由于模拟数据a、b中第一个元素的长度都是1,而最大的长度为2。系统会自动以2对齐,将不足的数据补0。

(4)将embedding_column_b和 embedding_column_a两个特征列传入函数sequence_input _layer中,得到input_layer。根据7.4.3小节介绍的规则,该输入层中数据的真实顺序为:特征列embedding_column_a在前,特征列embedding_column_b在后。最终input_layer的值为:[[5.,6.,14., 15., 16.],[0,0, 0,0,0]][[1.,2., 17., 18., 19.],[3.,4. 11., 12., 13.]]。

代码第61行,将运行图中的所有张量打印出来。可以通过观察TensorFlow内部创建词嵌入张量的情况,来验证共享特征列的功能。

5.代码实现:建立会话输出结果

建立会话输出结果。具体代码如下:

代码7-7 序列特征工程(续)

代码运行后,输出以下内容:

(1)输出3个词嵌入张量。第3个为共享列张量。

['sequence_input_layer/a_embedding/embedding_weights:0', 'sequence_input_layer/b_embedding/embedding_weights:0', 'sequence_input_layer_1/a_b_shared_embedding/embedding_weights:0']

(2)输出词嵌入的初始化值。

[[1. 2.]

[3. 4.]

[5. 6.]]

[[11. 12. 13.]

[14. 15. 16.]

[17. 18. 19.]]

[[1. 2.]

[3. 4.]

[5. 6.]]

输出的结果共有9行,每3行为一个数组:

  • 前3行是embedding_column_a。
  • 中间3行是embedding_column_b。
  • 最后3行是shared_embedding_columns。

(3)输出张量input_layer的内容。

[1 2]

[[[ 5. 6. 14. 15. 16.] [ 0. 0. 0. 0. 0.]]

[[ 1. 2. 17. 18. 19.] [ 3. 4. 11. 12. 13.]]]

输出的结果第1行是原始词向量的大小。后面两行是input_layer的具体内容。

(4)输出张量input_layer2的内容。

[1 2]

[[[5. 6. 3. 4.] [0. 0. 0. 0.]]

[[1. 2. 5. 6.] [3. 4. 1. 2.]]]

模拟数据sparse_input_a与sparse_input_b同时使用了共享词嵌入embedding_values_a。每个序列的数据被转化成两个维度的词嵌入数据。

以上内容来自于《深度学习之TensorFlow工程化项目实战》一书。如果你想更全面的了解TensorFlow的更多接口和使用方法,请参考此书。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档