+-
深度学习之Bert中文分类学习
首页 专栏 tensorflow 文章详情
0

深度学习之Bert中文分类学习

小鸡 发布于 5 月 10 日

深度学习之Bert中文分类学习

BERT实验

预训练结果分析

tfhub_handle_preprocess = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_preprocess/3" bert_preprocess_model = hub.KerasLayer(tfhub_handle_preprocess) text_test = ['我真是个天才啊!!'] text_preprocessed = bert_preprocess_model(text_test) print(f'Keys : {list(text_preprocessed.keys())}') print(f'Shape : {text_preprocessed["input_word_ids"].shape}') print(f'Word Ids : {text_preprocessed["input_word_ids"][0, :12]}') print(f'Input Mask : {text_preprocessed["input_mask"][0, :12]}') print(f'Type Ids : {text_preprocessed["input_type_ids"][0, :12]}')

打印结果

Keys : ['input_mask', 'input_type_ids', 'input_word_ids'] Shape : (1, 128) Word Ids : [ 101 2769 4696 3221 702 1921 2798 1557 8013 8013 102 0] Input Mask : [1 1 1 1 1 1 1 1 1 1 1 0] Type Ids : [0 0 0 0 0 0 0 0 0 0 0 0]

Shape中的128猜想应该是 最大长度。

Keys对应了三个属性,但其实bert应该是有7个特征属性。为什么另外四个属性在这里没有,目前不是很清楚,但我觉得Hub里面的Advance topics估计是在讲这个事情。但由于现在要做的任务是文本分类,以下的四个特征是不需要的。

input_ids: 输入的token对应的id input_mask: 输入的mask,1代表是正常输入,0代表的是padding的输入 segment_ids: 输入的0:代表句子A或者padding句子,1代表句子B masked_lm_positions:我们mask的token的位置 masked_lm_ids:我们mask的token的对应id masked_lm_weights:我们mask的token的权重,1代表是真实mask的,0代表的是padding的mask next_sentence_labels:句子A和B是否是上下句

接下来看下输出怎么理解。为了保证自己的猜想正确,我又丢了一句真是个天才啊!!进去看结果。把两个结果合并起来是这样的。

Word Ids : [ 101 2769 4696 3221 702 1921 2798 1557 8013 8013 102 0] Word Ids : [ 101 4696 3221 702 1921 2798 1557 8013 8013 102 0 0] Input Mask : [1 1 1 1 1 1 1 1 1 1 1 0] Input Mask : [1 1 1 1 1 1 1 1 1 1 0 0] Type Ids : [0 0 0 0 0 0 0 0 0 0 0 0] Type Ids : [0 0 0 0 0 0 0 0 0 0 0 0]

有意思吧。bert目前没有分词组的概念,它是一个一个词分开的。根据资料查看bert的预训练分成两步,这个显然是第一步。

我真是个天才啊啊!被分割成了[CLS] 我 真 是 个 天 才 啊 啊 ![SEP],所以这个跟Word Ids是能对得上的。
TypeIds应该跟segment_ids是对等的,也是对得上的,都是0。
Input Mask也是对得上的。它的算法如下
mask是1表示是"真正"的Token,0则是Padding出来的。
在后面的Attention时会通过tricky的技巧让模型不能attend to这些padding出来的Token上。
input_mask = [1] * len(input_ids)

结果输出分析

tfhub_handle_encoder = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_L-12_H-768_A-12/4" bert_model = hub.KerasLayer(tfhub_handle_encoder) bert_results = bert_model(text_preprocessed) print(f'Keys : {list(bert_results.keys())}') print(f'Pooled Outputs Shape:{bert_results["pooled_output"].shape}') print(f'Pooled Outputs Values:{bert_results["pooled_output"][0, :12]}') print(f'Sequence Outputs Shape:{bert_results["sequence_output"].shape}') print(f'Sequence Outputs Values:{bert_results["sequence_output"][0, :12]}') sequence_output:维度【batch_size, seq_length, hidden_size】,这是训练后每个token的词向量。 pooled_output:维度是【batch_size, hidden_size】,每个sequence第一个位置CLS的向量输出,用于分类任务。

BERT实战中文多分类

参考官方的BERT分类,官方用的是情感分析的场景,其实跟中文多分类场景类似。
要做的任务就是换BERT的pre-process和encoder,再把最后一层输出换成多分类即可。

基本可以照搬。

环境准备

