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

深度学习subword分词BPE

首先要安装:pip install subword-nmt

'subword-nmt'是一种用于处理自然语言处理任务中的词汇外问题的工具,支持几种不同的算法

一、介绍

BPE:这是一种流行分词算法,可以有效的平衡词汇表大小和步数,分词采用共现性。

步骤:

1.准备足够大的训练语料

2.确定期望的subword词表大小(超参)

3.将单词拆分为字符序列并在末尾添加后缀"</w>",这样就可以统计单词频率。 比如一开始有一个l字母,现在编程l</w>5,就说明出现了五次。停止符</w>的意义在于表示subword是词后缀。每次合并后词表可能出现三种变化:

+1:表明加入合并后的新字词,同时原来在的2个子词还保留;

0:如果一个字词不是单独出现的,就被消解;

-1:表明加入合并后的新词,同时原来2个字词都被消解。实际上,随着合并次数增加,词表大小通常先增加后减小。

4.统计每一个连续字节对出现的频率,选择最高频的配对比如lo</w>5

5.重复

“low” 和 “lower” 合并后,可能生成子词 “low” 和 “er”

二、代码

导包:

随机种子:

  1. 确保实验可复现性
  2. 控制以下随机过程的一致性:
    • 神经网络权重初始化
    • 数据加载顺序
    • 数据增强中的随机变换
    • Dropout等随机操作
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import sklearn
import pandas as pd
import os
import sys
import time
from tqdm.auto import tqdm
import torch
import torch.nn as nn
import torch.nn.functional as F

print(sys.version_info)
for module in mpl, np, pd, sklearn, torch:
    print(module.__name__, module.__version__)
    
device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")
print(device)

seed = 42
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)

准备数据:

cleaned_df = pd.read_csv("imdb_processed.csv")
print(cleaned_df.shape) # (50000, 2), 50000条评论, 2列

# 随机打乱数据,取训练集和测试集
np.random.seed(seed) #随机
cleaned_df = cleaned_df.sample(frac=1).reset_index(drop=True)#打乱,frac=1表示全部打乱(frac是比例,reset_index(drop=True)是重新索引
with open("imdb_train.txt", "w", encoding="utf8") as file:# 保存训练集
    for line in cleaned_df.processed.values[:25000]:#只保存了processed列,即评论文本,没有保存label列
        file.write(line.lower() + "\n") #变为小写,token数量少一些

with open("imdb_test.txt", "w", encoding="utf8") as file:# 保存测试集
    for line in cleaned_df.processed.values[25000:]:
        file.write(line.lower() + "\n")
  • np.random.seed(seed):固定随机种子,确保每次打乱顺序一致(可复现性)
  • sample(frac=1):按100%比例随机采样(即打乱数据顺序)
  • reset_index(drop=True):重置索引并丢弃旧索引
# 学习bpe分词(很慢,学一次就好)
# -i 选择学习的文件
# -o 核心输出文件,分词需要用到imdb_bpe_code,生成的 imdb_bpe_code 文件包含了学习到的 BPE 操作规则。这些规则用于将单词分割成子词单元
# --write-vocabulary 字典输出文件,imdb_bpe_vocab 文件包含了根据 BPE 规则生成的词汇表,列出了所有子词单元及其频率
# -s 词表大小
!subword-nmt learn-joint-bpe-and-vocab -i ./imdb_train.txt -o ./imdb_bpe_code --write-vocabulary ./imdb_bpe_vocab -s 8000

 使用 subword-nmt 工具包进行 BPE(Byte Pair Encoding)子词分词训练

