いつもの読み飛ばしポイント
どうも限界派遣SESのnikawamikanです。
アドカレもあと5日で終わりますね。25記事書くの頑張ります。
ちなみに本日も遅れています。
本日はクソアプリを作っていこうと思います。
逆翻訳ってクソおもろい
逆翻訳といえば何を思い浮かべますか?
私はバイヤー高橋さんです。
くっそ面白いですよねw
現代ではGoogle翻訳やDeepLなど真面目に使えるWeb翻訳などが充実していますが、これらをAPIとして使おうとすると有料なことが多いです。
仕方ないことですね。ディープラーニングのコストなどを考えると、Web翻訳を無料で使えるのはありがたいことです。
しかし、Pythonにはローカルで使える翻訳ライブラリがあります。
今回はargostranslate
を使って逆翻訳APIを作っていきます。
環境構築
今回は以下の環境で逆翻訳APIを作っていきます。
- Python 3.12
ライブラリのインストール
まずは以下のライブラリをインストールします。
ライブラリ | 用途 |
---|---|
argostranslate | 翻訳ライブラリ |
uvicorn | サーバー |
fastapi | APIフレームワーク |
pip install argostranslate uvicorn fastapi
次にargostranslate
のモデルをダウンロードしておきます。
argospm install translate
これを実行すると${HOME}/.local/cache/argos-translate
にモデルがダウンロードされます。
逆翻訳APIの作成
それでは逆翻訳APIを作成していきます。
翻訳関数
まずは翻訳関数を作成します。
from argostranslate import translate
from functools import lru_cache
@lru_cache(2 ** 10) # キャッシュを使って同じ組み合わせの翻訳を高速化
def translate_text(text: str, source_lang: str, target_lang: str) -> str:
translated_text = translate.translate(
text, # 翻訳したいテキスト
source_lang, # 翻訳前の言語
target_lang # 翻訳後の言語
)
return translated_text
この翻訳ですが、CPUで動作させる場合結構時間がかかります。
私のサーバーマシンはGPUを搭載していないので、残念ですがCPUで動かします。
なので、少しでも高速化するためにfunctools.lru_cache
を使ってキャッシュを使います。
これは引数が同じ場合、結果をキャッシュしたものを返すことの出来るデコレータです。
次に逆翻訳関数を作成します。
def re_translate_text(text: str, source_lang: str, via_langs: list[str]) -> str:
# 元の言語から中間言語へ翻訳
translated_text = translate_text(
text,
source_lang,
via_langs[0]
)
# 中間言語から中間言語へ翻訳
for i in range(len(via_langs) - 1):
translated_text = translate_text(
translated_text,
via_langs[i],
via_langs[i + 1]
)
# 中間言語から元の言語へ翻訳
translated_text = translate_text(
translated_text,
via_langs[-1],
source_lang
)
return translated_text
この関数は、元の言語から中間言語へ翻訳し、中間言語から中間言語へ翻訳し、中間言語から元の言語へ翻訳する関数です。
via_langsには中間言語のリストを渡します。
中間言語が1個の場合は、元の言語から中間言語へ翻訳し、中間言語から元の言語へ翻訳するだけです。
2個以上の場合は、中間言語から中間言語へ翻訳を行います。
FastAPIの作成
次にエンドポイントを定義します。
from fastapi.responses import PlainTextResponse
@app.get("/re_translate")
def re_translate(text: str, source_lang: str, via_langs: str):
"""再翻訳して人間が理解できないテキストに翻訳します。
"""
return PlainTextResponse(
re_translate_text(
text,
source_lang,
via_langs.split(",")))
@app.get
デコレータを使って、GETリクエストを受け取るエンドポイントを作成します。
/re_translate
エンドポイントにTranslateModel
を受け取り、re_translate_text
関数を呼び出して結果を返します。
この手軽さがFastAPIのいいところですね。
サーバーの起動
サーバーを起動して、APIを叩いてみます。
uvicorn main:app --reload
--reload
オプションをつけると、ファイルの変更を検知してサーバーを再起動してくれます。
これで実行しhttp://127.0.0.1:8000/docs
にアクセスすると、SwaggerUIが表示されます。
ここではエンドポイントを試すことができるので、試してみましょう。
textに今日のご飯何にする?おでん?みかん?
と入力し、source_langにja
、via_langsにen,ru
を入力してExecute
をクリックします。
すると、ペットと一緒に何をしますか?
という結果が返ってきました。
いいですねw
Discord Botに組み込む
汎用的に利用することを考えるとAPIのほうが便利ですが、人に見せたりするにはDiscord Botにすると見せやすくて面白いですよね。
Botの作成
まず追加のライブラリをインストールします。
ライブラリ | 用途 |
---|---|
py-cord | Discord Botライブラリ |
aiocache | キャッシュライブラリ |
pip install py-cord aiocache
次にBotを作成します。
import discord
from discord import option
import aiohttp
from aiocache import cached
intents = discord.Intents.default()
bot = discord.Bot(intents=intents)
# 翻訳関数
@cached(600) # キャッシュを使ってBOT側で再問い合わせを減らす
async def translate_text(text: str, source_lang: str, via_langs: str) -> str:
encoded_text = quote(text)
encoded_source_lang = quote(source_lang)
encoded_via_langs = quote(via_langs)
async with aiohttp.ClientSession() as session:
async with session.get(
f"http://127.0.0.1:8000/re_translate?text={encoded_text}&source_lang={
encoded_source_lang}&via_langs={encoded_via_langs}"
) as response:
translated_text = await response.text()
return translated_text
# 逆翻訳コマンド
@bot.slash_command(name="re_translate", description="逆翻訳します")
@option("text", description="翻訳するテキスト")
@option("source_lang", description="翻訳前の言語")
@option("via_langs", description="翻訳する言語 ex) en,ja")
async def re_translate(
ctx: discord.ApplicationContext,
text: str,
source_lang: str,
via_langs: str,
): # Takes one integer parameter
await ctx.defer() # 返信まで時間がかかるので、待機状態にする
try:
translated_text = await translate_text(text, source_lang, via_langs)
# followupで待機状態のメッセージを消して、結果を返す
await ctx.interaction.followup.send(translated_text)
except Exception as e:
await ctx.interaction.followup.send("エラーが発生しました")
raise e
bot.run("TOKEN")
TOKEN
にはBOTのトークンを入力してください。
できれば、.env
ファイルなどを使って環境変数にしたほうがセキュリティ的にはいいです。
軽く説明すると、
翻訳関数を定義し、翻訳関数にはAPIの呼び出しキャッシュを取得するためにcached
アノテーションを使っています。600
秒間キャッシュを保持することで、再問い合わせのコストを下げます。
@bot.slash_command
デコレータを使って、/re_translate
コマンドを作成しています。
@option
デコレータを使って、コマンドのオプションを設定しています。
この@option
デコレーターはなくてもいいですが、使うことで説明などを設定することができます。
Botの起動
botを起動は普通にmain.py
を実行するだけです。
python main.py
これでBotが起動します。
DiscordにBotを追加して、/re_translate
コマンドを実行してみましょう。
こんな感じで逆翻訳ができました。
なんかインパクトに欠けますね。
画像に変換する
インパクトの強い画像を生成するAPIといえば5000choyen-api
というものがあります。
これを使って、逆翻訳したテキストを画像に変換してみましょう。
まずはAPIを使って画像を生成する関数を作成します。
from io import BytesIO
import discord
from discord import option
import aiohttp
from urllib.parse import quote
from aiocache import cached
@cached(600)
async def text_to_image(text: str):
escaped_text = escape(text)
async with aiohttp.ClientSession() as session:
async with session.get(
f"https://gsapi.cbrx.io/image?top={escaped_text}&single=true"
) as response:
image = await response.read()
return image
ここで返却される画像はbytes
オブジェクトなので、これをdiscord.File
に変換して送信します。
@bot.slash_command(name="re_translate_image", description="逆翻訳して画像にします")
@option("text", description="翻訳するテキスト")
@option("source_lang", description="翻訳前の言語")
@option("via_langs", description="翻訳する言語 ex) en,ja")
async def re_translate_image(
ctx: discord.ApplicationContext,
text: str,
source_lang: str,
via_langs: str,
): # Takes one integer parameter
await ctx.defer()
try:
translated_text = await translate_text(text, source_lang, via_langs)
image = await text_to_image(translated_text)
byteio = BytesIO(image)
byteio.seek(0)
await ctx.interaction.followup.send(file=discord.File(byteio, "translated_image.png"))
except Exception as e:
await ctx.interaction.followup.send("エラーが発生しました")
raise e
これで/re_translate_image
コマンドを使って、逆翻訳したテキストを画像に変換することができます。
ByteIO
を使って、discord.File
に変換しています。
実行すると以下のような画像が生成されます。
まとめ
今回はargostranslate
を使って逆翻訳APIを作成しました。
また、この翻訳精度ですが、Google翻訳やDeepLに比べると精度は低いですが、逆翻訳として使う分には逆に面白い気がします。
クソアプリ作成の参考にしてみてください。
それでは。