生成乱码文本对于初学者来说是一个简单的编程练习。但有意义地完成一个句子需要大量的工作。随着神经网络方法的引入,自动补全技术领域发生了巨大变化。借助 Hugging Face 的 transformers 库,实现文本补全只需几行代码。在本综合教程中,您将实现多个示例,并探讨现代系统与传统系统有何不同,以及这些差异为何重要。
通过我的书籍《Hugging Face Transformers中的NLP》,快速启动您的项目。它提供了带有工作代码的自学教程。
让我们开始吧。

使用 GPT-2 模型进行自动补全式文本生成
图片由 Jayphen Simpson 提供。保留部分权利。
概述
本文分为六个部分;它们是
- 传统方法与神经网络方法
- 自动补全架构
- 基本的自动补全实现
- 缓存和批量输入
传统方法与神经网络方法
当您在 Google 搜索栏中输入一个词,例如“machine”时,您可能会发现一些额外的词被建议出来,例如“learning”,以构成“machine learning”。这就是自动补全技术。建议可能不是您所期望的,但它总是连贯的。
传统的自动补全系统依赖于相对统计学的方法。N-gram 模型通过查看固定窗口的先前单词并将其与收集的样本进行比较来预测下一个单词。这种方法难以处理更长的上下文和新颖的组合。基于词典的方法只能建议它们以前见过的单词,限制了它们处理新术语的能力。频率分析根据常见模式提供建议,但经常会错过当前文本的细微上下文。
神经自动补全系统,特别是基于 GPT-2 的系统,代表了能力上的根本性转变。这些系统理解上下文而不是匹配单词。它考虑了先前文本的完整范围,而不仅仅是几个单词。它们掌握语义关系,使它们能够建议不仅匹配语法而且匹配文本含义的补全。生成能力使它们能够生成与现有内容保持连贯的完整短语或句子。
自动补全架构
现代神经网络自动补全系统集成了几个复杂的组件,它们协同工作。
语言模型充当认知引擎。它处理输入文本并维护内部状态以捕获正在进行的文本生成过程的细微差别。分词组件充当人类可读文本和模型数值表示之间的桥梁。生成控制器协调过程,采用高级策略来过滤和排序潜在的补全。它仔细平衡响应时间和建议质量,确保用户在没有明显延迟的情况下收到有用的补全。
开发一个有效的神经网络自动补全系统涉及克服几个关键挑战。延迟是主要关注点,因为用户期望在处理神经网络操作的计算复杂性时,周转时间以毫秒计。
质量控制是另一个挑战。系统应该生成相关的建议,因此需要先进的过滤机制来防止不适当的补全并确保建议与用户的领域和写作风格一致。
当系统扩展以支持多个用户时,资源管理至关重要。神经网络模型的大量内存需求和文本生成的计算强度必须与系统资源和响应时间要求仔细平衡。
基本的自动补全实现
让我们抛开大型系统的考虑,专注于对部分文本进行简单的自动补全功能。使用 transformers 库中的预训练模型很容易实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
from transformers import GPT2LMHeadModel, GPT2Tokenizer import torch class AutoComplete: def __init__(self, model_name='gpt2'): """初始化自动补全系统。""" self.tokenizer = GPT2Tokenizer.from_pretrained(model_name) self.model = GPT2LMHeadModel.from_pretrained(model_name) self.device = 'cuda' if torch.cuda.is_available() else 'cpu' self.model.to(self.device) self.model.eval() # 设置为评估模式 def get_completion(self, text, max_length=50): """为输入文本生成补全。""" # 编码输入文本 inputs = self.tokenizer(text, add_special_tokens=False, return_tensors="pt") input_ids = inputs["input_ids"].to(self.device) attn_masks = inputs["attention_mask"].to(self.device) # 生成补全 with torch.no_grad(): outputs = self.model.generate( input_ids, attention_mask=attn_masks, max_length=max_length, num_return_sequences=1, pad_token_id=self.tokenizer.eos_token_id, do_sample=True, temperature=0.7 ) # 解码并提取补全 full_text = self.tokenizer.decode(outputs[0], skip_special_tokens=True) completion = full_text[len(text):] return completion # 使用自动补全来查看结果 auto_complete = AutoComplete() text = "The future of artificial" completion = auto_complete.get_completion(text) print(f"输入: {text}") print(f"补全: {completion}") |
让我们看看上面的代码做了什么。您定义了 `AutoComplete` 类,它加载 `GPT2Tokenizer` 作为文本分词器,加载 `GPT2LMHeadModel` 作为预训练的 GPT-2 模型,该模型能够生成文本。模型被设置为评估模式,因为您正在使用模型,而不是训练它。
文本生成在 `get_completion()` 函数中。输入文本在传递给模型之前被分词。您使用 `torch.no_grad()` 上下文调用模型,以跳过梯度计算,从而节省时间和内存。模型以 `temperature=0.7` 调用,以实现平衡的创造力。模型输出需要使用分词器转换回文本。`self.model.generate()` 中的其他参数是
- `num_return_sequences=1` 用于只生成一个补全。模型可能会为同一个输入生成多个输出。
- `pad_token_id=self.tokenizer.eos_token_id` 防止不必要的填充。
- `do_sample=True` 启用采样而不是确定性文本生成。您需要它来进行创造性生成。
缓存和批量输入
上面的代码作为一个简单的程序是可行的,但是您需要进行一些完善才能将其作为服务运行。
首先,让我们实现一个缓存系统以提高实时应用程序的性能。
1 2 3 4 5 6 7 8 9 |
from functools import lru_cache class CachedAutoComplete(AutoComplete): def __init__(self, cache_size=1000, **kwargs): """初始化,支持缓存。""" super().__init__(**kwargs) self.get_completion = lru_cache(maxsize=cache_size)( self.get_completion ) |
这通过使用 LRU 缓存装饰生成函数来构建前一个类。Python 库会自动处理缓存。只需使用 `CachedAutoComplete` 而不是 `AutoComplete`,所有功能将保持不变——只是缓存会立即返回先前处理过的输入结果。
现在,让我们进一步优化系统以获得更好的实时性能。创建服务的一个挑战是同时处理多个用户,因此批量处理多个输入将很有益。然而,这会增加内存使用。您可以通过使用 16 位浮点数来减小模型大小,从而减轻额外的负载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
class OptimizedAutoComplete(CachedAutoComplete): def __init__(self, **kwargs): """使用优化进行初始化。""" super().__init__(**kwargs) self.tokenizer.pad_token = self.tokenizer.eos_token if self.device == "cuda": self.model = self.model.half() # 在 GPU 上使用 FP16 # 使用评估模式和 CUDA 图 self.model.eval() def preprocess_batch(self, texts): """高效处理多个文本。""" # 一次性对所有文本进行分词 inputs = self.tokenizer(texts, padding=True, truncation=True, return_tensors="pt") return inputs.to(self.device) def generate_batch(self, texts, max_length=50): """为多个文本生成补全。""" # 批量预处理 inputs = self.preprocess_batch(texts) # 生成补全 with torch.no_grad(): outputs = self.model.generate( inputs['input_ids'], attention_mask=inputs['attention_mask'], max_length=max_length, num_return_sequences=1, pad_token_id=self.tokenizer.eos_token_id, do_sample=True, temperature=0.7 ) # 解码补全 completions = self.tokenizer.batch_decode(outputs, skip_special_tokens=True) # 提取新文本 results = [] for text, completion in zip(texts, completions): results.append(completion[len(text):]) return results |
在构造函数中,将模型转换为 16 位浮点数就像 `self.model = self.model.half()` 一样简单。大多数 CPU 不支持 16 位浮点数。因此,只有当您可以在 GPU 上运行模型时才应该这样做。请注意,`generate_batch()` 函数与之前的 `generate()` 函数大部分相同,但您需要处理并将批量输出放入列表中。
下面是完整的代码,包括如何使用批量生成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
from functools import lru_cache from transformers import GPT2LMHeadModel, GPT2Tokenizer import torch class AutoComplete: def __init__(self, model_name="gpt2"): """初始化自动补全系统。""" self.tokenizer = GPT2Tokenizer.from_pretrained(model_name, padding_side="left") self.model = GPT2LMHeadModel.from_pretrained(model_name) self.device = "cuda" if torch.cuda.is_available() else "cpu" self.model.to(self.device) self.model.eval() # 设置为评估模式 def get_completion(self, text, max_length=50): """为输入文本生成补全。""" print("**** 补全:", text) # 编码输入文本 inputs = self.tokenizer(text, add_special_tokens=False, return_tensors="pt") input_ids = inputs["input_ids"].to(self.device) attn_masks = inputs["attention_mask"].to(self.device) # 生成补全 with torch.no_grad(): outputs = self.model.generate( input_ids, attention_mask=attn_masks, max_length=max_length, num_return_sequences=1, pad_token_id=self.tokenizer.eos_token_id, do_sample=True, temperature=0.7 ) # 解码并提取补全 full_text = self.tokenizer.decode(outputs[0], skip_special_tokens=True) completion = full_text[len(text):] return completion class CachedAutoComplete(AutoComplete): def __init__(self, cache_size=1000, **kwargs): """初始化,支持缓存。""" super().__init__(**kwargs) self.get_completion = lru_cache(maxsize=cache_size)( self.get_completion ) class OptimizedAutoComplete(CachedAutoComplete): def __init__(self, **kwargs): """使用优化进行初始化。""" super().__init__(**kwargs) self.tokenizer.pad_token = self.tokenizer.eos_token if self.device == "cuda": self.model = self.model.half() # 在 GPU 上使用 FP16 # 使用评估模式和 CUDA 图 self.model.eval() def preprocess_batch(self, texts): """高效处理多个文本。""" # 一次性对所有文本进行分词 inputs = self.tokenizer(texts, padding=True, truncation=True, return_tensors="pt") return inputs.to(self.device) def generate_batch(self, texts, max_length=50): """为多个文本生成补全。""" # 批量预处理 inputs = self.preprocess_batch(texts) # 生成补全 with torch.no_grad(): outputs = self.model.generate( inputs["input_ids"], attention_mask=inputs["attention_mask"], max_length=max_length, num_return_sequences=1, pad_token_id=self.tokenizer.eos_token_id, do_sample=True, temperature=0.7 ) # 解码补全 completions = self.tokenizer.batch_decode(outputs, skip_special_tokens=True) # 提取新文本 results = [] for text, completion in zip(texts, completions): results.append(completion[len(text):]) return results # 示例:优化后的批量补全 optimized_complete = OptimizedAutoComplete() texts = [ "机器学习是", "深度神经网络可以", "训练过程涉及" ] completions = optimized_complete.generate_batch(texts) for text, completion in zip(texts, completions): print(f"\n输入: {text}") print(f"补全: {completion}") |
总结
在本教程中,您了解了如何使用 GPT-2 构建智能自动补全系统。具体来说,您学习了
- 神经网络自动补全系统背后的理论
- 如何实现基本的自动补全
- 如何添加缓存以获得更好的性能
- 如何进行上下文感知的建议
- 如何为实时使用进行优化
代码示例已可用于生产,可用于构建自动补全应用程序。
暂无评论。