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

一、文本预处理


文本预处理

  • 前言
  • 一、文本处理的基本方法
    • 1.1 分词
      • 1.1.1 举例:
      • 1.1.2 作用
      • 1.1.3 分词工具——jieba
        • 1.1.3.1 jieba特性
        • 1.1.3.2 jieba 的 API 代码演示
  • 二、命名实体识别
    • 2.1 命名实体
    • 2.2 命名实体识别
    • 2.3 举例
    • 2.4 作用
  • 三、词性标注
    • 3.1 词性
    • 3.2 词性标注
    • 3.3 jieba词性对照表
    • 3.4 举例说明
    • 3.5 作用
    • 3.6 jieba进行中文词性标注
  • 四、文本张量表示方法
    • 4.1 one-hot 词向量表示
      • 4.1.1 概念解释
      • 4.1.2 举例
      • 4.1.3 代码演示
      • 4.1.4 one-hot编码的优劣势
    • 4.2 Word2vec
      • 4.2.1 CBOW (Continuous bag of words) 模式
      • 4.2.2 skipgram模式
    • 4.3 Word Embedding
      • 4.3.1 词嵌入层
      • 4.3.2 代码演示
  • 五、文本数据分析
    • 5.1 标签数量分布
    • 5.2 句子长度分布
    • 5.3 获取正负样本长度散点分布
    • 5.4 获取不同词汇总数统计
    • 5.5 词频统计与关键词词云
  • 六、文本特征处理
    • 6.1 n-gram特征
      • 6.1.1 举例
    • 6.2 文本长度规范
  • 七、文本数据增强
    • 7.1 回译数据增强法
      • 7.1.1 概念
      • 7.1.2 回译数据增强优势:
      • 7.1.3 回译数据增强存在的问题
      • 7.1.4 高重复率解决办法:
      • 7.1.5 回译数据增强实现(基于有道翻译接口)
  • 总结


前言


一、文本处理的基本方法

1.1 分词

  • 分词就是将一句话,分成多个词的过程

1.1.1 举例:

  • 有一句话是:我爱北京天安门。可以分词为:
  • [“我”,“爱”,“北京”,“天安门”,“。”]

1.1.2 作用

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

1.1.3 分词工具——jieba

  • 目前 python 最流行的中文分词组件
1.1.3.1 jieba特性
  • 1.支持多种分词模式
    • 精确模式
    • 全模式
    • 搜索引擎模式
  • 2.支持中文繁体分词
  • 3.支持用户自定义的词典
1.1.3.2 jieba 的 API 代码演示
  • 1、精确模式分词:
    • 试图将句子最精确的切开,适合文本分析
import jieba
# 设置输出日志等级的
jieba.setLogLevel('INFO')

sent = '小米公司是一家上市公司,其产品理念是:为发烧而生。我是忠实的米粉。'

# 1 精确模式分词
res = jieba.cut(sent, cut_all=False)  # 只能返回一个生成器对象
print('返回的生成器对象是', res)
# 返回的生成器对象是 <generator object Tokenizer.cut at 0x000002ACFE8D1DD0>

# 通过列表接受分词结果
res = jieba.lcut(sent)
print('返回的列表内容是', res)
# 返回的列表内容是 ['小米', '公司', '是', '一家', '上市公司', ',', '其', '产品', '理念', '是', ':', '为', '发烧', '而生', '。', '我', '是', '忠实', '的', '米粉', '。']
  • 2、全模式分词:
    • 把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能消除歧义
import jieba
# 设置输出日志等级的
jieba.setLogLevel('INFO')

sent = '小米公司是一家上市公司,其产品理念是:为发烧而生。我是忠实的米粉。'

# 2 全模式分词
res = jieba.lcut(sent, cut_all=True)
print('全模式下的分词结果', res)
# 全模式下的分词结果 ['小米', '公司', '是', '一家', '上市', '上市公司', '公司', ',', '其', '产品', '理念', '是', ':', '为', '发烧', '而', '生', '。', '我', '是', '忠实', '的', '米粉', '。']
  • 3、搜索引擎分词:
    • 在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词
