深度学习处理文本(2)

news2025/4/2 2:32:32

建立词表索引

将文本拆分成词元之后,你需要将每个词元编码为数值表示。你可以用无状态的方式来执行此操作,比如将每个词元哈希编码为一个固定的二进制向量,但在实践中,你需要建立训练数据中所有单词(​“词表”​)的索引,并为词表中的每个单词分配唯一整数,如下所示。

vocabulary = {}
for text in dataset:
    text = standardize(text)
    tokens = tokenize(text)
    for token in tokens:
        if token not in vocabulary:
            vocabulary[token] = len(vocabulary)

然后,你可以将这个整数转换为神经网络能够处理的向量编码,比如one-hot向量。

def one_hot_encode_token(token):
    vector = np.zeros((len(vocabulary),))
    token_index = vocabulary[token]
    vector[token_index] = 1
    return vector

请注意,这一步通常会将词表限制为训练数据中前20 000或30 000个最常出现的单词。任何文本数据集中往往都包含大量独特的单词,其中大部分只出现一两次。对这些罕见词建立索引会导致特征空间过大,其中大部分特征几乎没有信息量。在IMDB数据集上训练了第一个深度学习模型,还记得吗?你使用的数据来自keras.datasets.imdb,它已经经过预处理转换为整数序列,其中每个整数代表一个特定单词。当时我们设置num_words=10000,其目的就是将词表限制为训练数据中前10 000个最常出现的单词。

这里有一个不可忽略的重要细节:当我们在词表索引中查找一个新的词元时,它可能不存在。你的训练数据中可能不包含“cherimoya”一词的任何实例(也可能是你将它从词表中去除了,因为它太罕见了)​,所以运行token_index =vocabulary[“cherimoya”]可能导致KeyError。要处理这种情况,你应该使用“未登录词”​(out of vocabulary,缩写为OOV)索引,以涵盖所有不在索引中的词元。OOV的索引通常是1,即设置token_index = vocabulary.get(token, 1)。将整数序列解码为单词时,你需要将1替换为“​[UNK]​”之类的词(叫作“OOV词元”​)​。你可能会问:​“为什么索引是1而不是0?​”这是因为0已经被占用了。有两个特殊词元你会经常用到:OOV词元(索引为1)和掩码词元(mask token,索引为0)​。OOV词元表示“这里有我们不认识的一个单词”​,掩码词元的含义则是“别理我,我不是一个单词”​。你会用掩码词元来填充序列数据:因为数据批量需要是连续的,一批序列数据中的所有序列必须具有相同的长度,所以需要对较短的序列进行填充,使其长度与最长序列相同。如果你想用序列[5, 7, 124, 4, 89]和[8, 34, 21]生成一个数据批量,那么它应该是这个样子:

[[5,  7, 124, 4, 89]
 [8, 34,  21, 0,  0]]

使用TextVectorization层

到目前为止的每一个步骤都很容易用纯Python实现。你可以写出如下所示的代码。

import string

class Vectorizer:
    def standardize(self, text):
        text = text.lower()
        return "".join(char for char in text
                       if char not in string.punctuation)

    def tokenize(self, text):
        text = self.standardize(text)
        return text.split()

    def make_vocabulary(self, dataset):
        self.vocabulary = {"": 0, "[UNK]": 1}
        for text in dataset:
            text = self.standardize(text)
            tokens = self.tokenize(text)
            for token in tokens:
                if token not in self.vocabulary:
                    self.vocabulary[token] = len(self.vocabulary)
        self.inverse_vocabulary = dict(
            (v, k) for k, v in self.vocabulary.items())

    def encode(self, text):
        ext = self.standardize(text)
        okens = self.tokenize(text)
        eturn [self.vocabulary.get(token, 1) for token in tokens]

    def decode(self, int_sequence):
        return " ".join(
            self.inverse_vocabulary.get(i, "[UNK]") for i in int_sequence)

vectorizer = Vectorizer()
dataset = [
    "I write, erase, rewrite",
    "Erase again, and then",
    "A poppy blooms.",
]
vectorizer.make_vocabulary(dataset)

