用 Natural Language 让应用更智能
Make apps smarter with Natural Language
2020年6月24日
一句话判断
Natural Language 框架在 iOS 14 中新增了自定义文本分类器和词嵌入(Embedding)支持——你可以用自己领域的训练数据构建专属分类器,不依赖云端 API,全部在设备端运行。
这场 Session 讲了什么
Natural Language 框架从 iOS 12 开始提供基础的 NLP 能力(分词、词性标注、命名实体识别)。iOS 14 把它带到了新高度:支持自定义 NLModel 的文本分类器和词序列标注器,以及全新的 NLEmbedding 词嵌入功能。
自定义文本分类器意味着你可以用自己的训练数据(通过 Create ML 的 MLTextClassifier 训练),在设备上运行领域专属的分类模型。比如一个新闻 app 可以训练一个「科技/体育/财经/娱乐」分类器,不需要把用户内容发到云端。
NLEmbedding 提供了词向量功能,可以计算两个词的语义相似度、查找最近邻词、以及做句子级别的相似度比较。苹果提供了预训练的词嵌入(支持多种语言),你也可以导入自己的词嵌入文件。
值得深挖的点
NLEmbedding 的实际应用场景
词嵌入最直接的应用是搜索增强。用户搜索「手机」时,用 embedding 可以找到语义相近的「智能手机」「iPhone」「移动设备」等内容,而不需要手动维护同义词表。Session 演示了如何用 NLEmbedding.nearestNeighbors(for:count:) 找到语义最相近的词,并用这个能力改善 app 内搜索的召回率。
自定义分类器的训练流程
用 Create ML 训练文本分类器:准备 JSON 格式的训练数据(每条包含 text 和 label),在 Create ML 中选择 Text Classifier 模板,导入数据,训练,导出 .mlmodel 文件。然后把模型文件拖进 Xcode 项目,用 NLModel(mlModel:) 加载。整个过程不需要写一行 Python。
代码片段
import NaturalLanguage
// 使用自定义文本分类器
class NewsClassifier {
let model: NLModel
init() {
// 加载用 Create ML 训练的分类模型
let mlModel = try! NewsCategoryClassifier(configuration: MLModelConfiguration()).model
model = try! NLModel(mlModel: mlModel)
}
func classify(text: String) -> String? {
// 获取预测标签和置信度
let hypothesis = model.predictedLabel(for: text)
return hypothesis
}
func classifyWithConfidence(text: String) -> [(String, Double)] {
let predictions = model.predictedLabelsHypotheses(for: text, maximumCount: 3)
return predictions.map { (label, confidence) in
return (label, Double(confidence))
}.sorted { $0.1 > $1.1 }
}
}
import NaturalLanguage
// 使用 NLEmbedding 做语义搜索
class SemanticSearch {
let embedding: NLEmbedding
init() {
// 使用系统预训练的中文词嵌入
if let emb = NLEmbedding.wordEmbedding(for: .simplifiedChinese) {
embedding = emb
} else {
// 也可以加载自定义词嵌入
let url = Bundle.main.url(forResource: "custom_embedding", withExtension: "txt")!
embedding = try! NLEmbedding(contentsOf: url)
}
}
// 查找语义相近的词
func findSimilarWords(to word: String, count: Int = 5) -> [(String, Double)] {
let neighbors = embedding.nearestNeighbors(for: word, maximumCount: count)
return neighbors.map { neighbor in
return (neighbor.0, Double(neighbor.1))
}
}
// 计算两个词的相似度
func similarity(between word1: String, and word2: String) -> Double {
return Double(embedding.distance(between: word1, and: word2))
}
// 基于词嵌入的搜索扩展
func expandSearchQuery(_ query: String) -> [String] {
var expandedTerms = [query]
let similarWords = findSimilarWords(to: query, count: 3)
expandedTerms.append(contentsOf: similarWords.map { $0.0 })
return expandedTerms
}
}
最佳实践
- 文本分类器的训练数据要均衡。如果「科技」类有 1000 条训练数据,「财经」类只有 100 条,模型会偏向科技类。每个类别的数据量差异不要超过 3 倍。
- 词嵌入搜索不是万能的。
NLEmbedding的预训练词嵌入覆盖常见词汇,但领域专业术语(如医学、法律术语)可能效果不好。对于专业领域,考虑导入自己的词嵌入文件。 - 缓存 NLModel 实例。模型加载有一次性开销,不要每次分类都重新加载。把
NLModel做成单例或在 app 启动时加载一次。
还有什么值得关注
NLTagger在 iOS 14 中新增了对.embeddingscheme 的支持,可以直接在 tagger pipeline 中获取词嵌入向量。- Create ML 的 Text Classifier 支持迁移学习(Transfer Learning),用苹果预训练的语言模型做基础,你的训练数据可以少到每类 20 条就能有不错的效果。
NLEmbedding支持的词向量维度是 300 维,和 word2vec 的标准维度一致,可以与外部的 NLP 工具链互通。