python实战(八)——情感识别(多分类)

一、任务目标

        本文使用的是来自Kaggle的一个情感识别数据集,这个数据集的总数据量是5934条,标签为anger、fear、joy三种情感的其中一种,很明显是一个多分类任务。这里,我们将使用微调技巧进行深度学习建模,同时我们会比较不微调和微调之间的模型表现差距,以便于我们理解微调的优势。

二、数据集处理

        首先,我们加载数据集看看:

import pandas as pd
df = pd.read_csv('./data/Emotion_classify_Data.csv')
print(df.info())
print(df.head())

       

        可以看到,数据集没有出现空值,但标签是文本类型,为了便于后续建模,我们将它转换为索引:

print(list(set(df['Emotion'])))
# 根据打印出来的标签类别,逐个编上编号索引
label_idx = {'anger':0, 'joy':1, 'fear':2}
df['Emotion'] = df['Emotion'].apply(lambda x: label_idx[x])
print(df.head())

        为了便于后面对比不微调和微调的模型表现差别,我们提前划分训练集和测试集:

X_train, X_test, y_train, y_test = train_test_split(df['Comment'].tolist(), df['Emotion'].tolist(), stratify=df['Emotion'].tolist(), test_size=0.3, random_state=2024)

三、模型框架构建

1、定义数据集类

        首先我们自定义一个Dataset类,便于进行数据的转换:

from torch.utils.data import Dataset

class SentimentDataset(Dataset):
    def __init__(self,text,target):
        self.text=text
        self.target=target
        self.tokenizer=transformers.BertTokenizer.from_pretrained(config['model_path'],do_lower_case=True)

    def __len__(self):
        return len(self.text)
    
    def __getitem__(self,index):
        
        text=str(self.text[index])
        text=" ".join(text.split())
        target=self.target[index]
        
        input=self.tokenizer.encode_plus(
            text,
            None,
            max_length=config['max_len'],
            truncation=True,
            pad_to_max_length=True,
        
        )
        ids=input['input_ids']
        mask=input['attention_mask']
        token_type_ids=input['token_type_ids']
        
        return {
        "ids":torch.tensor(ids,dtype=torch.long),
        "masks":torch.tensor(mask,dtype=torch.long),
        "token_type_ids":torch.tensor(token_type_ids,dtype=torch.long),
        "target":torch.tensor(target,dtype=torch.float)
                  }

2、定义模型类

        现在,我们定义一个模型类,这里上游模型是Bert,下游模型为全连接层,即MLP。

class Bertmodel(nn.Module):
    def __init__(self):
        super(Bertmodel,self).__init__()
        self.bert=transformers.BertModel.from_pretrained(config['model_path'])
        self.dropout=nn.Dropout(0.3)
        self.fc1=nn.Linear(768,128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 3)
    def forward(self,ids,mask,token_type):
        
        _,x=self.bert(ids,attention_mask=mask,token_type_ids=token_type,return_dict=False)
        
        x=self.dropout(x)
        x=self.fc1(x)
        x=self.dropout(x)
        x=self.fc2(x)
        x = self.fc3(x)
        return x

3、定义训练和测试函数

def train_fn(data_loader,model,optimizer,device,scheduler):
    model.train()
    
    for step,data  in enumerate(data_loader):
        
        ids=data['ids']
        masks=data['masks']
        token_type=data['token_type_ids']
        target=data['target']
        
        
        
        ids=ids.to(device,dtype=torch.long)
        masks=masks.to(device,dtype=torch.long)
        token_type=token_type.to(device,dtype=torch.long)
        target=target.to(device,dtype=torch.long)
        
    
        
        optimizer.zero_grad()
        
        
        preds=model(ids,masks,token_type)
        
        
        loss=loss_fn(preds,target)
        
        
        loss.backward()
        
        
        optimizer.step()
        
        
    # scheduler.step()
        
    return loss.item()