pip install -q tensorflow-text pip install -q tf-models-official import tensorflow as tf import os import shutil import tensorflow as tf import tensorflow_hub as hub import tensorflow_text as text from official.nlp import optimization # to create AdamW optimizer import matplotlib.pyplot as plt tfhub_handle_preprocess = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_preprocess/3" tfhub_handle_encoder = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_L-12_H-768_A-12/4" bert_preprocess_model = hub.KerasLayer(tfhub_handle_preprocess) bert_model = hub.KerasLayer(tfhub_handle_encoder)

准备数据集

因为是在Colab做的实验,上传貌似只能上传文件的形式。所以在本地压缩成一个zip包,上传到Colab后做解压。

import zipfile from pathlib import Path zFile = zipfile.ZipFile("path.zip","r") for fileM in zFile.namelist(): zFile.extract(fileM, "path") zFile.close();

文件格式的要求跟官方示例一致即可。

文件目录 分类1 分类1的标题1 分类1的标题2 ... 分类2 分类3 ...

拆分数据集

AUTOTUNE = tf.data.AUTOTUNE batch_size = 32 seed = 42 raw_train_ds = tf.keras.preprocessing.text_dataset_from_directory( 'train', batch_size=batch_size, validation_split=0.2, subset='training', seed=seed) class_names = raw_train_ds.class_names print(class_names) train_ds = raw_train_ds.cache().prefetch(buffer_size=AUTOTUNE) val_ds = tf.keras.preprocessing.text_dataset_from_directory( 'train', batch_size=batch_size, validation_split=0.2, subset='validation', seed=seed) val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

看一下数据集

for text_batch, label_batch in train_ds.take(1): print(text_batch) for i in range(3): print(f'Review: {text_batch.numpy()[i]}') label = label_batch.numpy()[i] print(f'Label : {label} ({class_names[label]})')

定义模型

def build_classifier_model(): text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text') preprocessing_layer = hub.KerasLayer(tfhub_handle_preprocess, name='preprocessing') encoder_inputs = preprocessing_layer(text_input) encoder = hub.KerasLayer(tfhub_handle_encoder, trainable=True, name='BERT_encoder') outputs = encoder(encoder_inputs) net = outputs['pooled_output'] net = tf.keras.layers.Dropout(0.5)(net) net = tf.keras.layers.Dense(6, activation='softmax', name='classifier')(net) return tf.keras.Model(text_input, net) classifier_model = build_classifier_model() epochs = 10 steps_per_epoch = tf.data.experimental.cardinality(train_ds).numpy() num_train_steps = steps_per_epoch * epochs print(num_train_steps) num_warmup_steps = int(0.1*num_train_steps) init_lr = 3e-5 optimizer = optimization.create_optimizer(init_lr=init_lr, num_train_steps=num_train_steps, num_warmup_steps=num_warmup_steps, optimizer_type='adamw') loss = tf.keras.losses.SparseCategoricalCrossentropy() metrics = tf.metrics.SparseCategoricalAccuracy() classifier_model.compile(optimizer=optimizer, loss=loss, metrics=metrics)

训练模型

history = classifier_model.fit(x=train_ds, validation_data=val_ds, epochs=epochs)

预测数据

发现虽然我只用了300条样本,train样本240条,val样本60条,但是准确率也能到90%。BERT果然叼。

import numpy as np def print_my_examples(inputs, results): result_for_printing = \ [f'input: {inputs[i]:<30} : class: {class_names[np.argmax(results[i])] }' for i in range(len(inputs))] print(*result_for_printing, sep='\n') print() examples = ['德国米技(MIJI)蒸汽喷淋式煮茶壶 全自动保温泡茶壶HK-K018', '公牛插座收纳盒装办公接线板家用创意电源', '生活元素烧水壶办公室家用多功能小型烧水壶保温一体煮茶器煮茶壶', '信安智囊老人防摔防跌倒术后保护重复使用智能气囊马甲服神器送礼', '七度空间卫生巾优雅系列日夜超值组合', ' 【贝拉家】贝拉爸爸自己家工厂生产的科技布狗窝'] example_result = classifier_model(tf.constant(examples)) print_my_examples(examples,example_result)

附录

官方示例BERT分类代码 Tensorflow hub Bert中文模型处理 知乎Bert详解 腾讯云 Bert详解 Bert源码解读
tensorflow
阅读 45 更新于 5 月 10 日
收藏
分享
本作品系原创, 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
大数据学习笔记
记录自己对大数据学习的点滴
关注专栏
avatar
小鸡