import jieba
# 设置输出日志等级的
jieba.setLogLevel('INFO')

sent = '小米公司是一家上市公司,其产品理念是:为发烧而生。我是忠实的米粉。'

# 3 搜索引擎模式分词
res = jieba.lcut_for_search(sent)
print('搜索引擎模式的分词', res)
# 搜索引擎模式的分词 ['小米', '公司', '是', '一家', '上市', '公司', '上市公司', ',', '其', '产品', '理念', '是', ':', '为', '发烧', '而生', '。', '我', '是', '忠实', '的', '米粉', '。']
  • 4、使用用户自定义词典:
    • 添加自定义词典后, jieba能够准确识别词典中出现的词汇,提升整体的识别准确率
    • 词典格式:每一行分三部分:词语、词频(可省略)、词性(可省略),用空格隔开,顺序不可颠倒。
import jieba

jieba.setLogLevel('INFO')

sent = '小米公司是一家上市公司,其产品理念是:为发烧而生。我是忠实的米粉。'

res = jieba.lcut(sent, cut_all=False)
print(f'没加载用户自定义词典之前:{res}')
# 没加载用户自定义词典之前:['小米', '公司', '是', '一家', '上市公司', ',', '其', '产品', '理念', '是', ':', '为', '发烧', '而生', '。', '我', '是', '忠实', '的', '米粉', '。']


# 我的词典userdict.txt中存放了以下三行数据
# 产品理念
# 为发烧而生
# 上市公司


# 加载用户词典
jieba.load_userdict('./userdict.txt')
res1 = jieba.lcut(sent, cut_all=False)
print(f'加载用户自定义词典以后的分词:{res1}')
# 加载用户自定义词典以后的分词:['小米', '公司', '是', '一家', '上市公司', ',', '其', '产品理念', '是', ':', '为发烧而生', '。', '我', '是', '忠实', '的', '米粉', '。']

二、命名实体识别

2.1 命名实体

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

2.2 命名实体识别

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

2.3 举例

  • 鲁迅,浙江绍兴人,五四新文化运动的重要参与者,代表作朝花夕拾。
    • 鲁迅 (人名) / 浙江绍兴 (地名) 人 / 五四新文化运动 (专有名词) / 重要参与者 / 代表作 / 朝花夕拾 (专有名词)

2.4 作用

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

三、词性标注

3.1 词性

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

3.2 词性标注

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

3.3 jieba词性对照表

  • a 形容词
    • ad 副形词
    • ag 形容词性语素
    • an 名形词
  • b 区别词
  • c 连词
  • d 副词
    • df
    • dg 副语素
  • e 叹词
  • f 方位词
  • g 语素
  • h 前接成分
  • i 成语
  • j 简称略称
  • k 后接成分
  • l 习用语
  • m 数词
    • mg
    • mq 数量词
  • n 名词
    • ng 名词性语素
    • nr 人名
    • nrfg
    • nrt
    • ns 地名
    • nt 机构团体名
    • nz 其他专名
  • o 拟声词
  • p 介词
  • q 量词
  • r 代词
    • rg 代词性语素
    • rr 人称代词
    • rz 指示代词
  • s 处所词
  • t 时间词
    • tg 时语素
  • u 助词
    • ud 结构助词 得
    • ug 时态助词
    • uj 结构助词 的
    • ul 时态助词 了
    • uv 结构助词 地
    • uz 时态助词 着
  • v 动词
    • vd 副动词
    • vg 动词性语素
    • vi 不及物动词
    • vn 名动词
    • vq
  • x 非语素词
  • y 语气词
  • z 状态词
    • zg

3.4 举例说明

  • 这里有一句话:我爱自然语言处理

    • 我/rr, 爱/v, 自然语言/n, 处理/vn
  • rr: 人称代词

  • v: 动词

  • n: 名词

  • vn: 动名词

3.5 作用

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

3.6 jieba进行中文词性标注