def eval_fn(data_loader,model,device):
    fin_targets=[]
    fin_outputs=[]
    
    
    
    model.eval()
    
    with torch.no_grad():
        for data in data_loader:
            ids=data['ids']
            masks=data['masks']
            token_type=data['token_type_ids']
            target=data['target']
        
        
        
            ids=ids.to(device,dtype=torch.long)
            masks=masks.to(device,dtype=torch.long)
            token_type=token_type.to(device,dtype=torch.long)
            target=target.to(device, dtype=torch.long)
            
        
            preds=model(ids,masks,token_type)
        
            loss=loss_fn(preds,target)
            
            target=target.cpu().detach()
            
            fin_targets.extend(target.numpy().tolist())
            outputs=torch.argmax(preds, dim=1).cpu().detach()
            fin_outputs.extend(outputs.numpy().tolist())
            
            return fin_outputs,fin_targets

4、定义损失函数和优化器

criterion = nn.CrossEntropyLoss()
def loss_fn(output,target):
    loss=criterion(output,target)
    return loss

def train(X_train, y_train):
    train_dataset=SentimentDataset(X_train, y_train)
    
    train_loader=torch.utils.data.DataLoader(train_dataset,batch_size=config['train_batch'],num_workers=1)
    
    
    num_train_steps=int(len(X_train)/config['train_batch']*config['epochs'])
    
    optimizer=AdamW([param for param in model.parameters() if param.requires_grad], lr=5e-5)
    scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.1)
    best_precision=0
    for epoch in range(config['epochs']):
        loss=train_fn(train_loader,model,optimizer,device,scheduler)
        print(f"Epoch_{epoch}, Train_Loss-->>{loss}")

5、训练模型

        在这里,我们初始化模型并确定哪些模型的参数需要参与训练。首先对于不微调预训练模型Bert的过程,我们仅解冻fc层参数,而微调版本则仅解冻Bert最后一层的output子层参数

device=torch.device("cuda")
model=Bertmodel()
model.to(device)

# 微调预训练模型版本
# unfreeze_layers = ['layer.11.output', 'fc']

# 不微调预训练模型版本,相当于仅训练MLP
unfreeze_layers = ['fc']

for name ,param in model.named_parameters():
    param.requires_grad = False
    for ele in unfreeze_layers:
        if ele in name:
            param.requires_grad = True
            break
for name, param in model.named_parameters():
    if param.requires_grad:
        print(name,param.size())


train(X_train, y_train)

6、测试模型表现

        评估模型表现:

from sklearn.metrics import precision_score, recall_score, f1_score

test_dataset=SentimentDataset(X_test, y_test)
test_loader=torch.utils.data.DataLoader(test_dataset,batch_size=16)
total_preds=[]
with torch.no_grad():
    for data in test_loader:
        ids=data["ids"].to(device,dtype=torch.long)
        mask=data["masks"].to(device,dtype=torch.long)
        token_type=data['token_type_ids'].to(device,dtype=torch.long)
    
        output=model(ids,mask,token_type)
        preds=torch.argmax(output, dim=1).cpu().detach()
        preds=preds.numpy().tolist()
        total_preds.extend(preds)

print('Precision', precision_score(y_test, total_preds, average='macro'))
print('Recall', recall_score(y_test, total_preds, average='macro'))
print('F1', f1_score(y_test, total_preds, average='macro'))

        下面这是不微调Bert模型,仅训练MLP的效果:

        下面是微调Bert,并训练MLP的效果,可以看到模型的效果大幅提升,即便我们都是只训练了10个epoch

四、完整代码

import logging
logging.basicConfig(level='ERROR')
import transformers
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sklearn
import torch
import torch.nn as nn
from sklearn import metrics
from sklearn.model_selection import train_test_split
from transformers import AdamW
from transformers import get_linear_schedule_with_warmup
from torch.utils.data import Dataset
from sklearn.metrics import precision_score, recall_score, f1_score
import warnings
warnings.filterwarnings("ignore")


config={
    "max_len":512,
    "train_batch":16,
    "valid_batch":16,
    "epochs":10,
    "model_path":"bert-base-uncased"
}