1.01的365次方=37.8

206 声望
18 粉丝
关注作者
0 条评论
得票数 最新
提交评论
你知道吗?

注册登录
avatar
小鸡

1.01的365次方=37.8

206 声望
18 粉丝
关注作者
宣传栏
目录

深度学习之Bert中文分类学习

BERT实验

预训练结果分析

tfhub_handle_preprocess = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_preprocess/3" bert_preprocess_model = hub.KerasLayer(tfhub_handle_preprocess) text_test = ['我真是个天才啊!!'] text_preprocessed = bert_preprocess_model(text_test) print(f'Keys : {list(text_preprocessed.keys())}') print(f'Shape : {text_preprocessed["input_word_ids"].shape}') print(f'Word Ids : {text_preprocessed["input_word_ids"][0, :12]}') print(f'Input Mask : {text_preprocessed["input_mask"][0, :12]}') print(f'Type Ids : {text_preprocessed["input_type_ids"][0, :12]}')

打印结果

Keys : ['input_mask', 'input_type_ids', 'input_word_ids'] Shape : (1, 128) Word Ids : [ 101 2769 4696 3221 702 1921 2798 1557 8013 8013 102 0] Input Mask : [1 1 1 1 1 1 1 1 1 1 1 0] Type Ids : [0 0 0 0 0 0 0 0 0 0 0 0]

Shape中的128猜想应该是 最大长度。

Keys对应了三个属性,但其实bert应该是有7个特征属性。为什么另外四个属性在这里没有,目前不是很清楚,但我觉得Hub里面的Advance topics估计是在讲这个事情。但由于现在要做的任务是文本分类,以下的四个特征是不需要的。

input_ids: 输入的token对应的id input_mask: 输入的mask,1代表是正常输入,0代表的是padding的输入 segment_ids: 输入的0:代表句子A或者padding句子,1代表句子B masked_lm_positions:我们mask的token的位置 masked_lm_ids:我们mask的token的对应id masked_lm_weights:我们mask的token的权重,1代表是真实mask的,0代表的是padding的mask next_sentence_labels:句子A和B是否是上下句

接下来看下输出怎么理解。为了保证自己的猜想正确,我又丢了一句真是个天才啊!!进去看结果。把两个结果合并起来是这样的。

Word Ids : [ 101 2769 4696 3221 702 1921 2798 1557 8013 8013 102 0] Word Ids : [ 101 4696 3221 702 1921 2798 1557 8013 8013 102 0 0] Input Mask : [1 1 1 1 1 1 1 1 1 1 1 0] Input Mask : [1 1 1 1 1 1 1 1 1 1 0 0] Type Ids : [0 0 0 0 0 0 0 0 0 0 0 0] Type Ids : [0 0 0 0 0 0 0 0 0 0 0 0]

有意思吧。bert目前没有分词组的概念,它是一个一个词分开的。根据资料查看bert的预训练分成两步,这个显然是第一步。

我真是个天才啊啊!被分割成了[CLS] 我 真 是 个 天 才 啊 啊 ![SEP],所以这个跟Word Ids是能对得上的。
TypeIds应该跟segment_ids是对等的,也是对得上的,都是0。
Input Mask也是对得上的。它的算法如下
mask是1表示是"真正"的Token,0则是Padding出来的。
在后面的Attention时会通过tricky的技巧让模型不能attend to这些padding出来的Token上。
input_mask = [1] * len(input_ids)

结果输出分析

tfhub_handle_encoder = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_L-12_H-768_A-12/4" bert_model = hub.KerasLayer(tfhub_handle_encoder) bert_results = bert_model(text_preprocessed) print(f'Keys : {list(bert_results.keys())}') print(f'Pooled Outputs Shape:{bert_results["pooled_output"].shape}') print(f'Pooled Outputs Values:{bert_results["pooled_output"][0, :12]}') print(f'Sequence Outputs Shape:{bert_results["sequence_output"].shape}') print(f'Sequence Outputs Values:{bert_results["sequence_output"][0, :12]}') sequence_output:维度【batch_size, seq_length, hidden_size】,这是训练后每个token的词向量。 pooled_output:维度是【batch_size, hidden_size】,每个sequence第一个位置CLS的向量输出,用于分类任务。

BERT实战中文多分类

参考官方的BERT分类,官方用的是情感分析的场景,其实跟中文多分类场景类似。
要做的任务就是换BERT的pre-process和encoder,再把最后一层输出换成多分类即可。

基本可以照搬。

环境准备

