機械翻訳のデモ

この Jupyter ノートブックはオンラインで起動でき、ブラウザーのウィンドウで対話型環境を開きます。ローカルにインストールすることもできます。次のオプションのいずれかを選択します。

Binder Google Colab GitHub

このデモでは、英語からドイツ語に翻訳するインテルの事前トレーニング済みモデルを利用しています。モデルの詳細については、こちらを参照してください。

このモデルは、Hugging Face の SentecePieceBPETokenizer を使用して文をエンコードします。トークナイザーの語彙は、OMZ ツールを使用して自動的にダウンロードされます。

Inputs モデルの入力は、次の構造を持つ最大 150 個のトークンのシーケンスです: <s> + トークン化された文 + <s> + <pad> (<pad> トークンは残りの空白スペースを埋めます)。

Output 推論後、最大 200 個のトークンのシーケンスが得られます。構造は入力と同じです。

目次

# # Install requirements
%pip install -q "openvino>=2023.1.0"
%pip install -q tokenizers
DEPRECATION: pytorch-lightning 1.6.5 has a non-standard dependency specifier torch>=1.8.*. pip 24.1 will enforce this behaviour change. A possible replacement is to upgrade to a newer version of pytorch-lightning or contact the author to suggest that they release a version with a conforming dependency specifiers. Discussion can be found at https://github.com/pypa/pip/issues/12063
Note: you may need to restart the kernel to use updated packages.
DEPRECATION: pytorch-lightning 1.6.5 has a non-standard dependency specifier torch>=1.8.*. pip 24.1 will enforce this behaviour change. A possible replacement is to upgrade to a newer version of pytorch-lightning or contact the author to suggest that they release a version with a conforming dependency specifiers. Discussion can be found at https://github.com/pypa/pip/issues/12063
Note: you may need to restart the kernel to use updated packages.
import time
import sys
import openvino as ov
import numpy as np
import itertools
from pathlib import Path
from tokenizers import SentencePieceBPETokenizer

sys.path.append("../utils")
from notebook_utils import download_file

モデルのダウンロード

次のコマンドは、モデルを現在のディレクトリーにダウンロードします。事前に pip install openvino-dev を実行していることを確認してください。

base_url = "https://storage.openvinotoolkit.org/repositories/open_model_zoo/2023.0/models_bin/1"
model_name = "machine-translation-nar-en-de-0002"
precision = "FP32"
model_base_dir = Path("model")
model_base_dir.mkdir(exist_ok=True)
model_path = model_base_dir / f"{model_name}.xml"
src_tok_dir = model_base_dir / "tokenizer_src"
target_tok_dir = model_base_dir / "tokenizer_tgt"
src_tok_dir.mkdir(exist_ok=True)
target_tok_dir.mkdir(exist_ok=True)

download_file(base_url + f'/{model_name}/{precision}/{model_name}.xml', f"{model_name}.xml", model_base_dir)
download_file(base_url + f'/{model_name}/{precision}/{model_name}.bin', f"{model_name}.bin", model_base_dir)
download_file(f"{base_url}/{model_name}/tokenizer_src/merges.txt", "merges.txt", src_tok_dir)
download_file(f"{base_url}/{model_name}/tokenizer_tgt/merges.txt", "merges.txt", target_tok_dir)
download_file(f"{base_url}/{model_name}/tokenizer_src/vocab.json", "vocab.json", src_tok_dir)
download_file(f"{base_url}/{model_name}/tokenizer_tgt/vocab.json", "vocab.json", target_tok_dir);
model/machine-translation-nar-en-de-0002.xml:   0%|          | 0.00/825k [00:00<?, ?B/s]
model/machine-translation-nar-en-de-0002.bin:   0%|          | 0.00/271M [00:00<?, ?B/s]
model/tokenizer_src/merges.txt:   0%|          | 0.00/311k [00:00<?, ?B/s]
model/tokenizer_tgt/merges.txt:   0%|          | 0.00/331k [00:00<?, ?B/s]
model/tokenizer_src/vocab.json:   0%|          | 0.00/523k [00:00<?, ?B/s]
model/tokenizer_tgt/vocab.json:   0%|          | 0.00/543k [00:00<?, ?B/s]

