作为一个一直使用tf1.x版本的人,为了继续使用google强大的开源技术,同时也为了适配高级的3090系列显卡,还是学习了一下tf2.x版本,并重构了一些nlp相关任务的代码,将其全部用tf2.0版本替换。
为了记录详细的nlp任务过程,本篇博文主要先记录一下从文本数据读取到处理成为batch数据输入到模型训练的一系列过程;这个过程可以主要分为tokenizer,embedding和data_generator这几个步骤。
一、Tokenizer
这个单词顾名思义就是将文本转换成token的表现形式,一个完整的句子是由很多个字词组成的,我们首先要将句子转成向量的形式,最简单的方法就是给每个字或者词对应唯一的id编号,这个id也就是token。
句子转换成tokens表示需要进行一下几个简单的步骤:
1.分词或分字
通常用jieba分词器就可以分词
2.词频和词性过滤
通过词频和词性过滤一些不需要的词或字
3.句子补齐padding
对于训练的数据需要输入定长序列,因此需要进行补齐和裁剪的操作
4.词/字索引表和标签索引表的构建
在处理完成所有的句子后,需要构建字/词和id对应的索引表并保存,方便模型训练玩后的预测过程的使用。
5.保存所有训练数据的tokens,方便后续更换模型的时候不需要再重新进行之前的处理步骤。
def word_to_index(self, all_words):
'''
生成词汇-索引字典
:param texts:
:return:
'''
#是否过滤低频词
if self.config['freq_filter']:
vocab = self.word_freq_filter(self.config['freq_filter'], all_words)
else:
vocab = self.get_vocab(all_words)
#设置词典大小
vocab = ["<PAD>", "<UNK>"] + vocab
self.vocab_size = self.config['vocab_size']
if len(vocab) < self.vocab_size:
self.vocab_size = len(vocab)
self.vocab = vocab[:self.vocab_size]
#构建词典索引
word_to_index = dict(zip(vocab, list(range(len(vocab)))))
return word_to_index
二、embedding
在完成每个字/词的tokenizer的步骤之后,需要将句子进行embedding的嵌入,也就是进行浅层语义特征的提取。比较常见的几种方法有,词频表示,tf-idf表示,one-hot词向量表示,word2vec词向量表示。下面的代码是gensim加载训练好的词向量的代码:
def load_word2vec_model(self):
'''
加载word2vec模型
:return:
'''
model_path = self.config.get('word2vec_path')
if not os.path.exists(model_path):
raise Exception("model_path did not exit, please check path")
model = gensim.models.KeyedVectors.load_word2vec_format(model_path, binary=False)
return model
三、data_generator
这一部分主要是生成训练数据,需要将数据划分成训练集和测试集,并以batch_size的形式输入到模型中进行训练,一般batch_size可设置成8,16,32,64等2的次方数,每次模型学习batch的数据,然后进行梯度下降更新模型参数,完成一次所有的batch数据的学习后称一个epoch。
def gen_data(self, inputs_idx, labels_idx):
'''
生成批次数据
:return:
'''
batch_token_ids, batch_output_ids = [], []
for i in range(len(inputs_idx)):
token_ids = inputs_idx[i]
target_ids = labels_idx[i]
batch_token_ids.append(token_ids)
batch_output_ids.extend(target_ids)
if len(batch_token_ids) == self.batch_size:
yield dict(
input_word_ids = np.array(batch_token_ids, dtype="int64"),
input_target_ids = np.array(batch_output_ids, dtype="float32")
)
batch_token_ids, batch_output_ids = [], []
文本数据的预处理是文本任务的第一步也是非常重要的一步,以上讲的都是比较常规的处理步骤,还有很多实际其他的预处理的方法也非常重要,例如样本数据不均衡,数据增强等提高数据质量的预处理手段。当然不是本系列博客的重点,如果有机会可以把这些内容也补全。
以上模块可以在https://github.com/dextroushands/nlp_tasks 的dataprocessor模块中找到相应的代码,都已经封装好,可以直接按任务完成数据的预处理。