NLP——文本预处理

本文思维导图

  • 文本预处理及其作用
    文本语料在输送给模型前一般需要一系列的预处理工作, 才能符合模型输入的要求, 如: 将文本转化成模型需要的张量, 规范张量的尺寸等, 而且科学的文本预处理环节还将有效指导模型超参数的选择, 提升模型的评估指标.

一、文本处理的基本方法

1.分词
  • 什么是分词

    分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。我们知道,在英文的行文中,单词之间是以空格作为自然分界符的,而中文只是字、句和段能通过明显的分界符来简单划界,唯独词没有一个形式上的分界符, 分词过程就是找到这样分界符的过程.

  • 分词的作用

    词作为语言语义理解的最小单元, 是人类理解文本语言的基础. 因此也是AI解决NLP领域高阶任务, 如自动问答, 机器翻译, 文本生成的重要基础环节.

  • 流行中文分词工具 jieba

    1.支持多种分词模式:1. 精确模式;2. 全模式;3. 搜索引擎模式

    2.支持中文繁体分词

    3.支持用户自定义词典

python 复制代码
import jieba
content = "无线电法国别研究"

# 1.精确模式分词
# 将返回一个生成器对象 <generator object Tokenizer.cut at 0x7f065c19e318>
jieba.cut(content, cut_all=False)  # cut_all默认为False
# 若需直接返回列表内容, 使用jieba.lcut即可 ['无线电', '法国', '别', '研究']
jieba.lcut(content, cut_all=False)

# 2. 全模式分词  ['无线', '无线电', '法国', '国别', '研究']
jieba.lcut(content, cut_all=True)

#3. 搜索引擎模式分词,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。
# ['无线', '无线电', '法国', '别', '研究']
jieba.lcut_for_search(content)
2. 命名实体识别

命名实体: 通常我们将人名, 地名, 机构名等专有名词统称命名实体. 如: 周杰伦, 黑山县, 孔子学院, 24辊方钢矫直机.

顾名思义, 命名实体识别(Named Entity Recognition,简称NER)就是识别出一段文本中可能存在的命名实体.

命名实体识别的作用:同词汇一样, 命名实体也是人类理解文本的基础单元, 因此也是AI解决NLP领域高阶任务的重要基础环节.

3. 词性标注

词性: 语言中对词的一种分类方法,以语法特征为主要依据、兼顾词汇意义对词进行划分的结果, 常见的词性有14种, 如: 名词, 动词, 形容词等.

顾名思义, 词性标注(Part-Of-Speech tagging, 简称POS)就是标注出一段文本中每个词汇的词性

词性标注的作用:词性标注以分词为基础, 是对文本语言的另一个角度的理解, 因此也常常成为AI解决NLP领域高阶任务的重要基础环节.

python 复制代码
import jieba.posseg as pseg
pseg.lcut("我爱北京天安门") 
# [pair('我', 'r'), pair('爱', 'v'), pair('北京', 'ns'), pair('天安门', 'ns')]
# 结果返回一个装有pair元组的列表, 每个pair元组中分别是词汇及其对应的词性, 具体词性含义请参照[附录: jieba词性对照表]()

二、文本张量表示方法

将一段文本使用张量进行表示,其中一般将词汇为表示成向量,称作词向量,再由各个词向量按顺序组成矩阵形成文本表示.

文本张量表示的作用:将文本表示成张量(矩阵)形式,能够使语言文本可以作为计算机处理程序的输入,进行接下来一系列的解析工作.

1. one-hot编码

又称独热编码,将每个词表示成具有n个元素的向量,这个词向量中只有一个元素是1,其他元素都是0,不同词汇元素为0的位置不同,其中n的大小是整个语料中不同词汇的总数.

ne-hot编码的优劣势:

优势:操作简单,容易理解.

劣势:完全割裂了词与词之间的联系,而且在大语料集下,每个向量的长度过大,占据大量内存.

正因为one-hot编码明显的劣势,这种编码方式被应用的地方越来越少,取而代之的是接下来我们要学习的稠密向量的表示方法word2vec和word embedding.

python 复制代码
import joblib
# 导入keras中的词汇映射器Tokenizer
from keras.preprocessing.text import Tokenizer
# 假定vocab为语料集所有不同词汇集合
vocab = {"周杰伦", "陈奕迅", "王力宏", "李宗盛", "吴亦凡", "鹿晗"}
# 实例化一个词汇映射器对象
t = Tokenizer(num_words=None, char_level=False)
# 使用映射器拟合现有文本数据
t.fit_on_texts(vocab)

