人工智能之玩转NLP

文本预处理的主要环节
文本处理基本方法
文本张量表示方法
文本语料的数据分析
文本特征处理
数据增强的方法

文本预处理方法

分词
词性标注
命名实体识别

文本张量的表示方法

one—hot 编码(不能计算不同单词之间的相似度)
Word2vd
Embedding

文本语料的数据分析

标签数量分布
句子长度分布
词频统计与关键词词云

文本特征处理

添加n—gram特征
文本长度规范

数据增强方法

回译数据增强法

文本预处理的方法

jieba分词

jieba.lcut()将生成器对象以列表的形式返回
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在jieba.lcut(文本)的默认情况下,是精确模式分词

import jieba

content = “工信处女干事每月经过下属科室都要亲口交代24口交换机等技术型器件”
print(jieba.cut(content,cut_all=False))
print(jieba.lcut(content,cut_all=False))

#更多的详情见上方代码图片

流行的中英文分词工具hanlp

  • 环境安装,Anaconda Prompt pip install hanlp
    然后切换pytorch环境再安装一下(我用的是pytorch里的python编译器)
  • hanlp处理的都是列表形式,如果文本不是列表形式的话,用list给解析一下
  • 本小节所使用的部分代码,由于篇幅较长,所以在图片选择的时候,没有选择全部 在敲代码的时候一定要看清括号的嵌套。

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

词性标注

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

文本张量的表示

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

one-hot编码实现

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

word2vec的实现

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

在这里插入图片描述
在这里插入图片描述
总的来说,CBOW就是用周围的词语来预测中间的词语,在整段语料上,选取窗口大小的语料,计算,并筛选最合适的,然后依次向后进行,直到完成所有语料的遍历。
skipgram则是使用中间的词语来预测前后词语的训练方式。

word2vec代码实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上数据加载是在Linux系统下的命令行实现的,接下来讲如何使用Python进行训练。
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

代码

import fasttext

model = fasttext.train_unsupervised('路径') #设置模型超参数
model.get_nearest_neighbors('某个词')# 查询最近的词向量
model.get_word_vector("the")#获取某个词向量
model.save_model("文件名.bin")#模型保存
model = fasttext.load_model("文件名.bin")#模型重加载


# 模型超参数的设定
model = fasttext.train_unsupervised('路径',"cbow", dim = 300, epoch = 1, lr = 0.1, thread = 8)#无监督训练的方式,dim词嵌入向量维度,epoch总体的数据集循环几轮,初始学习率lr,线程thread

# 模型效果检验
model.get_nearest_neighbors('cat')

#模型保存与重加载
model.save_model("文件名.bin")
model = fasttext.load_model("文件名.bin")

词嵌入

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

文本张量表示的小结

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

HMM模型与CRF模型

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

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

RNN模型

概念

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

实例

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

RNN模型的分类

在这里插入图片描述

N-N

在这里插入图片描述

N-1

在这里插入图片描述

1-N

在这里插入图片描述

N-M(seq2seq架构)

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

RNN小结

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

传统RNN模型

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

在代码中体现

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


import torch
import torch.nn as nn

# 实例化RNN对象
#第一个参数:input_size(输入张量x的维度)
#第二个参数:hidden_size(隐藏层的维度,隐藏层神经元的数量)
#第三个参数:num_layers(隐藏层的层数)
rnn = nn.RNN(input_size, hidden_size, num_layers)

#设定输入的张量x
# seq_len:输入序列的长度。batch_size:批次的样本数。input_size:输入张量x的维度
input = torch.randon(seq_len, batch_size, input_size)

#设定初始化的h0
#第一个参数:num_layers*num_directions(层数*网络方向数)
#batch_size:批次的样本数
#  hidden_size:隐藏层的维度,隐藏层神经元的数量
h0 = torch.randn(num_layers, batch_size, hidden_size)

#输入张量放入RNN中,得到输出结果
output, hn = rnn(input1, h0)

