Datawhale AI夏令营 第三期 逻辑推理 Task1:开营

2024Datawhale AI夏令营 逻辑推理 Task1:开营

跑通 baseline,拿下第一个分数,下面介绍basline:

cell1:安装库

!pip install scipy openai tiktoken retry dashscope loguru

这段代码是一条用于在Python环境中安装多个依赖包的命令。具体来说,它使用pip(Python的包管理工具)来安装以下五个包:

  1. scipy:一个用于科学计算和数据分析的Python库,提供了许多数学、科学和工程计算的功能。

  2. openai:OpenAI的Python库,用于与OpenAI的API进行交互,例如使用GPT-3模型进行文本生成、语言理解等任务。

  3. tiktoken:一个用于处理和转换token的库,通常用于自然语言处理任务中,特别是在处理OpenAI的API时。

  4. retry:一个用于实现重试机制的库,可以用于网络请求、数据库操作等场景,以处理临时性的错误。

  5. dashscope:一个用于创建和部署Dash应用的库,Dash是用于构建数据可视化应用的Python框架。

  6. loguru:一个用于记录日志的库,提供了简单易用的API,可以方便地记录日志到文件、控制台等。

cell2:导入库

from multiprocessing import Process, Manager
import json
import os
from pprint import pprint
import re
from tqdm import tqdm
import random

import uuid
import openai
import tiktoken
import json
import numpy as np
import requests
from retry import retry
from scipy import sparse
#from rank_bm25 import BM25Okapi
#import jieba
from http import HTTPStatus
import dashscope


from concurrent.futures import ThreadPoolExecutor, as_completed
from loguru import logger
import json
import time
from tqdm import tqdm

logger.remove()  # 移除默认的控制台输出
logger.add("logs/app_{time:YYYY-MM-DD}.log", level="INFO", rotation="00:00", retention="10 days", compression="zip")

MODEL_NAME = 'qwen2-7b-instruct'

这段代码是一个Python脚本,它使用了多个库来实现一些功能,包括多进程处理、日志记录、HTTP请求等。下面是对代码的详细解释:

导入的库

  • multiprocessing: 用于创建和管理多个进程。
  • json: 用于处理JSON数据。
  • os: 提供与操作系统交互的功能。
  • pprint: 用于格式化输出。
  • re: 用于正则表达式操作。
  • tqdm: 用于显示进度条。
  • random: 用于生成随机数。
  • uuid: 用于生成唯一标识符。
  • openai: 用于与OpenAI API交互。
  • tiktoken: 用于处理令牌。
  • numpy: 用于科学计算。
  • requests: 用于发送HTTP请求。
  • retry: 用于重试机制。
  • scipy.sparse: 用于稀疏矩阵操作。
  • http.HTTPStatus: 用于HTTP状态码。
  • dashscope: 可能是一个自定义或第三方库,用于特定功能。
  • concurrent.futures: 用于并发执行任务。
  • loguru: 用于日志记录。
  • time: 用于处理时间。
  • tqdm: 再次导入,可能是为了覆盖之前的导入。

日志配置

logger.remove()  # 移除默认的控制台输出
logger.add("logs/app_{time:YYYY-MM-DD}.log", level="INFO", rotation="00:00", retention="10 days", compression="zip")

这段代码配置了日志记录,将日志输出到文件中,文件名为app_YYYY-MM-DD.log,每天一个新文件。日志级别为INFO,文件会在每天午夜自动轮换,保留最近10天的日志,并使用ZIP压缩。

模型名称

MODEL_NAME = 'qwen2-7b-instruct'

定义了一个常量MODEL_NAME,值为'qwen2-7b-instruct',可能用于指定某个模型或API的名称。

注意事项

  1. 多进程处理:使用multiprocessing库可以并行处理任务,提高程序的执行效率。
  2. 日志记录:使用loguru库可以方便地记录日志,包括输出到控制台和文件,并支持日志轮换、压缩等功能。
  3. 并发执行:使用concurrent.futures.ThreadPoolExecutor可以并发执行多个任务,提高程序的响应速度。
  4. 错误处理:使用retry库可以自动重试失败的HTTP请求,提高程序的健壮性。
  5. 进度条:使用tqdm库可以显示任务的进度条,提高用户体验。

用途

这段代码可能是用于一个需要处理大量数据、进行并发操作、记录日志的应用程序的一部分。具体用途需要根据上下文和后续代码来确定。

cell3:填写自己的api

# 注意:这里需要填入你的key~ 咱们在第二步申请的。
dashscope.api_key="sk-"

cell4:api_retry调用API时有问题进行重试

def api_retry(MODEL_NAME, query):
    max_retries = 5
    retry_delay = 60  # in seconds
    attempts = 0
    while attempts < max_retries:
        try:
            return call_qwen_api(MODEL_NAME, query)
        except Exception as e:
            attempts += 1   
            if attempts < max_retries:
                logger.warning(f"Attempt {attempts} failed for text: {query}. Retrying in {retry_delay} seconds...")
                time.sleep(retry_delay)
            else:
                logger.error(f"All {max_retries} attempts failed for text: {query}. Error: {e}")
                raise

这段Python代码定义了一个名为api_retry的函数,用于在调用API时进行重试。以下是代码的详细解释:

函数定义

def api_retry(MODEL_NAME, query):
  • MODEL_NAME:API调用的模型名称。
  • query:要发送给API的查询内容。

