简洁高效的 NLP 入门指南: 200 行实现 Bert 文本分类 (TensorFlow 版)

简洁高效的 NLP 入门指南: 200 行实现 Bert 文本分类 TensorFlow 版

  • 概述
  • NLP 的不同任务
  • Bert 概述
  • MLM 任务 (Masked Language Modeling)
    • Tokenize
    • MLM 的工作原理
    • 为什么使用 MLM
  • NSP 任务 (Next Sentence Prediction)
    • NSP 任务的工作原理
    • NSP 任务栗子
    • NSP 任务的调整和局限性
  • 安装和环境配置
    • PyTorch
    • Transformers
  • Bert 架构
    • Transformer 模型基础
    • Transformer 的两个主要组成部分
    • Transformer Encoder
    • Bert 的 TransformerEncoder 工作流程
  • 200 行实现 Bert 文本分类 (Tensorflow)

概述

在当今信息时代, 自然语言处理 (NLP, Natural Linguistic Processing) 已经称为人工智能领域的一个关键分支. NLP 的目标是使计算机能够理解, 解释和操作人类语言, 从而在各种应用中发挥作用, 如语音识别, 机器翻译, 情感分析等. 随着技术的进步, NLP 已经从简单的规则和统计方法发展到使用复杂的深度学习模型, 今天我们要来介绍的就是 Bert.

Bert 文本分类

NLP 的不同任务

NLP 的不同任务包含:

  • 文本分类 (Text Classification): 根据文本主题, 将文本分为不同的类别, 李儒新闻分类
  • 情感分析 (Sentiment Analysis): 根据文本的情感倾向, 输出一个数, 表示文本的情感强度, 例如 0~5
  • 机器翻译 (Machine Translation): 根据源语言的文本, 生成目标语言的文本, 例如 zh->en
  • 命名实体识别 (Named Entity Recognition): 将文本中的实体 (例如人名, 地名, 组织名等) 进行标注
  • 句法分析 (Parsing): 根据句子的句法结构, 将句子分解为句子成分
  • 词性标注 (Part-of-speech Tagging): 根据词的语法特征, 给词标注一个词性

今年我们主要介绍的是文本分类任务.

Bert 概述

Bert (Bidirectional Encoder Representations from Transformers) 是一种基于 Transformer 架构的的模型. 在 2018 年由 Google 提出. Bert 采用了双向训练方法, 在模型学习给定的词时, 会考虑其上下文.

Bert 的双向训练方法包括下面两个方面:

  • 模型结构: Bert 模型结构采用了双向 Transformer 编码器, 即模型可以从输入两端同时进行编码
  • 预训练任务: Bert 的预训练任务包括 MLM (Masked Language Modeling) 任务和 NSP 任务, 这两个任务都需要 Bert 模型能够从文本的两端进行推理

Bert 架构

MLM 任务 (Masked Language Modeling)

MLM (Masked Language Modeling) 任务: 在 MLM 任务重, 会在输入文本中随机屏蔽一部分单词, 然后要求 Bert 模型预测被 Masked 单词的正确值.

Tokenize

分词 (Tokenization): 将文本按词 (Word) 为单位进行分割, 并转换为数字数据.
- 常见单词, 例如数据中的人名:
- Rachel对应 token id 5586
- Chandler对应 token id 13814
- Phoebe对应 token id 18188
- 上述 token id 对应 bert 的 vocab 中, roberta 的 vocab 表在服务器上, 懒得找了
- 特殊字符:
- [CLS]: token id 101, 表示句子的开始
- [SEP]: token id 102, 表示分隔句子或文本片段
- [PAD]: token id 0, 表示填充 (Padding), 当文本为达到指定长度时, 例如 512, 会用[PAD]进行填充
- [MASK]: token id 0, 表示填充 (Padding), 当文本为达到指定长度时, 例如 512, 会用[PAD]进行填充

上述字符在 Bert & Bert-like 模型中扮演着至关重要的角色, 在不同的任务重, 这些 Token ID 都是固定的, 例如 Bert 为 30522 个.

FYI: 上面的超链接是 jieba 分词的一个简单示例.

MLM 的工作原理

在 MLM 任务重, 输入文本首先被 Tokenize (分词), 词被转换为一个个数字数据, 文本由常见单词和特殊字符组成. 在处理过程中, 模型随机选择文本中的一定比例的 token (栗如: 15%). 并将这些标记替换为一个特定的特殊标记, 如[MASK](token id 0). 模型的任务是啥预测这些 mask token 的原始值.

