当前位置:   article > 正文

(10-3)BERT :BERT在文本分类任务中的应用_bert模型做分类任务

bert模型做分类任务

BERT在自然语言处理(NLP)领域的成功应用广泛,它的预训练模型能够学到通用的语言表示,从而在各种NLP任务中取得了出色的性能。在本节的内容中,将通过具体实例展示BERT在常见NLP任务中的应用过程。

10.3.1  文本分类中的BERT

请看下面的实例,通过一个完整项目展示了使用BERT模型进行文本分类的过程。该项目使用网络中的博文数据集进行情感分析,利用BERT模型对文本进行训练和分类。训练集包含已手动标记的推文,通过预处理和标记化后,使用BERT模型在五个情感类别上进行训练。项目通过AdamW优化器和学习率调度器进行模型优化,使用PyTorch DataLoader处理训练和验证数据。在训练过程中,通过输出训练损失和验证损失进行性能监控。最后,对测试集上的文本进行预处理和分类,并输出模型对文本情感的预测结果。

本项目用到的数据集文件是两个CSV文件:Corona_NLP_train.csv和Corona_NLP_test.csv,这是从媒体中提取的博文数据,经过人工标记进行了手动标签。为了避免隐私问题,博文中的姓名和用户名已经被编码处理。数据集中各个列的具体信息如下所示。

  1. 位置 (Location):这一列包含关于博文作者所在地的信息,可能包括城市、国家或其他地理位置信息。
  2. 博文时间 (Tweet At):这一列表示博文发布的时间戳,以日期和时间的形式呈现。
  3. 原始博文 (Original Tweet):这是博文的主要文本内容,包含用户在媒体中上发布的实际消息,这是文本分类任务的关键信息。
  4. 标签 (Label):这一列包含了对每条博文进行的手动分配的标签或类别。在本项目的上下文中,它是文本分类任务的目标变量。

