Python
mecab
自然言語処理
python3
word2vec

「赤の他人」の対義語は「白い恋人」 これを自動生成したい物語

背景

「造語対義語」がちょっと面白いと思ったので、Word2Vecを応用して機械に作らせてみよう!という試み。
やりたいことは、以下のようなギャグ対義語を自動生成すること。

  • 「赤の他人」⇔「白い恋人」
  • 「ウサギは寂しいと死ぬ」⇔「ゴリラは孤独を背負い生き抜く」
  • 「生きろそなたは美しい」⇔ 「死ねブス」
  • 「冷やし中華始めました」⇔ 「おでんはもう辞めました」
  • 「コアラのマーチ」 ⇔ 「ゴリラのレクイエム」
  • 「やせ我慢」 ⇔ 「デブ大暴れ」
  • 「生理的に無理」 ⇔ 「理論上は可能」
  • 「ゲスの極み乙女」 ⇔ 「ほんのりピュア親父」
  • 「週刊少年ジャンプ」⇔「月刊老人スクワット」
  • 「お母さんと一緒」 ⇔ 「お父さんは別居」
  • 「そんなんじゃ社会に出てから通用しないぞ」 ⇔ 「それだけの力があれば幼稚園では無敵だろう」

果たしてWord2Vecを活用して、このようなユーモアを生み出せるのか!?

本投稿の内容

  • 造語対義語はユーモアであり、正確性より面白さを優先します。
  • 試行錯誤した取り組みや考察の流れを記録したい目的であり、綺麗なコードは出てきません。
    • といいつつ、試したことを全て書くことは難しく、かなり端折っています。
    • 変数名とかおかしいのは気にしない・・・。
  • 環境はWindows10 + Python3 +JupyterNotebook を前提としています。
    • Word2Vecのモデルは学習済みのものを使うので比較的簡単に動かせます。
    • Mecab(形態素解析)やPythonの環境構築については触れません。
  • コードを見てね、ではなく、本文&実行結果だけで読み物的に楽しめる投稿を目指します。

最初の計画

Word2Vecとは?

  • 単語をベクトル化して、以下のような計算も可能になる仕組み
  • 「王様」 ー 「男」 + 「女」 = 「女王」

詳しくは、他に多数の良記事があるため、ググればすぐ分かるので、本稿では割愛する。

これを使えば対義語なんて簡単?
いやいやいやいや、例えば「ウサギ」の正反対を求めようとすると、
以下のように、全く関係のない言葉が出てくる。

> model.most_similar(negative=["ウサギ"], topn=7)

[('緊縮', 0.649797797203064),
 ('懸案', 0.6217541694641113),
 ('整備新幹線', 0.6201282143592834),
 ('補正予算', 0.6149194836616516),
 ('労使交渉', 0.6115709543228149),
 ('断行', 0.6105344295501709),
 ('予算案', 0.605506420135498)]

機械にとって、「無関係」なものこそが究極の反対であり、
「ウサギ」に対して「ゴリラ」などは、
実は物凄く近い言葉として認識されている。

好きの反対は嫌いではなく無視。好きな子にはいたずらする男の子のようですね。

Word2Vecの弱点 = 対義語に弱い

そんなWord2Vecにも弱点がある。Word2Vecは対義語に弱いのだ。
理由は簡単で、「私はあなたのことが好きです」と「私はあなたのことが嫌いです」のようにが対義語は同じ文脈で登場するからだ。
なので、もし対義語が重要な意味をもつ機能をWord2Vecで開発しようと思うのであれば少し検討し直したほうが良いだろう。

by https://deepage.net/bigdata/machine_learning/2016/09/02/word2vec_power_of_word_vector.html

上記の記事にもあるように、Word2Vecは本来対義語に弱いシステム。
検討段階で既に全否定されている。じゃあどうするの・・・?

工夫の方向性

「王様」 ー 「男」 + 「女」 = 「女王」 の例を元に、
「王様」の対義語として「女王」を出す流れを考えてみる。

まず、 先に対義語のリストとして、「男&女」などのリストを用意しておき、
「王様」≒「男」など、入力された語句に近い対義語の組を見つけて、
上記の演算を行う、という流れはどうだろうか?

例えば、「ウサギ」は「弱い」ので
「ウサギ」-「弱」+「強」、でゴリラとかにならないか?

> model.most_similar(positive = ["ウサギ", "強"], negative=["弱"], topn=15)

[('ツバメ', 0.8022700548171997),
 ('カエル', 0.7846293449401855),
 ('ワニ', 0.7720282673835754),
 ('フクロウ', 0.7702590823173523),
 ('クマ', 0.7532936930656433),
 ('オオカミ', 0.7518944144248962),
 ('ワシ', 0.7504986524581909)]

うーん、「ツバメ」や「カエル」はあまり強そうではない。
「ワニ」「クマ」「オオカミ」 は結構いい線いっているような気がする。
この時点であまりぴったりの結果になっていないため難しそうではあるが、
いろいろ工夫/調整をすれば見込みはありそうなレベル。まずやってみよう。

事前準備

準備: 対義語リストの準備

まず、以下のように元となる対義語リストを作成。100~300個くらいの組を用意する。
Webからある程度取得できるし、これくらいの個数ならば手で作っても良い。

対義語リストの読み込み
import pandas as pd

df = pd.read_csv('~ファイルパス省略~/Taigigolist.csv')
print (df[:3])       # show 3 column
# org, taigigo という見出しでcsvは保存されているよ。
結果
  org taigigo
0  安心      不安
1  安全      危険
2  委細      概略

安心⇒不安、と、不安⇒安心 の両方のパターンを同様に扱いたいので、
以下のようにして、逆にしたデータフレームと結合する。
こういった小さな加工の積み重ねをしておくと、試行錯誤を進めやすい。

対義語リスト+逆順の対義語リスト を生成
import pandas as pd

df = pd.read_csv('~ファイルパス省略~/Taigigolist.csv')

# dfと全く同じものを作成。コレに逆にしたデータを追加する。
df2 = pd.DataFrame(index=[], columns = df.columns)

#元のdfから、row番号を逆に([[0],[1]]を[[1],[0]]に)してappendしていく。
for index, rows in df.iterrows():
    #データフレームごとの列のクラスは、pd.Seriesを用いる
    series = pd.Series([rows[1], rows[0]], index = df2.columns )
    df2 = df2.append(series, ignore_index = True)

#indexが昔のままになるので、一応reset_indexで振りなおしておく。
#(ちな、drop=Trueにしないとindexが別カラムとして登場する)
df_alltaigigo = df_alltaigigo.reset_index(drop=True)
print(df_alltaigigo[:3])

