NLP08:huggingface transformers-使用Albert进行中文文本分类

公众号:数据挖掘与机器学习笔记

1.Albert简介

Alber相对于原始BERT模型主要有三点改进:

  • embedding 层参数因式分解

  • 跨层参数共享

  • 将 NSP 任务改为 SOP 任务

1.1 embedding 层参数因式分解(Factorized Embedding Parameterization)

原始的 BERT 模型以及各种依据 Transformer 的预训连语言模型都有一个共同特点,即 E = H E=H E=H,其中 E 指的是 Embedding Dimension, H H H 指的是 Hidden Dimension。这就会导致一个问题,当提升 Hidden Dimension 时,Embedding Dimension 也需要提升,最终会导致参数量呈平方级的增加。所以 ALBERT 的作者将 E 和 H E 和 H EH 进行解绑**,具体的操作就是**在 Embedding 后面加入一个矩阵进行维度变换。 E E E 的维度是不变的,如果 H H H 增大了,我们只需要在 E E E 后面进行一个升维操作即可

原本参数数量为 V ∗ H V∗H VH,V 表示的是 Vocab Size。分解成两步则减少为$ V∗E+E∗H , 当 ,当 H$ 的值很大时,这样的做法能够大幅降低参数数量

V∗H=30000∗768=23,040,000

V∗E+E∗H=30000∗256+256∗768=7,876,608

