赞
踩
命名实体识别的常用方法是BiLSTM-CRF和BERT-CRF,可以完美的匹配该任务。
命名实体识别 (NER) 是从属于预定义语义类型(如人、位置、组织等)的文本中识别刚性指示符的任务。NER 一直是许多自然语言应用的基础,如问答、文本摘要和机器翻译。早期的 NER 系统在以设计特定领域的特征和规则的人工工程成本实现良好性能方面取得了巨大成功。近年来,深度学习通过非线性处理得到连续实值向量表示和语义组合的支持,已被用于 NER 系统,产生了最先进的性能。在本文中,我们全面回顾了现有的 NER 深度学习技术。我们首先介绍 NER 资源,包括带标签的 NER 语料库和现成的 NER 工具。然后,我们根据沿三个轴的分类法对现有作品进行系统分类:输入的分布式表示、上下文编码器和标签解码器。接下来,我们调查了最近在新的 NER 问题设置和应用中应用深度学习技术的最具代表性的方法。最后,我们向读者展示了 NER 系统面临的挑战,并概述了该领域的未来方向。
Index Terms—Natural language processing, named entity recognition, deep learning, survey
NAMED 实体识别 (NER) 旨在从属于预定义语义类型(例如人、位置、组织等)的文本中识别刚性指示符的提及 [1]。 NER 不仅作为信息提取 (IE) 的独立工具,而且在文本理解 [2]、[3]、信息检索 [4]、 [5]、自动文本摘要[6]、问答[7]、机器翻译[8]、知识库构建[9]等
命名实体是一个单词或短语,可以从一组具有相似属性的其他项目中清楚地识别出一个项目 [28]。命名实体的示例是一般域中的组织、人员和位置名称;生物医学领域的基因、蛋白质、药物和疾病名称。 NER 是将文本中的命名实体定位和分类为预定义实体类别的过程。
形式上,给定一个标记序列 s = < w 1 , w 2 , . . . , w N > s = < w_1, w_2, ..., w_N> s=<w1,w2,...,wN> ,NER 是输出一个元组列表 < I s , I e , t i > < I_s, I_e, t_i> <Is,Ie,ti>。每一个都是s中提到的一个命名实体。
这里, I s ∈ [ 1 , N ] a n d I e ∈ [ 1 , N ] I_s ∈ [1, N] \ and\ I_e ∈ [1, N ] Is∈[1,N] and Ie∈[1,N]是命名实体提及的开始和结束索引;t 是预定义类别集中的实体类型。图 1 显示了一个示例,其中 NER 系统从给定的句子中识别三个命名实体。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hw0dugKq-1678240468627)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307162045505.png)]
当 NER 首次在 MUC 中定义时-6 [10],任务是识别文本中的人名、组织、位置和时间、货币、百分比表达式。请注意,该任务侧重于一小组粗略的实体类型,每个命名实体一个类型。我们将这种 NER 任务称为粗粒度 NER [10]、[11]。最近,一些细粒度 NER 任务 [29]-[33] 关注更大的实体类型集,其中一个提及项可能被分配多个细粒度类型。
NER 作为各种下游应用程序的重要预处理步骤例如信息检索、问答、机器翻译等。在这里,我们以语义搜索为例来说明 NER 在支持各种应用方面的重要性。语义搜索是指一组技术,它使搜索引擎能够理解用户查询背后的概念、含义和意图 [34]。根据 [4],大约 71% 的搜索查询包含至少一个命名实体。识别搜索查询中的命名实体将帮助我们更好地理解用户意图,从而提供更好的搜索结果。为了将命名实体纳入搜索中,Raviv 等人提出了基于实体的语言模型 [34],该模型考虑单个术语以及已注释为实体(在文档和查询中)的术语序列。 [35]。还有一些研究利用命名实体来增强用户体验,例如查询推荐 [36]、查询自动完成 [37]、[38] 和实体卡 [39]、[40]。
高质量的注释对于模型学习和评估都至关重要。在下文中,我们总结了英语 NER 广泛使用的数据集和现成的工具。
标记语料库是包含一种或多种实体类型注释的文档集合。表 1 列出了一些广泛使用的数据集及其数据源和实体类型(也称为标签类型)的数量。如表 1 所示,在 2005 年之前,数据集主要通过对新闻文章进行注释而开发,实体类型较少,适用于粗粒度的 NER 任务。之后,在各种文本源上开发了更多数据集,包括维基百科文章、对话和用户生成的文本(例如,推文和 YouTube 评论以及 W-NUT 中的 StackExchange 帖子)。标签类型的数量显着增加,例如 HYENA 中的 505。
Corpus Year Text Source #Tags URL
MUC-6 1995 Wall Street Journal 7
https://catalog.ldc.upenn.edu/LDC2003T13
MUC-6 Plus 1995 Additional news to MUC-6 7
https://catalog.ldc.upenn.edu/LDC96T10
MUC-7 1997 New York Times news 7
https://catalog.ldc.upenn.edu/LDC2001T02
CoNLL03 2003 Reuters news 4
https://www.clips.uantwerpen.be/conll2003/ner/
ACE 2000 - 2008 Transcripts, news 7
https://www.ldc.upenn.edu/collaborations/past-projects/ace
OntoNotes 2007 - 2012 Magazine, news, web, etc. 18
https://catalog.ldc.upenn.edu/LDC2013T19
W-NUT 2015 - 2018 User-generated text 6/10
http://noisy-text.github.io
BBN 2005 Wall Street Journal 64
https://catalog.ldc.upenn.edu/LDC2005T33
WikiGold 2009 Wikipedia 4 https://fifigshare.com/articles/Learning_multilingual_named_entity_recognition_from_Wikipedia/5462500
WiNER 2012 Wikipedia 4
http://rali.iro.umontreal.ca/rali/en/winer-wikipedia-for-ner
WikiFiger 2012 Wikipedia 112
https://github.com/xiaoling/fifiger
HYENA 2012 Wikipedia 505
https://www.mpi-inf.mpg.de/departments/databases-and-information-systems/research/yago-naga/hyena
N3 2014 News 3
http://aksw.org/Projects/N3NERNEDNIF.html
Gillick 2016 Magazine, news, web, etc. 89
https://arxiv.org/e-print/1412.1820v2
FG-NER 2018 Various 200
https://fgner.alt.ai/
NNE 2019 Newswire 114
https://github.com/nickyringland/nested_named_entities
GENIA 2004 Biology and clinical text 36
http://www.geniaproject.org/home
GENETAG 2005 MEDLINE 2
https://sourceforge.net/projects/bioc/fifiles/
FSU-PRGE 2010 PubMed and MEDLINE 5
https://julielab.de/Resources/FSU_PRGE.html
NCBI-Disease 2014 PubMed 1
https://www.ncbi.nlm.nih.gov/CBBresearch/Dogan/DISEASE/
BC5CDR 2015 PubMed 3
http://bioc.sourceforge.net/
DFKI 2018 Business news and social media 7
https://dfki-lt-re-group.bitbucket.io/product-corpus/
我们还列出了一些领域特定的数据集,特别是在 PubMed 和 MEDLINE 文本上开发的。实体类型的数量从 NCBI-Disease 中的 1 种到 GENIA 中的 36 种不等。我们注意到许多最近的 NER 作品报告了他们在 CoNLL03 和 OntoNotes 数据集上的表现(见表 3)。 CoNLL03 包含两种语言的路透社新闻注释:英语和德语。英语数据集有很大一部分体育新闻,带有四种实体类型(人物、位置、组织和杂项)的注释 [11]。 OntoNotes 项目的目标是注释大型语料库,包括各种类型(网络日志、新闻、脱口秀、广播、新闻组和电话对话),具有结构信息(句法和谓词参数结构)和浅层语义(词与本体论和共指相关联的意义)。有 5 个版本,从 Release 1.0 到 Release 5.0。这些文本用 18 种实体类型进行了注释。
我们还注意到两个托管一些 NER 语料库的 Github 存储库。网上有许多带有预训练模型的 NER 工具。表 2 总结了学术界(顶部)和工业界(底部)流行的英语 NER。
NER System URL
StanfordCoreNLP :https://stanfordnlp.github.io/CoreNLP/
OSU Twitter NLP :https://github.com/aritter/twitter_nlp
Illinois NLP :http://cogcomp.org/page/software/
NeuroNER :http://neuroner.com/
NERsuite : http://nersuite.nlplab.org/
Polyglot : https://polyglot.readthedocs.io
Gimli : http://bioinformatics.ua.pt/gimli
spaCy : https://spacy.io/api/entityrecognizer
NLTK :https://www.nltk.org
OpenNLP :https://opennlp.apache.org/
LingPipe :http://alias-i.com/lingpipe-3.9.3/
AllenNLP :https://demo.allennlp.org/
IBM Watson https://natural-language-understanding-demo.ng.bluemix.net
FG-NER https://fgner.alt.ai/extractor/
Intellexer http://demo.intellexer.com/
Repustate https://repustate.com/named-entity-recognition-api-demo
AYLIEN https://developer.aylien.com/text-api-demo
Dandelion API https://dandelion.eu/semantic-text/entity-extraction-demo
displaCy https://explosion.ai/demos/displacy-ent
ParallelDots https://www.paralleldots.com/named-entity-recognition
TextRazor https://www.textrazor.com/named_entity_recognition
NER 本质上涉及两个子任务:边界检测和类型识别。在“精确匹配评估”[11]、[41]、[42] 中,正确识别的实例需要系统同时正确识别其边界和类型。更具体地说,假阳性 (FP)、假阴性 (FN) 和真阳性 (TP) 的数量用于计算 Precision、Recall 和 F-score。
False Positive (FP):由NER 系统返回但未出现在基本事实中的实体。
False Negative (FN):不是由NER 系统返回但出现在基本事实中的实体。
True Positive (TP):由NER 系统返回并且也出现在基本事实中的实体。
精度是指正确识别的系统结果的百分比。召回率是指系统正确识别的实体总数的百分比。
结合精度和召回率的测量方法是精度和召回率的调和平均值,即传统的Fmeasure或平衡F-score。
此外,宏观平均 F-score 和微观平均 F-score 都考虑了跨多个实体类型的性能。 Macro-averaged F-score 独立计算不同实体类型的F-score,然后取F-scores的平均值。微平均 F 分数总结了所有实体类型中的个别漏报、漏报和真报,然后应用它们来获得统计数据。后者可能会受到语料库中大类实体识别质量的严重影响。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OhgO45iH-1678240468630)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307163236769.png)]
图2. 基于DL的NER的分类法。从输入序列到预测标签,一个基于DL的NER模型由输入、上下文编码器和标签解码器的分布式表示组成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FlAYlJRK-1678240468631)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307172234100.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qu336fCK-1678240468631)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307172244703.png)]
图 4. 在句子上下文中提取单词“华盛顿”的上下文字符串嵌入 [107]。从前向语言模型(以红色显示)中,模型提取单词中最后一个字符后的输出隐藏状态。从后向语言模型(以蓝色显示)中,该模型提取单词中第一个字符之前的输出隐藏状态。两个输出隐藏状态连接起来形成一个词的最终嵌入。
Collobert 等人[17] 提出了一种句子方法网络,其中一个词被标记为考虑整个句子,如图 5 所示。输入序列中的每个词在输入表示阶段之后被嵌入到一个 N 维向量中。然后使用一个卷积层来产生每个单词周围的局部特征,卷积层输出的大小取决于句子中单词的数量。全局特征向量是通过组合卷积层提取的局部特征向量构建的。全局特征向量的维度是固定的,与句子长度无关,以便应用后续的标准仿射层。
两种方法广泛用于提取全局特征:
最后,这些固定大小的全局特征被输入到标签解码器中,以计算网络输入中单词所有可能标签的分布分数。在 Collobert 的工作之后,Yao 等人。 [94] 提出了用于生物医学 NER 的 Bio-NER。吴等。 [120]利用卷积层生成由多个全局隐藏节点表示的全局特征。然后将局部特征和全局特征都输入到标准仿射网络中,以识别临床文本中的命名实体。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bTJ15COT-1678240468632)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307172521532.png)]
图5. 基于CNN的句子方法网络[17]。卷积层从整个句子中提取特征,将其视为一个具有全局结构的序列。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5aC1xNBR-1678240468632)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307165302687.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OBKxKGG9-1678240468633)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307165610965.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z8Rgs0Rx-1678240468634)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307172721745.png)]
图 8. NER 的双向递归神经网络 [98]。计算在两个方向上递归完成。自下而上的方向计算每个节点子树的语义组成,自上而下的方向将包含子树的语言结构传播到该节点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o0D9kuAf-1678240468635)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307165829873.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eL86qVyh-1678240468636)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307172755203.png)]
图 9. 带有附加语言建模目标的序列标记模型 [124],对句子“Fischler proposes measures”执行 NER。在每个标记位置(例如,“proposes”),网络被优化以预测序列中的前一个词(“Fischler”)、当前标签(“O”)和下一个词(“measures”)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-waQia8m9-1678240468636)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307172824779.png)]
图 10. 具有上下文表示的序列标记架构 [125]。来自双向语言模型的字符级表示、预训练词嵌入和上下文表示被连接起来,并进一步输入到上下文编码器中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y7HPX7xt-1678240468637)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307172922521.png)]
图 11. 预训练模型架构的差异 [119]。 Google BERT 使用双向 Transformer(缩写为“Trm”)。 OpenAI GPT 使用从左到右的 Transformer。 AllenNLP ELMo 使用独立训练的从左到右和从右到左 LSTM 的串联来为下游任务生成特征。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l8Drhyke-1678240468638)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307173009385.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VqBMqvj7-1678240468639)(/Users/duanyuqing/Library/Application Support/typora-user-images/image-20230307171640616.png)]
用于基于 DL 的 NER 的易于使用的工具包。最近,Röder 等人。 [210] 开发了 GERBIL,它为研究人员、最终用户和开发人员提供了易于使用的界面,用于对实体注释工具进行基准测试,以确保可重复和可存档的实验。但是,它不涉及最近基于 DL 的技术。 Ott [211] 提出了 FAIRSEQ,这是一种用于序列建模的快速、可扩展的工具包,尤其适用于机器翻译和文本标记化。 Dernoncourt 等人。 [212] 实现了一个名为 NeuroNER 的框架,它只依赖于递归神经网络的一个变体。近年来,许多深度学习框架(例如 TensorFlow、PyTorch 和 Keras)旨在通过高级编程接口为设计、训练和验证深度神经网络提供构建块。 3 为了重新实现表 3 中的架构,开发人员可以使用现有的深度学习框架从头开始编写代码。我们 3. https://developer.nvidia.com/deep-learning-frameworks 设想一个易于使用的 NER 工具包可以指导开发人员使用一些标准化模块来完成它:数据处理、输入表示、上下文编码器、标签解码器和有效性度量。我们相信专家和非专家都可以从此类工具包中受益。
import torch import torch.autograd as autograd import torch.nn as nn import torch.optim as optim torch.manual_seed(1) def prepare_sequence(seq, to_ix): idxs = [to_ix[w] for w in seq] return torch.tensor(idxs, dtype=torch.long) def argmax(vec): # 得到最大的值的索引 _, idx = torch.max(vec, 1) return idx.item() def log_sum_exp(vec): max_score = vec[0, argmax(vec)] # max_score的维度为1 max_score_broadcast = max_score.view(1, -1).expand(1, vec.size()[1]) # 维度为1*5 return max_score + torch.log(torch.sum(torch.exp(vec - max_score_broadcast))) #等同于torch.log(torch.sum(torch.exp(vec))),防止e的指数导致计算机上溢 class BiLSTM_CRF(nn.Module): def __init__(self, vocab_size, tag_to_ix, embedding_dim, hidden_dim): super(BiLSTM_CRF, self).__init__() self.embedding_dim = embedding_dim self.hidden_dim = hidden_dim self.vocab_size = vocab_size self.tag_to_ix = tag_to_ix self.tagset_size = len(tag_to_ix) self.word_embeds = nn.Embedding(vocab_size, embedding_dim) self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2, num_layers=1, bidirectional=True) self.hidden2tag = nn.Linear(hidden_dim, self.tagset_size) # 转移矩阵,transitions[i][j]表示从label_j转移到label_i的概率,虽然是随机生成的但是后面会迭代更新 self.transitions = nn.Parameter(torch.randn(self.tagset_size, self.tagset_size)) self.transitions.data[tag_to_ix[START_TAG], :] = -10000 # 从任何标签转移到START_TAG不可能 self.transitions.data[:, tag_to_ix[STOP_TAG]] = -10000 # 从STOP_TAG转移到任何标签不可能 self.hidden = self.init_hidden() # 随机初始化LSTM的输入(h_0, c_0) def init_hidden(self): return (torch.randn(2, 1, self.hidden_dim // 2), torch.randn(2, 1, self.hidden_dim // 2)) def _forward_alg(self, feats): ''' 输入:发射矩阵(emission score),实际上就是LSTM的输出——sentence的每个word经BiLSTM后,对应于每个label的得分 输出:所有可能路径得分之和/归一化因子/配分函数/Z(x) ''' init_alphas = torch.full((1, self.tagset_size), -10000.) init_alphas[0][self.tag_to_ix[START_TAG]] = 0. # 包装到一个变量里面以便自动反向传播 forward_var = init_alphas for feat in feats: # w_i alphas_t = [] for next_tag in range(self.tagset_size): # tag_j # t时刻tag_i emission score(1个)的广播。需要将其与t-1时刻的5个previous_tags转移到该tag_i的transition scors相加 emit_score = feat[next_tag].view(1, -1).expand(1, self.tagset_size) # 1*5 # t-1时刻的5个previous_tags到该tag_i的transition scors trans_score = self.transitions[next_tag].view(1, -1) # 维度是1*5 next_tag_var = forward_var + trans_score + emit_score # 求和,实现w_(t-1)到w_t的推进 alphas_t.append(log_sum_exp(next_tag_var).view(1)) forward_var = torch.cat(alphas_t).view(1, -1) # 1*5 # 最后将最后一个单词的forward var与转移 stop tag的概率相加 terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]] alpha = log_sum_exp(terminal_var) return alpha def _get_lstm_features(self, sentence): ''' 输入:id化的自然语言序列 输出:序列中每个字符的Emission Score ''' self.hidden = self.init_hidden() # (h_0, c_0) embeds = self.word_embeds(sentence).view(len(sentence), 1, -1) lstm_out, self.hidden = self.lstm(embeds, self.hidden) lstm_out = lstm_out.view(len(sentence), self.hidden_dim) lstm_feats = self.hidden2tag(lstm_out) # len(s)*5 return lstm_feats def _score_sentence(self, feats, tags): ''' 输入:feats——emission scores;tags——真实序列标注,以此确定转移矩阵中选择哪条路径 输出:真实路径得分 ''' score = torch.zeros(1) # 将START_TAG的标签3拼接到tag序列最前面 tags = torch.cat([torch.tensor([self.tag_to_ix[START_TAG]], dtype=torch.long), tags]) for i, feat in enumerate(feats): score = score + \ self.transitions[tags[i + 1], tags[i]] + feat[tags[i + 1]] score = score + self.transitions[self.tag_to_ix[STOP_TAG], tags[-1]] return score def _viterbi_decode(self, feats): # 预测序列的得分,维特比解码,输出得分与路径值 backpointers = [] init_vvars = torch.full((1, self.tagset_size), -10000.) init_vvars[0][self.tag_to_ix[START_TAG]] = 0 forward_var = init_vvars for feat in feats: bptrs_t = [] viterbivars_t = [] for next_tag in range(self.tagset_size): next_tag_var = forward_var + self.transitions[next_tag] # forward_var保存的是之前的最优路径的值 best_tag_id = argmax(next_tag_var) # 返回最大值对应的那个tag bptrs_t.append(best_tag_id) viterbivars_t.append(next_tag_var[0][best_tag_id].view(1)) forward_var = (torch.cat(viterbivars_t) + feat).view(1, -1) backpointers.append(bptrs_t) # bptrs_t有5个元素 # 其他标签到STOP_TAG的转移概率 terminal_var = forward_var + self.transitions[self.tag_to_ix[STOP_TAG]] best_tag_id = argmax(terminal_var) path_score = terminal_var[0][best_tag_id] best_path = [best_tag_id] for bptrs_t in reversed(backpointers): best_tag_id = bptrs_t[best_tag_id] best_path.append(best_tag_id) # 无需返回最开始的START位 start = best_path.pop() assert start == self.tag_to_ix[START_TAG] best_path.reverse() # 把从后向前的路径正过来 return path_score, best_path def neg_log_likelihood(self, sentence, tags): # 损失函数 feats = self._get_lstm_features(sentence) # len(s)*5 forward_score = self._forward_alg(feats) # 规范化因子/配分函数 gold_score = self._score_sentence(feats, tags) # 正确路径得分 return forward_score - gold_score # Loss(已取反) def forward(self, sentence): ''' 解码过程,维特比解码选择最大概率的标注路径 ''' lstm_feats = self._get_lstm_features(sentence) score, tag_seq = self._viterbi_decode(lstm_feats) return score, tag_seq START_TAG = "<START>" STOP_TAG = "<STOP>" EMBEDDING_DIM = 5 # 由于标签一共有B\I\O\START\STOP 5个,所以embedding_dim为5 HIDDEN_DIM = 4 # 这其实是BiLSTM的隐藏层的特征数量,因为是双向所以是2倍,单向为2 training_data = [( "the wall street journal reported today that apple corporation made money".split(), "B I I I O O O B I O O".split() ), ( "georgia tech is a university in georgia".split(), "B I O O O O B".split() )] word_to_ix = {} for sentence, tags in training_data: for word in sentence: if word not in word_to_ix: word_to_ix[word] = len(word_to_ix) tag_to_ix = {"B": 0, "I": 1, "O": 2, START_TAG: 3, STOP_TAG: 4} model = BiLSTM_CRF(len(word_to_ix), tag_to_ix, EMBEDDING_DIM, HIDDEN_DIM) optimizer = optim.SGD(model.parameters(), lr=0.01, weight_decay=1e-4) for epoch in range(300): for sentence, tags in training_data: model.zero_grad() # 输入 sentence_in = prepare_sequence(sentence, word_to_ix) targets = torch.tensor([tag_to_ix[t] for t in tags], dtype=torch.long) # 获取loss loss = model.neg_log_likelihood(sentence_in, targets) # 反向传播 loss.backward() optimizer.step() with torch.no_grad(): precheck_sent = prepare_sequence(training_data[0][0], word_to_ix) print(model(precheck_sent))
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。