循环神经网络2-文本预处理:从原始文本到数字索引

在深度学习中,处理文本数据是一个非常重要的步骤。文本数据通常以字符串的形式存在,而计算机模型只能处理数字。因此,我们需要将文本转换为数字形式,以便模型能够理解和处理。本文将带你一步步了解如何对文本进行预处理,从读取原始文本到将其转换为数字索引。

1. 读取数据集

首先,我们需要从某个地方获取文本数据。这里我们以H.G. Wells的《时间机器》为例。这个数据集虽然不大,但足够我们用来学习和实验。

python 复制代码
# d2l.py
DATA_HUB['time_machine'] = (
    DATA_URL + 'timemachine.txt',
    '090b5e7e70c295757f55df93cb0a180b9691891a'
)


def read_time_machine():
    """将时间机器数据集加载到文本行的列表中"""
    with open(download('time_machine'), 'r') as f:
        lines = f.readlines()
    return [re.sub('[^A-Za-z]+', ' ', line).strip().lower() for line in lines]


lines = d2l.read_time_machine()
print(f"# 文本总行数:{len(lines)}")
print(lines[0])
print(lines[10])

在这段代码中,我们首先下载并读取了《时间机器》的文本数据。然后,我们对每一行文本进行了简单的处理:去除了标点符号,并将所有字母转换为小写。

输出:

csharp 复制代码
# 文本总行数: 3221
the time machine by h g wells
twinkled and his usually pale face was flushed and animated the

2. 词元化

接下来,我们需要将文本拆分为更小的单位,这些单位可以是单词或字符。这个过程称为词元化(Tokenization)

python 复制代码
# d2l.py

def tokenize(lines, token='word'):
    """将文本行拆分为单词或字符词元"""
    if token == 'word':
        return [line.split() for line in lines]
    elif token == 'char':
        return [list(line) for line in lines]
    else:
        raise Exception(f'错误:未知词元类型:{token}')


tokens = d2l.tokenize(lines)
for i in range(11):
    print(tokens[i])

在这段代码中,我们定义了一个tokenize函数,它可以将文本行拆分为单词或字符。这里我们选择了按单词进行拆分。

输出:

css 复制代码
['the', 'time', 'machine', 'by', 'h', 'g', 'wells']
[]
[]
[]
[]
['i']
[]
[]
['the', 'time', 'traveller', 'for', 'so', 'it', 'will', 'be', 'convenient', 'to', 'speak', 'of', 'him']
['was', 'expounding', 'a', 'recondite', 'matter', 'to', 'us', 'his', 'grey', 'eyes', 'shone', 'and']
['twinkled', 'and', 'his', 'usually', 'pale', 'face', 'was', 'flushed', 'and', 'animated', 'the']

3. 构建词表

词元化之后,我们得到了一个个单词或字符。然而,模型无法直接处理这些字符串,因此我们需要将它们转换为数字。为此,我们需要构建一个词表(Vocabulary),将每个词元映射到一个唯一的数字索引。

python 复制代码
# d2l.py
import collections


class Vocab:
    """文本词表,用于将词元(token)映射为数字索引"""

    def __init__(self, tokens=None, min_freq=0, reserved_tokens=None):
        """
        初始化词表,构建词元到数字索引的映射

        参数:
        - tokens (list): 词元的列表,可以是文本数据中提取出的词元。每个词元可以是单词或字符,默认值为 `None`。
        - min_freq (int): 最小词频,只有词频大于或等于该值的词元才会被加入词表,默认值为 `0`。
        - reserved_tokens (list): 预留词元的列表,这些词元会优先添加到词表中(例如:`<unk>`、`<pad>`等)。默认值为 `None`。
        """
        if tokens is None:
            tokens = []
        if reserved_tokens is None:
            reserved_tokens = []
        # 统计词元频率,并按频率降序排序
        counter = count_corpus(tokens)
        self._token_freqs = sorted(counter.items(), key=lambda x: x[1], reverse=True)
        # 初始化词表,<unk>代表未知词元
        self.idx_to_token = ['<unk>'] + reserved_tokens
        self.token_to_idx = {idx: token for idx, token in enumerate(self.idx_to_token)}
        # 根据频率添加词元,忽略低频词
        for token, freq in self._token_freqs:
            if freq < min_freq:
                break
            if token not in self.token_to_idx:
                self.idx_to_token.append(token)
                self.token_to_idx[token] = len(self.idx_to_token) - 1

    def __len__(self):
        """返回词表大小"""
        return len(self.idx_to_token)

    def __getitem__(self, tokens):
        """
        根据词元返回对应的索引,支持列表输入

        参数:
        - tokens (str, list, tuple): 输入一个词元(字符串)或者词元列表(字符串列表)。
          如果是单个词元,返回该词元的索引;如果是列表或元组,返回每个词元的索引列表。

        返回:
        - (int, list): 如果输入是单个词元,返回对应的索引;如果是词元列表,返回一个索引列表。
        """
        if not isinstance(tokens, (tuple, list)):
            return self.token_to_idx.get(tokens, self.unk)
        return [self.__getitem__(token) for token in tokens]

    @property
    def unk(self):
        """返回未知词元的索引(0)"""
        return 0

    @property
    def token_freqs(self):
        """返回词元频率列表"""
        return self._token_freqs