for token in vocab:
    zero_list = [0]*len(vocab)
    # 使用映射器转化现有文本数据, 每个词汇对应从1开始的自然数
    # 返回样式如: [[2]], 取出其中的数字需要使用[0][0]
    token_index = t.texts_to_sequences([token])[0][0] - 1
    zero_list[token_index] = 1
    print(token, "的one-hot编码为:", zero_list)

# 使用joblib工具保存映射器, 以便之后使用
tokenizer_path = "./Tokenizer"
joblib.dump(t, tokenizer_path)
python 复制代码
# 加载之前保存的Tokenizer, 实例化一个t对象
t = joblib.load(tokenizer_path)

# 编码token为"李宗盛"
token = "李宗盛"
# 使用t获得token_index
token_index = t.texts_to_sequences([token])[0][0] - 1
# 初始化一个zero_list
zero_list = [0]*len(vocab)
# 令zero_List的对应索引为1
zero_list[token_index] = 1
print(token, "的one-hot编码为:", zero_list) 
2. Word2vec

word2vec是一种流行的将词汇表示成向量的无监督训练方法, 该过程将构建神经网络模型, 将网络参数作为词汇的向量表示, 它包含CBOW和skipgram两种训练模式.

  1. CBOW(Continuous bag of words)模式:
  2. skipgram模式
python 复制代码
import fasttext
# 使用fasttext的train_unsupervised(无监督训练方法)进行词向量的训练
# 它的参数是数据集的持久化文件路径'data/fil9'

# 注意,该行代码执行耗时很长
model1 = fasttext.train_unsupervised('data/fil9') 
model2 = fasttext.train_unsupervised('data/fil9', "cbow", dim=300, epoch=1, lr=0.1, thread=8)

# 可以使用以下代码加载已经训练好的模型
model1.save_model("fil9.bin")
model3 = fasttext.load_model("data/fil9.bin")

model1.get_word_vector("the")
model1.get_nearest_neighbors('music')
3.Word Embedding

通过一定的方式将词汇映射到指定维度(一般是更高维度)的空间.

广义的word embedding包括所有密集词汇向量的表示方法,如之前学习的word2vec, 即可认为是word embedding的一种.

狭义的word embedding是指在神经网络中加入的embedding层, 对整个网络进行训练的同时产生的embedding矩阵(embedding层的参数), 这个embedding矩阵就是训练过程中所有输入词汇的向量表示组成的矩阵.

python 复制代码
import tensorflow as tf
import tensorboard as tb
tf.io.gfile = tb.compat.tensorflow_stub.io.gfile

# 导入torch和tensorboard的摘要写入方法
import torch
import json
import fileinput
from torch.utils.tensorboard import SummaryWriter
# 实例化一个摘要写入对象
writer = SummaryWriter()

# 随机初始化一个100x50的矩阵, 认为它是我们已经得到的词嵌入矩阵
# 代表100个词汇, 每个词汇被表示成50维的向量
embedded = torch.randn(100, 50)

# 导入事先准备好的100个中文词汇文件, 形成meta列表原始词汇
meta = list(map(lambda x: x.strip(), fileinput.FileInput("./vocab100.csv")))
writer.add_embedding(embedded, metadata=meta)
writer.close()
python 复制代码
# 在终端启动tensorboard服务:
$ cd ~
$ tensorboard --logdir=runs --host 0.0.0.0

三、文本语料的数据分析

在深度学习模型评估中, 我们一般使用ACC作为评估指标, 若想将ACC的基线定义在50%左右, 则需要我们的正负样本比例维持在1:1左右, 否则就要进行必要的数据增强或数据删减. 上图中训练和验证集正负样本都稍有不均衡, 可以进行一些数据增强.

1. 标签数量分布
python 复制代码
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
# 设置显示风格
plt.style.use('fivethirtyeight') 

# 分别读取训练tsv和验证tsv
train_data = pd.read_csv("data/cn_data/train.tsv", sep="\t")
valid_data = pd.read_csv("data/cn_data/dev.tsv", sep="\t")

# 获得训练数据标签数量分布
sns.countplot("label", data=train_data)
plt.title("train_data")
plt.show()

# 获取验证数据标签数量分布
sns.countplot("label", data=valid_data)
plt.title("valid_data")
plt.show()
2. 句子长度分布
python 复制代码
# 在训练数据中添加新的句子长度列, 每个元素的值都是对应的句子列的长度
train_data["sentence_length"] = list(map(lambda x: len(x), train_data["sentence"]))

# 绘制句子长度列的数量分布图
sns.countplot("sentence_length", data=train_data)
# 主要关注count长度分布的纵坐标, 不需要绘制横坐标, 横坐标范围通过dist图进行查看
plt.xticks([])
plt.show()

# 绘制dist长度分布图
sns.distplot(train_data["sentence_length"])

