一起来学自然语言处理----语料库和词汇资源


  🙈🙈 没错,开始还是一大段劝你好好学习的废话,生活有苦有甜、鸡汤有淡有咸。无聊就好好学习!

  计算语言学是一个广泛应用于分析、软件应用程序和人机交互上下文的新兴领域。我们可以认为其是人工智能的一个子领域。计算语言学的应用范围包括机器翻译、语音识别、智能 Web 搜索、信息检索和智能拼写检查等。理解各种可以在自然语言文本上执行的预处理任务或者计算是至关重要的。那前问已经带领熟悉了自然语言处理中可能涉及的各种操作。那这里就重新开始,系统的开始学习自然语言处理。

1、自然语言工具包(NLTK)

  NLTK 创建于 2001 年,最初是宾州大学计算机与信息科学系计算语言学课程的一部分。从那以后,在数十名贡献者的帮助下不断发展壮大。如今,它已被几十所大学的课程所采纳,并作为许多研究项目的基础。下面列出了 NLTK 的一些最重要的模块。
在这里插入图片描述

2、获取文本语料

  在自然语言处理的实际项目中,通常要使用大量的语言数据或者语料库,那python有哪些语料库,分别适用于什么场景呢?

1.语料库

古腾堡语料库

  NLTK 包含古腾堡项目(Project Gutenberg)电子文本档案的经过挑选的一小部分文本。该项目大约有 25,000(现在是 36,000 了)本免费电子图书。通过下面代码认识了解这个语料库。

from nltk.corpus import gutenberg 
for fileid in gutenberg.fileids():
    num_chars = len(gutenberg.raw(fileid))
    num_words = len(gutenberg.words(fileid))
    num_sents = len(gutenberg.sents(fileid))
    num_vocab = len(set([w.lower() for w in gutenberg.words(fileid)]))
    print (int(num_chars/num_words), int(num_words/num_sents), int(num_words/num_vocab), fileid)

4 24 26 austen-emma.txt
4 26 16 austen-persuasion.txt
...

  上述代码显示每个文本的三个统计量:平均词长、平均句子长度和本文中每个词出现的平均次数(我们的词汇多样性得分)。请看,平均词长似乎是英语的一个一般属性,因为它的值总是 4。(事实上,平均词长是 3 而不是 4,因为 num_chars 变量计数了空白字符。)相比之下,平均句子长度和词汇多样性看上去是作者个人的特点。
  raw()函数给我们没有进行过任何语言学处理的文件的内容,包括词之间的空格。sents()函数把文本划分成句子,其中每一个句子是一个词链表。
  除了 words()、raw()和 sents()以外,大多数 NLTK 语料库阅读器还包括多种访问方法。一些语料库提供更加丰富的语言学内容,例如:词性标注,对话标记,句法树等。后面慢慢接触了解。

网络和聊天文本

  虽然古腾堡项目包含成千上万的书籍,它代表既定的文学。考虑较不正式的语言也是很重要的。NLTK 的网络文本小集合的内容包括 Firefox 交流论坛,在纽约无意听到的对话,《加勒比海盗》的电影剧本,个人广告和葡萄酒的评论等等。

from nltk.corpus import webtext
for fileid in webtext.fileids():
    print (fileid, webtext.raw(fileid)[:65])
    