準備: Word2Vecの学習済みのモデルの準備

Word2Vecのモデルは、学習済みのモデルをダウンロードしてローカルに保存しておく。
以下2例:

Word2Vecのモデルのロード
from gensim.models.word2vec import Word2Vec
import gensim

#複数の辞書を同時に扱うと重くなるため、本当は使うものだけにした方がよい。

# 日本語wikipedia
modelW_path = './word2vec_models/latest-ja-word2vec-gensim-model/word2vec.gensim.model'
modelW = Word2Vec.load(modelW_path)

# fasttext-vector
modelV_path = './word2vec_models/vector/model.vec'
modelV = gensim.models.KeyedVectors.load_word2vec_format(modelV_path, binary=False)


####ロードされた結果のテスト
#こんな感じで必要な時に切り替えて、複数の辞書を試す
model =  modelW
#類似度を表示する
sim_do = model.similarity(u'京都', u'東京')
print(sim_do)
# 0.719850214818 などとコサイン類似度が表示される

当初計画を実装してみる。しかし・・・

対義語リスト中から、最も「コアラ」に近い言葉を探す

コアラの対義語を求めるために、
まずコアラのイメージに近い対義語の組を探す方針であった。
例えば、コアラ = おとなしい を見つける。(今回のリスト中にはこの語句は無かったが)
おとなしい ⇔ 荒々しい という対義語の組であれば、
「コアラ」-「おとなしい」+「荒々しい」=?? によって、
コアラを荒くしたような動物 = コアラの対義語、ということ。

そこで、対義語リストの語句と入力語句の近似度を計算して、
大きい順に並べるようにする機能
を作る。

ここでちょっと面倒な点は、作成した対義語リストの言葉は、
必ずしも全てWord2Vecの辞書に入っているわけでは無いということ。
Word2Vecの演算が出来ない言葉が結構出てきてしまうので、
対義語リストを調整して、除外する必要がある。

また、Word2Vecモデル内のベクトルがイマイチに設定されていて、
演算しても上手い対義語が作れない組なども存在するかもしれない。
そういったいわばエラー語句も除外していく必要がある。
そのため、最初は手動で対義語リストをメンテナンスして、
チューニングしていく方法が有効だと考えていた。

しかし、それでは、Word2Vecのモデルを変更するたびに調整が発生し、
特定のWord2Vecモデルに依存したチューニングになってしまうために、
試行錯誤の段階としては望ましくない。

そこで、Word2Vecの辞書に含まれない言葉を自動で除外するようにして、
Word2Vecのモデルの変更や、対義語リストの追加変更に対応しやすいようした。

対義語リスト中から、入力語句に近い言葉を探す
# 対義語データ内で、辞書データにあるもののみを抜粋&再構築
def create_filterd_taigigo_list(input_word):
    # 空のデータフレームを生成する。
    df_alltaigigo_filterd = pd.DataFrame(index=[], columns = ["org","taigigo","sim_inp_org","sim_inp_tai","sim_org_tai"])
    for index, rows in df_alltaigigo.iterrows():
        #データフレームごとの列のクラスは、pd.Seriesを用いる
        try:
            #それぞれの単語の間の類似度を表示する
            sim_INP_ORG = model.similarity(input_word, rows[0])
            sim_INP_TAI = model.similarity(input_word, rows[1])
            sim_ORG_TAI = model.similarity(rows[0], rows[1])            
        except KeyError:
            #キーエラーが発生する場合は採用しない。
            #Word2Vecの辞書に無い用語が出てくるとエラーになるために、このエラー処理は重要。
            continue

        #エラーが無い場合は処理を継続して以下を実施        
        series = pd.Series([rows[0], rows[1], sim_INP_ORG, sim_INP_TAI, sim_ORG_TAI], index = df_alltaigigo_filterd.columns )

        df_alltaigigo_filterd = df_alltaigigo_filterd.append(series, ignore_index = True)

    return df_alltaigigo_filterd

# 本来は、このように単語ごとに実施する必要はなくて、
# Word2Vecのモデル、対義語リスト のロード時に一度だけ実施すれば良い部分もあるが、
# 将来、単語によって、使うモデル/リストが違うようにするかもしれないので、単語ごとに実施しておくね。

# 「コアラ」で実験するよ
df_alltaigigo_filterd = create_filterd_taigigo_list("コアラ")
# 「元のキーワード」と類似度が近い順に並び替えるよ。他の並び替えにするにはソートキーを変更すればいいよ。
df_alltaigigo_filterd = df_alltaigigo_filterd.sort_values(by='sim_inp_org', ascending=False)

#TOP5を表示するよ。
print (df_alltaigigo_filterd[:5])
結果
    org taigigo  sim_inp_org  sim_inp_tai  sim_org_tai
120  熱帯      寒帯     0.463996     0.306327     0.855372
318  満腹      空腹     0.391780     0.269278     0.795424
116  軟質      硬質     0.362629     0.170545     0.780534
387  人工      自然     0.360179     0.256472     0.565806
128  販売      購入     0.355862     0.180285     0.697335

「熱帯」が一番近い言葉で、その類似度は「0.463996」
これは、「王様」に近い言葉=「男」を見つけた、という部分に相当し、
「コアラ」に近い言葉=「熱帯」だったということ。(対義語リストに書いた語句の中で)

「熱帯」、「満腹」、はまぁまぁ理解できるが、なぜ「人工」の方が「自然」より近いのか、
「販売」が上位なのも謎であり、このあたり、既にWord2Vecモデルの精度の限度が垣間見れる

「コアラ」-「熱帯」+「寒帯」= カリフラワー!?

コアラの対義語をもとめるよ!(当初計画手法)
> model.most_similar(positive = ["コアラ", "寒帯"],negative=["熱帯"], topn = 15)

[('カリフラワー', 0.7316122055053711),
 ('ヒヤシンス', 0.7241581678390503),
 ('ヤマウズラ', 0.7234511375427246),
 ('未成魚', 0.7190210819244385),
 ('コノハズク', 0.7122239470481873),
 ('子グマ', 0.7119472026824951),
 ('ホオズキ', 0.7114987373352051),
 ('イトウ', 0.7092882394790649),
 ('若芽', 0.7086647748947144),
 ('ショーン・ホワイト', 0.7073724865913391),
 ('タマオ', 0.7060376405715942),
 ('蔓植物', 0.7052432298660278),
 ('モンシロチョウ', 0.7018641233444214),
 ('兵庫県高砂市', 0.7012842893600464),
 ('イワウチワ', 0.699478030204773)]

