首页 > 其他分享 >iOS MachineLearning 系列(19)—— 分析文本中的问题答案

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

时间:2023-06-04 23:03:46浏览次数:46  
标签:MachineLearning 19 词汇表 iOS startIndex let 序列 tagger append

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模式的输入输出:

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

需要注意,其输入有两个,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)
        }
    }
}

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

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


标签:MachineLearning,19,词汇表,iOS,startIndex,let,序列,tagger,append
From: https://blog.51cto.com/u_11643026/6412783

相关文章

  • iOS MachineLearning 系列(20)—— 训练生成CoreML模型
    iOSMachineLearning系列(20)——训练生成CoreML模型本系列前面的文章详细的介绍了在iOS中与AI能力相关的API的使用,也介绍了如何使用训练好的CoreML模型来实现更强大的AI能力。然而,无论是成熟的API提供的能力,还是各种各样的三方模型,有时候都并不能满足某一领域内的定制化需求。当我......
  • php 验证身份证有效性,根据国家标准GB 11643-1999 15位和18位通用
    校验函数//验证身份证是否有效functionvalidateIDCard($IDCard){if(strlen($IDCard)==18){returncheck18IDCard($IDCard);}elseif((strlen($IDCard)==15)){$IDCard=convertIDCard15to18($IDCard);returncheck18IDCard($IDC......
  • 深度解析iOS应用程序的生命周期
     摘要:iOS应用程序一般都是由自己编写的代码和系统框架组成,系统框架提供一些基本infrastructure给App来运行,而开发者则自己编写代码定制App的外观和行为,了解iOSInfrastructure及其如何工作对编写App很有帮助。iOS应用程序一般都是由自己编写的代码和系统框架(systemframeworks)组成......
  • 【iOS】GitHub上最受欢迎的iOS开源项目(二)
    “每一次的改变总意味着新的开始。”这句话用在iOS上可谓是再合适不过的了。GitHub上的iOS开源项目数不胜数,iOS每一次的改变,总会引发iOS开源项目的演变,从iOS1.x到如今的iOS7,有的项目已经被弃用,即使曾经的它很受开发者喜爱,有的项目则继续发扬光大,新项目更是层出不穷。在本文中,我们......
  • Oracle 12c/19c PDB数据库配置自动启动
    Oracle12c/19cPDB数据库配置自动启动在Oracle12c/19c多租户环境中,默认情况下,使用startup命令启动数据库实例后,你会发现PDB数据库的状态为MOUNT状态,PDB不会随着CDB启动而启动。如下例子所示:SQL>startupORACLEinstancestarted.TotalSystemGlobalArea2432695872by......
  • [SUCTF 2019]EasySQL 1 做题笔记
     题目考察的是SQL注入,先找一些常用的SQL命令1.判断注入类型:简单的注入类型有字符型和数字型2.判断列数?id=1’orderby4#?id=1'unionselect1,2,3#3.判断库名?id=1'unionselect1,database(),3#4.判断表名?id=1'unionselect1,group_concat(table_name),3......
  • 算法刷题记录:[NOIP1999]回文数
    题目链接https://ac.nowcoder.com/acm/contest/19859/G题目分析高精度相加+进制转换+判断回文的模拟题。AC代码//Problem:[NOIP1999]回文数//Contest:NowCoder//URL:https://ac.nowcoder.com/acm/contest/19859/G//MemoryLimit:262144MB//TimeLimit:20......
  • IPA50R190CE-ASEMI代理英飞凌MOS管IPA50R190CE
    编辑:llIPA50R190CE-ASEMI代理英飞凌MOS管IPA50R190CE型号:IPA50R190CE品牌:Infineon(英飞凌)封装:TO-220F最大漏源电流:24.8A漏源击穿电压:500VRDS(ON)Max:0.19Ω引脚数量:3沟道类型:N沟道MOS管芯片尺寸:MIL漏电流:恢复时间:5ns芯片材质:封装尺寸:如图特性:高压MOS管、N沟道MOS管工作结温:-40℃~150......
  • IPA50R190CE-ASEMI代理英飞凌MOS管IPA50R190CE
    编辑:llIPA50R190CE-ASEMI代理英飞凌MOS管IPA50R190CE型号:IPA50R190CE品牌:Infineon(英飞凌)封装:TO-220F最大漏源电流:24.8A漏源击穿电压:500VRDS(ON)Max:0.19Ω引脚数量:3沟道类型:N沟道MOS管芯片尺寸:MIL漏电流:恢复时间:5ns芯片材质:封装尺寸:如图特性:高压MOS管、N沟道MOS管......
  • [MtOI2019]幽灵军团
    题意求下面表达式的值,\[\prod_{i=1}^{A}\prod_{j=1}^{B}\prod_{k=1}^{C}\left(\frac{lcm(i,j)}{gcd(i,k)}\right)^{f(type)}\pmod{p}\]其中,\(type=\{0,1,2\}\),\(f(type)\)满足:\[f(0)=1,\]\[f(1)=i\timesj\timesk,\]\[f(2)=gcd(i,j,k).\]其中,......