def count_corpus(tokens):
    """
    统计词元频率,支持1D和2D列表

    参数:
    - tokens (list, list of lists): 输入词元列表,可以是一个一维的词元列表,或者是多个文本行的词元列表(二维列表)。

    返回:
    - (dict): 返回一个字典,键为词元,值为该词元在语料库中出现的频率。
    """
    if len(tokens) == 0 or isinstance(tokens[0], list):
        tokens = [token for line in tokens for token in line]
    return collections.Counter(tokens)
    
    
# 构建词表
vocad = d2l.Vocab(tokens)
print(list(vocad.token_to_idx.items())[:10])

在这段代码中,我们定义了一个Vocab类来构建词表。词表将每个词元映射到一个唯一的索引,并且我们还统计了每个词元的出现频率。

输出:

css 复制代码
[(0, '<unk>'), ('the', 1), ('i', 2), ('and', 3), ('of', 4), ('a', 5), ('to', 6), ('was', 7), ('in', 8), ('that', 9)]

4. 将文本转换为数字索引

有了词表之后,我们就可以将文本中的每个词元转换为对应的数字索引了。

python 复制代码
for i in [0, 10]:
    print("文本:", tokens[i])
    print("索引:", vocad[tokens[i]])

在这段代码中,我们将文本行中的每个词元转换为了对应的数字索引。这样,文本数据就变成了模型可以处理的数字序列。

输出:

less 复制代码
文本: ['the', 'time', 'machine', 'by', 'h', 'g', 'wells']
索引: [1, 19, 50, 40, 2183, 2184, 400]
文本: ['twinkled', 'and', 'his', 'usually', 'pale', 'face', 'was', 'flushed', 'and', 'animated', 'the']
索引: [2186, 3, 25, 1044, 362, 113, 7, 1421, 3, 1045, 1]

5. 整合所有功能

为了方便后续的使用,我们可以将上述所有步骤整合到一个函数中。

python 复制代码
# d2l.py

def load_corpus_time_machine(max_tokens=-1):
    """
    返回时光机器数据集的词元索引列表和词表

    参数:
    - max_tokens (int): 限制返回的词元数量。如果为负值,表示不限制数量,返回全部数据。默认值为 -1。

    返回:
    - corpus (list): 词元索引的列表,表示文本中的每个字符(词元)的索引。
    - vocab (Vocab): 词表对象,用于映射词元和索引之间的关系。
    """
    lines = read_time_machine()
    tokens = tokenize(lines, 'char')  # 使用字符级别的词元化
    vocab = Vocab(tokens)  # 构建词表
    # 将所有文本行展平为一个词元索引的列表
    corpus = [vocab[token] for line in tokens for token in line]
    # 如果限制了词元数量,则截取前 max_tokens 个词元
    if max_tokens > 0:
        corpus = corpus[:max_tokens]
    return corpus, vocab
    
    
corpus, vocab = d2l.load_corpus_time_machine()
print(len(corpus), len(vocab))

在这段代码中,我们将读取数据词元化构建词表 以及转换为数字索引的步骤整合到了一个函数中。最终,我们得到了一个包含所有词元索引的列表corpus,以及对应的词表vocab

输出:

复制代码
170580 28

6. 小结

通过本文,我们学习了如何对文本数据进行预处理。具体来说,我们完成了以下几个步骤:

  1. 读取数据集:从文件中加载文本数据。
  2. 词元化:将文本拆分为单词或字符。
  3. 构建词表:将词元映射到数字索引。
  4. 转换为数字索引:将文本数据转换为模型可以处理的数字序列。

这些步骤是处理文本数据的基础,后续我们可以将这些数字序列输入到模型中进行训练和预测。

相关推荐
liruiqiang052 小时前
循环神经网络 - 简单循环网络
人工智能·rnn·深度学习·神经网络·机器学习
鸿蒙布道师3 小时前
OpenAI战略转向:开源推理模型背后的行业博弈与技术趋势
人工智能·深度学习·神经网络·opencv·自然语言处理·openai·deepseek
小白的高手之路3 小时前
torch.nn.Conv2d介绍——Pytorch中的二维卷积层
人工智能·pytorch·python·深度学习·神经网络·机器学习·cnn
船长@Quant4 小时前
PyTorch量化进阶教程:第五章 Transformer 在量化交易中的应用
pytorch·python·深度学习·transformer·量化交易·sklearn·ta-lab
liruiqiang054 小时前
循环神经网络 - 通用近似定理 & 图灵完备
人工智能·rnn·深度学习·神经网络·机器学习
hunteritself5 小时前
DeepSeek重磅升级,豆包深度思考,ChatGPT原生生图,谷歌Gemini 2.5 Pro!| AI Weekly 3.24-3.30
人工智能·深度学习·chatgpt·开源·语音识别·deepseek
Panesle5 小时前
transformer架构与其它架构对比
人工智能·深度学习·transformer
dundunmm5 小时前
【论文阅读】Self-Correcting Clustering
论文阅读·深度学习·数据挖掘·聚类
Json_9 小时前
Vue 构造器 Vue.extend
前端·vue.js·深度学习
Json_9 小时前
Vue 实例方法
前端·vue.js·深度学习