カリフラワー!?ヒヤシンス!?
植物になってしまったし、「高砂市」などカオスな結果に。
「コアラ」の対義語は「カリフラワー」です、だとシュールすぎるのでダメ。

逆の計算ならば、もう少しマシな結果に

予想以上に「遠い」言葉になってしまったため、
「コアラ」に近い言葉に持ってくるために、
逆に「熱帯」側を足し算に使ったらどうか?と考えた。

コアラの対義語をもとめるよ!(逆)
> model.most_similar(positive = ["コアラ", "熱帯"],negative=["寒帯"], topn = 15)

[('多摩動物公園', 0.7533067464828491),
 ('ホッキョクグマ', 0.7488281726837158),
 ('ペットショップ', 0.7408534288406372),
 ('犬猫', 0.7375373840332031),
 ('セキセイインコ', 0.7364336252212524),
 ('ペット', 0.7325634360313416),
 ('アライグマ', 0.7280879020690918),
 ('熱帯魚', 0.7206511497497559),
 ('レッサーパンダ', 0.713334321975708),
 ('フラミンゴ', 0.7120300531387329),
 ('シャチ', 0.7114540338516235),
 ('イルカ', 0.7058818340301514),
 ('セイウチ', 0.7048503160476685),
 ('ミンク', 0.7012484073638916),
 ('アフリカゾウ', 0.7004272937774658)]

「多摩動物公園」が一位に出てくるのは気になるが、
植物になってしまうよりは良い結果に見える。
「ホッキョクグマ」などは多少納得感がある。
他の言葉でも、全体的に、「逆」に使った方がよい傾向があった。
この時点で当初の構想は修正が必要になった。
つまり、
「王様」 ー 「男」 + 「女」 = 「女王」ではなく、
「王様」 ー 「女」 + 「男」 = 「??」のような計算の方が良いということだ。
おそらく、元の言葉と近い言葉を引いてしまうと、
元の言葉から離れすぎてしまうために、意味不明になってしまうことが原因。
対義語といえど、ある程度元の言葉に近い言葉を選ぶべきということ。

また、「多摩動物公園」のような複合した言葉は除外する必要がある

課題: 怪しい言葉が上位に入る問題

「乙女」の対義語を取得しようとしたが、
「夜な夜な」とか「眠れる」「戯れる」などが上位に入っている。
品詞の判定も実施し、これらは除外しなければならない。
品詞の判定以外の面でも、乙女に対して夜な夜な戯れるのはケシカラン。

怪しい結果が結構上位にはいるよ!
> model.most_similar(positive = ["乙女", "質問"],negative=["回答"], topn = 15)

[('お姫様', 0.8300929665565491),
 ('夜な夜な', 0.8079479932785034),
 ('妖精', 0.8011484146118164),
 ('キューピッド', 0.7944810390472412),
 ('人魚', 0.7932041883468628),
 ('サテュロス', 0.7867072224617004),
 ('狩人', 0.7798398733139038),
 ('眠れる', 0.7614409923553467),
 ('戯れる', 0.7611073851585388),
 ('ニンフ', 0.7570786476135254),
 ('黒猫', 0.7554555535316467),
 ('魔女', 0.7538139820098877),
 ('白き', 0.7514758110046387),
 ('美しき', 0.7502076625823975),
 ('死霊', 0.7494140863418579)]

まだまだ、「乙女」⇔「オヤジ」を算出するのは難しそうだ。

最初の結果

課題はあるものの、とりあえず一通り実装してみた。

与えられた単語に対して、近い対義語(A⇔B)を求める。
「与えられた単語」ー「B」+「A」の演算で、造語対義語を求める。
この実装を関数化した。(実装はほぼ上述の通りのため省略)

そして、以下のように対義語を求めた。

  • 入力例=「コアラのマーチ」
  • MeCab(形態素解析)を使って分解
  • コアラ / の / マーチ
    • コアラ ⇒ 対義語化
    • の   ⇒ (品詞判定で、助詞などは対義語化しない)
    • マーチ ⇒ 対義語化
  • 「コアラ」の対義語 + の + 「マーチ」の対義語

初期段階での結果例

  • コアラのマーチ ⇔ 多摩動物公園のレスポール
     (期待:ゴリラのレクイエム)
  • クローズアップ現代 ⇔ 翻弄近代
     (期待:ぼんやり見る過去)
  • お母さんと一緒 ⇔ おばあちゃんと友達
     (期待:お父さんは別居)
  • プラダを着た悪魔 ⇔ ヴェルサーチを上着たサタン
     (期待:しまむらを脱いだ天使)
  • あきらめたらそこで試合終了ですよ ⇔ 忘れるたらたまたまで試合終盤復活ですよ
     (期待:勝ったと思ったら手を抜きなさい)
  • 赤の他人 ⇔ 黒文字の貧乏人
     (期待:白い恋人)
  • リア充爆発しろ ⇔ ボア俊誘爆しない
     (期待:キモオタ凝縮しろ)
  • そうだ京都行こう ⇔ そうだ浅草来るう
     (期待:うわああああ!大阪が来た!)
  • ゲスの極み乙女 ⇔ コラボ曲の自意識魔女
     (期待:ほんのりピュア親父)

初期結果への考察

それなりに「ユーモア」のある結果は得られたが、まだまだ調整が必要だ。
良く意味が分からない単語が来る場合も多いし、動詞などの変換は難しい。
辞書を入れ替えたり、ちょっとした調整ですぐに答えは変わる。
たとえ一発で良い結果が出せなくても、
乱数を入れて何度かやれば、面白い候補が出る時もある、というレベルか?

修正の計画と、その準備

工夫の方向性

  • 対義語リストの中から、どの組を使うのか、もっとチューニングする
  • 特定の組だけを使うと、全部が怪しい言葉になる可能性もあり、複数の組を使う
  • 結果の中から、同じ品詞の言葉だけを抜き出すようにする
  • 元の単語と、ある程度似ている単語を選んだほうが良い結果になる
  • 「多摩動物公園」などの複数語は採用しないようにする
  • 多少の乱数を入れ、複数の候補が出るようにする

準備: 品詞&複合語の判定用の機能

「多摩/動物/公園」などの複数語を除き、また、元の語句と違う品詞を排除するため、
Mecab(形態素解析)を用いて、語句の分解と、品詞判定を実施する機能を準備する。
合格となる結果としては、返却されるリストの長さが「1」であること(1単語ということ)と、
その品詞が、元々入力した語句の品詞と同一になっている、という2点を共に満たすこと。
そのチェックのために、入力単語を分解&品詞チェックをする関数を作る。