为什么使用 MLM

MLM 的主要目的是使模型能够更好的理解语言的上下文和语义. 在传统的语言模型 (如 N-gram, 隐马可夫模型 HMM, 循环神经网络 RNN) 训练中模型都是单向的, 即模型只能考虑单词的前面或后面的上下文. 通过 MLM, 模型被迫学习使用一个单词前后的上下文来预测这个单词, 从而获得更全面的语言理解能力.

NSP 任务 (Next Sentence Prediction)

NSP (Next Sentence Prediction) 是 Bert 模型中的一个关键组成部分. NSP 用于改善模型对句子关系的理解, 特别是在理解段落或文档中句子关系方面. 这种能力对许多 NLP 任务至关重要, 例如: 问答系统, 文本摘要, 对话系统等.

NSP 任务的工作原理

在 NSP 任务重, 模型被训练来预测两个句子是否在原始文本中相邻. 这个过程涉及对句子间和语义关系的深入理解. 个栗子: A & B 俩句子, 模型需要判断 B 是否是紧跟在 A 后面的下一句. 在 Training 过冲中, Half time B 确实是 A 的下一句, 另一半时间 B 则是从语料库中随机选取的与 A 无关的句子. NSP 就是基于这些句子判断他们是否是连续的, 强迫模型学习识别句子的连贯性和上下文关系.

NSP 任务栗子

连续:
- 句子 A: “我是小白呀今年才 18 岁”
- 句子 B: “真年轻”
- NSP: 连续, B 是对 A 的回应 (年龄), 表达了作者 “我” 十分年轻

不连续:
- 句子 A: “意大利面要拌”
- 句子 B: “42 号混凝土”
- NSP: 不连续, B 和 A 内容完全无关

NSP 任务的调整和局限性

尽管在 NSP 和 Bert 的初期奔波中被广泛使用, 但是 NSP 也存在一些局限性. NSP 任务有时可能过于简化, 无法完全捕捉复杂文本中的细微关系.

随着 NLP 模型的发展, 一些研究发现去除 NSP 对某些模型的性能影响不大, 例如: Roberta, Xlnet, 和 Deberta 等后续模型都去除了 NSP 任务. 因为这些模型的底层双向结构已经足够强大, 能欧在没有 NSP 的情况下理解句子间的复杂关系.

安装和环境配置

PyTorch

pip install pytorch

Transformers

pip install transformers

Bert 架构

Bert 架构

Transformer 模型基础

Transformer 模型在 2017 年被提出, 是一种基于注意力机制 (Attention) 的架构, 用于处理序列数据. 与之前的序列处理模型 (RNN 和 LSTM) 不同, Transformer 完全依赖于注意力机制来捕获序列的全局依赖关系, 这使得模型在处理长距离依赖时更加有效.

Transformer 的两个主要组成部分

  1. Encoder (编码器): 负责处理输入数据
  2. Decoder (解码器): 负责生成输出数据

Transformer Encoder

Bert 的核心组成部分之一是基于 Transformer 的编码器, 即 TrasnformerEncoder.

class TransformerEncoder(Layer):def __init__(self, encoder_layer, num_layers, norm=None):super(TransformerEncoder, self).__init__()# 由多层encoder_layer组成,论文中给出,bert-base是12层,bert-large是24层,一层结构就如上图中蓝色框里的结构# num_layers = 12 or 24# LayerList称之为容器,使用方法和python里的list类似self.layers = LayerList([(encoder_layer if i == 0 else type(encoder_layer)(**encoder_layer._config)) for i in range(num_layers)])self.num_layers = num_layers

TransformerEncoder 由多个相同的层堆叠而成, 每层包含两个主要子层:

  1. 多头自注意力机制 (Multi-Head Self-Attention): 这个机制允许模型在处理每个单词时考虑到句子中的所有其他单词, 从而捕获复杂的内部依赖关系. Multi-Head 的设计使得模型能够同时从不同的表示子空间中学习信息
  2. 前馈神经网络 (Feed-Forward Neural Network): 每个注意力层后面都跟着一个简单的前馈神经网络, 这个网络对每个位置的输出进行独立处理

