文本预处理
- 文本预处理
- 1.认识文本预处理
- 2.文本处理的基本方法
- 2.1.什么是分词
- 2.2 什么是命名实体化
- 2.3词性标注
- 3.文本张量的表示方法
- 3.1文本张量表示
- 3.2 one-hot词向量表示
- 4.Word2vec模型
- 4.1模型介绍
- 4.2word2vec的训练和使用
- 5.词嵌入word embedding 介绍
- 6.文本数据分析
- 1.文本数据分析介绍
- 2.数据集说明
- 3.获取标签的数量分布
- 4.获取句子长度分布
- 5.获取正负样本长度散点分布
- 6. 获取不用词汇总数统计
- 7.获取训练集高频形容词词云
- 8.获取验证集形容词词云
- 7.文本特征处理
- 1.特征处理的作用
文本预处理
1.认识文本预处理
- 文本预处理及作用
前提:之前用pandas等进行数据分析,也属于文本预处理的范围
文本预处理作用:将文本转换成模型能够识别的张量形式,进而实现模型的训练
规范张量的尺寸,指导模型超参数的选择,提升模型的评估指标
- 文本预处理的主要环节
1.文本处理的基本方法:分词、NER、POS
2.文本张量的表示方法:one-hot、word2vec、wordEmbedding
3.文本语料的数据分析:标签数量分析(类别不均衡问题)、句子长度分析、词频统计和关键词词云
4.文本特征处理:添加n-gram特征、文本长度规范
5.数据增强方法:回译数据增强
2.文本处理的基本方法
2.1.什么是分词
- 将连续的字符串列按照一定的规范重新组合成词序列的过程.在英文当中,单词之间以空格作为自然分界符,而中文只是字,句和段能通过明显的分界符来简单划界,维度词没有形式上的分界符,分词就是找到这样的分界符的过程
举个例子:使用结巴分词
关关雎鸠,在河之洲;窈窕淑女,君子好逑!
['关关雎', '鸠', ',', '在', '河之洲', ';', '窈窕淑女', ',', '君子好逑', '!']
2.分词的作用
词作为语言语义理解的最小单元,是人类语言的基础.
简化文本处理:使用分词,将原始文本分解为更小,更容易管理的片段,方便处理
提高效率:分词后文本更容易进行索引,从而提高信息检索的效率
理解语义结构:分词有助于解析句子的结构,帮助计算机理解文本的意义,这对于机器翻译系统等应用至关重要
减少数据维度:分词能够有效降低文本数据的维度,使模型训练更快,性能更好。
- jieba 的特性:
支持多分类分词模式:
精确模式
全模式
搜索引擎
支持中文繁体分词
支持用户自定义词典
- jieba的安装
pip install jieba
- jieba的使用
- 精确模式分词:
- 试图将句子最精确的切分开,适合作文本分析
import jieba
# 1.默认为精确模式
content = '关关雎鸠,在河之洲;窈窕淑女,君子好逑!'
# 精确模式分词: 试图将句子最精确的切分开,适合作文本分析
# 分词默认模式为:精确模式 cut_call = False
res4 = jieba.cut(content)
# 直接打印返回一个生成器对象
# 无需使用列表直接使用jieba.lcut即可
res = jieba.lcut(content, cut_all=False)
# 可用列表承接分词结果
print(list(res4))
print('精确分词模式', res
全模式分词:
把句子中所有的可以成词的词语都扫描出来,速度很快,但不能消除歧义
# 2.全模式
# 无需要使用列表,直接使用jieba.lcut即可
content1 = '关关雎鸠,在河之洲'
res1 = jieba.lcut(content1, cut_all=True)
print('全模式', res1)
# 全模式 ['关关', '关关雎', '雎鸠', ',', '在', '河之洲']
# 注意1: 关关雎鸠 分为三个词
# 注意2: 符号也被分词
- 搜索引擎模式
- 在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词
# 3.搜索引擎模式
# lcut_for_search:对搜索引擎优化,对长词进行精确模式分词,对短词进行全模式分词
res3 = jieba.lcut_for_search(content)
print('搜索引擎', res3)
# 搜索引擎 ['关关', '关关雎', '鸠', ',', '在', '河之洲', ';', '窈窕', '淑女', '窈窕淑女', ',', '君子', '好逑', '君子好逑', '!']
- 中文繁体分词
# 4.繁体字分词
sent1 = '煩惱即是菩提,我暫且不提'
print('繁体字分词', jieba.lcut(sent1))
# 繁体字分词 ['煩惱', '即', '是', '菩提', ',', '我', '暫且', '不', '提']
2.2 什么是命名实体化
- 命名实体化:通常就是将人名,地名,机构名等专有名词统称为命名实体化
- 命名实体识别(简称NER):就是识别出一段文本中可能存在的命名实体
- 举个例子
鲁迅, 浙江绍兴人, 五四新文化运动的重要参与者, 代表作朝花夕拾.
==>
鲁迅(人名) / 浙江绍兴(地名)人 / 五四新文化运动(专有名词) / 重要参与者 / 代表作 / 朝花夕拾(专有名词)
- 作用:
信息提取:识别出文本中的实体,更好的提取文本信息,高效的进行数据挖掘
问答系统:在构建智能问答系统时:,通过人名,地名,等更准确的定位答案
机器翻译:对于多语言的信息系统,正确识别和保留专有名词有助于提高机器翻译的质量,确保名字,地点不被误翻译
2.3词性标注
词性:语言中对词的一种分类方法,以特征为主要依据.兼顾词汇意义对词进行划分的结果,常见的词性有14种,比如:名词,动词,形容词等.
词性标注:标注一段文本中每个词汇的词性
词性标注作用:
对文本中的每个单词分配一个适当的词性标签,比如名词、动词、形容词、副词等。
语法分析:是构建复杂语法分析的基础步骤,通过识别词语词性更好理解句子结构
信息检索:词性标注可以帮助提高查询的准确性和相关性,例如区分同形异义词的不同含义。
机器翻译:机器翻译系统需要了解源语言中词语的词性,以便选择正确的目标语言表达形式。
文本分类与情感分析:有助于从文本中提取特征,这在文本分类和情感分析等任务中特别有用,比如识别出评价性词汇(通常是形容词或副词)来判断文本的情感倾向。
命名实体化:在识别文本中的人名、地名、组织名等命名实体时,词性标注可以帮助缩小候选范围,提高识别准确性。
- 使用jieba分词进行词性标注
import jieba.posseg as pseg
pseg.lcut("我爱北京天安门")
# 输出结果为
[pair('我', 'r'), pair('爱', 'v'), pair('北京', 'ns'), pair('天安门', 'ns')]
3.文本张量的表示方法
3.1文本张量表示
- 将一段文字使用张量表示,其中一般将词汇表示为向量,称为词向量,再由词向量按顺序组成矩阵.形成文本表示.
- 举个例子:
["人生", "该", "如何", "起头"]
==>
# 每个词对应矩阵中的一个向量
[[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]]
- 文本张量表示的作用:
- 将文本表示为张(矩阵)形式,能够使用文本作为计算机处理程序的输入
- 文本张量的表示方法
- one-hot编码
- Word2vec
- Word Embeding
3.2 one-hot词向量表示
- 又称独热编码,将每个词表示成具有n个元素的向量,这些词向量只有一个元素1其他都为0,不用词汇之间位置不同,其中n的大小是整个语料中不同词汇的总数
- 举个例子:
["改变", "要", "如何", "起手"]`
==>
[[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
- one-hot编码实现:
进行onehot编码
import jieba
# 导入keras中的词汇映射器Tokenizer
from tensorflow.keras.preprocessing.text import Tokenizer
# 导入用于对象保存与加载的joblib
from sklearn.externals import joblib
def onehot_gen(sent):
# 1.准备语料
vocabs = list(set(jieba.lcut(sent)))
# 2.实例化Tokenizer
tokenizer = Tokenizer(vocabs)
tokenizer.fit_on_texts(vocabs)
# 3.查询单词idx,赋值zero_list
for vocab in vocabs:
zero_list = [0] * len(vocabs)
# Tokenizer索引默认从1开始,所以要减1
idx = tokenizer.word_index[vocab] - 1
zero_list[idx] = 1
print(vocab, '的独热编码', zero_list)
# 4.使用joblib 保存模型
my_path = './onehot_gen.pkl'
joblib.dump(tokenizer, my_path)
print('保存成功')
# 打印
print(tokenizer.word_index)
print(tokenizer.index_word)
- 输出结果为:
。 的独热编码 [1, 0, 0, 0, 0, 0]
关关雎 的独热编码 [0, 1, 0, 0, 0, 0]
在 的独热编码 [0, 0, 1, 0, 0, 0]
河之洲 的独热编码 [0, 0, 0, 1, 0, 0]
鸠 的独热编码 [0, 0, 0, 0, 1, 0]
, 的独热编码 [0, 0, 0, 0, 0, 1]
保存成功
{'。': 1, '关关雎': 2, '在': 3, '河之洲': 4, '鸠': 5, ',': 6}
{1: '。', 2: '关关雎', 3: '在', 4: '河之洲', 5: '鸠', 6: ','}
- one-hot编码的使用
# 思路分析
# 1 加载已保存的词汇映射器Tokenizer joblib.load(mypath)
# 2 查询单词idx 赋值zero_list,生成onehot 以token为' 鸠'
# 3 token = "你好" 会出现异常
def onthot_use(char):
# 1.加载词汇映射器
my_path = './onehot_gen.pkl'
my_tokenizer = joblib.load(my_path)
# 2.查询index
try:
idx = my_tokenizer.word_index[char] - 1
except:
print('token没有这个')
# 3.获取都热编码
zero_list = [0] * len(my_tokenizer.word_index)
zero_list[idx] = 1![请添加图片描述](https://i-blog.csdnimg.cn/direct/643a2a8655a144f5bf3cc9c957f9ffae.png)
print(char, '的独热编码', zero_list)
- 输出结果为
鸠 的独热编码 [0, 0, 0, 0, 1, 0]
- one-hot编码的优劣势
- 优势:操作简单,容易理解
- 劣势:完全割裂词与词之间的联系,在语料集下,每个向量的长度过大,占据大量内存
4.Word2vec模型
4.1模型介绍
-
将词汇表示成向量的无监督训练方法,该过程构建神经网络模型,将网络参数作为词汇表示,包含CBOW和skipgram两种方法
-
CBOW模式:连续词袋模型
-
给定一段用于训练的文本语料,选定某段长度窗口 ,使用上下文词汇预测目标词汇
- 分析
- 窗口大小为9,使用前后4个词汇对目标词汇来预测
- CBOW模式下的word2vec过程说明:
- 假设我们给定的训练语料只有一句话: Hope can set you free (愿你自由成长),窗口大小为3,因此模型的第一个训练样本来自Hope can set,因为是CBOW模式,所以将使用Hope和set作为输入,can作为输出,在模型训练时, Hope,can,set等词汇都使用它们的one-hot编码. 如图所示: 每个one-hot编码的单词与各自的变换矩阵(即参数矩阵3x5, 这里的3是指最后得到的词向量维度)相乘之后再相加, 得到上下文表示矩阵(3x1).
- 接着, 将上下文表示矩阵与变换矩阵(参数矩阵5x3, 所有的变换矩阵共享参数)相乘, 得到5x1的结果矩阵, 它将与我们真正的目标矩阵即can的one-hot编码矩阵(5x1)进行损失的计算, 然后更新网络参数完成一次模型迭代.
最后窗口按序向后移动,重新更新参数,直到所有语料被遍历完成,得到最终的变换矩阵(3x5),这个变换矩阵与每个词汇的one-hot编码(5x1)相乘,得到的3x1的矩阵就是该词汇的word2vec张量表示.
- skipgram模式:被称为跳词模型
- 使用中心词来预测上下文文本
- 如图所示: 将token进行one-hot编码与变换矩阵(即参数矩阵4x5, 这里的4是指最后得到的词向量维度)相乘, 得到目标词汇表示矩阵(4x1).
假设我们给定的训练语料只有一句话: Hope can set you free (愿你自由成长),窗口大小为3,因此模型的第一个训练样本来自Hope can set,因为是skipgram模式,所以将使用can作为输入 ,Hope和set作为输出,在模型训练时, Hope,can,set等词汇都使用它们的one-hot编码. 如图所示: 将can的one-hot编码与变换矩阵(即参数矩阵3x5, 这里的3是指最后得到的词向量维度)相乘, 得到目标词汇表示矩阵(3x1).
最后窗口按序向后移动,重新更新参数,直到所有语料被遍历完成,得到最终的变换矩阵即参数矩阵(3x5),这个变换矩阵与每个词汇的one-hot编码(5x1)相乘,得到的3x1的矩阵就是该词汇的word2vec张量表示.
- 词向量的检索获取
- 以下通过CBOW模型举例说明如何检a单词的词向量
- 如下图所示:a的onehot编码[10000],用参数矩阵[3,5] * a的onehot编码[10000],可以把参数矩阵的第1列参数给取出来,这个[3,1]的值就是a的词向量。
4.2word2vec的训练和使用
1.词向量的训练保存和加载
fasttext 是facebook开源的一个词向量与文本分类工具,安装该工具包的方法
pip install fasttext-wheel
# 导入fasttext
import fasttext
def dm_fasttext_train_save_load():
# 1 使用train_unsupervised(无监督训练方法) 训练词向量
mymodel = fasttext.train_unsupervised('./data/fil9')
print('训练词向量 ok')
# 2 save_model()保存已经训练好词向量
# 注意,该行代码执行耗时很长
mymodel.save_model("./data/fil9.bin")
print('保存词向量 ok')
# 3 模型加载
mymodel = fasttext.load_model('./data/fil9.bin')
print('加载词向量 ok')
# 步骤1运行效果如下:
有效训练词汇量为124M, 共218316个单词
Read 124M words
Number of words: 218316
Number of labels: 0
Progress: 100.0% words/sec/thread: 53996 lr: 0.000000 loss: 0.734999 ETA: 0h 0m
2.查看单词对应的词向量
# 通过get_word_vector方法来获得指定词汇的词向量, 默认词向量训练出来是1个单词100特征
# 3.查看单词对应词向量
# 通过get_word_vector()方法获取指定的词向量,默认词向量训练出来1个单词100特征
def dm_fasttext_get_word_vector():
# 1.加载模型
my_model = fasttext.load_model('./data/fil9.bin')
# 2.查询某个词汇的词向量
# get_word_vector()查询词向量
result = my_model.get_word_vector('donkey')
print(f'donkey词向量为:{result}')
print(f'donkey词向量类型为:{type(result)}')
print(f'donkey词向量维度为:{result.shape}')
# 运行效果如下:
array([-0.03087516, 0.09221972, 0.17660329, 0.17308897, 0.12863874,
0.13912526, -0.09851588, 0.00739991, 0.37038437, -0.00845221,
...
-0.21184735, -0.05048715, -0.34571868, 0.23765688, 0.23726143],
dtype=float32)
3.模型效果检验
# 4.检验模型效果
# 查看'运动'的近邻单词,可以看到'体育网','运动汽车','运动服'等
def dm_fasttext_get_nearset_neighbors():
# 1.加载模型
my_model = fasttext.load_model('./data/fil9.bin')
# 2.查询sports的临近单词
result1 = my_model.get_nearest_neighbors('sports')
print(f'sports近邻单词为:{result1}')
[(0.8414610624313354, 'sportsnet'), (0.8134572505950928, 'sport'), (0.8100415468215942, 'sportscars'), (0.8021156787872314, 'sportsground'), (0.7889881134033203, 'sportswomen'), (0.7863013744354248, 'sportsplex'), (0.7786710262298584, 'sporty'), (0.7696356177330017, 'sportscar'), (0.7619683146476746, 'sportswear'), (0.7600985765457153, 'sportin')]
# 查询music的临近单词
result2 = my_model.get_nearest_neighbors('music')
print(f'music近邻单词为:{result2}')
[(0.8908010125160217, 'emusic'), (0.8464668393135071, 'musicmoz'), (0.8444250822067261, 'musics'), (0.8113634586334229, 'allmusic'), (0.8106718063354492, 'musices'), (0.8049437999725342, 'musicam'), (0.8004694581031799, 'musicom'), (0.7952923774719238, 'muchmusic'), (0.7852965593338013, 'musicweb'), (0.7767147421836853, 'musico')]
# 查询dog的临近单词
result3 = my_model.get_nearest_neighbors('dog')
print(f'dog近邻单词为:{result3}')
[(0.8456876873970032, 'catdog'), (0.7480780482292175, 'dogcow'), (0.7289096117019653, 'sleddog'), (0.7269964218139648, 'hotdog'), (0.7114801406860352, 'sheepdog'), (0.6947550773620605, 'dogo'), (0.6897546648979187, 'bodog'), (0.6621081829071045, 'maddog'), (0.6605004072189331, 'dogs'), (0.6398137211799622, 'dogpile')]
4.模型超参数设定
在训练词向量过程中, 我们可以设定很多常用超参数来调节我们的模型效果, 如:
无监督训练模式: ‘skipgram’ 或者 ‘cbow’, 默认为’skipgram’, 在实践中,skipgram模式在利用子词方面比cbow更好.
词嵌入维度dim: 默认为100, 但随着语料库的增大, 词嵌入的维度往往也要更大.
#数据循环次数epoch: 默认为5, 但当你的数据集足够大, 可能不需要那么多次.
#学习率lr: 默认为0.05, 根据经验, 建议选择[0.01,1]范围内.
使用的线程数thread: 默认为12个线程, 一般建议和你的cpu核数相同.
# 5.模型超参数设置
# 无监督训练模式model可选'skipgram(默认) or 'cbow'
# 词嵌入维度dim 默认为0
# 循环次数epoch 默认为5
# 学习率lr默认为0.05 建议[0,0.1,1]
def dm_fasttest_train_args():
my_model = fasttext.train_unsupervised('./data/fil9',
'cbow',
epoch=1,
lr=0.1,
dim=300,
thread=8)
# 保存模型
my_model.save_model('./data/fil9.bin')
5.词嵌入word embedding 介绍
- 通过一定方式将词汇映射到指定维度(一般是更高维度)空间
- 广义的word embedding 包括所有词汇向量的表示方法,如之前学的word2vec,也可认为是word embedding一种
- 狭义上讲word embedding指的是神经网络中加入embedding层,对整个网络进行训练同时产生embedding矩阵(embedding层参数),这个embedding矩阵就是训练过程中所有输入词汇的向量表示组成矩阵.
word embedding的可视化分析:
- 通过使用tensorboared可视化嵌入的词向量
- 1.导入数据包
import torch
import jieba
from tensorflow.keras.preprocessing.text import Tokenizer
import torch.nn as nn
from torch.utils.tensorboard import SummaryWriter
- 2.获取语料
def embedding_show():
# 1.获取语料数据
sent1 = '床前明月光,疑是地上霜;举头望明月,低头思故乡!'
sent2 = '关关雎鸠,在河之洲;窈窕阿港,君子好逑!'
sents = [sent1, sent2]
- 3.对句子分词并添加到word_list = []中
# 2.对句子分词在word_list = []
word_list = []
for s in sents:
word_list.append(jieba.lcut(s))
print(f'word_list:{word_list}')
- 4.实例化Tokenizer()训练语料数据获取word_index,index_word
# 3.实例化Tokenizer
my_tokenizer = Tokenizer()
# 训练拿到每个词的索引下标1
my_tokenizer.fit_on_texts(word_list)
# word_index:词为键值,索引为值,索引从1开始
print(my_tokenizer.word_index)
# index_word:索引为键值,词为值
print(my_tokenizer.index_word)
- 5.拿到所有的token并打印句子下标
# 4.拿到所有的token
my_token_list = my_tokenizer.index_word.values()
print(f'my_token_list:{my_token_list}')
# 5.打印句子id
# texts_to_sequences:将句子转换为id
seq2id = my_tokenizer.texts_to_sequences(word_list)
print(f'seq2id:{seq2id}')
- 6.创建Embedding层,拿到词频向量矩阵
# 6.拿到embedding矩阵,查表矩阵
my_embed = nn.Embedding(num_embeddings=len(my_token_list), embedding_dim=8)
print(f'my_embed.shape:', my_embed.weight.shape)
print(my_embed.weight.data)
- 7.查询token,和vec
# 7.查询token,vec
for idx in range(len(my_tokenizer.index_word)):
vec = my_embed(torch.tensor(idx))
word = my_tokenizer.index_word[idx + 1]
print(word, ':', vec)
8.数据可视化
# 8.可视化
# logdir:日志文件保存路径
logdir = './embedding_runs'
# SummaryWriter:记录日志
my_summary = SummaryWriter(log_dir=logdir)
# add_embedding:记录embedding数据
my_summary.add_embedding(my_embed.weight.data, my_token_list)
my_summary.close()
# 记得增加目录
# 终端操作可视化:tensorboard --logdir=embedding_runs --host 0.0.0.0
进入终端窗口输入
tensorboard --logdir=day02/embedding_runs --host 0.0.0.0 得到以下链接
点击更该链接为127.0.0.1:6006
展示得到
6.文本数据分析
1.文本数据分析介绍
- 文本数据分析作用
有效帮助我们理解数据语料,快速检查出语料可能存在的问题,并指导出之后模型训练过程中一些超参数的选择.
- 常用的数据分析方法
- 标签数量分布
作用:了解和分析标签数量分布更好的处理数据集特性,优化性能,适当处理数据不平衡问题
- 类别不平衡问题
- 数据集的多样性
- 句子长度分布
- 数据集的多样性:通过 句子长度分布,了解数据集中句子的长度变化范围,评估数据集的多样性和复杂性
- 异常值检测:识别过长或过短的句子,可能存在异常值或噪声,需要进一步处理
- 词频统计与关键词词云
- 词频统计:停用词过滤,通过词频统计,可以识别常见的停用词,并在后续的处理中过滤掉停用词,减少噪声
- 高频词汇可以作为特征选择的一部分,用于构建模型
- 关键词词云:直观地展示关键词
- 突出重要的词汇
2.数据集说明
- 将基于真实的中文酒店评论语料来讲解常用的文本数据分析方法.
- 中文酒店评论语料
- 属于二分类的中文情感分析语料
- 其中划分有训练集和测试集,二者数据集样式相同
3.获取标签的数量分布
- 导入数据包
# 导入必备工具包
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import jieba
# itertools方便地对数据进行迭代。
from itertools import chain
# posseg模块,用于分词,并返回词性
import jieba.posseg as pseg
# wordcloud用于绘制词云
from wordcloud import WordCloud
- 查看标签的分布情况用于不处理样本不均衡问题
# 思路分析:获取标签数量的分布
# 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()
# 分组可视化
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左右,上图训练和验证集正负样本都稍有不均衡,需要数据值增强
4.获取句子长度分布
# 思路
# 思路分析 : 获取句子长度分布 -绘制句子长度分布-柱状图 句子长度分布-密度曲线图
# 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'])
pltyticks([]) # y轴上不要提示信息
plt.show()
- 分析:
通过绘制句子长度分布图,可以得知语料大部句子长度分布范围因为,模型输入要求固定尺寸张量,合理长度范围对句子截断补齐,规范长度起到关键作用.
5.获取正负样本长度散点分布
# 获取正负样本长度散点分布,也就是按照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()
- 分析
通过查看正负样本长度散点图,可以有效定位异常点出现的位置,及时处理异常点
6. 获取不用词汇总数统计
chain函数作用:将多个可迭代对象连接成一个单一的可迭代对象
*运算符:可以map对象解包,使其元素可作为在参数传递给另一个函数
# 导入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))
7.获取训练集高频形容词词云
# 使用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)
8.获取验证集形容词词云
# 获得验证集上正样本
p_valid_data = valid_data[valid_data["label"]==1]["sentence"]
# 对正样本的每个句子的形容词
valid_p_a_vocab = chain(*map(lambda x: get_a_list(x), p_valid_data))
#print(train_p_n_vocab)
# 获得验证集上负样本
n_valid_data = valid_data[valid_data["label"]==0]["sentence"]
# 获取负样本的每个句子的形容词
valid_n_a_vocab = chain(*map(lambda x: get_a_list(x), n_valid_data))
# 调用绘制词云函数
get_word_cloud(valid_p_a_vocab)
get_word_cloud(valid_n_a_vocab)
分析
通过高频形容词词云显示.对当前语料进行简单评估,同时对违反语料标签含义的词汇进行人工审查和修正
7.文本特征处理
1.特征处理的作用
包括对语料添加具有普适性的文本特征,如n_gram特征,以对加入特征之后的文本语料进行必要处理,比如:长度规范,增强模型评估指标
常见的文本特征处理的必要方法:
添加n_ gram 特征
- 给定一段文本序列,其中n个词或子相邻之间共线特征及n-gram特征是bi-gram和tri-gram特征,分别对应2和3
# 一般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, 3, 2, 1, 5, 3])
{(3, 2), (1, 3), (2, 1), (1, 5), (5, 3)}
"""
return set(zip(*[input_list[i:] for i in range(ngram_range)]))
- 输出结果为
# 该输入列表的所有bi-gram特征
{(3, 2), (1, 3), (2, 1), (1, 5), (5, 3)}
文本长度规范及作用
- 模型的输入需要等尺寸大小的矩阵,因此在进入模型之前需要对每条文本数值映射后的长度进行规范,将句子长度分析覆盖绝大多数文本的合理长度,对超出文本进行截断,对不足文本补齐(一般使用数字0)
- 实现
from tensorflow.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]]