品詞&語句の個数をチェックするよ
import MeCab
def check_hinsi(taisyou_word):
    mecab = MeCab.Tagger("-Ochasen")
    WordHinsi = []
    #前処理:トリムなど。例「\t」はトリムしておく。全角スペース化
    trline = taisyou_word.replace(u'\t', u' ')

    #Mecabでの解析を実施
    parsed_line = mecab.parse(trline)

    #分析結果を、1行(単語)ごとに分割する。改行コードで分割する。
    wordsinfo_list = parsed_line.split('\n')
    #状況を見る場合はコメント解除
    #print(taisyou_word+"は"+str(len(wordsinfo_list))+"単語に分かれました")

    #単語ごとに、解析処理に入れていく。
    for wordsinfo in wordsinfo_list:
        #print("wordsinfo= " + wordsinfo)
        # 各単語の情報をタブで分割する。
        # たき火,タキビ,たき火,名詞-一般 などのような1行が、list形式になる。
        info_list = wordsinfo.split('\t')
        #print(info_list)

        #空白などの場合、三番目の要素が無い場合もあるため、
        #最初にそのリストの長さをチェック
        #以下のように無意味に3分割されるため、品詞情報が存在しないものは無視する。
        #['コアラ', 'コアラ', 'コアラ', '名詞-一般', '', '']
        #['EOS']
        #['']
        if(len(info_list)>2):
            WordHinsi.append((info_list[0], info_list[3]))

    return WordHinsi

check_hinsi("多摩動物公園")
結果
[('多摩', '名詞-固有名詞-地域-一般'), ('動物', '名詞-一般'), ('公園', '名詞-一般')]

工夫として、以下の部分で、MeCabの追加辞書を用いていないこと。

MeCab.Tagger("-Ochasen")

通常は、MeCabの精度をあげるために、より強力な辞書を用いたほうが良いのだが、
今回の目的から言うと、高精度の辞書を使ってしまうと、
「多摩動物公園」が固有名詞として登録済みになって、一語と判定されてしまう
分解の度合を設定する方法も良いのかもしれないが、
分解されすぎても困るので、今回は「中途半端な状態の辞書」として
敢えてデフォルトの辞書を使うことにしている。

準備: リスト中の単語を、似ている順に並び替える機能

「コアラ」に関して様々な演算をしていた時に、
ちょっと方向を誤ると全ての言葉が変な方向に行ってしまうことが分かった。

例えば、全くコアラの対義語っぽく無い単語が出てくる
「コアラ」+「合成」-「分解」= ①現代美術家  ②造形作家 ③パンジー

一方、演算の方向を逆にした場合は、動物という意味で良い結果も出る。
「コアラ」+「分解」-「合成」= ①シチメンチョウ ②ヘラジカ  ③マガモ
もちろん逆にしたら毎回上手くいくとは限らない。

コアラも植物になったり鳥になったり、動物公園になったり、狂気の実験だ

そこで、一つの対義語を選んで演算するのではなく、
いくつかの対義語を選んで、そのそれぞれの結果の上位1位、2位を集めて、
集めた中で、一番元の言葉に似ている語句を選べば、
無関係な方向に行ってしまうリスクを低減でき、安定感が上がる
と考えた。

複数の語句を、ある言葉に似ている順に並べるという機能を作る。

似ている順に並び替え
#特定の単語の中から似ているものを選び、順番に並べる
#全ての単語が辞書にある前提の動作なので、エラー時注意
def most_niteiru(input_word, entries_list):
    similarities = [model.similarity(input_word, entry) for entry in entries_list]
    results = [[similarity, entry] for (entry, similarity) in zip(entries_list, similarities)]
    return sorted(results, reverse=True)
    # 最も似ている単語を一つ返すだけならば以下のようにする。
    # return sorted(results, reverse=True)[0][1]

most_niteiru('芸術',   ['音楽', '絵画', '数学', '書道', '俳句', '科学', '歴史', '政治', '運動'])

上記は、「芸術」に近い順番に、リストの中の単語を並べるという例。
試してみると、なるほど、「芸術」に近い順番に並んでいる気がする。

結果
[[0.7420505983478638, '絵画'],
 [0.69370409674484712, '音楽'],
 [0.60828528861396158, '書道'],
 [0.59956030567022522, '俳句'],
 [0.57690359730963992, '科学'],
 [0.55100553510535721, '歴史'],
 [0.50427923095606197, '政治'],
 [0.46252147384040676, '数学'],
 [0.45263270924380355, '運動']]

そして、「りんご」から「みかん」へ

なんてヒドイ段落タイトルだ

「単語」単位で対義語を導く関数を実装(本稿の本体)

これまでの結果を総動員して、
ある単語に対して、造語対義語を出力する関数を作り、
その威力を試してみよう。

入力した単語&品詞に対して、その造語対義語を出力する関数
#前提① 事前にmodel にword2vecのmodelがロードしてある前提
#前提② ソート済みの対義語リストをグローバル変数から利用する前提
df_alltaigigo_filterd = df_alltaigigo_filterd.sort_values(by='sort_val', ascending=False)