以上代码的效果如下。

>>> test_sentence = "I write, rewrite, and still rewrite again"
>>> encoded_sentence = vectorizer.encode(test_sentence)
>>> print(encoded_sentence)
[2, 3, 5, 7, 1, 5, 6]
>>> decoded_sentence = vectorizer.decode(encoded_sentence)
>>> print(decoded_sentence)
"i write rewrite and [UNK] rewrite again"

但是,这种做法不是很高效。在实践中,我们会使用Keras的TextVectorization层。它快速高效,可直接用于tf.data管道或Keras模型中。TextVectorization层的用法如下所示。

from tensorflow.keras.layers import TextVectorization
text_vectorization = TextVectorization(
    output_mode="int",----设置该层的返回值是编码为整数索引的单词序列。还有其他几种可用的输出模式,稍后会看到其效果
)

默认情况下,TextVectorization层的文本标准化方法是“转换为小写字母并删除标点符号”​,词元化方法是“利用空格进行拆分”​。但重要的是,你也可以提供自定义函数来进行标准化和词元化,这表示该层足够灵活,可以处理任何用例。请注意,这种自定义函数的作用对象应该是tf.string张量,而不是普通的Python字符串。例如,该层的默认效果等同于下列代码。

import re
import string
import tensorflow as tf

def custom_standardization_fn(string_tensor):
    lowercase_string = tf.strings.lower(string_tensor)----将字符串转换为小写字母
    return tf.strings.regex_replace(----将标点符号替换为空字符串
        lowercase_string, f"[{re.escape(string.punctuation)}]", "")

def custom_split_fn(string_tensor):
    return tf.strings.split(string_tensor)----利用空格对字符串进行拆分

text_vectorization = TextVectorization(
    output_mode="int",
    standardize=custom_standardization_fn,
    split=custom_split_fn,
)

要想对文本语料库的词表建立索引,只需调用该层的adapt()方法,其参数是一个可以生成字符串的Dataset对象或者一个由Python字符串组成的列表。

dataset = [
    "I write, erase, rewrite",
    "Erase again, and then",
    "A poppy blooms.",
]
text_vectorization.adapt(dataset)

请注意,你可以利用get_vocabulary()来获取得到的词表,如代码清单11-1所示。对于编码为整数序列的文本,如果你需要将其转换回单词,那么这种方法很有用。词表的前两个元素是掩码词元(索引为0)和OOV词元(索引为1)​。词表中的元素按频率排列,所以对于来自现实世界的数据集,​“the”或“a”这样非常常见的单词会排在前面。

代码清单11-1 显示词表

>>> text_vectorization.get_vocabulary()
["", "[UNK]", "erase", "write", ...]

作为演示,我们对一个例句进行编码,然后再解码。

>>> vocabulary = text_vectorization.get_vocabulary()
>>> test_sentence = "I write, rewrite, and still rewrite again"
>>> encoded_sentence = text_vectorization(test_sentence)
>>> print(encoded_sentence)
tf.Tensor([ 7  3  5  9  1  5 10], shape=(7,), dtype=int64)
>>> inverse_vocab = dict(enumerate(vocabulary))
>>> decoded_sentence = " ".join(inverse_vocab[int(i)] for i in encoded_sentence)
>>> print(decoded_sentence)
"i write rewrite and [UNK] rewrite again"

在tf.data管道中使用TextVectorization层或者将TextVectorization层作为模型的一部分重要的是,TextVectorization主要是字典查询操作,所以它不能在GPU或TPU上运行,只能在CPU上运行。因此,如果在GPU上训练模型,那么TextVectorization层将在CPU上运行,然后将出发送至GPU,这会对性能造成很大影响。TextVectorization层有两种用法。第一种用法是将其放在tf.data管道中,如下所示。

int_sequence_dataset = string_dataset.map(---- string_dataset是一个能够生成字符串张量的数据集
    text_vectorization,
    num_parallel_calls=4)---- num_parallel_calls参数的作用是在多个CPU内核中并行调用map()

第二种用法是将其作为模型的一部分(毕竟它是一个Keras层)​,如下所示

