##好好好好###开源的标注工具

## 开源的标注工具

自然语言处理标记工具汇总
https://blog.csdn.net/wangyizhen_nju/article/details/94559607

spacy原来有两个标注工具,displaCy-ent和displaCy,一个ner一个依赖关系.

 

Annotator for Chinese Text Corpus (UNDER DEVELOPMENT) 中文文本标注工具

 

自然语言处理的大部分任务是监督学习问题。序列标注问题如中文分词、命名实体识别,分类问题如关系识别、情感分析、意图分析等,均需要标注数据进行模型训练。深度学习大行其道的今天,基于深度学习的 NLP 模型更是数据饥渴。

最前沿的 NLP 技术往往首先针对英文语料。英文 NLP 的生态很好,针对不同有意思的问题都有不少大规模语料公开供大家研究,如斯坦福的 SQuAD 阅读理解语料。中文方面开源语料就少得多,各种英文 NLP 上的犀利模型和前沿技术都因为中文语料的匮乏很难迁移过来。

另一方面,对于一些垂直领域,如医疗、金融、法律、公安等等,专有名词和特有需求甚多,很难将比较 general 的比如在 wikipedia dump 上面训练的模型直接拿过来用。

传统人工标注数据的过程往往是繁琐和低效率的。刚标了一个“联想”是公司名,又来一个“联想集团”,再标一次又来一个“联想集团有限公司”,如此的例子令标注过程含有大量的重复劳动。另一方面也没有一个易上手的标注 UI,标注工作者往往需要直接按预先定好的格式直接在写字板之类的软件中修改原始数据,格式错误率也较高。

能不能构建一个中文文本的标注工具,可以达到以下两个特点:

\1. 标注过程背后含有智能算法,将人工重复劳动降到最低;

\2. 标注界面显而易见地友好,让标注操作尽可能简便和符合直觉。

答案是可以的。事实上很多标注工具已经做到了这一点,最先进的如 Explosion.ai 的 Prodigy;然而开发了著名的 NLP 开源包 Spacy 的 explosion.ai 选择了将 Prodigy 闭源,而 Spacy 支持中文也仍然遥遥无期。我们希望构建一个开源的中文文本标注工具,而本文很多的技术灵感正是来自 Prodigy 文档。

主动学习的智能标注算法
流程:

\1. 用户标一个label;

\2. 主动学习的后台算法分为 online 和 offline 部分。online 部分即时更新模型,可使用诸如 SVM、bag of words 等尽可能快的传统方法;offline 部分当标注数据积累到一定数量时更新模型,可使用准确度较高的深度学习模型;

\3. 模型更新后,对尽可能多的 example 做预测,将确信度排序,取确信度最低的一个 example 作为待标注例子。重复 1 的过程。

可以想象如果模型训练得好的话,这个过程将直接忽略掉确信度最大的那些例子,而把所有重点放在分类边界上的那些确信度小的例子。这样可以尽算法所能减少用户端的人工工作量。

online 与 offline 模型互相协作,与用户手动标注的过程一起不断迭代;在最终标注任务完成之后,offline 模型可以重新在所有标注数据上重新训练,以达到最好的模型效果。

显而易见的友好标注前端
用户标注的界面应该尽可能符合直觉,让用户完全聚焦在当前的标注任务上。 Prodigy 给了一个非常好的 demo,每一次的标注只需要用户解决一个 case 的问题。以文本分类为例,对于算法给出的分类结果,只需要点击“正确”提供正样本,“错误”提供负样本,“略过”将不相关的信息滤除,“Redo”让用户撤回操作,四个功能键以最简模式让用户进行标注操作。

真正应用中,应该还要加入一个用户自己加入标注的交互方式,比如用户可以高亮一个词然后选择是“公司”,或者链接两个实体选择他们的关系等等。

 

以上是个人觉得的一个智能中文文本标注工具的最大亮点。算法本身还有很多细节需要思考,比如 online 机器学习算法与 offline 深度学习算法的协作、中文 NLP 的特征提取与模型构建、正则规则的引入、word embedding 的训练和使用等等。

系统本身还要考虑后台存储(SQLite?)和数据导入导出,前端框架选型和开发,前后端交互(django? flask? RestAPI?)等等的问题。下面是 Prodigy 的简单架构图。

 