def get_natural_taigigo_NEO(input_word, input_hinsi):
    FinalKouhoList = []

    for index, rows in df_alltaigigo_filterd.iterrows():
        # input_word +  rows['taigigo'] - rows['org'] のWord2Vecの演算を実施する。
        #★トップいくつまで取得するかは重要なチューニングパラメータ
        enzan_kekka_list = model.most_similar(positive = [input_word, rows['taigigo']], negative=[rows['org']], topn=15)

        KouhoList = []

        #似ている語句の上位から順番にfor文に入れていく。
        for enzan_kekka in enzan_kekka_list:

            #複数語判定&品詞判定の関数に入れる
            enzan_kekka_hinsi_list = check_hinsi(enzan_kekka[0])
            # 以下のような結果が返ってくる
            # [('多摩', '名詞-固有名詞-地域-一般'), ('動物', '名詞-一般'), ('公園', '名詞-一般')]

            #品詞リストを作成時に、複数に分かれた場合、そもそも一語ではないのでNG⇒長さが1の時だけ処理を継続
            #多摩動物公園は長さが3なので無視されるというワケ
            if( len(enzan_kekka_hinsi_list) == 1 ):
                #品詞チェックは、冒頭の2文字まで一致していればOKとする
                #(名詞~~のあとはあまり気にしない)
                # 厳密にチェックする場合 ⇒ if(input_hinsi == enzan_kekka_hinsi_list[0][1]):
                if(input_hinsi[:2] == enzan_kekka_hinsi_list[0][1][:2]):
                    #品詞も一致したため、候補に登録する。
                    KouhoList.append(enzan_kekka_hinsi_list[0][0])

        #候補リストが作られていて、2個以上あれば、その上位2個を取得する。(リストが作られない場合もあるよ。)
        #一個しかない場合、もともとイマイチなリストであったため、取得は避ける。
        if( len(KouhoList)>1 ):
            #途中経過(上位で取得したキーワード)を見る場合コメントを外す。
            #print(KouhoList)

            #おおもとの候補リストへ追加。
            FinalKouhoList.append( KouhoList[0] )
            FinalKouhoList.append( KouhoList[1] )

        #一定量のおおもとの候補がたまったら、全部検索する必要はないため、全文ループを修了する。
        #★いくつの候補まで取得するかは重要なチューニングパラメータ
        if(len(FinalKouhoList) > 10):
            break

    #複数の対義語リストをループしてどんどん候補を追加していった結果・・・・

    #もし、何も入っていない場合は、もとのキーワードを入れておく(変換しないので。)
    if(len(FinalKouhoList)<1):
        FinalKouhoList.append( input_word )

    #同じ言葉が何度も入る場合もあるため、作成したリストから、重複を除去する。
    #print(FinalKouhoList)
    FinalKouhoList_unique = list(set(FinalKouhoList))

    #候補リストの中から、最もINPUTに似たワードから並ぶように並び替えを行う。
    #上位N件を抽出する。(あまり下位を取得しても意味がないため、ある程度絞る)
    #★この絞り方も重要なチューニングパラメータ
    #最終的には、random.choice(list)で結果の中からランダムに取得する予定であるため。
    nita_word_list = most_niteiru(input_word, FinalKouhoList_unique)[:3]

    return nita_word_list

get_natural_taigigo_NEO("りんご","名詞-一般")
結果
[[0.92463033052039401, 'みかん'],
 [0.8783315085813681, '苺'],
 [0.86586162812338352, 'ねぎ']]

これは結構良い結果ではなかろうか!?
「りんご」に対して「みかん」は大本命だが、
「ねぎ」もヒネリが効いていて面白い。

「ねぎ」が出てくるとなんか面白い気がしてしまうのはなぜだろう。

別な言葉でも試してみる

入力
print(get_natural_taigigo_NEO("コアラ","名詞-一般"))
print(get_natural_taigigo_NEO("靴下","名詞-一般"))
print(get_natural_taigigo_NEO("カーテン","名詞-一般"))
print(get_natural_taigigo_NEO("乙女","名詞-一般"))
print(get_natural_taigigo_NEO("スマートフォン","名詞-一般"))
print(get_natural_taigigo_NEO("動物","名詞-一般"))
print(get_natural_taigigo_NEO("眼鏡","名詞-一般"))
print(get_natural_taigigo_NEO("カレー","名詞-一般"))
print(get_natural_taigigo_NEO("ラーメン","名詞-一般"))
print(get_natural_taigigo_NEO("ビール","名詞-一般"))
print(get_natural_taigigo_NEO("飛行機","名詞-一般"))
結果
[[0.79610911695616471, 'セキセイインコ'], [0.785405187925722, 'ミンク'], [0.76901927799464209, 'ホッキョクグマ']]
[[0.91507178306705739, '手袋'], [0.90161941201164741, '革靴'], [0.8959922878688289, 'セーター']]
[[0.85584726609771655, 'ソファー'], [0.83979639140314122, 'トランク'], [0.83008699669693065, 'ポーチ']]
[[0.87598038376876186, '人魚'], [0.86406344018281578, '妖精'], [0.77857242742516286, 'カーバンクル']]
[[0.92941233856523409, 'Android'], [0.89327022466870609, 'フィーチャーフォン'], [0.88803100750540598, 'アプリ']]
[[0.91011603498044402, '爬虫類'], [0.90208324646657068, '哺乳類'], [0.88445087859468874, 'イヌ']]
[[0.90876897001328705, 'メガネ'], [0.85207171387504843, 'ブラジャー'], [0.81841303414529509, '靴下']]
[[0.86714373685808754, '紅茶'], [0.85008168395949824, 'スパゲッティ'], [0.84403310700446899, 'ケチャップ']]
[[0.95296741693813036, 'うどん'], [0.91918231065962774, 'たこ焼き'], [0.90987101568564399, '餃子']]
[[0.89793376386512991, 'ウイスキー'], [0.89059171717745844, '飲料'], [0.88606741254739563, '牛乳']]
[[0.83567369949489889, '飛行船'], [0.82413943992220307, '気球'], [0.82146012956360659, 'グライダー']]

眼鏡とメガネが別な言葉扱いになっていたりとか、突っ込みどころはあるものの、
靴下vs手袋ラーメンvsうどん、などは大変良い。
カレーは飲み物としても属性を持っているから紅茶なのか!?
スマートフォンvsフィーチャーフォンは2位であるが綺麗な結果。
眼鏡vsブラジャー、も 共に二つの円形という共通点もあり、興味深い結果だ。
「眼鏡をかける」の対義語が「ブラジャーを外す」ならば、外したところをよく見ようとでもいうのか?

最終版:「赤の他人」は「水色の自分」になった

いよいよ、「文章」に対して上記の対義語処理を組み込む

ここまでくれば、「文章」に対して実施するのは難しくはない。
が、ちょっと長くなってきたし、一番重要な工夫は「単語」に対する処理なので、
もうコード無しで結果だけ書くことにする。

もし、本投稿への「いいね」が300達成したら、
「文章変換」の全量のコードと、いくつか端折った部分を載せて更新します!
もっといったらその後の精度改善とサービス公開の検討もします!

って宣言しておかないと、興味を持ってくれる人がいても面倒がって改善しなくなってしまう
⇒ 達成したため、追記しました。

最終結果サンプル

