当前位置: 首页 > news >正文

开始构建我们自己的大语言模型:数据处理部分

关注本专栏(NLP简论:手搓大语言模型实践) 继续学习从头编写、训练自己的大语言模型。

接上集,本章我们将深入说一下大语言模型数据处理部分的细节,并直接提供本部分的完整代码。

【配套资源】
暂时的词汇表:内部配套资料-LX2中文词汇表下载
*
在后面我们会根据需求微调

1. 数据准备

首先,我们需要准备原始文本数据。这些数据可以来自各种来源,如书籍、网页、社交媒体等。在处理这些数据之前,确保它们已经被清洗过,去除了噪声和无关内容。
暂时用不到所以不提供配套资源,后面会提供。
*

2. 词汇表构建

在构建大语言模型时,我们需要将文本转换为模型可以理解的数字形式。这通常通过构建一个词汇表(即Vocabulary)来实现(上一章我们提到过),词汇表中的每个词(或字符)都对应一个唯一的数字(我们成为索引)。

参考代码:

vocab = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,!?;'"  # 示例词汇表,之后要用到的中文版大型词汇表见【配套资源】。
char2idx = {u: i for i, u in enumerate(vocab)}  # 将每个字对应到数字
idx2char = np.array(vocab)  # 将数字对应到每个字# 打印部分词汇表以验证
print('{')
for char, _ in zip(char2idx, range(20)):print(f'  {repr(char)}: {char2idx[char]},')
print('  ...\n}')

3. 文本预处理

文本预处理是数据处理中的关键步骤,包括分词、去除停用词、词干提取等。在大语言模型中,由于我们通常在字符级别或子词级别操作,所以这些步骤可能相对简单。但是,我们仍然需要处理一些特殊字符,如换行符、制表符等。

4. 文本编码

将文本转换为模型可以处理的数字序列。这通常通过查找词汇表中每个字符的索引来实现。

5. 序列划分

由于大语言模型通常使用序列到序列(Seq2Seq)的学习方式,我们需要将长文本划分为多个较短的序列,每个序列包含一个输入部分和一个目标部分。这通常通过滑动窗口的方式实现。我们之前提到过,模型输入一段文本(序列)推理下一个token(这里是一个字)。

拓展知识(理解不了就不用理解了)


问:

为什么训练数据的X和y都是序列,按照之前说的,不应该是x是序列,y是序列的下一个token吗?

答:

在训练循环神经网络(RNN)时,我们通常使用教师强制(Teacher Forcing)技术。这意味着在每个时间步,模型的输入不仅包括来自上一时间步的隐藏状态,而且还包括来自训练数据集的真实下一个token,而不是模型自己在上一步预测的token。
*
例如,如果我们有一个训练序列 “abcd”,并且我们的目标是预测序列的下一个字符,那么目标序列将是"bcde"。在训练过程中,对于每一个时间步,我们都会给模型提供正确的下一个字符,而不是让模型基于自己的预测来做出下一步的预测。
*
具体来说,这是训练时的输入和目标是如何匹配的:
*
时间步1:输入是 “a”,目标是 “b”
时间步2:输入是 “b”,目标是 “c”
时间步3:输入是 “c”,目标是 “d”
时间步4:输入是 “d”,目标是 “e”
*
在这个过程中,模型的目标是在每个时间步预测出序列中的下一个字符。由于使用了教师强制,模型在每个时间步都得到了正确的输入,这有助于模型学习序列中的模式。但是,这种方法的一个潜在问题是,如果在推理阶段模型的预测出现错误,这个错误可能会在后续的预测中累积,因为模型现在是在用自己的预测作为输入,而不是真实的序列数据。

因此,训练和预测之间的主要区别在于,训练时使用教师强制提供正确的下一个token,而在预测时模型需要依靠自己的预测来决定后续的输入。


参考函数split_input_target

def split_input_target(chunk):input_text = chunk[:-1]  # 输入文本为当前序列除最后一个字符外的所有字符target_text = chunk[1:]  # 目标文本为当前序列从第二个字符开始的所有字符return input_text, target_text

6. 数据集准备