重试参数

max_retries = 5
retry_delay = 60  # in seconds
  • max_retries:最大重试次数,这里设置为5次。
  • retry_delay:每次重试之间的延迟时间,单位为秒,这里设置为60秒。

重试计数器

attempts = 0
  • attempts:记录当前重试次数,初始值为0。

重试循环

while attempts < max_retries:
  • 这个循环会执行最多max_retries次,即最多重试5次。

尝试调用API

try:
    return call_qwen_api(MODEL_NAME, query)
  • 尝试调用call_qwen_api函数,传入MODEL_NAMEquery参数。
  • 如果调用成功,函数会返回API的响应。

捕获异常并进行重试

except Exception as e:
    attempts += 1   
    if attempts < max_retries:
        logger.warning(f"Attempt {attempts} failed for text: {query}. Retrying in {retry_delay} seconds...")
        time.sleep(retry_delay)
    else:
        logger.error(f"All {max_retries} attempts failed for text: {query}. Error: {e}")
        raise
  • 如果在调用过程中发生异常,会捕获异常并增加重试次数。
  • 如果当前重试次数小于最大重试次数,记录警告日志并等待指定延迟时间后重试。
  • 如果达到最大重试次数,记录错误日志并重新抛出异常。

注意事项

  1. 依赖项:代码中使用了loggertime模块,需要确保这些模块已经导入。
  2. 异常处理:代码捕获了所有异常,这意味着任何类型的异常都会触发重试机制。在实际应用中,可能需要更精细地处理不同类型的异常。
  3. 日志记录:使用logger记录日志,确保日志级别和格式符合实际需求。
  4. API调用函数call_qwen_api函数需要事先定义,并且能够处理传入的MODEL_NAMEquery参数。

这段代码的主要用途是在调用API时,如果遇到临时性的错误(如网络问题或服务器负载过高),通过重试机制提高请求的成功率。

cell5:call_qwen_api用API调用模型并返回结果

def call_qwen_api(MODEL_NAME, query):
    # 这里采用dashscope的api调用模型推理,通过http传输的json封装返回结果
    messages = [
        {'role': 'user', 'content': query}]
    response = dashscope.Generation.call(
        MODEL_NAME,
        messages=messages,
        result_format='message',  # set the result is message format.
    )
    if response.status_code == HTTPStatus.OK:
        # print(response)
        return response['output']['choices'][0]['message']['content']
    else:
        print('Request id: %s, Status code: %s, error code: %s, error message: %s' % (
            response.request_id, response.status_code,
            response.code, response.message
        ))
        raise Exception()

这段Python代码定义了一个名为call_qwen_api的函数,用于调用一个名为dashscope的API来获取模型推理结果。具体来说,这个函数通过HTTP传输的JSON封装来发送请求,并返回模型生成的结果。下面是对代码的详细解释:

函数定义

def call_qwen_api(MODEL_NAME, query):
  • MODEL_NAME:模型名称,用于指定要调用的模型。
  • query:查询内容,即用户输入的查询字符串。

准备请求消息

messages = [
    {'role': 'user', 'content': query}]
  • 这里创建了一个包含用户查询的列表,用于封装请求消息。role字段指定消息的角色为用户,content字段包含查询内容。

调用API

response = dashscope.Generation.call(
    MODEL_NAME,
    messages=messages,
    result_format='message',  # set the result is message format.
)
  • 使用dashscope.Generation.call方法调用API,传入模型名称、消息列表和结果格式(设置为message格式)。
  • response变量存储API的响应。

检查响应状态

if response.status_code == HTTPStatus.OK:
  • 检查API响应的状态码是否为200(HTTPStatus.OK),表示请求成功。

返回结果

return response['output']['choices'][0]['message']['content']
  • 如果请求成功,从响应中提取模型生成的结果,并返回。

错误处理

else:
    print('Request id: %s, Status code: %s, error code: %s, error message: %s' % (
        response.request_id, response.status_code,
        response.code, response.message
    ))
    raise Exception()
  • 如果请求失败,打印详细的错误信息,包括请求ID、状态码、错误码和错误消息。
  • 抛出异常,终止函数执行。

注意事项

  1. 依赖库:代码依赖于dashscope库,需要确保该库已正确安装。
  2. HTTP状态码:代码假设API使用HTTP状态码200表示成功,如果API使用其他状态码表示成功,需要相应地修改条件。
  3. 异常处理:代码在请求失败时抛出异常,实际应用中可能需要更细致的错误处理逻辑。
  4. API调用:确保dashscope.Generation.call方法正确实现,并且API的URL、认证信息等配置正确。

这段代码的主要用途是通过API调用模型进行推理,并返回模型生成的结果。适用于需要与外部模型服务进行交互的场景,如自然语言处理、图像识别等。

cell6:get_prompt定义prompt推理模版

# 这里定义了prompt推理模版

def get_prompt(problem, question, options):

    options = '\n'.join(f"{'ABCDEFG'[i]}. {o}" for i, o in enumerate(options))  ## 在每个选项前加入字母标记

    prompt = f"""你是一个逻辑推理专家,擅长解决逻辑推理问题。以下是一个逻辑推理的题目,形式为单项选择题。所有的问题都是(close-world assumption)闭世界假设,即未观测事实都为假。请逐步分析问题并在最后一行输出答案,最后一行的格式为"答案是:A"。题目如下:

### 题目:  问题背景或情境
{problem}

### 问题:  具体的问题是什么
{question}
{options}   # 问题的选项,通常是列表形式
"""
    # print(prompt)
    return prompt