乱数で一度に5個の結果を出しているため、その中でちょっと面白いものを載せてみる。

  • 赤の他人 ⇔ 水色の自分
  • コアラのマーチ ⇔ ホッキョクグマのカー
  • クローズアップ現代 ⇔ 取り沙汰古典
  • お母さんと一緒 ⇔ おじさんと友達
  • プラダを着た悪魔 ⇔ グッチを穿くた死神
  • あきらめたらそこで試合終了ですよ ⇔ 忘れるたらあちこちでスパーリング継続ですよ
  • ちびまる子ちゃん ⇔ デカまる孫おじさん
  • アウトオブ眼中 ⇔ セーフエンパイアよどみ
  • ゲスの極み乙女 ⇔ ポッキーの無常人魚
  • 白線の内側までお下がりください ⇔ 手すりの上端までお上がるいただく
  • ボールは友達 ⇔ バットは彼氏
  • 僕は新世界の神になる ⇔ 俺は新宇宙の女神に入れ替わる
  • 機動戦士ガンダム ⇔ 対空邪神ジェイソン貯水池
  • ウサギとカメ ⇔ ネコとハリネズミ
  • 進撃の巨人 ⇔ 退却のヤクルト
  • 魔法少女 ⇔ 精霊美少年
  • 死に物狂い ⇔ 悲しむ野獣
  • 生理的に無理 ⇔ アレルギー純粋にまとも
  • 羊たちの沈黙 ⇔ 鶏達の驚愕
  • 冷やし中華始めました ⇔ オムライス志すました
  • ウサギは寂しいと死ぬ ⇔ カエルは辛いと絶える
  • 手のひらを太陽に ⇔ 口元を地球に
  • 週刊少年ジャンプ ⇔ 月刊幼女宙返り
  • やせ我慢 ⇔ とろける逆上
  • 謎解きはディナーの後で ⇔ 正体見つけ出すは朝食の直前で

最終結果考察

なかなか人が作った以上に面白いものは出来ないものの、
適当に語句を入れても、ある程度(90%くらい?)対義語っぽいものが返ってくる。
 (上記の結果サンプルは、全く認識がおかしい、という残り10%を除き、
  多少つまらないものも、精度のサンプルという意味で掲載)

お母さんと一緒なら大丈夫だが、おじさんと友達って誘拐されちゃうかも。
手すりの上端まで上がる、の無意味さ加減がいい。
ボールを友達にする男の子がいるなら、バットを彼氏にする女の子がいてもいいかもしれない。
ネコとハリネズミは、全くお互い無関係をつらぬきそうだ。
鶏達の驚愕は、とても小さなことに驚いているようで微笑ましい。
オムライスにいたっては、始める以前に志しただけかよ!
口元を地球に、は綺麗に対義語な感じもする。
月刊幼女宙返り、は一部に大変ウケそうな雑誌。お手本の「月刊老人スクワット」より面白い。
by 通りすがりのコメンテーター

やはり、「名詞」が一番扱いやすく、「動詞」は難しいという印象。
「赤の他人」から「白い恋人」までを再現することは出来なかったが、
そこまでやるには北海道の銘菓についても学習させる必要があるため、
「水色の自分」までが今回の限界な気がする。

実は、架空対義語botなる先駆者様がいらっしゃることも認識していたが、
アプローチの仕方が似ているようで異なり、出来た結果としても、
得手不得手がかなり異なる結果となったと見ている。

改善する方向としては、
Word2Vecのモデル及びMeCabの精度に依存するところが大きいため、
強力な辞書を使えれば精度が上がる可能性がある。
特に、表記揺れの修正や、動詞の扱い方の工夫、が出来ると望ましい。
(動詞の活用については特に面倒だし)
また、今回の対義語リストもシンプルなものであるため、
もっと本格的な対義語リストを用意し、
各種パラメーターのチューニングをきちんと行えば、
より精度があがるかもしれない。

そもそも「精度」とは何かというと、
今回の目的はあくまで「面白い」ことなので、
多少精度が悪い方が、逆に思いもしなかった言葉になって面白いかもしれない。
いまさら言いわけすんなよ
例えば、上記の例では「ガンダム」が認識されずに、
「ダム」が「貯水池」になってしまっている。
仮に「ガンダム」が「ザク」に変換されたとしても、どちらが面白いのか判断は難しい。
「対空邪神ジェイソン貯水池」は強そうだが対空に限定されているので使いどころが難しいユニット

正確に反対の意味になっていなかったとしても、
「沈黙」が「驚愕」になるような(微妙に外した)センスは、
なかなか人では思いつかない味があると思う。
どのあたりが面白いのかは、多くの人の意見を聞いてからチューニングした方がよいかもしれない。

~終わりに~ (ポエム)

「斬新なアイデア」は、「笑い」とよく似ていて、
どちらも、思いもよらなかったもの同士が結びつくことによって生じる。
ただし、本当に無関係なもの同士は、結んでもくっ付かないので意味が無い。
反対の位置にありながら、ある程度似たものを繋げる必要があるのだ。

今回のテーマは、まさにそれを機械で実現させようという試みである。
決して「役に立つ」ツールではないが、
思いもよらなかった発想、アイデア、笑い、を機械に出させる、という崇高な試みなのだ。たぶん。

面白いと思った方はぜひ「いいね」してみてください。
いいねが多い = 興味を持つ人が多い場合、全量コードの掲載や、その後の改善の追記更新をします。
さらに想定より大幅に多ければ、精度改善&サービスとしての公開も検討します。

以上。


多数のいいねへの御礼追記:例文追加版

2018/5/8 本章を追記:
予想をはるかに超える速度で、300どころか既に二日たたずに500いいねを頂いていました。
みなさまからのご声援、コメント、ここに御礼申し上げます

事前に宣言の通り、未記載のコードなどを追記する予定です。
が、急速すぎて記事作成が間に合わないので、当座しのぎとして、
書くのが楽なワリに見て面白いので
もう少しいろいろな言葉を入れてみて、作品例を増やしてみることにしました!
いろいろな言葉を試して傾向を見ないと、良い点悪い点は分からないものです。

  • 出力方法は前回同様に、乱数付きで5個生成させて、その中で一番面白そうなものを記載
    (一発勝負では作例ほどの面白さにならないので、普通に使うためにはもうちょい精度向上が必要)

追加作品例①:

ちょっと難しそうな長めの言葉や漫画などにも挑戦。

  • 犬も歩けば棒に当たる ⇔ 猫も楽しむばロープに準じる
    • (※犬、猫はやっぱりライバル)
  • どんぐりの背比べ ⇔ ひよこの鼻較べ
    • (※これは対義になっていないですが、大爆笑。これが一番か!?)
  • 石橋を叩いて渡る ⇔ 山崎を振り回すて辿る
    • (※石橋が人名的に扱われている模様。)
  • そうだ、嬉しいんだ生きる喜び、たとえ胸の傷が痛んでも ⇔ そうだ、うれしいいらっしゃいだ育む悲しみ、もちろん眉毛の火傷が治るでも
    • (※眉毛の火傷、が斬新)
  • 空を自由に飛びたいな、はい、タケコプター ⇔ 疾風を平等に飛び回るたいな、まあ、ドラえもん
    • (※平等、まあ、のあたりのテンションの低さが好き)
  • ありのまま今起こったことを話すぜ ⇔ ありのままいつ起きるた事を聞くぜ
    • (※何でも聞くぜ的な感じに)
  • 上杉達也は浅倉南を愛しています。世界中の誰よりも。 ⇔ 北条香織は早坂北を恐るてくるます。国内外のなによりも。
    • (※香織って誰だよ!?恐れるのかよ!?国内外の何よりもって回りくどいよ、とか突っ込みどころ多し)
  • おまえがナンバー1だ!! ⇔ あんたがグランツーリスモ3だ!!
    • (※シュールすぎて爆笑。なんでグランツーリスモ3なの!?ベジータさん。)
  • 金は命より重い! ⇔ 報奨は怨霊より軽い!
    • (※報奨が一見重そうに見えて実は軽すぎるぅぅぅ。賭博黙示録カイジのトネガワの言葉より)
  • たったひとつの真実見抜く、見た目は子供、頭脳は大人、その名は名探偵コナン ⇔ たった一種の真相騙す、手触りは大人、知力は幼児、何らかの総勢は名刑事銭形
    • (※今回の傑作かも。銭形のとっつぁん。。。「手触り」もGood)
  • 私の戦闘力は53万です ⇔ あなたの空戦能力は46兆です
    • (※対空邪神ジェイソン貯水池の能力が判明!?)

長めの言葉や動詞などは、やはりちょっと難易度が上がる気がします。

追加作品例②:(企業系)

企業系は、ライバル社などが出る場合も多いと判明。

  • お口の恋人、ロッテ ⇔ お小便の幼なじみ、阪神タイガース
    • (※口から入って最後は小便に、ということ?ライバルは球団で出た)
  • あなたと、コンビに、ファミリーマート ⇔ 私と、トリオに、ローソン
    • (※トリオの3人目は誰だよ)
  • セブンイレブン、いい気分 ⇔ ウルトラレボリューション、つまらない孤独
    • (※ウルトラマンセブンから??)
  • インテル入ってる ⇔ IBM移るちゃっ
    • (※移ることになった)
  • やめられない、とまらない、カルビー、かっぱえびせん ⇔ 勧めるられない、とまっない、サッポロビール、写ルンです
    • (※「写ルンです」への飛び具合)
  • カラダにピース、カルピス ⇔ ときめきにボックス、キユーピー
    • (※カラダから心のときめきに、カルピスとキューピーはなんかそれっぽい)
  • ココロも満タンに、コスモ石油 ⇔ トビラも満ラムに、三洋電機
    • (※タン、は肉として認識された可能性。)

補足コードは近日、5月中には記載します。。。
⇒記載しました。


御礼追記その②:文章処理版

事前に宣言の通り、「文章変換」の全量コードを掲載します。
(※これ宣言していなかったら多分サボって更新していなかったので、応援してくれた方に感謝)
また、もう少し精度の改善、面白さUPの方法も検討中です⇒別記事になる予定。

文章全体に対して、対義語を生成する関数

処理の流れとしては、Mecabで形態素解析をして、
出てきた単語を上部で作成しておいた、「単語」⇒「対義語リスト」にする関数
(「りんご」を「みかん」にする関数)に順番に入れていく。

文章全体に対して対義語を取る関数
import MeCab
import random

def get_Taigigo_bun(inputtext):
    mecab = MeCab.Tagger("-Ochasen")
    #語彙が多すぎる辞書を使うと、対義語にしたいワードが
    #一語と認識されてしまう場合が増えてしまうので、あえて辞書は使わない
    #mecab = MeCab.Tagger(r"-Ochasen -d .\mecab-ipadic-neologd")

    resulttext = ""

    #前処理:トリムなど。例「\t」はトリムして全角スペース化
    trline = inputtext.replace(u'\t', u' ')

    #####メイン処理:
    #Mecabでの解析を実施
    parsed_line = mecab.parse(trline)

    #途中経過を見てみる場合は以下のprintで。
    #parse結果は、改行区切りで全単語の品詞情報が格納されている。
    #print("== parseコマンドの結果 ==")
    #print(parsed_line)

    #分析結果を、1行(単語)ごとに分割する。改行コードで分割する。
    wordsinfo_list = parsed_line.split('\n')

    #単語ごとに、解析処理に入れていく。
    for wordsinfo in wordsinfo_list:
        #print("wordsinfo= " + wordsinfo)

        # 各単語の情報をタブで分割する。
        # たき火,タキビ,たき火,名詞-一般 などのような1行をlist形式にして要素を取り出せるように。
        info_list = wordsinfo.split('\t')
        #print(info_list)

        #空白などの場合、三番目の要素が無い場合もあるため、
        #最初に、出来たリストの長さをチェック
        if(len(info_list)>2):
            #一部の品詞や、一部の例外単語は、変換処理処理を実施しない。
            if( info_list[3].startswith('助詞') or 
                info_list[3].startswith('副詞-助詞類接続') or
                info_list[3].startswith('名詞-特殊-助動詞語幹') or
                info_list[3].startswith('フィラー') or
                info_list[3].startswith('動詞-接尾') or
                info_list[3].startswith('記号') or
                info_list[3].startswith('接頭詞') or
                info_list[3].startswith('助動詞') or
                info_list[0]==u'の'
                ):
                    #上記パターンは、何もしない
                    resulttext+=info_list[0]
            else :
                #動詞の場合、原型に対して対義語を取るか、元々の入力に対して対義語を取るか2通りある。
                #どうせ対義語化するときに変換はずれるため、多少のずれは無視して原型を対象とする。
                #(動詞以外は、原型も元々の入力も変わらないはずなので影響しない)
                #元々の入力を使う場合
                #word = info_list[0]
                #原型を使う場合
                word = info_list[2]

                #品詞を保存する
                word_hinsi = info_list[3]

                try:
                    #キーワードが辞書に入っているか確認する。
                    #中のベクトルそのものを出す。(なければエラーに)
                    out = model[word]

                    #以下で、別途作成の、単語⇒対義語のリスト、返す関数を呼び出す
                    taigigo_kouho_list = get_natural_taigigo_NEO(word, word_hinsi)

                    #乱数でそのなかから一語を選ぶ
                    taigigo = random.choice(taigigo_kouho_list)[1]
                    resulttext += taigigo

                except KeyError:
                    # モデル内にキーワードが無い場合
                    # 全体をエラーにさせないため、とりあえず元の言葉を入れておく
                    #print(word+"でエラー発生")
                    #print(KeyError)      
                    resulttext += word

        #各行を単語分割した結果ごとのFor文の終了

    #print(resulttext)
    return resulttext