每个子层后面有一个残差链接 (Residual Connection) 和层归一化 (Layer Normalization). 残差连接有助于避免在深层网络中出现的梯度消失 (Vanishing Gradient) 问题, 而层归一化则有助于稳定训练过程.

Bert 的 TransformerEncoder 工作流程

  1. 输入表示: 输入文本首先被转换成词嵌入向量, 然后加上位置编码 (Positional Encoding), 以提供位置信息
  2. 通过多头自注意力 (Multi-Head Self-Attention) 层, 模型学习如何更加其他单词信息调整每个单词的表示
  3. 前馈网络: 每个位置的输出被送入前馈网络, 进一步处理每个单词的表示
  4. 重复多层处理: 过程在多个TransformersEncoder层中重复进行, 每一层都进一步增强了模型对文本的理解

200 行实现 Bert 文本分类 (Tensorflow)

"""
@Module Name: bert.py
@Author: CSDN@我是小白呀
@Date: December 14, 2023Description:
200 行实现 Bert 文本分类
"""
"""
@Module Name: bert.py
@Author: CSDN@我是小白呀
@Date: December 14, 2023Description:
200 行实现 Bert 文本分类
"""
import numpy as np
import tensorflow as tf
from tensorflow.keras import Model
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam
from transformers import BertTokenizer, TFBertModel
import pickle
import logging# 超参数
EPOCHS = 20  # 迭代次数
BATCH_SIZE = 16  # 单词训练样本数目
learning_rate = 5e-6  # 学习率
INPUT_DIM = 50000
MAX_LENGTH = 512
optimizer = Adam(learning_rate=learning_rate)  # 优化器
loss = tf.keras.losses.MeanSquaredError()  # 损失
bert_tokenizer = BertTokenizer.from_pretrained("bert-large-uncased")  # Bert的分词器
bert_model = TFBertModel.from_pretrained("bert-large-uncased")  # Bert的分词器
prefix = "_raw"
logging.basicConfig(filename='model/bert_large/training_log.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')class TrainingLoggingCallback(tf.keras.callbacks.Callback):def on_epoch_end(self, epoch, logs=None):if logs is not None:logging.info(f"Epoch {epoch + 1}/{EPOCHS}")logging.info(f"loss: {logs['loss']} - accuracy: {logs['accuracy']}")logging.info(f"val_loss: {logs['val_loss']} - val_accuracy: {logs['val_accuracy']}")logging.info(f"lr: {self.model.optimizer.lr.numpy()}")def get_data():"""读取数据:return: 返回分批完的训练集和测试集"""# 读取with open('../save/raw/train.pkl', 'rb') as f:combined_data = pickle.load(f)X_train = combined_data['X_train']X_valid = combined_data['X_valid']y_train = combined_data['y_train']y_valid = combined_data['y_valid']# 获取input/masktrain_input = X_train["input_ids"]train_mask = X_train["attention_mask"]train_input = np.asarray(train_input)train_mask = np.asarray(train_mask)val_input = X_valid["input_ids"]val_mask = X_valid["attention_mask"]val_input = np.asarray(val_input)val_mask = np.asarray(val_mask)return train_input, val_input, train_mask, val_mask, y_train, y_validdef lr_schedule(epoch, lr):"""学习率递减"""if epoch < 1:return lrelse:return lr * 0.9def main():# 加载数据X_train_input, X_test_input, X_train_mask, X_test_mask, y_train, y_test = get_data()print(X_train_input[:5], X_train_input.shape)print(X_test_input[:5], X_test_input.shape)print(X_train_mask[:5], X_train_mask.shape)print(X_test_mask[:5], X_test_mask.shape)print(y_train[:5], y_train.shape)print(y_test[:5], y_test.shape)input_ids = tf.keras.Input(shape=(MAX_LENGTH,), dtype=tf.int32)masks = tf.keras.Input(shape=(MAX_LENGTH,), dtype=tf.int32)bert = bert_model([input_ids, masks])bert = bert[1]classifier = Dense(24, activation='softmax')(bert)# 模型model = Model(inputs=[input_ids, masks], outputs=classifier)print(model.summary())# 组合model.compile(optimizer=optimizer, loss=loss, metrics=["accuracy"])# 保存checkpoint = tf.keras.callbacks.ModelCheckpoint("model/bert_large/bert_large.ckpt", monitor='val_loss',verbose=1, save_best_only=True, mode='min',save_weights_only=True)# 学习率调度lr_scheduler = tf.keras.callbacks.LearningRateScheduler(lr_schedule)# 早停early_stopping = tf.keras.callbacks.EarlyStopping(monitor='val_loss',  patience=2, verbose=1,restore_best_weights=True)# 训练model.fit([X_train_input, X_train_mask], y_train, validation_data=([X_test_input, X_test_mask], y_test),epochs=EPOCHS, batch_size=BATCH_SIZE,callbacks=[checkpoint, lr_scheduler, TrainingLoggingCallback(), early_stopping])if __name__ == '__main__':main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/225790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UE5.2】从零开始控制角色移动、游泳、下潜、上浮

