iOS MachineLearning 系列(19)—— 分析文本中的问题答案

简介: 本篇文章将介绍Apple官方推荐的唯一的一个文本处理模型:BERT-SQuAD。此模型用来分析一段文本,并根据提供的问题在文本中寻找答案。需要注意,BERT模型不会生成新的句子,它会从提供的文本中找到最有可能的答案段落或句子。

iOS MachineLearning 系列(19)—— 分析文本中的问题答案

本篇文章将介绍Apple官方推荐的唯一的一个文本处理模型:BERT-SQuAD。此模型用来分析一段文本,并根据提供的问题在文本中寻找答案。需要注意,BERT模型不会生成新的句子,它会从提供的文本中找到最有可能的答案段落或句子。

BERT模型的使用比较复杂,大致可以分为如下几步:

  1. 将词汇表导入。
  2. 将问题和原文档分解为Token序列。
  3. 使用词汇表将Token序列转换成id序列。
  4. 将id序列转换成模型需要的多维数组进行输入。
  5. 根据分析的结果解析答案序列。
  6. 根据词汇表将答案序列转换为可读的字符串。

我们一步一步来进行介绍。

1 - 词汇表

词汇表没有过多需要讲的,其中定义了每个词汇对应的id,在本文末尾会有示例代码工程,工程中自带了需要使用的词汇表。此词汇表包含了3万余个词汇,每个词汇独占一行,其行号即表示当前词汇的id值。

加载词汇表的示例代码如下:

var tokensDic = Dictionary<Substring, Int>()
func readDictionary() {
   
   
    // 读取文件中的数据
    let fileName = "bert-base-uncased-vocab"
    guard let url = Bundle.main.url(forResource: fileName, withExtension: "txt") else {
   
   
        fatalError("Vocabulary file is missing")
    }
    guard let rawVocabulary = try? String(contentsOf: url) else {
   
   
        fatalError("Vocabulary file has no contents.")
    }
    // 按行进行分割
    let words = rawVocabulary.split(separator: "\n")
    let values = 0..<words.count
    // 加载到字典
    tokensDic = Dictionary(uniqueKeysWithValues: zip(words, values))
}

2 - 将问题和原文档分解为Token序列

在本系列前面的文章中,有介绍过NaturalLanguage这个框架,其实用来进行自然语言处理的,当然也包含文本的Token分解功能。示例代码如下:

var wordTokens = [Substring]()
let tagger = NLTagger(tagSchemes: [.tokenType])
// 全部转换成小写
tagger.string = content.lowercased()
tagger.enumerateTags(in: tagger.string!.startIndex ..< tagger.string!.endIndex,
                     unit: .word,
                     scheme: .tokenType,
                     options: [.omitWhitespace]) {
   
    (_, range) -> Bool in
    wordTokens.append(tagger.string![range])
    return true
}

var questionTokens = [Substring]()
tagger.string = question.lowercased()
tagger.enumerateTags(in: tagger.string!.startIndex ..< tagger.string!.endIndex,
                     unit: .word,
                     scheme: .tokenType,
                     options: [.omitWhitespace]) {
   
    (_, range) -> Bool in
    questionTokens.append(tagger.string![range])
    return true
}

3 - 将Token序列转换成ID序列

第三步,根据词汇表来将Token序列转换成ID序列,如下:

// 加载词汇表
readDictionary()
// 转换问题Token序列
let questionTokenIds = questionTokens.compactMap {
   
    token in
    tokensDic[token]
}
// 转换原文档Token序列
let contentTokenIds = wordTokens.compactMap {
   
    token in
    tokensDic[token]
}

4 - 将ID序列转换为模型的输入

这一步略微复杂,首先我们先看下BERT-SQuAD模式的输入输出:

需要注意,其输入有两个,wordIDs是ID序列二维数组,其中包含问题,源文档,使用特殊的分隔符进行分割。wordTypes也是一个二维数组,对应的标记wordIDs数组中每个元素的意义。示例代码如下:

