首页 > 编程语言 >python实战(六)——推特文本分类

python实战(六)——推特文本分类

时间:2024-11-05 09:16:21浏览次数:5  
标签:推特 keyword texts python text df location print 文本

一、任务目标

        这次我们用的是kaggle的入门数据集《Natural Language Processing with Disaster Tweets》,为了便于评估建模效果,我们仅使用带标签的train.csv文件。这个任务的目标是根据给出的推特文本判断是否真的是发生了灾难,这是由于一些人会使用与灾难相关的词语去描述正在发生的事情,但却并不是真的发生了灾难。比如“大地震!xxx发布了最新的自研操作系统!”,这里用到了地震这个词,但实际上只是表达作者对此的震惊,并不是真的地震了。我们需要训练一个模型去划分哪些是真的灾难,哪些只是夸张的表达(二分类问题)

二、深度学习建模

1、探索性数据分析

        首先看看数据量和各个列:

import pandas as pd
df = pd.read_csv('./data/disasterTweets.csv')
print('数据量:', len(df))
print(df.head())

        打印一下各列的信息:

print(df.info())

        我们可以看到总共有5列,其中最主要的是text和target列,同时,keyword和location列存在一定数量的空值。

2、数据预处理

        由于keyword和location列存在一些空值,这里我们需要思考处理的策略。假设我们仅使用text和target进行建模,那么大可直接去掉keyword和location两列。然而,凭直觉判断,或许这两列能够为建模提供一些额外的信息,比如可能在哪个地方发生了真实的灾难,推特文本中也会带上这个地点名,又或者关键词可以帮助我们判断到底推文的重点的地方在哪里,那么我们就不能简单的删掉这两列又或是删掉包含空值的行(因为要预测的新数据或许这两个字段就是空的,我们需要考虑这种情况的处理,总不能把要预测的新数据中的这种含空值列也删掉)

        在这里,笔者选择使用文本填充空值(原字段数据类型就是文本):

df = df.fillna('EMPTY')

3、特征工程

(1)处理location列

        这里我们考虑构造一些新的特征,因为我们打算充分使用keyword和location两列。首先处理location,我们统计一下,location是否在推特正文中出现过:

loc_in_text = []
for loc, twe in zip(df['location'], df['text']):
    if loc in twe:
        loc_in_text.append(1)
    else:
        loc_in_text.append(0)
df['loc_in_text'] = loc_in_text

        接着,我们看一下在正文中出现了location且实际发生灾难的比例:

have_disaster = 0
no_disaster = 0
for lab, lit in zip(df['target'], df['loc_in_text']):
    if lab==lit:
        if lab==0:
            no_disaster += 1
        else:
            have_disaster += 1
print('没有发生灾难的数据量:{},文中出现location的数据量:{}'.format(df['target'].tolist().count(0), no_disaster))
print('发生灾难的数据量:{},文中出现location的数据量:{}'.format(df['target'].tolist().count(1), have_disaster))

        可以看到,虽然实际发生灾难的推文中出现location的推文只有数十条,但是在没发生灾难的推文中大部分都出现了location,可见location应该有一定用处。

(2)处理keyword列

        这里我们如法炮制:

keyword_in_text = []
for kw, twe in zip(df['keyword'], df['text']):
    if kw in twe:
        keyword_in_text.append(1)
    else:
        keyword_in_text.append(0)
df['keyword_in_text'] = keyword_in_text

have_keyword = 0
no_keyword = 0
for lab, kit in zip(df['target'], df['keyword_in_text']):
    if lab==kit:
        if lab==0:
            no_keyword += 1
        else:
            have_keyword += 1
print('没有发生灾难的数据量:{},文中出现keyword的数据量:{}'.format(df['target'].tolist().count(0), no_keyword))
print('发生灾难的数据量:{},文中出现keyword的数据量:{}'.format(df['target'].tolist().count(1), have_keyword))

        可见,keyword在两类数据中的出现概率差距要比location小,但是我们目前先保留。

4、文本表示

        为了对比加入location和keyword辅助信息与否对建模结果的影响,这里我们分两种情况进行文本表示。同时,本文直接应用bert模型进行文本向量化,不对bert进行任务微调,展示一下预训练语言模型拿来即用的效果。首先加载模型:

from transformers import BertTokenizer, BertModel
import torch
 
# 同样,可以去HuggingFace找更强大的模型
model_name = 'bert-base-uncased'
# 初始化分词器
tokenizer = BertTokenizer.from_pretrained(model_name)
# 加载模型
model = BertModel.from_pretrained(model_name)

(1)原始推文文本表示

        由于数据量不大,这里不使用DataLoader了,直接写循环:

texts = df['text'].tolist()
# 定义批量大小
batch_size = 16
# 创建一个空列表来存储所有的句子嵌入
all_sentence_embeddings = []
# 处理数据的批量
for i in range(0, len(texts), batch_size):
    if i%100==0:
        print('processing No.{} sample'.format(i))
        
    batch_texts = texts[i:i+batch_size]
    
    # 分词器生成Bert必要的输出格式
    inputs = tokenizer(batch_texts, padding=True, truncation=True, return_tensors="pt")
    
    # 禁用梯度,打开预测模式
    with torch.no_grad():
        # 预测输出
        outputs = model(**inputs)
    
    # 取最后一层的token平均向量
    sentence_embeddings = outputs.pooler_output
    
    # 将当前批量的句子嵌入添加到列表中
    all_sentence_embeddings += sentence_embeddings

(2)原始推文加辅助信息文本表示

        首先,文本类信息无法像数值类数据建模那样使用简单的0、1索引,我们选择通过在原推文添加额外文本的形式将附加的location和keyword信息融入进文本中(某种程度上来看,这属于语义增强的一种方式)。下面这里只是一个示例,实际上可以有很多种方式的语义增强,得益于预训练模型强大的理解能力,我们所增加的文本信息能够较好地被模型理解并表达出来,这是传统文本表示模型所无法比拟的。

texts = df['text'].tolist()
location_info = '. With location:'
keyword_info = 'With keyword:'
texts_with_tips = []
for text, loc, kw, word in zip(texts, loc_in_text, keyword_in_text, df['keyword'].tolist()):
    if loc==1:
        text += location_info+'True.'
    else:
        text += location_info+'False.'
    if kw==1:
        text += keyword_info+word+'True.'+'Keyword:'+word+'.'
    else:
        text += keyword_info+'False.'
    texts_with_tips.append(text)
texts = texts_with_tips
# 定义批量大小
batch_size = 16
# 创建一个空列表来存储所有的句子嵌入
all_sentence_embeddings = []
# 处理数据的批量
for i in range(0, len(texts), batch_size):
    if i%100==0:
        print('processing No.{} sample'.format(i))
        
    batch_texts = texts[i:i+batch_size]
    
    # 分词器生成Bert必要的输出格式
    inputs = tokenizer(batch_texts, padding=True, truncation=True, return_tensors="pt")
    
    # 禁用梯度,打开预测模式
    with torch.no_grad():
        # 预测输出
        outputs = model(**inputs)
    
    # 取最后一层的token平均向量
    sentence_embeddings = outputs.pooler_output
    
    # 将当前批量的句子嵌入添加到列表中
    all_sentence_embeddings += sentence_embeddings

5、分类建模

        先划分数据集,sklearn模型无法处理torch的tensor,我们将它转换成list类型:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split([list(li) for li in all_sentence_embeddings], df['target'].tolist(), stratify=df['target'].tolist(), test_size=0.3, random_state=2024)

        这里下游模型直接使用一个简单的MLP:

from sklearn.neural_network import MLPClassifier
from sklearn.metrics import precision_score, recall_score, f1_score
dtc = MLPClassifier(hidden_layer_sizes=(128,64), max_iter=100, random_state=2024)
dtc.fit(X_train, y_train)
y_pred = dtc.predict(X_test)
print('Prec:', precision_score(y_test, y_pred))
print('Rec:', recall_score(y_test, y_pred))
print('F1:', f1_score(y_test, y_pred))

(1)原始推文文本表示建模结果

(2)语义增强后的推文文本表示建模结果

        显然,增加了语义信息之后,从F1值的角度来看,一定程度上提升了模型性能(recall提升了5个点,但precision降低了5个点)。当然这个结果并不算得上好,可以进一步通过优化语义增强方式、微调Bert或者调整下游模型来提升整体模型的预测能力(具体可参考kaggle上的开源方案)。