这段Python代码定义了一个函数get_prompt,用于生成逻辑推理问题的提示(prompt)。这个函数接受三个参数:problem(问题背景或情境)、question(具体问题是什么)和options(问题的选项)。以下是代码的详细解释:

实现原理

  1. 参数处理

    • options参数是一个包含选项的列表。函数首先使用列表推导式将每个选项前加上对应的字母标记(A、B、C、D、E、F、G)。
  2. 生成提示

    • 使用f-string格式化字符串,将问题背景、具体问题和选项插入到预定义的提示模板中。这个模板包括一个逻辑推理专家的介绍、问题的背景、具体问题以及选项。
  3. 返回提示

    • 最后,函数返回生成的提示字符串。

用途

这个函数的主要用途是生成逻辑推理问题的提示,以便在逻辑推理专家系统中使用。通过这种方式,可以方便地提供问题的上下文和选项,让逻辑推理专家能够根据这些信息进行推理和分析。

注意事项

  1. 字母标记

    • 代码中使用了{'ABCDEFG'[i]}来为每个选项生成字母标记。这种方法适用于选项数量不超过7个的情况。如果选项数量超过7个,需要相应地调整字母标记的生成方式。
  2. 格式要求

    • 最后一行的答案格式必须严格按照"答案是:A"的格式输出,其中A是选项的字母标记。
  3. 闭世界假设

    • 提示中明确指出所有未观测事实都为假,这是逻辑推理问题的常见假设,确保推理过程基于已知信息进行。
  4. 输入验证

    • 在实际使用中,应该对输入参数进行验证,确保problemquestionoptions不为空,且options列表包含至少一个选项。

通过这个函数,可以快速生成逻辑推理问题的提示,便于逻辑推理专家进行问题的分析和解答。

cell7:extract提取结果

# 这里使用extract抽取模获得抽取的结果

def extract(input_text):
    ans_pattern = re.compile(r"答案是:(.)", re.S)

    problems = ans_pattern.findall(input_text)
    # print(problems)  if(problems == ''):错误,修改为空列表
    if(problems == []):
        return 'A'
    return problems[0]

这段Python代码定义了一个名为extract的函数,用于从输入的文本中提取答案。具体来说,它通过正则表达式匹配来查找文本中包含的答案。下面是对代码的详细解释:

实现原理

  1. 正则表达式匹配

    • 使用re.compile函数定义了一个正则表达式模式ans_pattern,该模式用于匹配文本中包含“答案是:”后跟一个字符的字符串。
    • r"答案是:(.)"中的r表示原始字符串,(.)表示匹配任意单个字符,re.S表示使.匹配包括换行符在内的所有字符。
  2. 查找匹配项

    • 使用ans_pattern.findall(input_text)方法在输入文本input_text中查找所有匹配该正则表达式的子字符串。
    • findall方法返回一个包含所有匹配项的列表。
  3. 处理匹配结果

    • 如果problems列表为空(即没有找到匹配项),则返回字符串'A'
    • 否则,返回列表中的第一个匹配项(即答案)。

用途

该函数的用途是从包含答案的文本中提取答案。例如,它可以用于处理包含标准答案的题目或试卷,以便进行自动评分或分析。

注意事项

  1. 输入文本格式

    • 输入文本需要包含“答案是:”这样的格式,并且答案紧跟在其后。
    • 如果答案不是紧跟在“答案是:”之后,或者格式不一致,函数可能无法正确提取答案。
  2. 正则表达式模式

    • 正则表达式模式r"答案是:(.)"假设答案是一个字符。如果答案可能包含多个字符,需要修改正则表达式以匹配更复杂的模式。
  3. 返回值

    • 函数返回一个字符串,即使答案是一个字符。如果需要处理多个答案,可能需要修改函数以返回一个列表或其他数据结构。
  4. 错误处理

    • 代码中没有处理可能的异常情况,例如输入文本为None或空字符串。在实际使用中,可能需要添加错误处理逻辑。
# 这里使用extract抽取模获得抽取的结果

def extract(input_text):
    ans_pattern = re.compile(r"答案是:(.)", re.S)

    problems = ans_pattern.findall(input_text)
    # print(problems)
    if(problems == ''):
        return 'A'
    return problems[0]

这段Python代码定义了一个名为extract的函数,用于从输入的文本中提取答案。具体来说,它通过正则表达式匹配来查找文本中包含的答案。下面是对代码的详细解释:

实现原理

  1. 正则表达式匹配

    • 使用re.compile函数定义了一个正则表达式模式ans_pattern,该模式用于匹配文本中包含“答案是:”后跟一个字符的字符串。
    • r"答案是:(.)"中的r表示原始字符串,(.)表示匹配任意单个字符,re.S表示使.匹配包括换行符在内的所有字符。
  2. 查找匹配项

    • 使用ans_pattern.findall(input_text)方法在输入文本input_text中查找所有匹配该正则表达式的子字符串。
    • findall方法返回一个包含所有匹配项的列表。
  3. 处理匹配结果

    • 如果problems列表为空(即没有找到匹配项),则返回字符串'A'
    • 否则,返回列表中的第一个匹配项(即答案)。

用途

该函数的用途是从包含答案的文本中提取答案。例如,它可以用于处理包含标准答案的题目或试卷,以便进行自动评分或分析。

