一、介绍及数据处理思路
1.1 介绍
Task2的任务包含了对数据处理策略的说明、微调步骤的详细介绍、baseline代码的深入解析以及相关附加知识的讲解。
1.2 数据处理思路
1.2.1 微调数据
数据集包含了近年来语文和英语阅读材料的数据,依据讯飞大模型定制训练平台的要求进行处理。数据集分为input和target两部分。Input部分由用于训练的阅读材料及其相关要求组成,提供了大模型生成阅读题目的基础内容;target部分则是训练目标,包含选项和参考答案。总之,输入到讯飞大模型的数据集由两部分组成:一部分是阅读原文(这是模型训练的核心基础),另一部分是范例题目(用于训练大模型以生成后续的题目)。
1.2.2 数据处理方案
本文使用了正则表达式模块re来进行字符串处理,并使用pandas库来处理xlsx文件。
自Python 1.5版本起,引入的re模块提供了类似于Perl的正则表达式功能。通过re.compile函数,可以将模式字符串编译为正则表达式对象,该对象具备匹配和替换的能力。re模块还提供了直接使用模式字符串的函数,其功能与正则表达式对象的方法一致。
Pandas是一个基于Numpy的开源数据分析工具库,其名称来源于“panel data”和“Python data analysis”。该库提供了高效的工具,用于从CSV、JSON、SQL、Excel等格式的数据中进行导入、归并、重塑、选择和清洗,广泛应用于学术研究、金融和统计领域。
基于上述方法,将文件中的数据读取后,使用re.compile整理数据,并将其保存为适合微调的数据格式。
二、微调流程
2.1 大模型模型介绍
大语言模型(LLM)是一种旨在理解和生成自然语言的人工智能模型,通常拥有数十亿甚至更多的参数,并在大量文本数据上进行了训练,如GPT-3、GPT-4、PaLM等。
LLM与传统模型的主要差异在于其涌现能力,包括上下文学习、指令执行和逐步推理,这使得LLM能够更灵活地应对复杂任务,并提供更加精准的回答和推理能力。
LLM拥有庞大的参数规模,支持预训练与微调,具备强大的上下文感知、多语言与多模态处理能力,广泛应用于文本生成、自动翻译等领域。
然而,LLM也面临着伦理与风险挑战,如生成有害内容、侵犯隐私和存在认知偏差等问题。
2.2 微调介绍
微调步骤如图 1所示。
图 1 微调步骤
2.2.1 使用微调的情况(条件)和原因
使用微调的条件为使用的数据集和预训练模型的数据集类似(即相似的特征提取)、自己搭建模型的正确率太低、数据集数量太少和计算资源太少。
使用微调的原因在于:
- 需要有限数据量即可,节约时间和计算资源。
- 收敛存在模型,优化参数,提高准确率,解决模型泛化能力低、过拟合等问题。
2.2.2 不同数据集的微调方法
不同的数据集以数据量和相似度作为特点来区分,不同特点的数据集有不同的微调解决方法,根据精讲,可以将微调方法总结如所示。
表 1 不同数据集的微调方法
数据集编号 | 特点 | 解决方法 |
1 | 数据量少且相似度高 | 修改模型的最后几层或softmax图层的输出类别 |
2 | 数据量少且相似度低 | 冻结预训练模型的初始层并重新训练较高层 |
3 | 大量但相似度低的数据 | 从头训练神经网络 |
4 | 数据量大且相似度高 | 保留预训练模型的架构和初始权重进行再训练 |
2.2.3 微调注意事项
通常情况下,会截断预训练网络的最后一层并替换为与我们任务相关的新softmax层,如从1000个类别改为10个类别。在训练时使用较小的学习率,以避免过快扭曲预训练的权重。对于小数据集,仅训练最后一层;对于中等数据集,冻结前几层权重以保留通用特征,让网络专注于学习深层特定特征。
2.2.4 高效微调方法-Lora
LoRA (Low-rank Adaptation) 是一种高效微调大语言模型的方法,旨在解决全量参数 Fine-tune 的资源消耗问题。传统的 Fine-tune 需要调整模型所有参数,随着预训练模型规模的扩大,资源压力倍增。
LoRA 改进了传统的 Adapt Tuning 方法,通过优化密集层的秩分解矩阵来实现微调,避免了增加额外参数带来的推理延迟问题。它可以有效构建小型 LoRA 模块,针对不同任务切换,并使用自适应优化器,减少硬件要求。
LoRA 主要在 Transformer 结构中的注意力模块应用,通过低秩分解更新部分权重矩阵,冻结其他部分,进而高效完成微调。
通过 peft 库,LoRA 的实现变得便捷,可以轻松应用于大模型的微调。具体步骤包括确定需要使用 LoRA 的层,替换为 LoRA 层并冻结原参数,然后使用 Trainer 进行训练。
LoRA微调方法在不显著增加计算量的情况下,提高了模型在不同任务上的适应性。
三、baseline1精讲 %源于Datawhale
|
!pip install pandas openpyxl |
|
2.1 数据加载 # coding~ import pandas as pd import re # 读取Excel文件 df = pd.read_excel('训练集-语文.xlsx') df = df.replace('.', '.', regex=True) df = df.replace('(', '(', regex=True) # 读取第二行(即第三行)“选项”列的内容 # 可以使用loc获取某行的数据 second_row_option_content = df.loc[2, '选项'] # 显示第二行“选项”列的内容 print(second_row_option_content)
def chinese_multiple_choice_questions(questions_with_answers): # 输入的题目文本 text = questions_with_answers question_pattern = re.compile(r'\d+\..*?(?=\d+\.|$)', re.DOTALL) # 这一行作用是匹配一个以数字开头、后面跟着一个点字符的字符串, #。直到遇到下一个数字和点字符或字符串结束。 choice_pattern = re.compile(r'([A-D])\s*(.*?)(?=[A-D]|$|\n)', re.DOTALL) # 这一行作用是匹配一个以字母[A到D]开头、后面跟着一个点字符的字符串, #直到遇到下一个[A到D]或字符串结束。 # 找到所有问题 questions = question_pattern.findall(text) # 初始化选择题和简答题列表 multiple_choice_questions = [] short_answer_questions = [] # 处理每个问题 for id,question in enumerate(questions): # 这里取到的question,如果是选择题会带着选择题的选项。 # 检查是否是选择题 因为选择题内有ABCD这样的选项 if re.search(r'[A-D]', question): # 如果有选项,提取出选项的内容 choices = choice_pattern.findall(question) # 这里提取了题目的内容,因为每个题目都会有一个打分的(X分)这样的标记 # 以左括号为目标,截取选择题选项中的内容 question_text = re.split(r'\n', question.split('(')[0])[0] pattern_question = re.compile(r'(\d+)\.(.*)') # 这里清洗了选择题的编号,重新用循环中的id进行编号。 # 如果不做这一步可以发现给定的数据中编号是乱序的。 matches_question = str(id+1)+'.'+ pattern_question.findall(question_text)[0][1] # 取出问题后重排序 # print(str(id+1)+'.'+matches_question) # 这里我们实现声明好了存储的列表 # 将每个问题和选项以字典的形式存入方便我们处理 multiple_choice_questions.append({ 'question': matches_question, 'choices': choices }) else: # 大家可以想想这里怎么用? short_answer_questions.append(question.strip()) # 最后我们返回抽取后的选择题字典列表 return multiple_choice_questions
def chinese_multiple_choice_answers(questions_with_answers): # 首先清洗输入字段,因为答案字段中的格式不统一,清洗后便于统一处理。 # 这里删除了所有的换行和空格 questions_with_answers = questions_with_answers.replace(" ", "").replace("\n", "") # print(questions_with_answers) # 使用正则表达式匹配答案 # 这里我们主要使用第一个匹配 一个数字+点+字母ABCD之间一个 choice_pattern = re.compile(r'(\d+)\.([A-Z]+)') # 下面这句匹配的是简答题答案~ 目前可以忽略 short_pattern = re.compile(r'(\d+)\.([^A-Z]+)') # 找到所有匹配的答案 choice_matches = choice_pattern.findall(questions_with_answers) short_matches = short_pattern.findall(questions_with_answers) # 将匹配结果转换为字典 choice_answers = {int(index): answer for index, answer in choice_matches} short_answers = {int(index): answer for index, answer in short_matches} # 按序号重新排序 sorted_choice_answers = sorted(choice_answers.items()) sorted_short_answers = sorted(short_answers.items()) answers = [] # 输出结果 # print("选择题答案:") for id in range(len(sorted_choice_answers)): # 这里我们也将重新编号号的答案作为返回,返回的是一个列表,方便与问题字典列表匹配~ answers.append(f"{id+1}. {sorted_choice_answers[id][1]}") return answers
def get_prompt_cn(text): prompt = f''' 你是⼀个⾼考选择题出题专家,你出的题有⼀定深度,你将根据阅读文本,出4道单项选择题,包含题目选项,以及对应的答案,注意:不⽤给出原文,每道题由1个问题和4个选项组成,仅存在1个正确答案,请严格按照要求执行。 阅读文本主要是中文,你出的题目需要满足以下要点,紧扣文章内容且题干和答案为中文: ### 回答要求 (1)理解文中重要概念的含义 (2)理解文中重要句子的含意 (3)分析论点、论据和论证方法 ### 阅读文本 {text} ''' return prompt
def process_cn(df): # 定义好返回列表 res_input = [] res_output = [] for id in range(len(df)): # 逐个遍历每行的选项、答案、阅读文本的内容 data_options = df.loc[id, '选项'] data_answers = df.loc[id,'答案'] data_prompt = df.loc[id,'阅读文本'] # 处理选项部分,抽取出选择题题目及选项 data_options = chinese_multiple_choice_questions(data_options) # 处理答案部分,抽取出选择题答案 data_answers = chinese_multiple_choice_answers(data_answers) # 抽取阅读材料组合成input内容 data_prompt = get_prompt_cn(data_prompt) # print(data_options) # print(data_answers) # 做数据验证,因为训练数据格式不能确定每组数据都能被正常处理(会有一部分处理失败) # 我们验证一下两个列表的长度 如果相同代表数据处理正确 if(len(data_answers)==len(data_options)): # 定义output的数据字符串 res = '' # 处理选择题目中的每个数据,逐个拼入到output字符串 for id_,question in enumerate(data_options): # 首先放入题目 res += f''' {question['question']}? '''+'\n' # 然后找到选择题的每个选项,进行choices列表循环 for choise in question['choices']: # 逐个将选项拼接到字符串 res = res+ choise[0] + choise[1]+ '\n' # 最后将答案拼接到每个选择题的最后 # 以 答案:题号.选项的格式 res = res + '答案:' + str(data_answers[id_].split('.')[-1]) + '\n' # 最后将处理得到的input、output数据存入到列表 res_output.append(res) res_input.append(data_prompt) # break return res_input,res_output |
|
3.1 数据加载 # coding~ import pandas as pd # 读取Excel文件 df = pd.read_excel('训练集-英语.xlsx') # 英文数据处理中有一部分ocr识别的题目,这种题目中看上去是字母A,但是实际为俄文的字母,, # 所以开始使用全局匹配做了清洗…… df = df.replace('.', '.', regex=True).replace('А.', 'A.', regex=True).replace('В.', 'B.', regex=True).replace('С.', 'C.', regex=True).replace('D.', 'D.', regex=True) # df = df.replace('(', '(', regex=True) # 读取第二行(即第三行)“选项”列的内容 second_row_option_content = df.loc[0, '选项'] # 显示第二行“选项”列的内容 print(second_row_option_content)
import re # 示例文本 text = second_row_option_content def get_questions(text): # 数据清洗,将所有换行改为两个空格方便统一处理 text = text.replace('\n', ' ')+' ' # print(text) # 正则表达式模式 # 通过匹配以数字开头然后带一个点,为题干 # 然后抽取选项A 以A开头 后面带一个点 最后以两个空格结尾 # 为什么是两个空格?部分数据换行时为换行符,我们已经换成了两个空格,有些是以多个空格分割,我们默认为两个空格 # 接着匹配B C D选项内容 # 最后有一个 pattern = re.compile(r'(\d+\..*?)(A\..*?\s{2})([B-D]\..*?\s{2})([B-D]\..*?\s{2})(D\..*?\s{2})', re.DOTALL) # 查找所有匹配项 matches = pattern.findall(text) # 存储结果的字典列表 questions_dict_list = [] # 打印结果 for match in matches: question, option1, option2, option3, option4 = match pattern_question = re.compile(r'(\d+)\.(.*)') # 第一个为选择题的题目 提前存到question_text question_text = pattern_question.findall(question.strip())[0][1] # 提取选项字母和内容 options = {option1[0]: option1, option2[0]: option2, option3[0]: option3, option4[0]: option4} question_dict = { 'question': question_text, # 这一步就是防止ACBD这种乱序,我们进行重新匹配,将可能是ACBD的数据以首字母按位置排好号 'options': { 'A': options.get('A', '').strip(), 'B': options.get('B', '').strip(), 'C': options.get('C', '').strip(), 'D': options.get('D', '').strip() } } questions_dict_list.append(question_dict) # 最后获得 return questions_dict_list # 调用函数并打印结果 questions = get_questions(text) for q in questions: print(q)
# 首先做数据清洗,将空格、换行符及点都删除 def remove_whitespace_and_newlines(input_string): # 使用str.replace()方法删除空格和换行符 result = input_string.replace(" ", "").replace("\n", "").replace(".", "") return result import re # 示例文本 text = """ 32. B. The underlying logic of the effect. 33.D. estimates were not fully independent. 34.C. The discussion process. 35.D. Approving. """ def get_answers(text): text = remove_whitespace_and_newlines(text) # 正则表达式模式 # 这里是一个数字加一个A-D的大写字母表示为答案区域,因为有些答案中有解释,这样的匹配规则可以尽可能匹配到答案 pattern = re.compile(r'(\d)\s*([A-D])') # 查找所有匹配项 matches = pattern.findall(text) res = [] # 打印结果 for match in matches: number_dot, first_letter = match res.append(first_letter) return res
def get_prompt_en(text): prompt = f''' 你是⼀个⾼考选择题出题专家,你出的题有⼀定深度,你将根据阅读文本,出4道单项选择题,包含题目选项,以及对应的答案,注意:不⽤给出原文,每道题由1个问题和4个选项组成,仅存在1个正确答案,请严格按照要求执行。 The reading text is mainly in English. The questions and answers you raised need to be completed in English for at least the following points: ### 回答要求 (1)Understanding the main idea of the main idea. (2)Understand the specific information in the text. (3)infering the meaning of words and phrases from the context ### 阅读文本 {text} ''' return prompt
def process_en(df): res_input = [] res_output = [] for id in range(len(df)): data_options = df.loc[id, '选项'] data_answers = df.loc[id,'答案'] data_prompt = df.loc[id,'阅读文本'] data_options = get_questions(data_options) data_answers = get_answers(data_answers) data_prompt = get_prompt_en(data_prompt) # print(data_options) # print(data_answers) if(len(data_answers)==len(data_options)): res = '' for id,question in enumerate(data_options): res += f''' {id+1}.{question['question']} {question['options']['A']} {question['options']['B']} {question['options']['C']} {question['options']['D']} answer:{data_answers[id]} '''+'\n' res_output.append(res) res_input.append(data_prompt) return res_input,res_output # break |
|
# 将两个列表转换为DataFrame df_new = pd.DataFrame({'input': cn_input+cn_input[:30]+en_input+en_input[:20], 'output': cn_output+cn_output[:30]+en_output+en_output[:20]}) |
四、相关学习资料
- Python:二次元的Datawhale的个人空间-二次元的Datawhale个人主页-哔哩哔哩视频 (bilibili.com)
- Pandas相关知识:https://github.com/datawhalechina/joyful-pandas
- 大语言模型知识:
https://github.com/datawhalechina/so-large-lm