# 主要关注dist长度分布横坐标, 不需要绘制纵坐标
plt.yticks([])
plt.show()


# 在验证数据中添加新的句子长度列, 每个元素的值都是对应的句子列的长度
valid_data["sentence_length"] = list(map(lambda x: len(x), valid_data["sentence"]))

# 绘制句子长度列的数量分布图
sns.countplot("sentence_length", data=valid_data)

# 主要关注count长度分布的纵坐标, 不需要绘制横坐标, 横坐标范围通过dist图进行查看
plt.xticks([])
plt.show()

# 绘制dist长度分布图
sns.distplot(valid_data["sentence_length"])

# 主要关注dist长度分布横坐标, 不需要绘制纵坐标
plt.yticks([])
plt.show()
  • 获取正负样本长度散点分布
    通过查看正负样本长度散点图, 可以有效定位异常点的出现位置, 帮助我们更准确进行人工语料审查
python 复制代码
# 绘制训练集长度分布的散点图
sns.stripplot(y='sentence_length',x='label',data=train_data)
plt.show()

# 绘制验证集长度分布的散点图
sns.stripplot(y='sentence_length',x='label',data=valid_data)
plt.show()
3. 词频统计与关键词词云
python 复制代码
# 导入chain方法用于扁平化列表
import jieba
from itertools import chain

# 进行训练集的句子进行分词, 并统计出不同词汇的总数
train_vocab = set(chain(*map(lambda x: jieba.lcut(x), train_data["sentence"])))
print("训练集共包含不同词汇总数为:", len(train_vocab))

# 进行验证集的句子进行分词, 并统计出不同词汇的总数
valid_vocab = set(chain(*map(lambda x: jieba.lcut(x), valid_data["sentence"])))
print("训练集共包含不同词汇总数为:", len(valid_vocab))
# 训练集共包含不同词汇总数为: 12147
# 训练集共包含不同词汇总数为: 6857
python 复制代码
# 使用jieba中的词性标注功能
import jieba.posseg as pseg

def get_a_list(text):
    """用于获取形容词列表"""
    # 使用jieba的词性标注方法切分文本,获得具有词性属性flag和词汇属性word的对象, 
    # 从而判断flag是否为形容词,来返回对应的词汇
    r = []
    for g in pseg.lcut(text):
        if g.flag == "a":
            r.append(g.word)
    return r

# 导入绘制词云的工具包
from wordcloud import WordCloud

def get_word_cloud(keywords_list):
    # 实例化绘制词云的类, 其中参数font_path是字体路径, 为了能够显示中文, 
    # max_words指词云图像最多显示多少个词, background_color为背景颜色 
    wordcloud = WordCloud(font_path="./SimHei.ttf", max_words=100, background_color="white")
    # 将传入的列表转化成词云生成器需要的字符串形式
    keywords_string = " ".join(keywords_list)
    # 生成词云
    wordcloud.generate(keywords_string)

    # 绘制图像并显示
    plt.figure()
    plt.imshow(wordcloud, interpolation="bilinear")
    plt.axis("off")
    plt.show()

# 获得训练集上正样本
p_train_data = train_data[train_data["label"]==1]["sentence"]

# 对正样本的每个句子的形容词
train_p_a_vocab = chain(*map(lambda x: get_a_list(x), p_train_data))
#print(train_p_n_vocab)

# 获得训练集上负样本
n_train_data = train_data[train_data["label"]==0]["sentence"]

# 获取负样本的每个句子的形容词
train_n_a_vocab = chain(*map(lambda x: get_a_list(x), n_train_data))

# 调用绘制词云函数
get_word_cloud(train_p_a_vocab)
get_word_cloud(train_n_a_vocab)

四、文本特征处理

文本特征处理包括为语料添加具有普适性的文本特征, 如:n-gram特征, 以及对加入特征之后的文本语料进行必要的处理, 如: 长度规范. 这些特征处理工作能够有效的将重要的文本特征加入模型训练中, 增强模型评估指标.

1. 添加n-gram特征

给定一段文本序列, 其中n个词或字的相邻共现特征即n-gram特征, 常用的n-gram特征是bi-gram和tri-gram特征, 分别对应n为2和3.

python 复制代码
# 一般n-gram中的n取2或者3, 这里取2为例
ngram_range = 2

def create_ngram_set(input_list):
    """
    description: 从数值列表中提取所有的n-gram特征
    :param input_list: 输入的数值列表, 可以看作是词汇映射后的列表, 
                       里面每个数字的取值范围为[1, 25000]
    :return: n-gram特征组成的集合

    eg:
    >>> create_ngram_set([1, 4, 9, 4, 1, 4])
    {(4, 9), (4, 1), (1, 4), (9, 4)}
    """ 
    return set(zip(*[input_list[i:] for i in range(ngram_range)]))