我们希望专注于中文文本标注的功能。前期我们想实现三种中文 NLP 任务的标注工具:中文命名实体识别,中文关系识别,中文文本分类。未来如果有更多如中文图片问答、中文图片描述之类的任务,我们可以再研究加入图片标注这一块。

希望这个工具的开发会是以中文社区的开源协作方式,为整个中文 NLP 的开源生态做出一点贡献。

Zhong__CentOS7安装配置Brat
https://blog.csdn.net/anyedianxia/article/details/96832229

中文文本标注工具调研以及BRAT安装使用

find ztbgg -name '*.txt'|sed -e 's|.txt|.ann|g'|xargs touch

http://brat.nlplab.org/standoff.html

用brat创建的批注以对峙格式存储在磁盘上:批注与带批注的文档文本分开存储,该工具永远不会对其进行修改。

对于系统中的每个文本文档,都有一个对应的注释文件。例如,该文件:两个由文件命名约定,他们的基本名称(无后缀的文件名)是一样的associatied DOC-1000.ann包含文件的注释DOC-1000.txt。

在文档中,各个注释通过字符偏移量连接到特定的文本范围。例如,在开始于“今天被...袭击日本”的文档中,文本“日本”由偏移范围0..5标识。(所有偏移量都从0开始索引,并在起始偏移量处包含字符,但在结束偏移量处排除字符。)

臭小子所使用的特定防区味类似于 BioNLP Shared Task防区格式,并在下面进行了详细描述。

文字档(.txt)
文本文件应具有后缀.txt,并包含输入到系统中的原始文档的文本。

 

文档文本存储在使用UTF-8编码的纯文本文件中 (ASCII的扩展名 -纯ASCII文本也起作用)。 文档文本可能包含换行符,这些换行符将以小标题显示为换行符。但是,文档中不必包含任何换行符:brat可以使用可靠的算法执行自己的句子分段以供显示。(无论原始文本文档中是否包含换行符,文本文件本身都不会被修改。)

注释文件(.ann)
批注以.ann 后缀存储在文件中。下面讨论这些文件中可能包含的各种注释类型。

通用注释结构
所有注释都遵循相同的基本结构:每行包含一个注释,并且为每个注释赋予一个ID,该ID首先出现在该行上,并由一个TAB字符与其余注释分开。其余结构因注释类型而异。

实体(T1),事件触发器(T2),事件(E1) 和关系(R1)的注释示例如下所示。

 


T1  组织0 4 索尼
T2  合并组织14 27 合资企业
T3  组织33 41 爱立信
E1  MERGE-ORG:T2 Org1:T1 Org2:T3
T4  国家75 81 瑞典
R1  原点Arg1:T3 Arg2:T4

这些注释的详细说明在下面给出。

文本绑定注释
绑定文本的注释是与实体和事件注释相关的注释的重要类别。绑定文本的注释标识特定的文本范围并为其指定类型。

 

所有与文本绑定的注释都遵循相同的结构。与所有注释一样,该ID首先出现,并以TAB字符与该行的其余部分分隔。主要注释以空格分隔的三元组(类型,起始偏移量,结束偏移量)给出。起始偏移量是文本(“ .txt”文件)中带注释的跨度的第一个字符的索引,即,其前面的文档中的字符数。结束偏移是带注释的跨度之后第一个字符的索引。因此,在偏移结束位置中的字符不包括在带注释的跨度中。作为参考,包含由注释跨越的文本,以TAB字符分隔。

从v1.3开始,小子还支持 不连续的文本绑定注释,其中注释涉及多个连续字符。这些注释的隔离表示是单跨度情况的直接扩展。例如,“北美和南美”的一种可能的注释将表示如下:

 

形成不连续注释的(开始偏移,结束偏移)对之间用分号分隔,并且这些跨度的文本由单个空格字符连接起来以形成注释的参考文本。

注释ID约定
所有注释ID均由一个识别注释类型的大写字母和一个数字组成。初始ID字符与注释类型有关,如下所示:

T:文本绑定注释

R:关系

E:事件

A:属性

M:修改(属性的别名,用于向后兼容)

N:规范化[v1.3中的新功能]

#:注意

此外,在特殊情况下,星号(“ *”)可用作ID的占位符。