将处理好的数据转换为TensorFlow数据集格式,以便进行高效的批处理和训练。

参考代码片段:

import tensorflow as tf# 假设 BUFFER_SIZE 和 BATCH_SIZE 已经定义
BUFFER_SIZE = 50000
BATCH_SIZE = 64# 假设 vocab_size, embedding_dim, rnn_units, window 这里就不做定义了
# vocab_size:词汇表大小(有多少个token)
# embedding_dim:编码层维度(从几个维度理解token们)
# rnn_units:RNN(上面拓展知识提到过)层的神经元数量
# window:模型上下文长度(模型基于多长的原文预测下一个token)# 加载保存的数据集
def load_dataset(path):dataset = tf.data.experimental.load(path)return dataset# 加载数据集
dataset = load_dataset(r'path_to_your_dataset')
# 具体代码见文末

7. 注意事项

  • 数据清洗:确保数据干净,无噪声。
  • 内存管理:处理大型数据集时,注意内存使用,避免内存溢出。
  • 数据增强:如果数据量不足,可以考虑数据增强技术。
  • 效率优化:优化数据处理流程,提高训练效率。

通过以上步骤,我们可以为构建大语言模型准备好干净、高效的数据集。接下来,就可以进入模型构建和训练阶段了。

本部分完整代码:

                                            
def train(mt=3,big_file=False,#是否采用大文件加载策略#微调数据集path_to_file = r'en_novel.txt',ntype_='_en',#保存为微调模型名称#设置vocab版本vtype_='_lx',#type_#fen=50,#数据量分几份fwidx=0,#第几份BATCH_SIZE = 64,loadmodel=False,pass_=-1,ste=0,):'''多出的参数不必理会,后面会用到'''global LR,param_data,p_ntypep_ntype=ntype_if ntype_[0]!='_':ntype_='_'+ntype_type_=ntype_print('path_to_file',path_to_file)print('LR',LR)import os#dataset与vocab是配对的!if not os.path.exists(r'E:\小思框架\论文\ganskchat\vocab'+vtype_+'.txt'):raise Exception("can't reading vocab from "+r'E:\小思框架\论文\ganskchat\vocab'+vtype_+'.txt')else:with open('E:\\小思框架\\论文\\ganskchat\\vocab'+vtype_+'.txt','r',encoding='utf-8') as f:vocab=eval(f.read())UNK=0unkli=[]char2idx = {u:i for i, u in enumerate(vocab)}idx2char = np.array(vocab)print('{')for char,_ in zip(char2idx, range(20)):print('  {:4s}: {:3d},'.format(repr(char), char2idx[char]))print('  ...\n}')# 设定每个输入句子长度的最大值seq_length = dic[mt][2]def split_input_target(chunk):input_text = chunk[:-1]target_text = chunk[1:]return input_text, target_textimport tensorflow as tfimport pickle# 假设 BATCH_SIZE 和 BUFFER_SIZE 已经定义好if 1:# 设定缓冲区大小,以重新排列数据集BUFFER_SIZE = 50000# 词集的长度vocab_size = len(vocab)# 嵌入的维度embedding_dim = dic[mt][0]#int(1024*2*1)# RNN 的单元数量rnn_units = dic[mt][1]#int(1024*4*2)window = dic[mt][2]# 加载保存的数据集def load_dataset(path):dataset = tf.data.experimental.load(path)return datasetif os.path.exists(r'E:\小思框架\论文\ganskchat\dataset'+ntype_+'_'+str(fwidx)):#换了batch后要重新处理数据集print('loading dataset')# 加载已经打乱过的数据集dataset = load_dataset(r'E:\小思框架\论文\ganskchat\dataset'+ntype_+'_'+str(fwidx))else:if big_file:text=[]with open(path_to_file, 'r',encoding='utf-8') as f:idxlen=0print('getting length\n')for _ in tqdm.tqdm(f):idxlen+=1st=idxlen//fen*fwidxed=idxlen//fen*(fwidx+1)with open(path_to_file, 'r',encoding='utf-8') as f:idx=0print('\n\nrunning data\n')for _ in tqdm.tqdm(f):if idx<st:continueif idx>=ed:breaktext.append(_)idx+=1text=''.join(text)else:text = open(path_to_file, 'r',encoding='utf-8').read()idxlen=len(text)st=idxlen//fen*fwidxed=idxlen//fen*(fwidx+1)text=text[st:ed]print('data size:',len(text))#text_as_int = np.array([char2idx[c] for c in text])text_as_int=[]cks=list(char2idx.keys())unk_li=set()len_=0for c in tqdm.tqdm(text):if c in cks:text_as_int.append(char2idx[c])else:c=cc.convert(c)if c in cks:#转为简体再次尝试text_as_int.append(char2idx[c])else:if not is_add:if not c in unk_li:with open('unk'+ntype_+'.txt','w',encoding='utf-8') as f:f.write(str(len_)+'\n'+str(list(unk_li)))#print('unknow:',repr(c))unk_li.add(c)len_+=1text_as_int.append(UNK)text_as_int=np.array(text_as_int)#————————————————————————————# 创建训练样本 / 目标char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)if 0:for i in char_dataset.take(5):print(i.numpy())print(idx2char[i.numpy()])sequences = char_dataset.batch(seq_length+1, drop_remainder=True)for item in sequences.take(5):print(repr(''.join(idx2char[(item.numpy())])))dataset = sequences.map(split_input_target)dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)tf.data.Dataset.save(dataset,'./dataset'+ntype_+'_'+str(fwidx) )

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 「豆包Marscode体验官」 | 云端 IDE 启动 Rust 体验
  • <数据集>木材缺陷检测数据集<目标检测>
  • 【所谓生活】马太效应
  • ABAP使用SQL直接更新数据库与使用IN UPDATE TASK的区别
  • 【ARMv8/v9 GIC- 700 系列 2 -- GIC-700 上电控制寄存器 GICR_PWRR】
  • 基于Gunicorn、Flask和Docker的高并发部署
  • 嵌入式物联网在医疗行业中的应用——案例分析
  • Go语言并发编程-Channel通信_2
  • Leetcode 383. 赎金信
  • 社交内容电商的进化与AI智能名片的新兴角色
  • 最新开源的PDF版面分析工具 PDF-Extract-Kit
  • HC05主从一体蓝牙模块的裸机使用——单片机<-->蓝牙模块
  • PostgreSQL使用(一)
  • python用selenium网页模拟时xpath无法定位元素解决方法3
  • C/C++ xml库
  • CentOS7简单部署NFS
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • java2019面试题北京
  • Java方法详解
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • Map集合、散列表、红黑树介绍
  • PHP 7 修改了什么呢 -- 2
  • python 学习笔记 - Queue Pipes,进程间通讯
  • SpiderData 2019年2月23日 DApp数据排行榜
  • 翻译--Thinking in React
  • 分布式熔断降级平台aegis
  • 服务器之间,相同帐号,实现免密钥登录
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 什么是Javascript函数节流?
  • 树莓派用上kodexplorer也能玩成私有网盘
  • 数据可视化之下发图实践
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​【数据结构与算法】冒泡排序:简单易懂的排序算法解析
  • ​【已解决】npm install​卡主不动的情况
  • ​卜东波研究员:高观点下的少儿计算思维
  • ​决定德拉瓦州地区版图的关键历史事件
  • #pragma once与条件编译
  • #预处理和函数的对比以及条件编译
  • (09)Hive——CTE 公共表达式
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (回溯) LeetCode 131. 分割回文串
  • (回溯) LeetCode 46. 全排列
  • (佳作)两轮平衡小车(原理图、PCB、程序源码、BOM等)
  • .net core使用EPPlus设置Excel的页眉和页脚
  • .NET 解决重复提交问题
  • .Net 垃圾回收机制原理(二)
  • .NET 中让 Task 支持带超时的异步等待
  • .NET/C#⾯试题汇总系列:⾯向对象
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...
  • .Net各种迷惑命名解释
  • .NET企业级应用架构设计系列之结尾篇
  • .NET未来路在何方?
  • .NET之C#编程:懒汉模式的终结,单例模式的正确打开方式
  • :O)修改linux硬件时间
  • @angular/cli项目构建--http(2)