三、完整代码

        

import pandas as pd
from transformers import BertTokenizer, BertModel
from sklearn.model_selection import train_test_split
from sklearn.neural_network import MLPClassifier
from sklearn.metrics import precision_score, recall_score, f1_score
import torch
#设置显示窗口数据显示完整
pd.set_option('display.max_rows', 500)
pd.set_option('display.max_columns', 100)
pd.set_option('display.width', 1000)


# 读取数据
df = pd.read_csv('./data/disasterTweets.csv')
print('数据量:', len(df))
print(df.head())
print(df.info())
df = df.fillna('EMPTY')

# 统计位置特征在推文中的出现次数
loc_in_text = []
for loc, twe in zip(df['location'], df['text']):
    if loc in twe:
        loc_in_text.append(1)
    else:
        loc_in_text.append(0)
df['loc_in_text'] = loc_in_text

have_disaster = 0
no_disaster = 0
for lab, lit in zip(df['target'], df['loc_in_text']):
    if lab==lit:
        if lab==0:
            no_disaster += 1
        else:
            have_disaster += 1
print('没有发生灾难的数据量:{},文中出现location的数据量:{}'.format(df['target'].tolist().count(0), no_disaster))
print('发生灾难的数据量:{},文中出现location的数据量:{}'.format(df['target'].tolist().count(1), have_disaster))

# 统计关键词特征在推文中的出现次数
keyword_in_text = []
for kw, twe in zip(df['keyword'], df['text']):
    if kw in twe:
        keyword_in_text.append(1)
    else:
        keyword_in_text.append(0)
df['keyword_in_text'] = keyword_in_text

have_keyword = 0
no_keyword = 0
for lab, kit in zip(df['target'], df['keyword_in_text']):
    if lab==kit:
        if lab==0:
            no_keyword += 1
        else:
            have_keyword += 1
print('没有发生灾难的数据量:{},文中出现keyword的数据量:{}'.format(df['target'].tolist().count(0), no_keyword))
print('发生灾难的数据量:{},文中出现keyword的数据量:{}'.format(df['target'].tolist().count(1), have_keyword))


# 定义模型权重,可以去HuggingFace找更强大的模型
model_name = 'bert-base-uncased'
# 初始化分词器
tokenizer = BertTokenizer.from_pretrained(model_name)
# 加载模型
model = BertModel.from_pretrained(model_name)

# texts = df['text'].tolist()
texts = df['text'].tolist()
location_info = '. With location:'
keyword_info = 'With keyword:'
texts_with_tips = []
for text, loc, kw, word in zip(texts, loc_in_text, keyword_in_text, df['keyword'].tolist()):
    if loc==1:
        text += location_info+'True.'
    else:
        text += location_info+'False.'
    if kw==1:
        text += keyword_info+word+'True.'+'Keyword:'+word+'.'
    else:
        text += keyword_info+'False.'
    texts_with_tips.append(text)
texts = texts_with_tips

# 定义批量大小
batch_size = 16
# 创建一个空列表来存储所有的句子嵌入
all_sentence_embeddings = []
# 处理数据的批量
for i in range(0, len(texts), batch_size):
    if i % 100 == 0:
        print('processing No.{} sample'.format(i))

    batch_texts = texts[i:i + batch_size]

    # 分词器生成Bert必要的输出格式
    inputs = tokenizer(batch_texts, padding=True, truncation=True, return_tensors="pt")

    # 禁用梯度,打开预测模式
    with torch.no_grad():
        # 预测输出
        outputs = model(**inputs)

    # 取最后一层的token平均向量
    sentence_embeddings = outputs.pooler_output

    # 将当前批量的句子嵌入添加到列表中
    all_sentence_embeddings += sentence_embeddings

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split([list(li) for li in all_sentence_embeddings],
                                                    df['target'].tolist(), stratify=df['target'].tolist(), test_size=0.3, random_state=2024)

# 训练MLP
dtc = MLPClassifier(hidden_layer_sizes=(128,64), max_iter=100, random_state=2024)
dtc.fit(X_train, y_train)
y_pred = dtc.predict(X_test)
print('Prec:', precision_score(y_test, y_pred))
print('Rec:', recall_score(y_test, y_pred))
print('F1:', f1_score(y_test, y_pred))

