Datawhale AI夏令营 从零入门 AI 逻辑推理 心得第一弹

#AI夏令营 #Datawhale

一、前言

一次偶然的机会,看到了Datawhale的AI夏令营,授课的内容正是当下火热的机器学习——人工智能技术发展的根基。可以说,没有机器学习理论的发展,也没有人工智能的今天。但是呢,学习机器学习的过程并不是那么容易,网上有很多资源:GitHub上的各种开源代码、B站的各种中外网课,亦或是像“西瓜书”“南瓜书”等等,但是对于一个新手来说,确实不知道从哪里下手。但是看到了Datawhale的这个夏令营后,我认为这也许是一次新的机会,于是我果断选择了报名参加。因为我自己是数学与应用数学专业的学生,我就选择了自己较为擅长的逻辑推理。

二、执行任务

考虑到有很多的小白参加这期夏令营,所以第一期的任务较为轻松。

step1、报名赛事

附上链接:http://competition.sais.com.cn/competitionDetail/532231/format

这次夏令营是与第二届世界科学智能大赛这项比赛相关联的,也就是说,我们每次的任务运行的结果都要以jsonl文件的形式上传至第二届世界科学智能大赛的官网。

报名的过程很简单,只需要手机号就可以完成。但是报名完成后需要认证才能查看原始的数据集,认证方式较为严格,类似于老外的手持证件拍照的认证方式(见图1)。

图1:赛事认证证件照要求

赛事的要求较为严格,代码需要在32GB显存的电脑上运行3小时内跑出结果,而且代码和原始数据总共的大小不超过50GB。这一下可把我吓坏了,我平时用的显卡可都只有8个GB,而且见到的代码都没有超过10MB的,这也许是我第一次接触这样规模的数据集和代码了吧!

step2、申领大模型API

附上链接:https://dashscope.console.aliyun.com/apiKey

申领的过程比较简单,我们只需要在阿里云上面注册账号就可以得到新用户的100w免费的token额度。我们之后的代码就需要调用API,使用的是qwen2-7b-instruct模型。跑一次代码大概是消耗60w的token,我之前没注意就咔咔调用了三次我的模型,结果收到了阿里云的欠费短信(悲)。

图2:超过给定额度的悲惨经历

 step3、启动魔搭notebook

附上链接:https://www.modelscope.cn/my/mynotebook/preset

我们的代码可以在本地完成,也可以在云端完成。为了照顾像我这样的新手,本地配置环境以及解释器可能需要考虑诸多问题,于是夏令营团队建议我们在云端完成代码运行。过程也很简单,由于我们的过程不需要涉及诸多复杂的推理,我们只需选择CPU即可(云电脑启动的过程需要两分多钟,要耐心等待)。

step4、30分钟体验一站式baseline(关键)

我们需要下载夏令营提供的代码以及比赛官方提供的数据集,baseline如下:
 

!pip install scipy openai tiktoken retry dashscope loguru

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'

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

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

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()

# 这里定义了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


# 这里使用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]

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

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

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)

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')


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

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

sorted_data

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)

len(missing_ids)

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:]))
        

with open('upload.jsonl', 'w') as writer:
    for sample in sorted_data:
        writer.write(json.dumps(sample, ensure_ascii=False))
        writer.write('\n')

源文件应该是个ipynb文件,但我不知道怎么以附件的形式发布出来。这个相较于原本的baseline文件有了一定程度的改善(夏令营这么讲的,虽然我也不懂)。

还有数据集,由于有大概23w的字,不好放出来,不过可以在文章置顶的资源中找到。

大概过了30分钟代码就能跑出来了,我们可以得到一个名为'upload.jsonl'的文件,下载至本地即可。

step5、提交文件

提交后过个一分钟就可以查看分数啦!如图是我的三次提交的分数结果。

三次得分

三、其他

这应该是我第一次体验利用人工智能技术并调用API去完成逻辑推理。但是实际的比赛是不能调用API的(断网运行),所以这仅仅是一次体验,还有更重要的挑战等着我们!

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值