线索
- 文本预处理及其作用
- 文本预处理中包含的环节
- 文本处理的基本方法
- 文本张量的表示方法
- 文本语料的数据分析
- 文本特征分析
- 数据增强
笔记
-
文本预处理及其作用:
-
文本预料在输送给模型前一般要进行一系列的预处理工作,才能符合模型输入的要求。如:将文本转化为模型需要的张量,规范张量的尺寸等;科学的文本预处理环节还将有效指导模型超参数的选择,提升模型的评估指标。
-
文本预处理中包含的主要环节
- 文本处理的基本方法
- 文本张量的表示方法
- 文本语料的数据分析
- 文本特征分析
- 数据增强方法
-
文本处理的基本方法
- 分词
- 词性标注
- 命名实体识别
-
文本张量的表示方法
- one-hot编码
- Word2vec
- Word Embedding
-
文本语料的数据分析
- 标签数量分布
- 句子长度分布
- 词频统计与关键词词云
-
文本特征处理
- 添加n-gram特征
- 文本长度规范
-
数据增强方法
- 回译数据增强法
文本处理的基本方法
分词
分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。英文中可以根据单词间的空格作为自然分界符,但中文中,只有字、句子、段落之间存在明显的自然分界,在词上没有一个形式上的分界符,因此,对于中文而言,分词过程就是找到这样的分界符的过程。
eg:九号平衡车,儿童平衡车智能两轮腿控电动车体感车白色(不适配卡丁车)
==>
['九号','平衡车','儿童','平衡车','智能','两轮','腿控','电动车','体感车','白色','不','适合','卡丁车']
-
中文分词工具:jieba
-
jieba的特性:
-
支持多种分词模式
- 精确模式
- 全模式
- 搜索引擎模式
-
支持中文繁体分词
-
支持用户自定义词典
-
-
jieba的使用
-
精确模式的分词
试图将句子最精确地分开,适合文本分析。
-
-
import jieba
content = "九号平衡车,儿童平衡车智能两轮腿控电动车体感车白色(不适配卡丁车)"
jieba.cut(content, cut_all=False) # cut_all默认为False
jieba.lcut(content, cut_all=False)
- 全模式分词
- 把句子中所有的可以成词的词语都扫描出来,速度非常快,但是无法消除歧义
jieba.cut(content,cut_all = True)
jieba.lcut(contene,cut_all = True)
- 搜索引擎模式
- 在精确模式的基础上,对长词再次进行切分,提高召回率,适合用于搜索引擎切分
jieba.cut_for_search(content)
jieba.lcut_for_search(content)
- 使用用户自定义词典
> 添加自定义词典后,jieba能够准确识别词典中出现的词汇,提升整体的识别准确率;
> 词典格式:每一行分三部分:词语、词频(可忽略)、词性(可忽略),用空格隔开。顺序不可颠倒;
1. 先定义用户自定义的文件 userdict.txt
2. 加载文件到jieba词库 jieba.load_userdict('./userdict.txt')
词典格式如下:
分词 2 v
云计算 5 n
大数据 8 n
命名实体识别
- 命名实体:通常我们将人名、地名、机构名等专有名词统称命名实体,如:淘宝,孝义市等。
- 命名实体识别(NER)就是识别出一段文本中可能存在的命名实体
张三,XXXX学校学生,出生于XX市。
==>
张三(人名) / XXXX学校(机构名) / 学生 / 出生 / 于 / XX市(地名)
词性标注
- 词性标注(POS)就是标注出一段文本中每个词汇的作用
我敲代码
==>
我/rr,敲/v,代码/n
作用:以分词为基础,是对文本语言的另一个角度的理解,因此也常成为AI解决NLP领域高阶任务的重要基础环节。
- 使用jieba进行中文词性标注:
import jieba.posseg as pseg
pseg.lcut("我爱北京天安门")
文本张量的表示方法
- 将一段文本使用张量进行表示,一般将词汇表示成向量,称作词向量,再由各个词向量按顺序组成矩阵形成文本表示
- 作用:将文本表示成张量形式,能够使语言文本可以作为计算机处理程序的输入,进行解析
- one-hot编码
n:整个语料中不同词汇的总数;将每个词表示成具有n个元素的向量,每个词向量中只有一个元素是1,其他元素是0,不同词汇为1 的位置不同。
# 导入用于对象保存与加载的包
from sklearn.externals import joblib
# 导入keras中的词汇映射中的Tokenizer
from keras.preprocessing.text import Tokenizer
# 设置语料集不同词汇的集合
vocal = {"张三","李四","王五"}
# 实例化词汇映射器
t = Tokenizer(num_words=None,char_level=False)
# 拟合现有文本数据
t.fit_on_texts(vocal)
for token in vocal:
zero_list = [0]*len(vocal)
# 使用映射器转化现有文本数据:将每个词汇分别对应从1开始的自然数
token_index = t.texts_to_sequences([token])[0][0] - 1
zero_list[token_index] = 1
print(token,"的独热编码为:",zero_list)
# 使用joblib保存映射器
tokenizer_path = "./Tokenizer"
joblib.dump(t,tokenizer_path)
onehot编码器的使用
# 导入之前保存的编码器
t = joblib.load(tokenizer_path)
word = "张三"
word_index = t.text_to_sequences([word])[0][0]-1
#初始化一个纯零的向量,令该向量的第word_index位置1
zero_list = [0]*len(vocal)
zero_list[word_index] = 1
print(word, "的独热编码为:",zero_list)
one-hot编码的优劣势:
-
优势:操作简单,容易理解
-
劣势:完全割裂了词与词之间的联系,而且在大语料集下,每个向量的长度过大,占据大量内存
-
Word2vec
-
将词汇表示成向量的无监督训练方法,该过程将构建神经网络模型,将网络参数作为词汇的向量表示,包含CBOW和skipgram两种训练模式。
-
CBOW模式
- 给定一段用于训练的文本语料,再选定某段长度(窗口)作为研究对象,使用上下文词汇预测目标词汇
窗口大小为9,用前后4个词预测中间的词,即使用前后4个词作为输入,中间的词jump作为输出。
- 在模型训练时,每个词汇使用它们的独热编码;每个词汇的独热编码与各自的变换矩阵相乘后再相加,得到上下文表示矩阵;
- 将上下文表示矩阵与变换矩阵相乘,得到结果矩阵
- 窗口向后移动,重新更新参数,直到所有的语料都被遍历完毕,得到最终的变换矩阵,该变换矩阵与词汇独热编码相乘得到该词汇的word2vec表示
-
L=∑n∈Dlogp(w∣Context(w))L=\sum_{n \in D} \log p(w \mid Context(w)) L=n∈D∑logp(w∣Context(w))
- skipgram模式
- 使用fasttext工具实现word2vec的训练和使用
- 获取训练数据
- 训练词向量
import fasttext
model = fasttext.train_unsupervised('./data/fil9')
# 查看对应词的词向量
model.get_word_vector("the")
- 模型超参数设定
> 在训练词向量的过程中,可以设定很多常用的超参数来调节模型效果,如:
1. 无监督训练模式:'skipgram'或'cbow',默认为'skipgram',在实践中,skipgram模式在利用子词方面比cbow更好;
2. 词嵌入维度dim:默认为100,但随着语料库的增大,词嵌入的维度往往也要增大;
3. 数据循环次数epoch:默认为5,但数据集足够大时,可能不需要那么多次;
4. 学习率lr:默认为0.05,根据经验,建议选择[0.01,1]范围内
5. 使用的线程数thread:默认为12个线程,可设置为-1(自动匹配cpu核数)
model = fasttext.train_unsupervised('data/fil9',dim=300,epoch=1,lr=0.1,thread=-1)
- 模型效果检验
> 检验模型效果可以通过查看邻居词汇,主观判断该临近词汇是否与目标词汇词汇相关
model.get_nearest_neighbors('sports')
# 输出结果为sports模型判断出的临近词(向量,词汇)
- 模型的保存与重加载
- 保存:model_save_model('模型名字.bin')
- 重加载:fasttext.load_model()
-
Word Embedding
- 通过一定的方式将词汇映射到指定维度(一般是更高维度)的空间;
- 广义的word embedding 包括所有密集词汇向量的表示方法,如word2vec,即可认为是word embedding的一种;
- 狭义的word embedding 是指在神经网络中加入的embedding层,对整个网络进行训练的同时产生的embedding矩阵(embedding层的参数),这个embedding矩阵就是训练过程中所有输入词汇的向量表示组成的矩阵
word embedding 的可视化分析:
- 通过使用tensorboard可视化嵌入的词向量
# _*_ coding:utf-8 _*_
import tensorflow as tf
import tensorboard as tb
tf.io.gfile = tb.compat.tensorflow_stub.io.gfile
import torch
import json
import fileinput
from torch.utils.tensorboard import SummaryWriter
# 实例化一个摘要写入对象
writer = SummaryWriter()
# 随机初始化一个100*50的矩阵 作为我们已经得到的词嵌入矩阵
# 代表100个词汇,每个词汇被表示成50维的向量
embedded = torch.randn(100, 50)
# 导入事先准备的100个中文词汇文件,形成meta列表原始词汇
meta = list(map(lambda x: x.strip(), fileinput.FileInput("./vocab100.csv",, openhook=fileinput.hook_encoded('utf-8', ''))))
writer.add_embedding(embedded, metadata=meta)
writer.close()
文本数据分析
使用中文酒店评价数据集进行分析展示常用的文本数据分析方法
数据集样式👇🏼
有监督学习的数据集,共分为两列,第一列为带有情感色彩的评论文本;第二列为标签,0表示消极,1表示积极评价。
- 🧐获得训练集和验证集的标签数量分布
# 导入必备工具包
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
# 分别读取验证数据集和测试数据集
train_data = pd.read_csv("./cn_data/train.tsv", sep="\t")
valid_data = pd.read_csv("./cn_data/dev.tsv", sep="\t")
# 获得训练数据标签分布
sns.countplot(x="label", data=train_data)
plt.title("train_data")
plt.show()
# 验证集
sns.countplot(x="label", data=valid_data)
plt.title("valid_data")
plt.show()
> 分析:
- 在深度学习模型评估中,一般使用ACC作为评估指标,若想将ACC的基线定义在50%左右,则需要正负样本维持在1:1左右,否则需要进行必要的数据增强或数据删减。
- 上图中数据略不平衡,可以适当进行数据增强
- 🧐句子长度分布
train_data["sentence_len"] = list(map(lambda x: len(x),train_data["sentence"]))
valid_data["sentence_len"] = list(map(lambda x: len(x),valid_data["sentence"]))
sns.countplot(x="sentence_len", data=train_data)
plt.xticks([])
plt.show()
sns.countplot(x="sentence_len", data=valid_data)
plt.xticks([])
plt.show()
sns.histplot(data=train_data["sentence_len"])
plt.yticks([])
plt.show()
sns.histplot(data=valid_data["sentence_len"])
plt.yticks([])
plt.show()
> 分析:
1. 通过绘制句子长度分布图,可以得知我们语料中大部分句子长度的分布范围,由于模型的输入要求为固定尺寸的张量,合理的长度范围对之后进行句子截断补齐(规范长度)起到关键的指导作用;
2. 分析上图,得到数据集中句子长度主要集中在[20,250]
- 🧐获取数据集正负样本长度散点分布
sns.stripplot(y='sentence_len', x='label', data=train_data)
plt.title("train")
plt.show()
sns.stripplot(y="sentence_len", x='label', data=valid_data)
plt.title("valid")
plt.show()
> 分析:
通过绘制句子长度散点分布图,可以帮助直观地发现数据集中异常点的出现,比如在训练样本中,正例中出现了一个孤立的散点,句子长度达到了3500字,可以人工进行检查
- 🧐获得数据集不同词汇总数的统计
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))
- 🧐绘制数据集词云图
# 使用jieba的词性标注方法切分文本,获得具有词性属性flag 和 词汇属性word的对象
import jieba.posseg as pseg
def get_a_list(text):
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为背景颜色
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"]
p_train_a_data = chain(*map(lambda x: get_a_list(x),p_train_data))
n_train_data = train_data[train_data["label"]==0]["sentence"]
n_train_a_data = chain(*map(lambda x:get_a_list(x),n_train_data))
get_word_cloud(p_train_a_data)
get_word_cloud(n_train_a_data)
正例:
负例:
作用:评估数据质量,便于后期人工审核
文本特征处理
文本特征处理包括为语料添加具有普适性的文本特征,如n-gram特征,以及对加入特征之后的文本语料进行必要的处理,如:长度规范,这些特征处理工作能够有效的将重要的文本特征加入模型训练中,增强模型评估指标
-
常见的文本特征处理方法
-
n-gram特征添加
给定一段文本序列,其中n个词或字相邻共现特征即n-gram特征,常用的n-gram特征有:bi-gram、tri-gram,分别对应n=2、n=3
-
ngram_range = 2
def create_ngram_set(input_list):
return set(zip(*[input_list[i:] for i in range(ngram_range)]))
input = [1,3,2,1,5,3]
res = create_ngram_set(input)
print(res)
-
文本长度规范
一般模型输入需要等尺寸大小的矩阵,因此在进入模型前需要对每条文本数值映射后的长度进行规范,此时将根据句子长度分布分析出覆盖绝大多数文本的合理长度,对超长文本进行截断,对不足文本进行补齐(一般用0)
from keras_preprocessing import sequence
# cutlen:根据数据分析中句子的长度,得到的能够覆盖语料集中90%数据的最短长度
cutlen = 10
def padding(x_train):
return sequence.pad_sequences(x_train,cutlen)
x_train = [[1,23,24,16,12,45,12,98,42,21,19],[76,47,23,39]]
res = padding(x_train)
print(res)
文本数据增强
-
回译数据增强法
一般基于google翻译接口,将文本数据翻译成另外一种语言,之后再翻译回原语言,即可得到认为与原语料相同标签的新语料,将新语料加入到原语料中,即可认为是对原数据集的增强。
优势:
- 操作简便;
- 质量较高
缺点:
- 重复率较高,可能无法有效增加样本的特征空间
解决办法:进行连续的多语言翻译。(根据经验,最多采用3次连续翻译)
x_sample1 = "男高中初中潮流双肩包大容量"
x_sample2 = "女学生简约商务旅行电脑背包"
y_sample1 = "小香风大衣外套炸街套装三件套"
y_sample2 = "森马毛衣女提花V领气质2022秋季新款字母毛衫"
from google_trans_new import google_translator
translator = google_translator()
translations = translator.translate([x_sample2, x_sample1, y_sample2, y_sample1],'ko')
trans_res = list(map(lambda x: x.text, translations))
print("中间翻译结果:", trans_res)
translations = translator.translate(trans_res,'cn')
res = list(map(lambda x: x.text, translations))
print("回译结果:", res)