2.1 数据处理
基于BERT模型的数据处理¶
学习目标¶
- 掌握BERT模型的相关细节.
- 掌握数据处理的工具函数代码实现.
投满分项目数据预处理¶
- 本项目中对数据部分的预处理步骤如下:
- 第一步: 查看项目数据集
- 第二步: 查看预训练模型相关数据
- 第三步: 编写工具类函数
第一步: 查看项目数据集¶
- 数据集的路径: /home/ec2-user/toutiao/data/data/
-
项目的数据集包括5个文件, 依次来看一下:
-
标签文件/home/ec2-user/toutiao/data/data/class.txt
finance
realty
stocks
education
science
society
politics
sports
game
entertainment
- class.txt中包含10个类别标签, 每行一个标签, 为英文单词的展示格式.
- 训练数据集/home/ec2-user/toutiao/data/data/train.txt
中华女子学院:本科层次仅1专业招男生 3
两天价网站背后重重迷雾:做个网站究竟要多少钱 4
东5环海棠公社230-290平2居准现房98折优惠 1
卡佩罗:告诉你德国脚生猛的原因 不希望英德战踢点球 7
82岁老太为学生做饭扫地44年获授港大荣誉院士 5
记者回访地震中可乐男孩:将受邀赴美国参观 5
冯德伦徐若�隔空传情 默认其是女友 9
传郭晶晶欲落户香港战伦敦奥运 装修别墅当婚房 1
《赤壁OL》攻城战诸侯战硝烟又起 8
“手机钱包”亮相科博会 4
- train.txt中包含180000行样本, 每行包括两列, 第一列为待分类的中文文本, 第二列是数字化标签, 中间用\t作为分隔符.
- 验证数据集/home/ec2-user/toutiao/data/data/dev.txt
体验2D巅峰 倚天屠龙记十大创新概览 8
60年铁树开花形状似玉米芯(组图) 5
同步A股首秀:港股缩量回调 2
中青宝sg现场抓拍 兔子舞热辣表演 8
锌价难续去年辉煌 0
2岁男童爬窗台不慎7楼坠下获救(图) 5
布拉特:放球员一条生路吧 FIFA能消化俱乐部的攻击 7
金科西府 名墅天成 1
状元心经:考前一周重点是回顾和整理 3
发改委治理涉企收费每年为企业减负超百亿 6
- dev.txt中包含10000行样本, 每行包括两列, 第一列为待分类的中文文本, 第二列是数字化标签, 中 间用\t作为分隔符.
- 测试数据集/home/ec2-user/toutiao/data/data/test.txt
词汇阅读是关键 08年考研暑期英语复习全指南 3
中国人民公安大学2012年硕士研究生目录及书目 3
日本地震:金吉列关注在日学子系列报道 3
名师辅导:2012考研英语虚拟语气三种用法 3
自考经验谈:自考生毕业论文选题技巧 3
本科未录取还有这些路可以走 3
2009年成人高考招生统一考试时间表 3
去新西兰体验舌尖上的饕餮之旅(组图) 3
四级阅读与考研阅读比较分析与应试策略 3
备考2012高考作文必读美文50篇(一) 3
- test.txt中包含10000行样本, 每行包括两列, 第一列为待分类的中文文本, 第二列是数字化标签, 中 间用\t作为分隔符.
- 词典文件/home/ec2-user/toutiao/data/data/vocab.pkl为不可读文件, 训练模型中使用.
第二步: 查看预训练模型相关数据¶
- 预训练模型相关数据的文件夹路径为/home/ec2-user/toutiao/data/bert_pretrain/
-
预训练模型相关数据共包含3个文件:
-
BERT模型的超参数配置文件/home/ec2-user/toutiao/data/bert_pretrain/bert_config.json
{
"attention_probs_dropout_prob": 0.1,
"directionality": "bidi",
"hidden_act": "gelu",
"hidden_dropout_prob": 0.1,
"hidden_size": 768,
"initializer_range": 0.02,
"intermediate_size": 3072,
"max_position_embeddings": 512,
"num_attention_heads": 12,
"num_hidden_layers": 12,
"pooler_fc_size": 768,
"pooler_num_attention_heads": 12,
"pooler_num_fc_layers": 3,
"pooler_size_per_head": 128,
"pooler_type": "first_token_transform",
"type_vocab_size": 2,
"vocab_size": 21128
}
- BERT预训练模型文件/home/ec2-user/toutiao/data/bert_pretrain/pytorch_model.bin
-rw-r--r-- 1 root root 411578458 1月 9 11:50 pytorch_model.bin
- BERT预训练模型词典文件/home/ec2-user/toutiao/data/bert_pretrain/vocab.txt
[PAD]
[unused1]
[unused2]
[unused3]
[unused4]
[unused5]
[unused6]
[unused7]
[unused8]
[unused9]
[unused10]
......
......
......
[unused98]
[unused99]
[UNK]
[CLS]
[SEP]
[MASK]
<S>
<T>
!
......
......
......
第三步: 编写工具类函数¶
- 工具类函数的路径为/home/ec2-user/toutiao/src/utils.py
- 第一个工具类函数build_vocab(), 位于utils.py中的独立函数.
def build_vocab(file_path, tokenizer, max_size, min_freq):
vocab_dic = {}
with open(file_path, "r", encoding="UTF-8") as f:
for line in tqdm(f):
lin = line.strip()
if not lin:
continue
content = lin.split("\t")[0]
for word in tokenizer(content):
vocab_dic[word] = vocab_dic.get(word, 0) + 1
vocab_list = sorted(
[_ for _ in vocab_dic.items() if _[1]>=min_freq],key=lambda x:x[1],reverse=True)[:max_size]
vocab_dic = {word_count[0]: idx for idx, word_count in enumerate(vocab_list)}
vocab_dic.update({UNK: len(vocab_dic), PAD: len(vocab_dic) + 1})
return vocab_dic
- 第二个工具类函数build_dataset(), 位于utils.py中的独立函数.
def build_dataset(config):
def load_dataset(path, pad_size=32):
contents = []
with open(path, "r", encoding="UTF-8") as f:
for line in tqdm(f):
line = line.strip()
if not line:
continue
content, label = line.split("\t")
token = config.tokenizer.tokenize(content)
token = [CLS] + token
seq_len = len(token)
mask = []
token_ids = config.tokenizer.convert_tokens_to_ids(token)
if pad_size:
if len(token) < pad_size:
mask = [1] * len(token_ids) + [0] * (pad_size - len(token))
token_ids += [0] * (pad_size - len(token))
else:
mask = [1] * pad_size
token_ids = token_ids[:pad_size]
seq_len = pad_size
contents.append((token_ids, int(label), seq_len, mask))
return contents
train = load_dataset(config.train_path, config.pad_size)
dev = load_dataset(config.dev_path, config.pad_size)
test = load_dataset(config.test_path, config.pad_size)
return train, dev, test
- 第三个工具函数build_iterator(), 包括数据迭代器的类class DatasetIterater(), 位于utils.py中的独立函数和类.
class DatasetIterater(object):
def __init__(self, batches, batch_size, device, model_name):
self.batch_size = batch_size
self.batches = batches
self.model_name = model_name
self.n_batches = len(batches) // batch_size
self.residue = False # 记录batch数量是否为整数
if len(batches) % self.n_batches != 0:
self.residue = True
self.index = 0
self.device = device
def _to_tensor(self, datas):
x = torch.LongTensor([_[0] for _ in datas]).to(self.device)
y = torch.LongTensor([_[1] for _ in datas]).to(self.device)
# pad前的长度(超过pad_size的设为pad_size)
seq_len = torch.LongTensor([_[2] for _ in datas]).to(self.device)
if self.model_name == "bert" or self.model_name == "multi_task_bert":
mask = torch.LongTensor([_[3] for _ in datas]).to(self.device)
return (x, seq_len, mask), y
def __next__(self):
if self.residue and self.index == self.n_batches:
batches = self.batches[self.index * self.batch_size : len(self.batches)]
self.index += 1
batches = self._to_tensor(batches)
return batches
elif self.index >= self.n_batches:
self.index = 0
raise StopIteration
else:
batches = self.batches[self.index * self.batch_size : (self.index + 1) * self.batch_size]
self.index += 1
batches = self._to_tensor(batches)
return batches
def __iter__(self):
return self
def __len__(self):
if self.residue:
return self.n_batches + 1
else:
return self.n_batches
def build_iterator(dataset, config):
iter = DatasetIterater(dataset, config.batch_size, config.device, config.model_name)
return iter
- 第四个工具类函数get_time_dif(), 位于utils.py中的独立函数.
def get_time_dif(start_time):
# 获取已使用时间
end_time = time.time()
time_dif = end_time - start_time
return timedelta(seconds=int(round(time_dif)))
小节总结¶
- 本小节讲解了BERT模型的相关技术细节, 并实现了若干工具函数, 这些工具函数会在未来的项目代码中应用.