print(output)
print(output.shape)
print(hn)
print(hn.shape)

LSTM模型

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

遗忘门部分结构图

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

两个输入: 当前时间步的输入xt 与 上一个时间步隐含状态h(t-1)拼接 得到[xt,h(t-1)]经过全连接层与sigmoid函数得到f
在这里插入图片描述在这里插入图片描述
在RNN中,tanh的作用是将流经网络的值压缩为[-1,1]之间
而在LSTM中,sigmoid函数的作用是将流经网络中的值压缩在[0,1]之间

输入门部分结构图

在这里插入图片描述
在这里插入图片描述
将拼接后的值复制两份,然后一个进入sigmoid函数,一个进入tanh函数

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

将刚刚得到遗忘门门值与上一个时间步得到的C(t-1)相乘,再加上输入门门值与当前时间步得到的未更新C(t)相乘的结果

输出门部分结构图

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

计算输入门的门值,将拼接后的放入sigmoid函数里。再跟进入tanh函数的细胞状态做乘法,最后将二者进行乘积

Bi-LSTM

在这里插入图片描述
Bi-LSTM的第一个h0是将正向的LSTM的h0与反向LSTM最后一个hn拼接得到的

在这里插入图片描述

在Pytorch中使用LSTM

在这里插入图片描述

# 定义LSTM的参数含义: (input_size, hidden_size, num_layers)
# 定义输入张量的参数含义:(sequence_length, batch_size, input_size)
# 定义隐藏层初始张量和细胞初始状态张量的参数含义:
# (num_layers * num_directions, batch_size, hidden_size)
import torch.nn as nn
import torch
lstm = nn.LSTM(5, 6, 2)
input = torch.randn(1, 3, 5)
h0 = torch.randn(2, 3, 6)
c0 = torch.randn(2, 3, 6)
output, (hn, cn) = rnn(input, (h0, c0))


# 实例化LSTM对象
# 第一个参数:input_size(输入张量x的维度)
# 第二个参数:hidden_size(隐藏层的维度,隐藏层的神经元的数量)
# 第三个参数: num_layers(隐藏层的层数)
lstm = nn.LSTM(5,6,2)

# 初始化输入张量x
# 第一个参数:sequence_length(输入序列的长度)
# 第二个参数:batch_size(批次的样本数量)
# 第三个参数:input_size(输入张量x的维度)
input1 = torch.randn(1,3,5)

# 初始化隐藏层张量h0和细胞状态c0
# 第一个参数: num_layers * num_directions(隐藏层的层数*方向数)
# 第二个参数: batch_size(批次的样本数量)
# 第三个参数: hidden_size(隐藏层的维度,隐藏层的神经元的数量)
h0 = torch.randn(2, 3, 6)
c0 = torch.randn(2, 3, 6)

# 将input1, h0, c0,输入lstm中,得到输出张量结果
output, (hn, cn) = rnn(input, (h0, c0))
print(output)
print(output.shape)
print(hn)
print(hn.shape)
print(cn)
print(cn.shape)

LSTM优劣对比

在这里插入图片描述

GRU模型

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

总体来说,两个门四个计算公式。两个输入分别是上一个时间步的隐藏层的输出h(t-1)与当前时间步的输入xt,做一个拼接然后复制两份,一份进入sigmoid函数作为更新门zt,另一部分也进入sigmoid函数作为重置门rt

GRU代码

#跟前面不能说一模一样,只能说一模一样
import torch
import torch.nn as nn

rnn = nn.GRU(5, 6, 2)
input = torch.randn(1,3,5)
h0 = torch.randn(2,3,6)
output,hn = rnn(input,h0)

# 实例化GRU对象
# 第一个参数:input_size(输入张量x的维度)
# 第二个参数:hidden_size(隐藏层的维度,隐藏层的神经元的数量)
# 第三个参数: num_layers(隐藏层的层数)
gru = nn.GRU(5,6,2)