代码如下(示例):

import jieba
import jieba.posseg as pseg
jieba.setLogLevel('INFO')

sent = '北京欢迎你,我爱北京天安门'

res = pseg.lcut(sent)
print(res)
# [pair('北京', 'ns'), pair('欢迎', 'v'), pair('你', 'r'), pair(',', 'x'), pair('我', 'r'), pair('爱', 'v'), pair('北京', 'ns'), pair('天安门', 'ns')]
# 结果返回一个装有pair元组的列表, 每个pair元组中分别是词汇及其对应的词性

四、文本张量表示方法

  • 将一句话拆分成若干个词,将每个词用一个向量来表示,那么一句话就会变成一个矩阵,这样我们就完成了文本转化成计算机能识别的形式
# 例如:我们将“我爱北京天安门。”切分成以下的词:
# [“我”,“爱”,“北京”,“天安门”,“。”]
# 每个词对应矩阵中的一个向量,如下所示:

[[1.32, 4,32, 0,32, 5.2],
 [3.1, 5.43, 0.34, 3.2],
 [3.21, 5.32, 2, 4.32],
 [2.54, 7.32, 5.12, 9.54],
 [3.8, 4.56, 0.98, 8.46]]

4.1 one-hot 词向量表示

4.1.1 概念解释

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

4.1.2 举例

# ["我", "将", "如何", "存在"] 可以被 one-hot 编码成下面矩阵

[[1, 0, 0, 0],
 [0, 1, 0, 0],
 [0, 0, 1, 0],
 [0, 0, 0, 1]]

4.1.3 代码演示

代码如下(示例):

import jieba
import numpy as np
# 设置打印日志等级
jieba.setLogLevel('INFO')

def onehoe_encoder(sent):
	# 先对进来的句子进行分词操作
    vocabs = list(set(jieba.lcut(sent)))
    print(vocabs)
    # 获取词汇表大小
    vocab_size = len(vocabs)

    # 初始化一个全零的二维数组
    one_hot = np.zeros((vocab_size, vocab_size))

    # 生成one-hot编码
    for i, word in enumerate(vocabs):
    	one_hot[i, word_to_index[word]] = 1
    return one_hot

if __name__ == '__main__':
    sent = '阿辉是个小烧杯'
    print(onehoe_encoder(sent))
# 打印结果如下:
# ['烧杯', '阿辉', '是', '个', '小']
"""
[[1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 1.]] 
"""

4.1.4 one-hot编码的优劣势

  • 优势:操作简单,容易理解.
  • 劣势:完全割裂了词与词之间的联系,而且在大语料集下,每个向量的长度过大,占据大量内存.
    • 正因为one-hot编码明显的劣势,这种编码方式被应用的地方越来越少,取而代之的是接下来我们要学习的稠密向量的表示方法 word2vec 和 word embedding.

4.2 Word2vec

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

4.2.1 CBOW (Continuous bag of words) 模式

  • 给定一段用于训练的文本语料,再选定某段长度 (窗口) 作为研究对象,使用上下文词汇预测目标词汇
  • 假设我们有一句话为:Hope can set you free (愿你自由成长),窗口大小为 3,步骤为下图所示:
    在这里插入图片描述
    • 1 .首先确定窗口大小为3,那么我们先取 Hope can set 这三个单词,并且都通过 one-hot 进行编码
    • 2 .其次因为是 CBOW 模式,所以我们使用 Hope 和 set 作为输入,can 作为输出来构建神经网络模型
    • 3.对于 Hope 来说,它是一个 5x1 的向量,经过与参数矩阵 W(这里是3x5,这里的 3 指最后得到的词向量维度)进行运算得到一个 3x1 的 上下文表示矩阵
    • 4.对于 set 来说,也通过 W 参数矩阵,得到一个 3x1 的上下文矩阵
    • 5.将 Hope 和 set 分别得到的 3x1 的向量进行相加,得到最终的 3*1 的上下文表示矩阵
    • 6.将上一步得到的 1x3 的上下文表示矩阵,再通过一个参数矩阵 W1 得到一个 5x1 的结果矩阵
    • 7.将预测的结果矩阵与我们真正的目标矩阵即 can 的 one-hot 编码矩阵(5x1) 进行损失的计算,然后更新网络参数(所有的变换矩阵共享参数)完成一次模型迭代
    • 8.最后窗口按序向后移动,重新更新参数,直到所有语料被遍历完成,得到最终的变换矩阵W (3x5),这个变换矩阵与每个词汇的one-hot编码(5x1)相乘,得到的(3x1)的矩阵就是该词汇的 word2vec 张量表示

