网站栏目规划,杭州建设厅网站,网络专题的设计策划方案,wordpress 新建表插件今天学了第一个基于Pytorch框架的NLP任务#xff1a;
判断文本中是否出现指定字
思路#xff1a;#xff08;注意#xff1a;这是基于字的算法#xff09;
任务#xff1a;判断文本中是否出现“xyz”#xff0c;出现其中之一即可
训练部分#xff1a;
一#xff…今天学了第一个基于Pytorch框架的NLP任务
判断文本中是否出现指定字
思路注意这是基于字的算法
任务判断文本中是否出现“xyz”出现其中之一即可
训练部分
一我们要先设计一个模型去训练数据。 这个Pytorch的模型 首先通过embedding层将字符转化为离散数值矩阵 通过线性层设置网络的连接层进行映射 通过dropout层将一部分输入设为0可去掉 通过激活层sigmoid激活 通过一个pooling层降维将矩阵-向量 通过另一个输出线性层使输出是一维1或0 通过一个激活层*sigmoid激活。
二设置一个函数这个函数能将设定的字符变成字符集将每一个字符设定一个代号比如说“我爱你”- 我1爱2你3。当出现你爱我时计算机接受的是321。这样方便计算机处理字符。
三因为我们没有训练样本和测试样本所以我们要自己生成一些随机样本。通过random.choice在字符集中随机顺序输出字符作为输入并将输入中含有xyz的样本的输出值为“1”反之为“0”
四设置一个函数将随机得到的样本放入数据集中列表便于运算。
五设置测试函数随机建立一些样本根据样本的输出来设定有多少个正样本多少个负样本再将预测的样本输出来与样本输出对比得到正确率。
六最后的main函数按照训练轮数和训练组数通过BP反向传播更新权重进行训练。然后调取测试函数得到acc等数据。将loss和acc的数值绘制下来保存模型和词表。
预测部分
将保存的词表和模型加载进来将输入的字符转化为列表然后进入模型forward函数进行预测最后打印出结果。
代码实现
import torch
import torch.nn as nn
import numpy as np
import random
import json
import matplotlib.pyplot as plt
基于pytorch的网络编写
实现一个网络完成一个简单nlp任务
判断文本中是否有某些特定字符出现
class TorchModel(nn.Module):def __init__(self, input_dim, sentence_length, vocab):super(TorchModel, self).__init__()self.embedding nn.Embedding(len(vocab) 1, input_dim) #embedding层将字符转化为离散数值self.layer nn.Linear(input_dim, input_dim) #对输入数据做线性变换self.classify nn.Linear(input_dim, 1) #映射到一维self.pool nn.AvgPool1d(sentence_length) #pooling层降维self.activation torch.sigmoid #sigmoid做激活函数self.dropout nn.Dropout(0.1) #一部分输入为0self.loss nn.functional.mse_loss #loss采用均方差损失#当输入真实标签返回loss值无真实标签返回预测值def forward(self, x, yNone):x self.embedding(x)#输入维度:(batch_size, sen_len)输出维度(batch_size, sen_len, input_dim)将文本-矩阵x self.layer(x)#输入维度:(batch_size, sen_len, input_dim)输出维度(batch_size, sen_len, input_dim)x self.dropout(x)#输入维度:(batch_size, sen_len, input_dim)输出维度(batch_size, sen_len, input_dim)x self.activation(x)#输入维度:(batch_size, sen_len, input_dim)输出维度(batch_size, sen_len, input_dim)x self.pool(x.transpose(1,2)).squeeze()#输入维度:(batch_size, sen_len, input_dim)输出维度(batch_size, input_dim)将矩阵-向量x self.classify(x)#输入维度:(batch_size, input_dim)输出维度(batch_size, 1)y_pred self.activation(x)#输入维度:(batch_size, 1)输出维度(batch_size, 1)if y is not None:return self.loss(y_pred, y)else:return y_pred#字符集随便挑了一些汉字实际上还可以扩充
#为每个汉字生成一个标号
#{不:1, 东:2, 个:3...}
#不东个-[1,2,3]
def build_vocab():chars 不东个么买五你儿几发可同名呢方人上额旅法xyz #随便设置一个字符集vocab {}for index, char in enumerate(chars):vocab[char] index 1 #每个字对应一个序号,1是序号从1开始vocab[unk] len(vocab)1 #不在表中的值设为前一个1return vocab#随机生成一个样本
#从所有字中选取sentence_length个字
#如果vocab中的xyz出现在样本中则为正样本
#反之为负样本
def build_sample(vocab, sentence_length):#将vacab转化为字表随机从字表选取sentence_length个字可能重复x [random.choice(list(vocab.keys())) for _ in range(sentence_length)]#指定哪些字必须在正样本出现if set(xyz) set(x): #若xyz与x中的字符相匹配则为1为正样本y 1else:y 0x [vocab.get(word,vocab[unk]) for word in x] #将字转换成序号return x, y#建立数据集
#输入需要的样本数量。需要多少生成多少
def build_dataset(sample_length,vocab, sentence_length):dataset_x []dataset_y []for i in range(sample_length):x, y build_sample(vocab, sentence_length)dataset_x.append(x)dataset_y.append([y])return torch.LongTensor(dataset_x), torch.FloatTensor(dataset_y)#建立模型
def build_model(vocab, char_dim, sentence_length):model TorchModel(char_dim, sentence_length, vocab)return model#测试代码
#用来测试每轮模型的准确率
def evaluate(model, vocab, sample_length):model.eval()x, y build_dataset(200, vocab, sample_length)#建立200个用于测试的样本因为测试样本是随机生成的所以不存在过拟合print(本次预测集中共有%d个正样本%d个负样本%(sum(y), 200 - sum(y)))correct, wrong 0, 0with torch.no_grad():y_pred model(x) #调用Pytorch模型预测for y_p, y_t in zip(y_pred, y): #与真实标签进行对比if float(y_p) 0.5 and int(y_t) 0:correct 1 #负样本判断正确elif float(y_p) 0.5 and int(y_t) 1:correct 1 #正样本判断正确else:wrong 1print(正确预测个数%d, 正确率%f%(correct, correct/(correctwrong)))return correct/(correctwrong)def main():epoch_num 10 #训练轮数batch_size 20 #每次训练样本个数train_sample 1000 #每轮训练总共训练的样本总数char_dim 20 #每个字的维度sentence_length 10 #样本文本长度vocab build_vocab() #建立字表model build_model(vocab, char_dim, sentence_length) #建立模型optim torch.optim.Adam(model.parameters(), lr0.005) #建立优化器log []for epoch in range(epoch_num):model.train()watch_loss []for batch in range(int(train_sample / batch_size)):x, y build_dataset(batch_size, vocab, sentence_length) #每次训练构建一组训练样本optim.zero_grad() #梯度归零loss model(x, y) #计算losswatch_loss.append(loss.item()) #将loss存下来方便画图loss.backward() #计算梯度optim.step() #更新权重print(\n第%d轮平均loss:%f % (epoch 1, np.mean(watch_loss)))acc evaluate(model, vocab, sentence_length) #测试本轮模型结果log.append([acc, np.mean(watch_loss)])plt.plot(range(len(log)), [l[0] for l in log]) #画acc曲线蓝色的plt.plot(range(len(log)), [l[1] for l in log]) #画loss曲线黄色的plt.show()#保存模型torch.save(model.state_dict(), model.pth)writer open(vocab.json, w, encodingutf8)#保存词表writer.write(json.dumps(vocab, ensure_asciiFalse, indent2))writer.close()return#最终预测
def predict(model_path, vocab_path, input_strings):char_dim 20 # 每个字的维度sentence_length 10 # 样本文本长度vocab json.load(open(vocab_path, r, encodingutf8))model build_model(vocab, char_dim, sentence_length) #建立模型model.load_state_dict(torch.load(model_path)) #将模型文件加载进来x []for input_string in input_strings: #转化输入x.append([vocab[char] for char in input_string])model.eval() #在torch中预测注意这个停止dropoutwith torch.no_grad(): #在torch中预测注意这个停止梯度result model.forward(torch.LongTensor(x)) #根据自己设计的函数定义只输入x就会输出预测值for i, input_string in enumerate(input_strings):print(round(float(result[i])), input_string, result[i])#round(float(result))是将预测结果四舍五入得到0或1的预测值if __name__ __main__:main()#如果是进行预测将下面两行解除注释将main()注释掉即可调用最终预测函数进行预测# test_strings [个么买不东五你儿x发, 不东东么儿几买五你发, 不东个么买五你个么买, 不z个五么买你儿几发]# predict(model.pth, vocab.json, test_strings)运行结果展示
训练部分 蓝色是acc曲线黄色是loss曲线
预测部分 一些补充
一model.eval()或者model.train()的作用
如果模型中有BN层(Batch Normalization和Dropout需要在训练时添加model.train()在测试时添加model.eval()。其中model.train()是保证BN层用每一批数据的均值和方差而model.eval()是保证BN用全部训练数据的均值和方差而对于Dropoutmodel.train()是随机取一部分网络连接来训练更新参数而model.eval()是利用到了所有网络连接。
二Pytorch模型中用了两次激活函数
在每一个网络层后使用一个激活层是一种比较常见的模型搭建方式但不是必要的。这个只是举例去掉也是可行的。在具体任务中带着好还是不带好也跟数据和任务本身有关没有确定答案如果在代码 中把第一个激活层注释掉 反而性能更好
三对x self.pool(x.transpose(1,2)).squeeze()代码的解读
通过shape方法我们能知道在pool前x的维度输出是[20,10,20]代表20个10×20的矩阵代表着[这一批的个数样本文本长度输入维度]transpose(1,2)是将x中行和列调换转置然后通过pooling层将[20,20,10]-[20,20,1]最后通过squeeze()进行降维变成[20,20]。 池化层的作用及理解
四embedding层的理解
embedding层并不是单纯的单词映射而是将单词表中每个单词的数值与权重相乘。在第一次时有默认权重然后在接下来的训练中embedding层的权重与分类权重一起经过训练。