实体注释
每个实体注释都有一个唯一的ID,并由类型(例如Person或Organization)和包含实体说明的字符范围(表示为“开始-结束”偏移对)定义。

 

每行包含一个文本绑定的注释, 用于标识文本中的实体。

事件注释
每个事件注释都有一个唯一的ID,并由类型(例如MERGE-ORG),事件触发器(说明事件的文本)和参数定义。

 

事件触发器(标记单词的注释或表示每个事件的单词)是文本绑定的注释 ,其格式与实体的格式相同。(触发器的ID与实体的ID占用相同的空间,并且它们不能重叠。)

对于所有注释,事件ID首先出现,并用TAB字符分隔。事件触发器指定为TYPE:ID,并通过ID标识事件类型及其触发器。按照约定,事件类型在触发器注释和事件注释中都指定。事件触发器由SPACE与事件参数分开。事件参数是一组用空格分隔的ROLE:ID对,其中ROLE是事件和任务特定的参数角色之一(例如Theme,Cause,Site),并且ID标识填充该角色的实体或事件。请注意,多个事件可以共享同一触发器,并且虽然应首先指定事件触发器,但事件参数可以按任何顺序出现。

关系注释
二进制关系具有唯一的ID,并由其类型(例如Origin,Part-of)及其参数定义。

 

格式与事件所用的格式类似,不同之处在于,注释未标识表示关系的特定文本(“触发器”):ID用TAB字符分隔,关系类型和自变量用SPACE分隔。 。

关系参数通常简单地标识为Arg1和Arg2,但是系统可以配置为在对峙表示中使用任何标签(例如Anaphor 和Antecedent)。

等价关系

系统还支持对等关系的特殊语法。等价关系是对称的和传递的关系,它们将注释的集合定义为在某种意义上是等价的(例如,引用相同的现实世界实体)。可以以紧凑的方式将这些关系表示为等效注释的ID的以空格分隔的列表。

 

为了向后兼容现有的防区隔格式,小子还支持特殊的“空” ID值“ * ”用于等价关系注释。

属性和修改注释
属性注释是二进制或多值“标志”,用于指定其他注释的其他方面。属性具有唯一的ID,并且通过引用该属性标记的注释的ID和属性值来定义。

 

至于其他注释,ID用TAB分隔,其他字段用空格分隔。

上面示例中的二进制属性(例如A1)仅需要指定属性名称和已标记注释的ID:二进制属性隐含值true。缺少二进制属性注释将被解释为值为false的属性。

多值属性还指定属性值,以SPACE分隔。多值属性的值是完全可配置的。

为了与现有隔离格式向后兼容,小子还可以识别属性的ID前缀“ M ”。

规范化注释
从v1.3开始,支持规范化注释。每个规范化注释具有唯一的ID,并通过参考规范化所附加的注释的ID以及一RID:EID对标识外部资源(RID)和该资源中的条目(EID)的对进行定义。此外,每个规范化注释都具有类型Reference(当前未定义该类型的其他值)和所引用条目的人类可读字符串值。

以下示例显示附加到文本绑定的注释“ T1”(未显示)的规范化注释,并将其与具有Wikipedia ID“ 534366”(“ Barack Obama”)的Wikipedia条目相关联。

 

(请注意,EID“ Wikipedia”或“ GO” 之类的值与相关外部资源的关联未在对峙中表示,而是由tools.conf配置文件控制 。)

对于文本绑定的注释,ID和文本由TAB字符以及其他字段(此处为“参考”,“ T1”和“ Wikipedia:534366”)分隔,并由SPACE分隔。

注释注释
注释注释提供了一种将自由格式文本与文档或特定注释相关联的方法。注释行以数字(或“哈希”)符号#开头。

 

以#开头,后跟TAB字符的“ ID” 注释将附加到特定注释。对于这些注释,第二个TAB分隔字段包含注释类型和注释附加到的注释的ID,第三个TAB分隔字段包含注释文本。

注释类型可以自由分配,并且可以将任意数量的注释附加到单个注释中。(但是,目前只能从臭名昭著的用户界面中编辑AnnotatorNotes类型的单个注释。)

 

Part B
brat里面四个配置文件

annotation.conf: 标注类别

visual.conf: 标注显示

tools.conf: 标注工具

kb_shortcuts.conf: 快捷键

 

annotation.conf 分成四个section:
[entities]
基本结构是