4.2.2 skipgram模式

  • 给定一段用于训练的文本语料,再选定某段长度(窗口)作为研究对象,使用目标词汇预测上下文词汇
  • 假设我们有一句话为:Hope can set you free (愿你自由成长),窗口大小为 3,步骤为下图所示:
    在这里插入图片描述
    • 1 .首先确定窗口大小为 3,那么我们先取 Hope can set 这三个单词,并且都通过 one-hot 进行编码
    • 2 .其次因为是 skipgram 模式,所以我们使用 can 作为输入,Hope和set作为输出来构建神经网络模型
    • 3.对于 can 来说,它是一个 5x1 的向量,经过与参数矩阵 W(这里是3x5,这里的 3 指最后得到的词向量维度)进行运算得到一个 3x1 的 目标词汇表示矩阵
    • 4.将目标词汇表示矩阵与多个变换矩阵(参数矩阵5x3)相乘, 得到多个5x1的结果矩阵
    • 5.结果矩阵将与我们 Hope 和 se t对应的 one-hot 编码矩阵(5x1)进行损失的计算, 然后更新网络参数完成一次模型迭代
    • 6.最后窗口按序向后移动,重新更新参数,直到所有语料被遍历完成,得到最终的变换矩阵W (3x5),这个变换矩阵与每个词汇的one-hot编码(5x1)相乘,得到的(3x1)的矩阵就是该词汇的 word2vec 张量表示

4.3 Word Embedding

  • 通过一定的方式将词汇映射到指定维度 (一般是更高维度) 的空间
    • 广义的word embedding包括所有密集词汇向量的表示方法,如之前学习的word2vec,即可认为是word embedding的一种
    • 狭义的word embedding是指在神经网络中加入的 embedding 层,对整个网络进行训练的同时产生的embedding矩阵(embedding层的参数),这个embedding 矩阵就是训练过程中所有输入词汇的向量表示组成的矩阵

4.3.1 词嵌入层

  • 词嵌入层的作用就是将文本转换为向量
  • 词嵌入层首先会根据输入的词的数量构建一个词向量矩阵,例如: 我们有 100 个词,每个词希望转换成 128 维度的向量,那么构建的矩阵形状即为: 100*128,输入的每个词都对应了一个该矩阵中的一个向量。
    在这里插入图片描述

4.3.2 代码演示

  • 在 PyTorch 中,使用 nn.Embedding 词嵌入层来实现输入词的向量化
from torch import nn
# num_embeddings 表示词的数量
# embedding_dim 表示用多少维的向量来表示每个词
nn.Embedding(num_embeddings=10, embedding_dim=4)

代码如下:

import torch
import torch.nn as nn
import jieba

if __name__ == '__main__':
    # 0.文本数据
    text = '优秀的公司赚取利润,伟大的公司赢得人心。'
    # 1. 文本分词
    words = jieba.lcut(text)
    print('文本分词:', words)
    # 2.分词去重并保留原来的顺序获取所有的词语
    unique_words = list(set(words))
    print("去重后词的个数:\n", len(unique_words))
    # 3. 构建词嵌入层:num_embeddings: 表示词的总数量;embedding_dim: 表示词嵌入的维度
    embed = nn.Embedding(num_embeddings=len(unique_words), embedding_dim=4)
    print("词嵌入的结果:\n", embed)
    # 4. 词语的词向量表示
    for i, word in enumerate(unique_words):
        # 获得词嵌入向量
        word_vec = embed(torch.tensor(i))
        print('%3s\t' % word, word_vec)