class SentimentDataset(Dataset):
    def __init__(self,text,target):
        self.text=text
        self.target=target
        self.tokenizer=transformers.BertTokenizer.from_pretrained(config['model_path'],do_lower_case=True)

    def __len__(self):
        return len(self.text)
    
    def __getitem__(self,index):
        
        text=str(self.text[index])
        text=" ".join(text.split())
        target=self.target[index]
        
        input=self.tokenizer.encode_plus(
            text,
            None,
            max_length=config['max_len'],
            truncation=True,
            pad_to_max_length=True,
        
        )
        ids=input['input_ids']
        mask=input['attention_mask']
        token_type_ids=input['token_type_ids']
        
        return {
        "ids":torch.tensor(ids,dtype=torch.long),
        "masks":torch.tensor(mask,dtype=torch.long),
        "token_type_ids":torch.tensor(token_type_ids,dtype=torch.long),
        "target":torch.tensor(target,dtype=torch.float)
                  }

class Bertmodel(nn.Module):
    def __init__(self):
        super(Bertmodel,self).__init__()
        self.bert=transformers.BertModel.from_pretrained(config['model_path'])
        self.dropout=nn.Dropout(0.3)
        self.fc1=nn.Linear(768,128)
        self.fc2 = nn.Linear(128, 64)
        self.fc3 = nn.Linear(64, 3)
    def forward(self,ids,mask,token_type):
        
        _,x=self.bert(ids,attention_mask=mask,token_type_ids=token_type,return_dict=False)
        
        x=self.dropout(x)
        x=self.fc1(x)
        x=self.dropout(x)
        x=self.fc2(x)
        x = self.fc3(x)
        return x

def train_fn(data_loader,model,optimizer,device,scheduler):
    model.train()
    
    for step,data  in enumerate(data_loader):
        
        ids=data['ids']
        masks=data['masks']
        token_type=data['token_type_ids']
        target=data['target']
        
        
        
        ids=ids.to(device,dtype=torch.long)
        masks=masks.to(device,dtype=torch.long)
        token_type=token_type.to(device,dtype=torch.long)
        target=target.to(device,dtype=torch.long)
        
    
        
        optimizer.zero_grad()
        
        
        preds=model(ids,masks,token_type)
        
        
        loss=loss_fn(preds,target)
        
        
        loss.backward()
        
        
        optimizer.step()
        
        
    # scheduler.step()
        
    return loss.item()

def eval_fn(data_loader,model,device):
    fin_targets=[]
    fin_outputs=[]
    
    
    
    model.eval()
    
    with torch.no_grad():
        for data in data_loader:
            ids=data['ids']
            masks=data['masks']
            token_type=data['token_type_ids']
            target=data['target']
        
        
        
            ids=ids.to(device,dtype=torch.long)
            masks=masks.to(device,dtype=torch.long)
            token_type=token_type.to(device,dtype=torch.long)
            target=target.to(device, dtype=torch.long)
            
        
            preds=model(ids,masks,token_type)
        
            loss=loss_fn(preds,target)
            
            target=target.cpu().detach()
            
            fin_targets.extend(target.numpy().tolist())
            outputs=torch.argmax(preds, dim=1).cpu().detach()
            fin_outputs.extend(outputs.numpy().tolist())
            
            return fin_outputs,fin_targets

criterion = nn.CrossEntropyLoss()
def loss_fn(output,target):
    loss=criterion(output,target)
    return loss

def train(X_train, y_train):
    train_dataset=SentimentDataset(X_train, y_train)
    
    train_loader=torch.utils.data.DataLoader(train_dataset,batch_size=config['train_batch'],num_workers=1)
    
    
    num_train_steps=int(len(X_train)/config['train_batch']*config['epochs'])
    
    optimizer=AdamW([param for param in model.parameters() if param.requires_grad], lr=5e-5)
    scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.1)
    best_precision=0
    for epoch in range(config['epochs']):
        loss=train_fn(train_loader,model,optimizer,device,scheduler)
        print(f"Epoch_{epoch}, Train_Loss-->>{loss}")

device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model=Bertmodel()
model.to(device)

# 微调预训练模型版本
# unfreeze_layers = ['layer.11.output', 'fc']

# 不微调预训练模型版本,相当于仅训练MLP
unfreeze_layers = ['fc']

for name ,param in model.named_parameters():
    param.requires_grad = False
    for ele in unfreeze_layers:
        if ele in name:
            param.requires_grad = True
            break
