一、介绍
神经机器翻译 (NMT) 是一种深度学习技术,它使用人工神经网络将单词从一种语言翻译成另一种语言。本指南演示了如何使用 Tensorflow 构建基于注意力的序列到序列 (seq2seq) 模型,该模型在 Vultr 云 GPU 上将用户对不和谐的输入从英语转换为西班牙语。
二、模型概述
seq2seq 模型包括三个块;编码器、隐藏矢量和解码器。编码器将输入转换为一维向量(隐藏向量),而解码器将隐藏向量转换为输出序列。例如,给定句子“会议是几点”,编码器将句子转换为浮点值向量,每个浮点值表示句子上的一个单词。然后,解码器将矢量转换为西班牙语输出序列A quã© hora es la reuniãn。
该模型适用于短句;但是,当提供较长的输入时,模型表现不佳。性能下降来自将较长的输入压缩到固定向量中。为了应对这一挑战,使用了注意力机制。注意力允许模型专注于输入序列中最重要的部分。编码器一次接收一个单词,直到句子结束(<EOS>)并输出隐藏的句子表示。解码器从句子开头开始生成目标句子 (<BOS>)。解码器可以通过注意力机制生成的上下文访问源句子。解码器使用最相关的单词来预测下一个单词。解码器始终可以访问输入序列。
三、准备工作
- 创建一个不和谐的帐户..
- 部署在 Ubuntu 22.04 上运行的 Vultr Cloud GPU。
- 更新 Ubuntu 服务器。
- 创建一个具有 sudo 访问权限的非 root 用户并使用 SSH 登录。
四、环境设置
- 以非根用户身份通过 SSH 连接到 Ubuntu Cloud GPU 服务器。
- 创建项目文件夹并导航到该文件夹。
$ mkdir discordTF && cd discordTF
- 安装虚拟环境。
$ sudo apt install virtualenv
- 创建一个名为 env 的虚拟环境。
$ python3 -m virtualenv env
- 激活虚拟环境。
$ source env/bin/activate
- 使用纳米创建。
requirements.txt
$ nano requirements.txt
- 粘贴以下依赖项,然后保存并退出文件。
tensorflow==2.11.0 tensorflow-text==2.11.0 einops==0.6.0 requests==2.23.0 numpy==1.21.6
- 安装依赖项。
$ pip install -r requirements.txt
五、数据预处理
Anki成对提供双语数据集。本指南使用英语和西班牙语对。这些对的形式是:
Be brave. Sé fuerte. CC-BY 2.0 (France) Attribution: tatoeba.org #4054941 (CK) & #5707804 (arh)
在预处理步骤中,我们需要将数据转换为:
Be brave. Sé fuerte.
以下是步骤:
- 创建数据文件夹。
$ mkdir data
- 创建文件 。
preprocessing.py
$ nano preprocessing.py
- 将下面的代码复制到 ,然后保存并退出文件。
preprocessing.py
import requests import os from zipfile import ZipFile import numpy as np import tensorflow as tf import tensorflow_text as tf_text class PreprocessingAndStandardization: def __init__(self, url): self.url = url self.path_to_file = '' self.english_raw_data = None self.spanish_raw_data = None self.training_raw_dataset=None self.validation_raw_dataset=None self.training_processed_dataset=None self.validation_processed_dataset=None self.english_text_processor=None self.spanish_text_processor=None def download_zip(self): headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'} request = requests.get(self.url, stream=True, headers=headers) zip_filename = os.path.basename(self.url) with open(zip_filename, 'wb') as zfile: zfile.write(request.content) with ZipFile(zip_filename, 'r') as f: f.extractall('data') self.path_to_file='data/spa.txt' os.remove(zip_filename) def load_data(self): with open(self.path_to_file) as f: lines = f.readlines() language_pairs = [line.split('\t')[0:2] for line in lines] self.spanish_raw_data = np.array([spanish for english, spanish in language_pairs]) self.english_raw_data = np.array([english for english, spanish in language_pairs]) def create_tf_dataset(self): BUFFER_SIZE = len(self.english_raw_data) BATCH_SIZE = 64 is_train = np.random.uniform(size=(len(self.english_raw_data),)) < 0.8 self.training_raw_dataset = ( tf.data.Dataset .from_tensor_slices((self.english_raw_data[is_train], self.spanish_raw_data[is_train])) .shuffle(BUFFER_SIZE) .batch(BATCH_SIZE)) self.validation_raw_dataset =( tf.data.Dataset .from_tensor_slices((self.english_raw_data[~is_train], self.spanish_raw_data[~is_train])) .shuffle(BUFFER_SIZE) .batch(BATCH_SIZE)) def normalization_of_data(self, text_to_normalize): text_to_normalize = tf_text.normalize_utf8(text_to_normalize, 'NFKD') text_to_normalize = tf.strings.lower(text_to_normalize) text_to_normalize = tf.strings.regex_replace(text_to_normalize, '[^ a-z.?!,¿]', '') text_to_normalize = tf.strings.regex_replace(text_to_normalize, '[.?!¡,¿]', r' \0 ') text_to_normalize = tf.strings.strip(text_to_normalize) text_to_normalize = tf.strings.join(['[MWANZO]', text_to_normalize, '[MWISHO]'], separator=' ') return text_to_normalize def text_vectorization(self) : max_vocab_size = 10000 self.english_text_processor = tf.keras.layers.TextVectorization( standardize = self.normalization_of_data, max_tokens=max_vocab_size, ragged=True) self.english_text_processor.adapt(self.training_raw_dataset.map(lambda english, spanish: english)) self.spanish_text_processor = tf.keras.layers.TextVectorization( standardize= self.normalization_of_data, max_tokens=max_vocab_size, ragged=True) self.spanish_text_processor.adapt(self.training_raw_dataset.map(lambda english, spanish: spanish)) def text_preprocessing(self,english_context,spanish_target ): english_context = self.english_text_processor(english_context).to_tensor() spanish_target = self.spanish_text_processor(spanish_target) spanish_target_in = spanish_target[:,:-1].to_tensor() spanish_target_out = spanish_target[:,1:].to_tensor() return (english_context, spanish_target_in), spanish_target_out def process_dataset(self): self.training_processed_dataset=self.training_raw_dataset.map(self.text_preprocessing, tf.data.AUTOTUNE) self.validation_processed_dataset=self.validation_raw_dataset.map(self.text_preprocessing, tf.data.AUTOTUNE) def __call__(self): self.download_zip() self.load_data() self.create_tf_dataset() self.text_vectorization() self.process_dataset() return (self.english_text_processor, self.spanish_text_processor , self.training_processed_dataset, self.validation_processed_dataset)
代码细分如下:
- 导入相关模块。
- 在类中,构造函数接受一个 URL。该 URL 将是用于从 Anki 下载 zip 文件的路径。
- 构造函数启动以下变量:
- Self.url – 分配给类初始化时传递的 URL。
- Self.path_to_file – 将保存从 Anki 下载的数据集的路径。
- Self.english_raw_data – 将存储提取的英语短语。
- Self.spanish_raw_data – 将存储提取的西班牙语短语。
- Self.trainingraw_dataset – 将保存原始训练集。
- Self.validation_raw_dataset – 将保存原始验证集。
- Self.training_processed_dataset – 将保存已处理的训练集。
- Self.validation_processed_dataset – 将保存已处理的验证集。
- Self.english_text_processor 和 self.spanish_text_processor – 将分别保存英语和西班牙语的 Keras 文本矢量化层。矢量化层是将文本要素映射到整数序列的预处理层。
- download_zip函数发送一个 get 请求,该请求下载包含数据集的 zip 文件。将提取 zip,并将内容作为 保存到数据文件夹中。随后,将从系统中删除 zip 文件。
spa.txt
- load_data函数打开包含双语对的文本文件,并逐行拆分对。所有西班牙语句子都保存在spanish_raw_data,而英语保存在english_raw_data中。
- create_tf_dataset将原始数据转换为张量。数据分为两部分;20% 用于验证,80% 用于培训。
- normalization_of_data按以下顺序规范化文本:
- 将文本转换为 utf8 格式。
- 将所有字符转换为小写。
- 保留选定的标点符号 (.?!,â¿)、a 到 z 和空格。
- 在标点符号周围添加空格。
- 删除空格。
- 将<SOS>和<EOS>添加到短语中。
在这种情况下,我们使用既不是西班牙语也不是英语的单词。[MWANZO]担任<SOS>,而[MWISHO]担任<EOS>。
¿Por qué no? # <-- Original sentence [MWANZO] ¿Por que no? [MWISHO] # <-- final sentence
- text_vectorization将文本要素映射到最大令牌大小为 10,000 的整数序列。矢量化层具有自适应方法,该方法读取训练数据的一个纪元,并根据该数据初始化该层以确定词汇表。矢量化是针对英语和西班牙语训练数据集完成的。
- text_preprocessing函数将数据转换为令牌 ID 的零填充张量。Keras.Model.fit接受(输入,标签)对;因此,输入被转换为(english_context,spanish_target_in),spanish_target_out。english_context spanish_target_in)是输入,spanish_target_out是标签。spanish_target_out和spanish_target_in之间的区别在于它们移动了一步。
- process_dataset将text_preprocessing函数应用于训练和验证数据集。
[2 9 1512 10 136 883 7 137 148 4] <-- spanish\_target\_in [9 1512 10 136 883 7 137 148 4 3] <-- spanish\_target\_out
- __call__ 方法将所有内容绑定在一起,并返回english_text_processor、spanish_text_processor、training_processed_dataset和validation_processed_dataset。
六、实用
实用程序是跨其他模块使用的函数。
- 创建一个实用程序文件 。
utils.py
$ nano utils.py
- 将以下代码复制到 ,然后保存并退出文件。
utils.py
import tensorflow as tf import einops class ShapeChecker(): def __init__(self): self.shapes = {} def __call__(self, tensor, names, broadcast=False): if not tf.executing_eagerly(): return parsed = einops.parse_shape(tensor, names) for name, new_dim in parsed.items(): old_dim = self.shapes.get(name, None) if (broadcast and new_dim == 1): continue if old_dim is None: self.shapes[name] = new_dim continue if new_dim != old_dim: raise ValueError(f"Shape mismatch for dimension: '{name}'\n" f" found: {new_dim}\n" f" expected: {old_dim}\n") def masked_loss(y_true, y_pred): loss_fn = tf.keras.losses.SparseCategoricalCrossentropy( from_logits=True, reduction='none') loss = loss_fn(y_true, y_pred) mask = tf.cast(y_true != 0, loss.dtype) loss *= mask return tf.reduce_sum(loss)/tf.reduce_sum(mask) def masked_acc(y_true, y_pred): y_pred = tf.argmax(y_pred, axis=-1) y_pred = tf.cast(y_pred, y_true.dtype) match = tf.cast(y_true == y_pred, tf.float32) mask = tf.cast(y_true != 0, tf.float32) return tf.reduce_sum(match)/tf.reduce_sum(mask)
遍历实用程序代码:
- ShapeChecker 类将用于检查张量的形状,并在出现形状错误的张量时抛出错误。
- masked_loss和masked_acc是模型训练期间使用的指标函数。他们计算批次中每个项目的损失,并在填充物上计算损失。
七、编码器
编码器会将英语上下文序列处理为英语序列向量,以供解码器使用。在处理过程中将使用双向RNN,因为编码器内的信息流没有任何限制。
- 创建编码器文件 。
encoder.py
$ nano encoder.py
- 将下面的编码器类粘贴到 ,然后保存并退出文件。
encoder.py
import tensorflow as tf from utils import ShapeChecker class Encoder(tf.keras.layers.Layer): def __init__(self, text_processor, units): super(Encoder, self).__init__() self.text_processor = text_processor self.vocab_size = text_processor.vocabulary_size() self.units = units self.embedding = tf.keras.layers.Embedding(self.vocab_size, units, mask_zero=True) self.rnn = tf.keras.layers.Bidirectional( merge_mode='sum', layer=tf.keras.layers.GRU(units, return_sequences=True, recurrent_initializer='glorot_uniform')) def __call__(self, x): shape_checker = ShapeChecker() shape_checker(x, 'batch s') x = self.embedding(x) shape_checker(x, 'batch s units') x = self.rnn(x) shape_checker(x, 'batch s units') return x #inference helper function used in translation.py def convert_input(self, texts): texts = tf.convert_to_tensor(texts) if len(texts.shape) == 0: texts = tf.convert_to_tensor(texts)[tf.newaxis] context = self.text_processor(texts).to_tensor() context = self(context) return context
编码器类执行以下操作:
- 构造函数采用确定输出空间维度的text_processor和单元。词汇大小派生自text_processor。
- 令牌将转换为嵌入层上的向量。
- 双向RNN层按顺序处理向量。
- 嵌入层查找调用函数上每个标记的嵌入向量。
- GRU(门控循环单元)处理嵌入序列。
- 返回要传递给注意机制的已处理序列。
- 翻译过程中将使用convert_input将输入文本处理到上下文中。
八、添加
注意力层从英语上下文序列计算向量,并添加到解码器的输出中。该机制为解码器提供编码器提取的信息。
- 创建一个文件 。
attention.py
$ nano attention.py
- 将下面的代码复制到 ,然后保存并退出文件。
attention.py
import tensorflow as tf from utils import ShapeChecker class CrossAttention(tf.keras.layers.Layer): def __init__(self, units, **kwargs): super().__init__() self.mha = tf.keras.layers.MultiHeadAttention(key_dim=units, num_heads=1, **kwargs) self.layernorm = tf.keras.layers.LayerNormalization() self.add = tf.keras.layers.Add() def __call__(self, x, context): shape_checker = ShapeChecker() shape_checker(x, 'batch t units') shape_checker(context, 'batch s units') attention_output, attention_scores = self.mha( query=x, value=context, return_attention_scores=True) shape_checker(x, 'batch t units') shape_checker(attention_scores, 'batch heads t s') attention_scores = tf.reduce_mean(attention_scores, axis=1) shape_checker(attention_scores, 'batch t s') self.last_attention_weights = attention_scores x = self.add([x, attention_output]) x = self.layernorm(x) return x
九、译码器
解码器在西班牙语目标序列中的每个位置生成下一个令牌的预测。解码器通过以下方式执行此操作:
- 查找英语目标序列中每个标记的嵌入。
- 使用 RNN 处理英文目标序列并跟踪当前生成的令牌。
- 通过使用 RNN 输出作为对注意力层的查询来关注编码器输出。
- 在每个位置,解码器预测下一个令牌。因此,解码器存在单向信息流。因此,使用单向RNN来处理英语目标序列。
- 创建文件 。
decoder.py
$ nano decoder.py
- 将下面的代码复制到 ,然后保存并退出文件。
decoder.py
import tensorflow as tf from utils import ShapeChecker from attention import CrossAttention class Decoder(tf.keras.layers.Layer): def __init__(self, text_processor, units): super(Decoder, self).__init__() self.text_processor = text_processor self.vocab_size = text_processor.vocabulary_size() self.word_to_id = tf.keras.layers.StringLookup( vocabulary=text_processor.get_vocabulary(), mask_token='', oov_token='[UNK]') self.id_to_word = tf.keras.layers.StringLookup( vocabulary=text_processor.get_vocabulary(), mask_token='', oov_token='[UNK]', invert=True) self.start_token = self.word_to_id('[MWANZO]') self.end_token = self.word_to_id('[MWISHO]') self.units = units self.embedding = tf.keras.layers.Embedding(self.vocab_size, units, mask_zero=True) self.rnn = tf.keras.layers.GRU(units, return_sequences=True, return_state=True, recurrent_initializer='glorot_uniform') self.attention = CrossAttention(units) self.output_layer = tf.keras.layers.Dense(self.vocab_size) def __call__(self, context, target_seq_input, state=None, return_state=False): shape_checker = ShapeChecker() shape_checker(target_seq_input, 'batch t') shape_checker(context, 'batch s units') target_seq_input = self.embedding(target_seq_input) shape_checker(target_seq_input, 'batch t units') target_seq_input, state = self.rnn(target_seq_input, initial_state=state) shape_checker(target_seq_input, 'batch t units') target_seq_input = self.attention(target_seq_input, context) self.last_attention_weights = self.attention.last_attention_weights shape_checker(target_seq_input, 'batch t units') shape_checker(self.last_attention_weights, 'batch t s') logits = self.output_layer(target_seq_input) shape_checker(logits, 'batch t target_vocab_size') if return_state: return logits, state else: return logits ## inference methods for later def get_initial_state(self, context): batch_size = tf.shape(context)[0] start_tokens = tf.fill([batch_size, 1], self.start_token) done = tf.zeros([batch_size, 1], dtype=tf.bool) embedded = self.embedding(start_tokens) return start_tokens, done, self.rnn.get_initial_state(embedded)[0] def tokens_to_text(self, tokens): words = self.id_to_word(tokens) result = tf.strings.reduce_join(words, axis=-1, separator=' ') result = tf.strings.regex_replace(result, '^ *\[MWANZO\] *', '') result = tf.strings.regex_replace(result, ' *\[MWISHO\] *$', '') return result def get_next_token(self, context, next_token, done, state, temperature = 0.0): logits, state = self( context, next_token, state = state, return_state=True) if temperature == 0.0: next_token = tf.argmax(logits, axis=-1) else: logits = logits[:, -1, :]/temperature next_token = tf.random.categorical(logits, num_samples=1) done = done | (next_token == self.end_token) next_token = tf.where(done, tf.constant(0, dtype=tf.int64), next_token) return next_token, done, state
解码器类执行以下操作:
- 构造函数接受文本处理器和单元数。词汇大小派生自text_processor。
- word_to_id和id_to_word Keras 层被初始化并用于将<SOS>和<EOS>转换为令牌。
- 嵌入层将令牌 ID 转换为矢量。
- 单向 RNN 跟踪当前生成的单元。
- RNN 输出成为从 attention.py 导入的注意力层的查询。
- 调用方法接受四个参数;编码器输出中的上下文、英语目标序列输入、状态和返回状态。
- 我们在调用方法中查找嵌入,然后使用 RNN 处理目标序列。RNN 输出用作对上下文的注意力查询。返回下一个令牌的 Logit 预测。
- 其余方法将在推理阶段使用。
十、在线翻译
现在,所有组件都已准备好训练模型,请将它们组合在一起以生成模型。
- 创建文件 。
translator.py
$ nano translator.py
- 将下面的代码复制到 ,然后保存并退出文件。
translator.py
import numpy as np import tensorflow as tf import einops from encoder import Encoder from decoder import Decoder from utils import ShapeChecker class Translator(tf.keras.Model): def __init__(self, units, context_text_processor, target_text_processor): super().__init__() encoder = Encoder(context_text_processor, units) decoder = Decoder(target_text_processor, units) self.encoder = encoder self.decoder = decoder def translate(self, texts, *, max_length=500, temperature=tf.constant(0.0)): shape_checker = ShapeChecker() context = self.encoder.convert_input(texts) batch_size = tf.shape(context)[0] shape_checker(context, 'batch s units') next_token, done, state = self.decoder.get_initial_state(context) tokens = tf.TensorArray(tf.int64, size=1, dynamic_size=True) for t in tf.range(max_length): next_token, done, state = self.decoder.get_next_token( context, next_token, done, state, temperature) shape_checker(next_token, 'batch t1') tokens = tokens.write(t, next_token) if tf.reduce_all(done): break tokens = tokens.stack() shape_checker(tokens, 't batch t1') tokens = einops.rearrange(tokens, 't batch 1 -> batch t') shape_checker(tokens, 'batch t') text = self.decoder.tokens_to_text(tokens) shape_checker(text, 'batch') return text def __call__(self, inputs,training=False): context, x = inputs context = self.encoder(context) logits = self.decoder(context, x) return logits
转换器返回用于训练模型的日志。
十一、导出模型
- 创建一个用于存储保存模型的目录,并在其中创建一个子文件夹。我们需要这样做作为对模型进行版本控制的一种方式。Tensorflow 服务还要求文件夹结构在创建 REST API 以查询模型时包含编号文件夹。Tensorflow服务将为最新的模型提供服务,因此需要对模型进行版本控制。
1
$ mkdir models && cd models
- 在模型文件夹中创建一个子文件夹。
$ mkdir englishtospanish && cd englishtospanish
- 在英语到西班牙语文件夹中创建一个子文件夹,然后导航回项目目录。
$ mkdir 1
- 导航到项目文件夹根目录。
$ cd .. && cd ..
- 创建一个文件 。
index.py
$ nano index.py
- 将下面的代码粘贴到 .它包含将所有不同组件组合在一起并导出准备用于推理的模型的所有代码。
index.py
from decoder import Decoder import tensorflow as tf from preprocessing import PreprocessingAndStandardization from utils import ShapeChecker,masked_loss,masked_acc from translator import Translator from encoder import Encoder from attention import CrossAttention UNITS=256 def run_example(url): (english_text_processor,spanish_text_processor,training_processed_dataset,validation_processed_dataset) = PreprocessingAndStandardization(url)() for (ex_context_tok, ex_tar_in), ex_tar_out in training_processed_dataset.take(1): print('+'*1000) print(ex_context_tok[0, :10].numpy()) print() print(ex_tar_in[0, :10].numpy()) print(ex_tar_out[0, :10].numpy()) # Encode the input sequence. encoder = Encoder(english_text_processor, UNITS) ex_context = encoder(ex_context_tok) print(f'Context tokens, shape (batch, s): {ex_context_tok.shape}') print(f'Encoder output, shape (batch, s, units): {ex_context.shape}') attention_layer = CrossAttention(UNITS) # Attend to the encoded tokens embed = tf.keras.layers.Embedding(spanish_text_processor.vocabulary_size(), output_dim=UNITS, mask_zero=True) ex_tar_embed = embed(ex_tar_in) result = attention_layer(ex_tar_embed, ex_context) print(f'Context sequence, shape (batch, s, units): {ex_context.shape}') print(f'Target sequence, shape (batch, t, units): {ex_tar_embed.shape}') print(f'Attention result, shape (batch, t, units): {result.shape}') print(f'Attention weights, shape (batch, t, s): {attention_layer.last_attention_weights.shape}') print('Sum ---> ',attention_layer.last_attention_weights[0].numpy().sum(axis=-1)) attention_weights = attention_layer.last_attention_weights print('afteer attention weight') decoder = Decoder(spanish_text_processor, UNITS) model = Translator(UNITS, english_text_processor, spanish_text_processor) model.compile(optimizer='adam', loss=masked_loss, metrics=[masked_acc, masked_loss]) vocab_size = 1.0 * spanish_text_processor.vocabulary_size() {"expected_loss": tf.math.log(vocab_size).numpy(), "expected_acc": 1/vocab_size} model.evaluate(validation_processed_dataset, steps=20, return_dict=True) model.fit(training_processed_dataset.repeat(), epochs=50, steps_per_epoch = 100, validation_data=validation_processed_dataset, validation_steps = 50, callbacks=[ tf.keras.callbacks.EarlyStopping(patience=3)]) inputs = [ "Its really cold here.",# 'Hace mucho frio aqui.', "This is my life." , # 'Esta es mi vida.', "His room is a mess.", #'Su cuarto es un desastre.' # ] for t in inputs: print(model.translate([t])[0].numpy().decode()) print() result = model.translate(inputs) print(result[0].numpy().decode()) print(result[1].numpy().decode()) print(result[2].numpy().decode()) print() return model url = 'http://www.manythings.org/anki/spa-eng.zip' model = run_example(url) print('0'*100) class Export(tf.Module): def __init__(self, model): self.model = model @tf.function(input_signature=[tf.TensorSpec(dtype=tf.string, shape=[None])]) def translate(self, inputs): return self.model.translate(inputs) export = Export(model) tf.saved_model.save(export, 'models/englishtospanish/1', signatures={'serving_default': export.translate})
- Run the file.
index.py
$ python3 index.py
The output should be:
Epoch 38/50 100/100 [==============================] - 90s 899ms/step - loss: 0.9116 - masked_acc: 0.7811 - masked_loss: 0.9132 - val_loss: 1.2271 - val_masked_acc: 0.7397 - val_masked_loss: 1.2285 Epoch 39/50 100/100 [==============================] - 89s 887ms/step - loss: 0.9140 - masked_acc: 0.7828 - masked_loss: 0.9150 - val_loss: 1.2303 - val_masked_acc: 0.7397 - val_masked_loss: 1.2316 aqui hace mucho frio . esta es mi vida . su habitacion es un quilombo .
十二、使用 Docker 的 Tensorflow Serve
- 安装 Docker 并运行 hello world 示例,以确保 Docker 正确安装和运行。
- 使用 Tensorflow 服务创建用于推理的 REST 端点。
$ docker run -t -d --rm -p 8501:8501 -v ~/discordTF/models/englishtospanish:/models/englishtospanish -e MODEL_NAME=englishtospanish tensorflow/serving
- 通过使用 curl 发送 POST 请求来测试端口 8501 上是否提供模型。
$ curl -d '{"inputs": ["This is an english sentence"]}' -X POST http://localhost:8501/v1/models/englishtospanish:predict -H Content-Type:Application/Json
- 翻译将打印在终端上。
{ "outputs": [ "es una sentencia de ingles . " ] }
十三、其他
- 安装 Discord Python 模块并使用 pip。
python-dotenv
$ pip install discord.py python-dotenv
- 创建 .env 文件
$ nano .env
- 在您的浏览器上,导航到 Discord 的应用程序页面。
- 通过单击“新建应用程序”按钮并为应用程序命名来创建新应用程序。
- 导航到“机器人”选项卡,单击“构建机器人”下的“添加机器人”按钮,然后在弹出窗口中单击“是”。此外,允许权限网关意图部分下的消息意图权限。
- 复制令牌并将其粘贴到带有密钥令牌的环境上。如果看不到令牌,请单击重置按钮。
TOKEN=paste_token_here
- 返回 Discord 页面,在 OAuth2 选项卡上,单击 URL 生成器。
- 向机器人授予所有文本权限、“管理服务器”、“管理频道”和“管理事件”权限。
- 复制生成的 URL 并在新选项卡上打开它。
- 将机器人添加到要使用它的服务器。
- 在您的 Discord 服务器上,创建两个新频道;英语和西班牙语。默认情况下,Discord 机器人会收听来自所有频道的消息。
- 创建一个文件 。
app.py
$ nano app.py
- 将下面的代码添加到 ,然后保存并退出文件。
app.py
import requests import json import os from dotenv import load_dotenv import discord load_dotenv() TOKEN= os.getenv("TOKEN") intents = discord.Intents.default() intents.message_content = True client = discord.Client(intents=intents) @client.event async def on_ready(): print(f'We have logged in as {client.user}') @client.event async def on_message(message): if message.author == client.user: return if message.content.startswith('$translate'): spanish_channel = discord.utils.get(client.get_all_channels(), name='spanish') message = message.content.replace('$translate ', '') data = json.dumps({"inputs": [message]}) url ="http://localhost:8501/v1/models/englishtospanish:predict" response = requests.post(url, data=data ,headers={"Content-Type":"Application/Json"}) spanish_translation = json.loads(response.text)['outputs'][0] await spanish_channel.send(spanish_translation) client.run(TOKEN)
上面的代码使用生成的令牌与我们的 discord 服务器建立连接。Discord 机器人侦听所有以 $translate 开头的通道上的消息,并对 Docker 上的运行模型进行 API 调用。翻译后的文本被推送到西班牙语频道。
- 使用以下方法运行代码:
$ python3 app.py
输出应类似于:
2022-12-07 04:15:08 INFO discord.client logging in using static token 2022-12-07 04:15:09 INFO discord.gateway Shard ID None has connected to Gateway (Session ID: 7915162e394cc1eab4e9fb5d5243c94d). We have logged in as englishToSpanishTFBot#8393
- 前往 Discord 服务器并通过发送以 $translate开头的英文消息与机器人互动。机器人将在#spanish频道上回复西班牙语翻译。