结果如下:

文本分词: ['优秀', '的', '公司', '赚取', '利润', ',', '伟大', '的', '公司', '赢得', '人心', '。']
去重后词的个数:
 10
词嵌入的结果:
 Embedding(10, 4)
 人心	 tensor([1.0198, 0.3295, 0.0804, 0.7331], grad_fn=<EmbeddingBackward0>)
 伟大	 tensor([-0.7830,  0.9381, -0.1188,  0.0803], grad_fn=<EmbeddingBackward0>)
 优秀	 tensor([-0.0610,  0.9905, -0.4726, -2.1333], grad_fn=<EmbeddingBackward0>)
  的	 tensor([ 0.0793, -0.2007,  0.0263,  0.3062], grad_fn=<EmbeddingBackward0>)
 公司	 tensor([-1.8080, -0.8241,  0.8195, -2.2967], grad_fn=<EmbeddingBackward0>)
 利润	 tensor([ 2.5544,  1.7704, -1.3012,  0.6469], grad_fn=<EmbeddingBackward0>)
  。	 tensor([-0.5065, -1.5874, -0.6295, -1.0325], grad_fn=<EmbeddingBackward0>)
 赚取	 tensor([-1.2589, -0.8940,  0.6219, -0.4133], grad_fn=<EmbeddingBackward0>)
 赢得	 tensor([ 0.6651,  0.2513, -0.0312, -1.6196], grad_fn=<EmbeddingBackward0>)
  ,	 tensor([-1.0262, -0.4078,  0.6153,  1.4638], grad_fn=<EmbeddingBackward0>)

五、文本数据分析

  • 文本数据分析能够有效帮助我们理解数据语料,快速检查出语料可能存在的问题,并指导之后模型训练过程中一些超参数的选择

5.1 标签数量分布

演示代码如下:

# 导入必备工具包
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt

# 思路分析 : 获取标签数量分布
# 0 什么标签数量分布:求标签0有多少个 标签1有多少个 标签2有多少个
# 1 设置显示风格plt.style.use('fivethirtyeight')
# 2 pd.read_csv(path, sep='\t') 读训练集 验证集数据
# 3 sns.countplot() 统计label标签的0、1分组数量
# 4 画图展示 plt.title() plt.show()
# 注意1:sns.countplot()相当于select * from tab1 group by
def dm_label_sns_countplot():

    # 1 设置显示风格plt.style.use('fivethirtyeight')
    plt.style.use('fivethirtyeight')

    # 2 pd.read_csv 读训练集 验证集数据
    train_data = pd.read_csv(filepath_or_buffer = './cn_data/train.tsv', sep='\t')
    dev_data = pd.read_csv(filepath_or_buffer = './cn_data/dev.tsv', sep='\t')

    # 3 sns.countplot() 统计label标签的0、1分组数量
    sns.countplot(x='label', data = train_data)

    # 4 画图展示 plt.title() plt.show()
    plt.title('train_label')
    plt.show()

    # 验证集上标签的数量分布
    # 3-2 sns.countplot() 统计label标签的0、1分组数量
    sns.countplot(x='label', data = dev_data)

    # 4-2 画图展示 plt.title() plt.show()
    plt.title('dev_label')
    plt.show()
  • 训练集标签数量分布
    在这里插入图片描述
  • 验证集标签数量分布
    在这里插入图片描述
  • 分析
    • 在深度学习模型评估中,我们一般使用ACC作为评估指标,若想将ACC的基线定义在50%左右,则需要我们的正负样本比例维持在1:1左右,否则就要进行必要的数据增强或数据删减。
    • 上图中训练和验证集正负样本都稍有不均衡,可以进行一些数据增强

5.2 句子长度分布

演示代码如下:

# 思路分析 : 获取句子长度分布 -绘制句子长度分布-柱状图 句子长度分布-密度曲线图
# 0 什么是句子长度分布:求长度为50的有多少个 长度51的有多少个 长度为52的有多少个
# 1 设置显示风格plt.style.use('fivethirtyeight')
# 2 pd.read_csv(path, sep='\t') 读训练集 验证集数据
# 3 新增数据长度列:train_data['sentence_length'] = list(map(lambda x:len(x) , ...))
# 4-1 绘制数据长度分布图-柱状图 sns.countplot(x='sentence_length', data=train_data)
#  画图展示 plt.xticks([]) plt.show()
# 4-2  绘制数据长度分布图-曲线图 sns.displot(x='sentence_length', data=train_data)
# 画图展示 plt.yticks([]) plt.show()

def dm_len_sns_countplot_distplot():
    # 1 设置显示风格plt.style.use('fivethirtyeight')
    plt.style.use('fivethirtyeight')

    # 2 pd.read_csv 读训练集 验证集数据
    train_data = pd.read_csv(filepath_or_buffer='./cn_data/train.tsv', sep='\t')
    dev_data = pd.read_csv(filepath_or_buffer='./cn_data/dev.tsv', sep='\t')

    # 3 求数据长度列 然后求数据长度的分布
    train_data['sentence_length'] =  list( map(lambda x: len(x), train_data['sentence']))

    # 4 绘制数据长度分布图-柱状图
    sns.countplot(x='sentence_length', data=train_data)
    # sns.countplot(x=train_data['sentence_length'])
    plt.xticks([]) # x轴上不要提示信息
    # plt.title('sentence_length countplot')
    plt.show()

    # 5 绘制数据长度分布图-曲线图
    sns.displot(x='sentence_length', data=train_data)
    # sns.displot(x=train_data['sentence_length'])
    plt.yticks([]) # y轴上不要提示信息
    plt.show()

    # 验证集
    # 3 求数据长度列 然后求数据长度的分布
    dev_data['sentence_length'] = list(map(lambda x: len(x), dev_data['sentence']))

    # 4 绘制数据长度分布图-柱状图
    sns.countplot(x='sentence_length', data=dev_data)
    # sns.countplot(x=dev_data['sentence_length'])
    plt.xticks([])  # x轴上不要提示信息
    # plt.title('sentence_length countplot')
    plt.show()

    # 5 绘制数据长度分布图-曲线图
    sns.displot(x='sentence_length', data=dev_data)
    # sns.displot(x=dev_data['sentence_length'])
    plt.yticks([])  # y轴上不要提示信息
    plt.show()
  • 训练集句子长度分布
    在这里插入图片描述
    在这里插入图片描述

  • 验证集句子长度分布

在这里插入图片描述
在这里插入图片描述

  • 分析
    • 通过绘制句子长度分布图,可以得知我们的语料中大部分句子长度的分布范围,因为模型的输入要求为固定尺寸的张量,合理的长度范围对之后进行句子截断补齐(规范长度)起到关键的指导作用。
    • 上图中大部分句子长度的范围大致为 20-250 之间。

5.3 获取正负样本长度散点分布

演示代码如下:

# 获取正负样本长度散点分布,也就是按照x正负样本进行分组 再按照y长度进行散点图
# train_data['sentence_length'] = list(map(lambda x: len(x), train_data['sentence']))
#  sns.stripplot(y='sentence_length', x='label', data=train_data)
def dm03_sns_stripplot():
    # 1 设置显示风格plt.style.use('fivethirtyeight')
    plt.style.use('fivethirtyeight')

    # 2 pd.read_csv 读训练集 验证集数据
    train_data = pd.read_csv(filepath_or_buffer='./cn_data/train.tsv', sep='\t')
    dev_data = pd.read_csv(filepath_or_buffer='./cn_data/dev.tsv', sep='\t')

    # 3 求数据长度列 然后求数据长度的分布
    train_data['sentence_length'] = list(map(lambda x: len(x), train_data['sentence']))

    # 4 统计正负样本长度散点图 (对train_data数据,按照label进行分组,统计正样本散点图)
    sns.stripplot(y='sentence_length', x='label', data=train_data)
    plt.show()

    sns.stripplot(y='sentence_length', x='label', data=dev_data)
    plt.show()
  • 训练集上正负样本的长度散点分布
    在这里插入图片描述
  • 验证集上正负样本的长度散点分布
    在这里插入图片描述
  • 分析
    • 通过查看正负样本长度散点图,可以有效定位异常点的出现位置,帮助我们更准确进行人工语料审查。
    • 上图中在训练集正样本中出现了异常点,它的句子长度近3500左右,需要我们人工审查