text_input = keras.Input(shape=(), dtype="string")----创建输入的符号张量,数据类型为字符串
vectorized_text = text_vectorization(text_input)----对输入应用文本向量化层
embedded_input = keras.layers.Embedding(...)(vectorized_text)---- (本行及以下2)你可以继续添加新层,就像普通的函数式API模型一样
output = ...
model = keras.Model(text_input, output)

两种用法之间有一个重要区别:如果向量化是模型的一部分,那么它将与模型的其他部分同步进行。这意味着在每个训练步骤中,模型的其余部分(在GPU上运行)必须等待TextVectorization层(在CPU上运行)的出准备好,才能开始工作。与此相对,如果将该层放在tf.data管道中,则可以在CPU上对数据进行异步预处理:模型在GPU上对一批向量化数据进行处理时,CPU可以对下一批原始字符串进行向量化。因此,如果在GPU或TPU上训练模型,你可能会选择第一种用法,以获得最佳性能。本章的所有实例都会使用这种方法。但如果在CPU上训练,那么同步处理也可以:无论选择哪种方法,内核利用率都会达到100%。接下来,如果想将模型导出到生产环境中,你可能希望导出一个接收原始字符串作为入的模型(类似上面第二种用法的代码片段)​,否则,你需要在生产环境中(可能是JavaScript)重新实现文本标准化和词元化,可能会引入较小的预处理偏差,从而降低模型精度。值得庆幸的是,TextVectorization层可以将文本预处理直接包含在模型中,使其更容易部署,即使一开始将该层用在tf.data管道中也是如此。

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

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

相关文章

python实现股票数据可视化

最近在做一个涉及到股票数据清洗及预测的项目,项目中需要用到可视化股票数据这一功能,这里我与大家分享一下股票数据可视化的一些基本方法。 股票数据获取 目前,我已知的使用python来获取股票数据方式有以下三种: 爬虫获取,实现…

JavaScript DOM与元素操作

目录 DOM 树、DOM 对象、元素操作 一、DOM 树与 DOM 对象 二、获取 DOM 元素 1. 基础方法 2. 现代方法(ES6) 三、修改元素内容 四、修改元素常见属性 1. 标准属性 2. 通用方法 五、通过 style 修改样式 六、通过类名修改样式 1. className 属…

ARM向量表

向量表作用说明RVBAR在 AArch64 中,重置向量不再是异常向量表的一部分。 有复位向量的专用配置输入引脚和寄存器。在 AArch64 中,处理器从 IMPLEMENTAION‑DEFINED 地址开始执行, 该地址由硬件输入引 脚RVBARADDR定义, 可以通过 R…

leetcode刷题日记——除自身以外数组的乘积

[ 题目描述 ]: [ 思路 ]: 题目要求获取数组中每个元素除自己以外的各元素的乘积最简单的方法就是算出数组所有元素的乘积,然后除以自身,即可得到除自身外各元素的乘积 但要考虑到其自身为0的情况,即当期自身为0时&am…

【信奥一本通提高篇】基础算法之贪心算法

原文 https://bbs.fmcraft.top/blog/index.php/archives/22/ 贪心算法 概述 近年来的信息学竞赛试题,经常出现求一个问题的可行解或最优解的题目。这类问题就是我们通常所说的最优化问题。贪心算法是求解这类问题的一种常用算法。在众多的算法中,贪心…

PyQt6实例_批量下载pdf工具_批量pdf网址获取

目录 前置: 步骤: step one 安装包 step two 获取股票代码 step three 敲代码,实现 step four 网址转pdf网址 视频 前置: 1 本系列将以 “PyQt6实例_批量下载pdf工具”开头,放在 【PyQt6实例】 专栏 2 本节讲…

KMeans算法案例

KMeans算法案例 案例介绍 已知:客户性别、年龄、年收入、消费指数 需求:对客户进行分析,找到业务突破口,寻找黄金客户 数据集共包含顾客的数据, 数据共有 4 个特征, 数据共有 200 条。接下来,使用聚类算法对具有相似…

IDApro直接 debug STM32 MCU