// 开始标记,使用特殊数值101
let startToken = 101
// 分隔符标记,使用特殊数值102
let separatorToken = 102
// 补位标记,使用特殊数值0
let padToken = 0
// 输入wordIDs
var inputTokens:[Int] = []
// 先拼入开始标记
inputTokens.append(startToken)
// 拼入问题ID序列
inputTokens.append(contentsOf: questionTokenIds)
// 拼入分隔符标记
inputTokens.append(separatorToken)
// 拼入源文档ID序列
inputTokens.append(contentsOf: contentTokenIds)
// 拼入分隔符标记
inputTokens.append(separatorToken)
// 不够384位,则用0补齐
while inputTokens.count < 384 {
   
   
    inputTokens.append(padToken)
}
// 输入wordTypes
var inputTokenTypes:[Int] = []
// 开始标记,分隔符,和补位标对应的数据位设0
inputTokenTypes.append(0)
// 问题内容位设1
inputTokenTypes.append(contentsOf: Array(repeating: 1, count: questionTokenIds.count))
inputTokenTypes.append(0)
// 源文档内容位设1
inputTokenTypes.append(contentsOf:  Array(repeating: 1, count: contentTokenIds.count))
inputTokenTypes.append(0)
while inputTokenTypes.count < 384 {
   
   
    inputTokenTypes.append(0)
}

// 构造MLMultiArray二维数组,其结构为1*384的二维结构
var tokenMultiArray = try! MLMultiArray(shape: [1, 384], dataType: .int32)
for (index, inputToken) in inputTokens.enumerated() {
   
   
    tokenMultiArray[[0, NSNumber(integerLiteral: index)]] = NSNumber(integerLiteral: inputToken)
}

var typesMultiArray = try! MLMultiArray(shape: [1, 384], dataType: .int32)
for (index, inputToken) in inputTokenTypes.enumerated() {
   
   
    typesMultiArray[[0, NSNumber(integerLiteral: index)]] = NSNumber(integerLiteral: inputToken)
}

5 - 使用模型进行预测

准备好了输入数据,这一步就非常简单,示例如下:

let model = try! BERTSQUADFP16(configuration: MLModelConfiguration())
let input = BERTSQUADFP16Input(wordIDs: tokenMultiArray, wordTypes: typesMultiArray)
let output = try! model.prediction(input: input)
handleOutput(output: output)

6 - 处理输出

BERT-SQuAD模型的输出为两个1*1*384*1的四位数组,指定了答案的起始位置与结束位置。虽然输出数据为4维的,但是其有3各维度都只有1个元素,因此我们可以将其提取为一维的,定义方法如下:

extension MLMultiArray {
   
   
    func doubleArray() -> [Double] {
   
   
        let unsafeMutablePointer = dataPointer.bindMemory(to: Double.self, capacity: count)
        let unsafeBufferPointer = UnsafeBufferPointer(start: unsafeMutablePointer, count: count)
        return [Double](unsafeBufferPointer)
    }
}

处理输出数据如下:

func handleOutput(output: BERTSQUADFP16Output) {
   
   
    // 值越大,表示当前索引为答案的开始位置的可能性越大,找到最可能的答案开始位置
    var startIndex = 0
    for p in startIndex ..< output.startLogits.doubleArray().count {
   
   
        if output.startLogits.doubleArray()[p] > output.startLogits.doubleArray()[startIndex] {
   
   
            startIndex = p
        }
    }
    // 同理,找到最可能得答案结束位置,这里我们设置答案长度不超过5个Token
    var endIndex = startIndex
    for p in endIndex ..< startIndex + 5 {
   
   
        if output.endLogits.doubleArray()[p] > output.endLogits.doubleArray()[startIndex] {
   
   
            endIndex = p
        }
    }
    // 获取答案ID序列
    let subs = inputTokens[startIndex ..< endIndex]
    // 将ID序列转回字符串
    for i in subs {
   
   
        for item in tokensDic where item.value == i {
   
   
            print(item.key)
        }
    }
}

代码运行效果如下图所示:

本中所涉及到的代码,都可以在如下 Demo 中找到:

https://github.com/ZYHshao/MachineLearnDemo