四、总结

        本文使用推特分类数据集展示了文本数据的分类建模过程,其中使用了Bert预训练语言模型进行文本向量化,并训练一个MLP神经网络进行文本分类。从结果中可以看到,即便是未经过微调,Bert模型仍然表现出了较为强大的文本表示能力,同时使用一定的辅助信息进行语义增强也能在一定程度上提升分类性能。

标签:推特,keyword,texts,python,text,df,location,print,文本
From: https://blog.csdn.net/ChaneMo/article/details/143356712

相关文章

  • Python中的平方功能:方便实用的数据处理利器
    Python作为一门广泛应用于数据科学、机器学习和人工智能领域的编程语言,具有许多实用的功能。其中,Python中的平方功能是一个非常有用和实用的数据处理利器。简洁易用的语法Python中的平方功能使用的是**运算符,其语法为**数**,其中数可以是任意实数、整数或字符串。例如,要计......
  • python基础——02
    一、输入与输出1、print输出第一种:直接输出print("helloworld")name="python"age=3print("项目:",name,"学龄:",age)第二种: 格式化输出 【旧版本代码中常见,现在不推荐使用】%c单字符%s字符串%d%i十进制整数%x%X十六进制整数%o八进制整数%f小数%e%E科学计数......
  • arkUI:文本框、文本域的创建和常见用法(TextInput 、TextArea)
    arkUI:文本框、文本域的创建和常见用法(TextInput、TextArea)1主要内容说明2例子2.1文本框、文本域的创建(TextInput、TextArea)2.1.1源码1(文本框、文本域的创建)2.1.2源码1运行效果2.2设置文本框的输入类型2.2.1源码2(设置输入框的输入类型)2.2.2源码2运行效果2.3......
  • 知识点:Python中的列表合并操作
    知识点:该题目考查的知识点是Python中的列表合并操作。以下是该知识点的相关内容和题目的详细解答过程:知识点:Python中的列表合并操作在Python中,合并两个或多个列表是一项常见的操作,可以通过以下几种方法实现:使用+运算符:使用+运算符可以将两个列表直接拼接在一起,形成一个......
  • python项目实战 小说下载源码
     小说下载#引入框架importtkinterastkimportrequestsfromlxmlimportetree#类classQuery:#类里面的一个固定方法def__init__(self,master)->None:self.wd_1=master#设置窗口大小self.wd_1.geometry("400x330+......
  • 力扣 Python 第7题
    ​classSolution(object):defreverse(self,x):""":typex:int:rtype:int"""y=abs(x)#取绝对值res=0x_min=2**31#找到最左边边界x_max=2**31-1#找到最右边边界......
  • Python列表
    1.列表的定义在Python中,列表是一种有序、可变、允许重复元素的数据结构。它是由一组元素组成的,这些元素可以是不同数据类型的对象,包括数字、字符串、布尔值、其他列表,甚至是自定义对象。列表是用方括号[]括起来的,元素之间用逗号分隔下面是一个简单的python列表的示列:ls=[1,2......
  • Python中self的作用!
    首先self必须跟python中的类class一起使用。在Python中,self是一个在类方法中使用的约定名称,用来代表类的实例本身。实例是指类的具体对象如下面代码的p,后续调用self可以访问类的属性和方法(类中的函数),使得我们能够在类的不同方法中共享数据。理解的关键在于类和实例。以下......
  • 基于yolov8的生猪检测和统计系统,支持图像、视频和摄像实时检测【pytorch框架、python
     更多目标检测和图像分类识别项目可看我主页其他文章功能演示:基于yolov8的生猪检测和统计系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili(一)简介基于yolov8的生猪检测和统计系统是在PyTorch框架之下得以实现的。这是一个完备的项目,涵盖......
  • 0基础学Python装饰器封装、类成员与静态方法、魔术方法
    0基础学Python装饰器封装、类成员与静态方法、魔术方法装饰器封装类成员和静态方法实例属性和方法类属性和类方法静态方法使用场景魔术方法定义初始化与表示方法比较运算方法算术运算方法代码演示装饰器封装装饰器(decorators)是一种高阶函数,用于在不修改原有函数或......