实例10-2:综合实战:使用BERT模型进行文本分类(源码路径:daima\10\text-classification.ipynb

实例文件text-classification.ipynb的具体实现流程如下所示。

(1)下面代码使用pandas库读取了两个CSV文件,分别是Corona_NLP_train.csv和Corona_NLP_test.csv,将它们加载为DataFrame对象df和df_test。通过设置encoding='latin-1'参数,解决了潜在的编码问题。这为实现进一步的数据分析和文本分类任务提供了准备好的训练集和测试集。

  1. import pandas as pd
  2. # 读取训练数据集 'Corona_NLP_train.csv'
  3. df = pd.read_csv('covid-19-nlp-text-classification/Corona_NLP_train.csv', encoding='latin-1')
  4. # 读取测试数据集 'Corona_NLP_test.csv'
  5. df_test = pd.read_csv('covid-19-nlp-text-classification/Corona_NLP_test.csv', encoding='latin-1')

(2)使用DataFrame 对象的方法df.head()显示数据集的前几行数据。

df.head()

执行上述代码后,将显示数据集中的前几行,包括 Location、Tweet At、Original Tweet 和 Label 列的信息:

  1. UserName ScreenName Location TweetAt OriginalTweet Sentiment
  2. 0 3799 48751 London 16-03-2020 @MeNyrbie @Phil_Gahan @Chrisitv https://t.co/i... Neutral
  3. 1 3800 48752 UK 16-03-2020 advice Talk to your neighbours family to excha... Positive
  4. 2 3801 48753 Vagabonds 16-03-2020 Coronavirus Australia: Woolworths to give elde... Positive
  5. 3 3802 48754 NaN 16-03-2020 My food stock is not the only one which is emp... Positive
  6. 4 3803 48755 NaN 16-03-2020 Me, ready to go at supermarket during the #COV... Extremely Negative

由此可见,该数据集包含原始博文及其对应的情感标签。情感标签分为五类,分别是:极度负面(Extremely Negative)、负面(Negative)、中性(Neutral)、正面(Positive)、极度正面(Extremely Positive)。

(3)df.info() 是 pandas DataFrame 对象中的方法,用于提供关于数据集的详细信息,包括每列的非空值数量、数据类型等。通过下面的代码,可以获得关于 DataFrame 结构和内容的总体概览。

df.info()

执行上述代码后将看到关于数据集的摘要信息,例如每列的名称、非空值数量、数据类型等:

  1. <class 'pandas.core.frame.DataFrame'>
  2. RangeIndex: 41157 entries, 0 to 41156
  3. Data columns (total 6 columns):
  4. # Column Non-Null Count Dtype
  5. --- ------ -------------- -----
  6. 0 UserName 41157 non-null int64
  7. 1 ScreenName 41157 non-null int64
  8. 2 Location 32567 non-null object
  9. 3 TweetAt 41157 non-null object
  10. 4 OriginalTweet 41157 non-null object
  11. 5 Sentiment 41157 non-null object
  12. dtypes: int64(2), object(4)
  13. memory usage: 1.9+ MB

(4)通过如下代码查看df_test 数据集中的前几行内容。

df_test.head()

执行后将显示测试数据集的前几行,包括 Location、Tweet At、Original Tweet 和 Label 列的信息:

  1. 0,1,44953,NYC,02-03-2020,TRENDING: New Yorkers encounter empty supermar...,Extremely Negative
  2. 1,2,44954,"Seattle, WA",02-03-2020,When I couldn't find hand sanitizer at Fred Me...,Positive
  3. 2,3,44955,NaN,02-03-2020,Find out how you can protect yourself and love...,Extremely Positive
  4. 3,4,44956,Chicagoland,02-03-2020,#Panic buying hits #NewYork City as anxious sh...,Negative
  5. 4,5,44957,"Melbourne, Victoria",03-03-2020,#toiletpaper #dunnypaper #coronavirus #coronav...,Neutral

(5)df['Sentiment'].value_counts() 是一个用于统计 DataFrame 中某一列(在这里是 'Sentiment' 列)中每个唯一值的数量的 pandas 方法。执行下面的代码后,将获得 'Sentiment' 列中每个唯一值的计数。

df['Sentiment'].value_counts()

如果 'Sentiment' 列是关于情感分析的标签,那么这个命令将展示每个情感类别的样本数量。例如,如果 'Sentiment' 列包含正面、负面和中性情感的标签,那么这行代码将告诉我们数据集中每种情感类别的样本数量。执行后将输出一个包含每个情感类别及其对应数量的结果:

  1. Positive              11422
  2. Negative               9917
  3. Neutral                7713
  4. Extremely Positive     6624
  5. Extremely Negative     5481
  6. Name: Sentiment, dtype: int64

(6)下面的方法df_test['Sentiment'].value_counts()是一个 pandas DataFrame 对象的方法,用于统计测试数据集中 'Sentiment' 列中每个唯一情感类别的样本数量。这行代码提供了测试数据中不同情感类别的分布信息,有助于了解模型在各种情感标签上的性能表现。例如,如果 'Sentiment' 列包含正面、负面和中性情感的标签。

df_test['Sentiment'].value_counts()

这行代码将输出各类别样本的数量,为评估模型的分类准确性提供了有用的统计数据:

  1. Negative              1041
  2. Positive               947
  3. Neutral                619
  4. Extremely Positive     599
  5. Extremely Negative     592
  6. Name: Sentiment, dtype: int64

(7)通过下面的代码打印输出数据集中 'OriginalTweet' 列的前五行文本内容,这是一个简单的方式来查看部分推文数据,以便初步了解文本的格式和内容。

print(df['OriginalTweet'][0:5])

执行后会输出:

  1. 0    @MeNyrbie @Phil_Gahan @Chrisitv https://t.co/i...
  2. 1    advice Talk to your neighbours family to excha...
  3. 2    Coronavirus Australia: Woolworths to give elde...
  4. 3    My food stock is not the only one which is emp...
  5. 4    Me, ready to go at supermarket during the #COV...
  6. Name: OriginalTweet, dtype: object

(8)如下代码创建了一个新的列 'Sentiment_class',该列包含了根据 labelmap 字典映射而来的 'Sentiment' 列的数值表示。这有助于将情感标签转换为数字形式,便于在机器学习模型中使用。

  1. labelmap = {"Extremely Negative": 0, "Negative": 1, "Neutral": 2, "Positive": 3, "Extremely Positive": 4}
  2. df['Sentiment_class'] = df['Sentiment'].map(labelmap)
  3. df.head()

对上述代码的具体说明如下所示:

  1. labelmap 字典:定义了情感标签到数值的映射关系。
  2. df['Sentiment'].map(labelmap):使用 map 方法将 'Sentiment' 列中的文本标签映射为相应的数值,并创建了新的列 'Sentiment_class'。
  3. df.head():打印输出 DataFrame 的前几行,包括新创建的 'Sentiment_class' 列:
  1. UserName ScreenName Location TweetAt OriginalTweet Sentiment Sentiment_class
  2. 0 3799 48751 London 16-03-2020 @MeNyrbie @Phil_Gahan @Chrisitv https://t.co/i... Neutral 2
  3. 1 3800 48752 UK 16-03-2020 advice Talk to your neighbours family to excha... Positive 3
  4. 2 3801 48753 Vagabonds 16-03-2020 Coronavirus Australia: Woolworths to give elde... Positive 3
  5. 3 3802 48754 NaN 16-03-2020 My food stock is not the only one which is emp... Positive 3
  6. 4 3803 48755 NaN 16-03-2020 Me, ready to go at supermarket during the #COV... Extremely Negative 0

(9)下面这段代码与上一段代码类似,为测试数据集创建了一个新的列 'Sentiment_class',其中包含了根据 labelmap 字典映射而来的 'Sentiment' 列的数值表示。

  1. labelmap = {"Extremely Negative": 0, "Negative": 1, "Neutral": 2, "Positive": 3, "Extremely Positive": 4}
  2. df_test['Sentiment_class'] = df_test['Sentiment'].map(labelmap)
  3. df_test.head()

对上述代码的具体说明如下所示:

  1. labelmap字典:定义了情感标签到数值的映射关系。
  2. df_test['Sentiment'].map(labelmap):使用 map 方法将 'Sentiment' 列中的文本标签映射为相应的数值,并创建了新的列 'Sentiment_class'。
  3. df_test.head():打印输出DataFrame的前几行内容,包括新创建的 'Sentiment_class' 列:
  1. UserName ScreenName Location TweetAt OriginalTweet Sentiment Sentiment_class
  2. 0 1 44953 NYC 02-03-2020 TRENDING: New Yorkers encounter empty supermar... Extremely Negative 0
  3. 1 2 44954 Seattle, WA 02-03-2020 When I couldn't find hand sanitizer at Fred Me... Positive 3
  4. 2 3 44955 NaN 02-03-2020 Find out how you can protect yourself and love... Extremely Positive 4
  5. 3 4 44956 Chicagoland 02-03-2020 #Panic buying hits #NewYork City as anxious sh... Negative 1
  6. 4 5 44957 Melbourne, Victoria 03-03-2020 #toiletpaper #dunnypaper #coronavirus #coronav... Neutral 2

(10)下面这段代码导入了与文本处理相关的Python库和资源,包括string模块、NLTK自然语言处理库,并下载了NLTK所需的标点符号和停用词资源。这为后续文本处理任务提供了必要的工具和数据,包括字符串处理、分词和去除常用停用词等操作,为文本数据的清理和准备提供基础。

  1. import string
  2. from nltk.corpus import stopwords
  3. from nltk.tokenize import word_tokenize
  4. import nltk
  5. nltk.download('punkt')
  6. nltk.download('stopwords')

执行后会输出:

  1. [nltk_data] Downloading package punkt to /usr/share/nltk_data...
  2. [nltk_data]   Package punkt is already up-to-date!
  3. [nltk_data] Downloading package stopwords to /usr/share/nltk_data...
  4. [nltk_data]   Package stopwords is already up-to-date!
  5. True

(11)下面这段代码定义了两个集合,其中stop_words 包含了英语的停用词,而 punctuations 包含了常见的标点符号。这些集合将在后续的文本处理过程中用于去除文本中的停用词和标点符号,以准备文本数据进行更深入的分析或机器学习任务。

  1. # 定义要移除的停用词和标点符号集合
  2. stop_words = set(stopwords.words('english'))
  3. punctuations = set(string.punctuation)

(12)下面的这段代码定义了一个名为 preprocess_text 的函数,用于对文本数据进行预处理。在这个函数中,对文本进行了小写转换,移除了URL、用户名、数字和标点符号,然后进行了分词并移除了停用词。最后,将处理后的文本重新组合成字符串。这个函数被应用于数据集中 'OriginalTweet' 列的每个文本,以清理和准备文本数据供进一步的分析或机器学习使用。

  1. # 预处理文本数据的函数
  2. def preprocess_text(text):
  3. text = text.lower() # 转换为小写
  4. text = re.sub(r'http\S+', '', text) # 移除URL
  5. text = re.sub(r'@\S+', '', text) # 移除用户名
  6. text = re.sub(r'\d+', '', text) # 移除数字
  7. text = re.sub(r'[^\w\s]', '', text) # 移除标点符号
  8. text = word_tokenize(text) # 分词
  9. text = [word for word in text if word not in stop_words] # 移除停用词
  10. text = [word for word in text if word not in punctuations] # 移除标点符号
  11. text = ' '.join(text)
  12. return text
  13. # 对 'OriginalTweet' 列中的文本应用预处理函数
  14. df['OriginalTweet'] = df['OriginalTweet'].apply(preprocess_text)
  15. df['OriginalTweet'].head()

执行后会输出:

  1. 0                                                     
  2. 1    advice talk neighbours family exchange phone n...
  3. 2    coronavirus australia woolworths give elderly ...
  4. 3    food stock one empty please dont panic enough ...
  5. 4    ready go supermarket covid outbreak im paranoi...
  6. Name: OriginalTweet, dtype: object

由此可见,执行后的输出显示了经过预处理的文本数据,其中 OriginalTweet 列的前几行被清理和转换成了经过处理的文本。下面是对上面输出的具体说明。

  1. 行0:经过预处理后,该行的文本为空。
  2. 行1:文本经过小写转换,移除了URL、用户名、数字和标点符号,进行了分词,并移除了停用词。
  3. 行2:类似于行1,文本经过了相同的处理步骤。
  4. 行3:类似于前两行,文本被清理并经过了相同的处理步骤。
  5. 行4:类似于前三行,文本被清理并经过了相同的处理步骤。

这些清理后的文本数据现在更适合进行文本分析或机器学习任务,因为它们已经去除了一些噪声和非关键信息。

(13)下面的代码导入了一些与机器学习和自然语言处理相关的库和模块,这些库和模块通常在自然语言处理任务中使用,其中 BERT 模型是一个预训练的深度学习模型,可用于文本分类等任务。

  1. from sklearn.model_selection import train_test_split
  2. from transformers import BertTokenizer, BertForSequenceClassification, AdamW

(14)下面的代码指定了使用的BERT模型为预训练的 'bert-base-uncased' 模型,并创建了与该模型相对应的标记器 tokenizer 和序列分类模型 model。num_labels=5 指定了输出标签的数量,适用于处理具有五个不同情感类别的文本分类任务。这为后续的模型训练和预测提供了基本的结构和配置。

  1. # 定义BERT模型和标记器
  2. model_name = 'bert-base-uncased'
  3. tokenizer = BertTokenizer.from_pretrained(model_name)
  4. model = BertForSequenceClassification.from_pretrained(model_name, num_labels=5)

执行后会输出:

  1. Downloading (…)solve/main/vocab.txt: 100%
  2. 232k/232k [00:00&lt;00:00, 3.59MB/s]
  3. Downloading (…)okenizer_config.json: 100%
  4. 28.0/28.0 [00:00&lt;00:00, 1.12kB/s]
  5. Downloading (…)lve/main/config.json: 100%
  6. 570/570 [00:00&lt;00:00, 33.7kB/s]
  7. Downloading (…)&quot;pytorch_model.bin&quot;;: 100%
  8. 440M/440M [00:02&lt;00:00, 191MB/s]
  9. Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertForSequenceClassification: ['cls.predictions.transform.LayerNorm.weight', 'cls.predictions.transform.dense.bias', 'cls.predictions.transform.dense.weight', 'cls.predictions.bias', 'cls.seq_relationship.weight', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.decoder.weight', 'cls.seq_relationship.bias']
  10. - This IS expected if you are initializing BertForSequenceClassification from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
  11. - This IS NOT expected if you are initializing BertForSequenceClassification from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
  12. Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-uncased and are newly initialized: ['classifier.bias', 'classifier.weight']
  13. You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.

上述输出信息表示正在从 Hugging Face 模型库下载 BERT 模型的相关文件,包括词汇表、模型配置、权重等。其中,模型 bert-base-uncased 的下载进度以及下载速度也被显示出来。最后的提示建议对该模型进行下游任务的训练,以便能够在预测和推理中使用。这是因为在下游任务中微调模型权重通常是必要的,以提高模型性能。

(15)下面代码使用 train_test_split 函数将原始数据集中的 'OriginalTweet' 列和 'Sentiment_class' 列分割为训练集和验证集。

train_texts, val_texts, train_labels, val_labels = train_test_split(df['OriginalTweet'], df['Sentiment_class'], test_size=0.2, random_state=42)

对上述代码的具体说明如下所示:

  1. train_texts包含了训练集中的文本数据,而 val_texts 包含了验证集中的文本数据。
  2. train_labels包含了训练集中对应的情感类别标签,而 val_labels 包含了验证集中对应的情感类别标签。
  3. test_size=0.2指定了验证集的大小占总数据集的20%。
  4. random_state=42用于设置随机种子,以确保可复现的随机分割结果。

(16)下面代码是准备将数据输入到 BERT 模型中的关键步骤。使用 BERT 模型的标记器 tokenizer 将训练集和验证集的文本数据进行标记化,同时进行截断和填充以保证相同长度。标记化后的文本数据被转换成模型可接受的输入张量形式,训练集和验证集的情感类别标签也被转换为 PyTorch 张量,以便进行后续的模型训练和评估。

  1. # 对文本进行标记化并转换为输入张量
  2. train_encodings = tokenizer(list(train_texts), truncation=True, padding=True)
  3. val_encodings = tokenizer(list(val_texts), truncation=True, padding=True)
  4. train_labels = torch.tensor(list(train_labels))
  5. val_labels = torch.tensor(list(val_labels))

(17)下面代码创建了用于训练的数据加载器,将标记化后的输入文本张量和情感类别标签整合为 TensorDataset 类型的数据集。

  1. # 为训练设置数据加载器
  2. from torch.utils.data import TensorDataset
  3. train_dataset = torch.utils.data.TensorDataset(
  4. torch.tensor(train_encodings['input_ids']),
  5. torch.tensor(train_encodings['attention_mask']),
  6. train_labels
  7. )
  8. val_dataset = torch.utils.data.TensorDataset(
  9. torch.tensor(val_encodings['input_ids']),
  10. torch.tensor(val_encodings['attention_mask']),
  11. val_labels
  12. )

在上述代码中,train_dataset 包含了训练集的输入文本张量(input_ids)、注意力掩码张量(attention_mask)以及情感类别标签。val_dataset 包含了验证集的相同类型的张量数据。这为后续的模型训练提供了适用于 PyTorch DataLoader 的数据集,使数据能够以批量的形式输入到模型中进行训练。

(18)下面这段代码利用PyTorch的DataLoader创建了训练和验证数据的迭代器,分别采用了RandomSampler和SequentialSampler来对训练集和验证集进行采样。通过设置合适的批量大小,这些数据迭代器为BERT模型的训练和验证准备了有效的小批量数据输入。RandomSampler确保了训练数据的随机采样,而SequentialSampler保持了验证数据的顺序采样,为模型的优化和评估提供了合适的数据组织方式。

  1. from torch.utils.data import DataLoader, RandomSampler, SequentialSampler
  2. # 创建训练数据迭代器
  3. dataloader_train = DataLoader(train_dataset,
  4. sampler=RandomSampler(train_dataset),
  5. batch_size=3)
  6. # 创建验证数据迭代器
  7. dataloader_validation = DataLoader(val_dataset,
  8. sampler=SequentialSampler(val_dataset),
  9. batch_size=3)

(19)下面这段代码使用Hugging Face Transformers库中的AdamW优化器和学习率调度器,对BERT模型的参数进行优化。将学习率设置为1e-5,同时使用了线性调度器和预热,以在模型训练的初期进行学习率的逐渐升高,然后进行线性衰减。这有助于更稳健地训练模型,提高其性能。

  1. # 使用AdamW优化器和学习率调度器设置BERT模型的参数
  2. from transformers import AdamW, get_linear_schedule_with_warmup
  3. optimizer = AdamW(model.parameters(),lr=1e-5, eps=1e-8)
  4. epochs = 5
  5. # 设置学习率调度器,使用线性衰减和预热进行模型训练
  6. scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=0,num_training_steps=len(dataloader_train)*epochs)

(20)下面代码定义了一个计算加权 F1 分数的函数,用于在训练和验证过程中评估模型性能。函数f1_score_func()接受模型的预测结果 preds 和实际标签 labels,将预测结果转换为一维数组,然后使用 f1_score 函数计算加权 F1 分数。这有助于在文本分类任务中量化模型的性能,特别是在处理不平衡类别的情况下。

  1. # 定义计算加权 F1 分数的函数
  2. from sklearn.metrics import f1_score
  3. def f1_score_func(preds, labels):
  4. preds_flat = np.argmax(preds, axis=1).flatten()
  5. labels_flat = labels.flatten()
  6. return f1_score(labels_flat, preds_flat, average='weighted')

(21)下面代码设置了随机种子,确保实验在不同运行时具有相同的随机性,有助于结果的可重复性。同时,将PyTorch设备设置为GPU(如果可用),以加速模型训练和推理。

  1. # 设置随机种子和设备,确保实验的可重复性和在GPU上运行
  2. import random
  3. seed_val = 17
  4. random.seed(seed_val)
  5. np.random.seed(seed_val)
  6. torch.manual_seed(seed_val)
  7. torch.cuda.manual_seed_all(seed_val)
  8. device = torch.device('cuda')

(22)使用BERT模型对训练集进行多个epoch的训练。每个epoch中,模型在训练数据上进行前向传播、损失计算和反向传播,通过梯度裁剪和优化器更新参数。在训练过程中,通过进度条显示了每个小批量的训练损失。

  1. # 将模型移动到GPU,然后进行多个epoch的模型训练
  2. from tqdm.notebook import tqdm
  3. model.to(device)
  4. for epoch in tqdm(range(1, epochs+1)):
  5. model.train()
  6. loss_train_total = 0
  7. # 遍历训练数据加载器进行训练
  8. progress_bar = tqdm(dataloader_train, desc='Epoch {:1d}'.format(epoch), leave=False, disable=False)
  9. for batch in progress_bar:
  10. model.zero_grad()
  11. batch = tuple(b.to(device) for b in batch)
  12. inputs = {'input_ids': batch[0].to(device),
  13. 'attention_mask': batch[1].to(device),
  14. 'labels': batch[2].to(device),
  15. }
  16. # 前向传播和损失计算
  17. outputs = model(**inputs)
  18. loss = outputs[0]
  19. loss_train_total += loss.item()
  20. loss.backward()
  21. torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
  22. optimizer.step()
  23. scheduler.step()
  24. progress_bar.set_postfix({'training_loss': '{:.3f}'.format(loss.item()/len(batch))})
  25. # 输出每个epoch的训练损失
  26. tqdm.write(f'\nEpoch {epoch}')
  27. loss_train_avg = loss_train_total/len(dataloader_train)
  28. tqdm.write(f'Training loss: {loss_train_avg}')

训练完成后,输出显示每个epoch的平均训练损失:

  1. 100% 5/5 [1:02:23&lt;00:00, 747.41s/it]
  2. Epoch 1
  3. Training loss: 0.9643278913025941
  4. Epoch 2
  5. Training loss: 0.811084389945594
  6. Epoch 3
  7. Training loss: 0.6857011029520337
  8. Epoch 4
  9. Training loss: 0.570854452219028
  10. Epoch 5
  11. Training loss: 0.46446557194648996

(23)下面代码定义了一个用于在验证集上评估BERT模型性能的函数evaluate(dataloader_val)。函数evaluate(dataloader_val)通过遍历验证数据加载器,计算模型在验证集上的损失,同时记录预测值和真实标签。最后,函数返回平均验证损失、模型的预测值和真实标签,以用于后续性能分析。

  1. # 定义评估函数,用于在验证集上评估模型性能
  2. def evaluate(dataloader_val):
  3. model.eval()
  4. loss_val_total = 0
  5. predictions, true_vals = [], []
  6. # 遍历验证数据加载器进行模型评估
  7. for batch in dataloader_val:
  8. batch = tuple(b.to(device) for b in batch)
  9. inputs = {'input_ids': batch[0],
  10. 'attention_mask': batch[1],
  11. 'labels': batch[2],
  12. }
  13. with torch.no_grad():
  14. outputs = model(**inputs)
  15. loss = outputs[0]
  16. logits = outputs[1]
  17. loss_val_total += loss.item()
  18. logits = logits.detach().cpu().numpy()
  19. label_ids = inputs['labels'].cpu().numpy()
  20. predictions.append(logits)
  21. true_vals.append(label_ids)
  22. loss_val_avg = loss_val_total/len(dataloader_val)
  23. # 将预测和真实标签连接成数组并返回
  24. predictions = np.concatenate(predictions, axis=0)
  25. true_vals = np.concatenate(true_vals, axis=0)
  26. return loss_val_avg, predictions, true_vals

(24)下面代码调用了之前定义的评估函数 evaluate 来在验证集上评估BERT模型的性能,并计算了验证集上的损失和F1分数。

  1. # 在验证集上进行模型评估并输出验证损失和F1分数
  2. val_loss, predictions, true_vals = evaluate(dataloader_validation)
  3. val_f1 = f1_score_func(predictions, true_vals)
  4. # 打印验证损失和F1分数
  5. print('Val Loss = ', val_loss)
  6. print('Val F1 = ', val_f1)

执行后会输出:

  1. Val Loss =  1.211540051294692
  2. Val F1 =  0.7862622789639281

(25)true_vals 是在验证集上真实的情感类别标签数组。在模型评估过程中,通过 evaluate 函数获取了验证集上每个样本的真实标签。

true_vals

执行后会输出数组:

array([2, 0, 3, ..., 1, 2, 1])

这个数组包含了验证集中每个样本的真实情感类别,用于与模型的预测进行比较和性能评估。

(26)下面代码定义了一个计算分类准确率的函数 get_accuracy,然后使用该函数计算了在验证集上的准确率。

  1. # 定义计算分类准确率的函数
  2. def get_accuracy(predictions, true_vals):
  3. preds = np.argmax(predictions, axis=1)
  4. acc = accuracy_score(true_vals, preds)
  5. return acc
  6. # 计算并打印在验证集上的准确率
  7. accuracy = get_accuracy(predictions, true_vals)
  8. print('Accuracy on Validation Set = ', accuracy)

函数 get_accuracy使用 np.argmax 获取预测值中的最大概率对应的类别,并与真实标签比较以计算准确率。最后,打印输出准确率,提供了对模型在验证集上的整体性能的评估。

0.7865646258503401

(27)读取测试数据集 Corona_NLP_test.csv,并打印输出其中的前几行数据。

  1. df_test = pd.read_csv('covid-19-nlp-text-classification/Corona_NLP_test.csv', encoding='latin-1')
  2. df_test.head()

执行后会输出:

  1. UserName ScreenName Location TweetAt OriginalTweet Sentiment
  2. 0 1 44953 NYC 02-03-2020 TRENDING: New Yorkers encounter empty supermar... Extremely Negative
  3. 1 2 44954 Seattle, WA 02-03-2020 When I couldn't find hand sanitizer at Fred Me... Positive
  4. 2 3 44955 NaN 02-03-2020 Find out how you can protect yourself and love... Extremely Positive
  5. 3 4 44956 Chicagoland 02-03-2020 #Panic buying hits #NewYork City as anxious sh... Negative
  6. 4 5 44957 Melbourne, Victoria 03-03-2020 #toiletpaper #dunnypaper #coronavirus #coronav... Neutral

(28)下面代码获取了测试集中的文本数据,存储在 test_texts 变量中。此外,将BERT模型设置为评估模式(model.eval()),这是因为在测试阶段不再进行梯度更新和训练,而是用于生成模型的预测结果。

  1. test_texts = df_test['OriginalTweet']
  2. model.eval()

(29)如下代码首先对测试数据的原始文本进行预处理,包括转换为小写、删除URLs、删除用户名等。接着,使用之前定义的 preprocess_text 函数对测试集中的 'OriginalTweet' 列进行处理。最后,代码展示了预处理后的测试文本的前几行。注释中的标记器部分被注释掉了,大家可以根据需要取消注释并使用标记器对测试数据进行处理。

  1. # 使用标记器对测试数据进行预处理
  2. df_test['OriginalTweet'] = df_test['OriginalTweet'].apply(preprocess_text)
  3. # 对预处理后的测试文本进行标记化,并截断、填充以及转换为模型可接受的PyTorch张量
  4. # test_inputs = tokenizer(test_texts, padding=True, truncation=True, max_length=512, return_tensors='pt')
  5. # test_inputs = test_inputs.to(device)
  6. # 打印预处理后的测试文本的前几行
  7. df_test['OriginalTweet'].head()

执行后会输出:

  1. 0    trending new yorkers encounter empty supermark...
  2. 1    couldnt find hand sanitizer fred meyer turned ...
  3. 2                  find protect loved ones coronavirus
  4. 3    panic buying hits newyork city anxious shopper...
  5. 4    toiletpaper dunnypaper coronavirus coronavirus...
  6. Name: OriginalTweet, dtype: object

上面输出的是经过预处理后的测试数据的前几行,这些文本经过转换为小写、去除URLs、去除用户名等处理,以便进行后续的标记化和模型输入。在输出中,每行都包含了经过处理的文本。

(30)通过如下代码输出了经过预处理后的测试集中第一行的文本。在这个例子中,文本是处理后的 "OriginalTweet" 列的第一个样本。

print(df_test['OriginalTweet'].head(1))

执行后会输出:

  1. 0    trending new yorkers encounter empty supermark...
  2. Name: OriginalTweet, dtype: object

(31)下面代码首先对给定的测试文本进行标记化,然后使用训练好的BERT模型进行预测。最后,打印输出每个测试样本的文本和预测标签。

  1. # 对测试数据进行标记化,并使用训练好的BERT模型进行预测
  2. test_texts = ["find protect loved ones coronavirus","couldnt find hand sanitizer fred meyer turned"]
  3. test_inputs = tokenizer(test_texts, padding=True, truncation=True, max_length=512, return_tensors='pt')
  4. test_inputs = test_inputs.to(device)
  5. # 利用训练好的模型进行预测,并输出预测结果
  6. with torch.no_grad():
  7. test_outputs = model(**test_inputs)
  8. test_predictions = test_outputs[0].argmax(axis=1)
  9. # 打印预测结果
  10. for i in range(len(test_texts)):
  11. text = test_texts[i]
  12. label = test_predictions[i]
  13. print(f'Text: {text}\nPredicted Label: {label}\n')

执行后会输出:

  1. Text: find protect loved ones coronavirus
  2. Predicted Label: 4
  3. Text: couldnt find hand sanitizer fred meyer turned
  4. Predicted Label: 3

上面的输出结果表明了我们的模型对指定的测试文本进行了分类预测,每个文本样本都伴随着其对应的预测标签,这些标签是根据模型的预测结果确定的。在这个例子中,第一个文本的预测标签是4,第二个文本的预测标签是3。这些标签通常与情感类别映射相关,具体映射可能需要查看或定义在训练期间使用的情感标签映射。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/780789
推荐阅读
相关标签
  

闽ICP备14008679号