input_list = [1, 3, 2, 1, 5, 3]
res = create_ngram_set(input_list)
print(res)

# 该输入列表的所有bi-gram特征
# {(3, 2), (1, 3), (2, 1), (1, 5), (5, 3)}
2. 文本长度规范

一般模型的输入需要等尺寸大小的矩阵, 因此在进入模型前需要对每条文本数值映射后的长度进行规范, 此时将根据句子长度分布分析出覆盖绝大多数文本的合理长度, 对超长文本进行截断, 对不足文本进行补齐(一般使用数字0), 这个过程就是文本长度规范.

python 复制代码
from keras.preprocessing import sequence

# cutlen根据数据分析中句子长度分布,覆盖90%左右语料的最短长度.
# 这里假定cutlen为10
cutlen = 10

def padding(x_train):
    """
    description: 对输入文本张量进行长度规范
    :param x_train: 文本的张量表示, 形如: [[1, 32, 32, 61], [2, 54, 21, 7, 19]]
    :return: 进行截断补齐后的文本张量表示 
    """
    # 使用sequence.pad_sequences即可完成
    return sequence.pad_sequences(x_train, cutlen)

# 假定x_train里面有两条文本, 一条长度大于10, 一天小于10
x_train = [[1, 23, 5, 32, 55, 63, 2, 21, 78, 32, 23, 1],
           [2, 32, 1, 23, 1]]

res = padding(x_train)
print(res)

# [[ 5 32 55 63  2 21 78 32 23  1]
#  [ 0  0  0  0  0  2 32  1 23  1]]

五、数据增强方法------回译数据增强法

回译数据增强目前是文本数据增强方面效果较好的增强方法, 一般基于google翻译接口, 将文本数据翻译成另外一种语言(一般选择小语种),之后再翻译回原语言, 即可认为得到与与原语料同标签的新语料, 新语料加入到原数据集中即可认为是对原数据集数据增强.

优势:操作简便, 获得新语料质量高.

问题:在短文本回译过程中, 新语料与原语料可能存在很高的重复率, 并不能有效增大样本的特征空间.

办法:进行连续的多语言翻译, 如: 中文→韩文→日语→英文→中文, 根据经验, 最多只采用3次连续翻译, 更多的翻译次数将产生效率低下, 语义失真等问题.

python 复制代码
# 导入对应的工具包
from google_trans_new import google_translator

# 实例化翻译对象
translator = google_translator()
# 进行第一次翻译, 目标语言是韩语
texts= ['这家价格很便宜', '这家价格很便宜']
tra_list= []
for text in texts:
    ko_res= translator.translate(text, lang_src='zh-cn', lang_tgt='ko')
    tra_list.append(ko_res)
# 打印中间结果
print('中间结果是:')
print(tra_list)

# 进行第二次翻译, 目标语言是汉语
cn_list = []
for text in tra_list:
    cn_res = translator.translate(text, lang_src='ko', lang_tgt='zh-cn')
    cn_list.append(cn_res)
# 打印最后的结果
print("最后的结果是:", cn_list)
相关推荐
少说多想勤做2 小时前
【计算机视觉前沿研究 热点 顶会】ECCV 2024中Mamba有关的论文
人工智能·计算机视觉·目标跟踪·论文笔记·mamba·状态空间模型·eccv
宜向华3 小时前
opencv 实现两个图片的拼接去重功能
人工智能·opencv·计算机视觉
OpenVINO生态社区4 小时前
【了解ADC差分非线性(DNL)错误】
人工智能
醉后才知酒浓4 小时前
图像处理之蒸馏
图像处理·人工智能·深度学习·计算机视觉
炸弹气旋5 小时前
基于CNN卷积神经网络迁移学习的图像识别实现
人工智能·深度学习·神经网络·计算机视觉·cnn·自动驾驶·迁移学习
python_知世5 小时前
时下改变AI的6大NLP语言模型
人工智能·深度学习·自然语言处理·nlp·大语言模型·ai大模型·大模型应用
愤怒的可乐5 小时前
Sentence-BERT实现文本匹配【CoSENT损失】
人工智能·深度学习·bert
冻感糕人~5 小时前
HRGraph: 利用大型语言模型(LLMs)构建基于信息传播的HR数据知识图谱与职位推荐
人工智能·深度学习·自然语言处理·知识图谱·ai大模型·llms·大模型应用
花生糖@5 小时前
Midjourney即将推出的AI生视频产品:CEO洞见分享
人工智能·ai·aigc·midjourney
小言从不摸鱼5 小时前
【NLP自然语言处理】文本处理的基本方法
人工智能·python·自然语言处理