首页 > 解决方案 > 有没有办法设置 spacy 的 POS 标记?

问题描述

我们有一个包含三列的 pandas 数据框:主语、动词和宾语。

我正在尝试用 spacy 对所有这些列进行词形还原,以添加三个新列:subject_lemma、verb_lemma 和 object_lemma。我可以用下面的代码做到这一点,但是我遇到了一些问题没有被正确地词形还原(例如,动词列中带有“loved”的单元格没有被更改为现在时态“love”,它只是保持不变)。在做了一些测试之后,我认为这是因为 spacy 将“loved”标记为形容词而不是动词。我有什么办法可以解决这个问题吗?我在想我可以将整个动词列的 pos 标记设置为动词,这样它就可以正确地对动词进行词形还原,比如“loved”,但是动词列中有一些不是动词的词(例如,不在“did”中)不是”)。

可能这里有另一篇文章解释了这一点,如果我看不到它,请见谅!非常感谢您的建议!

import pandas as pd
import spacy
nlp = spacy.load('en_core_web_trf')

def spacy_lemmatizer(text):
    doc = nlp(text)
    lemmatized_sentence = " ".join([token.lemma_ for token in doc])
    return(lemmatized_sentence)

dataframe.loc[:,'subject_lemma'] = dataframe.loc[:,'subject'].apply(spacy_lemmatizer)
dataframe.loc[:,'verb_lemma'] = dataframe.loc[:,'verb'].apply(spacy_lemmatizer)
dataframe.loc[:,'object_lemma'] = dataframe.loc[:,'object'].apply(spacy_lemmatizer)

标签: pythonspacy

解决方案


首先要注意的是词形还原并不总是正确的。但是,在您的情况下,更成问题的是它是一个上下文相关的操作。在句子的上下文中,“loved”被正确识别为动词并相应地进行词形化。我的解决方案是将完整的句子传递给 SpaCy,然后将生成的词条重新映射到原始标记化,这需要更多的工作,但在词条化方面应该会得到最好的结果。

1.重新加入和nlp-ing句子

df = pd.DataFrame.from_records([['He', 'loved', 'floors'],
                                ['I', 'don\'t like', 'vacuums']], columns=['subject', 'verb', 'object'])

df['raw_tokens'] = df[['subject', 'verb', 'object']].values.tolist()
df['doc'] = df.raw_tokens.agg(' '.join).apply(nlp)
  subject        verb   object                raw_tokens                          doc
0      He       loved   floors       [He, loved, floors]          (He, loved, floors)
1       I  don't like  vacuums  [I, don't like, vacuums]  (I, do, n't, like, vacuums)

2.对齐两个标记化

我们需要将该doc列转换为引理列表,稍后我们可以在保留原始标记化的同时对其进行重新组合。实现此目的的一种方法是使用 SpaCy 的内置Alignment模块。给定两个具有不同标记化的标记列表,这将通过索引为您提供一个列表到另一个列表的映射,例如,

from spacy.training import Alignment

raw_toks = ['I', "don't like", 'vacuums']
spacy_toks = ['I', 'do', "n't", 'like', 'vacuums']

alignment = Alignment.from_strings(raw_toks, spacy_toks)

# [0, 1, 2, 3, 4]
print(list(alignment.x2y.dataXd))

# [0, 1, 1, 1, 2]
print(list(alignment.y2x.dataXd))

这告诉我们索引 1、2 和 3 inspacy_toks处的标记属于标记 1 in raw_toks。在下面的函数中,我用它在使用 SpaCy 处理令牌后将令牌映射到原始令牌化,但我们收集的不是令牌字符串,而是引理。这个想法是将spacy_toks上面的输入变成[['I'], ['do', "n't", 'like'], ['vacuum']].

from spacy.training import Alignment
from itertools import groupby

def lemmatize(row):
    
    tokens, lemmas = zip(*((x.text, x.lemma_) for x in row.doc))
    lemmas = iter(lemmas) # so we can use next()
    
    # get alignment of surface token strings 
    alignment = Alignment.from_strings(row.raw_tokens, tokens).y2x.dataXd
    lemma_map = list()
    
    # collect lemmas into subgroups
    for _,g in groupby(alignment):
        lemma_map.append([next(lemmas) for _ in g])
    
    return [' '.join(w) for w in lemma_map]

3. 将其应用于数据框

df['lemmas'] = df.apply(lemmatize, axis=1)

# some cleaning
df[['subject_lemma', 'verb_lemma', 'object_lemma']] = pd.DataFrame(df.lemmas.tolist(), index=df.index)

最后结果:

  subject        verb   object subject_lemma   verb_lemma object_lemma
0      He       loved   floors            he         love        floor
1       I  don't like  vacuums             I  do n't like       vacuum


推荐阅读