#上記の関数は、ランダムで一つを返すため、任意の回数繰り返したり、
#デバッグ用に表示する関数をつけておく。
def get_Taigigo_bun_kurikaesi(input_bun, kurikaesi):
    print(input_bun)
    print("== 結果 ==")
    for i in range(kurikaesi):
        print(get_Taigigo_bun(input_bun))
    print("  ")

#テスト
get_Taigigo_bun_kurikaesi(u'赤くて丸くておいしいりんご',5)
結果
== 入力 ==
赤くて丸くておいしいりんご
== 結果 ==
黒いて平たいてうまい苺
青いて平たいてうまいみかん
黒いて四角くて美味しい苺
白いて細長いておいしくねぎ
青いて細長いてうまい苺

上のようにランダムで5個出るので、この場合、
「白いて細長いておいしくねぎ」なんて奇跡的に上手く出ているので、
こいつをおいしくいただくことにしましょうか。 出力例として記載しよう!

つまり、以下のように処理している。

  • 赤く⇒赤い ⇒黒い、青い、白い
  • 丸い⇒丸い ⇒平たい、四角い、細長い
  • おいしい ⇒ 美味しい、おいしく(こういう変換は本来は除外すべき)、うまい
  • りんご ⇒ みかん、苺、ねぎ

文章処理の結果考察

この例は、弱点と良い点が分かりやすくでている。
良い点としては、個別の変換処理(特に名詞)に対しては、
上手く動いていることだと思う。

一方、弱点は以下

  • 弱点
    • 動詞や形容詞などの活用形を、一旦原型に戻しているため、ちょっと語尾がおかしい。
      • これは専門家なら直してくれそう?本稿の趣旨とちょっとずれた取り組みになるため深追いせず
      • 誰か直してくれたら嬉しいな
    • 美味しい、おいしく、などほぼ同様の意味の単語が辞書に含まれ、それが出てしまう。
      • 全く同一の読み方のものは、例外処理で除外は可能。 面倒なので やっていないだけ
      • 違う活用のものも、原型を比較して除外可能。上と合わせて本来はやったほうがよい。
    • 単語ごとに解析するため、「白い」「ねぎ」はただの偶然
      • 「青い」「苺」、なども出現している。
      • 前後の単語との関連性も考えると、より自然な表現になるかもしれない。
      • ただし、どちがら面白い表現になるかは全く分からない
    • Mecabの分解性能に依存(上記例ではないが)
      • ガンダム⇒「ガン」「ダム」に対して「ジェイソン」と「貯水池」が出た例
      • 分解する場所によって、おかしな変換になってしまう。
      • ただし、どちがら面白い表現になるかは全く分からない

チューニングのポイント

チューニングによって結果が変わるところは多数ある。
本質的な箇所は★

  • 全体
    • ★Mecabの辞書
    • ★Word2Vecのモデル
      • ※本来はWord2Vecモデルも借物にせず、Mecabで作れば辞書は合う
    • ★対義語の見本表csv(今回は200語~300語の簡易的なもの)
  • 文章⇒対義語の関数について(記事中の関数のコメントにも記載)
    • ★品詞や例外語句の設定、扱い方
    • 動詞、形容詞に対して、原型を処理or元の入力値を処理
    • 生成処理時に何個生成するか?(今回は5個ずつ出している)
  • 単語⇒対義語リストの関数について(記事中の関数のコメントにも記載)
    • ★対義語の見本表(csv)に対して、何をソートキーにするか(入力後との類似度などから演算)
    • 言葉の演算処理時に、結果上位topいくつを取得するか?
    • 「候補」を集める際に、いくつの候補を取得するか?
    • 元の言葉に近い順に選定した時に、いくつの候補を取得するか?

機械が作る面白さの本質とは?(ポエム)

上記のチューニングポイントを眺めていただき、
ひとつ、本投稿の中で最大のポイントとも言うべき観点は、
「ギャグ」「面白いこと」を一切学習させていないということ。

「面白いこと」を機械学習させて、
人間が面白いことを言うのと同様に、人間の真似をするというアプローチも、
一つのアプローチとして重要だと思う。

しかし、「面白さ」=「関連性があるのになかなか思いつかないこと」として、
全く人間が作った面白さから全く学ばなかったとしても、
機械に面白いことを言わせることが出来るのだと思う。

今回の結果に限って言えば、機械が不備/不十分であるために
(シュール要素が入って)面白かったという結果もあるが、
仮に、MecabやWord2Vecのモデルが物凄く高精度に出来ていても、
むしろ高精度に出来ていた方が、面白い結果が生まれたと思われる。

あらゆる言葉の方向性(ベクトル)を機械は均等に扱うために、
通常人間では全く思いもつかなかった方向へ言葉を捻じ曲げることが出来るのだ。

笑いだけでなく、アイデア出しやブレストにおいても、この性質は有用だ。
機械の主観は、人間の主観とまた違った感覚を生み出してくれ、
しかもそれを目的に応じてチューニングできる。将来よい相談相手になるだろう。

おまけ:追加作品例③:

折角なのでいくつか追加作品例を。

  • 赤い彗星のシャア ⇔ 青い土星のジュドー
    • (※ジュドー!シャアと対照的かも?)
  • 蚊の鳴くような声 ⇔ ゴキブリの吠える様な羞恥心
    • (※ゴキブリの吠えるような、ときて、「羞恥心」。なさそう。)
  • いつもお世話になっております ⇔ ちょっと有頂天に入れ替わるてくっます
    • (※ラリった感)
  • 一寸の虫にも五分の魂 ⇔ 拾のゴキブリにも四ダース不死
    • (※10なのに4ダース!?さすが不死身のゴキブリ)
  • 海老で鯛を釣る ⇔ 徳利で鰹を食す
    • (※一杯やっている感)
  • 鬼の居ぬ間に洗濯⇔ 雷神のやってくるぬあいだに搾乳
    • (※搾乳、はなかなか出てこない表現)
  • 芸能人は歯が命⇔ 有名人は頭骨が危難
    • (※頭が悪いというわけではない。骨が危ない)
  • 元気ハツラツ!オロナミンC ⇔ カラダナカジマ!オロナミンA
    • (※ナカジマって誰だよ、カラダナカジマの破壊力)
  • 上上下下左右左右BA⇔ 下下ハローグッバイ腰部腰部Mi
    • (※ハローグッバイがいい。腰を振っている?)

以上。