for name, param in model.named_parameters():
    if param.requires_grad:
        print(name,param.size())


train(X_train, y_train)


test_dataset=SentimentDataset(X_test, y_test)
test_loader=torch.utils.data.DataLoader(test_dataset,batch_size=16)
total_preds=[]
with torch.no_grad():
    for data in test_loader:
        ids=data["ids"].to(device,dtype=torch.long)
        mask=data["masks"].to(device,dtype=torch.long)
        token_type=data['token_type_ids'].to(device,dtype=torch.long)
    
        output=model(ids,mask,token_type)
        preds=torch.argmax(output, dim=1).cpu().detach()
        preds=preds.numpy().tolist()
        total_preds.extend(preds)

print('Precision', precision_score(y_test, total_preds, average='macro'))
print('Recall', recall_score(y_test, total_preds, average='macro'))
print('F1', f1_score(y_test, total_preds, average='macro'))

五、总结

        可以看到,如果仅仅是应用预训练模型的embeddings,任务建模的效果有限,这是由于预训练的过程中学习到的大都是通用知识。经过微调之后,预训练模型更有效地理解了任务的需求并能够生成更为有效的文本表示,从而大大提升建模效果,这就是微调的魅力。但是微调并不是在任何情况下都能够取得更好效果的,这受到解冻层数、参数量的选取,以及学习率等超参数的设置等多方因素的影响,需要我们根据经验调整到最优状态,否则微调的效果可能还不如不微调。本文并未对建模过程中的代码设计展开详细的介绍,这将在下一篇博文中重点讲解,敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/914204.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

价格战背后:即时零售三小龙的致命伤

价格战,从来就不仅仅是低价,低价前面永远要加上定语:确保品质和服务的。价格战是减法,更是加法。减去的是价格水分,加上的是品质和服务保障。 转载|原创新熵 作者丨宜新 编辑丨赛柯 今年双十一的热点,让人…

sd1.5/sdxl的推理,训练

1.sd1.5/sdxl的推理 主要讲述一下unet的降噪,以及采样器的作用,已sd1.5为例,sdxl类似 unet的降噪过程中,如20步降噪,这20个unet共用的一个权重 1.1 timesteps 根据unet的降噪步数,即num_inference_steps…

有哪些工具可以快速压缩图片呢?分享三个简单好用的图片压缩工具

现在的图片经常会因为图片过大的问题,影响在网上的上传使用,一般在平台上传图片时需要比较小的图片。但是随着现在图片质量的提升,导致图片的文件也越来越大,想要缩小图片大小,可以选择使用压缩图片的方法来处理。下面…

Java集合 List——针对实习面试

目录 Java集合 ListJava List的三种主要实现是什么?它们各自的特点是什么?Java List和Array(数组)的区别?Java List和Set有什么区别?ArrayList和Vector有什么区别?什么是LinkedList?…

请求接口时跨域问题详细解决方案

浏览器中的报错: 跨域问题通常需要前端和后端协作解决。以下是一些常用的解决方法,分别从前端和后端的角度进行讲解: 一. 后端解决方案 设置 CORS 头部: 最常用和推荐的方法是后端服务器设置 Access-Control-Allow-Origin 响应头…

同三维T610UDP-4K60 4K60 DP或HDMI或手机信号采集卡

1路DP/HDMI/TYPE-C(手机/平板等)视频信号输入1路MIC1路LINE OUT,带1路HDMI环出,USB免驱,分辨率4K60,可采集3路信号中其中1路,按钮切换,可采集带TYPE-C接口的各品牌手机/平板/笔记本电脑等 同三维…

初级数据结构——顺序表

目录 前言一、定义与特点二、类型三、基本操作四、应用场景五、优缺点六、元素插入和删除动态图解插入删除 七、代码模板八、使用顺序表的经典例题1.求奇数的乘积代码题解 2.数值统计代码题解 九、总结结语 前言 顺序表示最基础的数据结构之一,它也是我们学习开始学…

arkUI:遍历数据数组动态渲染(forEach)

arkUI:遍历数据数组动态渲染(forEach) 1 主要内容说明2 相关内容2.1 ForEach 的基本语法2.2 简单遍历数组2.2 多维数组遍历2.4 使用唯一键2.5 源码1的相关说明2.5.1 源码1 (遍历数据数组动态渲染)2.5.2 源码1运行效果 …

