Kaggle - LLM Science Exam(二):Open Book QAdebertav3-large详解

在这里插入图片描述

文章目录

    • 前言:优秀notebook介绍
    • 三、Open Book Q&A
      • 3.1 概述
      • 3.2 安装依赖,导入数据
      • 3.3 数据预处理
        • 3.3.1 处理prompt
        • 3.3.2 处理wiki数据
      • 3.4 使用faiss搜索获取匹配的Prompt-Sentence Pairs
      • 3.5 查看context结果并保存
      • 3.6 推理
        • 3.6.1 加载测试集
        • 3.6.2 定义预处理函数和后处理函数
        • 3.6.3 开始推理
        • 3.6.3.1 使用trainer进行推理(方法7)
        • 3.6.3.2 使用Pytorch进行推理(方法8)

前言:优秀notebook介绍

本文完整kaggle notebook见《Open Book QA&debertav3-large详解》,如果觉得有用,请投一票,谢谢

  本系列第一篇文章《Kaggle - LLM Science Exam(一):赛事概述、数据收集、BERT Baseline》中详细介绍了赛事背景、数据集、评测指标等等,以及下面列举优秀notebook中的方法1、3、4,可供参考。

以下是收集的部分优秀notebook:

  1. 《Starter Notebook: Ranked Predictions with BERT》:Bert Baseline,使用bert-base-cased和比赛提供的200个训练集样本进行训练,Public Score=0.545

  2. 《[EDA, Data gathering] LLM-SE ~ Wiki STEM | 1k DS》(制作训练数据):比赛提供的200个样本太少了,作者LEONID KULYK先分析了比赛数据集,然后同样使用 gpt3.5 制作了1000个Wikipedia样本,数据集上传在Wikipedia STEM 1k。

  3. 《LLM-SE ~ deberta-v3-large -i | 1k Wiki》:LEONID KULYK将自己收集的1000个Wikipedia样本和比赛训练集合并,一起训练,模型是deberta-v3-large。notebook中有最终模型权重,可直接推理,LB= 0.709

  4. 《New dataset + DEBERTA v3 large training!》:0.723→0.759

    • Radek 基于方法3,使用自己生成的500个额外数据训练DEBERTA v3 large,Public Score=0.723

    • Radek后来又生成了6000条数据,跟之前的500条融合为6.5K数据集,并在此基础上进行三次训练,得到了三个模型权重,上传在Science Exam Trained Model Weights中。然后通过下面两种方法,进行推理:

      • 《Inference using 3 trained Deberta v3 models》:三个模型分别预测之后概率取平均,Public Score=0.737

      • An introduction to Voting Ensemble:作者在这个notebook中详细介绍了Voting Ensemble以及使用方法,Public Score=0.759

    • 作者最后上传了15k high-quality train examples。

  5. 《Open Book LLM Science Exam》:jjinho首次提出了Open Book方法,演示了如何在训练集中,使用faiss 执行相似性搜索,找到与问答数据最相似的context(Wikipedia数据),以增强问答效果。

  6. 《Open Book LLM Science Exam - Reduced RAM usage》:quangbk改进了方法5中的内存效率。

  7. 《OpenBook DeBERTaV3-Large Baseline (Single Model》): Anil将方法4和方法6结合起来。他将先测试集数据按照方法6搜索出context,然后将其与prompt合并,得到新的测试集。然后加载方法4训练的模型并使用trainer进行推理,Public Score=0.771

    test_df["prompt"] = test_df["context"] + " #### " +  test_df["prompt"]
    
  8. 《Sharing my trained-with-context model》:Mgoksu同样使用了方法7,只是使用了自己制作的数据集进行离线训练,得到一个更好的模型llm-science-run-context-2,然后进行推理(没有使用trainer),top public LB=0.807。方法8和7只是模型以及最后推理部分代码不一样。

  9. 《How To Train Open Book Model - Part 1》、《How To Train Open Book Model - Part 2》:

    • CHRIS DEOTTE在part1中,参照方法8在自己制作的60k数据集进行训练,得到模型model_v2;然后在part2中使用方法8中的模型llm-science-run-context-2以及model_v2分别进行推理,得到的两个概率取平均,得到最终结果(Public Score=0.819)。
    • 在part1中,作者使用了竞赛指标MAP@3 来评估模型,并讨论了一些训练技巧,例如使用 PEFT或冻结model embeddings&model layers来减少训练参数、增加 LR 并减少 epochs来减少计算量、 使用gradient_checkpointing(这使用磁盘来节省RAM)、使用gradient_accumlation_steps模拟更大的批次等等。
  10. 《LLM Science Exam Optimise Ensemble Weights》:作者首先使用了方法9训练的模型权重;另外为了增加多样性,还融合了其它几个没有使用Open Book的deberta-v3-large模型,最终Public Score=0.837。作者还写了以下notebook:

    • 《Incorporate MAP@k metrics into HF Trainer》:在Trainer中加入MAP@k指标
    • 《Introducing Adversarial Weight Perturbation (AWP)》、《Adversarial Weight Perturbation (AWP) Inference》:介绍对抗性权重扰动AWP,以及推理方法。
    • 《Using DeepSpeed with HF🤗 Trainer》,希望可以节约内存,以便训练更大的模型。
  11. 《LLM-SciEx Optimise Ensemble Weights(better models)》:类似方法10,通过模型融合,Public Score=0.846

  12. 《with only 270K articles》:作者自己制作了270K Wikipedia数据,使用LongFormer 模型而不是deberta-v3-large进行训练,Public Score=0.862

  13. 《Platypus2-70B with Wikipedia RAG》:SIMJEG结合了方法8和12,最终Public Score=0.872ALI在 《Explained Platypus2-70B + Wikipedia RAG》中对此notebook做了详细的说明。

