,,pytorch实现textCNN的具体操作

,,pytorch实现textCNN的具体操作 本文主要介绍pytorch实现textCNN的具体操作过程,具有很好的参考价值,希望对大家有所帮助如有错误或不足之处,请不吝赐教

1. 原理

2014年的一篇文章开创了cnn使用文本分类的先河 用于句子分类的卷积神经网络 原理简单明了其实就是单层CNN加全连接层: 但与图像中的cnn相比,卷积核的宽度固定为一个字向量的维数,而长度一般为2、3、4、5 上面第一张图中每个单词对应的线是单词向量,可以用word2vec或者glove进行预训练在这个例子中,使用了随机初始化的向量

2. 数据预处理

手头有三个文件,分别是train.txt、valid.txt和test.txt每行都是一个字符串字典,格式为{'type':' xx ',' text ':'xxxxx

'}。

2.1 转换为csv格式

首先,将每个文件转换为csv文件,该文件分为两列:文本和标签。有四种标签可以转换成数字表示。代码如下:

#获取文件内容

def getData(文件):

f=打开(文件,“r”)

raw_data=f.readlines()

返回原始数据

#转换文件格式

def d2csv(raw_data,label_map,name):

文本=[]

标签=[]

i=0

对于raw_data中的行:

D=eval(line) #将字符串的每一行转换成字典

如果len (d ['type'])=1或len (d ['text'])=1: #过滤掉无效数据

继续

Y=label_map[d['type']] #根据label_map将标注转换为数字表示。

x=d['text']

文本.追加(x)

标签.追加(y)

i=1

如果i00==0:

打印(一)

df=pd。DataFrame({'text':texts,' label':labels})

Df.to _ csv ('data/'name ')。csv ',index=false,sep=' \ t') #保存文件

Label_map={'Execution': 0,' Criminal': 1,' Civil': 2,' Administration': 3}

train _ data=get data(' data/train . txt ')# 22000行

d2csv(列车数据,标签地图,“列车”)

valid _ data=get data(' data/valid . txt ')# 2000行

d2csv(有效数据,标签映射,'有效')

test _ data=get data(' data/test . txt ')# 2000行

d2csv(测试数据,标签映射,“测试”)

2.2 观察数据分布

对于这个任务,需要观察分词后每段文字的长度。因为每句话的长度不一样,所以需要给模型设定一个固定的长度。数据中不够长的部分被填充,多余的部分被丢弃。

训练时只有训练数据,所以观察训练数据的文本长度分布。可以用解霸分词之类的工具。

train_text=[]

对于train_data中的行:

d=评估(线)

t=jieba.cut(d['text'])

火车_文本.附加(t)

sentence _ length=[len(x)for x in train _ text]# train _ text是train.csv中每一行分词后的数据。

%matplotlib笔记本

将matplotlib.pyplot作为plt导入

plt.hist(句子长度,1000,正常=1,累积=真)

plt.xlim(0,1000)

plt.show()

得到长度分布图:

可以看出,长度小于1000的文本占所有训练数据的80%左右,所以训练时每篇文本的长度都是固定的1000字。

2.3 由文本得到训练用的mini-batch数据

目前我们手里的数据是csv形式的两列数据,一列字符串文本,一列数字标签。标签部分不再需要处理,但是文本部分离可训练数据很远。

假设每个词对应的词向量维数为D i m Dim Dim,每个样本的分段长度已知为W=1000 W=1000 W=1000,每个小批量的大小为N N N N那么我们要得到维数为n w d I m n * w * dimnwdim的浮点数据作为模型的小批量输入。

于是还需要以下几个步骤:

去掉分词的停用词,建立一个词表(词表是词到index的映射,index从0到m,m是已知词的个数,比如{ '可爱':0,'漂亮':1,…})。将分词和去除停用词后的数据转换为下标数据,维数应为n a l w n _ { all } * w nall w,n a l n _ { all }。其中长度小于w的样本用特定字符补充,长度超过w的样本被截断。将数据分成N W N * W NW大小的小批量作为模型的输入。根据mini-batch数据,通过映射到词向量,得到最终输入的n w d I m n * w * dimnwdim的大小。(这一步在模型中)

看起来很复杂,我都哭了。手动处理真的很麻烦。不过后来发现有一个和pytorch相关的包,torchtext,可以很方便的做这些步骤,所以我就介绍一下直接用这个包的做法。

在贴代码之前先贴两个torchtext的教程。如果你仍然不理解torchtext入门教程,请阅读torchtext文档。还是不明白,请直接看源代码。对照教程看下面的代码。

首先,它是一个分词函数,写成一个只有一个参数的函数:

定义标记符(x):

res=[w for w in jieba.cut(x)]

返回资源

然后停止使用这个词汇,在网上找一个停用词资源(你也可以跳过这一步):

stop_words=[]

打印(“构建停用词集”)

用open('data/stopwords.dat ')作为f:

对于f.readlines()中的l:

stop_words.append(l.strip())

然后设置两个字段,文本和标签。并定义参数的含义。参见上述文档或教程。

文本=数据。字段(sequential=True,tokenize=tokenizer,fix_length=1000,stop_words=stop_words)

标签=数据。Field(sequential=False,use_vocab=False)

读文件,分词,去掉停用词等等。直接挥手带走:

训练,有效,测试=数据。tabular dataset . splits(path=' data ',train='train.csv ',

验证='valid.csv ',测试='test.csv ',

格式='csv ',

skip_header=True,CSV _ reader _ params={ ' delimiter ':' \ t ' },

fields=[('text ',text),(' label,LABEL)])

建立词汇:

TEXT.build_vocab(火车)

以迭代器形式生成小批量数据:

train_iter,val_iter,test_iter=data。Iterator.splits((train,valid,test),

batch _ size=(args.batch_size,args.batch_size,args . batch _ size),

device=args.device

sort_key=lambda x:len(x.text),

sort_within_batch=False,

重复=假)

仅此而已!简单得可怕!虽然花了很长时间才理解这些功能。最后,这些xxx_iter将生成我们需要的维数为n w n * wnw的数据。

3. 模型

其实模型比较简单,只有一个嵌入图,一层cnn,一个激活函数,一个全连接。

但是你要注意不同大小的卷积核的写法。

您可以选择使用多个神经网络。Conv2d然后手动拼写。这里,nn。使用了ModuleList模块。实际上,它仍然使用多个Conv2d,然后将它们放在一起。

进口火炬

将torch.nn作为nn导入

导入torch.nn.functional as F

类textCNN(nn。模块):

def __init__(self,args):

超级(textCNN,self)。__init__()

self.args=args

Vocab=args.embed_num ##已知字数

Dim=args.embed_dim ##每个单词的向量长度

Cla=args.class_num ##类别数

Ci=1 ##输入的频道数

Knum=args.kernel_num ##每个卷积核的数量

Ks=args.kernel _ sizes # #卷积核列表,格式为[2,3,4]

自我。embed=nn。嵌入(词汇,dim) # #词向量,这里直接随机。

self . convs=nn . modulelist([nn . conv 2d(ci,knum,(k,dim)) for k in ks]) # #卷积层

self.dropout=nn。辍学(args.dropout)

self.fc=nn .线性(len(Ks)*Knum,Cla) ##全连接层

向前定义(自身,x):

x=self.embed(x) #(N,W,D)

x=x.unsqueeze(1) #(N,Ci,W,D)

x=[F.relu(conv(x)).为self.convs] # len(Ks)*(N,Knum,W)中的卷积神经挤压(3)

x=[F.max_pool1d(line,line.size(2)).为x] # len(Ks)*(N,Knum)中的线挤压(2)

x=torch.cat(x,1) #(N,Knum*len(Ks))

x=自我放弃(十)

logit=self.fc(x)

返回分对数

4. 训练脚本

导入操作系统

导入系统

进口火炬

进口火炬。亲笔签名

导入火炬. nn .功能为F

定义训练(训练项,开发项,模型,参数):

if args.cuda:

模型。cuda(参数。设备)

优化器=火炬。optim。亚当(模特。parameters(),lr=args.lr)

步骤=0

best_acc=0

最后一步=0

model.train()

打印('培训.')

对于范围内的纪元(1,args.epochs 1):

对于火车_iter中的批处理:

feature,target=batch.text,batch.label #(W,N) (N)

feature.data.t_()

if args.cuda:

feature,target=feature.cuda(),target.cuda()

optimizer.zero_grad()

logit=模型(特征)

loss=F.cross _熵(逻辑,目标)

loss.backward()

optimizer.step()

步骤=1

如果步骤% args.log_interval==0:

result=torch.max(logit,1)[1].视图(target.size())

更正=(结果。数据==目标。数据).总和()

准确度=校正值* 100.0/批次。批次大小

sys。stdout。写(' \ r batch[{ }]-loss:{:6f } ACC:{:4f } $({ }/{ })' .格式(步骤,

loss.data.item(),

准确性,

纠正,

batch.batch_size))

如果步骤% args.dev_interval==0:

dev_acc=eval(dev_iter,model,args)

如果开发_acc最佳_acc:

最佳_acc=开发_acc

最后一步=步数

if args.save_best:

保存(model,args.save_dir,'最佳,步骤)

否则:

如果步骤-最后一步=参数。早_停:

打印('提前{}步停止。format(args.early_stop))

否则如果步骤% args.save_interval==0:

保存(model,args.save_dir,"快照",步骤)

训练脚本中还有设置【计算机】优化程序以及失败的部分。其余部分比较琐碎的。

模型的保存:

定义保存(型号、保存目录、保存前缀、步骤):

如果不是os.path.isdir(save_dir):

os.makedirs(保存目录)

save_prefix=os.path.join(保存目录,保存前缀)

save_path='{}_steps_{} .角.格式(保存前缀,步骤)

torch.save(model.state_dict(),save_path)

评价评价函数,用来评估验证集与测试集合上的准确率acc。

def eval(data_iter,model,args):

model.eval()

校正,avg_loss=0,0

对于数据_iter中的批处理:

feature,target=batch.text批处理。标签

feature.data.t_()

if args.cuda:

feature,target=feature.cuda(),target.cuda()

logit=模型(特征)

loss=F.cross _熵(逻辑,目标)

avg_loss=loss.data[0]

result=torch.max(logit,1)[1]

修正=(结果。查看(目标。size().data==target.data).总和()

size=len(data_iter.dataset)

avg_loss /=size

准确度=100.0 *校正/尺寸

打印(' \ n评估-损失:{:6f} acc: {:4f}%({}/{}) \n ' .格式(avg_loss,accuracy,corrects,size))

返回精度

5. main函数

这暂时就不贴了。可以参考下一部分给出的github。

在最终的测试集中,准确率达到百分之九十七(毕竟只是四类)。

但是有一个问题,就是随着精度的提高,损耗也在快速增加。

经过一番研究,我大致得出结论,这是没有问题的。比如本例中是四分类,加上全连接层输出的结果是[-10000,0,0,10000],而正确的分类是0。

那么这就是一个错误的结果。计算这单个样本的损失。先计算softmax,大约等于[E20000,E10000,E10000,1E {-20000},E {-10000},E {-10000},1E20000,E10000,E10000,E10000,1]。的真实标签是[1,0,0,0],所以交叉熵是20000。

于是我们发现,这个错题的损失会这么大。最后损失较大也是正常的。

然而,为什么当准确率接近100%时,损耗会迅速增加,这需要进一步研究。很可能是因为随着准确率的提高,结果越来越接近训练集的分布,这样与验证集或测试集分布差异极大的情况就会越来越多。

6.引用

部分代码参考了很多这位老哥的github,在此致谢。和他不一样的主要是数据处理部分。

以上个人经历,希望能给大家一个参考,也希望大家多多支持我们。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • word文档无法编辑是怎么回事-
  • 华为手机怎么设置返回键(华为手机下面的三个按键设置方法)
  • lumia950怎么样(Lumia950体验分享)
  • otg连接是什么意思(OTG连接手机方法)
  • 笔记本触摸板怎么右键(笔记本电脑触控板手势操作设置)
  • 真我x7怎么样(realme X7 系列体验)
  • 苹果的A16处理器有多强(苹果的A16处理器的介绍)
  • 小米互传怎么用(小米手机的连接与共享教程)
  • 怎么设置电脑桌面图标自动对齐 设置电脑桌面自动整理图标的方法
  • 宽带错误651最简单解决方法(处理宽带错误651的措施)
  • 大学生手机有什么推荐(大学生换手机攻略)
  • 天玑1100和骁龙778g哪个好(骁龙778G、天玑900、天玑1100购选建议)
  • yum update 升级报错的解决办法
  • Windows10禁用屏保教程
  • 连接wifi显示无互联网连接怎么办(无线连上了却不能上网处理绝招)
  • 留言与评论(共有 条评论)
       
    验证码: