pip install transformers
* *transformers**:是由 Hugging Face 公司开发并开源的一个核心库,专门用于自然语言处理(NLP)、计算机视觉(CV)和音频任务。
* *transformers 库的用途**:它是目前深度学习领域最重要、最流行的库之一。它提供了大量最先进的预训练模型(如 GPT 系列、BERT、LLaMA、GLM 等),以及用于文本分类、信息抽取、问答、摘要、翻译、图像分类等任务的 API。开发者可以使用它极其方便地下载、加载和微调这些大模型,而无需从零开始编写复杂的神经网络代码。
深度学习框架依赖transformers 库本身不包含底层的深度学习计算引擎,它需要依赖 PyTorch 或 TensorFlow。通常你需要先安装好 PyTorch 或 TensorFlow,然后再安装 transformers。如果未安装底层框架transformers 可能会安装一个极简版,但在加载模型时会报错。
import torch
sentences = [
'Today is a sunny day',
'Today is a rainy day'
]
# Tokenization function
def tokenize(text):
return text.lower().split()
# Build the vocabulary
def build_vocab(sentences):
vocab = {}
for sentence in sentences:
tokens = tokenize(sentence)
for token in tokens:
if token not in vocab:
vocab[token] = len(vocab) + 1 # starting index from 1, 0 can be used for padding
return vocab
# Create the vocabulary index
vocab = build_vocab(sentences)
print("Vocabulary Index:", vocab)下面我将为你详细解释这段Python代码的含义、实现原理、用途以及相关的注意事项。
### 1. 代码含义与实现原理
这段代码实现了一个自然语言处理(NLP)中最基础的任务:**构建文本词汇表并为其分配索引**。
代码的执行流程如下:
1. 定义语料库:给定了两个简单的英文句子。
2. 分词tokenize 函数将输入的连续文本字符串转换为离散的标记列表。它首先将所有字母转为小写,然后按空格切分。
3. 构建词汇表build_vocab 函数遍历所有句子,对每个句子进行分词。如果当前词语不在词汇表字典中,就将其加入字典,并赋予一个从1开始递增的整数索引len(vocab) + 1)。
4. 输出结果:打印出构建好的词汇表字典,键为单词,值为对应的索引。
最终输出结果:
```text
Vocabulary Index: {'today': 1, 'is': 2, 'a': 3, 'sunny': 4, 'day': 5, 'rainy': 6}
```
---
### 2. 代码用途
在深度学习和NLP领域,计算机无法直接处理文本字符串,必须将文本转换为数值张量。这段代码的用途正是**文本数据预处理**:
* 特征编码:将离散的文本类别数据映射为连续的整数索引,这是后续进行词嵌入(Word Embedding,如Word2Vec、GloVe)或直接作为模型输入的先决条件。
* 与PyTorch配合:虽然代码中导入torch但未直接使用,但构建出的词汇字典通常会和 torch.nn.Embedding 层配合使用Embedding 层需要知道词汇表的大小,并通过这些整数索引来查找对应的词向量。
---
### 3. 注意事项与改进建议
虽然这段代码逻辑清晰且能正常运行,但在实际工程应用中,有以下几个需要注意和改进的地方:
#### (1) 分词方式的局限性
* 当前问题text.lower().split() 只能按空格切分,无法处理标点符号(如 "day." 会被切分为 "day." 而不是 "day"),也无法处理缩写(如 "don't")或非英文语言(如中文没有空格)。
* 改进建议:在实际项目中,建议使用成熟的分词工具,如 NLTK、spaCy,或者基于子词的分词器如 HuggingFace 的 transformers 库中的 BertTokenizer 等。
#### (2) 索引从1开始的合理性
* 当前设计:代码中 len(vocab) + 1 使得索引从 1 开始,这是**非常合理且常见**的做法。
* 原因:在深度学习模型中(尤其是使用PyTorch的 Embedding 或 RNNs 时),通常将索引 0 保留给 Padding(填充) 符号。因为不同长度的句子需要拼成Batch输入模型,短句子需要用0填充对齐,后续计算时可以通过 mask 忽略索引为0的词。
#### (3) 缺少未知词和填充词的特殊处理
* 当前问题:如果在模型预测时遇到了训练集(词汇表)中没有的新词,代码会报错或无法处理。
* 改进建议:应该在词汇表中显式地加入 <PAD>(填充符,索引0)和 <UNK>(未知词符,通常索引1),并将其他词的索引顺延。当遇到词汇表外的词时,统一映射为 <UNK>。
#### (4) 导入了未使用的库
* 当前问题:代码第一行 import torch 并未在后续代码中使用,属于冗余代码,应予以删除,或者在后续代码中真正使用 torch 来将索引转换为张量。
---
### 4. 改进后的代码示例
结合上述注意事项,这里提供一个更健壮、更符合工业界标准的代码版本:
import torch
sentences = [
'Today is a sunny day.',
'Today is a rainy day!'
]
# 改进的分词函数:简单去除标点并按空格切分(实际工程建议用正则或分词库)
def tokenize(text):
# 简单的标点处理
punctuation = ".,!?;:"
for p in punctuation:
text = text.replace(p, "")
return text.lower().split()
# 改进的词汇表构建函数:预留 PAD 和 UNK
def build_vocab(sentences):
vocab = {'<PAD>': 0, '<UNK>': 1} # 预留特殊标记
for sentence in sentences:
tokens = tokenize(sentence)
for token in tokens:
if token not in vocab:
vocab[token] = len(vocab) # 索引将从2开始自动递增
return vocab
vocab = build_vocab(sentences)
print("Vocabulary Index:", vocab)
# 演示如何将文本转换为 PyTorch 张量
def text_to_tensor(text, vocab):
tokens = tokenize(text)
# 未知词映射为 <UNK> 的索引 1
indices = [vocab.get(token, vocab['<UNK>']) for token in tokens]
return torch.tensor(indices, dtype=torch.long)
# 测试
test_sentence = "Today is a snowy day"
tensor_output = text_to_tensor(test_sentence, vocab)
print(f"Test Sentence: '{test_sentence}'")
print(f"Tensor Output: {tensor_output}")
# 输出: tensor([2, 3, 4, 1, 6])
# 其中 snowy 不在词汇表中,被转换为了 1 (<UNK>)```
希望这个详细的解释和改进示例对你有所帮助!如果你还有其他关于代码或深度学习预处理的问题,欢迎随时提问。
访问国内镜像:https://hf-mirror.com/google-bert/bert-base-uncased/tree/main
下载以下 3个核心文件(点击文件右侧的下载图标):
vocab.txt (词表文件,必须)
config.json (配置文件,必须)
tokenizer.json (快速分词器配置,必须)
from transformers import BertTokenizerFast
sentences = [
'Today is a sunny day',
'Today is a rainy day'
]
# 🔥 核心修改:将 'bert-base-uncased' 改为本地文件夹路径 './bert-base-uncased-local'
# 这样代码就不会去外网下载,而是直接读取你本地文件夹中的 vocab.txt 等文件
local_model_path = './bert-base-uncased-local'
tokenizer = BertTokenizerFast.from_pretrained(local_model_path)
# Tokenize the sentences and encode them
encoded_inputs = tokenizer(sentences, padding=True, truncation=True, return_tensors='pt')
# To see the tokens for each input (helpful for understanding the output)
tokens = [tokenizer.convert_ids_to_tokens(ids) for ids in encoded_inputs["input_ids"]]
# To get the word index similar to Keras' tokenizer
word_index = tokenizer.get_vocab()
print("Tokens:", tokens)
print("Token IDs:", encoded_inputs['input_ids'])
print("Word Index:", dict(list(word_index.items())[:10])) # show only the first 10 for brevity
这段代码使用的是Hugging Facetransformers库,主要功能是**加载本地的BERT分词器,对输入文本进行分词和编码,并展示分词结果、对应的数字ID以及词表**。
下面我将从实现原理、用途和注意事项三个方面为你详细解释这段代码:
### 一、 实现原理与代码逐行解释
1. 加载本地分词器
```python
local_model_path = './bert-base-uncased-local'
tokenizer = BertTokenizerFast.from_pretrained(local_model_path)
```
- BertTokenizerFast 是基于Rust实现的快速分词器,比普通版本速度快很多。
- from_pretrained 方法通常用于从Hugging Face Hub下载模型,但在这里,传入了一个**本地路径**。程序会自动寻找该路径下的 vocab.txt(词表文件)tokenizer.json 或 config.json 等配置文件来初始化分词器,从而实现**离线加载**。
2. 文本分词与编码
```python
encoded_inputs = tokenizer(sentences, padding=True, truncation=True, return_tensors='pt')
```
这一行是核心操作,它同时完成了多个步骤:
- 分词:将句子切分为词元。例如 "Today" 会被切分为 "today"(因为uncased模型会转为小写)。
- 转换为ID (Convert to IDs):根据词表将每个Token映射为整数索引。
- 添加特殊标记:自动在句首添加 [CLS] (ID为101),句末添加 [SEP] (ID为102)。
- 填充padding=True 会将短句子补零(PAD的ID通常为0),使得batch内所有序列等长。
- 截断truncation=True 确保如果句子超过模型最大长度(如512),会被自动截断。
- 返回张量return_tensors='pt' 表示返回PyTorch的Tensor格式,可以直接送入神经网络模型。
3. 提取与展示信息
```python
tokens = [tokenizer.convert_ids_to_tokens(ids) for ids in encoded_inputs["input_ids"]]
word_index = tokenizer.get_vocab()
```
- convert_ids_to_tokens:将编码后的数字ID反向转换回Token字符串,方便我们直观地看到分词结果(包括 [CLS], [SEP], [PAD])。
- get_vocab:获取完整的 词典映射(Token -> ID),类似于Keras Tokenizer的 word_index。
---
### 二、 用途
1. 自然语言处理(NLP)的数据预处理:这是使用BERT等预训练模型进行文本分类、命名实体识别、阅读理解等任务前的标准必备步骤。神经网络无法直接理解字符串,必须通过此步骤将其转化为数字矩阵。
2. 离线/内网环境部署:代码中特意强调了使用本地路径 ./bert-base-uncased-local,这在企业内网环境、Docker容器部署或无外网的服务器上非常实用,避免了因网络问题导致的模型下载失败。
---
### 三、 注意事项
1. 本地文件夹的内容要求:
- 指定的 ./bert-base-uncased-local 文件夹中必须包含必要的分词器文件,至少要有 vocab.txt,或者 tokenizer.json 和 tokenizer_config.json。如果文件缺失from_pretrained 会报错。
2. Padding的副作用:
- 代码中使用了 padding=True,由于第一句话较短,第二句话较长,第一句话会被补齐。注意,返回的 encoded_inputs 中除了 input_ids,还会包含一个 attention_mask 张量(全1代表真实Token,0代表Padding)。**在将数据喂给BERT模型时,必须同时传入 attention_mask**,否则模型会把Padding的0也当作有效信息进行处理。
3. Uncased模型的大小写处理:
- bert-base-uncased 模型的词表中全部是小写字母。因此,输入的 "Today" 在分词时会被自动转换为 "today"。如果你的任务对大小写敏感(如区分专有名词),应该使用 bert-base-cased 及其对应的分词器。
4. 子词切分:
- BERT使用的是WordPiece分词算法。对于词表中没有的词(OOV,Out-of-Vocabulary),它会被切分为更小的子词。例如,"sunnyday" 可能会被切分为 ["sunny", "##day"]## 表示该词元是前一个词的后续部分)。在这个例子中,"sunny" 和 "rainy" 都在词表中,所以被完整保留。
### 💡 补充:预期输出示例
运行该代码,你大致会看到如下输出:
```text
Tokens: [['[CLS]', 'today', 'is', 'a', 'sunny', 'day', '[SEP]'], ['[CLS]', 'today', 'is', 'a', 'rainy', 'day', '[SEP]']]
Token IDs: tensor([[ 101, 2769, 2003, 1037, 4756, 2154, 102, 0],
[ 101, 2769, 2003, 1037, 5398, 2154, 102, 0]])
Word Index: {'[PAD]': 0, '[unused0]': 1, ..., 'the': 1996, ...}
```
(注:由于两句话长度恰好相同,这里可能没有触发明显的0填充,但如果第一句是 "Today is sunny",就会出现 [PAD] 即 ID为0的填充项)