三、Open Book Q&A

3.1 概述

  在方法5中,我们先搜索出跟每条样本的prompt_embeddings最相似维基百科文章,然后将这些文章分割为句子。然后综合prompt+answer得到question_embeddings进行第二次搜索,在上一步分割的句子中搜索出与每个question_embeddings最相似的句子,得到与问答最相关的上下文context。

  方法7中,合并context与prompt,然后进行常规的推理过程,总的来说就是使用最有帮助的维基百科句子来增强问答数据。下面详细讲解。

  1. Open Book LLM Science Exam - Reduced RAM usage(方法5)

  Open Book Q&A方法使用的Wikipedia Plaintext数据集,包含 2023 年 7 月 1 日维基百科转储中的 6,286,775 篇文章、标题、文本和类别。文章按字母数字顺序排序,并分成与文章标题的第一个字符对应parquet文件,即分区为 a - z 、 number (以数字开头的标题)和 other (以符号开头的标题)的 parquet 文件。
在这里插入图片描述

整个算法步骤如下:

  • 读取Wikipedia Plaintext数据集
  • 使用 sentence transformers(all-MiniLM-L6-v2 model)将训练集prompts(问题)和Wikipedia文章 转为embeddings,使用每篇文章的第一句增强上下文。
  • 使用 faiss 执行相似性搜索,以查找出200条测试集问答数据prompt最可能具有其所需信息的前 k 篇文章的索引(本文取k=3,最终得到576篇文章而不是600篇,因为部分是重复的)
  • 根据索引获取文章的全文(wiki_text_data),使用 blingfire 库将它们分成句子
  • 合并prompt和全部answer,执行相似性搜索以获得每个问答数据(question_embeddings)最匹配的前m个匹配句子(context)
  • 将prompt、answer和context结合起来,要么直接进行问题回答(方法6),要么输入LLM(方法7)
  1. OpenBook DeBERTaV3-Large Baseline (Single Model)(方法7)

  上一个notebook中,我们使用faiss 执行相似性搜索,得到了与prompt和answer最相似的context。将其与问答数据合并,增强上下文,得到新的测试集问答数据:

test_df["prompt"] = test_df["context"] + " #### " +  test_df["prompt"]

然后对这个增强的测试集,执行常规的多选问答推理。

在这里插入图片描述

3.2 安装依赖,导入数据

# 因为是notebook比赛模式,不能联网,所以从input中安装依赖库
!pip install -U /kaggle/input/faiss-gpu-173-python310/faiss_gpu-1.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
!cp -rf /kaggle/input/sentence-transformers-222/sentence-transformers /kaggle/working/sentence-transformers
!pip install -U /kaggle/working/sentence-transformers
!pip install -U /kaggle/input/blingfire-018/blingfire-0.1.8-py3-none-any.whl!pip install --no-index --no-deps /kaggle/input/llm-whls/transformers-4.31.0-py3-none-any.whl
#!pip install --no-index --no-deps /kaggle/input/llm-whls/peft-0.4.0-py3-none-any.whl
!pip install --no-index --no-deps /kaggle/input/llm-whls/datasets-2.14.3-py3-none-any.whl
#!pip install --no-index --no-deps /kaggle/input/llm-whls/trl-0.5.0-py3-none-any.whl
import os,gc,re,faiss
import pandas as pd
import numpy as np
import blingfire as bffrom tqdm.auto import tqdm
from collections.abc import Iterable
from faiss import write_index, read_index
from sentence_transformers import SentenceTransformerimport torch
import ctypes
libc = ctypes.CDLL("libc.so.6")
MODEL = '/kaggle/input/sentencetransformers-allminilml6v2/sentence-transformers_all-MiniLM-L6-v2'
DEVICE = 0
MAX_LENGTH = 384
BATCH_SIZE = 16WIKI_PATH = "/kaggle/input/wikipedia-20230701"
wiki_files = os.listdir(WIKI_PATH) 					# ['x.parquet', 'h.parquet', 'w.parquet',...]

3.3 数据预处理

3.3.1 处理prompt

  我们观察到prompt的主题几乎总是在最后。下面使用 sentence_transformers 对整个prompt进行编码,方便后续使用语义搜索来查找相关文章。

model = SentenceTransformer(MODEL, device='cuda')
model.max_seq_length = MAX_LENGTH
model = model.half()trn = pd.read_csv("/kaggle/input/kaggle-llm-science-exam/test.csv").drop("id", 1)
trn.head()

在这里插入图片描述

prompt_embeddings = model.encode(trn.prompt.values, batch_size=BATCH_SIZE, device=DEVICE, show_progress_bar=True, convert_to_tensor=True, normalize_embeddings=True)
# 将查询向量从GPU上的张量转换为NumPy数组,以便后续在Faiss中使用
prompt_embeddings = prompt_embeddings.detach().cpu().numpy()
print(prompt_embeddings,prompt_embeddings.shape)
# 手动触发Python的垃圾回收,以释放之前的不再使用的内存
_ = gc.collect()

prompt_embeddings加.half(),后面sentence_index.search会报错TypeError: in method 'IndexFlat_search', argument 3 of type 'float const *'

array([[-4.5478687e-02, -1.6479595e-02,  4.3971878e-02, ...,-5.1500157e-02, -9.1974944e-02,  7.7763526e-03],[-2.6538833e-03, -5.8031674e-02, -4.0828194e-02, ...,3.2339178e-02,  1.4145578e-02, -2.4660153e-02],[-6.8248026e-02,  9.7041421e-02, -7.8083292e-02, ...,4.0758580e-02,  4.6507336e-02,  2.7111586e-02],...,[-7.3988572e-02, -1.4278200e-02, -4.0605428e-05, ...,-1.0465340e-02, -2.3960188e-02, -3.3164129e-02],[-2.5413906e-02,  3.4185217e-04, -4.3551046e-02, ...,-4.7686603e-02,  1.2639660e-01, -5.0002653e-02],[-5.9531994e-02, -7.2587818e-02, -2.9483654e-02, ...,2.7272794e-03, -2.6037039e-02,  2.4748029e-02]], dtype=float32)(200, 384)
3.3.2 处理wiki数据
  1. 获取与prompt最关联的wiki文档
