赞
踩
内容:使用bert进行新闻文本分类,
目的:熟悉预训练模型的使用过程以及数据处理,和模型的各个接口,输入输出,做到对bert的简单使用
环境:windows,pytorch,transformer,sklearn这些库都需要自行下载,
另外,文章字不多,所有解释都在代码的注释中,基本每一行都有注释,我也手撕过bert、transformer代码,有时间或者有用的话也写出来分享给大家。
文章参考:https://blog.csdn.net/weixin_45552370/article/details/119788420
本人是研一学生,知识图谱方向,深度学习小白,对transformer以及bet等的理解并不深,这是在学习过程中写的,一方面给自己加深印象,另一方面,我也深知中茫然不知怎么学的痛苦迷惘,希望对有需要的人能有一点点帮助。
我深知,文章中可能有些错误或者理解不到位的地方,倘若您能指出,我将非常感谢。
写文章的过程中(其实是写注释的过程中),有过放弃的想法,心想我自己看明白了就行啊,为什么还要写明白,分享给别人,又不能卖钱,但回想自己在学习过程中的遇到不会的地方的迷茫,看到好文章的惊喜,才让我有了写完的动力,
尽管只是一个小知识点,一篇不超过一万字(而且主要是代码)的短文,对我自己确是一种成长。
感谢那些无私分享知识的同志,这也许就是互联网的精神吧,第一次发文,如果你看完本文能有用些许收获。欢迎点赞评论,对我并无利益可言
只是希望能让有需要的人看到。**自然如果我哪里写的不正确或者有偏差,非常非常欢迎您的批评指正。**
新闻短文本数据集 链接: 新闻短文本数据集.
!!!更新数据集地址网盘,密码:upup
# 加深理解,注释都是非常简单的,非常详细,一是希望自己能够彻底理解处理流程, # 二是希望基础薄弱的读着看完能有所收获 # 来自:https://blog.csdn.net/weixin_45552370/article/details/119788420 import torch # pytor库,必用 import pandas as pd # pandas库是一个处理各种数据文件的库,类似于wps,可以打开,保存各种word,ppt等格式的文件 import torch.nn as nn #导入nn,这个库里面是一些全连接网络,池化层,这种神经网络中非常基本的一些模块,这里我们主要是用nn.linear是一个全连接层 from transformers import BertModel, BertTokenizer# transformer库是一个把各种预训练模型集成在一起的库,导入之后,你就可以选择性的使用自己想用的模型,这里使用的BERT模型。所以导入了bert模型,和bert的分词器,这里是对bert的使用,而不是bert自身的源码,如果有时间或者兴趣的话我会在另一篇文章写bert的源码实现 from sklearn.model_selection import train_test_split #sklearn是一个非常基础的机器学习库,里面都是一切基础工具,类似于聚类算法啊,逻辑回归算法啊,各种对数据处理的方法啊,这里我们使用的train_test_split方法,是把数据,一部分用作训练,一部分用作测试的划分数据的方法 # 第一部分 # 加载训练集,第一个参数文件位置,默认会以空格划分每行的内容, # delimier参数设置的备选用制表符划分, # 第三个参数 是不满足要求的行舍弃掉,正常情况下,每行都是一个序号,一个标签,一段文本, # 如果不是这样的,我们就舍弃这一行 train_set = pd.read_csv("./data/train.tsv",delimiter="\t",error_bad_lines=False) #model_name = "hfl/chinese-bert-wwm" #我们是要使用bert模型,但是bert也有很多模型,比如处理英文的,处理法语的,我们这里使用处理中文,且全mask的方法,感兴趣可以看这里了解https://github.com/ymcui/Chinese-BERT-wwm,另外,如果手码代码出错了,可能是因为字符串打错了,fhl而不是hf1,是L而不是数字1 # 下面我们就获得了bert模型中的hfl/chinese-bert-wwm模型的模型以及模型分词方法,这里是原始模型,我们要使用微调,所以下面自写类 # tokenizer = BertTokenizer.from_pretrained(model_name) # model = BertModel.from_pretrained(model_name) # 第二部分 # 采用bert微调策略,在反向传播时一同调整BERT和线性层的参数,使bert更加适合分类任务 # bert微调,在这里 就是bert(类的前四行)+一个全连接层(第五行 self.fc = nn.Linear(768,15))组成一个全新模型, class BertClassfication(nn.Module):#括号里面是继承什么,该类继承自nn.module,因为我们的模型在根本上是神经网络的,所以继承nn.modelu,继承它的基本属性是自然的了 def __init__(self): # 前四行就是bert,第五行是全连接层,即bert微调,就是本文要使用的模型 super(BertClassfication,self).__init__() self.model_name = 'hfl/chinese-bert-wwm' self.model = BertModel.from_pretrained(self.model_name) self.tokenizer = BertTokenizer.from_pretrained(self.model_name) self.fc = nn.Linear(768,15) #768取决于BERT结构,2-layer, 768-hidden, 12-heads, 110M parameters def forward(self,x):#这里的输入x是一个list,也就是输入文本x:RNG全队梦游失误频频不敌FW,后续淘汰赛成绩引人担忧,我这里是用一句话举例子,实际上的数据是很多很多句话(哈哈,好不专业,很多很多) # 这句话是对文本1.进行分词,2.进行编码可以这里理解一下https://www.cnblogs.com/zjuhaohaoxuexi/p/15135466.html # 第一个参数x自然也就是文本了 # 第二个参数add_special_token,就是说要不要加入一些特殊字符,比如标记开头的cls,等等 # 第三个参数就是最长长度, # 第四个参数是pad_to_max_len:就是说如果不够max_len的长度,就补长到最大长度 # 还有一个参数这里没用到,就是比最长的长久减掉,第四第五个参数也就是长的减,短的补 batch_tokenized = self.tokenizer.batch_encode_plus(x, add_special_tokens=True, max_length=148, pad_to_max_length=True) #tokenize、add special token、pad # 可以看到上一步的结果是好几个维度,(建议一遍写代码,一遍在jupyter里面调试,看看每一步的结果或者是什么形式) # 取出input_ids:这是对汉语的编码 # attention_mask:这是对每个字是否mask的一个标记,原本的词的位置是1,如果由于词长不够max_len,用pad或者其他的填充了,该位置就是0,意味着在模型中不注意他,因为该位置是填充上的没有意义的字符,我们为什么要管他呢? input_ids = torch.tensor(batch_tokenized['input_ids']) attention_mask = torch.tensor(batch_tokenized['attention_mask']) # 把上边两个输入bert模型,得到bert最后一层的隐藏层的输出, hiden_outputs = self.model(input_ids,attention_mask=attention_mask) # bert的输出结果有四个维度: # last_hidden_state:shape是(batch_size, sequence_length, hidden_size),hidden_size=768,它是模型最后一层输出的隐藏状态。 # pooler_output:shape是(batch_size, hidden_size),这是序列的第一个token(classification token)的最后一层的隐藏状态,它是由线性层和Tanh激活函数进一步处理的。(通常用于句子分类,至于是使用这个表示,还是使用整个输入序列的隐藏状态序列的平均化或池化,视情况而定) # hidden_states:这是输出的一个可选项,如果输出,需要指定config.output_hidden_states=True,它也是一个元组,它的第一个元素是embedding,其余元素是各层的输出,每个元素的形状是(batch_size, sequence_length, hidden_size) # attentions:这也是输出的一个可选项,如果输出,需要指定config.output_attentions=True,它也是一个元组,它的元素是每一层的注意力权重,用于计算self-attention heads的加权平均值。 # 我们是微调模式,需要获取bert最后一个隐藏层的输出输入到下一个全连接层,所以取第一个维度,也就是hiden_outputs[0] # 此时shape是(batch_size, sequence_length, hidden_size),[:,0,:]的意思是取出第一个也就是cls对应的结果,至于为什么这样操作,我也不知道,有人告诉我因为它最具有代表性,但为什么我还是不知道,有大神能给我讲一下吗 outputs = hiden_outputs[0][:,0,:] #[0]表示输出结果部分,[:,0,:]表示[CLS]对应的结果 # 把bert最后一层的结果输入到全连接层中,全连接层是{768,15},会输出15分类的一个结果, output = self.fc(outputs) # 这里就是返回最终地分类结果了 return output # 第三部分,整理数据集 # 可以看一下tsv文件的格式,就知道下面这两行什么意思了,一个存储文本,一个存储改文本对应的标签 sentences = train_set['text_a'].values targets = train_set['label'].values train_features,test_features,train_targets,test_targets = train_test_split(sentences,targets)# 这里是把数据分为训练集和测试集,开头导入这个库的方法时说了 batch_size = 64 #把64句话当成一个块来处理,相当于一段有64句话 batch_count = int(len(train_features) / batch_size) #这里就是所有的数据一共可以分为多少块(段) batch_train_inputs, batch_train_targets = [], []# 一个列表存储分段的文本,一个列表存储分段的标签 # 分段 for i in range(batch_count): batch_train_inputs.append(train_features[i*batch_size : (i+1)*batch_size]) batch_train_targets.append(train_targets[i*batch_size : (i+1)*batch_size]) # 第四部分,训练 bertclassfication = BertClassfication() #实例化 lossfuction = nn.CrossEntropyLoss() #定义损失函数,交叉熵损失函数 optimizer = torch.optim.Adam(bertclassfication.parameters(),lr=2e-5)#torch.optim里面都是一些优化器,就是一些反向传播调整参数的方法,比如梯度下降,随机梯度下降等,这里使用ADAM,一种随机梯度下降的改进优化方法 epoch = 5 # 训练轮数,5轮就是所有数据跑五遍 for _ in range(epoch): los = 0 # 损失,写在这里,因为每一轮训练的损失都好应该重新开始计数 for i in range(batch_count):#刚才说了batch_count的意思有多少块(段),每段有64句话 inputs = batch_train_inputs[i] targets = torch.tensor(batch_train_targets[i]) optimizer.zero_grad()#1.梯度置零 outputs= bertclassfication(inputs)#2.模型获得结果 loss = lossfuction(outputs,targets)#3.计算损失 loss.backward()#4.反向传播 optimizer.step()# 5.修改参数,w,b los += loss.item() #item()返回loss的值 # 下面每处理五个段,我们看一下当前损失是多少 if i%5==0: print("Batch:%d,Loss %.4f" % ((i),los/5)) los = 5 # 第四部分 验证 hit = 0 #用来计数,看看预测对了多少个 total = len(test_features) # 看看一共多少例子 for i in range(total): outputs = model([test_features[i]]) _,predict = torch.max(outputs,1)# 这里你需要了解一下torch.max函数,详见https://www.jianshu.com/p/3ed11362b54f if predict==test_targets[i]:# 预测对 hit+=1 print('准确率为%.4f'%(hit/len(test_features))) # 小实验,效果呈现 # 模型训练完事之后,由于原数据集的label只有数字,没有中文注释,于是我自己人工注释了0-14这15个数字对应的类别 transform_dict = {0:'文学',1:'娱乐资讯',2:'体育',3:'财经',4:'房产与住宅',5:'汽车',6:'教育', 7:'科技与互联网',8:'军事',9:'旅游',10:'国际新闻与时政',11:'股票',12:'三农', 13:'电子竞技',14:'小说、故事与轶闻趣事' } result = bertclassfication(["2022年是政治之年"]) _,result = torch.max(result,1) print(transform_dict[result])
时间太长了(个人电脑配置一般),一轮就花费了很长时间,
![在这里插入图片描述](https://img-blog.csdnimg.cn/98c33111da32491d81fac1c7db1ac5ec.png)
如果你没有一台非常好的电脑或者没有实验室的服务器可以用,建议把数据文件可以适当删除一些。
感谢您能看到这里,
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。