# 初始化输入张量x
# 第一个参数:sequence_length(输入序列的长度)
# 第二个参数:batch_size(批次的样本数量)
# 第三个参数:input_size(输入张量x的维度)
input1 = torch.randn(1,3,5)

# 初始化隐藏层张量h0
# 第一个参数: num_layers * num_directions(隐藏层的层数*方向数)
# 第二个参数: batch_size(批次的样本数量)
# 第三个参数: hidden_size(隐藏层的维度,隐藏层的神经元的数量)
h0 = torch.randn(2, 3, 6)


# 将input1, h0,输入GRU中,得到输出张量结果
output, hn = gru(input1, h0)
print(output)
print(output.shape)
print(hn)
print(hn.shape)

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

注意力机制

注意力机制概念

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

注意力机制的代码实现

import torch
import torch.nn as nn
import torch.nn.functional as F

class Attn(nn.Module):
	def __init__(self, query_size, key_size, value_size1, value_size2, output_size):
	# query_size代表的是Q的最后一个维度,key_size代表的是K的最后一个维度
	# V的尺寸表示(1,value_size1,value_size2)
	# output_size代表输出的最后一个维度的大小
		super(Attn, self).__init__()
		self.query_size = query_size
		self.ket_size = key_size
		self.value_size1 = value_size1
		self.value_size2 = value_size2
		self,output_size = output_size

	# 初始化注意力机制实现中的第一步的线性层
		self.attn = nn.Linear(self.query_size + self.key_size, self.value_size1)

	# 初始化注意力机制实现中第三步的线性层
		self.attn_combine = nn.Linear(self.query_size + self.value_size2, self.output_size)

	def forward(self, Q, K, V):
		# 注意我们假定Q,K,V都是三维张量
		# 第一步,将Q,K进行纵轴的拼接,然后做一次线性变换,最后使用softmax进行处理得到注意力向量
		attn_weights = F.softmax(self.attn(torch.cat((Q[0],K[0]),1)),dim = 1)

		#将注意力矩阵和V进行一次bmm运算
		attn_applied = torch.bmm(attn_weights.unsqueeze(0), V)

		#再次去Q[0]进行降维,再次和上面的运算结果进行一次拼接
		output = torch.cat((Q[0], attn_applied[0]),1)

		#第三步就是将上面的输出进行一次线性变换,然后再扩展维度成3维张量
		output = self.attn_combine(output).unsqueeze(0)
		return output, attn_weights


query_size = 32
key_size = 32
value_size1 = 32
value_size2 = 64
output_size = 64

attn = Attn(query_size, value_size1, value_size2, output_size)
Q = torch.randn(1,1,32)
K = torch.randn(1,1,32)
V = torch.randn(1,32,64)
output = attn(Q, K, V)
print(output[0])
print(output[0].size())
print(output[1])
print(output[1].size())

注意力机制小结

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

Transformer的优势

在这里插入图片描述

总体架构图

在这里插入图片描述
在这里插入图片描述
== 左边为源文本,右边Outputs为目标文本。==
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

输入部分的实现(包括代码)

在这里插入图片描述

Embedding层
import torch
import torch.nn as nn
import math
from torch.autograd import Variable


'''
# 定义Embeddings类来实现文本嵌入层,这里s说明代表两个一模一样的嵌入层,他们共享参数,
# 该类继承nn.Moudule,这样就有标准层的一些功能,这里我们也可以理解为一种模式,我们自己实现所有层
class Embeddings(nn.Module):
	def __init__(self, d_model, vocal):
		# 类的初始化函数,有两个参数,d_model:指词嵌入的维度,vocab:指词表的大小
		#接着就是用super()方式来指明继承nn.Moudle的初始化函数,我们自己实现所有的层都会这样去定义
		super(Embeddings,self).__init__()
		#之后就是调用nn中的预定义层Embedding,获得一个词嵌入对象self.lut
		self.lut = nn.Embedding(vocab, d_model)
		# 最后就是将d_model传入类中
		self.d_model = d_model
	
	def forward(self, x):
		# 可以将其理解为该层的前向传播逻辑,所有层中都会有此函数
		# 当传给该类的实例化对象参数时,自动调用该类函数
		# 参数x:因为Embedding层是首层,所以代表输入给的墨香的文本通过词汇映射后的张量

		# 将x传给self.lut并与根号下self.d_model相乘作为结果返回
		return self.lut(x) * math.sqrt(self.d_model)

'''




# 构建Embedding类来实现文本嵌入层
class Embeddings(nn.Module):
	def __init__(self, d_model, vocab):
		# d_model:词嵌入的维度
		# vocab:词表的大小
		super(Embeddings, self).__init__()
		# 定义Embedding层
		self.lut = nn.Embedding(vocab, d_model)
		# 参数传入类中
		self.d_model = d_model

	def forward(self, x):
	# x:代表输入进模型文本通过词汇映射后的数字张量
		return self.lut(x) * math.sqrt(self.d_model)
d_model = 512
vocab = 1000

x = Variable(torch.LongTensor([[100, 2, 421, 508],[491, 998, 1, 221]]))

emb = Embeddings(d_model, vocab)
embr = emb(x)
print("embr:", embr)
print(embr.shape)

位置编码器

在这里插入图片描述

# 定义位置编码器,我们同样把它看作一个层,因此会继承nn.Moudle
class PositionalEncoding(nn.Module):
	def __init__(self, d_model, dropout, max_len = 5000):
	'''位置编码器类的初始化函数,共有三个参数,分别是
		d_model:词嵌入维度,
		dropout:置0比率,
		max_len:每个句子的最大长度'''
		super(PositionalEncoding, self).__init__()
	# 实例化nn中预定义的Dropout层,并将dropout传入其中,获得对象self.dropout
		self.dropout = nn.Dropout(p = dropout)
	# 初始化一个位置编码矩阵,他是一个0阵,矩阵的大小是max_len * d_model
		pe = torch.zeros(max_len, d_model)
	# 初始化一个绝对位置矩阵,在我们这里,词汇的绝对位置就是用它的索引去表示
	# 所以我们首先使用arange方法获得一个连续自然数向量,然后再使用unsqueeze方法拓展向量维度
	# 又因为参数传的是1,代表矩阵拓展的位置,会使向量变成一个max_len * 1的矩阵
		position = torch.arange(0,max_len).unsqueeze(1)
	# 绝对位置矩阵初始化之后,接下来就是考虑如何将这些位置信息加入到位置编码矩阵中,
	# 最简单的思路就是先将max_len x 1的绝对位置矩阵,变换成max_len x d_model形状,然后覆盖原来的初始位置编码矩阵即可,
	# 要做这种矩阵变换,就需要一个1xd_model形状的变换矩阵div_term,我们对这个变换矩阵的要求除了形状外,
	# 还希望它能够将自然数的绝对位置编码缩放成足够小的数字,有助于在之后的梯度下降过程中更快的收敛,这样我们就可以开始初始
	# 首先使用arange获得一个自然数矩阵,但是会发现,我们这里并没有按照预计的一样初始化一个1 * d_model的矩阵,
	# 而是有了一个跳跃,只初始化了一半即1xd_model/2的矩阵。为什么是一半呢,其实这里并不是真正意义上的初始化了一半的矩阵,
	# 我们可以把它看作是初始化了两次,而每次初始化的变换矩阵会做不同的处理,第一次初始化的变换矩阵分布在正弦波上,第二次初始化
	# 并把这两个矩阵分别填充在位置编码矩阵的偶数和奇数位置上,组成最终的位置编码矩阵
		div_term = torch.exp(torch.arange(0, d_model, 2) *  (math.log(10000.0) / d_model))
		pe[:, 0::2] = torch.sin(position * div_term)
		pe[:, 1::2] = torch.cos(position * div_term)
	# 这样我们就得到了位置编码矩阵pe,pe现在还只是一个二维矩阵,要想和embedding的输出(一个#就必须拓展一个维度,所以这里使用unsqueeze拓展维度.
		pe = pe.unsqueeze(0)
	#最后把pe位置编码矩阵注册成模型的buffer,什么是buffer呢,
	#我们把它认为是对模型效果有帮助的,但是却不是模型结构中超参数或者参邀,不需要随着优化步骤
	#注册之后我们就可以在模型保存后重加载时和模型结构与参数一同被加载.
		self.register_buffer('pe', pe)
	def forward(self, ×):
	"“forward函数的参数是x,表示文本序列的词嵌入表示"""
	#在相加之前我们对pe做一些适配工作,将这个三维张量的第二维也就是句子最大长度的那一维将切
	#因为我们默认max_len为5000一般来讲实在太大了,很难有一条句子包含5000个词汇,所以要进行
	#最后使用Variable进行封装,使其与x的样式相同,但是它是不需要进行梯度求解的,因此把request
		X = × + Variable(self.pe[:,:x.size(1)],requires_grad=False)
	#最后使用self.dropout对象进行'丢弃'操作,并返回结果.
		return self.dropout(×)	
