#AI夏令营 #Datawhale
一、前言
一次偶然的机会,看到了Datawhale的AI夏令营,授课的内容正是当下火热的机器学习——人工智能技术发展的根基。可以说,没有机器学习理论的发展,也没有人工智能的今天。但是呢,学习机器学习的过程并不是那么容易,网上有很多资源:GitHub上的各种开源代码、B站的各种中外网课,亦或是像“西瓜书”“南瓜书”等等,但是对于一个新手来说,确实不知道从哪里下手。但是看到了Datawhale的这个夏令营后,我认为这也许是一次新的机会,于是我果断选择了报名参加。因为我自己是数学与应用数学专业的学生,我就选择了自己较为擅长的逻辑推理。
二、执行任务
考虑到有很多的小白参加这期夏令营,所以第一期的任务较为轻松。
step1、报名赛事
附上链接:http://competition.sais.com.cn/competitionDetail/532231/format
这次夏令营是与第二届世界科学智能大赛这项比赛相关联的,也就是说,我们每次的任务运行的结果都要以jsonl文件的形式上传至第二届世界科学智能大赛的官网。
报名的过程很简单,只需要手机号就可以完成。但是报名完成后需要认证才能查看原始的数据集,认证方式较为严格,类似于老外的手持证件拍照的认证方式(见图1)。
![](https://i-blog.csdnimg.cn/direct/9b0374bce5cd40518d1afad768390a01.png)
赛事的要求较为严格,代码需要在32GB显存的电脑上运行3小时内跑出结果,而且代码和原始数据总共的大小不超过50GB。这一下可把我吓坏了,我平时用的显卡可都只有8个GB,而且见到的代码都没有超过10MB的,这也许是我第一次接触这样规模的数据集和代码了吧!
step2、申领大模型API
附上链接:https://dashscope.console.aliyun.com/apiKey
申领的过程比较简单,我们只需要在阿里云上面注册账号就可以得到新用户的100w免费的token额度。我们之后的代码就需要调用API,使用的是qwen2-7b-instruct模型。跑一次代码大概是消耗60w的token,我之前没注意就咔咔调用了三次我的模型,结果收到了阿里云的欠费短信(悲)。
![](https://i-blog.csdnimg.cn/direct/417150665926431fb9d5495bbec3d49c.png)
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、提交文件
提交后过个一分钟就可以查看分数啦!如图是我的三次提交的分数结果。
![](https://i-blog.csdnimg.cn/direct/d1b223e9830d40e8a58fd09f46785247.png)
三、其他
这应该是我第一次体验利用人工智能技术并调用API去完成逻辑推理。但是实际的比赛是不能调用API的(断网运行),所以这仅仅是一次体验,还有更重要的挑战等着我们!