注意事项

  1. 输入文本格式

    • 输入文本需要包含“答案是:”这样的格式,并且答案紧跟在其后。
    • 如果答案不是紧跟在“答案是:”之后,或者格式不一致,函数可能无法正确提取答案。
  2. 正则表达式模式

    • 正则表达式模式r"答案是:(.)"假设答案是一个字符。如果答案可能包含多个字符,需要修改正则表达式以匹配更复杂的模式。
  3. 返回值

    • 函数返回一个字符串,即使答案是一个字符。如果需要处理多个答案,可能需要修改函数以返回一个列表或其他数据结构。
  4. 错误处理

    • 代码中没有处理可能的异常情况,例如输入文本为None或空字符串。在实际使用中,可能需要添加错误处理逻辑。

cell8:process_datas处理数据集,并使用多线程并行处理任务

def process_datas(datas,MODEL_NAME):
    results = []
    with ThreadPoolExecutor(max_workers=16) as executor:
        future_data = {}
        lasttask = ''
        lastmark = 0
        lens = 0
        for data in tqdm(datas, desc="Submitting tasks", total=len(datas)):
            problem = data['problem']
            for id,question in enumerate(data['questions']):
                prompt = get_prompt(problem, 
                                    question['question'], 
                                    question['options'],
                                    )

                future = executor.submit(api_retry, MODEL_NAME, prompt)
                
                future_data[future] = (data,id)
                time.sleep(0.6)  # 控制每0.5秒提交一个任务
                lens += 1
        for future in tqdm(as_completed(future_data), total=lens, desc="Processing tasks"):
            # print('data',data)
            data = future_data[future][0]
            problem_id = future_data[future][1]
            try:
                res  = future.result()
                extract_response = extract(res)
                # print('res',extract_response)
                data['questions'][problem_id]['answer'] = extract_response
                results.append(data)
                # print('data',data)
                
            except Exception as e:
                logger.error(f"Failed to process text: {data}. Error: {e}")
    
    return results

这段Python代码定义了一个名为process_datas的函数,用于处理数据集,并使用多线程并行处理任务。下面是对代码的详细解释:

函数定义

def process_datas(datas, MODEL_NAME):
  • datas:一个包含多个数据项的列表,每个数据项是一个字典,包含问题(problem)和多个问题(questions)。
  • MODEL_NAME:模型名称,用于调用API。

初始化结果列表

results = []
  • results:用于存储处理后的数据。

使用线程池

with ThreadPoolExecutor(max_workers=16) as executor:
  • ThreadPoolExecutor:创建一个线程池,最大工作线程数为16。

初始化变量

future_data = {}
lasttask = ''
lastmark = 0
lens = 0
  • future_data:字典,用于存储任务和对应的数据。
  • lasttasklastmarklens:用于记录任务状态,但在这段代码中未使用。

遍历数据集

for data in tqdm(datas, desc="Submitting tasks", total=len(datas)):
  • tqdm:用于显示进度条。
  • 遍历datas中的每个数据项。

处理每个问题

problem = data['problem']
for id, question in enumerate(data['questions']):
    prompt = get_prompt(problem, question['question'], question['options'])
    future = executor.submit(api_retry, MODEL_NAME, prompt)
    future_data[future] = (data, id)
    time.sleep(0.6)  # 控制每0.5秒提交一个任务
    lens += 1
  • get_prompt:生成用于API调用的提示。
  • api_retry:调用API并重试,如果失败则重试。
  • future_data:存储任务和对应的数据。
  • time.sleep(0.6):控制每0.5秒提交一个任务,以避免API请求过于频繁。

处理任务结果

for future in tqdm(as_completed(future_data), total=lens, desc="Processing tasks"):
    data = future_data[future][0]
    problem_id = future_data[future][1]
    try:
        res = future.result()
        extract_response = extract(res)
        data['questions'][problem_id]['answer'] = extract_response
        results.append(data)
    except Exception as e:
        logger.error(f"Failed to process text: {data}. Error: {e}")
  • as_completed:等待任务完成。
  • future.result():获取任务结果。
  • extract:从API响应中提取答案。
  • 将答案添加到数据项中,并存储到results列表中。
  • 如果任务失败,记录错误日志。

返回结果

return results
  • 返回处理后的数据列表。

注意事项

  1. 线程池大小max_workers=16表示最多同时运行16个线程,可以根据实际情况调整。
  2. API调用频率time.sleep(0.6)用于控制API调用频率,避免触发API限制。
  3. 错误处理:使用try-except捕获并记录处理过程中的异常。
  4. 日志记录:使用logger.error记录错误信息,便于调试和监控。

这段代码的主要用途是并行处理大量数据,通过多线程提高处理速度,适用于需要大量API调用的场景。

cell9: main处理输入文件,输出结果文件

def main(ifn, ofn):
    if os.path.exists(ofn):
        pass
    data = []
    # 按行读取数据
    with open(ifn) as reader:
        for line in reader:
            sample = json.loads(line)
            data.append(sample)
    datas = data
    # print(data)
    # 均匀地分成多个数据集
    return_list = process_datas(datas,MODEL_NAME)
    print(len(return_list))
    print("All tasks finished!")
    return return_list

这段Python代码定义了一个名为main的函数,用于处理输入文件(ifn)中的数据,并将处理后的数据保存到输出文件(ofn)中。下面是对代码的详细解释:

函数定义

def main(ifn, ofn):
  • ifn:输入文件的路径。
  • ofn:输出文件的路径。

检查输出文件是否存在

    if os.path.exists(ofn):
        pass
  • 使用os.path.exists函数检查输出文件是否已经存在。如果存在,则不做任何操作(pass)。

初始化数据列表

    data = []
  • 初始化一个空列表data,用于存储从输入文件中读取的数据。

读取输入文件

    with open(ifn) as reader:
        for line in reader:
            sample = json.loads(line)
            data.append(sample)
  • 使用with open(ifn) as reader语句打开输入文件,并确保文件在使用后被正确关闭。
  • 使用for line in reader循环逐行读取文件内容。
  • 使用json.loads(line)将每行数据解析为JSON对象,并添加到data列表中。

处理数据

    datas = data
    return_list = process_datas(datas, MODEL_NAME)
  • 将读取到的数据赋值给datas变量。
  • 调用process_datas函数处理数据,并将处理后的结果存储在return_list中。MODEL_NAME是一个全局变量,用于指定处理数据的模型名称。

输出结果

    print(len(return_list))
    print("All tasks finished!")
  • 打印处理后的数据集的长度。
  • 打印“所有任务完成!”的消息。

返回结果

    return return_list
  • 返回处理后的数据集。

注意事项

  1. 依赖库:代码中使用了osjson库,确保在运行代码前已经导入这些库。
  2. 全局变量:代码中使用了MODEL_NAME这个全局变量,确保在使用前已经定义。
  3. 错误处理:代码中没有包含错误处理机制,例如文件不存在、JSON解析错误等。在实际应用中,应该添加适当的错误处理逻辑。
  4. 性能:如果输入文件非常大,逐行读取和处理可能会比较慢。可以考虑使用更高效的数据处理方法,例如使用pandas库进行批量处理。

这段代码的主要用途是从输入文件中读取数据,使用指定的模型进行处理,并将处理后的结果返回。

cell10:evaluate评估

def evaluate(ofn):
    data = []
    with open(ofn) as reader:
        for line in reader:
            sample = json.loads(line)
            data.append(sample)

    pse = 0
    cnt = 0
    tot = 0
    for task in data:
        for question in task['questions']:
            
            if MODEL_NAME in question:
                tot += 1
                cnt += question[MODEL_NAME] == question['answer']
            else:
                pse += 1

    print(cnt, tot, cnt/tot, pse)

这段Python代码定义了一个名为evaluate的函数,用于评估某个模型在处理一组任务中的表现。下面是对代码的详细解释:

函数定义

def evaluate(ofn):
  • evaluate函数接受一个参数ofn,它是一个字符串,表示输入文件的路径。

读取数据

    data = []
    with open(ofn) as reader:
        for line in reader:
            sample = json.loads(line)
            data.append(sample)
  • 首先,定义了一个空列表data,用于存储从文件中读取的数据。
  • 使用with open(ofn) as reader:语句打开文件,并创建一个文件读取器reader
  • 通过for line in reader:循环逐行读取文件内容。
  • 使用json.loads(line)将每行JSON格式的字符串转换为Python字典,并添加到data列表中。

初始化计数器

    pse = 0
    cnt = 0
    tot = 0
  • 初始化三个计数器:
    • pse:用于记录没有包含MODEL_NAME键的样本数量。
    • cnt:用于记录模型正确回答的问题数量。
    • tot:用于记录总的问题数量。

遍历数据并计算准确率

    for task in data:
        for question in task['questions']:
            
            if MODEL_NAME in question:
                tot += 1
                cnt += question[MODEL_NAME] == question['answer']
            else:
                pse += 1
  • 遍历data列表中的每个任务task
  • 对于每个任务中的每个问题question
    • 检查问题中是否包含MODEL_NAME键。
    • 如果包含,则将总问题数量tot加1,并检查模型预测的答案是否与实际答案相同,如果相同则将正确回答的数量cnt加1。
    • 如果不包含,则将没有包含MODEL_NAME键的样本数量pse加1。

输出结果

    print(cnt, tot, cnt/tot, pse)
  • 打印出正确回答的问题数量cnt、总问题数量tot、准确率(正确回答的问题数量除以总问题数量)以及没有包含MODEL_NAME键的样本数量pse

注意事项

  • MODEL_NAME应该是一个全局变量,表示模型的名字,用于在问题中查找模型预测的答案。
  • 输入文件ofn应该是一个包含JSON格式字符串的文件,每行一个JSON对象,每个对象包含一个或多个问题。
  • 代码假设每个问题都有一个answer键表示实际答案,以及一个MODEL_NAME键表示模型预测的答案。如果这些键不存在,代码会抛出异常。

cell11:if __name__ == '__main__':提取一段文本中的特定信息

if __name__ == '__main__':

    a = extract("""根据欧几里得算法,逐步解析计算两个数6和7的最大公约数(gcd)的步骤如下:

1. 判断6和7是否相等:不相等。
2. 判断6和7大小关系,7 > 6,所以用更大的数7减去较小的数6得到结果1。
3. 现在计算6和1的最大公约数。
4. 6 > 1,根据算法用更大的数6减去较小的数1得到结果5。
5. 再计算5和1的最大公约数。
6. 5 > 1,用5减去1得到结果4。
7. 再计算4和1的最大公约数。
8. 4 > 1,用4减去1得到结果3。
9. 再计算3和1的最大公约数。
10. 3 > 1,用3减去1得到结果2。
11. 再计算2和1的最大公约数。
12. 2 > 1,用2减去1得到结果1。
13. 最后计算1和1的最大公约数,两数相等,gcd即为这两个数,也就是1。

因此,6和7的最大公约数是1。

答案是:C.""")

    print(a)
    return_list = main('round1_test_data.jsonl', 'upload.jsonl')

