NLTagger是苹果的情感分析API。它集成在iOS和macOS中,在设备本地运行,不需要服务器,只需三行代码即可使用。当你搜索"Swift情感分析"时,它是第一个出现的结果。
对于任何开发者在日常工作中会写的文本,它返回以下结果:
| 消息 | NLTagger | 实际情况 |
|---|---|---|
| “delete the temp file” | -0.8 | 中性指令 |
| “ok” | -0.8 | 中性确认 |
| “run make test” | -0.6 | 中性指令 |
| “commit and push” | -0.4 | 中性指令 |
| “great job, thanks!” | +1.0 | 正面(正确) |
| “this is fucking broken” | -1.0 | 负面(正确) |
评分范围从-1.0(极其负面)到+1.0(极其正面)。根据苹果的评判,“delete the temp file"与"this is fucking broken"具有几乎相同的情感强度。而"ok”——英语中最中性的回应——得分是**-0.8**。
这些不是编造的数字。这些是在任何运行macOS 14+的Mac上都能重现的结果。
如何重现
import NaturalLanguage
let tagger = NLTagger(tagSchemes: [.sentimentScore])
tagger.string = "delete the temp file"
let (tag, _) = tagger.tag(
at: tagger.string!.startIndex,
unit: .paragraph,
scheme: .sentimentScore
)
print(tag?.rawValue ?? "nil")
// "-0.8"
八行Swift代码。结果是确定性的:相同的文本在每次执行、每个设备上都产生相同的分数。这不是随机错误,而是系统性偏见。
为什么会发生
NLTagger使用的模型是基于消费者文本训练的:产品评价、社交媒体评论、餐厅评价。在那个领域,它表现相当不错。“This product is terrible"得分-0.9,“I love this app"得分+0.9。两个都正确。
问题在于技术词汇与情感词汇共享单词,但含义完全不同:
- kill、terminate、abort —— 进程的正常操作
- fatal、critical、panic —— 日志级别
- crash、dead、zombie —— 系统状态
- delete、destroy、drop —— 数据操作
- reject、deny、block —— 访问控制
对于一个基于亚马逊评价训练的模型来说,“delete"是破坏性的,“kill"是暴力的,“fatal"是灾难性的。它没有上下文来理解"kill the background process"与"关门时请关灯"一样平淡无奇。
这称为词汇偏见:模型对单个词汇分配极性而不理解领域背景。这相当于机器翻译将"I’m killing it"解释为正在进行的凶杀案。
没人注意到
有趣的是:这种偏见没有出现在任何学术论文中。有数百篇关于软件工程中情感分析的文章——代码评审分析、提交中的毒性检测、问题分类——但没有一篇提及NLTagger。自然语言处理社区完全忽略了它。
原因很简单:没人在生产环境中使用NLTagger。研究人员使用Hugging Face的模型。企业使用OpenAI或Google的API。NLTagger是独立开发者为iOS应用寻找快速解决方案时使用的,一个下午就实现了,而且从不对照真实数据验证结果。
这意味着App Store中有未知数量的应用正在使用一个认为"ok"几乎与明确侮辱一样负面的模型来分类技术文本。而且它们都不知道这个问题。
真实案例:代码会话监控
这个问题不是理论性的。我构建了Tokamak,一个监控Claude Code会话的macOS应用。我想添加的功能之一是挫折检测:如果你花了两小时与一个bug搏斗,应用应该能够通过你消息的语调检测到这一点。
显而易见的方法是使用NLTagger。三行代码,零依赖,设备本地运行。原型在20分钟内就工作了。
然后它将"删除临时文件并运行测试"分类为-0.7。这是一个完全中性的指令,你会给代码助手的那种。根据NLTagger,我正处于情感危机的边缘。
我们如何解决:确定性层优先
解决方案不是"使用更好的模型”(虽然那也有帮助)。解决方案是不要完全依赖单一的不透明模型。
SentimentKit是我为解决这个问题编写的库。它使用4层管道,确定性分析首先运行,机器学习只在有歧义时介入:
消息
│
├─ 第1层:关键词检测器(确定性,约20KB)
│ 精心策划的脏话、挫折和积极表达词典。
│ 8种语言(ES, EN, PT, DE, FR, ZH, JA, KO)。
│
├─ 第2层:VADER规则(确定性,约500KB)
│ 否定("not good"),增强词("very bad"),
│ 大写字母,标点符号。
│
├─ 第3层:CoreML DistilBERT(可选)
│ 仅用于长且有歧义的消息。
│
└─ 第4层:LLM评分器(可选,需要API)
仅用于前面层无法解决的情况。
关键是第1层和第2层是确定性的。它们每次都产生相同的结果。没有不透明的模型。你可以审核每个词典,每个规则。最重要的是:中性的技术命令得分是0.0,而不是-0.8。
import SentimentKit
let analyzer = SentimentAnalyzer()
let result = analyzer.analyze("delete the temp file and run make test")
// result.score = 0.0 -- 中性,应该如此
let angry = analyzer.analyze("qué coño es esto, no funciona una mierda")
// angry.score = -2.0 -- 检测到两个脏话
VADER:应该更为人知的工具
第2层的规则受到VADER(Valence Aware Dictionary and sEntiment Reasoner)的启发,这是C.J. Hutto和Eric Gilbert在2014年在佐治亚理工学院创建的系统。VADER是基于规则而不是机器学习的情感分析器,使用由人工标注的约7500个单词的情绪价值词典。
VADER的有趣之处在于它处理词袋模型忽略的语言修饰符:
- 否定:“not good"反转极性
- 增强词:“very good"放大分数
- 大写字母:“GOOD"比"good"得分更高
- 转折:“the food was great BUT the service was terrible”——“but"给第二个分句更多权重
VADER有局限性(不理解讽刺,在英语中效果更好),但其透明度是最大的优点。当VADER分类错误时,你可以打开词典,找到条目并纠正它。使用NLTagger时,你只能耸耸肩。
讽刺:苹果创造了问题也提供了解决方案
从macOS 26 / iOS 26开始,苹果提供了Foundation Models框架,一个在设备本地运行的约30亿参数的LLM。它在情感分析方面比NLTagger好得无法比较,因为它理解上下文,而不仅仅是单个词汇。
同一个苹果公司卖给你一个认为"ok"是负面的分类器,现在又为你提供一个理解"kill the process"是技术指令的语言模型。正确的解决方案存在于同一生态系统内;只是晚了十年。
SentimentKit可以使用Foundation Models作为第4层(LLM评分器)。使用Apple Intelligence来纠正NLTagger错误的讽刺意味我并非没有察觉。
Anthropic如何检测挫折(剧透:正则表达式)
一个有趣的事实。在Claude Code(Anthropic的Claude CLI)的代码中,用户挫折检测是用正则表达式实现的。不是用NLTagger。不是用机器学习模型。而是用正则表达式搜索"this is broken”、“doesn’t work”、“what the"等模式。
这是一种粗糙但有效的方法:在技术命令中没有误报,因为模式是明确的。Anthropic,世界上最先进的LLM之一背后的公司,使用正则表达式来检测挫折。如果这还不能说明生产环境中情感分析的现状,没有什么能说明了。
黄金测试:144个样本并在增加
SentimentKit包含144个带精确断言的黄金消息:35个西班牙语,35个英语,20个葡萄牙语,19个德语,20个法语和15个中文。数据来自已发布的数据集(cardiffnlp/tweet_sentiment_multilingual,sepidmnorozy/Chinese_sentiment)和公共仓库的真实代码评审。
每个样本都有期望的分数范围、应该检测到的表达式和不应该检测到的表达式。测试系统包括PHANTOM检测(词典中从未与真实数据匹配的条目)和UNCONSUMED检测(词典中缺失的真实表达式)。这与我在Tokamak中用于验证DTO与生产数据的方法相同:如果LLM编造了数据,测试就会失败。
试用
git clone https://github.com/frr149/SentimentKit
cd SentimentKit
swift test
或作为Swift Package Manager依赖:
.package(url: "https://github.com/frr149/SentimentKit.git", from: "1.0.0")
这个包在没有CoreML和互联网连接的情况下也能工作。确定性层(关键词 + VADER)对技术文本中的大多数用例都足够了。
教训
如果你将NLTagger用于产品评价以外的任何文本,你得到的是带有两位小数虚假精度的垃圾数据。模型没有损坏;它超出了适用领域。而API不会警告你。
自然语言处理社区忽略它。苹果没有将其记录为限制。使用它的开发者从不验证结果,因为-1.0到1.0之间的Double看起来很严格,尽管它没有任何意义。
在信任情感分数之前,要问自己:“ok"应该是-0.8吗?如果你的工具说是,问题不在用户身上。问题在工具上。