firefox.txt Cookie Manager: "Don't allow sites that set removed cookies to se
grail.txt SCENE 1: [wind] [clop clop clop] 
KING ARTHUR: Whoa there!  [clop
overheard.txt White guy: So, do you have any plans for this evening?
Asian girl
pirates.txt PIRATES OF THE CARRIBEAN: DEAD MAN'S CHEST, by Ted Elliott & Terr
singles.txt 25 SEXY MALE, seeks attrac older single lady, for discreet encoun
wine.txt Lovely delicate, fragrant Rhone wine. Polished leather and strawb

  还有一个即时消息聊天会话语料库,最初由美国海军研究生院为研究自动检测互联网幼童虐待癖而收集的。语料库包含超过 10,000 张帖子,以“UserNNN”形式的通用名替换掉用户名,手工编辑消除任何其他身份信息,制作而成。语料库被分成 15 个文件,每个文件包含几百个按特定日期和特定年龄的聊天室(青少年、20 岁、30 岁、40 岁、再加上一个通用的成年人聊天室)收集的帖子。文件名中包含日期、聊天室和帖子数量,例如:10-19-20s_706posts.xml 包含 2006 年 10 月 19 日从 20 多岁聊天室收集的 706 个帖子。

from nltk.corpus import nps_chat
chatroom = nps_chat.posts('10-19-20s_706posts.xml')
print(chatroom[123])

['i', 'do', "n't", 'want', 'hot', 'pics', 'of', 'a', 'female', ',', 'I', 'can', 'look', 'in', 'a', 'mirror', '.']

布朗语料库

  布朗语料库是第一个百万词级的英语电子语料库的,由布朗大学于 1961 年创建。这个语料库包含 500 个不同来源的文本,按照文体分类,如:新闻、社论等。下面给出了各个文体的例子。
在这里插入图片描述
  布朗语料库是一个研究文体之间的系统性差异——一种叫做文体学的语言学研究——很方便的资源。让我们来比较不同文体中的情态动词的用法。

from nltk.corpus import brown
brown.categories()
brown.words(categories='news')
brown.words(fileids=['ca16'])
import nltk
from nltk.corpus import brown
news_text = brown.words(categories='news')
fdist = nltk.FreqDist([w.lower() for w in news_text])
modals = ['can', 'could', 'may', 'might', 'must', 'will']
for m in modals:
    print (m + ':', fdist[m])

can: 94
could: 87
may: 93
might: 38
must: 53
will: 389

  我们来统计每一个感兴趣的文体。我们使用 NLTK 提供的带条件的频率分布函数,将在后面讲解到,这里只看用法和结果,不看细节。

cfd = nltk.ConditionalFreqDist(
	(genre, word)
	for genre in brown.categories()
	for word in brown.words(categories=genre))
genres = ['news', 'religion', 'hobbies', 'science_fiction', 'romance', 'humor']
modals = ['can', 'could', 'may', 'might', 'must', 'will']
cfd.tabulate(conditions=genres, samples=modals)

                  can could   may might  must  will 
           news    93    86    66    38    50   389 
       religion    82    59    78    12    54    71 
        hobbies   268    58   131    22    83   264 
science_fiction    16    49     4    12     8    16 
        romance    74   193    11    51    45    43 
          humor    16    30     8     8     9    13 

  请看:新闻文体中最常见的情态动词是 will,而言情文体中最常见的情态动词是 could。怎么样,有考虑过为什么嘛?这种可以区分文体的词计数方法将在后面再次谈及。

路透社语料库

  路透社语料库包含 10,788 个新闻文档,共计 130 万字。这些文档分成 90 个主题,按照“训练”和“测试”分为两组。因此,fileid 为“test/14826”的文档属于测试组。
  与布朗语料库不同,路透社语料库的类别是有互相重叠的,只是因为新闻报道往往涉及多个主题。我们可以查找由一个或多个文档涵盖的主题,也可以查找包含在一个或多个类别中的文档。为方便起见,语料库方法既接受单个的 fileid 也接受 fileids 列表作为参数。类似的,我们可以以文档或类别为单位查找我们想要的词或句子。这些文本中最开始的几个词是标题,按照惯例以大写字母存储。

from nltk.corpus import reuters
reuters.fileids()
reuters.categories()
reuters.categories('training/9865')
reuters.categories(['training/9865', 'training/9880'])
print(reuters.words('training/9865')[:14])

就职演说语料库

  是美国总统就是演讲文件,语料库实际上是 55 个文本的集合,每个文本都是一个总统的演说。这个集合的一个有趣特性是它的时间维度。

from nltk.corpus import inaugural
inaugural.fileids()
print([fileid[:4] for fileid in inaugural.fileids()])

['1789', '1793', '1797', '1801', '1805', '1809', '1813', '1817', '1821', '1825', '1829', '1833', '1837', '1841', '1845', '1849', '1853', '1857', '1861', '1865', '1869', '1873', '1877', '1881', '1885', '1889', '1893', '1897', '1901', '1905', '1909', '1913', '1917', '1921', '1925', '1929', '1933', '1937', '1941', '1945', '1949', '1953', '1957', '1961', '1965', '1969', '1973', '1977', '1981', '1985', '1989', '1993', '1997', '2001', '2005', '2009', '2013', '2017', '2021']

标注文本语料库

  许多文本语料库都包含语言学标注,有词性标注、命名实体、句法结构、语义角色等。NLTK 中提供了很方便的方式来访问这些语料库中的几个,还有一个包含语料库和语料样本的数据包,用于教学和科研的话可以免费下载。

在其他语言的语料库

  NLTK 包含多国语言语料库。某些情况下你在使用这些语料库之前需要学习如何在 Python 中处理字符编码。这些语料库的最后,udhr,是超过 300 种语言的世界人权宣言。这个语料库的 fileids包括有关文件所使用的字符编码,如:UTF8 或者 Latin1。让我们用条件频率分布来研究“世界人权宣言”(udhr)语料库中不同语言版本中的字长差异。

nltk.corpus.cess_esp.words()
nltk.corpus.floresta.words()
nltk.corpus.udhr.fileids()
nltk.corpus.udhr.words('Javanese-Latin1')[11:]

['Saben', 'umat', 'manungsa', 'lair', 'kanthi', 'hak', ...]
from nltk.corpus import udhr
languages = ['Chickasaw', 'English', 'German_Deutsch',
'Greenlandic_Inuktikut', 'Hungarian_Magyar', 'Ibibio_Efik']
cfd = nltk.ConditionalFreqDist(
    (lang, len(word))
    for lang in languages
    for word in udhr.words(lang + '-Latin1'))
cfd.plot(cumulative = True)

在这里插入图片描述

语料库结构

  到目前为止,我们已经看到了大量的语料库结构。最简单的一种语料库是一些孤立的没有什么特别的组织的文本集合;一些语料库按如文体(布朗语料库)等分类组织结构;一些分类会重叠,如主题类别(路透社语料库);另外一些语料库可以表示随时间变化语言用法的改变(就职演说语料库)。
  下表给出基本语料库函数:
在这里插入图片描述

载入自己的语料库

  如果你有自己收集的文本文件,并且想使用前面讨论的方法访问它们,你可以很容易地在 NLTK 中的 PlaintextCorpusReader 帮助下载入它们。

from nltk.corpus import PlaintextCorpusReader
corpus_root = '/usr/share/dict'
wordlists = PlaintextCorpusReader(corpus_root, '.*')
wordlists.fileids()

  假定你的文件在/usr/share/dict 目录下。不管是什么位置,将变量corpus_root的值设置为这个目录。PlaintextCorpusReader 初始化函数的第二个参数可以是一个如[‘a.txt’, ‘test/b.txt’]这样的 fileids 链表,或者一个匹配所有fileids 的模式,如:’[abc]/.*.txt’。
  这个对于后面制作自己的语料库至关重要,只有制作了自己的语料库,才能实现学习这个的初衷----完成文案优化以及流程化刊登。

2.条件频率分布

理解条件频率分布

  条件频率分布可以理解是在特定条件下的词汇频率分布,它是频率分布的集合,每个频率分布有一个不同的“条件”。这个条件通常是文本的类别。
  nltk中处理条件频率分布不是处理词序列,而是配对序列,即条件和词序列对,形如(条件,事件)。为什么这样?因为对于词本身来说,没法统计也没法计算频率,只有在加了条件后才有效,比如,计算某一本书中某个单词出现的次数。然后我们看下面例子理解这个东西。

cfd = nltk.ConditionalFreqDist((genre, word)
for genre in brown.categories()
for word in brown.words(categories=genre))
genres = ['news', 'religion', 'hobbies', 'science_fiction', 'romance', 'humor']
modals = ['can', 'could', 'may', 'might', 'must', 'will']
cfd.tabulate(conditions=genres, samples=modals)

out:
                  can could   may might  must  will 
           news    93    86    66    38    50   389 
       religion    82    59    78    12    54    71 
        hobbies   268    58   131    22    83   264 
science_fiction    16    49     4    12     8    16 
        romance    74   193    11    51    45    43 
          humor    16    30     8     8     9    13 

  上面的结果输出过程是怎样的呢?分解步骤:
第一步:构建配对序列

genre_word = [(genre, word)
    for genre in ['news', 'romance']
    for word in brown.words(categories=genre)]
genre_word[:4]
[('news', 'The'), ('news', 'Fulton'), ('news', 'County'), ('news', 'Grand')]

第二步:计算对应条件下的频率

cfd = nltk.ConditionalFreqDist(genre_word)
cfd.conditions()# 条件
['news', 'romance']
cfd['romance']["can"]# ramance条件下的can的频率
74

genres = ['news', 'romance']
modals = ['can', 'could', 'may', 'might', 'must', 'will']
cfd.tabulate(conditions=genres, samples=modals) # 制表查看
          can could   may might  must  will 
   news    93    86    66    38    50   389 
romance    74   193    11    51    45    43 

  在 plot()和 tabulate()方法中,我们可以使用 conditions= parameter 来选择指定哪些条件显示。如果我们忽略它,所有条件都会显示。同样,我们可以使用 samples= parameter 来限制要显示的样本。这使得载入大量数据到一个条件频率分布,然后通过选定条件和样品,绘图或制表的探索成为可能。

使用双连词生成随机文本

  bigrams()函数接受一个词汇链表,并建立一个连续的词对链表。我们把每个词作为一个条件,对每个词我们有效的创建它的后续词的频率分布。然后就可以记录哪些词汇最有可能跟在给定词的后面。

# 《创世记》文本中所有的双连词作为测试数据
# 自定义输出单词的函数
def generate_model(cfdist, word, num=15):
    for i in range(num):
        print (word)
        word = cfdist[word].max()
        
text = nltk.corpus.genesis.words('english-kjv.txt')
bigrams = nltk.bigrams(text)
cfd = nltk.ConditionalFreqDist(bigrams)
cfd['living']
FreqDist({'creature': 7, 'thing': 4, 'substance': 2, 'soul': 1, '.': 1, ',': 1})
cfd['living']max()
'creature'

  条件频率分布是一个对许多 NLP 任务都有用的数据结构。下表总结了它们常用的方法。
在这里插入图片描述

3、词典资源

  词典或者词典资源是一个词和/或短语以及一些相关信息的集合,例如:词性和词意定义等相关信息。词典资源附属于文本,通常在文本的帮助下创建和丰富。例如:如果我们定义了一个文本 my_text,然后 vocab = sorted(set(my_text))建立 my_text 的词汇表,同时 word_freq = FreqDist(my_text)计数文本中每个词的频率。vocab 和 word_freq都是简单的词汇资源。
  一个词项包括词 目(也叫词条)以及其他附加信息,例如:词性和词意定义。两个不同的词拼写相同被称为同音异义词。
  一种简单的词典资源是除了一个词汇列表外什么也没有。复杂的词典资源包括在词汇项内和跨词汇项的复杂的结构。我们来看看 NLTK 中的一些词典资源。

1. 词汇列表语料库(简单的词典)

  NLTK 包括一些仅仅包含词汇列表的语料库。词汇语料库是 Unix 中的/usr/dict/words 文件,被一些拼写检查程序使用。我们可以用它来寻找文本语料中不寻常的或拼写错误的词汇。
例1:过滤文本

# 查找不存在于words词典的词
def unusual_words(text):
    text_vocab = set(w.lower() for w in text if w.isalpha())
    english_vocab = set(w.lower() for w in nltk.corpus.words.words())
    unusual = text_vocab.difference(english_vocab)
    return sorted(unusual)
unusual_words(nltk.corpus.gutenberg.words('austen-sense.txt'))

例2:停用词,正常使用都会先剔除停用词,再后续处理分析。

# 计算剔除停用词之后的文本占比
def content_fraction(text):
    stopwords = nltk.corpus.stopwords.words('english')
    content = [w for w in text if w.lower() not in stopwords]
    return len(content) / len(text)

content_fraction(nltk.corpus.reuters.words())

2. 发音的词典

  一个稍微丰富的词典资源是一个表格(或电子表格),在每一行中含有一个词加一些性质。NLTK 中包括美国英语的 CMU 发音词典,它是为语音合成器使用而设计的。

entries = nltk.corpus.cmudict.entries()
len(entries)
133737

for entry in entries[39943:39951]:
    print (entry)
    
('explorer', ['IH0', 'K', 'S', 'P', 'L', 'AO1', 'R', 'ER0'])
('explorers', ['IH0', 'K', 'S', 'P', 'L', 'AO1', 'R', 'ER0', 'Z'])
('explores', ['IH0', 'K', 'S', 'P', 'L', 'AO1', 'R', 'Z'])
('exploring', ['IH0', 'K', 'S', 'P', 'L', 'AO1', 'R', 'IH0', 'NG'])
('explosion', ['IH0', 'K', 'S', 'P', 'L', 'OW1', 'ZH', 'AH0', 'N'])
('explosions', ['IH0', 'K', 'S', 'P', 'L', 'OW1', 'ZH', 'AH0', 'N', 'Z'])
('explosive', ['IH0', 'K', 'S', 'P', 'L', 'OW1', 'S', 'IH0', 'V'])
('explosively', ['EH2', 'K', 'S', 'P', 'L', 'OW1', 'S', 'IH0', 'V', 'L', 'IY0'])

  对每一个词,这个词典资源提供语音的代码——不同的声音不同的标签——叫做音素。

3. 比较词表

  表格词典的另一个例子是比较词表。NLTK 中包含了所谓的斯瓦迪士核心词列表(Swadesh wordlists),几种语言中约 200 个常用词的列表。语言标识符使用 ISO639 双字母码。

from nltk.corpus import swadesh
swadesh.fileids() # 包含的语种
swadesh.words('en')

  我们可以通过在 entries()方法中指定一个语言链表来访问多语言中的同源词。更进一
步,我们可以把它转换成一个简单的词典

fr2en = swadesh.entries(['fr', 'en'])
fr2en

[('je', 'I'),
 ('tu, vous', 'you (singular), thou'),
 ('il', 'he'),
 ('nous', 'we'),
 ('vous', 'you (plural)'),
 ('ils, elles', 'they'),
 ('ceci', 'this'),
 ...]

translate = dict(fr2en)
translate['chien']
"dog"

翻译器例子:
  通过添加其他源语言,我们可以让我们这个简单的翻译器更为有用。让我们使用 dict()函数把德语-英语和西班牙语-英语对相互转换成一个词典,然后用这些添加的映射更新我们原来的翻译词典.

de2en = swadesh.entries(['de', 'en']) # German-English
es2en = swadesh.entries(['es', 'en']) # Spanish-English
translate.update(dict(de2en))
translate.update(dict(es2en))
translate['Hund']
'dog'

4. 词汇工具Toolbox和Shoebox

  可能最流行的语言学家用来管理数据的工具是 Toolbox(工具箱),以前叫做 Shoebox(鞋柜),因为它用满满的档案卡片占据了语言学家的旧鞋盒。Toolbox 可以免费从 http://www.sil.org/computing/toolbox/下载。一个 Toolbox 文件由一个大量条目的集合组成,其中每个条目由一个或多个字段组成。大多数字段都是可选的或重复的,这意味着这个词汇资源不能作为一个表格或电子表格来处理。

from nltk.corpus import toolbox
toolbox.entries('rotokas.dic')

[('kaa',
  [('ps', 'V'),
   ('pt', 'A'),
   ('ge', 'gag'),
   ('tkp', 'nek i pas'),
   ('dcsv', 'true'),
   ('vx', '1'),
   ('sc', '???'),
   ('dt', '29/Oct/2005'),
   ('ex', 'Apoka ira kaaroi aioa-ia reoreopaoro.'),
   ('xp', 'Kaikai i pas long nek bilong Apoka bikos em i kaikai na toktok.'),
   ('xe', 'Apoka is gagging from food while talking.')]),...]

  条目包括一系列的属性-值对,如(‘ps’, ‘V’),表示词性是’V’(动词),(‘ge’, ‘gag’)表示英文注释是’gag’。最后的 3 个配对包含一个罗托卡特语例句和它的巴布亚皮钦语及英语翻译。
  Toolbox 文件松散的结构是我们在现阶段很难更好的利用它。XML 提供了一种强有力的方式来处理这种语料库,我们后面讲解。

4、WordNet

1. 意义和同义词

  WordNet 是面向语义的英语词典,类似于传统辞典,但具有更丰富的结构。NLTK 中包括英语 WordNet。我们将以寻找同义词和它们在 WordNet 中如何访问开始。
  用 automobile 替换掉(a)中的词 motorcar,变成(b),句子的意思几乎保持不变。

a. Benz is credited with the invention of the motorcar.
b. Benz is credited with the invention of the automobile.

  因为句子中所有其他成分都保持不变,我们可以得出结论:motorcar 和 automobile 有相
同的含义即它们是同义词。在 WordNet 的帮助下,我们可以探索这些词:

from nltk.corpus import wordnet as wn
wn.synsets('motorcar')
[Synset('car.n.01')]

  因此,motorcar 只有一个可能的含义,它被定义为 car.n.01,car 的第一个名词意义。car.n.01 被称为 synset 或“同义词集”,意义相同的词(或“词条”)的集合:

wn.synset('car.n.01').lemma_names()
['car', 'auto', 'automobile', 'machine', 'motorcar']

  同义词集中的每个词可以有多种含义,例如:car 也可能是火车车厢、一个货车或电梯厢。但我们只对这个同义词集中所有词来说最常用的一个意义感兴趣。同义词集也有一些一般的定义和例句:

wn.synset('car.n.01').definition()
'a motor vehicle with four wheels; usually propelled by an internal combustion engine'

wn.synset('car.n.01').examples()
['he needs a car to get to work']

  为了消除歧义,我们将这些词标注为 car.n.01.automobile,car.n.01.motorcar 等。这种同义词集和词的配对叫做词条。我们可以得到指定同义词集的所有词条,查找特定的词条,得到一个词条对应的同义词集,也可以得到一个词条的“名字”:

wn.synset('car.n.01').lemmas()
[Lemma('car.n.01.car'),
 Lemma('car.n.01.auto'),
 Lemma('car.n.01.automobile'),
 Lemma('car.n.01.machine'),
 Lemma('car.n.01.motorcar')]
 
wn.lemma('car.n.01.automobile')
Lemma('car.n.01.automobile')

wn.lemma('car.n.01.automobile').synset()
Synset('car.n.01')

wn.lemma('car.n.01.automobile').name()
'automobile'

  与词 automobile 和 motorcar 这些意义明确的只有一个同义词集的词不同,词 car 是含糊的,有五个同义词集:

wn.synsets('car')
[Synset('car.n.01'),
 Synset('car.n.02'),
 Synset('car.n.03'),
 Synset('car.n.04'),
 Synset('cable_car.n.01')]

2. WordNet的层级结构

  WordNet 的同义词集对应于抽象的概念,它们并不总是有对应的英语词汇。这些概念在层次结构中相互联系在一起。一些概念也很一般,如实体、状态、事件;这些被称为独一无二的根同义词集。其结构层级如下图:
在这里插入图片描述
  WordNet 概念层次片段:每个节点对应一个同义词集;边表示上位词/下位词关系,即上级概念与从属概念的关系。
  上位词和下位词被称为词汇关系,因为它们是同义集之间的关系。这个关系定位上下为“是一个”层次。WordNet 网络另一个重要的漫游方式是从物品到它们的部件(部分)或到它们被包含其中的东西(整体)。
  我们已经看到同义词集之间构成复杂的词汇关系网络。给定一个同义词集,我们可以遍历 WordNet 网络来查找相关含义的同义词集。知道哪些词是语义相关的,对索引文本集合非常有用,当搜索一个一般性的用语——例如:车辆——时就可以匹配包含具体用语——例 如豪华轿车——的文档。
  回想一下每个同义词集都有一个或多个上位词路径连接到一个根上位词,如 entity.n.01。连接到同一个根的两个同义词集可能有一些共同的上位词(见图 2-8)。如果两个同义词集共用一个非常具体的上位词——在上位词层次结构中处于较低层的上位词——它们一定有密切的联系。
  WordNet 同义词集的集合上定义了类似的函数能够深入的观察。例如:path_similarityassigns 是基于上位词层次结构中相互连接的概念之间的最短路径在 0-1 范围的打分(两者之间没有路径就返回-1)。同义词集与自身比较将返回 1。考虑以下的相似度:露脊鲸与小须鲸、逆戟鲸、乌龟以及小说。数字本身的意义并不大,当我们从海洋生物的语义空间转移到非生物时它是减少的。

right = wn.synset('right_whale.n.01')
orca = wn.synset('orca.n.01')
minke = wn.synset('minke_whale.n.01')
tortoise = wn.synset('tortoise.n.01')
right.path_similarity(minke)
0.25

right.path_similarity(orca)
0.16666666666666666

right.path_similarity(tortoise)
0.07692307692307693

right.path_similarity(right)
1.0
  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值