自然语言处理 学习笔记(一)

版权声明:文章禁止转载 https://blog.csdn.net/weixin_43477010/article/details/84576591

个人学习nlp笔记:学习材料CS124、COSC572和《Speech and Language Processing》第三版

在这里插入图片描述

1.正则表达式和文本标准化

Regular Expressions, Text Normalization & Edit Distanc

1.1正则表达式

在线正则测试: 连接
一段英文文本: 连接
相关文字推荐推荐: 连接
在这里插入图片描述
[]表示字符集合,仅代表一个字符,在regexpal上使用[Oon]和[Oo]n就明白了
在这里插入图片描述
^表示否定,[^Oo ]匹配满足条件“开头不为O、o或空格,第二字符为n”的字符**,**[^e^]匹配满足条件既不是e也不是^的字符,[^a-x .,-\]’”“],匹配y和z,用\来表示转义字符,[^A-Za-z][Tt]he[^A-Za-z]表示定位the同时剔除other, there…

在这里插入图片描述

在这里插入图片描述
^和$用于表示开头和结尾
在这里插入图片描述

Error type 匹配"the" 错误原因 解决办法
False positive the 没有匹配大写的The 提高准确率(accuracy & precision)
False negative [tT]he 把其他词也给一起匹配了(there) 提高召回率(recall)

正确方法: [^a-zA-Z][tT]he[^a-zA-Z]!

精确率是针对我们预测结果而言的,它表示的是预测为正的样本中有多少是真正的正样本。召回率是针对我们原来的样本而言的,它表示的是样本中的正例有多少被预测正确了。
召回率和准确率的理解

1.2文本标准化(text normalization)

文本标准化本质就是把所有文本转化为一个更方便处理和标准化的形式,比如把U.S.A和USA匹配。
每一个NLP的工作都需要做文本标准化:

  1. 对文本进行分词(Segmenting\tokenizing words)
  2. Normalizing word formats
  3. Segmenting sentences in running text

1.2.1词语切分(word tokenization)

tokenization指词语的词语切分,比如在中文中没有空格符一句话应该这样切分,已得到词语数据:

我/今天/很/开心

在英文或其他语言中也存在着这样的问题,比如单词San Francisco应当理解成一个词而不是因为空格而被切分,不过一般都用Segmentation来表示中文的分词。

在这里插入图片描述
一些英文分词会遇到的问题,大都和符号以及缩写有关

在这里插入图片描述
在cs124中介绍了一种非常简单的中文分词方法,大致就是一次找到词典中最长的词,但是对英文显然效果不好。

1.2.2大写转换(Case folding)

字母在句子开头为大写,如The和the,因此需要进行标准化处理, 比如把所有字符都转为小写,统一大小写(当然有的词大小写的化意思不一样如china和China)。

1.2.3词型还原(lemmatization)

把形式发生变化的单词转换为最原始的形态,比如are,is都为be,这样对查找表达中心意思词的情况下会很有帮助。(在一些机器翻译的场景下)

I [wanted] to learn -> I want to learn
want 就能轻松被定位和翻译为想要…

Stemming看一看做简化版的lemmatization,只对单词的后缀进行处理。
语素(Morphemes)表达的意义有两种:词汇意义(Stems)和语法意义(Affixes),将其还原成根形式(stems)。

respectful
respect [真正代表词汇意义的部分]
-ful [代表形容词词型的后缀]

automate(s),automatic, automation 全部还原到 automate

在这里插入图片描述
一些常见的后缀等等,但基于规则也会出错,比如sing和string虽然有ing,但就是一个词,但这类词前面都是没有元音(vowel)的,我们又可以基于这个规则剔除sing…这类词(但不是完美的,比如something和nothing怎么办?)

[aeiou].*ing$ 正则

