我一直致力于创建一个Python模块,该模块执行功能的特征提取,最终被机器学习算法用于此过程。
我的方法是使用(手工制作)功能扩充初始金标准数据集,从而创建一个新的数据集,因此培训不涉及任何特征创建,这可能很昂贵。我相信这是大多数数据集的标准 - 总是包括核心功能(例如,词性标签,命名实体标签,语义标签等)。
我使用的数据集仅包括标记为格式为XML标记的所有句子。例如:
<s>
<lex begin='351' end='354'>The</lex>
<lex begin='355' end='361'>people</lex>
<lex begin='362' end='366'>here</lex>
<lex begin='367' end='370'>are</lex>
<lex begin='371' end='374'>far</lex>
<lex begin='375' end='384'>wealthier</lex>
<lex begin='384' end='385'>.</lex>
</s>
我想为每个令牌添加其他信息,例如词性,NER,语义标签等。
我一直在使用Stanford NLP POS tagger和Stanford NLP NER tagger。这些速度非常慢,但(希望)速度可以提供更准确的POS和NER标签。我还投入另一个解析器来获取语义标签。下面是新的句子,增加了功能。
<s>
<lex ner='O' begin='351' end='354' pos='DT' label='None'>The</lex>
<lex CATEGORY='#ref-category PERSON' begin='355' end='361'
FORM='#ref-category COMMON-NOUN/PLURAL' ENDS-AT='#edges ending at 3'
CONSTITUENTS='NIL' USED-IN='NIL' Type='SPARSER::EDGE' LEFT-DAUGHTER='#word "people"'
pos='NNS' RULE='#PSR577 person - "people"' label='SPATIAL_ENTITY'
REFERENT='#people 1' POSITION-IN-RESOURCE-ARRAY='1' SPANNED-WORDS='NIL'
RIGHT-DAUGHTER=':SINGLE-TERM' ner='O' Class='#STRUCTURE-CLASS SPARSER::EDGE'
STARTS-AT='#edges starting at 2'>people</lex>
<lex CATEGORY='#ref-category DEICTIC-LOCATION' begin='362' end='366'
FORM='#ref-category PROPER-NOUN' ENDS-AT='#edges ending at 4'
CONSTITUENTS='NIL' USED-IN='NIL' Type='SPARSER::EDGE' LEFT-DAUGHTER='#word "here"'
pos='RB' RULE='#PSR271 deictic-location - "here"' label='PLACE'
REFERENT='#deictic-location "here" 3' POSITION-IN-RESOURCE-ARRAY='3'
SPANNED-WORDS='NIL' RIGHT-DAUGHTER=':SINGLE-TERM' ner='O'
Class='#STRUCTURE-CLASS SPARSER::EDGE' STARTS-AT='#edges starting at 3'>here</lex>
<lex CATEGORY='#ref-category BE' begin='367' end='370'
FORM='#ref-category VERB' ENDS-AT='#edges ending at 5' CONSTITUENTS='NIL'
USED-IN='NIL' Type='SPARSER::EDGE' LEFT-DAUGHTER='#word "are"' pos='VBP'
RULE='#PSR145 be - "are"' label='None' REFERENT='#be 1'
POSITION-IN-RESOURCE-ARRAY='4' SPANNED-WORDS='NIL' RIGHT-DAUGHTER=':SINGLE-TERM'
ner='O' Class='#STRUCTURE-CLASS SPARSER::EDGE' STARTS-AT='#edges starting at 4'>are</lex>
<lex CATEGORY='#word "far"' begin='371' end='374'
FORM='#ref-category SPATIAL-PREPOSITION' ENDS-AT='#edges ending at 6'
CONSTITUENTS='NIL' USED-IN='NIL' Type='SPARSER::EDGE' LEFT-DAUGHTER='#word "far"'
pos='RB' RULE='(5)' label='None' REFERENT='#word "far"'
POSITION-IN-RESOURCE-ARRAY='5' SPANNED-WORDS='NIL' RIGHT-DAUGHTER=':LITERAL-IN-A-RULE'
ner='O' Class='#STRUCTURE-CLASS SPARSER::EDGE' STARTS-AT='#edges starting at 5'>far</lex>
<lex ner='O' begin='375' end='384' pos='JJR' label='None'>wealthier</lex>
<lex begin='384' end='385'>.</lex>
</s>
显然它不再是人类可读的,但这并不重要,因为这些只是通过管道传输到机器学习算法的功能。
就我的目的而言,我只需要这样做一次,并且可以在训练之前添加更便宜的功能,例如是资本化的词。
然而,我目前的解决方案非常糟糕,而且我不确定如何重新考虑它,以便将来某人可以放入他们自己的钩子/功能(例如他们想要快速添加其他一些新功能解析器)很容易。这是我的工作解决方案:
xml_tokens_pattern = re.compile(r'<TOKENS>.+</TOKENS>', re.DOTALL)
sentence_pattern = re.compile(r'<s>.+?</s>', re.DOTALL)
lex_attrs_pattern = re.compile(r'(?<=<lex)[^>]+')
class Feature_Process(object):
"""Wrapper for adding features to xmls.
"""
def __init__(self, xmls, golddir, newdir='', suffix='++',
feature_functions=[], renew=False, debug=False):
self.xmls = xmls
self.golddir = golddir
self.newdir = newdir
self.suffix = suffix
self.feature_functions = feature_functions
self.renew = renew
self.debug = debug
self.heavy = False
def process(self):
for xml in self.xmls:
path = setup_newdir(xml, self.golddir, self.newdir,
self.suffix, self.renew)
if not path:
continue
mkparentdirs(path)
with open(xml, 'r') as oldfile:
text = oldfile.read()
doc = Space_Document(xml)
tags = [tag for tag in doc.tags if 'start' in tag.attrib]
new_text = text
for (i,m) in enumerate(re.finditer(sentence_pattern, text)):
sentence = doc.sentences[i]
doc_lexes = sentence.getchildren()
xml_sentence = m.group()
tokens = [''.join([c if ord(c) < 128
else u2ascii[c]
for c in x.text]).encode('utf-8')
for x in doc_lexes]
(pos_tags, ner_tags, edges) = ([], [], [])
if self.heavy:
pos_tags = pos.tag(tokens)
ner_tags = ner.tag(tokens)
try:
if self.debug:
print ' '.join([x for x in tokens])
edges = p(' '.join([x for x in tokens]), split=True)
except:
'somehow got here'
c = 0
for (j, n) in enumerate(re.finditer(lex_attrs_pattern,
xml_sentence)):
doc_lex = doc_lexes[j]
new_lex = Lex(doc_lex.text, doc_lex.attrib)
attributes = n.group()
tag = binary_search((int(doc_lex.attrib['begin']),
int(doc_lex.attrib['end']),
doc_lex.text), tags)
label = 'None'
if type(tag) != type(None):
label = tag.tag
new_lex.add(('label', label))
new_lex.add(('word', new_lex.text.encode('utf-8')))
if type(tag) != type(None):
new_lex.addAll([(key, tag.attrib[key]) for key in tag.attrib])
if pos_tags:
if tokens[j] == pos_tags[c][0]:
new_lex.add(('pos', pos_tags[c][1]))
pos_tags.remove(pos_tags[c])
if ner_tags: #this error case comes up for RFC/Durango.xml
if tokens[j] == ner_tags[c][0]:
new_lex.add(('ner', ner_tags[c][1]))
ner_tags.remove(ner_tags[c])
if edges:
sparser_edge = ledge(edges, tokens[j])
if sparser_edge:
if sparser_edge.keyvalues:
keyvalues = sparser_edge.keyvalues[sparser_edge.keyvalues.keys()[0]]
new_lex.addAll([(key, keyvalues[key]) for key in keyvalues])
new_lex.addAll([function(new_lex) for function in self.feature_functions])
new_text = new_text.replace(attributes, str(new_lex))
w = open(path, 'w')
print>>w, new_text
w.close()
答案 0 :(得分:1)
首先,如果您要使用XML作为源和目标,那么尝试手动解析XML几乎总是错误的。使用Python XML解析库(其中有几个)来生成可以操作的结构或流。
如果你的主要目标是提供一个API来添加可以由分类器解释的词法特征,我唯一的建议是明确地将序列化/反序列化与数据结构的操作分开。但是在这么简单的情况下,这并不是特别难。