当前位置: 首页 > article >正文

Chapter2 文本规范化

在NLP任务中,第一步往往要做文本规范化,其作用是将字符串表示成更易于计算机处理的规范形式。一般包括三个步骤:1.分词 2.词规范化 3.分句

2.1 分词

分词:将一段字符序列文本转化成词元(token)序列的过程。词元不一定等于词,可以是字符、子词和词等。

2.1.1 基于空格与标点符号的分词

import re

sentence = 'I learn natural language processing with dongshouxueNLP, too.'
tokens = sentence.split(' ')
print(f"输入语句:{sentence}")
print(f"分词结果:{tokens}")
# 分词结果:['I', 'learn', 'natural', 'language', 'processing', 'with', 'dongshouxueNLP,', 'too.']

# 去除句子中的 . 和 ,
sentence = re.sub(r'[,\.]', '', string=sentence)
print(sentence) # I learn natural language processing with dongshouxueNLP too
tokens = sentence.split(' ')
print(f"分词结果:{tokens}")
# 分词结果:['I', 'learn', 'natural', 'language', 'processing', 'with', 'dongshouxueNLP', 'too']

2.1.2 基于正则表达式的分词

import re
import nltk
from nltk.tokenize import word_tokenize
from nltk.tokenize import regexp_tokenize

sentence = "Did you spend $3.4 on arxiv.org for your pre-print?" +\
           " No, it's free! It's..."
pattern = r"\w+"
print(re.findall(pattern, sentence))
# ['Did', 'you', 'spend', '3', '4', 'on', 'arxiv', 'org', 'for', 'your',
# 'pre', 'print', 'No', 'it', 's', 'free', 'It', 's']

pattern = r"\w+|\S\w*"
print(re.findall(pattern, sentence))
# ['Did', 'you', 'spend', '$3', '.4', 'on', 'arxiv', '.org', 'for', 'your', 'pre', '-print', '?', 'No', ',', 'it',
# "'s", 'free', '!', 'It', "'s", '.', '.', '.']

print("1.匹配可能含有连续字符的词")
pattern = r"\w+(?:[-']\w+)*"
print(re.findall(pattern, sentence))
# ['Did', 'you', 'spend', '3', '4', 'on', 'arxiv', 'org', 'for', 'your', 'pre-print', 'No', "it's", 'free', "It's"]

# 和前面的pattern组合
pattern = r"\w+(?:[-']\w+)*|\S\w*"
print(re.findall(pattern, sentence))
# ['Did', 'you', 'spend', '$3', '.4', 'on', 'arxiv', '.org', 'for', 'your', 'pre-print', '?', 'No', ',',
# "it's", 'free', '!', "It's", '.', '.', '.']

print("2.匹配简写和网址")
new_pattern = r"(?:\w+\.)+\w+(?:\.)*"
pattern = new_pattern + r"|" + pattern
print(re.findall(pattern, sentence))
# ['Did', 'you', 'spend', '$3', '.4', 'on', 'arxiv.org', 'for', 'your', 'pre-print', '?', 'No', ',', "it's", 'free',
# '!', "It's", '.', '.', '.']

print("3.货币和百分比")
new_pattern2 = r"\$?\d+(?:\.\d+)?%?"
pattern = new_pattern2 + r"|" + pattern
print(re.findall(pattern, sentence))
# ['Did', 'you', 'spend', '$3.4', 'on', 'arxiv.org', 'for', 'your', 'pre-print', '?', 'No', ',', "it's", 'free',
# '!', "It's", '.', '.', '.']

print("4.英文省略号")
new_pattern3 = r"\.\.\."
pattern = new_pattern3 + r"|" + pattern
print(re.findall(pattern, sentence))
# ['Did', 'you', 'spend', '$3.4', 'on', 'arxiv.org', 'for', 'your', 'pre-print', '?', 'No', ',', "it's", 'free', '!',
# "It's", '...']

print('----------------')
print("NLTK是基于python的NLP工具包,也可以实现前面基于正则表达式的分词")
tokens = regexp_tokenize(sentence, pattern)
print(tokens)

Tips:

1、\w表示匹配a-z、A-Z、0-9、_这四种类型的字符,等价于[a-zA-Z0-9_];+ 表示匹配前面的表达式1次或者多次。

2、\s在正则表达式中表示空格字符,\S表示\s的补集。

3、* 表示匹配前面的表达式0次或多次,\S\w*表示先匹配除空格外的1个字符,后面可以包含0个或多个\w字符。