nn.Dropout演示
m = nn.Dropout(p = 0.2)
input1 = torch.randn(4, 5)
output = m(input1)
# print(output)
x = torch.tensor([1, 2, 3, 4])
y = torch.unsqueeze(x, 0)
print(y)
z = torch.unsqueeze(x, 1)
print(z)
torch.unsqueeze演示
x = torch.tensor([1, 2, 3, 4])
y = torch.unsqueeze(x, 0)
print(y)
#输出结果为 tensor([[ 1, 2, 3, 4]]),这是x在第0个维度上扩展
z = torch.unsqueeze(x, 1)
print(z)
#输出结果为tensor([[ 1],
#			[2],
#			[3],
#		    [4]])这是x在列的维度上扩展


构建位置编码器代码

构建位置编码器的类

class PositionalEncoding(nn.Moudle):
	def __init__(self, d_model, dropout, max_len = 5000):
		# d_model: 代表词嵌入的维度
		# dropout: 代表Dropout层的置零比率
		# max_len: 代表句子的最大长度
		super(PositionalEncoding,self).__init__()

		# 实例化Dropout层
		self.dropout = nn.Dropout(p = dropout)

		# 初始化一个位置编码矩阵,大小是max_len * d_model
		pe = torch.zeros(max_len, d_model)

		# 初始化一个位置编码矩阵, 大小是max_len * d_model
		pe = torch.zeros(max_len, d_model)

		#初始化一个绝对位置矩阵
		position = torch.arange(0,max_len).unsqueeze(1)

		#定义一个变换矩阵div_term,跳跃式的初始化
		div_term = torch.exp(torch.arange(0, d_model,2) * -(math.log(10000.0) / d_model))
		
		#将前面定义的变换矩阵进行奇数,偶数的分别赋值
		pe[:,0::2] = torch.sin(position * div_term)
		pe[:,1::2] = torch.cos(position * div_term)

		# 将二维张量扩充成三维张量
		pe = pe.unsqueeze(0)

		# 将位置编码矩阵注册成模型的buffer,这个buffer不是模型中的参数,不跟随优化器同步更新
		# 注册成buffer后我们就可以在模型保存后重新加载的时候,将这个位置编码器和模型参数一同加载进来
		self.register_buffer('pe',pe)

def forward(self, x):
	# x:代表文本序列的词嵌入表示
	# 首先明确pe的编码太长了,将第二个维度,也就是max_len对应的那个维度缩小成x的句子长度同等的长度
	x = x + Variable(self.pe[:,:x.size(1)], requires_grad=False)
	return self.dropout(x)

d_model = 512
dropout = 0.1
max_len = 60 

x = embr
pe = PositionalEncoding(d_model, dropout, max_len)
pe_result = pe(x)
print(pe_result)
print(pe_result.shape)	

编码器部分的实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值