1. 项目概述
最近在研究Transformer模型时,我决定从最基础的部分开始,手动实现一个简化版的Transformer语言模型。这个项目的目标是使用《鲁迅全集》作为训练数据,构建一个能够生成类似鲁迅风格文本的模型。虽然市面上已经有很多成熟的Transformer实现,但自己动手写一遍才能真正理解其中的原理和细节。
我选择鲁迅的作品有几个原因:首先,鲁迅的文字风格独特,具有很高的辨识度;其次,他的作品属于公共领域,可以自由使用;最后,中文文本处理本身就是一个有趣的挑战。这个项目不仅让我深入理解了Transformer的工作原理,也让我对中文自然语言处理有了更深刻的认识。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据处理与准备
2.1 数据加载与预处理
数据处理是任何NLP项目的第一步,也是至关重要的一步。我使用的数据是《鲁迅全集》的纯文本文件,编码格式为UTF-8。以下是数据加载的基本代码:
python复制file_name = "鲁迅全集.txt"
with open(file_name, 'r', encoding='utf-8') as f:
text = f.read()
这里有几个需要注意的地方:
- 必须明确指定编码为UTF-8,否则可能会遇到编码错误
- 对于大型文本文件,可以考虑分块读取
- 在实际项目中,应该检查文件是否存在,并处理可能的IO异常
2.2 字符级分词与编码
与常见的BPE或WordPiece分词不同,我选择了更简单的字符级分词。这种方法虽然不如子词分词高效,但对于理解Transformer的基本原理来说已经足够。
首先,我们需要构建字符到ID的映射:
python复制chars = sorted(list(set(text)))
vocab_size = len(chars)
stoi = {ch:i for i,ch in enumerate(chars)} # 字符到ID
itos = {i:ch for i,ch in enumerate(chars)} # ID到字符
# 编码解码函数
encode = lambda s: [stoi[c] for c in s]
decode = lambda l: ''.join([itos[i] for i in l])
这种简单的字符级处理有几个特点:
- 词表大小相对较小(中文常用字约3000-5000个)
- 不需要复杂的分词算法
- 生成的序列长度会比词级分词长很多
注意:在实际生产环境中,字符级分词通常效果不如子词分词,但对于学习目的和风格模仿任务来说已经足够。
2.3 数据集划分
为了评估模型性能,我们需要将数据划分为训练集和验证集。我采用了简单的90%-10%划分:
python复制data = torch.tensor(encode(text), dtype=torch.long)
n = int(0.9 * len(data))
train_data = data[:n]
val_data = data[n:]
更专业的做法是使用PyTorch的Dataset和DataLoader,这能提供更好的数据管理和批处理功能。下面是一个更完整的实现:
python复制from torch.utils.data import Dataset, DataLoader
from sklearn.model_selection import train_test_split
# 使用train_test_split划分数据
data = torch.tensor(encode(text), dtype=torch.long)
train_data, val_data = train_test_split(
data,
test_size=0.1,
random_state=42,
shuffle=False
)
class TextDataset(Dataset):
def __init__(self, data, block_size):
self.data = data
self.block_size = block_size
self.length = len(data) - block_size
def __len__(self):
return self.length
