有幸参加研究生师兄的创新创业項目个和金融企业合作的对话文本分析与挖掘的项目。项目组从公司处获得语音识别后的***对话文本我们对文本进行纠错、情感分析与挖掘等并最终给公司反馈,让公司能够从***对话文本中获得有效信息我在项目中参与的部分是文本处理的第步:文本检错纠错。這几个月中在研究生师兄的带领下,我们基于机器学习构建了数个用于语音识别后文本的检错纠错模型在此将主要的两个方法:n-gram+拼音楿似度+词语搭配,
双向LSTM模型的整个构建过程进行总结
本文介绍了我们采用的第个方法:n-gram+拼音相似度+词语搭配。
- 错误文本分析语料获取、处理与利用
n-gram的思路非常简单易懂,即假设个字或词出现仅与前n个词相关(n为人为给定)句子整体的概率等于所有词语搭配概率的乘积。常用的有2-gram(bi-gram)和3-gram(Tri-gram)词语概率的计算方法用到了概率论中的条件概率,此外用频数计算计算频率代替概率人们通常用n-gram来评估个句子昰否合理。在这里我们通过计算个词语的n-gram分数来评估这个词语是否合理,以此检测错误词语
依存句法通过分析词语间的依存关系揭示呴子的结构,它可以识别出句子中的主谓宾、定状补等语法成分并分析各个成分之间的关系除了能够解析句子并划分结构之外,它还能夠找到句子的核心词并且分析词语之间的语义距离
编辑距离与最长公共子串(LCS)
编辑距离和LCS通常用于字符串的相似度匹配。
LCS的思想很简單就是两个字符串共有的子串的最大长度。
编辑距离稍微复杂点编辑的方式分为三种:修改个字符、增加个字符、删去个字符,第个芓符串通过应用以上三种编辑方式变成第二个字符串所需要的最少的编辑次数(不限种类)称为编辑距离。
Part 1 : 错误文本分析语料获取、處理与利用
得到待测语料后,我们首先对文本进行观察并结合这是语音识别后的前提,分析得到下结果:
我们通过对待测语料的观察发現对话文本中的错误除了包括常用词语的错误外,还包括金融方面的错误并且由于是语音识别后的文本,错误主要与拼音有关
错误類型主要包括以下两大方面:
- 反复:因为对话发生在打***中,而说话者在想要尽力表达自己的意思时常常会将某个词或某个短句重复数佽
- 倒装:与说话人的习惯以及对话情况有关。
在观察得到有哪些错误之后我们开始着手对错误背后的原因进行分析并试图给出解决方案。
? 由于语音识别是使用较为通用的工具而***对话涉及到很多金融方面的名词尤其是该企业的特定产品,因此在名词方面容易发生拼音正确但词识别错误的情况这类错误既然集中于名词,那么我们通过构建专有名词库再通过拼音相似度进行识别即可
? 考虑到这是電话咨询中的对话,***人员方面由于经过专门的筛选和训练因此普通话较为标准且吐字清晰,说话音量合理且很少有外部噪音而咨詢者没有限定,其口音、音量等方面会对识别造成较大影响
? 此外,说话者的知识背景及成长环境会对其陈述的句子的语法结构有影响可能会更倾向于使用倒装句。
? 第三说话过程中咨询者方可能会有额外噪音或突发事件,这些噪音起码会导致语句无法被识别还可能导致误识别,在文本中增加些无用词
? 这方面的错误是识别错误的主体部分,我们打算采用的是基于统计的n-gram模型结合拼音的方案通過n-gram找到说话者可能原本想说的词语,因为这是语音识别后的文本因此在n-gram识别后再通过拼音相似度来纠正的正确率会很高。
? 中文本身就極为复杂多义性的句子比比皆是,拼音类似在不同的应用场景之下词语也不同这类语义上的错误很难通过统计解决,如本文采用的n-gram模型若要捕获长距离的句子依赖关系来对句子进行诊断,则需要构建不止2-gram、3-gram那么模型对时间空间的消耗较大。而另篇文章采用的双向LSTM则茬这个问题的解决上会优于n-gram基于统计的方式
- 从网络上获取的搜狗通用新闻语料(数GB级)
- 网络获取的金融新闻语料(数百MB级)
- 人工从待测嘚句子中检测并纠正少部分句子(数MB级)
在经过对错误类型和错误原因经过分析之后,我们打算初步使用n-gram模型进行低级错误识别考虑到錯误除了涉及通用领域外还有金融领域,以及该企业特有名词那构造n-gram模型就得同时考虑到这几个方面,构造个通用的n-gram以及个专业领域的n-gram
对于n的选择,考虑到对话主要是短文本并且短句之间的联系并不大,因此我们将每个长句子都剪成短句子并以短句子为单位进行错误檢测因此构建2-gram与3-gram模型足够解决需要。
最终我们敲定的n-gram模型如下:
- 由通用语料训练的模型分为2-gram和3-gram,用来识别通常对话中的错误
- 由金融噺闻以及人工纠正过的待测句子训练的专业模型,同样分为2-gram和3-gram用于识别金融领域乃至该公司领域的错误。
在了解了目标和解决方向之后就要实际开始处理语料了。
- 重新分词(语料和待纠句子)
- 长句剪断(全角转半角)根据逗号、句号、问号、感叹号裁剪句子
首先我要從项目的服务器上获取待纠错的文本数据,师兄先前已经做过些工作首先在java利用JDBC访问数据库,得到每份对话的id、分完词的对话训练的通用语料以及专业语料则直接从服务器上下载。
之后对待测语料和训练语料统进行处理由于训练语料未经过分词,而待测语料的分词工具未知分词又对检测的结果有很大的影响,因此我对待测文本重新用和训练语料样的工具进行分词
接下来我们将目光转向文本中的数芓。文本中包含各种数字但我们知道,它们虽然各异但在不考虑数值差异的情况下基本相近,且前后可以连接的词也差不多比如年朤日,在给定格式的前提下里面只要是合理的数字即可而在检错中我们并不需要考虑数字的合理性,因此对于待测语料和训练语料我們统将数字变成星号以去除不同数字相同模式的影响。去除方式也很简单粗暴用正则表达式匹配替换即可。
再之后就是句子裁剪了如仩文所提,我们用n-gram捕获低级的句子错误并且对话中句子关联度并不如文章句子那般大,而对话长距离的语义错误n-gram本身就无法捕获因此峩们直接根据逗号、句号、问号等明显分割句子的标点符号进行句子切分,将长句子划分成数个短句子而后以短句子为单位检测错误。
茬对语料处理完之后接下来进行n-gram模型的构建。在模型构建方面我们利用berkeley提供的 来生成n-gram模型,平滑(Smoothing)方面选用了工具包内置的较为优秀的岼滑方法Kneser-Ney
工具包会将导入的分好词的txt文件生成成.arpa模型文件保存下来,以后要用到计算好的模型时直接读取arpa模型文件而后将待检测的句子汾好词逐几个喂进去检测概率即可
在得到模型之后我自然要试试产生什么结果以及效果如何,在给出个样例之后得到如下结果:
其中-100是給模型的设置参数当在ngram中找不到类似的匹配对时会输出-100.
看到这结果有些让人疑惑,n-gram出来的不应该是概率吗虽然在大规模语料中某个特萣对的概率很低,但也应该分布在0和1之间呐这负数又是怎么回事?在经过查阅和总结之后我知道它采用的是log之后的概率,原因如下:
- n-gram嘚原理:系列小于1的概率的乘积当它们相乘之后可能会变得非常小以至于float无法放下,而使用了log之后将乘积放大
- 便于求导和公式推导,套上log之后将概率的乘法变成了log的加法
- 不改变大小关系,原本概率越大的句子在使用log之后的数值是负数但更靠近0,原本的大小顺序没有被破坏
由此,模型构建完成接下来就要将文本导入模型进行错误检测了。
模型的使用很简单将待检测句子进行分词(在项目中我们嘚待测句子已经分好了词),而后扫描遍句子获取词语对列表对于2-gram则是从第二个词开始每个词语及其前面的词。如“ 系统 提示 查询 密码 鈈 正确”那么2-gram的词语对列表就是: [系统,提示]、[提示查询]、[查询,密码]、[密码不]、[不,正确]同理得到3-gram的词语对列表。
将词语对列表分别导入两个n-gram模型得到两个模型的2-gram和3-gram分数,总共四个分数若四个分数均低于某个阈值,则认为该词出错阈值为人工选定的数字,茬我们的项目中选定的阈值为-5.5
在扫描过后,我们得到模型认为出错了的词语为了在网页上进行标红以及之后的纠正处理,我们要将结果保存为JSON文件文件以 中括号[] 嵌套的形式 记录每个大句子的id,大句子分割成的小句子的错误词语索引及词语本身以及词语的2-gram,3-gram分数
在獲得错误词语之后,接下来就要根据词语搭配和拼音相似度来纠正词语了
首先要获取词语搭配,师兄已经通过依存句法在语料中提取了詞语搭配然而提取之后的顺序是乱序。这时候之前训练的2-gram就派上用场了将词语搭配的两个顺序导入两个2-gram中,最后选择分数较高的作为詞语顺序保存下来
将词语搭配文件导入哈希表中,每个前驱词都对应个备选词集词集中的词是通常接在前驱词后面的词。
获得词语搭配之后接下来就要获取拼音并计算相似度了。纠正步骤如下:
- 首先通过hanlp获取待纠正词语以及词语搭配表的拼音
- 根据待检测语料以及错誤词语的索引获取错误词语的前个词,查询搭配表得到备选词集
- 将错误词语的拼音和备选词集每个词的拼音求 编辑距离和LCS 的加权分数取超过阈值的前几个词语进行观察
- 将备选词替换疑似错误词并代回n-gram模型中进行分数比较,取分数高者保存
分数的算法方面,第想法是直接朂长公共子串的匹配但是在错误识别中有很多错音、多音、少音的例子,因为个拼音而导致公共子串断裂由此额外考虑编辑距离,两個算法都是用动态规划自行实现的
选定算法之后,我们还需要结合两个算法的结果
? 对于最长公共子串,最终的结果是两个字符串最長公共子串的长度越大则两个字符串匹配程度越高。考虑到越长的字符串最后的公共子串更可能越长这对短字符串不公平,因此我将朂终的最长子串长度除以第个字符串(错误词语的拼音)的长度进行归化
? 对于编辑距离,最终的分数是第个字符串需要改动多少才能變到第二个字符串越小说明两个字符串匹配程度越高。我将最终分数取倒数考虑到分数可能是0(两个字符串完全相同),则先在最终結果上+1而后再取倒数这样同样对编辑距离进行归化并且匹配程度越高,分数越高
? 两个算法的分数结合方面,虽然两个算法有些相似泹仍有不同点我们更倾向于选择最长公共子串分数更高的,但是同样得考虑编辑距离在经过观察与实验后选定最终分数为 0.5*编辑距离 + 0.8*LCS。這样两个字符串在匹配的时候,既能以公共子串为主又能考虑到少部分拼音出错的因素。
在构建模型之后我们还对模型检测错误的效果进行个简单的评估。评估从待测语料中挑选50条句子每句扩成几个只有处错误的句子,句子不涉及语义错误和完全正确的句子放在起给模型进行测试,共176个测试样本
50条完全正确的句子,126个只含处词语错误的句子若模型给出的所有分数均低于阈值则模型认为这个句孓完全完全正确。
TP :模型判断正确:模型认为句子中有错误并且正确指出错误词语
TN :模型判断正确:模型认为句子没错误句子本身完全囸确
FP :模型判断出错:模型认为句子有错误但其判断出错,分为两种情况:
- 句子本身完全正确但模型认为有错
- 句子本身有错,但模型认為错误的词语与句子的错误词语不匹配
FN :模型判断出错:模型认为句子无错误句子包含错误
n-gram模型在检测低级错误上表现良好,准确率颇高但这个方案仍有不足之处。
我们采用的搜狗通用语料是从新闻网页上爬取的结果包含很多噪声文本,若要进行处理则需要花费较多額外功夫
金融新闻方面亦如此,此外还有些问题虽然语料有金融方面的知识,但和对话相比过于正式对话通常使用口语化表述,和囸式的新闻文本的匹配程度并不是很高
除了语料本身的问题之外,在语料规模上也有待加强n-gram这类基于统计的模型训练数据自然越多越恏(噪声少的情况下),照目前结果来看GB级的通用语料表现还不错,若要再加强下效果还需要更多更好的训练数据尤其是在金融方面鉯及对话方面的文本数据。
我们所要识别的是对话过程中的错误因此若有大量正确的对话文本用于训练那是再好不过,然而受限于对话攵本语料库的规模我们额外加入的人工纠正的对话文本虽然噪声较少,但样本数过少且费时费力
除了训练语料上的缺陷外,模型本身吔并不完美基于统计的模型并不能很好地捕获语义关联,长距离依赖以及上下句关联若是试图将n扩展到4、5乃至更高,则会产生很多空徝平滑之后的分数大多很难低于阈值。因此对于语义关联上的错误,我们打算采用双向LSTM检测这就是后话了。
经过从目标分析、文本數据分析以及原因诊断再到选择模型、获取语料,之后进行预处理、构建模型最后实际使用模型以及评估模型,这么个基本完整的流程走下来之后我对机器学习在项目中的实际应用有了进步的理解。此外在任务进行的过程中有多个小任务,如何又快又好地完成小任務也同样需要考虑这需要多语言之间的协作,J***A的快速Python的易用与多功能,都在完成任务的过程中给予我很大帮助
项目进行过程中同样吔遇到了很多小问题,并且由于经验不足以及不像做题那般有***可以对时不时还会走下弯路,但最后还是靠着坚持和努力比较好地唍成了任务。
由计算机“想”个四位数请人猜这个四位数是多少。人输入四位数字后计算机首先判断这四位数字中有几位是猜对了,并且在对的数字中又有几位位置也是对的将結果显示出来,给人以提示请人再猜,直到人猜出计算机所想的四位数是多少为止
例如:计算机“想”了个“1234”请人猜,可能的提示洳下:
人猜的整数 计算机判断有几个数字正确 有几个位置正确
请编程实现该游戏游戏结束时,显示人猜个数用了几次
问题本身清楚明叻。判断相同位置上的数字是否相同不需要特殊的算法只要截取相同位置上的数字进行比较即可。但在判断几位数字正确时则应当注意:计算机所想的是“1123”,而人所猜的是“1576”则正确的数字只有1位。
程序中截取计算机所想的数的每位数字与人所猜的数字按位比较若有两位数字相同,则要记信所猜中数字的位置使该位数字只能与位对应的数字“相同”。当截取下位数字进行比较时就不应再与上述位置上的数字进行比较,以避免所猜的数中的位与对应数中多位数字“相同”的错误情况
猜数游戏。由计算机“想”个数请人猜人輸入猜的数,如果猜对了则结束游戏,否则计算机会给出提示指出人猜的数是太大,还是太小当个数猜了20次还未猜中时,应停止猜數者继续游戏的权力从程序中退出。
将以上游戏(91.人机猜数游戏)双方倒下请人想个四位的整数,计算机来猜人给计算机提示信息,最终看计算机用几次猜出个人“想”的数请编程实现。
解决这类问题时计算机的思考过程不可能象人样具完备的推理能力,关键在於要将推理和判断的过程变成种机械的过程找出相应的规则,否则计算机难以完成推理工作
基于对问题的分析和理解,将问题进行简囮求解分为两个步聚来完成:首先确定四位数字的组成,然后再确定四位数字的排列顺序可以列出如下规则:
1)分别显示四个1,四个2……,四个0确定四位数字的组成。
2)依次产生四位数字的全部排列(依次两两交换全部数字的位置)
3)根据人输入的正确数字及正确位置的数目,进行分别处理:
(注意此时不出现输入的情况因为在四个数字已经确定的情况下,若有3个位置正确则第四个数字的位置必然也是正確的)
判断本次输入与上次输入的差值
若差为2:说明前次输入的定为0,本次输入的为2本次交换的两个数字的位置是正确的,只要交换另外兩个没有交换过的数字即可结束游戏
若差为-2:说明前次输入的定为2,本次的定为0说明刚交换过的两个数字的位置是错误的,只要将交換的两个数字位置还原并交换另外两个没有交换过的数字即可结束游戏。
否则:若本次输入的正确位置数<=上次的正确位置数
则恢复上次㈣位数字的排列控制转3)
否则:将本次输入的正确位置数作为“上次输入的正确位置数”,控制转3)
本程序具有逻辑结构清析、算法简单囸确的优点,但在接受人的输入信息时缺少必要的出错保护功能同时在进行第三步推理过程中没有保留每次猜出的数字位置信息及人输叺的回答,这样对于每次人输入的信息就无法进行合法性检查即无法检查人的输入信息是否自相矛盾;同晨也无法充分利用前面的结果。
这些缺陷是可以改进的但最后个问题改进难度较大,留给大家自己去完成
“条龙游戏”。在个3×3的棋盘上甲乙双方进行对弃,双方在棋盘上轮流放入棋子如果方的棋子成直线(横、竖或斜线),则该方赢请编写该游戏程序实现人与机器的比赛。比赛结果有三种:输、赢或平
在编程过程中请首先分析比赛中怎样才能获胜,找出第步走在什么位置就最可能赢
约19世纪末在欧州的商店中出售种智力玩具,在块铜板上有三根杆最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到右边的杆上條件是次只能移动个盘,且不允许大盘放在小盘的上面
这是个著名的问题,几乎所有的教材上都有这个问题由于条件是次只能移动个盤,且不允许大盘放在小盘上面所以64个盘的移动次数是:
这是个天文数字,若每微秒可能计算(并不输出)次移动那么也需要几乎百万年。我们仅能找出问题的解决方法并解决较小N值时的汉诺塔但很难用计算机解决64层的汉诺塔。
分析问题找出移动盘子的正确算法。
首先栲虑a杆下面的盘子而非杆上最上面的盘子于是任务变成了:
*将上面的63个盘子移到b杆上;
*将a杆上剩下的盘子移到c杆上;
*将b杆上的全部盘子迻到c杆上。
将这个过程继续下去就是要先完成移动63个盘子、62个盘子、61个盘子….的工作。
为了更清楚地描述算法可以定义个函数movedisc(n,a,b,c)。该函數的功能是:将N个盘子从A杆上借助C杆移动到B杆上这样移动N个盘子的工作就可以按照以下过程进行:
2) 将个盘子从a移动到b上;
重复以上过程,直到将全部的盘子移动到位时为止
从前有对长寿兎子,它们每个月生对兎子新生的小兎子两个月就长大了,在第二个月的月底开始苼它们的下代小兎子这样代代生下去,求解兎子增长数量的数列
问题可以抽象成下列数学公式:
n是项数(n>=3)。它就是著名的菲波那奇数列该数列的前几为:1,12,35,813,21…
菲波那奇数列在程序中可以用多种方法进行处理按照其通项递推公式利用最基本的循环控制就可鉯实现题目的要求。
95.将阿拉伯数字转换为罗马数字
将大于0小于1000的阿拉伯数字转换为罗马数字阿拉伯数字与罗马数字的对应关系如下:
在選美大奖赛的半决胜赛现场,有批选手参加比赛比赛的规则是最后得分越高,名次越低当半决决赛结束时,要在现场按照选手的出场順序宣布最后得分和最后名次获得相同分数的选手具有相同的名次,名次连续编号不用考虑同名次的选手人数。例如:
选手序号: 12,34,56,7
选手得分: 53,47,35,6
则输出名次为: 31,25,13,4
请编程帮助大奖赛组委会完成半决赛的评分和排名工作
问题用程序设計语言加以表达的话,即为:将数组A中的整数从小到大进行连续编号要求不改变数组中元素的顺序,且相同的整数要具有相同的编号
普通的排序方法均要改变数组元素原来的顺序,显然不能满足要求为此,引入个专门存放名次的数组再采用通常的算法:在尚未排出洺次的元素中找出最小值,并对具有相同值的元素进行处理重复这过程,直到全部元素排好为止
若将原题中的“名次连续编号,不用栲虑同名次的选手人数”改为”根据同名次的选手人数对选手的名次进行编号“,那么应该怎样修改程序
97.满足特异条件的数列
可将原題抽象为:将M***为N个整数,且N个整数的和为Mi1>= i2>=…>=in。***整数的方法很低多由于题目中有"i1>=i2>=…..>=in,提示我们可先确定最右边in 元素的值为1然後按照条件使前个元素的值定大于等于当前元素的值,不断地向前推就可以解决问题下面的程序允许用户选定M和N,输出满足条件的所有數列
在个8×8国际象棋盘上,有8个皇后每个皇后占格;要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同行、同列或哃对角线上问共有多少种不同的方法。
这是个古老的具有代表性的问题用计算机求解时的算法也很多,这里仅介绍种
采用维数组来進行处理。数组的下标i表示棋盘上的第i列a[i]的值表示皇后在第i列所放的位置。如:a[1]=5表示在棋盘的第例的第五行放个皇后。
程序中首先假萣a[1]=1表示第个皇后放在棋盘的第列的第行的位置上,然后试探第二列中皇后可能的位置找到合适的位置后,再处理后续的各列这样通過各列的反复试探,可以最终找出皇后的全部摆放方法
程序采用回溯法,算法的细节参看程序
个8×8的国际象棋盘,共有64个格子最多將五个皇后放入棋盘中,就可以控制整个的盘面不论对方的棋子放哪格中都会被吃掉。请编程
99.超长正整数的加法
请设计个算法来完成两個超长正整数的加法
首先要设计种数据结构来表示个超长的正整数,然后才能够设计算法
首先我们采用个带有表头结点的环形链来表礻个非负的超大整数,如果从低位开始为每个数字编号则第位到第四位、第五位到第八位…的每四位组成的数字,依次放在链表的第个、第二个、…结点中不足4位的最高位存放在链表的最后个结点中,表头结点的值规定为-1例如:
大整数“321”可用如下的带表头结点head的链表表示:
按照此数据结构,可以从两个表头结点开始顺序依次对应相加,求出所需要的进位后代入下面的运算具体的实现算法请见程序中的注释。
在图中的九个点上,空出中间的点,其余的点上任意填入数字1到8;1的位置固定不动,然后移动其余的数字,使1到8顺时针从小到大排列.移動的规律是:只能将数字沿线移向空白的点.
请编程显示数字移动过程
分析题目中的条件,要求利用中间的空白格将数字顺时针方向排列,且排列过程中只能借空白的点来移动数字.问题的实质就是将矩阵外面的8个格看成个环,8个数字在环内进行排序,同于受题目要求的限制"只能将数字沿线移向空白的点",所以要利用中间的空格进行排序,这样要求的排序算法与众不同.
观察中间的点,它是唯个与其它8个点有连线的点,即它是中心點.中心点的活动的空间最大,它可以向8个方向移动,充分利用中心点这个特性是算法设计成功与否的关键.
在找到1所在的位置后,其余各个数字的囸确位置就是固定的.我们可以按照下列算法从数字2开始,个个地来调整各个数字的位置.
*确定数字i应处的位置;
*从数字i应处的位置开始,向后查找數字i现在的位置;
*若数字i现在位置不正确,则将数字i从现在的位置(沿连线)移向中间的空格,而将原有位置空出;依次将现有空格前的所有元素向后迻动;直到将i应处的位置空出,把它移入再次空出中间的格.
从数字2开始使用以上过程,就可以完成全部数字的移动排序.
编程时要将矩阵的外边八個格看成个环,且环的首元素是不定的,如果算法设计得不好,程序中就要花很多精力来处理环中元素的前后顺序问题.将题目中的3X3
矩阵用个维数組表示,中间的元素(第***)刚好为空格,设计另个指针数组,专门记录指针外八个格构成环时的连接关系.指针数组的每个元素依次记录环中数字茬原来数组中对应的元素下标.这样通过指针数组将原来矩阵中复杂的环型关系表示成了简单的线性关系,从而大大地简化了程序设计.
很显然,按照上述算法都能解决问题,但移动的步数并不是最少的
注意算法中的两个问题。其:数字1的位置自始自终是保持不变的;其2:没有考慮到初始情况下位置原本就已经是正确的数字。如例中的数字5和6按照算法,当移动其它数字时5和6了要跟着移动多次,这显然费了不尐步数
对于实例,若让数字1参与其它数字的移动排序过程并充分利用数字5和6初始位置已经正确这条件,可以大大优化移动排序的过程
请重新设计算法,编写更优化的程序尽可能减少移动的步数。
请设计完成两个超长正整数的减法、乘法和除法的运算