機械翻訳APIコンテナの実装
動機
ローカルで動くLLMがほしいなと思って、いろいろと調べていました。
しかし、軽量のLLMは日本語が怪しくなるという話をよく聞きます
そこで、一度機械翻訳を間に入れて、
日本語指示 → 機械翻訳 → 英語指示 → LLM→英語結果 → 機械翻訳 → 日本語結果
としたいと思います。
(実際に、機械翻訳を入れたときの性能がどうなるかは要検証です)
機械翻訳について調べてみると、気軽に使えるAPIでは、DeepLもしくはGoogle Translateが最も性能が良いようです。時点で、MicrosoftのTranslate。
また、LLMを使った機械翻訳も研究されているようですが、DeepLのほうが性能が良さそうです
上記のLLMの構成で呼び出す機械翻訳部分をローカルのAPIサーバーを経由することにしました。
そして、ローカルのAPIサーバーの中で、機械翻訳の種類を選択できるようにします
また、ローカルのAPIとしてサーバーを立てることで、LLM以外の用途にも利用できるようにしておきます
この実装では以下の種類の機械翻訳を扱えるようになりました
- DeepL
- Google Translate
- Argos Translate
ディレクトリ構成
Dev Containerで開発しています。そうじゃない人には、.devcontainerは必要ないです
.
├── .devcontainer
│ ├── Dockerfile
│ ├── devcontainer.json
│ └── docker-compose.yml
├── app
│ ├── __init__.py
│ ├── argostoranslate_bind.py
│ ├── main.py
│ └── other_api_bind.py
└── client.py
.devcontainer ディレクトリ
名前付けとかはお好きなものをどうぞ
{
"name": "python-translator-api-docker",
"service": "translator-api",
"dockerComposeFile": "docker-compose.yml",
"remoteUser": "vscode",
"workspaceFolder": "/work",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python"
]
}
}
}
ポートは8282につながるようにしました。好きなポートでどうぞ
services:
translator-api:
container_name: 'python-translator-api-container'
hostname: 'python-translator-api-container'
build: .
restart: always
working_dir: '/work'
tty: true
volumes:
- type: bind
source: ..
target: /work
ports:
- 8282:8000
pythonのスリムをベースにしました。
fastapiを使ってapiサーバーにします
FROM python:3.10-slim
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm
RUN apt-get update \
&& groupadd --gid $USER_GID $USERNAME \
&& useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME \
&& apt-get install -y sudo \
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
&& chmod 0440 /etc/sudoers.d/$USERNAME \
&& apt-get -y install locales \
&& localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
RUN apt-get -y install git
RUN pip install -U pip &&\
pip install --no-cache-dir fastapi uvicorn requests
RUN pip install --no-cache-dir googletrans==4.0.0-rc1 argostranslate
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
DeepLだけ利用するならgoogletransとargostranslateはいらない
argostranslateに5.0GBくらい容量を割かれました。
pytorchとかcudnnとかも内包してる機械学習モデルを利用した機械翻訳エンジンなので、まぁ妥当か?
ほかのエンジンを利用したい場合には、その都度拡張してください
app ディレクトリ
__init__.py
は空のファイルです。pythonがディレクトリをモジュールとして認識するためのマーカーですね。早くこの仕様なくなってほしい
メインのapiの実装はfastapiの記法に任せてます。fastapiとかflaskとかを使うと、こんなに短いコードでapiサーバーの処理が書ける時代になったんだなーって毎回思います。
from fastapi import FastAPI
from pydantic import BaseModel
import app.other_api_bind as other_api
import app.argostoranslate_bind as argostoranslate_bind
class Info(BaseModel):
translator_type: str
source_lang: str
target_lang: str
text: str
app = FastAPI()
@app.post('/api/translate')
async def translate(info: Info):
ret_text = ''
if info.translator_type == 'DeepL':
ret_text = other_api.deepL_free_requests(info.source_lang, info.target_lang, info.text)
if info.translator_type == 'Googletrans_py':
ret_text = other_api.googletrans_requests(info.source_lang, info.target_lang, info.text)
if info.translator_type == 'Argos_translate':
ret_text = argostoranslate_bind.call_argostranslate(info.source_lang, info.target_lang, info.text)
ret = {
'text':ret_text
}
return ret
DeepLとかの外部apiを呼び出す処理です。他のapiを利用したい場合はここに追記すればヨロシ
DeepLを利用したい場合は、apiキーを取得して、プログラム中のdeepl_api_keyを変更してください
また、DeepLの無料版と有料版でapiのURLが違うので注意。以下のコードのURLは無料版のもの
google翻訳は、googletransというライブラリを利用した。本当は、Google CloudにCloud Translationというapiがあるのだけど、登録が面倒でこれを使いました。
googletransは文字数制約が厳し目なのと、バージョン指定をしないとまともに動かないのが欠点。
import requests
import json
deepl_api_key = "xxx"
deepl_api_url = "https://api-free.deepl.com/v2/translate"
def deepL_free_requests(source_lang: str, target_lang: str, text: str):
post_data = {
"auth_key":deepl_api_key,
"source_lang":source_lang,
"target_lang":target_lang,
"text":text
}
ret = requests.post(deepl_api_url, data=post_data)
if ret.status_code == requests.codes.ok:
ret = ret.json()
return ret["translations"][0]["text"]
else:
return f'error : status_code={ret.status_code}'
from googletrans import Translator
googletrans_param_dict = {
'JA':'ja',
'EN':'en'
}
def googletrans_requests(source_lang: str, target_lang: str, text: str):
translator = Translator()
ret = translator.translate(text, dest=googletrans_param_dict[target_lang], src=googletrans_param_dict[source_lang])
return ret.text
argostransを呼び出すプログラム。公式のサンプルだと、一方通行の翻訳パッケージしかダウンロードしてくれないので、そこを書き換えました
argostransも短いコードで書けるし、短文の翻訳ならDeepLやGoogle翻訳の代替にはなりそう。
import argostranslate.package
import argostranslate.translate
from_code = "ja"
to_code = "en"
def check_download_package(package):
if package.from_code == to_code and package.to_code == from_code:
return True
if package.from_code == from_code and package.to_code == to_code:
return True
return False
# Download and install Argos Translate package
argostranslate.package.update_package_index()
available_packages = argostranslate.package.get_available_packages()
for package in available_packages:
if check_download_package(package):
argostranslate.package.install_from_path(package.download())
argostranslate_param_dict = {
'JA':'ja',
'EN':'en'
}
def call_argostranslate(source_lang: str, target_lang: str, text: str):
ret = argostranslate.translate.translate(text, argostranslate_param_dict[source_lang], argostranslate_param_dict[target_lang])
return ret
client.py
テストするためのサンプルプログラムです
translator_typeを
- "DeepL"
- "Googletrans_py"
- "Argos_translate"
で指定して、テストしてみてください。DeepLは月ごとの使用文字数制限があるのでテストしすぎないようにしてください。
urlのlocalhostはサーバーのipアドレスを指定してください
import json
import requests
import pprint
url = "http://localhost:8282/api/translate"
translator_type = "Googletrans_py"
test_info = {
"translator_type":translator_type,
"source_lang":"EN",
"target_lang":"JA",
"text":"this is test message"
}
print("post text")
pprint.pprint(test_info)
ret = requests.post(url, json.dumps(test_info))
ret_info = json.loads(ret.text)
print("respons text")
pprint.pprint(ret_info)
test_info = {
"translator_type":translator_type,
"source_lang":"JA",
"target_lang":"EN",
"text":"これはテストメッセージです"
}
print("post text")
pprint.pprint(test_info)
ret = requests.post(url, json.dumps(test_info))
ret_info = json.loads(ret.text)
print("respons text")
pprint.pprint(ret_info)
成功すれば以下のような出力が帰ってきます
post text
{'source_lang': 'EN',
'target_lang': 'JA',
'text': 'this is test message',
'translator_type': 'Googletrans_py'}
respons text
{'text': 'これはテストメッセージです'}
post text
{'source_lang': 'JA',
'target_lang': 'EN',
'text': 'これはテストメッセージです',
'translator_type': 'Googletrans_py'}
respons text
{'text': 'This is a test message'}
Discussion