使用IDA pro 逆向分析muc 固件的时候, 难免要进行一些动态的debug,来进一步搞清楚一些内存的数据、算法等,这时候使用远程debug 的方式直接在mcu上进行debug 最合适不过了。 不过有个前提条件就是一般来说有的mcu 会被运行中的代码屏蔽 RDP、…

六十天前端强化训练之第三十六天之E2E测试(Cypress)大师级完整指南

欢迎来到编程星辰海的博客讲解 看完可以给一个免费的三连吗,谢谢大佬! 目录 一、知识讲解 1. E2E测试核心概念 2. Cypress框架特性 3. 工作原理 4. 测试金字塔定位 二、核心代码示例:用户登录全流程测试 三、实现效果展示 四、学习要…

20250330-傅里叶级数专题之离散傅里叶变换(5/6)

5. 傅里叶级数专题之离散傅里叶变换 推荐视频: 工科生以最快的速度理解离散傅立叶变换(DFT) 哔哩哔哩 20250328-傅里叶级数专题之数学基础(0/6)-CSDN博客20250330-傅里叶级数专题之傅里叶级数(1/6)-CSDN博客20250330-傅里叶级数专题之傅里叶变换(2/6)-CSDN博客20250330-傅里叶…

3.29:数据结构-绪论线性表-上

一、时间复杂度 1、ADT 2、定义法计算时间复杂度:统计核心语句的总执行次数 (1)例题1,与2022年的真题对比着写 此题关键在于求和公式的转化,类型为:线性循环嵌套非线性循环 2022年那道题如果考场上实在脑…

大模型架构记录13【hr agent】

一 Function calling 函数调用 from dotenv import load_dotenv, find_dotenvload_dotenv(find_dotenv())from openai import OpenAI import jsonclient OpenAI()# Example dummy function hard coded to return the same weather # In production, this could be your back…

conda 清除 tarballs 减少磁盘占用 、 conda rename 重命名环境、conda create -n qwen --clone 当前环境

🥇 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 🎉 声明: 作为全网 AI 领域 干货最多的博主之一,❤️ 不负光阴不负卿 ❤️ 文章目录 conda clean --tarballsconda rename 重命名环境conda create -n qwen --clone …

pycharm相对路径引用方法

用于打字不方便,以下直接手写放图,直观理解

新能源智慧灯杆的智能照明系统如何实现节能?

叁仟新能源智慧灯杆的智能照明系统可通过以下多种方式实现节能: 智能调光控制 光传感器技术:在灯杆上安装光传感器,实时监测周围环境的光照强度。当环境光线充足时,如白天或有其他强光源时,智能照明系统会自动降低路…

Jenkins教程(自动化部署)

Jenkins教程(自动化部署) 1. Jenkins是什么? Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,广泛用于项目开发,具有自动化构建、测试和部署等功能。Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行&…

行业智能体大爆发,分布式智能云有解

Manus的一夜爆红,在全球范围内引爆关于AI智能体的讨论。 与过去一般的AI助手不同,智能体(AI Agent)并非只是被动响应,而是主动感知、决策并执行的应用。Gartner预测,到2028年,15%的日常工作决策…

日语Learn,英语再认识(5)

This is a dedicated function — it exists solely to solve this case. This is a dedicated function. It’s a dedicated method for solving this case. 其他备选词(但没dedicated精准): special → 含糊,有时只是“特别”…

【区块链安全 | 第十四篇】类型之值类型(一)

文章目录 值类型布尔值整数运算符取模运算指数运算 定点数地址(Address)类型转换地址的成员balance 和 transfersendcall,delegatecall 和 staticcallcode 和 codehash 合约类型(Contract Types)固定大小字节数组&…

音视频入门基础:MPEG2-TS专题(25)——通过FFmpeg命令使用UDP发送TS流

一、通过FFmpeg命令使用UDP发送TS流 通过以下FFmpeg命令可以将一个mp4文件转换为ts封装,并基于UDP发送(推流): ffmpeg.exe -re -i input.mp4 -vcodec copy -acodec copy -f mpegts udp://127.0.0.1:1234 其中: “in…