这段代码的主要功能是提取一段文本中的特定信息,并调用一个名为 main 的函数,传入两个文件名作为参数。下面是对代码的详细解释:

  1. if __name__ == '__main__'::

    • 这是一个常见的 Python 语句,用于判断当前脚本是否作为主程序运行。如果是,则执行下面的代码块。
  2. a = extract("""根据欧几里得算法,逐步解析计算两个数6和7的最大公约数(gcd)的步骤如下:...""":

    • 这行代码调用了一个名为 extract 的函数,并传入一个多行字符串作为参数。这个字符串包含了一段关于欧几里得算法计算两个数(6和7)的最大公约数(gcd)的步骤描述。
    • extract 函数的作用是解析这个字符串,提取出需要的信息。具体提取什么信息,取决于 extract 函数的实现。
  3. print(a):

    • 这行代码将 extract 函数的返回值打印出来。a 变量存储了 extract 函数处理后的结果。
  4. return_list = main('round1_test_data.jsonl', 'upload.jsonl'):

    • 这行代码调用了一个名为 main 的函数,并传入了两个文件名作为参数。这两个文件名分别是 'round1_test_data.jsonl''upload.jsonl'
    • main 函数的具体功能取决于它的实现,但从文件名来看,它可能是处理或分析这两个 JSON Lines 格式的文件。

注意事项:

  • extractmain 函数的实现没有在代码中给出,因此无法确定它们的具体功能。需要查看这些函数的定义才能了解它们的具体作用。
  • print(a) 语句在 main 函数调用之后,如果 main 函数没有返回值,那么 a 变量可能不会被正确赋值,从而导致 print(a) 抛出 NameError 异常。
  • main 函数的返回值被赋值给 return_list 变量,但代码中没有对 return_list 进行任何操作,这可能意味着 main 函数的返回值没有被使用,或者后续代码中会用到它。

cell12:has_complete_answerfilter_problems对结果进行判断处理

def has_complete_answer(questions):
    # 这里假设完整答案的判断逻辑是:每个question都有一个'answer'键
    for question in questions:
        if 'answer' not in question:
            return False
    return True

def filter_problems(data):
    result = []
    problem_set = set()

    for item in data:
        # print('处理的item' ,item)
        problem = item['problem']
        if problem in problem_set:
            # 找到已存在的字典
            for existing_item in result:
                if existing_item['problem'] == problem:
                    # 如果当前字典有完整答案,替换已存在的字典
                    if has_complete_answer(item['questions']):
                        existing_item['questions'] = item['questions']
                        existing_item['id'] = item['id']
                    break
        else:
            # 如果当前字典有完整答案,添加到结果列表
            if has_complete_answer(item['questions']):
                result.append(item)
                problem_set.add(problem)

    return result

这段Python代码定义了两个函数:has_complete_answerfilter_problems。下面是对这两个函数的详细解释:

has_complete_answer函数

功能

判断一个问题的集合是否每个问题都有答案。

实现原理
  • 接受一个参数questions,它是一个包含多个问题的列表。
  • 遍历questions列表中的每个问题。
  • 检查每个问题是否包含一个名为'answer'的键。
  • 如果任何一个问题没有'answer'键,函数返回False
  • 如果所有问题都有'answer'键,函数返回True
用途

用于验证问题集合的完整性,确保每个问题都有答案。

注意事项
  • 假设每个问题的答案是以'answer'键的形式存储的。
  • 如果问题的答案存储方式不同,需要修改代码以适应实际的存储结构。

filter_problems函数

功能

过滤掉问题集合中重复的问题,并保留每个问题的完整答案。

实现原理
  • 接受一个参数data,它是一个包含多个字典的列表,每个字典代表一个问题及其相关信息。
  • 初始化一个空列表result用于存储最终结果,以及一个空集合problem_set用于存储已经处理过的问题。
  • 遍历data列表中的每个字典(每个字典代表一个问题及其相关信息)。
  • 从当前字典中提取problem(问题内容)。
  • 如果problem已经在problem_set中,说明之前已经处理过这个问题:
    • 遍历result列表,找到与当前problem相同的问题。
    • 如果当前字典有完整答案,则替换result列表中对应的字典。
  • 如果problem不在problem_set中,说明这是一个新的问题:
    • 如果当前字典有完整答案,将其添加到result列表,并将problem添加到problem_set
  • 返回result列表,其中包含去重且每个问题都有完整答案的问题。
用途

用于处理和整理问题集合,确保每个问题只出现一次,并且每个问题都有完整的答案。

注意事项
  • 假设每个字典中都有一个'problem'键和一个'questions'键,其中'questions'键的值是一个包含多个问题的列表。
  • 假设每个问题的答案是以'answer'键的形式存储在'questions'列表中的每个问题字典中。
  • 如果字典的结构不同,需要修改代码以适应实际的存储结构。
return_list
return_list = filter_problems(return_list)
sorted_data = sorted(return_list, key=lambda x: int(str(x['id'])[-3:]))
print(sorted_data)

这段代码的主要功能是对一个包含问题的列表进行过滤和排序。下面是对代码的详细解释:

  1. return_list:这是一个列表,其中包含了一些问题对象。每个问题对象是一个字典,包含了一些属性,比如id

  2. filter_problems(return_list):这是一个函数调用,假设filter_problems函数的作用是对return_list进行过滤,返回一个新的列表。具体的过滤逻辑没有在代码中给出,但可以推测它可能根据某些条件筛选出符合要求的问题。

  3. sorted_data = sorted(return_list, key=lambda x: int(str(x['id'])[-3:])):这行代码对过滤后的列表return_list进行排序。排序的依据是每个问题对象的id属性。具体来说,它将id属性转换为字符串,然后取字符串的最后三个字符,再将这些字符转换为整数进行比较。这样做的目的是按照id的最后三位数字进行排序。

  4. print(sorted_data):最后,代码打印出排序后的列表sorted_data

实现原理

  • filter_problems函数的具体实现没有给出,但可以推测它可能使用列表推导式、filter函数或for循环等来筛选出符合条件的问题。
  • sorted函数用于对列表进行排序,key参数指定了排序的依据,这里使用了lambda函数来定义排序规则。

用途

  • 这段代码的用途是对一组问题进行筛选和排序,以便于后续处理或展示。例如,在处理一个包含大量问题的数据集时,可能需要先筛选出特定类型的问题,然后按照某种顺序(如按照id的最后三位数字)进行排序。

注意事项

  • filter_problems函数的实现需要确保返回的是一个列表,且列表中的每个元素都是字典,并且包含id属性。
  • 在排序时,如果id的长度小于3,可能会导致int(str(x['id'])[-3:])抛出IndexError异常。因此,在实际应用中,可能需要添加一些错误处理逻辑来确保代码的健壮性。

cell13:展示数据

sorted_data
[{'problem': '有一群人和一些食物类型。下列是关于这些个体和食物的已知信息:\n\n1. 鸡肉是一种食物。\n2. 苹果是一种食物。\n3. 如果X吃了Y,且X活着,则Y是一种食物。\n4. Bill存活。\n5. Bill吃了花生。\n6. John吃所有食物。\n7. Sue吃所有Bill吃的食物。\n8. John喜欢所有食物。\n\n根据以上信息,回答以下选择题:',
  'questions': [{'question': '选择题 1:\n谁喜欢吃花生?',
    'options': ['Bill', 'Sue', 'John', 'None of the above'],
    'answer': 'C'}],
  'id': 'round1_test_data_000'},
 {'problem': '设有两个列表操作,第一个列表中包含若干元素,并且可以将第二个列表的元素追加到第一个列表的末尾形成一个新的列表。根据这个操作,请回答以下问题:',
  'questions': [{'question': '选择题 1:\n当第一个列表是[a, b, c],第二个列表是[d, e]时,新的列表是什么?',
    'options': ['[a, b, c, d]',
     '[a, d, e, b, c]',
     '[a, b, c, d, e]',
     '[d, e, a, b, c]'],
    'answer': 'C'},
   {'question': '选择题 2:\n如果新列表是[a, b, c, d, e],而第二个列表是[d, e],那么第一个列表是什么?',
    'options': ['[a]', '[a, b]', '[a, b, c]', '[b, c]'],
    'answer': 'C'},
   {'question': '选择题 3:\n当第一个列表是[a, b, c],新的列表是[a, b, c, d, e],那么第二个列表是什么?',
    'options': ['[d, e]', '[b, c, d]', '[c, d, e]', '[b, d, e]'],
    'answer': 'A'}],
  'id': 'round1_test_data_001'},
  ……

cell14:find_missing_ids找出缺失的序号

def find_missing_ids(dict_list):
    # 提取所有序号
    extracted_ids = {int(d['id'][-3:]) for d in dict_list}
    
    # 创建0-500的序号集合
    all_ids = set(range(500))
    
    # 找出缺失的序号
    missing_ids = all_ids - extracted_ids
    
    return sorted(missing_ids)

# 示例字典列表
dict_list = sorted_data

# 找出缺失的序号
missing_ids = find_missing_ids(dict_list)
print("缺失的序号:", missing_ids)

这段Python代码的目的是在一个字典列表中找出缺失的序号。具体来说,它从一个字典列表中提取出所有序号,然后与一个完整的序号集合进行比较,找出那些在字典列表中缺失的序号。

实现原理

  1. 提取所有序号:通过遍历字典列表,提取每个字典中键为’id’的值,并从这些值中提取最后三位数字,形成一个新的集合extracted_ids。这里假设’id’的值是一个字符串,且最后三位数字是序号。

  2. 创建0-500的序号集合:创建一个包含0到500的整数集合all_ids,表示所有可能的序号范围。

  3. 找出缺失的序号:通过集合的差集操作,从all_ids中减去extracted_ids,得到缺失的序号集合missing_ids

  4. 返回并打印缺失的序号:将缺失的序号集合排序后返回,并打印出来。

用途

这个函数可以用于数据完整性检查,特别是在处理有序数据时,比如日志文件、序列号等,确保所有预期的序号都存在。

注意事项

  1. 假设条件:代码假设字典列表中的每个字典都有一个’id’键,且’id’的值是一个字符串,最后三位数字是序号。如果实际情况不符合这个假设,代码可能会出错。

  2. 序号范围:代码中硬编码了序号的范围为0到500。如果实际应用中的序号范围不同,需要相应地修改代码。

  3. 性能:对于非常大的字典列表,提取和比较操作可能会消耗较多的时间和内存。

  4. 排序:代码中使用了sorted函数对缺失的序号进行排序,确保输出结果是有序的。如果不需要排序,可以移除这一行。

  5. 输入数据:代码中的sorted_data应该是一个已经定义好的字典列表,且已经按照某种逻辑排序。如果sorted_data未定义或未排序,代码将无法正确运行。

cell15:读取jsonl文件,并格式化

data  = []
with open('round1_test_data.jsonl') as reader:
    for id,line in enumerate(reader):
        if(id in missing_ids):
            sample = json.loads(line)
            for question in sample['questions']:
                question['answer'] = 'A'
            sorted_data.append(sample)
sorted_data = sorted(sorted_data, key=lambda x: int(str(x['id'])[-3:]))

这段Python代码的主要功能是从一个JSONL文件中读取数据,对特定ID的数据进行处理,然后将处理后的数据按特定规则排序并存储。下面是对代码的详细解释:

  1. 初始化空列表

    data = []
    

    初始化一个空列表data,用于存储处理后的数据。

  2. 打开并读取JSONL文件

    with open('round1_test_data.jsonl') as reader:
    

    使用with open语句打开名为round1_test_data.jsonl的文件,并创建一个文件读取器readerjsonl文件是一种每行包含一个JSON对象的文件格式。

  3. 遍历文件中的每一行

    for id, line in enumerate(reader):
    

    使用enumerate函数遍历文件中的每一行,id是行号,line是当前行的内容。

  4. 检查ID是否在missing_ids列表中

    if(id in missing_ids):
    

    如果当前行的ID在missing_ids列表中,则执行以下操作。

  5. 加载JSON数据

    sample = json.loads(line)
    

    使用json.loads函数将当前行的JSON字符串转换为Python字典对象sample

  6. 修改sample中的数据

    for question in sample['questions']:
        question['answer'] = 'A'
    

    遍历sample字典中的questions列表,将每个问题的答案设置为’A’。

  7. 将处理后的数据添加到sorted_data列表中

    sorted_data.append(sample)
    

    将处理后的sample添加到sorted_data列表中。

  8. sorted_data列表进行排序

    sorted_data = sorted(sorted_data, key=lambda x: int(str(x['id'])[-3:]))
    

    使用sorted函数对sorted_data列表进行排序,排序的依据是每个样本的ID的最后三位数字。key=lambda x: int(str(x['id'])[-3:])表示将ID转换为字符串,取最后三位,然后转换为整数进行比较。

注意事项

  • missing_ids列表应该在代码的其他部分定义,并且包含需要处理的ID。
  • sorted_data列表在处理过程中被修改,最终包含了处理后的数据。
  • 代码假设round1_test_data.jsonl文件中的每个JSON对象都有一个id字段和一个questions字段,且questions字段是一个列表。如果文件格式不符合这些假设,代码可能会抛出异常。
with open('upload.jsonl', 'w') as writer:
    for sample in sorted_data:
        writer.write(json.dumps(sample, ensure_ascii=False))
        writer.write('\n')

这段Python代码的主要功能是将一个名为sorted_data的列表中的数据写入到一个名为upload.jsonl的文件中。具体来说,它将每个样本(sample)转换为JSON格式,并写入文件,每个样本占一行。下面是对代码的详细解释:

  1. 打开文件

    with open('upload.jsonl', 'w') as writer:
    

    这行代码使用with语句打开一个名为upload.jsonl的文件,并以写入模式(‘w’)打开。with语句确保文件在操作完成后会被正确关闭,即使在写入过程中发生异常也是如此。writer是一个文件对象,用于后续的文件写入操作。

  2. 遍历数据

    for sample in sorted_data:
    

    这行代码遍历sorted_data列表中的每个元素,每个元素被命名为samplesorted_data应该是一个包含多个字典或其他可序列化为JSON格式的对象的列表。

  3. 将数据转换为JSON格式并写入文件

    writer.write(json.dumps(sample, ensure_ascii=False))
    writer.write('\n')
    
    • json.dumps(sample, ensure_ascii=False):这行代码将sample对象转换为JSON格式的字符串。ensure_ascii=False参数确保非ASCII字符(如中文)被正确编码,而不是被转义为Unicode字符。
    • writer.write(...):这行代码将转换后的JSON字符串写入文件。
    • writer.write('\n'):这行代码在写入JSON字符串后添加一个换行符,确保每个JSON对象占一行。

用途
这段代码的用途是将一个包含多个JSON对象的列表写入到一个JSON Lines格式的文件中。JSON Lines格式是一种方便的文本格式,每个JSON对象占一行,适用于处理大量数据,因为它允许逐行读取和处理数据,而不需要一次性加载整个文件到内存中。

注意事项

  • 确保在运行代码之前,sorted_data列表已经被正确初始化并包含有效的数据。
  • 如果upload.jsonl文件已经存在,这段代码会覆盖该文件的内容。如果需要追加内容而不是覆盖,可以将文件打开模式改为'a'(追加模式)。
  • json.dumps函数的ensure_ascii参数设置为False是为了确保非ASCII字符被正确处理,这在处理包含中文字符的数据时尤其重要。
  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值