目录 效果 步骤 一、项目准备 二、控制角色移动 三、控制角色游泳 四、实现角色潜水、上浮 五、解决在水面上浮的Bug 效果 步骤 一、项目准备 1. 新建一个空白工程&#xff0c;创建一个Basic关卡&#xff0c;添加第三人称游戏资源到内容浏览器 2. 在插件中启用“W…

IDEA——还在手动new对象set值嘛,GenerateAllSetter插件帮你解决!!!

IDEA插件 一、GenerateAllSetter插件介绍二、如何下载安装三、如何使用 总结 最近项目上有些测试需要有很多属性&#xff0c;而且大部分的属性都是要设置值的&#xff0c;一个一个手动set设值很繁琐&#xff0c;就想着有没有能解决这个问题的办法&#xff0c;就发现了一个非常好…

HarmonyOS(十二)——全面认识HarmonyOS三种渲染控制

渲染控制概述 ArkUI通过自定义组件的build()函数和builder装饰器中的声明式UI描述语句构建相应的UI。在声明式描述语句中开发者除了使用系统组件外&#xff0c;还可以使用渲染控制语句来辅助UI的构建&#xff0c;这些渲染控制语句包括控制组件是否显示的条件渲染语句&#xff…

微软Microsoft二面面试题分享通过总结(不是标准答案分享

误打误撞 我写的shitty代码 当年面试算法开发岗竟然通过了 Background 先说下背景&#xff0c;软件工程本科毕业之后&#xff0c;当年8月到北欧读两年制硕士。面试发生在当年的11月&#xff0c;微软哥本哈根&#xff0c;location在丹麦的哥本哈根lingby&#xff08;是不是这么…

C++异步网络库workflow系列教程(3)Series串联任务流

往期教程 如果觉得写的可以,请给一个点赞关注支持一下 观看之前请先看,往期的两篇博客教程,否则这篇博客没办法看懂 workFlow c异步网络库编译教程与简介 C异步网络库workflow入门教程(1)HTTP任务 C异步网络库workflow系列教程(2)redis任务 简介 首先,workflow是任务流的意…

ThingWorx/Vuforia—工业物联网和AR平台

产品概述 ThingWorx是美国PTC公司旗下的一款物联网和AR平台&#xff0c;它提供了适用于IoT的开发工具和能力&#xff0c;使开发者可以为工业物联网快速构建和部署变革性的智能互联解决方案&#xff0c;使创新者能够快速为当今的智能互联世界提供优异的应用程序、解决方案和用户…

人工智能计算机视觉:解析现状与未来趋势

导言 随着人工智能的迅速发展&#xff0c;计算机视觉技术逐渐成为引领创新的关键领域。本文将深入探讨人工智能在计算机视觉方面的最新进展、关键挑战以及未来可能的趋势。 1. 简介 计算机视觉是人工智能的一个重要分支&#xff0c;其目标是使机器具备类似于人类视觉的能力。这…

k8syaml提供的几个有意思的功能,Kubernetes在线工具网站

k8syaml.cn 提供的几个有意思的功能。 一、yaml资源快速生成 之前编写operator的helm的时候就需要自己写deployment、service、configmap这些资源&#xff0c;那么多字段也记不清&#xff0c;都是先找个模版&#xff0c;然后copy改改&#xff0c;再看官方文档&#xff0c;添加…

智能优化算法应用:基于和声算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于和声算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于和声算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.和声算法4.实验参数设定5.算法结果6.参考文献7.MA…

Jenkins Pipeline 脚本优化实践:从繁琐到简洁

引言 在持续集成的过程中&#xff0c;Jenkins Pipeline 是非常关键的一环。它定义了如何自动编译、测试和部署代码。随着项目的不断发展&#xff0c;Pipeline 的复杂性也在不断上升&#xff0c;这就需要我们持续优化 Pipeline 脚本&#xff0c;以提高代码的可读性和维护性。本…

Python如何匹配库的版本

目录 1. 匹配库的版本 2. Python中pip&#xff0c;库&#xff0c;编译环境的问题回答总结 2.1 虚拟环境 2.2 pip&#xff0c;安装库&#xff0c;版本 1. 匹配库的版本 &#xff08;别的库的版本冲突同理&#xff09; 在搭建pyansys环境的时候&#xff0c;安装grpcio-tools…

RT-DETR优化:轻量化卷积设计 | DualConv双卷积魔改RT-DETR结构

🚀🚀🚀本文改进: DualConv双卷积魔改v8结构,达到轻量化的同时并能够实现小幅涨点 🚀🚀🚀RT-DETR改进创新专栏:http://t.csdnimg.cn/vuQTz 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研; RT-DETR模型创新优化,涨点技巧分享,科研小助手; 1.DualC…

软件测试经典面试题(答案解析+视频讲解)

前言 &#xff08;第一个就刷掉一大批人&#xff09; 有很多“会自动化”的同学来咨询技术问题&#xff0c;他总会问到我一些元素定位的问题。元素定位其实都不算自动化面试的问题。 一般我都会问&#xff1a;你是定位不到吗&#xff1f;通常结果都是说确实定位不到。 做自…

真正可行的vue3迁移到nuxt3方法(本人亲测,完全避坑)

终于到了总结经验的时候了&#xff0c;这绝对是全网唯一、完全真正可行的干货。 在我看来&#xff0c;知识就是要拿来分享的&#xff0c;分享给他人也是在提高自己。我绝对不会搞什么订阅或者vip专栏来搞钱坑害各位&#xff0c; 因为我在csdn写文章最主要的目的是为了记录和总…

OpenJDK十几种发行版中强烈推荐的发行版:Adoptium Eclipse Temurin和Amazon Corretto

Adoptium Eclipse Temurin官网地址&#xff1a;Home | Adoptium 支持及维护它的厂家&#xff1a; 说明&#xff1a;它的前身是著名的AdoptOpenJDK&#xff0c;更新到jdk16后就停止更新了&#xff0c;因为AdoptOpenJDK移交给Eclipse基金会后改名为&#xff1a;Adoptium Eclipse…

多线程 (下) - 学习笔记

常见锁策略 乐观锁和悲观锁 悲观锁 总是假设最坏的情况, 每次去拿数据的时候都会认为会被别人修改, 因此会上锁, 防止数据在使用过程中被别的线程修改, 乐观锁 假设数据一般情况下不会产生并发冲突,因此在拿数据,操作数据的过程中不加锁, 而在数据进行提交更新的时候, 才会正…

LeetCode day24

LeetCode day24 今天主打一个快乐happy(▽ʃ♡ƪ)&#xff0c;主要是今天写哈夫曼树被经典文件读取坑麻了&#xff08;为啥绝对路径能读取&#xff0c;相对不行。罢了&#xff09; 一个中等题&#xff0c;但是咋感觉很小学捏。。。 2177. 找到和为给定整数的三个连续整数 相…

C与C++编程语言的区别和联系

一、引言 C和C是两种广泛使用的编程语言&#xff0c;它们都在软件开发领域有着广泛的应用。虽然C是从C语言演化而来的&#xff0c;但两者之间存在一些重要的区别和联系。本文将详细介绍这两种编程语言的相同点和不同点&#xff0c;并通过实际例子进行说明。 二、C与C的相同点 …

RocketMq常见问题

如何保证消息不丢失? 产生丢失消息的节点主要有以下几点 生产者发到brokerbroker把消息从缓存写入磁盘breker同步到从节点消费者消费消息消息积压太多, 会删除历史消息, 这里不会校验消息有没有消费 解决: 生产者同步发送消息, 如果发送失败, 写重试逻辑, 如果重试多次还失…

基于Java SSM框架实现抗疫医疗用品销售系统项目【项目源码+论文说明】

基于java的SSM框架实现抗疫医疗用品销售系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;抗疫医疗用品销售平台当然也不能排除在外。抗疫医疗用品销售平台是以实…