AI・機械学習 python 技術関連

[tensorflow/AutoEncoder]中国人の視点で日本語を翻訳してみる

更新日:

概要

作成したもの

  • 日本人が雰囲気で中国語を翻訳するAI

挨拶 → 誤:挨拶 訳:拷問
手紙 → 誤:手紙 訳:トイレットペーパー
愛人 → 誤:愛人 訳:奥さん
维生素 → 誤:繊維? 訳:ビタミン
干部 → 誤:干物? 訳:幹部

上のように中国語をAIを使って、正しい翻訳でなく誤訳をさせる

使用した技術

  • AutoEncoder(tensorflow)

動機

中国語と日本語で意味が同じ漢字があるものの全然意味違う漢字があることを知り、これをAIで再現できたら面白いのでは?と思い、自然言語処理の勉強を兼ねてコードを書いてみました。

アーキテクチャ

処理の流れ

  1. 中国語単語
  2. 単語を文字に分解、画像に変換
  3. 文字を似ている日本語の漢字に変換(ビジュアル変換)
  4. 変換した文字を結合して日本語の辞書で検索

中国語単語を文字単位に分解して文字を画像化。オートエンコーダで文字画像の特徴量を学習して、見た目が類似する日本語の漢字に変換(例 灭→火)。文字を結合して単語を作成し、日本語の辞書で翻訳します。

アーキ図

ビジュアル変換


文字を画像にしてオートエンコーダで特徴ベクトルを取り出し、日本語漢字の特徴ベクトルとのcos類似をとります。

単語の翻訳


生成した単語をMecabで分解して、日本語辞書で意味検索します。検索した意味を足し合わせて、1つの意味にします。

実装

python3.5x環境での実装になります。ライブラリ関連のインストールは省略。

AutoEnoder

CNN-AutoEnoderをtensorflowでさらっと実装します。

from tensorflow.contrib import slim
import tensorflow as tf
from util.layer import lrelu

class CAE(object):
    def __init__(self, arch, is_training=False):
        self.arch = arch
        self.is_training = is_training
        self._generate = tf.make_template(
            'Generator',
            self._generator)
        self._encode = tf.make_template(
            'Encoder',
            self._encoder)


    def _encoder(self, x, is_training):
        n_layer = len(self.arch['encoder']['output'])
        subnet = self.arch['encoder']
        with slim.arg_scope(
                [slim.batch_norm],
                scale=True,
                updates_collections=None,
                decay=0.9, epsilon=1e-5,
                is_training=is_training,
                reuse=None):
            with slim.arg_scope(
                    [slim.conv2d],
                    weights_regularizer=slim.l2_regularizer(subnet['l2-reg']),
                    normalizer_fn=slim.batch_norm,
                    activation_fn=lrelu):

                for i in range(n_layer):
                    x = slim.conv2d(
                        x,
                        subnet['output'][i],
                        subnet['kernel'][i],
                        subnet['stride'][i])

        x = slim.flatten(x)

        with slim.arg_scope(
            [slim.fully_connected],
            num_outputs=self.arch['z_dim'],
            weights_regularizer=slim.l2_regularizer(subnet['l2-reg']),
            normalizer_fn=None,
            activation_fn=None):
            z = slim.fully_connected(x)
        return z


    def _generator(self, z, is_training):
        subnet = self.arch['generator']
        n_layer = len(subnet['output'])
        h, w, c = subnet['hwc']
        with slim.arg_scope(
            [slim.batch_norm],
            scale=True,
            updates_collections=None,
            decay=0.9, epsilon=1e-5,
            is_training=is_training,
            scope='BN'):

            x = slim.fully_connected(
                z,
                h * w * c,
                normalizer_fn=slim.batch_norm,
                activation_fn=tf.nn.relu)
            x = tf.reshape(x, [-1, h, w, c])

            with slim.arg_scope(
                    [slim.conv2d_transpose],
                    weights_regularizer=slim.l2_regularizer(subnet['l2-reg']),
                    normalizer_fn=slim.batch_norm,
                    activation_fn=tf.nn.relu):

                for i in range(n_layer -1):
                    x = slim.conv2d_transpose(
                        x,
                        subnet['output'][i],
                        subnet['kernel'][i],
                        subnet['stride'][i])

                # Don't apply BN for the last layer of G
                x = slim.conv2d_transpose(
                    x,
                    subnet['output'][-1],
                    subnet['kernel'][-1],
                    subnet['stride'][-1],
                    normalizer_fn=None,
                    activation_fn=tf.nn.tanh)
        return x


    def loss(self, x):
        z = self._encode(x, is_training=self.is_training)
        xh = self._generate(z, is_training=self.is_training)

        with tf.name_scope('loss'):
            loss = tf.reduce_mean(tf.square(tf.subtract(xh, x)))

            # For summaries
            with tf.name_scope('Summary'):
                tf.summary.histogram('z', z)
                tf.summary.image("G", xh)
        return loss

    def sample(self, z=128):
        z = tf.random_normal(shape=[z, self.arch['z_dim']])
        return self._generate(z, is_training=False)

    def encode(self, x):
        return self._encode(x, is_training=False)

    def decode(self, z, y=None, tanh=False):
        return self._generate(z, is_training=False)

結果

ビジュアル変換の結果

<中国語→日本語>

发电机 → 愛屯机
干部 → 干部
灭火 → 火火
压岁钱 → 圧支恨

いい感じで中国語を似ている日本語漢字に変換できてると思います。

翻訳結果

干部: 部を干渉すること。部分を干上がること。
維生素: 維新が平素生きること。素顔が繊維を生い立つこと。
日本鬼子: 親子の鬼ごっこが本来日帰りすること。様子の鬼が本質を日帰りすること。

ぎこちない文章ですが、それっぽい意味を生成することができました。

感想/考察

自然言語は、やっぱり難しいです。改めてきれいな文章をAIで生成することの難しさに触れることができました。

おまけ

今回のアルゴリズムを使ったWebサービスです。
http://hanzi.ai/jp/

-AI・機械学習, python, 技術関連

Copyright© AIなんて気合いダッ! , 2019 All Rights Reserved Powered by AFFINGER5.