pip install -q tensorflow-text pip install -q tf-models-official import tensorflow as tf import os import shutil import tensorflow as tf import tensorflow_hub as hub import tensorflow_text as text from official.nlp import optimization # to create AdamW optimizer import matplotlib.pyplot as plt tfhub_handle_preprocess = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_preprocess/3" tfhub_handle_encoder = "https://hub.tensorflow.google.cn/tensorflow/bert_zh_L-12_H-768_A-12/4" bert_preprocess_model = hub.KerasLayer(tfhub_handle_preprocess) bert_model = hub.KerasLayer(tfhub_handle_encoder)

准备数据集

因为是在Colab做的实验,上传貌似只能上传文件的形式。所以在本地压缩成一个zip包,上传到Colab后做解压。

import zipfile from pathlib import Path zFile = zipfile.ZipFile("path.zip","r") for fileM in zFile.namelist(): zFile.extract(fileM, "path") zFile.close();

文件格式的要求跟官方示例一致即可。

文件目录 分类1 分类1的标题1 分类1的标题2 ... 分类2 分类3 ...

拆分数据集

AUTOTUNE = tf.data.AUTOTUNE batch_size = 32 seed = 42 raw_train_ds = tf.keras.preprocessing.text_dataset_from_directory( 'train', batch_size=batch_size, validation_split=0.2, subset='training', seed=seed) class_names = raw_train_ds.class_names print(class_names) train_ds = raw_train_ds.cache().prefetch(buffer_size=AUTOTUNE) val_ds = tf.keras.preprocessing.text_dataset_from_directory( 'train', batch_size=batch_size, validation_split=0.2, subset='validation', seed=seed) val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

看一下数据集

for text_batch, label_batch in train_ds.take(1): print(text_batch) for i in range(3): print(f'Review: {text_batch.numpy()[i]}') label = label_batch.numpy()[i] print(f'Label : {label} ({class_names[label]})')

定义模型

def build_classifier_model(): text_input = tf.keras.layers.Input(shape=(), dtype=tf.string, name='text') preprocessing_layer = hub.KerasLayer(tfhub_handle_preprocess, name='preprocessing') encoder_inputs = preprocessing_layer(text_input) encoder = hub.KerasLayer(tfhub_handle_encoder, trainable=True, name='BERT_encoder') outputs = encoder(encoder_inputs) net = outputs['pooled_output'] net = tf.keras.layers.Dropout(0.5)(net) net = tf.keras.layers.Dense(6, activation='softmax', name='classifier')(net) return tf.keras.Model(text_input, net) classifier_model = build_classifier_model() epochs = 10 steps_per_epoch = tf.data.experimental.cardinality(train_ds).numpy() num_train_steps = steps_per_epoch * epochs print(num_train_steps) num_warmup_steps = int(0.1*num_train_steps) init_lr = 3e-5 optimizer = optimization.create_optimizer(init_lr=init_lr, num_train_steps=num_train_steps, num_warmup_steps=num_warmup_steps, optimizer_type='adamw') loss = tf.keras.losses.SparseCategoricalCrossentropy() metrics = tf.metrics.SparseCategoricalAccuracy() classifier_model.compile(optimizer=optimizer, loss=loss, metrics=metrics)

训练模型

history = classifier_model.fit(x=train_ds, validation_data=val_ds, epochs=epochs)

预测数据

发现虽然我只用了300条样本,train样本240条,val样本60条,但是准确率也能到90%。BERT果然叼。

import numpy as np def print_my_examples(inputs, results): result_for_printing = \ [f'input: {inputs[i]:<30} : class: {class_names[np.argmax(results[i])] }' for i in range(len(inputs))] print(*result_for_printing, sep='\n') print() examples = ['德国米技(MIJI)蒸汽喷淋式煮茶壶 全自动保温泡茶壶HK-K018', '公牛插座收纳盒装办公接线板家用创意电源', '生活元素烧水壶办公室家用多功能小型烧水壶保温一体煮茶器煮茶壶', '信安智囊老人防摔防跌倒术后保护重复使用智能气囊马甲服神器送礼', '七度空间卫生巾优雅系列日夜超值组合', ' 【贝拉家】贝拉爸爸自己家工厂生产的科技布狗窝'] example_result = classifier_model(tf.constant(examples)) print_my_examples(examples,example_result)

附录

官方示例BERT分类代码 Tensorflow hub Bert中文模型处理 知乎Bert详解 腾讯云 Bert详解 Bert源码解读