[entities]

Person

Location

Organization

复杂的层级结构如下:

[entities]

Living-thing

Person

Animal

Plant

Nonliving-thing

Building

Vehicle

[relations]
只能表示二元关系,如下:

[relations]

Family Arg1:Person, Arg2:Person

Employment Arg1:Person, Arg2:Organization

当然,每个二元关系中的实体可以是多种类型,如下:

[relations]

Located Arg1:Person, Arg2:Building|City|Country

Located Arg1:Building, Arg2:City|Country

Located Arg1:City, Arg2:Country

[events]
事件与关系有点类似,但是可以是一元到多元的

[events]

Marriage Participant1:Person, Participant2:Person

Bankruptcy Org:Company

[attributes]
属性可以用来标记其他标记,比如对event标记进行标记,标记类型可以是二值的(true/false)或者多值的,如下:

[attributes]

Negated Arg:<EVENT>

Confidence Arg:<EVENT>, Value:L1|L2|L3

Visual configuration (visual.conf)
可视化分成两个部分:

[labels]
标签是用来可视化的,为了空间考虑,有不同的缩写形式

[labels]

Organization | Organization | Org

Immaterial-thing | Immaterial thing | Immaterial | Immat

[drawing]
用来控制显示的颜色,不设置则采用系统默认设置

[drawing]

Person bgColor:#ffccaa

Family fgColor:darkgreen, arrowHead:triangle-5

 

Tool configuration (tools.conf)
标记工具有五个部分:

[options]
有如下选项可以使用

Tokens tokenizer:VALUE, where VALUE=

whitespace: split by whitespace characters in source text (only)

ptblike: emulate Penn Treebank tokenization

mecab: perform Japanese tokenization using MeCab

Sentences splitter:VALUE, where VALUE=

regex: regular expression-based sentence splitting

newline: split by newline characters in source text (only)

Validation validate:VALUE, where VALUE=

all: perform full validation

none: don't perform any validation

Annotation-log logfile:VALUE, where VALUE=

<NONE>: no annotation logging

NAME: log into file NAME (e.g. "/home/brat/work/annotation.log")

下面是一个实例:

[options]

Tokens tokenizer:mecab

Sentences splitter:newline

Validation validate:all

Annotation-log logfile:/home/brat/work/annotation.log

[search]
[search]

Google <URL>:http://www.google.com/search?q=%s

Wikipedia <URL>:http://en.wikipedia.org/wiki/%s

[normalization]
[normalization]

Wiki DB:dbs/wiki, <URL>:http://en.wikipedia.org, <URLBASE>:http://en.wikipedia.org/?curid=%s

UniProt <URL>:http://www.uniprot.org/, <URLBASE>:http://www.uniprot.org/uniprot/%s

[annotators]
[annotators]

SNER-CoNLL tool:Stanford_NER, model:CoNLL, <URL>:http://example.com:80/tagger/

 

[disambiguators]
[disambiguators]

simsem-MUC tool:simsem, model:MUC, <URL>:http://example.com:80/simsem/%s

 

下面是一个小例子:

在data 文件夹下新建目录stock,包括三个文件

stock

--1.txt 待标记文本

--1.ann 空文件

--annotation.conf 配置文件

配置文件如下:

[entities]

OTH

LOC

NAME

ORG

TIME

TIL

NUM

[relations]

[events]

[attributes]

标注过程如下:

 

 

 

参考文献:

https://blog.csdn.net/tcx1992/article/details/80580089

http://brat.nlplab.org

FAQ
1. 待标注数据集如何分割?

应该分为按句子、按段落、按文章三种,写入配置文件由用户选择。 原因是命名实体识别与关系抽取可能按句子或者段落为单位给用户标注比较合适;同时可能用户会有全文章分类的需求,需要给出全文。

2. 为什么要使用 online?

用户标注数据 + offline 标注数据,为什么还要使用 online model 更新数据呢?原因是 offline 的模型往往在全量数据上重新学习,也很可能需要使用深度学习模型,训练的速度会很慢。而 active learning 的人机迭代过程要求模型给出几乎实时的 stream 级别的训练和推断速度,这时候就需要 online model 来先行更新数据。

3. 使用什么机制触发 offline model?