4、- 表示匹配连字符,(?:[-']\w+)*表示匹配0次或多次括号内的模式。(?: ...)表示匹配括号内的模式,可以和 +/*等字符连用,其中,?: 表示不保存匹配到的括号中的内容。

5、. 表示匹配任意字符,因此需要 \. 才能表示 . 本身,其他的像+ ? “” $ 等符号也需要加 \ 。

参考:正则表达式-常见正则表达式符号和特殊字符_正则匹配特殊符号-CSDN博客

2.1.4 基于子词的分词

当前常见三种基于子词的分词方法:

1、字节对编码(BPE)

2、一元语言建模分词

3、词片(WordPiece)

(1)一个词元学习器在大量训练语料上学习,并构建出一个词表(表示词元的集合)。

(2)给定一个新的句子,词元分词器会根据总结出的词表进行分词。

print("step1 根据语料构建初始的词表")
str1 = 'nan ' * 5
str2 = 'nanjing ' * 2
str3 = 'beijing ' * 6
str4 = 'dongbei ' * 3
str5 = 'bei bei'
corpus = str1 + str2 + str3 + str4 + str5
tokens = corpus.split(' ')

# 构建基于字符的初始词表
vocabulary = set(corpus)
vocabulary.remove(' ')
vocabulary.add('_')
vocabulary = sorted(list(vocabulary))

# 根据语料构建词频统计表
corpus_dict = dict()
for token in tokens:
    key = token
    token += '_'
    if key not in corpus_dict:
        corpus_dict[key] = {'split': list(token), 'count': 0}
    corpus_dict[key]['count'] += 1

# 打印语料和词表
print("词频统计表:")
for key in corpus_dict.keys():
    print(corpus_dict[key]['count'], corpus_dict[key]['split'])
print("词表:", vocabulary)

print("step2 词元学习器通过迭代的方式逐步组合新的符号并加入词表中")
for step in range(10):
    print(f"第{step+1}次迭代:")
    split_dict = {}
    for key in corpus_dict:
        splits = corpus_dict[key]['split']
        for i in range(len(splits) - 1):
            current_group = splits[i] + splits[i+1]
            if current_group not in split_dict:
                split_dict[current_group] = 0
            split_dict[current_group] += corpus_dict[key]['count']

    group_hist = [(k, v) for k, v in sorted(split_dict.items(),
                  key=lambda x: x[1], reverse=True)]
    print(f"当前最常出现的前5个符号组合:{group_hist[: 5]}")

    merge_key = group_hist[0][0]
    print("本次迭代组合的符号为:", merge_key)
    vocabulary.append(merge_key)

    for key in corpus_dict:
        splits = corpus_dict[key]['split']
        new_splits = []
        i = 0
        while i < len(splits):
            if i+1 >= len(splits):
                new_splits.append(splits[i])
                i += 1
                break
            if splits[i] + splits[i+1] == merge_key:
                new_splits.append(merge_key)
                i += 2
            else:
                new_splits.append(splits[i])
                i += 1
        corpus_dict[key]['split'] = new_splits

    print()
    print("迭代后的词频统计表为:")
    for key in corpus_dict:
        print(corpus_dict[key]['count'], corpus_dict[key]['split'])
    print("迭代后的词表为:", vocabulary)
    print('-------------------------')

print("基于BPE的词元分词器")
ordered_vocabulary = {key: x for x, key in enumerate(vocabulary)}
sentence = 'nanjing beijing'
print("输入语句:", sentence)
tokens = sentence.split(' ')
tokenized_string = []
for token in tokens:
    splits = list(token)
    key = token + '_'
    flag = 1
    while flag:
        flag = 0
        split_dict = {}
        for i in range(len(splits) - 1):
            current_group = splits[i] + splits[i+1]
            if current_group not in ordered_vocabulary:
                continue
            if current_group not in split_dict:
                split_dict[current_group] = ordered_vocabulary[current_group]
                flag = 1
        if not flag:
            continue

        group_hist = [(k, v) for k, v in sorted(split_dict.items(),
                      key=lambda x: x[1])]

        merge_key = group_hist[0][0]
        new_splits = []
        i = 0

        while i < len(splits):
            if i+1 >= len(splits):
                new_splits.append(splits[i])
                i += 1
                break
            if splits[i] + splits[i+1] == merge_key:
                new_splits.append(merge_key)
                i += 2
            else:
                new_splits.append(splits[i])
                i += 1
        splits = new_splits
    tokenized_string += splits
print("分词结果:", tokenized_string)

2.2 词规范化

将词或词元变成标准形式的过程:标准化缩写、大小写转换、动词目态转化、繁体转简体。

import nltk
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from nltk.corpus import wordnet

# 2.2.1 大小写折叠
sentence = "let's study Hands-On-NLP"
print(sentence.lower())

# 2.2.2 词目还原
lemma_dict = {
    'am': 'be', 'is': 'be', 'are': 'be', 'cats': 'cat', "cats'": 'cat', "cat's": 'cat',
    'dogs': 'dog', "dogs'": 'dog', "dog's": 'dog', 'chasing': 'chase'
}

sentence = "Two dogs are chasing three cats"
words = sentence.split(' ')
print("词目还原前:", words)
lemmatized_words = []
for word in words:
    if word in lemma_dict:
        lemmatized_words.append(lemma_dict[word])
    else:
        lemmatized_words.append(word)
print("词目还原后:", lemmatized_words)


# 另外,也可以利用NLTK自带的词典来进行词目还原
nltk.download('punkt', quiet=True)
nltk.download('wordnet', quiet=True)

lemmatizer = WordNetLemmatizer()
words = word_tokenize(sentence)
print("词目还原前:", words)
lemmatized_words = []
for word in words:
    lemmatized_words.append(lemmatizer.lemmatize(word, wordnet.VERB))
print("词目还原后:", lemmatized_words)

2.3 分句

先做分词,使用基于正则表达式或者基于机器学习的分词方法将文本分解成词元,然后基于标点符号判断句子分界

import re
import nltk
from nltk.tokenize import word_tokenize
from nltk.tokenize import regexp_tokenize

pattern = r"\w+(?:[-']\w+)*|\S\w*"
new_pattern = r"(?:\w+\.)+\w+(?:\.)*"
pattern = new_pattern + r"|" + pattern
new_pattern2 = r"\$?\d+(?:\.\d+)?%?"
pattern = new_pattern2 + r"|" + pattern
new_pattern3 = r"\.\.\."
pattern = new_pattern3 + r"|" + pattern

seg_label = [".", "?", '!', '...']
sentence_spliter = set(seg_label)

sentence = "Did you spend $3.4 on arxiv.org for your pre-print?" +\
           " No, it's free! It's..."
tokens = regexp_tokenize(sentence, pattern)
sentences = []
boundary = [0]
for token_id, token in enumerate(tokens):
    if token in sentence_spliter:
        # 如果是句子边界,则把分句结果加入进去
        sentences.append(tokens[boundary[-1]: token_id+1])
        # 将下一句句子起始位置加入boundary
        boundary.append(token_id+1)

if boundary[-1] != len(tokens):
    sentences.append(sentence[boundary[-1]: ])
print("分句结果:")
for seg_sentence in sentences:
    print(seg_sentence)

2.4 小结

文本规范化:分词、词规范化、分句。

1、分词中通过正则表达式的方式,将一些属于词元的标点符号很好的区分出来。

2、词的规范化可以将词元转换成标准形式,从而让计算机更好处理这些词元。

3、分句则可以将长文本切割成多个短文本,从而然计算机更快处理。


http://www.kler.cn/a/466348.html

相关文章:

  • 基于氢氧燃料电池的分布式三相电力系统Simulink建模与仿真
  • 云打印之菜鸟打印组件交互协议
  • 【CSS】第二天 画盒子、文字控制属性
  • SqlSugar-文章目录
  • 读“2024 A16Z AI 应用精选清单”有感——2025AI执行力之年
  • Fabric环境部署-Git和Node安装
  • #C02L02P01. C02.L02.一维数组最值问题.知识点1.求最大值
  • Elasticsearch:利用 AutoOps 检测长时间运行的搜索查询
  • 【2025最新计算机毕业设计】基于SpringBoot+Vue智慧养老医护系统(高质量源码,提供文档,免费部署到本地)【提供源码+答辩PPT+文档+项目部署】
  • unity学习2:关于最近github的2FA(two-factor authentication)新认证
  • 深入理解正则表达式及基本使用教程
  • 图像转换 VM与其他格式互转
  • CLIP论文笔记
  • 2025年度全国会计专业技术资格考试 (甘肃考区)报名公告
  • 从 SQL 到 SPL:分组后每组前面增加符合条件的记录
  • 分布式练手:Server
  • 如何得到深度学习模型的参数量和计算复杂度
  • 【图像处理】OpenCv + Python 实现 Photoshop 中的色彩平衡功能
  • 机器学习经典算法——逻辑回归
  • 在K8S中,Pod请求另一个Pod偶尔出现超时或延迟,如何排查?
  • 【LeetCode】803、打砖块
  • BurpSuite2024.11
  • JLINK V9插入电脑没反应
  • 基于深度学习的视觉检测小项目(二) 环境和框架搭建
  • pytorch张量高级索引介绍
  • Sublime Text4 4189 安装激活【 2025年1月3日 亲测可用】