モデルをロードして構成

モデルは、model/ フォルダーで利用できるようになりました。以下では、入力と出力をロードして構成します。

core = ov.Core()
model = core.read_model(model_path)
input_name = "tokens"
output_name = "pred"
model.output(output_name)
max_tokens = model.input(input_name).shape[1]

推論デバイスの選択

OpenVINO を使用して推論を実行するためにドロップダウン・リストからデバイスを選択します。

import ipywidgets as widgets

core = ov.Core()

device = widgets.Dropdown(
                                        options=core.available_devices + ["AUTO"],
                                        value='AUTO',
                                        description='Device:',
                                        disabled=False,
)

device
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')
compiled_model = core.compile_model(model, device.value)

トークナイザーをロード

NLP モデルは通常、トークンのリストを標準入力として受け取ります。トークンは、単一の単語を整数に変換したものです。適切な入力を提供するには、マッピングのため語彙が必要です。merges.txt を使用して、文字列を形成するトークンを調べます。 vocab.json トークンと整数間のマッピングを指定します。

入力はモデルが理解できるトークンシーケンスに変換する必要があり、出力は人間が判読できる文に変換する必要があります。

入力 src_tokenizer と出力 tgt_tokenizer のトークナイザーを初期化します。

src_tokenizer = SentencePieceBPETokenizer.from_file(
                                        str(src_tok_dir / 'vocab.json'),
                                        str(src_tok_dir / 'merges.txt')
)
tgt_tokenizer = SentencePieceBPETokenizer.from_file(
                                        str(target_tok_dir / 'vocab.json'),
                                        str(target_tok_dir / 'merges.txt')
)

翻訳を実行

次の関数は、英語の文をドイツ語に翻訳します。

def translate(sentence: str) -> str:
    """
    Tokenize the sentence using the downloaded tokenizer and run the model,
    whose output is decoded into a human readable string.

    :param sentence: a string containing the phrase to be translated
    :return: the translated string
    """
    # Remove leading and trailing white spaces
    sentence = sentence.strip()
    assert len(sentence) > 0
    tokens = src_tokenizer.encode(sentence).ids
    # Transform the tokenized sentence into the model's input format
    tokens = [src_tokenizer.token_to_id('<s>')] + \
        tokens + [src_tokenizer.token_to_id('</s>')]
    pad_length = max_tokens - len(tokens)

    # If the sentence size is less than the maximum allowed tokens,
    # fill the remaining tokens with '<pad>'.
    if pad_length > 0:
        tokens = tokens + [src_tokenizer.token_to_id('<pad>')] * pad_length
    assert len(tokens) == max_tokens, "input sentence is too long"
    encoded_sentence = np.array(tokens).reshape(1, -1)

    # Perform inference
    enc_translated = compiled_model({input_name: encoded_sentence})
    output_key = compiled_model.output(output_name)
    enc_translated = enc_translated[output_key][0]

    # Decode the sentence
    sentence = tgt_tokenizer.decode(enc_translated)

    # Remove <pad> tokens, as well as '<s>' and '</s>' tokens which mark the
    # beginning and ending of the sentence.
    for s in ['</s>', '<s>', '<pad>']:
        sentence = sentence.replace(s, '')

    # Transform sentence into lower case and join words by a white space
    sentence = sentence.lower().split()
    sentence = " ".join(key for key, _ in itertools.groupby(sentence))
    return sentence

文を翻訳

次の関数は、文章を翻訳する基本ループです。

def run_translator():
    """
    Run the translation in real time, reading the input from the user.
    This function prints the translated sentence and the time
    spent during inference.
    :return:
    """
    while True:
        input_sentence = input()
        if input_sentence == "":
            break

        start_time = time.perf_counter()
        translated = translate(input_sentence)
        end_time = time.perf_counter()
        print(f'Translated: {translated}')
        print(f'Time: {end_time - start_time:.2f}s')
# uncomment the following line for a real time translation of your input
# run_translator()

翻訳をテスト

英文を含む次のセルを実行すると、ドイツ語に翻訳されます

sentence = "My name is openvino"
print(f'Translated: {translate(sentence)}')
Translated: mein name ist openvino.