新的恶意软件活动通过游戏应用程序瞄准 Windows 用户

一种新的恶意软件 Winos4.0 被积极用于网络攻击活动。FortiGuard实验室发现,这种先进的恶意框架是从臭名昭著的 Gh0strat 演变而来的,配备了模块化组件,可在受感染的设备上进行一系列恶意活动。 这些攻击已在游戏相关应用程序中发现&#xf…

Maven学习——创建Maven的Java和Web工程,并运行在Tomcat上

一、Maven介绍 Maven 是一款为 Java 项目管理构建、依赖管理的工具(软件),使用 Maven 可以自动化构建、测试、打包和发布项目,大大提高了开发效率和质量。 二、Maven安装步骤 1.下载后解压到没有空格、特殊字符和中文的目录中 2…

【刷题】优选算法

优选算法 双指针 202. 快乐数 链接:. - 力扣(LeetCode) 【思路】 第一个实例是快乐数,因为会变为1且不断是1的循环 第二个实例不可能为1,因为会陷入一个没有1的循环 根据两个实例和鸽巢原理可以发现不断的平方和最…

在unity中实现把普通的照片,图片 变成油画风格的shader实现

可以通过对shader的Radius的值得设置来改变油画风格的力度,0最小,10是最大。

【项目开发 | 跨域认证】JSON Web Token(JWT)

未经许可,不得转载。 文章目录 JWT设计背景:跨域认证JWT 原理JWT 结构JWT 使用方式注意JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理、结构及用法。 JWT设计背景:跨域认证 互联网服务的用户认证流程是现代应用中的核心组成部分,通常的流程…

vue3中如何实现标准元素 拖动 功能 【收藏备用】

最近在用vue3做一个企业后台管理系统的项目,在登录页面的时候需要用户滑动滑块来获取验证码登录系统 用到了元素拖放 这里也顺便记录一下 如何使用的. 目录 1.功能介绍 2.代码部分 3 实现过程 3.1 设置可拖动元素 3.2 拖动什么 3.3 放到何处 3.4 进行放置 1.功能介绍…

Linux(CentOS)运行 jar 包

1、在本地终端运行,关闭终端,程序就会终止 java -jar tlias-0.0.1-SNAPSHOT.jar 发送请求,成功 关闭终端(程序也会终止) 发送请求,失败 2、在远程终端运行,关闭终端,程序就会终止 …

Local Dimming和Mini LED简介

文章目录 Local Dimming和Mini LED的介绍区别和联系联系区别总结 Local Dimming和Mini LED的介绍 电视显示技术中的Local Dimming和Mini LED都是用于提升画面质量的背光技术,主要目的是增强对比度和改善黑色表现。以下是对它们的详细介绍: Local Dimmin…

数据结构选择题及答案

一、选择题 1、下列查找方法中,( )适用于查找有序单链表。 A.分块查找; B.哈希查找; C.顺序查找; D.二分查找; 2、在有n个结点的二叉树的二叉链表表示中,空指针数为( )。 A&#xf…

npm完整发包流程(亲测可验证)

1. 准备工作 (1) 在npm官网上注册一个账号 (2) 注册成功之后,npm会发送一封邮件给你,点击邮件里面的链接,做确认关联操作(必需) 2. 创建自己的npm包 (…

3.2 软件需求:面对过程分析模型

面对过程分析模型 1. 需求分析的模型概述1.1 面对过程分析模型-结构化分析方法1.2 结构化分析的过程 2. 功能模型:数据流图初步2.1 加工2.2 外部实体(数据源点/终点)2.3 数据流2.4 数据存储2.5 注意事项 3. 功能模型:数据流图进阶…

【机器学习】机器学习中用到的高等数学知识-1.线性代数 (Linear Algebra)

向量(Vector)和矩阵(Matrix):用于表示数据集(Dataset)和特征(Feature)。矩阵运算:加法、乘法和逆矩阵(Inverse Matrix)等,用于计算模型参数。特征值(Eigenvalues)和特征向量(Eigenvectors)&…