# 应用bpe分词,-c 指定 BPE 编码的配置文件,就是上面生成的 imdb_bpe_code 文件
!subword-nmt apply-bpe -c ./imdb_bpe_code -i ./imdb_train.txt -o ./imdb_train_bpe.txt
!subword-nmt apply-bpe -c ./imdb_bpe_code -i ./imdb_test.txt -o ./imdb_test_bpe.txt
#-c ./imdb_bpe_code   # 指定 BPE 合并规则配置文件(学习阶段生成)
#-i ./imdb_train.txt  # 输入:原始训练集文本
#-o ./imdb_train_bpe.txt  # 输出:子词化后的训练文本
工作流程
  1. 加载规则:读取 imdb_bpe_code 中的合并优先级(如 e s → es
  2. 贪婪匹配:对每个单词从长到短尝试应用已有合并规则
  3. 添加分隔符:通常用 @@ 标记子词边界(可配置)

 

from torch.utils.data import Dataset, DataLoader

# 随后加载数据集就从bpe分词的文件里加载
class IMDBDataset(Dataset):
    def __init__(self, mode="train"):
        df = pd.read_csv("imdb_subwords.csv").query("split == '{}'".format(mode)) # 加载训练集或测试集,query语句筛选
        self.texts = df["subwords10k"].values # 评论文本
        self.labels = df["label"].values # 评论标签
    
    def __len__(self):
        return len(self.labels)
    
    def __getitem__(self, idx):
        return self.texts[idx].split(), self.labels[idx] # 返回文本和标签
    
    
train_ds = IMDBDataset("train")
test_ds = IMDBDataset("test")

 df = pd.read_csv("imdb_subwords.csv").query("split == '{}'".format(mode))详解:

  1. pd.read_csv("imdb_subwords.csv")

    • 这是Pandas库读取CSV文件的标准方法
    • 会返回一个DataFrame对象
    • 文件"imdb_subwords.csv"应位于当前工作目录或指定路径下
  1. .query("split == '{}'".format(mode))

    • 使用DataFrame的query()方法进行数据过滤
    • split是DataFrame中的一个列名(应该表示数据集划分类型)
    • '{}'是字符串格式化占位符,会被mode变量的值替换
    • 例如:当mode = "train"时,实际执行的查询条件是split == 'train'
  • 文本:BPE分词字符串(如"I love this movie")→ 通过.split()转为列表(["I", "love", ...]

 构造字典:

#载入词表,看下词表长度,词表就像英语字典
word2idx = {
    "[PAD]": 0,     # 填充 token
    "[BOS]": 1,     # begin of sentence
    "[UNK]": 2,     # 未知 token
    "[EOS]": 3,     # end of sentence
}
idx2word = {value: key for key, value in word2idx.items()}
print(idx2word)
index = len(idx2word) # 词表长度,现在为4

#%%
threshold = 1  # 出现次数低于此的token舍弃
with open("imdb_bpe_vocab", "r", encoding="utf8") as file:
    for line in tqdm(file.readlines()):
        token, counts = line.strip().split()#左右空格去掉
        if int(counts) >= threshold: #词频大于等于1就要
            word2idx[token] = index # 加入词表
            idx2word[index] = token # 加入反向词典
            index += 1

vocab_size = len(word2idx)
print("vocab_size: {}".format(vocab_size))
#%%
# 选择 max_length
length_collect = {}
for text, label in train_ds:
    length = len(text)
    length_collect[length] = length_collect.get(length, 0) + 1
    
MAX_LENGTH = 500
plt.bar(length_collect.keys(), length_collect.values())
plt.axvline(MAX_LENGTH, label="max length", c="gray", ls=":")
plt.legend()
plt.show()

相关文章:

  • 在 DEM 中模拟粒子破损
  • 领域驱动设计(DDD)是什么?
  • 深度剖析Redis:双写一致性问题及解决方案全景解析
  • Vue3实战学习(Vue3集成Vue-Router(路由跳转、编程式路由跳转。路由跳转的单参数、多参数传递。设置默认页面路由))(上)(7)
  • 位运算的应用3(获取⼆进制中的指定位、leetcode 190.颠倒二进制位)
  • Word 小黑第15套
  • Linux_16进程地址空间
  • 音视频软件工程师面试题
  • JDK安装过程中误删path怎么办?
  • 开发、科研、日常办公工具汇总(自用,持续更新)
  • 一个基于LSTM的字符级文本生成模型的训练+使用(pytorch)
  • 【threejs实战教程一】初识Three.js,场景Scene、相机Camera、渲染器Renderer
  • mysql索引机制深度剖析
  • SVT-AV1源码分析函数 svt_av1_optimize_b
  • react中字段响应式
  • 简述你对 Spring MVC 的理解
  • GRU门控循环单元
  • android用java设置button之间的间距 笔记250311
  • 高效微调算法 (Parameter-Efficient Fine-tuning, PEFT) 详解
  • 深度学习与大模型-张量
  • 外交部:习近平主席同普京总统达成许多新的重要共识
  • 开局良好,我国第一季度广告业务收入保持较快增速
  • “一嗨租车”陷“五年后扣费”疑云,用户:违章处理莫名消失
  • “上海之帆”巡展在日本大阪开幕,松江区组织企业集体出展
  • 深入贯彻中央八项规定精神学习教育中央第六指导组指导督导中国工商银行见面会召开
  • 公元1057年:千年龙虎榜到底有多厉害?