# 预计算的维基百科索引
# 使用read_index函数从读取Faiss索引文件(wikipedia_202307.index),并将其加载到sentence_index变量中
sentence_index = read_index("/kaggle/input/wikipedia-2023-07-faiss-index/wikipedia_202307.index")# 查看第一条数据的embedding
first_embedding = sentence_index.reconstruct(0)
print( first_embedding,first_embedding.shape)
[-1.56402588e-03  1.73797607e-02 -5.81665039e-02 -6.96182251e-03-6.85424805e-02  6.41479492e-02  1.04446411e-02  1.54647827e-02...-1.45645142e-02 -4.87670898e-02  4.37545776e-03 -7.89184570e-029.16137695e-02 -9.45434570e-02  2.29339600e-02  7.09838867e-02]
(384,)

  sentence_index是Faiss索引对象(本示例中,它是从指定路径读取的 Faiss 索引文件),它的内容通常包括embeddings、索引结构(根据嵌入向量数据构建的,以便能够快速找到最相似的向量)以及 其他元信息(如索引维度、距离度量标准等)。

# 获取可能包含感兴趣主题的前3页
search_score, search_index = sentence_index.search(prompt_embeddings, 3)
search_score,search_index,search_index.shape
# search_score,shape为(200, 3)
array([[0.8242705 , 0.9412608 , 0.9816439 ],[0.3884983 , 0.79613495, 0.82003427],...[0.6471102 , 0.6917976 , 0.73008466]], dtype=float32)# search_index,shape为(200, 3)
array([[3573843, 4906500, 1830796],[1431454, 5135549, 5135229],...[1464446,  363737, 1464453]]

  sentence_index.search 是 Faiss 库中用于执行向量检索操作的方法,上述代码是对 prompt_embeddings 中的每个查询向量(每一行,也就是数据集中每个样本的嵌入向量)执行向量查找操作。对于每个查询向量,Faiss 库会在 sentence_index 中查找与其最相似的向量,并返回相似度分数search_score及相似向量的位置索引search_index(使用这些索引位置可以检索实际的嵌入向量或相关数据)。

  以上操作的目的,是为了找到与一组查询向量最相似的数据点,以便进行相关任务,比如信息检索或推荐系统。

# 内存涨到12.3GB,删除不再需要变量,释放内存,降到3.3GB
del sentence_index
del prompt_embeddings
_ = gc.collect()
libc.malloc_trim(0)
  • _ = gc.collect():这行代码手动触发了 Python 的垃圾回收机制(Garbage Collection)。垃圾回收是一种用来识别和释放不再使用的内存的机制。虽然 Python 通常会自动执行垃圾回收,但在某些情况下,手动触发垃圾回收可以更快地释放内存。
  • libc.malloc_trim(0):在特定情况下执行的系统级内存管理操作,用于释放更多内存。malloc_trim 是与 C 语言中的内存分配函数 malloc 相关的操作,它的目的是将未使用的内存返回给操作系统。
# 加载维基百科索引文件,仅包括'id'和'file'两列
df = pd.read_parquet("/kaggle/input/wikipedia-20230701/wiki_2023_index.parquet", columns=['id', 'file'])
df		# 6286775 rows × 2 columns
				id			file
0			49495844	a.parquet
1			3579086		a.parquet...	...	...6286773		18920475	z.parquet
6286774		51132758	z.parquet

使用索引获取文章和关联的文件位置

wikipedia_file_data = []# 遍历search_score和search_index,使用tqdm显示进度
for i, (scr, idx) in tqdm(enumerate(zip(search_score, search_index)), total=len(search_score)):    # scr_idx = idx[np.where(scr <= 0.85)] # 作者准备设置分数阈值,最后取消了scr_idx = idx_df = df.loc[scr_idx].copy() 		   # 从df中选择与索引对应的行,并创建副本_df['prompt_id'] = iwikipedia_file_data.append(_df)wikipedia_file_data 					   # 长为200的列表,每个都包含prompt的索引以及最相似的维基百科文章的索引及其位置
			id       file  		prompt_id
3573843  55518323  m.parquet          0
4906500  15739602  r.parquet          0
1830796     12571  g.parquet          0id       file  		prompt_id
1431454  52303418  d.parquet          1
5135549  58495269  s.parquet          1
5135229   5782346  s.parquet          1,
...
wikipedia_file_data = pd.concat(wikipedia_file_data).reset_index(drop=True)
# drop_duplicates对数据去重,然后按file和id进行排序,最后重装索引
wikipedia_file_data = wikipedia_file_data[['id', 'prompt_id', 'file']].drop_duplicates().sort_values(['file', 'id']).reset_index(drop=True)
wikipedia_file_data 
	id		prompt_id	  file
0	1141		151		a.parquet
1	11963992	185		a.parquet
2	1200		63		a.parquet
3	1234		130		a.parquet
4	1317		89		a.parquet
...	...	...	...
595	810077		27		x.parquet
596	1063160		46		y.parquet
597	31557501	49		y.parquet
598	47610211	49		y.parquet
599	34527		103		z.parquet

  这样我们就得到了与200个问题最相似的维基百科文章的全部索引(id),以及每个文章所在的parquet文件。

# 删除不再需要的 df ,节省内存
del df
_ = gc.collect()
libc.malloc_trim(0)

  下面我们遍历每个parquet文件名(file,需要去重),先获取所有当前parquet文件需要查找的文章索引(id),然后读取parquet文件的id和text两列;最后根据id筛选出其中的文档text,存储在wiki_text_data中。

wiki_text_data = []# 遍历唯一的文件名列表
for file in tqdm(wikipedia_file_data.file.unique(), total=len(wikipedia_file_data.file.unique())):# 从文件名相匹配的行中提取 'id' 列的值,并将其转为一个字符串列表_id = [str(i) for i in wikipedia_file_data[wikipedia_file_data['file']==file]['id'].tolist()]# 读取parquet文件中'id'和'text'列_df = pd.read_parquet(f"{WIKI_PATH}/{file}", columns=['id', 'text'])# 创建一个副本,筛选出parquet文件中搜索出的id行_df_temp = _df[_df['id'].isin(_id)].copy()del _df_ = gc.collect()  	 # 手动触发垃圾回收,释放内存libc.malloc_trim(0)  # 释放更多内存(这部分可能是特定于系统的内存管理操作)wiki_text_data.append(_df_temp)# 将结果列表中的数据合并成一个新的DataFrame,去除重复项,并重置索引
wiki_text_data = pd.concat(wiki_text_data).drop_duplicates().reset_index(drop=True)
wiki_text_data
		id						text
0	1550261	The American Petroleum Institute gravity, or A...
1	46674381	In mathematics and physics, acceleration is th...
2	424420	Accelerator physics is a branch of applied phy...
3	1234	Acoustic theory is a scientific field that rel...
4	68418053	Alan Louis Selman (April 2, 1941 – January 22,...
...	...	...
571	61265537	Xenambulacraria is a proposed clade of animals...
572	31557501	Year of No Light is a French post-metal band f...
573	47610211	
574	1063160	was a Japanese-American physicist and professo...
575	34527	The zodiacal light (also called false dawn whe...

上述代码中,我们遍历唯一的文件名列表wikipedia_file_data.file.unique():

  • wikipedia_file_data[wikipedia_file_data['file']==file]:使用布尔索引来选择 wikipedia_file_data 中当前迭代的file(文件名 )的行。
  • ['id'].tolist():从过滤后的结果中选择 ‘id’ 列(Series格式),然后转为Python列表格式
  • [str(i) for i in ...]:使用列表推导式,将这个列表中的每个值转换为字符串,并存储在 _id 列表中。
  1. 定义文档处理函数,将Wik文档拆分为句子并编码
# 主函数,用于处理文档
def process_documents(documents: Iterable[str],document_ids: Iterable,split_sentences: bool = True,filter_len: int = 3,disable_progress_bar: bool = False) -> pd.DataFrame:"""主要辅助函数,用于处理来自电子病历的文档。:param documents: 包含文档的可迭代对象,每个元素是字符串:param document_ids: 包含文档唯一标识符的可迭代对象:param split_sentences: 标志,确定是否进一步将文档分割为句子:param filter_len: 默认句子的最小字符长度为3,否则过滤掉。:param disable_progress_bar: 是否禁用 tqdm 进度条:return: DataFrame格式,包含列 `document_id`, `text`, `section`, `offset`  """# 调用 sectionize_documents 函数来获取文档的章节信息df = sectionize_documents(documents, document_ids, disable_progress_bar)# split_sentences默认为true,表示需要进一步分割句子if split_sentences:# 调用 sentencize 函数,传入文本、文档标识符、偏移量等信息df = sentencize(df.text.values, df.document_id.values,df.offset.values, filter_len, disable_progress_bar)return df# 辅函数,用于提取文档的章节信息
def sectionize_documents(documents: Iterable[str],document_ids: Iterable,disable_progress_bar: bool = False) -> pd.DataFrame:"""获取imaging reports并仅返回所选章节(默认为 FINDINGS、IMPRESSION 和 ADDENDUM)。:param documents: 包含文档的可迭代对象,每个元素是字符串:param document_ids: 包含文档唯一标识符的可迭代对象:param disable_progress_bar: 是否 tqdm 进度条的标志:return: DataFrame格式,包含列 `document_id`, `text`, `offset`"""processed_documents = []# 使用 tqdm 迭代处理文档,显示进度条for document_id, document in tqdm(zip(document_ids, documents), total=len(documents), disable=disable_progress_bar):row = {}text, start, end = (document, 0, len(document))row['document_id'] = document_idrow['text'] = textrow['offset'] = (start, end)processed_documents.append(row)# 创建  DataFrame 并根据 'document_id' 和 'offset' 进行排序_df = pd.DataFrame(processed_documents)if _df.shape[0] > 0:return _df.sort_values(['document_id', 'offset']).reset_index(drop=True)else:return _df# 辅函数,用于将文档分割为句子
def sentencize(documents: Iterable[str],document_ids: Iterable,offsets: Iterable[tuple[int, int]],filter_len: int = 3,disable_progress_bar: bool = False) -> pd.DataFrame:"""将文档分割为句子。可以与 `sectionize_documents` 一起使用,进一步将文档分割为更易处理的部分。接受偏移量以确保分割后的句子可以与原始文档中的位置匹配。:param documents: 包含文档的可迭代对象,每个元素是字符串:param document_ids: 包含文档唯一标识符的可迭代对象:param offsets: 可迭代的元组,表示开始和结束索引:param filter_len: 句子的最小字符长度(否则过滤掉):return: 包含列 `document_id`, `text`, `section`, `offset` 的 Pandas DataFrame"""document_sentences = []# 使用 tqdm 迭代处理文档,显示进度条for document, document_id, offset in tqdm(zip(documents, document_ids, offsets), total=len(documents), disable=disable_progress_bar):try:# 使用 bf 库中的函数将文档转换为句子和偏移量_, sentence_offsets = bf.text_to_sentences_and_offsets(document)for o in sentence_offsets:if o[1] - o[0] > filter_len:sentence = document[o[0]:o[1]]abs_offsets = (o[0] + offset[0], o[1] + offset[0])row = {}row['document_id'] = document_idrow['text'] = sentencerow['offset'] = abs_offsetsdocument_sentences.append(row)except:continuereturn pd.DataFrame(document_sentences)
# 将完整的维基百科文档拆分为句子
processed_wiki_text_data = process_documents(wiki_text_data.text.values, wiki_text_data.id.values)
processed_wiki_text_data
		document_id							text						offset
0		10087606	In group theory, geometry, representation theo...	(0, 207)
1		10087606	For example, as transformations of an object i...	(208, 329)
2		10087606	Such symmetry operations are performed with re...	(330, 441)
3		10087606	In the context of molecular symmetry, a symmet...	(442, 631)
4		10087606	Two basic facts follow from this definition, w...	(632, 709)
...	...	...	...
32308	9962772		Mit Beitr.	(6031, 6041)
32309	9962772		Barth, 1957) == Selected publications == *Carl...	(6049, 6223)
32310	9962772		(Received 7 September 1920, published in issue...	(6224, 7033)
32311	9962772		Volume 1 Part 2 The Quantum Theory of Planck, ...	(7034, 7169)
32312	9962772		(Springer, 1982) *Walker, Mark German National...	(7170, 7553)
# 获取句子embeddings
wiki_data_embeddings = model.encode(processed_wiki_text_data.text, batch_size=BATCH_SIZE, device=DEVICE, show_progress_bar=True, convert_to_tensor=True, normalize_embeddings=True)
wiki_data_embeddings = wiki_data_embeddings.detach().cpu().numpy()  # (32313, 384)
_ = gc.collect()
  1. 编码问答数据
    在很多情况下,GPT3.5 似乎直接从文本中获取答案。因此,我们希望检索最相似的句子来提供上下文。下面将训练集prompt和所有answer进行合并,然后进行编码,后面会根据其编码结果来进行搜索,也就是搜索与question_embeddings(prompt+answer embeddings)最相似的wiki_data_embeddings
# 组合所有答案
trn['answer_all'] = trn.apply(lambda x: " ".join([x['A'], x['B'], x['C'], x['D'], x['E']]), axis=1)# 合并prompt+answer,然后编码
trn['prompt_answer_stem'] = trn['prompt'] + " " + trn['answer_all']question_embeddings = model.encode(trn.prompt_answer_stem.values, batch_size=BATCH_SIZE, device=DEVICE, show_progress_bar=True, convert_to_tensor=True, normalize_embeddings=True)
question_embeddings = question_embeddings.detach().cpu().numpy()

3.4 使用faiss搜索获取匹配的Prompt-Sentence Pairs

NUM_SENTENCES_INCLUDE = 5		# 用于确定要包含多少个相关句子
#prompt_contexts = []			# 包含 Question, Choices, Context的列表
contexts = []					# 只包含 Context的列表for r in tqdm(trn.itertuples(), total=len(trn)):prompt_id = r.Indexprompt_indices = processed_wiki_text_data[processed_wiki_text_data['document_id'].isin(wikipedia_file_data[wikipedia_file_data['prompt_id']==prompt_id]['id'].values)].index.valuesif prompt_indices.shape[0] > 0:prompt_index = faiss.index_factory(wiki_data_embeddings.shape[1], "Flat")prompt_index.add(wiki_data_embeddings[prompt_indices])context = ""# 获取最相似的ss, ii = prompt_index.search(question_embeddings, NUM_SENTENCES_INCLUDE)for _s, _i in zip(ss[prompt_id], ii[prompt_id]):context += processed_wiki_text_data.loc[prompt_indices]['text'].iloc[_i] + " "contexts.append(context)

3.5 查看context结果并保存

trn['context'] = contexts
trn[["prompt", "context", "A", "B", "C", "D", "E"]].to_csv("./test_context.csv", index=False)
model.cpu()
del model
del question_embeddings, wiki_data_embeddings
_ = gc.collect()
libc.malloc_trim(0)
torch.cuda.empty_cache()

  下面我们打印10条训练数据,可以看到这些数据不仅提供了问题和选择,还提供了维基百科的上下文。这些上下文可能会提供关键的提示,甚至答案本身!

for i, p in enumerate(contexts[:2]):print(f"Question {i}")print(p)print()
Question 0
The presence of a clustered thick disk-like component of dark matter in the Galaxy has been suggested by Sanchez-Salcedo (1997, 1999) and Kerins (1997).Kerins, E. J. 1997, Astronomy and Astrophysics, 322, 709-718 (ADS entry )Sánchez-Salcedo, F. J. 1997, Astrophysical Journal, 487, L61-L64 (ADS entry )Sánchez-Salcedo, F. J. 1999, Monthly Notices of the Royal Astronomical Society, 303, 755-772 (ADS entry ) ==See also== * Dark matter * Brown dwarfs * White dwarfs * Microlensing * Hypercompact stellar system * Massive compact halo object (MACHOs) * Weakly interacting massive particles (WIMPs) ==References== Category:Star clusters Category:Open clusters Observations of the Bullet Cluster are the strongest evidence for the existence of dark matter; however, Brownstein and Moffat have shown that their modified gravity theory can also account for the properties of the cluster. == Observational methods == Clusters of galaxies have been found in surveys by a number of observational techniques and have been studied in detail using many methods: * Optical or infrared: The individual galaxies of clusters can be studied through optical or infrared imaging and spectroscopy. The observed distortions can be used to model the distribution of dark matter in the cluster. == Temperature and density == Clusters of galaxies are the most recent and most massive objects to have arisen in the hierarchical structure formation of the Universe and the study of clusters tells one about the way galaxies form and evolve. A 2021 article postulated that approximately 50% of all baryonic matter is outside dark matter haloes, filling the space between galaxies, and that this would explain the missing baryons not accounted for in the 2017 paper. == Current state == Currently, many groups have observed the intergalactic medium and circum-galactic medium to obtain more measurements and observations of baryons to support the leading observations. In cosmology, the missing baryon problem is an observed discrepancy between the amount of baryonic matter detected from shortly after the Big Bang and from more recent epochs. Question 1
Many of these systems evolve in a self-similar fashion in the sense that data obtained from the snapshot at any fixed time is similar to the respective data taken from the snapshot of any earlier or later time. Many other seemingly disparate systems which are found to exhibit dynamic scaling. The form of their proposal for dynamic scaling was: :f(x,t)\sim t^{-w}x^{-\tau} \varphi \left( \frac x {t^z} \right), where the exponents satisfy the following relation: :w=(2-\tau)z. == Test for dynamic scaling == In such systems we can define a certain time-dependent stochastic variable x. Dynamic scaling (sometimes known as Family-Vicsek scaling) is a litmus test that shows whether an evolving system exhibits self-similarity. Essentially such systems can be termed as temporal self-similarity since the same system is similar at different times. == Examples == Many phenomena investigated by physicists are not static but evolve probabilistically with time (i.e. Stochastic process). 

3.6 推理

3.6.1 加载测试集
import torch
import numpy as np
import pandas as pd
from datasets import Dataset
from transformers import AutoTokenizer
from transformers import AutoModelForMultipleChoice, TrainingArguments, Trainertest_df = pd.read_csv("test_context.csv")
test_df["prompt"] = test_df["context"] + " #### " +  test_df["prompt"]
#test_df["prompt"] = test_df["context"].apply(lambda x: x[:1750]) + " #### " +  test_df["prompt"]
test_df=test_df.drop(columns=['context'])# 添加答案列以保持和训练集格式一致,这样可以使用和训练集一样的预处理函数
test_df['answer'] = 'A'
test_df.head(3)

在这里插入图片描述

3.6.2 定义预处理函数和后处理函数

  下面定义预处理函数,用于将待推理数据编码为多选问答需要的格式;定义后处理 函数,将推理结果转为提交的格式,这部分和方法1中完全一样。

  1. 将问题和5个答案选项组合,并进行解码
options = 'ABCDE'
indices = list(range(5))
option_to_index = {option: index for option, index in zip(options, indices)}
index_to_option = {index: option for option, index in zip(options, indices)}def preprocess(example):# AutoModelForMultipleChoice 需要的是question/answer对,所以问题被复制5次first_sentence = [example['prompt']] * 5second_sentence = []# 遍历选项(A 到 E)并将它们添加到 second_sentence 列表中for option in options:second_sentence.append(example[option])tokenized_example = tokenizer(first_sentence, second_sentence, truncation=True)# 将答案映射为索引,并将其添加到 tokenized_example 中作为标签tokenized_example['label'] = option_to_index[example['answer']]return tokenized_example

  可以看到,每个样本的问题被重复5次后和5个选项合并,解码后的结果input_ids、token_type_ids、attention_mask都是5个元素的嵌套列表,等于一个样本被拆成5个样本。

  1. 定义data_collator
#  datacollator 来自 https://huggingface.co/docs/transformers/tasks/multiple_choice
# 每个batch中对问答对进行动态填充(dynamically pad),所以不需要将每个问答对都填充到模型最大序列长度
from dataclasses import dataclass
from transformers.tokenization_utils_base import PreTrainedTokenizerBase, PaddingStrategy
from typing import Optional, Union@dataclass
class DataCollatorForMultipleChoice:tokenizer: PreTrainedTokenizerBasepadding: Union[bool, str, PaddingStrategy] = Truemax_length: Optional[int] = Nonepad_to_multiple_of: Optional[int] = Nonedef __call__(self, features):# features就是4个样本(batch size=4)label_name = "label" if 'label' in features[0].keys() else 'labels'# 对每个样本(feature,字典格式)使用pop删除key为label的键值对,返回被删除的值# 所以feature被删除了label键值对,而labels的值是四个样本label列表[0, 0, 1, 0]labels = [feature.pop(label_name) for feature in features] batch_size = len(features)  						# 批次大小num_choices = len(features[0]['input_ids'])			# 选项数flattened_features = [[{k: v[i] for k, v in feature.items()} for i in range(num_choices)] for feature in features]flattened_features = sum(flattened_features, [])batch = self.tokenizer.pad(flattened_features,padding=self.padding,max_length=self.max_length,pad_to_multiple_of=self.pad_to_multiple_of,return_tensors='pt',)batch = {k: v.view(batch_size, num_choices, -1) for k, v in batch.items()}batch['labels'] = torch.tensor(labels, dtype=torch.int64)return batch

最终返回的batch格式为:

{'input_ids': tensor([[[ 101, 2627...,    0]]]),
'token_type_ids': tensor([[[0, 0, 0,  ..., 0, 0]]]),
'attention_mask': tensor([[[1, 1, 1,  ..., 0, 0]]]),
'labels': tensor([0, 0, 1, 0])}
  1. 定义后处理函数,用于将预测结果(概率)转为比赛规定的提交格式
def predictions_to_map_output(predictions):sorted_answer_indices = np.argsort(-predictions)top_answer_indices = sorted_answer_indices[:,:3] # Get the first three answers in each rowtop_answers = np.vectorize(index_to_option.get)(top_answer_indices)return np.apply_along_axis(lambda row: ' '.join(row), 1, top_answers)
3.6.3 开始推理
3.6.3.1 使用trainer进行推理(方法7)

方法7加载llm-se-debertav3-large模型,方法8加载llm-science-run-context-2模型

  1. 加载模型和分词器
model_dir = "/kaggle/input/llm-se-debertav3-large"
tokenizer = AutoTokenizer.from_pretrained(model_dir)
model = AutoModelForMultipleChoice.from_pretrained(model_dir)
model.eval()
  1. 数据预处理
test_ds = Dataset.from_pandas(test_df)
# 使用数据集映射(map)预处理函数到训练数据集,同时删除不需要的列
tokenized_test_ds = test_ds.map(preprocess, batched=False, remove_columns=['prompt', 'A', 'B', 'C', 'D', 'E', 'answer'])
tokenized_test_ds
Dataset({features: ['input_ids', 'token_type_ids', 'attention_mask', 'label'],num_rows: 200
})
  1. 开启训练
trainer = Trainer(model=model,tokenizer=tokenizer,data_collator=DataCollatorForMultipleChoice(tokenizer=tokenizer)
)test_predictions = trainer.predict(tokenized_test_ds)
  1. 提交结果
submission_df = pd.DataFrame({"id": np.arange(len(test_df))})
submission_df['prediction'] = predictions_to_map_output(test_predictions.predictions)
submission_df.to_csv('submission.csv', index=False)
submission_df.head()
  id	prediction
0	0	D B A
1	1	A B E
2	2	A C D
3	3	C E B
4	4	D A B
3.6.3.2 使用Pytorch进行推理(方法8)
model_dir = "/kaggle/input/llm-science-run-context-2"
tokenizer = AutoTokenizer.from_pretrained(model_dir)
model = AutoModelForMultipleChoice.from_pretrained(model_dir)
model.eval()
from torch.utils.data import DataLoadertokenized_test_dataset = Dataset.from_pandas(test_df).map(preprocess, remove_columns=['prompt', 'A', 'B', 'C', 'D', 'E', 'answer'])
data_collator = DataCollatorForMultipleChoice(tokenizer=tokenizer)
test_dataloader = DataLoader(tokenized_test_dataset, batch_size=1, shuffle=False, collate_fn=data_collator)
test_predictions = []
for batch in test_dataloader:for k in batch.keys():batch[k] = batch[k].cuda()with torch.no_grad():outputs = model(**batch)test_predictions.append(outputs.logits.cpu().detach())test_predictions = torch.cat(test_predictions)
submission_df = pd.DataFrame({"id": np.arange(len(test_df))})
submission_df['prediction'] = predictions_to_map_output(test_predictions)
submission_df.to_csv('submission.csv', index=False)
submission_df.head()
  id	prediction
0	0	D B E
1	1	A B D
2	2	A D B
3	3	C D E
4	4	D A B

  《Open Book QA&debertav3-large 详解》使用的是llm-se-debertav3-large模型,得分是0.771。如果改成llm-science-run-context-2,得分会是0.807。只使用openbook方法,而是直接用llm-science-run-context-2模型进行推理,得分是0.746。

  Tips:有一版我导出这篇博客的md格式,使用jupytext --set-formats ipynb,md filename.md 将其转为ipynb格式,上传到kaggle上跑第一遍没有问题,但是提交后显示Submission CSV Not Found ,而我明明是有保存提交文件的,output中也看得到。


  后面在本地jupyterlab中打开这份ipynb文件报错,估计是这个原因导致最后提交没有结果。后面将md文件在jupyterlab中以ipynb打开,再保存为ipynb格式,导入kaggle,提交比赛,就没问题了。

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

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

相关文章

ARM汇编学习录 1 -基础概念

指令集概述 现阶段有四个不同的指令集 名称概述ARM3232位指令集Thumb16位指令集,ARM32子集,提供高密度低功耗Thumb232位指令集,ARMv6T2 引入.是thumb超集ARM6464位指令集 note&#xff1a; ARM某一个时刻只能运行单独ARM指令集或者Thumb指令,通过CPSR的T标志位决定. 如何当前…

人机言语交互模型的评估要素

智能客服中的言语交互模型评估要素&#xff0c;主要包括以下几个方面&#xff1a; 有效性&#xff1a;指模型能否准确识别和理解用户的言语意图&#xff0c;以及生成正确和合适的回答。可以通过比较模型生成的回答与人工回答的准确率来评估。流畅性&#xff1a;指模型在回答问…

c语言:通讯录管理系统(动态分配内存版)

前言&#xff1a;在大多数高校内&#xff0c;都是通过设计一个通讯录管理系统来作为c语言课程设计&#xff0c;通过一个具体的系统设计将我们学习过的结构体和函数等知识糅合起来&#xff0c;可以很好的锻炼学生的编程思维&#xff0c;本文旨在为通讯录管理系统的设计提供思路和…

国科大体系结构习题 | 第三章 二进制与逻辑电路

第三章 Q1: A1:(1) 原码&#xff1a; [ − ( 2 63 − 1 &#xff0c; 2 63 − 1 ] [-(2^{63}-1&#xff0c;2^{63}-1] [−(263−1&#xff0c;263−1] 补码&#xff1a; [ − ( 2 63 &#xff0c; 2 63 − 1 ] [-(2^{63}&#xff0c;2^{63}-1] [−(263&#xff0c;263−1] …

门窗定制销售小程序商城的作用是什么

门窗属于建材物料行业里重要的体系&#xff0c;生活中需求度非常高&#xff0c;虽然门窗产品使用周期较长&#xff0c;客户旧换新复购率较低&#xff0c;但由于产品属于家家户户都需要&#xff0c;因此市场规模依然是稳增不减。 互联网环境下&#xff0c;客户更习惯于线上获得…

cpp primer plus笔记01-注意事项

cpp尽量以int main()写函数头而不是以main()或者int main(void)或者void main()写。 cpp尽量上图用第4行的注释而不是用第5行注释。 尽量不要引用命名空间比如:using namespace std; 函数体内引用的命名空间会随着函数生命周期结束而失效&#xff0c;放置在全局引用的命名空…

Leetcode901-股票价格跨度

一、前言 本题基于leetcode901股票价格趋势这道题&#xff0c;说一下通过java解决的一些方法。并且解释一下笔者写这道题之前的想法和一些自己遇到的错误。需要注意的是&#xff0c;该题最多调用 next 方法 10^4 次,一般出现该提示说明需要注意时间复杂度。 二、解决思路 ①…

云原生微服务 第六章 Spring Cloud中使用OpenFeign

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 文章目录 系列文章目录前言1、OpenFeign的实现…

SAP从入门到放弃系列之QM目录类别、代码组、选择集维护

目录 一、概念相关内容1.1 目录类别1.2 代码组和代码1.3 选择集和选择集代码 二、系统操作相关内容 一、概念相关内容 1.1 目录类别 目录类别是对定性数据的一种归纳&#xff0c;描述了业务的主题。根据PA的教材中表述&#xff0c;目录类型 0 - 9 和 A - O 由 SAP 定义&#…

如何自学(黑客)网络安全技术————(详细分析学习思路)方法

前言 前几天发布了一篇 网络安全&#xff08;黑客&#xff09;自学 没想到收到了许多人的私信想要学习网安黑客技术&#xff01;却不知道从哪里开始学起&#xff01;怎么学&#xff1f;如何学&#xff1f; 今天给大家分享一下&#xff0c;很多人上来就说想学习黑客&#xff0c…

883. 高斯消元解线性方程组

883. 高斯消元解线性方程组 - AcWing题库 输入一个包含 n 个方程 n 个未知数的线性方程组。 方程组中的系数为实数。 求解这个方程组。 下图为一个包含 m 个方程 n 个未知数的线性方程组示例&#xff1a; 输入格式 第一行包含整数 n。 接下来 n 行&#xff0c;每行包含 n1…

网络安全(黑客)——自学笔记

前言&#xff1a; 想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“…

Java卷上天,可以转行干什么?

小刚是某名企里的一位有5年经验的高级Java开发工程师&#xff0c;每天沉重的的工作让他疲惫不堪&#xff0c;让他萌生出想换工作的心理&#xff0c;但是转行其他工作他又不清楚该找什么样的工作 因为JAVA 这几年的更新实在是太太太……快了&#xff0c;JAVA 8 都还没用多久&am…

《第一行代码Andorid》阅读笔记-第六章

第六章 内容提供器 在上一章中我们学了Android数据持久化的技术&#xff0c;包括文件存储、SharedPreferences存储以及数据库存储。使用这些持久化技术所保存的数据都只能在当前应用程序中访问。 虽然文件和SharedPreferences存储中提供了MODE_WORLD_READABLE和MODE_WORLD_WRI…

Vue中如何进行分布式任务调度与定时任务管理

在Vue中进行分布式任务调度与定时任务管理 分布式任务调度和定时任务管理是许多应用程序中的关键功能之一。它们用于执行周期性的、异步的、重复的任务&#xff0c;例如数据备份、邮件发送、定时报告生成等。在Vue.js应用中&#xff0c;我们可以结合后端服务实现分布式任务调度…

苹果手机的祛魅时刻,国产厂商的颠覆征程

“iPhone翻车了&#xff1f;”有网友如此质疑。 发布未满一个月&#xff0c;iPhone 15系列多次因负面问题登上热搜。 首先曝出钛金属边框容易沾染指纹的情况&#xff0c;尚未涉及功能性方面&#xff0c;但后续接连曝出发热严重、电池循环次数低、外放破音、Wi-Fi链接缓慢的问…

进制转换

1.十进制转化为其他进制 这里可能将十进制转化为14或15进制&#xff0c;所以10用A&#xff0c;11用B表示&#xff0c;依次类推。 2.其他进制转化为10进制&#xff1a; 将其他进制下的数转化为10进制下的数&#xff0c;通常采用秦九韶算法。 上代码&#xff1a; #include &…

新手选MT4还是MT5,anzo capital昂首资本建议选择MT4,一个原因

在交易中就订单执行策略而言&#xff0c;MT4和MT5哪个更好&#xff0c;相信很多交易者和&#xff0c;anzo capital昂首资本一样很难做出判断。在MT5中&#xff0c;虽然开发人员对发送订单的流程进行了额外的复杂化&#xff0c;同时MT5在订单执行政策方面的优势在于其能够调整全…

stm32 - 中断/定时器

stm32 - 中断/定时器 概念时钟树定时器类型基准时钟&#xff08;系统时钟&#xff09;预分频器 - 时基单元CNT计数器 - 时基单元自动重装寄存器 - 时基单元基本定时器结构通用定时器计数器模式内外时钟源选择 定时中断基本结构时序预分频器时序计数器时序 例子通用定时器 - 内部…

OpenCV4(C++) —— 图像数据类型转换和颜色模型转换

文章目录 一、图像数据类型转换二、颜色模型转换三、通道的分离和融合 一、图像数据类型转换 OpenCV中使用imread读取一张彩色图像时&#xff0c;默认采用的是BGR通道和整数类型(0-255&#xff0c;CV_8U)。 在某些情况下&#xff0c;会将整数类型(0-255)转换为浮点类型(0-1)&a…