5.4 获取不同词汇总数统计

演示代码如下:

# 导入jieba用于分词
# 导入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

5.5 词频统计与关键词词云

演示代码如下:

# 使用jieba中的词性标注功能
import jieba.posseg as pseg
from wordcloud import WordCloud

# 每句话产生形容词列表
def get_a_list(text):
    r = []

    # 使用jieba的词性标注方法切分文本 找到形容词存入到列表中返回
    for g in pseg.lcut(text):
        if g.flag == "a":
            r.append(g.word)
    return r

# 根据词云列表产生词云
def  get_word_cloud(keywords_list):
    # 实例化词云生成器对象
    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()


# 思路分析 训练集正样本词云 训练集负样本词云
# 1 获得训练集上正样本 p_train_data
#   eg: 先使用逻辑==操作检索符合正样本 train_data[train_data['label'] == 1]
# 2 获取正样本的每个句子的形容词 p_a_train_vocab = chain(*map(a,b))
# 3 调用绘制词云函数
def dm_word_cloud():
    # 1 获得训练集上正样本p_train_data
    #   eg: 先使用逻辑==操作检索符合正样本 train_data[train_data['label'] == 1]
    train_data = pd.read_csv(filepath_or_buffer='./cn_data/train.tsv', sep='\t')
    p_train_data = train_data[train_data['label'] == 1 ]['sentence']

    # 2 获取正样本的每个句子的形容词 p_a_train_vocab = chain(*map(a,b))
    p_a_train_vocab = chain(*map(lambda x: get_a_list(x) , p_train_data))
    # print(p_a_train_vocab)
    # print(list(p_a_train_vocab))

    # 3 调用绘制词云函数
    get_word_cloud(p_a_train_vocab)


    print('*' * 60 )
    # 训练集负样本词云
    n_train_data = train_data[train_data['label'] == 0 ]['sentence']

    # 2 获取正样本的每个句子的形容词 p_a_train_vocab = chain(*map(a,b))
    n_a_train_vocab = chain(*map(lambda x: get_a_list(x) , n_train_data)  )
    # print(n_a_dev_vocab)
    # print(list(n_a_dev_vocab))

    # 3 调用绘制词云函数
    get_word_cloud(n_a_train_vocab)
  • 训练集正样本形容词词云
    在这里插入图片描述

  • 训练集负样本形容词词云
    在这里插入图片描述

  • 分析

    • 根据高频形容词词云显示,我们可以对当前语料质量进行简单评估,同时对违反语料标签含义的词汇进行人工审查和修正,来保证绝大多数语料符合训练标准。
    • 上图中的正样本大多数是褒义词,而负样本大多数是贬义词,基本符合要求,但是负样本词云中也存在"便利"这样的褒义词,因此可以人工进行审查。

六、文本特征处理

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

6.1 n-gram特征

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

6.1.1 举例

假设给定分词列表: ["是谁", "敲动", "我心"]

对应的数值映射列表为: [1, 34, 21]

我们可以认为数值映射列表中的每个数字是词汇特征.

除此之外, 我们还可以把"是谁"和"敲动"两个词共同出现且相邻也作为一种特征加入到序列列表中,

假设1000就代表"是谁"和"敲动"共同出现且相邻

此时数值映射列表就变成了包含2-gram特征的特征列表: [1, 34, 21, 1000]

这里的"是谁"和"敲动"共同出现且相邻就是bi-gram特征中的一个.