虽然再test data上出现没见过的单词,比如lower,而训练集上已经有low,lowest,我们可以用Stemming 或者 lemmatizing方法将其归类到low上,但有时候我们又想保留这些信息,比如在做词性标记时候,low和lower还是需要加以区分的。除了Porter’s algorithm外,在《Speech and Language Processing》还介绍了Byte-Pair Encoding方法,一种用迭代的方式把一起出现频率高的单词字母融合。这样在我们对test data进行分词时候,我们就直接用这些处理后的词典(merges)来进行分词。

原理非常直观,就是将单词(加上一个结尾符)分为各个字符,然后以频率为权重依次合并。
在这里插入图片描述
BTW,还学到了python中collections.defaultdict()的用法,更方便地初始化一个词典 相关连接
不知道是不是代码是py2的原因,一直跑不对,所以稍做了下修改

"""Use byte pair encoding (BPE) to learn a variable-length encoding of the vocabulary in a text.
Unlike the original BPE, it does not compress the plain text, but can be used to reduce the vocabulary
of a text to a configurable number of symbols, with only a small increase in the number of tokens.
This is an (inefficient) toy implementation that shows the algorithm. For processing large datasets,
indexing and incremental updates can be used to speed up the implementation (see learn_bpe.py).
Reference:
Rico Sennrich, Barry Haddow and Alexandra Birch (2016). Neural Machine Translation of Rare Words with Subword Units.
Proceedings of the 54th Annual Meeting of the Association for Computational Linguistics (ACL 2016). Berlin, Germany.
"""

import re
import sys
import collections

def get_stats(vocab):
    pairs = collections.defaultdict(int) # 初始化dict
    for word, freq in vocab.items():
        symbols = word.split()
        for i in range(len(symbols)-1):
        # for i in range(len(word) - 1):
            pairs[symbols[i],symbols[i+1]] += freq
    return pairs

def merge_vocab(pair, v_in):
    # best, vocab
    v_out = {}
    bigram_pattern = re.escape(' '.join(pair))
    p = re.compile(r'(?<!\S)' + bigram_pattern + r'(?!\S)')
    for word in v_in:
        w_out = p.sub(''.join(pair), word)  # 查找字符
        v_out[w_out] = v_in[word]  # 新的字典
    return v_out

vocab = {'l o w .' : 5, 'l o w e s t .' : 2, 'n e w e r .' :6,
         'n e w .' : 2, 'w i d e r .' : 3}
num_merges = 5
for i in range(num_merges):
    pairs = get_stats(vocab)
    try:
        best = max(pairs, key=pairs.get)
    except ValueError:
        break
    if pairs[best] < 2:
        sys.stderr.write('no pair has frequency > 1. Stopping\n')
        break
    vocab = merge_vocab(best, vocab)
    print("第%d次merge:\n" % (i+1), vocab)

输出结果
第1次merge:
{‘l o w .’: 5, ‘l o w e s t .’: 2, ‘n e w er .’: 6, ‘n e w .’: 2, ‘w i d er .’: 3}
第2次merge:
{‘l o w .’: 5, ‘l o w e s t .’: 2, ‘n e w er.’: 6, ‘n e w .’: 2, ‘w i d er.’: 3}
第3次merge:
{‘l o w .’: 5, ‘l o w e s t .’: 2, ‘ne w er.’: 6, ‘ne w .’: 2, ‘w i d er.’: 3}
第4次merge:
{‘l o w .’: 5, ‘l o w e s t .’: 2, ‘new er.’: 6, ‘new .’: 2, ‘w i d er.’: 3}
第5次merge:
{‘lo w .’: 5, ‘lo w e s t .’: 2, ‘new er.’: 6, ‘new .’: 2, ‘w i d er.’: 3}

1.2.4 断句(Sentence Segmentation)

虽然英文句子都以’.‘结尾,但是有时如Mr.或Inc.同样也会出现’.’,所以断句也是文本规范化的一个重要的步骤。以Inc.为例,缩写和句子结尾的标识符’.’(sentence boundary marker)同时出现了,显然以此断句显得很不清楚,因此我们需要用断句和分词的方法一起解决这个问题。

