最近特别火的DeepSeek,是一个大语言模型,那一个模型是如何构建起来的呢?DeepSeek基于Transformer架构,接下来我们也从零开始构建一个基于Transformer架构的小型语言模型,并说明构建的详细步骤及内部组件说明。我们以构建一个字符级语言模型(Char-Level LM)为例,目标是通过训练模型预测序列中的下一个字符。
全文采用的python语言。
想了解个人windows电脑上安装DeepSeek大模型,看我的文章:个人windows电脑上安装DeepSeek大模型(完整详细可用教程)_deepseek-r1-distill-qwen-1.5b-gguf-CSDN博客
本文的前置基础,Windows安装Hugging Face Transformers库,看我的文章:Windows安装Hugging Face Transformers库并实现案例训练的详细教程-CSDN博客
一、整体流程概览
听说了太多的大模型,那么大模型是如何一步一步建立起来的呢?我们接下来就从一个小的模型开始,逐步分解,让大家知道其中的逻辑、构成等关键内容。从基础开始,逐步实现,包括数据准备、模型架构、训练和评估。
首先,确定模型的目标。小语言模型通常用于生成文本或理解语言结构。我们先构建一个简单的能够处理简单任务的模型,比如字符级或单词级的生成。接下来,数据准备是关键,需要选择合适的语料库,并进行预处理,比如分词、构建词汇表等。
然后,是模型架构。使用Transformer的话,可能需要简化标准的Transformer结构,比如减少层数、注意力头的数量,或者使用更小的嵌入维度。需要解释每个组件的功能,比如嵌入层、位置编码、自注意力机制、前馈网络等。
接下来,是训练过程。要说明如何设置损失函数(比如交叉熵)、优化器(如Adam)、学习率调度,以及如何处理过拟合(如Dropout、权重衰减)。掌握训练循环的步骤,包括前向传播、损失计算、反向传播和参数更新。
评估部分需要包括训练损失、验证损失的计算,以及生成样本检查模型效果。可能还需要困惑度作为评估指标。
大家可能还会关心实际代码的实现,所以可能需要简要提及框架如PyTorch,所以给出一个简单的模型结构代码示例可能对大家有帮助。
另外,为了能让大家实现这个小模型,所以尽量缩小模型参数,比如更少的层数、更小的隐藏层维度。以及讨论训练资源,比如在CPU或单GPU上训练,时间预估等。也会存在一些挑战,包括数据不足、过拟合、训练时间过长。后期我尽量会给出一些解决方案,比如数据增强、正则化技术、提前停止等。
所以综合,本文将从以下步骤实现一个小模型:
1.数据准备 → 2.模型架构设计 → 3.训练 → 4.评估与生成
每一步大概包含的操作有:
1. 数据准备:加载数据,创建字符映射,编码文本,生成训练样本。
2. 模型定义:嵌入层、位置编码、自注意力、FFN、解码器层、输出层。
3. 训练循环:超参数设置,损失函数,优化器,迭代训练。
4. 生成函数:使用模型生成文本,监控损失。
5. 示例运行:使用简单数据训练模型并生成结果。
二、详细步骤与组件说明
1. 数据准备
目标:将原始文本转化为模型可处理的数值化序列。
以下是数据准备阶段的流程:
图1 数据准备阶段的流程图
1.1 语料库选择
选择小型文本数据集(如莎士比亚诗集、新闻标题等),避免过大的计算负担。
示例数据:"hello world\nhow are you?"
1.2 字符级预处理
(1)字符映射表(Vocabulary):
对语料库的原始文本字符串,提取所有唯一字符(如26字母、标点、空格等),构建字符到索引的映射表。预处理操作过程如下:
[输入] 原始文本字符串
[操作]
1. 提取所有唯一字符(包括空格、换行符)
2. 创建字符→索引字典
[输出] vocab = {'h':0, 'e':1, ...}
以下就是语料库示例的映射表(用python语言的字典表示):
vocab = {'h':0, 'e':1, 'l':2, 'o':3, ' ':4, 'w':5, 'r':6, 'd':7, '\n':8, ...}
字符映射表的示意图如下:
图2 字符映射表的示意图
(2)文本转序列:
将原始文本按字符转换为整数序列(索引值)。
示例输入:"hello" → [0, 1, 2, 2, 3]
原始文本转换为序列的形成过程如下图:
图3 原始文本转换为序列的形成过程
1.3 构建训练样本
(1)滑动窗口分割:
将原始语料库中的内容变成训练样本,就要先确定自变量和因变量,构建一个自变量与因变量之间的函数关系,并在训练过程中不断调整函数,使得这个函数能够表达当前文本序列之间的关系。切割操作过程如下:
[输入] 长整数序列(假如长度=1000)
[操作]
1. 滑动窗口分割(窗口大小=block_size+1)
2. 切分为X(前block_size项)和Y(后block_size项)
[示例]
原始序列: [0,1,2,3,4]