"敲动"和"我心"也是共现且相邻的两个词汇, 因此它们也是bi-gram特征.

假设1001代表"敲动"和"我心"共同出现且相邻

那么, 最后原始的数值映射列表 [1, 34, 21] 添加了bi-gram特征之后就变成了 [1, 34, 21, 1000, 1001]

6.2 文本长度规范

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

七、文本数据增强

7.1 回译数据增强法

7.1.1 概念

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

7.1.2 回译数据增强优势:

  • 操作简便,获得新语料质量高

7.1.3 回译数据增强存在的问题

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

7.1.4 高重复率解决办法:

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

7.1.5 回译数据增强实现(基于有道翻译接口)

演示代码如下:

# 导入必备的工具包
import requests

# 思路分析
# 1 定义需要访问的有道翻译API接口--url
# 2 定义需要翻译的文本:text
# 3 定义data数据:from代表原始语言, to代表目标语言, i代表需要翻译的文本, doctype:文本的类型
# 4 requests.post(url=url, params=data)即代表访问api接口的方法


def dm_translate():
    url = 'http://fanyi.youdao.com/translate'
    # 第一次翻译,目标语言英文
    text1 = '这个价格非常便宜'
    data1 = {'from': 'zh-CHS', 'to': 'en', 'i': text1, 'doctype': 'json'}
    response1 = requests.post(url=url, params=data1)
    res1 = response1.json()
    # 打印第一次翻译结果
    print(res1)

    # 第二次翻译, 目标语言中文
    text2 = 'The price is very cheap'
    data2 = {'from': 'en', 'to': 'zh-CHS', 'i': text2, 'doctype': 'json'}
    response2 = requests.post(url=url, params=data2)
    res2 = response2.json()
    # 打印第二次翻译结果
    print(res2)

打印结果如下:

第一次翻译结果:{'type': 'ZH_CN2EN', 'errorCode': 0, 'elapsedTime': 1, 'translateResult': [[{'src': '这个价格非常便宜', 'tgt': 'The price is very cheap'}]]}

第二次翻译结果:{'type': 'EN2ZH_CN', 'errorCode': 0, 'elapsedTime': 1, 'translateResult': [[{'src': 'The price is very cheap', 'tgt': '价格非常便宜'}]]}

总结

  • 文章对在自然语言处理阶段的开始阶段:即对文本转换张量或者文本处理进行了总结分析。为后边的模型训练奠定良好的基础。

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

相关文章:

  • 用三维模型的顶点法向量计算法线贴图
  • 【包教包会】CocosCreator3.x——重写Sprite,圆角、3D翻转、纹理循环、可合批调色板、不影响子节点的位移旋转缩放透明度
  • 记录一次网关异常
  • vue3+element-plus多选框全选与单选
  • Z2400032基于Java+Mysql+SSM的校园在线点餐系统的设计与实现 代码 论文
  • 神经网络入门实战:(九)分类问题 → 神经网络模型搭建模版和训练四步曲
  • mysql order by后进行limit分页查询出现重复数据
  • shell脚本30个案例(五)
  • Spring AI 框架介绍
  • WuCup网络安全技能大赛WP
  • Java 单例模式:深度解析与应用
  • mysql线上问题集合
  • Stable Diffusion 3 论文
  • 淘宝商品数据获取:Python爬虫技术的应用与实践
  • 大数据营销
  • Flink四大基石之窗口(Window)使用详解
  • 如何实现人机环境之间动态交互的事实与价值编排组合
  • 前端面试热门题(二)[html\css\js\node\vue)
  • Docker 容器隔离关键技术:SELinux
  • el-table 组件二次封装(vue2)
  • 【MATLAB源码-第230期】基于matlab的32QAM系统相位偏移估计HOS算法仿真,对比补偿前后的星座图误码率。
  • 机器学习算法(六)---逻辑回归
  • Qt Serial Bus 前置介绍篇
  • ​导游|基于SprinBoot+vue的在线预约导游系统
  • 【Django-xadmin】
  • Git命令大全(超详细)