通常来说,断句可以用一个binary分类器(基于规则或机器学习)解决是否这个’.'代表一个词的一部分或者是一个句子的结尾,因此使用一个常见的缩写单词词典会很有帮助。当然最新的断句方法都是依靠机器学习来完成这个任务的。

2.编辑距离(Edit Distance)

在自然语言处理中,知道如何计算两个字符串的相似度也是很多人关心的,比如在拼写纠错的应用里就有用到。编辑距离是一种用于衡量两个字符串相似度的距离,
一般字符有三种不同:
1.额外字符被插入(insertion)
2.字符被删除(deletion)
3.字符发生替换(substitution)

我们把两个字符看出队列(alignment):
在这里插入图片描述
substution cost 2 指得是LCS distance,只能插入和删除,因此替换需要2个cost,《speech and language processing》中说这是另一个版本的Levenshtein distance,问题不大。

我们同样可以把编辑距离(edit distance)运用到nlp的其他方面,比如评估一个机器翻译和语音识别模型,以及用于改进命名实体(named entity)相关的工作:
在这里插入图片描述

2.1最小编辑距离问题

在这里插入图片描述
该怎么做呢,可以列出所有可能的子路径,再用Viterbi算法找(可能性太多了,太麻烦了,尽管我们可以去掉其中很多重复的选项)。

2.1.1 构建距离矩阵

我们可以构建一个距离矩阵来解决这个问题 ,Wagner and Fischer (1974)
在这里插入图片描述
n和m实质为两个字符x和y的长度,构建一个n×m的矩阵D。
在这里插入图片描述
在这里插入图片描述
初始化一个占位符#横纵坐标为0(用于初始化赋值)。

# Author: VinceLim

import numpy as np

x = 'intention'
y = 'execution'


def minEditDist(x, y):
    # 输入x, y为两个字符串
    n = len(x)
    m = len(y)
    DistMatrix = np.zeros([n+1, m+1])
    # Initialize
    DistMatrix[1:n+1, 0] = np.linspace(1, n, n)
    DistMatrix[0, 1:m+1] = np.linspace(1, m, m)
    # Recurrence Relation -> 获得距离矩阵DistMatrix
    for i in range(1, n+1):
        for j in range(1, m+1):
            # 大小取决于判别方程的那个矩阵
            d = DistMatrix[i - 1, j - 1]
            if x[i-1] != y[j-1]:     # 矩阵多了一位字符,但是字符串没有
                d += 2
            DistMatrix[i, j] = min(DistMatrix[i-1, j] + 1, DistMatrix[i, j-1] + 1, d)
    
    return(DistMatrix)


if __name__ == "__main__":
    DistMatrix = minEditDist(x, y)

在这里插入图片描述

2.1.2 回溯(Backtrace )

简单的距离矩阵不能满足所有的需求,比如我们有时候想知道在字符串x中哪个字符与字符串y中的字符有关,在这种情况下,我们需要引入回溯(Backtrace)来记录。

We often need to align(指针) each character of the two strings to each other.

在这里插入图片描述
用了书上的图片,也就是python输出的版本,因此backtrace的方向应该改成从右下到左上

在这里插入图片描述

在这里插入图片描述

大致意思是计算距离矩阵时利用指针记录最小错误(不同)的类型(方向),当到达左下角时候,顺着指针回溯整个队列。ppt中图片和书上(python实际效果)是反的,需要修改下同时,左下角的8为两个字符串的最小的编辑距离。

由此可得到:
在这里插入图片描述

2.1.4 时间复杂度

在这里插入图片描述

2.2 加权距离矩阵

键入错误的情况下,出错的字符概率时不相同的,比如很明显a和e之间的替换就比a和b多,而在dna排序中也一样,碱基的替换插入和删除也存在着不同的概率,所以加权我们能得到更好的结果。
在这里插入图片描述
The Needleman-Wunsch算法,del[x(i)]为删除字符x(i)的cost:
在这里插入图片描述

3. 总结

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43477010/article/details/84576591