通过因式分解 Embedding 的实验可以看出,对于参数不共享的版本,随着 E E E 的增大,效果是不断提升的。但是参数共享的版本似乎不是这样, E E E 最大并不是效果最好。同时也能发现参数共享对于效果可能带来 1-2 个点的下降

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kDorEh4L-1601211705022)(https://s1.ax1x.com/2020/08/18/du74L6.png#shadow)]

1.2 跨层参数共享(Cross-Layer Parameter Sharing)

传统 Transformer 的每一层参数都是独立的,包括各层的 self-attention、全连接。这样就导致层数增加时,参数量也会明显上升。之前有工作试过单独将 self-attention 或者全连接层进行共享,都取得了一些效果。ALBERT 作者尝试将所有层的参数进行共享,相当于只学习第一层的参数,并在剩下的所有层中重用该层的参数,而不是每个层都学习不同的参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JcsxdNwN-1601211705024)(https://s1.ax1x.com/2020/08/18/duRSC6.png#shadow)]

同时作者通过实验还发现了,使用参数共享可以有效的提升模型稳定性,实验结果如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tqf5ZkQ1-1601211705028)(https://s1.ax1x.com/2020/08/18/duW7m4.png#shadow)]

BERT-base 和 ALBERT 使用相同的层数以及 768 个隐藏单元,结果 BERT-base 共有 1.1 亿个参数,而 ALBERT 只有 3100 万个参数。通过实验发现,feed-forward 层的参数共享会对精度产生比较大的影响;共享注意力参数的影响是最小的

在这里插入图片描述

1.3 将 NSP 任务改为 SOP 任务(Sentence-Order Prediciton (SOP))

BERT 引入了一个叫做下一个句子预测的二分类问题。这是专门为提高使用句子对,如 “自然语言推理” 的下游任务的性能而创建的。但是像 RoBERTa 和 XLNet 这样的论文已经阐明了 NSP 的无效性,并且发现它对下游任务的影响是不可靠的

因此,ALBERT 提出了另一个任务 —— 句子顺序预测。关键思想是:

  • 从同一个文档中取两个连续的句子作为一个正样本
  • 交换这两个句子的顺序,并使用它作为一个负样本

在这里插入图片描述

SOP 提高了下游多种任务(SQUAD,MNLI,SST-2,RACE)的表现
在这里插入图片描述

2.使用Albert进行文本分类

import torch
from transformers import BertTokenizer,BertModel,BertConfig
import numpy as np
from torch.utils import data
from sklearn.model_selection import train_test_split
import pandas as pd

2.1 加载预训练模型

pretrained = 'voidful/albert_chinese_small' #使用small版本Albert
tokenizer = BertTokenizer.from_pretrained(pretrained)
model=BertModel.from_pretrained(pretrained)
config=BertConfig.from_pretrained(pretrained)
inputtext = "今天心情情很好啊,买了很多东西,我特别喜欢,终于有了自己喜欢的电子产品,这次总算可以好好学习了"
tokenized_text=tokenizer.encode(inputtext)
input_ids=torch.tensor(tokenized_text).view(-1,len(tokenized_text))
outputs=model(input_ids)

输出字向量表示和句向量

outputs[0].shape,outputs[1].shape

2.2 构建模型网络结构

class AlbertClassfier(torch.nn.Module):
    def __init__(self,bert_model,bert_config,num_class):
        super(AlbertClassfier,self).__init__()
        self.bert_model=bert_model
        self.dropout=torch.nn.Dropout(0.4)
        self.fc1=torch.nn.Linear(bert_config.hidden_size,bert_config.hidden_size)
        self.fc2=torch.nn.Linear(bert_config.hidden_size,num_class)
    def forward(self,token_ids):
        bert_out=self.bert_model(token_ids)[1] #句向量 [batch_size,hidden_size]
        bert_out=self.dropout(bert_out)
        bert_out=self.fc1(bert_out) 
        bert_out=self.dropout(bert_out)
        bert_out=self.fc2(bert_out) #[batch_size,num_class]
        return bert_out
albertBertClassifier=AlbertClassfier(model,config,2)
device=torch.device("cuda:0") if torch.cuda.is_available() else 'cpu'
albertBertClassifier=albertBertClassifier.to(device)

2.3 准备训练数据和验证数据

def get_train_test_data(pos_file_path,neg_file_path,max_length=100,test_size=0.2):
    data=[]
    label=[]
    pos_df=pd.read_excel(pos_file_path,header=None)
    pos_df.columns=['content']
    for index, row in pos_df.iterrows():
        row=row['content']
        ids=tokenizer.encode(row.strip(),max_length=max_length,padding='max_length',truncation=True)
        data.append(ids)
        label.append(1)
        
    neg_df=pd.read_excel(neg_file_path,header=None)
    neg_df.columns=['content']
    for index, row in neg_df.iterrows():
        row=row['content']
        ids=tokenizer.encode(row.strip(),max_length=max_length,padding='max_length',truncation=True)
        data.append(ids)
        label.append(0)
    X_train, X_test, y_train, y_test=train_test_split(data,label,test_size=test_size,shuffle=True)
    return (X_train,y_train),(X_test,y_test)
pos_file_path="../input/data01/pos.xls"
neg_file_path="../input/data01/neg.xls"
(X_train,y_train),(X_test,y_test)=get_train_test_data(pos_file_path,neg_file_path)
len(X_train),len(X_test),len(y_train),len(y_test),len(X_train[0])
class DataGen(data.Dataset):
    def __init__(self,data,label):
        self.data=data
        self.label=label
    def __len__(self):
        return len(self.data)
            
    def __getitem__(self,index):
        return np.array(self.data[index]),np.array(self.label[index])
train_dataset=DataGen(X_train,y_train)
test_dataset=DataGen(X_test,y_test)
train_dataloader=data.DataLoader(train_dataset,batch_size=256)
test_dataloader=data.DataLoader(test_dataset,batch_size=256)

2.4.定义优化器和损失函数

criterion=torch.nn.CrossEntropyLoss()
optimizer=torch.optim.SGD(albertBertClassifier.parameters(),lr=0.01,momentum=0.9,weight_decay=1e-4)

2.5.模型训练和测试

for epoch in range(50):
    loss_sum=0.0
    accu=0
    albertBertClassifier.train()
    for step,(token_ids,label) in enumerate(train_dataloader):
        token_ids=token_ids.to(device)
        label=label.to(device)
        out=albertBertClassifier(token_ids)
        loss=criterion(out,label)
        optimizer.zero_grad()
        loss.backward() #反向传播
        optimizer.step() #梯度更新
        loss_sum+=loss.cpu().data.numpy()
        accu+=(out.argmax(1)==label).sum().cpu().data.numpy()
        
    test_loss_sum=0.0
    test_accu=0
    albertBertClassifier.eval()
    for step,(token_ids,label) in enumerate(test_dataloader):
        token_ids=token_ids.to(device)
        label=label.to(device)
        with torch.no_grad():
            out=albertBertClassifier(token_ids)
            loss=criterion(out,label)
            test_loss_sum+=loss.cpu().data.numpy()
            test_accu+=(out.argmax(1)==label).sum().cpu().data.numpy()
    print("epoch % d,train loss:%f,train acc:%f,test loss:%f,test acc:%f"%(epoch,loss_sum/len(train_dataset),accu/len(train_dataset),test_loss_sum/len(test_dataset),test_accu/len(test_dataset)))   

参考:

【1】https://www.wmathor.com/index.php/archives/1480/

代码:https://github.com/chongzicbo/nlp-ml-dl-notes/blob/master/code/textclassification/nlp08_huggingface_transformers_albert.ipynb

在这里插入图片描述

  • 7
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 25
    评论
Huggingface vivalbert是指Huggingface库中的一个模型,它基于ALBERT模型进行了改进和优化。ALBERT模型是一种基于Transformer的预训练语言模型,其特点是将Embedding Dimension(E)和Hidden Dimension(H)解耦,通过在Embedding后面添加一个矩阵进行维度变换,从而实现参数量更少的模型。而vivalbertHuggingfaceALBERT基础上进行的改进版本,它在ALBERT的基础上进一步优化了模型的性能和效果。 关于使用hugging face vivalbert进行文本分类的方法,可以参考以下步骤: 1. 安装hugging face库:通过pip install transformers命令来下载和安装hugging face的库。 2. 下载预训练模型参数:通过使用AlbertModel.from_pretrained('voidful/albert_chinese_base')命令来加载vivalbert的预训练模型参数。 3. 使用BertTokenizer进行词索引的转换:通过使用BertTokenizer.from_pretrained('bert-base-chinese')命令来加载hugging facebert_tokenize进行词索引的转换。 4. 准备语料库:准备一个语料库,例如人民日报标注好的语料库,其中可以包含TIME、PERSON、LOCATION和OTHER等标签。 5. 使用LSTM进行训练:可以使用LSTM对语料库进行训练,得到一个发射矩阵(非归一化数值,(batch_size, seq_len, num_entities))作为观测变量。 6. 使用CRF进行解码:使用CRF的解码公式,其中等式右边的第二项为LSTM输出的发射矩阵,第一项为需要模型学习的转移矩阵(状态→状态,随机初始化)。 7. 定义损失函数:损失函数中的score就是发射矩阵与转移矩阵(真实路径)的乘积,Z()表示所有路径的和,通过最大化真实路径在所有路径中的比例,可以得到动态规划类似的公式。 以上是使用hugging face vivalbert进行文本分类的一种方法和步骤。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [NLP08huggingface transformers-使用Albert进行中文文本分类](https://blog.csdn.net/u013230189/article/details/108836511)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [利用hugging face进行albert-lstm-crf的命名实体识别](https://blog.csdn.net/hyzhyzhyz12345/article/details/106685321)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 25
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值