这也可以是写入配置文件的参数。一种是用户标够了 100 个或提前设置好的足够多的新的数据,就可以启用 offline model 进行训练;另一种是给用户一个按钮,用户可以点击启动后台的 offline 模型训练并给出进度条。

4. 系统使用什么格式的配置文件?

推荐 json 格式的配置文件。请参考一个例子在这里。

5. AIgo Factory 是什么?和 User Instance 里面的部分是不是有点重合?

Algo factory 是算法的代码模块,你可以想象一堆 tensorflow 或者 sklearn 的代码;而 user instance 是 config 文件与模型参数,是一堆用户生成的 json 文件和模型文件。algo factory 是可以不同 user instance 传入参数复用的,而每一个 user instance 代表了一个用户任务的实例。

这样设计的目的,是尽可能使系统可复用部分模块化,而抽出用户具体任务的配置与数据单独存储管理。

附录:几个开源文本标注工具
• IEPY

整个工程比较完整,有用户管理系统。前端略重,对用户不是非常友好。

代码:https://github.com/machinalis/iepy

说明:http://iepy.readthedocs.io/en/latest/index.html

• DeepDive (Mindtagger)

 

△ Screenshot of Mindtagger precision task in progress

前端比较简单,用户界面友好。

介绍:http://deepdive.stanford.edu/labeling

前端代码:https://github.com/HazyResearch/mindbender

将 DeepDive 的 corenlp 部分转为支持中文的代码尝试:

https://github.com/SongRb/DeepDiveChineseApps

https://github.com/qiangsiwei/DeepDive_Chinese

https://github.com/mcavdar/deepdive/commit/6882178cbd38a5bbbf4eee8b76b1e215537425b2

• BRAT

 

介绍:http://brat.nlplab.org/index.html

在线试用:http://weaver.nlplab.org/~brat/demo/latest/#/

代码:https://github.com/nlplab/brat

• SUTDAnnotator
 

用的不是网页前端而是 pythonGUI,但比较轻量。

代码:https://github.com/jiesutd/SUTDAnnotator

Paper:https://github.com/jiesutd/SUTDAnnotator/blob/master/lrec2018.pdf

• Snorkel
 

Page: https://hazyresearch.github.io/snorkel/

Github: https://github.com/HazyResearch/snorkel

Demo Paper: https://hazyresearch.github.io/snorkel/pdfs/snorkel_demo.pdf

• Slate

 

 

Code: https://bitbucket.org/dainkaplan/slate/

Paper: http://www.jlcl.org/2011_Heft2/11.pdf

• Prodigy

 

和著名的 spacy 是一家做的。

Website: https://prodi.gy/docs/

Blog: https://explosion.ai/blog/prodigy-annotation-tool-active-learning

招募
中文文本标注工具 Chinese-Annotator,是一个集合了前后端开发和 NLP 算法开发的富有挑战性的全栈开源项目。

万事开头难。前期的开发需要核心团队协同完成,我们需要你的帮助!

1. 程序架构

程序框架开发,与前后端及算法模块紧密沟通,定义函数功能,规范接口和参数。 我们希望参考rasa_nlu的用户配置设计将通用算法模块和用户数据隔离开,同时参考iepy的前后端设计和算法尤其是 active learning 部分的设计。

2. 前后端开发

前端使用 vue+flask 构架全栈开发,完成 web 用户中文文本标注的动态交互。后端业务逻辑支撑和可扩展的数据持久化。

3. NLP 算法模块开发

传统 NLP 特征工程,传统分类(SVM)和序列标注(CRF,HMM)算法开发,深度学习 NLP 的分类和序列标注算法开发。online 及 offline 算法优化,以及接口 API 开发。

 

Chinese-Annotator Gitter 聊天室
我可以先搞离线的序列标注试试,  

能不能用ChineseAnnotator标注事件?

暂时只是分类模型和序列标注模型的标注。事件可以当做分类模型来看?

你好,有计划做中文分词的标注吗

@ljwh 中文分词也是序列标注模型,可以做的。计划把这几种不同的应用定义好格式,做成标注工具template,直接load就可以用了。

@gwc19880401 欢迎给出一些应用的例子,我们可以根据实际需求来更好地设计后台算法和前端交互。

依存分析比较重要。不知道大家有没有标注经验?

我放一个例子上来。

三个月的宝宝为啥每天晚上睡着了过了半小时都没到就哭

这是一个真实的问句。我们的目标是回答这样的问句。

@crownpku 在序列标注中,可不可以把分词,句法分析,词性标注,ner等等融合到一起,一次标注多个标签

@gwc19880401 依存分析,可否给出你的这个例子的标注结果?

@ljwh 这是个好主意,但可能一次标这么多会加重标注者的压力,带来一些困惑和标注门槛。这个前端交互需要仔细考量一下。

@crownpku 嗯,可以做成可配的

 

现在是什么进度了??

暂时进展不是很快,最近假期比较多...当前还在做后台任务调度的一些部分。

项目什么时候可以跑起来?

现在到什么程度了?

 

运行docker, 反馈没有/Chinese-Annotator/chi_annotator/webui/apis/ca.py文件,我也在源代码中没有找到相应文件

 

@sunnybest1990 最近大家都很忙 项目还不可用

后续会把部署的脚本全部完成

@JavanTang

运行是需要使用Docker吗?

 

Jun 06 2018 19:04

这个项目太酷了!直击现在标注类工具效率低下的痛点,太有价值了

这个项目还在进行吗? 刚尝试运行了一下,各种问题,Dockerfile 里面好几个地方都没有配置对, 改完之后django服务起来了,浏览器访问报 page not found。

我也想参加项目,标注是NLP迈过不去的槛

@Anandaely

Jun 26 2018 18:53

很赞,最近在负责一个标注平台项目,在做一些“基于主动学习算法减少人工标注量,提升文本语料标注效率与准确率的方案探究”,想试试用该方案实践下

 

 

 

@grzhan

Jun 30 2018 18:31

@crownpku 你好,这边是森亿智能医疗公司的工程师,之前负责公司 NLP 标注系统的研发。 我们的标注系统是取自 brat 的灵感,在公司内已经用了两年了: 这是目前开源的 Editor:https://github.com/synyi/annotation-tool 现在来看代码还是有些问题的,最近正在起个新项目重构然后开源。

编辑器基本上是基于 SVG 做的,标注系统本身采用 Angular + Python(Hug) 项目虽然因为是内部项目当时开发比较匆忙,很多地方没考虑周全不过所幸基本可用这样

我们对于 Chinese-Annotator 这个项目非常感兴趣,鉴于目前还没发布,所以想问问有什么可用帮忙地方

 

找了半天资料,费了好大劲把项目搭起来。项目现在用的技术很多,我看到webui这边就分两种,django和nodejs的,都实现了文本分类,其它好像好没有找到使用方法。有能帮上忙的请说

@grzhan 你们的产品形态正是我们想要做的

 

@suiyuan2009

Jul 27 2018 18:11

请问现在项目是什么进度,我们现在在做金融文本上的实体识别和关系识别,目前有什么好的标注工具吗?

 

目前我们的标注工具已经重写并命名为 Poplar (https://github.com/synyi/poplar ),今天也发布了一个简单的 demo:https://synyi.github.io/poplar/ 感兴趣的同学也可以看一下,我们会持续开发并维护这个项目,欢迎提交 issue 与 star :)

 

Aug 30 2018 09:56

@playma 首先安装npm包,然后构造Annotator对象即可

@playma 详细文档在这里 https://github.com/synyi/poplar/blob/master/doc/zh.md

 

@playma 其实不一定需要typescript

@playma 你把所有的类型去掉

@playma 就是普通的js

我下次把文档改成纯js的吧 

@playma

Aug 30 2018 10:06

​ create 一個 html $ create 一個 main.js,把這段複製進去

import {Annotator} from 'poplar-annotation' /**

构造Annotator对象

@param data 数据,可以为JSON格式或纯文本

@param htmlElement 要放置内容的html元素

@param config 配置对象 */ new Annotator(data: string, htmlElement: HTMLElement, config?: Object)

$ node main.js

龙方淞

 

@longfangsong

Aug 30 2018 10:06

@playma js的话这样启动

let originString = 'hello world';
let annotator = new Annotator(originString, document.getElementById('test'));
@playma 这是前端的包,你需要加在html里的<script>里面,用node跑是没用的

 

@longfangsong

Aug 30 2018 10:19

@playma 你可以参考下我们的demo

https://github.com/synyi/poplar/tree/demo

是用Vue写的
 

©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值