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

news2024/12/23 5:34:24

一、任务目标

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

23423234

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话: 知不足而奋进,望远山而前行&am…

opencv入门学习总结

opencv学习总结 不多bb,直接上代码!!! 案例一: import cv2 # 返回当前安装的 OpenCV 库的版本信息 并且是字符串格式 print(cv2.getVersionString()) """ 作用:它可以读取不同格式的图像文…

MySQL 中的索引下推功能

看到索引,应该大家都可以联想到这个是和查询效率有关系的,既然有这个功能,那么那句古话说的好啊:存在即合理。那么这个就是说有了这个功能,可以提升查询效率。 什么是索引下推 我们先有一个大概的理解:在…

重拾CSS,前端样式精读-媒体查询

前言 本文收录于CSS系列文章中,欢迎阅读指正 说到媒体查询,大家首先想到的可能是有关响应式的知识点,除此之外,它还可以用于条件加载资源,字体大小,图像和视频的优化,用户界面调整等等方面&am…

物理设备命名规则(Linux网络服务器 15)

Linux系统中的一切都是文件,硬件设备也不例外。既然都是文件,就必须有文件名称。系统内核中udev设备管理器会自动把硬件名称规范化起来,目的是让用户通过设备文件的名字可以大致了解设备属性以及分区信息。这对于陌生的设备来说特别方便。另外…

NVIDIA NIM 开发者指南:入门

NVIDIA NIM 开发者指南:入门 NVIDIA 开发者计划 想要了解有关 NIM 的更多信息?加入 NVIDIA 开发者计划,即可免费访问任何基础设施云、数据中心或个人工作站上最多 16 个 GPU 上的自托管 NVIDIA NIM 和微服务。 加入免费的 NVIDIA 开发者计…

猿创征文|Inscode桌面IDE:打造高效开发新体验

猿创征文|Inscode桌面IDE:打造高效开发新体验 引言 在当今快速发展的软件开发领域,一个高效、易用的集成开发环境(IDE)是每个开发者必不可少的工具。Inscode 桌面 IDE 作为一款新兴的开发工具,凭借其强大…

力扣 LeetCode 142. 环形链表II(Day2:链表)

解题思路&#xff1a; 使用set判断是否重复添加&#xff0c;如果set加入不进去证明之前到达过该节点&#xff0c;有环 public class Solution {public ListNode detectCycle(ListNode head) {Set<ListNode> set new HashSet<>();ListNode cur head;while (cur …

激活函数解析:神经网络背后的“驱动力”

神经网络中的激活函数&#xff08;Activation Function&#xff09;是其运作的核心组件之一&#xff0c;它们决定了神经元如何根据输入信号进行“激活”&#xff0c;进而影响整个模型的表现。理解激活函数的工作原理对于设计和优化神经网络至关重要。本篇博客将深入浅出地介绍各…

基于表格滚动截屏(表格全部展开,没有滚动条)

import html2canvasPro from html2canvas // 截图&#xff0c;平辅表格 async function resetAgSize() {const allColumns gridApi.value.getColumns()let totalColumnWidth 0let totalColumnHeight 0// 遍历每一个行节点gridApi.value.forEachNode((rowNode) > {totalCo…

vs2015QT项目添加多语言翻译总结

一、简介 当软件有国际化的需求时&#xff0c;就需要多语言翻译功能&#xff0c;最常见的语言就是支持中文和英语&#xff0c;本文介绍在vs2015QT环境下&#xff0c;进行国际化翻译的具体流程。 二、多语言翻译实现流程 1.底层实现原理介绍 QT写的客户端软件&#xff0c;能…

wireshark演进之路——从GTK到Qt

Wireshark 自 1998 年诞生至今&#xff0c;已有超过26年的历史了。它最早由 Gerald Combs 创建&#xff0c;最初名为 Ethereal。2006 年&#xff0c;Ethereal 更名为 Wireshark&#xff0c;并继续发展成了全球领先且人尽皆知的网络协议分析工具&#xff0c;其GUI演变就是其中非…

哈希表的实现--C++

文章目录 一、哈希概念1.1、直接定址法1.2、哈希冲突1.3、负载因子1.4、将关键字转为整数1.5、哈希函数1.5.1、除法散列法/除留余数法1.5.2、乘法散列法1.5.3、全域散列法1.5.4、其他方法 二、处理哈希冲突2.1、开放定址法2.1.1、线性探测2.1.2、二次探测2.1.3、双重散列2.1.4、…

Python学习从0到1 day26 第三阶段 Spark ② 数据计算Ⅰ

人总是会执着于失去的&#xff0c;而又不珍惜现在所拥有的 —— 24.11.9 一、map方法 PySpark的数据计算&#xff0c;都是基于RDD对象来进行的&#xff0c;采用依赖进行&#xff0c;RDD对象内置丰富的成员方法&#xff08;算子&#xff09; map算子 功能&#xff1a;map算子…

D67【python 接口自动化学习】- python基础之数据库

day67 Python操作MySQL基础使用 学习日期&#xff1a;20241113 学习目标&#xff1a;MySQL数据库-- 136 Python操作MySQL基础使用 学习笔记&#xff1a; pymysql 创建MySQL的数据库链接 执行sql语句 总结 Python中使用第三方库&#xff1a;pymysql来操作MySQL&#xff0c;…

Linux驱动开发基础——Hello驱动程序(一)

目录 一、Hello驱动 一、Hello驱动 我们选用的内核都是 4.x 版本&#xff0c;操作都是类似的&#xff1a; 1.1、APP 打开的文件在内核中如何表示 open函数原型&#xff1a; int open(const char *pathname, int flags, mode_t mode); 仔细看函数的参数&#xff0c;再对比看 内…

2.初始sui move

vscode安装move插件 查看sui 客户端版本号 sui client --version 创建新项目 sui move new <项目名> sui move new hello_world 项目目录结构&#xff1a; hello_world ├── Move.toml ├── sources │ └── hello_world.move └── tests└── hello_world…

学习日志009--面向对象的编程

一、面向对象 面向对象编程&#xff08;Object-Oriented Programming&#xff0c;简称OOP&#xff09;是一种编程范式&#xff0c;它使用“对象”来设计应用程序和计算机程序。它利用了抽象、封装、继承和多态这些概念。 一、面向对象编程的核心概念 封装&#xff08;Encaps…

Redis8:商户查询缓存2

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

在 WPF 中,如何实现数据的双向绑定?

在 WPF 中&#xff0c;数据绑定是一个非常重要的特性&#xff0c;它允许 UI 与数据源之间自动同步。双向绑定是一种常见的绑定方式&#xff0c;当数据源更新时&#xff0c;UI 会自动更新&#xff1b;同样&#xff0c;当 UI 中的元素&#xff08;如文本框&#xff09;发生改变时…