语言标注器错误地标注为 'OtherWord'

Linguistic tagger incorrectly tagging as 'OtherWord'

我一直在将 NSLinguisticTagger 与句子一起使用,但在使用 'I am hungry' 或 'I am drunk' 等句子时遇到了一个奇怪的问题。虽然人们希望 'I' 被标记为代词,'am' 被标记为动词,而 'hungry' 被标记为形容词,但事实并非如此。相反,它们都被标记为 OtherWord.

我做错了什么吗?

NSString *input = @"I am hungry";
NSLinguisticTaggerOptions options = NSLinguisticTaggerOmitWhitespace;
NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:[NSLinguisticTagger availableTagSchemesForLanguage:@"en"] options:options];
tagger.string = input;

[tagger enumerateTagsInRange:NSMakeRange(0, input.length) scheme:NSLinguisticTagSchemeNameTypeOrLexicalClass options:options usingBlock:^(NSString *tag, NSRange tokenRange, NSRange sentenceRange, BOOL *stop) {
    NSString *token = [input substringWithRange:tokenRange];
    NSString *lemma = [tagger tagAtIndex:tokenRange.location
                                  scheme:NSLinguisticTagSchemeLemma
                              tokenRange: NULL
                           sentenceRange:NULL];
    NSLog(@"%@ (%@) : %@\n", token, lemma, tag);
}];

输出为:

I ((null)) : OtherWord
am ((null)) : OtherWord
hungry ((null)) : OtherWord

quite some time in chat 之后我们发现了问题:

The sentence does not contain enough information to determine its language.

要解决此问题,您可以:

在您的实际句子之后用您选择的语言添加一个演示句子。这应该可以保证检测到您的首选语言。

告诉标注器使用什么语言:添加行

[tagger setOrthography:[NSOrthography orthographyWithDominantScript:@"Latn" languageMap:@{@"Latn" : @[@"en"]}] range:NSMakeRange(0, input.length)];

enumerate 调用之前。通过这种方式,您可以明确告诉标注器您希望文本使用哪种语言,在本例中,英语 (en) 作为拉丁语主导语言 (Latn) 的一部分。

如果您不确定该语言,如果单词被标记为 OtherWord 意味着无法检测到该语言,则使用这些方法中的任何一种作为后备方法可能很有用。

API 是一团混乱,所以请记住,有多种方法可以传递 schemesoptions 来尝试表达您的词性数据想。

除此之外,您传递给标记器的方案必须与传递给标记器上的枚举方法的方案相匹配,否则它会 return OtherWord 否则它不会枚举完全没有。

如果您将 .nameType 指定为 scheme,那么它将 return OtherWord 用于所有不是人名、地名等的词

如果您将其更改为 .nameTypeOrLexicalClass,那么它会描述每个单词的非名称词性并列出名称类型。

这也取决于给定的unit;例如对于 paragraph 单位,它将显示 Other,因为它不能将其描述为 Noun 或类似的东西。


示例 1

假设您像这样实例化标记器:

let text = "Frank said 'Don't take the easy way out. Build it slowly and meaningfully. Work multiple jobs to pay the bills if you need to. Take breaks and then pick it back up and keep moving. This is what it is; don't dilute the experience.'"
let options: NSLinguisticTagger.Options = [.omitWhitespace, .omitPunctuation]
let tagger = NSLinguisticTagger(tagSchemes: [.nameType, .lexicalClass, .lemma], options: Int(options.rawValue))
tagger.string = text
let range = NSRange(location: 0, length: text.utf8.count)
tagger.enumerateTags(in: range, unit: .word, scheme: .nameType, options: options) { tag, tokenRange, _ in
    guard let tag = tag else { return }
    let token = (text as NSString).substring(with: tokenRange)
    print("{token: \(token), tag: \(tag.rawValue), range: \(tokenRange)}")
}

这将打印出类似

的内容

{token: Frank, tag: PersonalName, range: {0, 5}}

{token: said, tag: OtherWord, range: {6, 4}}

{token: Do, tag: OtherWord, range: {12, 2}}

{token: n't, tag: OtherWord, range: {14, 3}}

{token: take, tag: OtherWord, range: {18, 4}}

即使您的标记器是使用 .lexicalClass 方案实例化的,您的枚举请求仅使用 .nameType 方案调用。这意味着标注器具有可用的词汇 class 信息,但您并未对其进行枚举。

API 以这种方式有点混乱,但这是因为枚举可以 运行 以多种方式对同一标注器进行枚举,标注器充当数据集。

更令人困惑的是,如果您尝试将枚举的方案更改为 .nameTypeOrLexicalClass,它不会枚举任何内容。这是因为您没有将 .nameTypeOrLexicalClass 传递给标记器选项,而是将它们作为 .nameType, .lexicalClass 单独传递。说真的,这是一个糟糕的 API 设计,但它就是这样。


示例 2

如果您想同时拥有名称和词法 classes,则需要明确使用 .nameTypeOrLexicalClass 选项:

let text = "Frank said 'Don't take the easy way out. Build it slowly and meaningfully. Work multiple jobs to pay the bills if you need to. Take breaks and then pick it back up and keep moving. This is what it is; don't dilute the experience.'"
let options: NSLinguisticTagger.Options = [.omitWhitespace, .omitPunctuation]
let tagger = NSLinguisticTagger(tagSchemes: [.nameTypeOrLexicalClass, .lemma], options: Int(options.rawValue))
tagger.string = text
let range = NSRange(location: 0, length: text.utf8.count)
tagger.enumerateTags(in: range, unit: .word, scheme: .nameTypeOrLexicalClass, options: options) { tag, tokenRange, _ in
    guard let tag = tag else { return }
    let token = (text as NSString).substring(with: tokenRange)
    print("{token: \(token), tag: \(tag.rawValue), range: \(tokenRange)}")
}

这将打印出类似

的内容

{token: Frank, tag: PersonalName, range: {0, 5}}

{token: said, tag: Verb, range: {6, 4}}

{token: Do, tag: Verb, range: {12, 2}}

{token: n't, tag: Adverb, range: {14, 3}}

{token: take, tag: Verb, range: {18, 4}}

{token: the, tag: Determiner, range: {23, 3}}


语言

对于额外的调试,您也可以设置语言或检查您想要的方案是否在您尝试标记的语言中可用(因为它在很大程度上取决于语言):

// check that it determined the language correctly
print("dominant language is: \(tagger.dominantLanguage)")

// or set the language specifically
tagger.setOrthography(NSOrthography.defaultOrthography(forLanguage: "en-US"), range: range)

// or list which tag schemes are even available for this language
let tagSchemes = NSLinguisticTagger.availableTagSchemes(forLanguage: "en")
print(tagSchemes)