本系列文章到此已经将Apple官方所推荐的模型都做了介绍,当然这些模式的训练都是广泛的,不一定会适用于你的应用场景,CoreML框架也提供了更加强大的模型训练能力,我们可以根据自己的场景并提供有针对性的数据进行个性化的模型训练,在后续文章中会再详细讨论。

目录
相关文章
|
9天前
|
搜索推荐 Android开发 iOS开发
安卓与iOS系统的用户界面设计对比分析
本文通过对安卓和iOS两大操作系统的用户界面设计进行对比分析,探讨它们在设计理念、交互方式、视觉风格等方面的差异及各自特点,旨在帮助读者更好地理解和评估不同系统的用户体验。
35 1
|
9天前
|
Android开发 数据安全/隐私保护 iOS开发
安卓与iOS系统的发展趋势与比较分析
【2月更文挑战第6天】 在移动互联网时代,安卓和iOS系统作为两大主流移动操作系统,各自呈现出不同的发展趋势。本文将从技术角度出发,对安卓和iOS系统的发展方向、特点及未来趋势进行比较分析,以期为读者提供更深入的了解和思考。
49 4
|
8月前
|
C语言 索引
09-iOS之load和initialize底层调用原理分析
09-iOS之load和initialize底层调用原理分析
61 0
|
9天前
|
安全 搜索推荐 Android开发
Android 与 iOS 的比较分析
【2月更文挑战第5天】 Android 和 iOS 是目前市场上两种最流行的移动操作系统,它们都拥有自己的特点和优势。本文将会分别从操作系统设计、应用生态、安全性等方面对这两种操作系统进行比较和分析,希望能够帮助读者更好地选择适合自己的移动设备。
|
机器学习/深度学习 API iOS开发
iOS MachineLearning 系列(17)—— 几个常用的对象识别 CoreML 模型
上一篇文章中,我们介绍了几个官方的图片分类的模型,图片分类模型的应用场景在于将图片中最主要的事物进行识别,在已有的词库中找到最可能得事物。而对象识别则要更高级一些。再之前的文章,我们介绍过可以使用官方提供的API来进行矩形识别,文本识别,二维码识别以及人脸识别等,这类识别功能的特点是我们不仅可以将图片中的物体位置和尺寸分析出来,还可以对其进行类别的分类。
265 0
|
10月前
|
机器学习/深度学习 人工智能 自然语言处理
iOS MachineLearning 系列(22)——将其他三方模型转换成CoreML模型
本篇文章将是本系列文章的最后一篇。本专题将iOS中有关Machine Learning的相关内容做了整体梳理。下面是专题中的其他文章地址,希望如果你有需要,本专题可以帮助到你。
245 0
|
10月前
|
数据可视化 数据挖掘 iOS开发
iOS MachineLearning 系列(21)——CoreML模型的更多训练模板
前面文章中,有介绍如何训练生成定制化需求的 CoreML 模型,以图像分类为例做了演示.
133 0
|
10月前
|
存储 编解码 缓存
HTTP Live Streaming直播(iOS直播)技术分析与实现
HTTP Live Streaming直播(iOS直播)技术分析与实现
139 1
|
12月前
|
人工智能 数据挖掘 API
iOS MachineLearning 系列(20)—— 训练生成CoreML模型
本系列前面的文章详细的介绍了在iOS中与AI能力相关的API的使用,也介绍了如何使用训练好的CoreML模型来实现更强大的AI能力。然而,无论是成熟的API提供的能力,还是各种各样的三方模型,有时候都并不能满足某一领域内的定制化需求。当我们拥有很多的课训练数据,且需要定制化的AI能力时,其实就可以自己训练生成CoreML模型,将此定制化的模型应用到工程中去。
288 0
iOS MachineLearning 系列(20)—— 训练生成CoreML模型
|
12月前
|
存储 API vr&ar
iOS MachineLearning 系列(18)—— PoseNet,DeeplabV3与FCRN-DepthPrediction模型
本篇文章将再介绍三个官方的CoreML模型:PoseNet,DeeplabV3和FCRN-DepthPrediction。
230 0
iOS MachineLearning 系列(18)—— PoseNet,DeeplabV3与FCRN-DepthPrediction模型