Make apps smarter with Natural Language
Machine Learning & AI 进阶 16m

用 Natural Language 让应用更智能

Make apps smarter with Natural Language

2020年6月24日

在 Apple 官方观看视频

一句话判断

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
    }
}

最佳实践

  1. 文本分类器的训练数据要均衡。如果「科技」类有 1000 条训练数据,「财经」类只有 100 条,模型会偏向科技类。每个类别的数据量差异不要超过 3 倍。
  2. 词嵌入搜索不是万能的NLEmbedding 的预训练词嵌入覆盖常见词汇,但领域专业术语(如医学、法律术语)可能效果不好。对于专业领域,考虑导入自己的词嵌入文件。
  3. 缓存 NLModel 实例。模型加载有一次性开销,不要每次分类都重新加载。把 NLModel 做成单例或在 app 启动时加载一次。

还有什么值得关注

  • NLTagger 在 iOS 14 中新增了对 .embedding scheme 的支持,可以直接在 tagger pipeline 中获取词嵌入向量。
  • Create ML 的 Text Classifier 支持迁移学习(Transfer Learning),用苹果预训练的语言模型做基础,你的训练数据可以少到每类 20 条就能有不错的效果。
  • NLEmbedding 支持的词向量维度是 300 维,和 word2vec 的标准维度一致,可以与外部的 NLP 工具链互通。
WWDC 2020