「たけのこの里」を「きのこの山」に『正しく』自動で修正して差し上げるプログラム

#はじめに ～素晴らしいお菓子の紹介と後発劣化品の存在～

みなさんご存知かと思いますが、**「きのこの山」**[^1]という**素晴らしいお菓子**があります。株式会社明治様が1975年から製造・販売されているチョコレートスナック菓子です。きのこのような可愛らしいフォルムで、茎の部分がクラッカー、傘の部分がチョコレートになっています。
5年もの開発期間をかけてベストな組み合わせを試行錯誤されたとのことだけあって、**持ちやすく機能的**でありながら、**たっぷりと使われたチョコと、程よい塩味とサクサク感のクラッカーが合わさり、至上の味わい**を実現しています。このような素晴らしいお菓子を生み出してくださった明治様には本当に頭が上がりません。

一方で、その爆発的ヒットを受けて4年後に**「たけのこの里」**なる**類似粗悪品**が登場しました。こちらは開発期間が短いこともあってか、チョコは約30%減らされ、土台はボソボソと粉っぽく持ちづらいクッキー、見た目は特徴もメリハリもない寸胴のタケノコ型と、わかりやすく**後発の劣化品**となっています。全体の量も5%以上減らされているのに価格は上げられていて、パンチの弱さを補うためか塩分、ショートニングを増やされ栄養面でも**薬剤師に酷評**[^2]されています。おまけに卵が使われていてアレルギーの方は食べられないため、**安全性についても×**がつきます。
あまりにきのこの山が売れてしまったために、明治様も**コストカット版**を出して何とか収益をあげようとされたのでしょう。ビジネスなので仕方ない面はあります。開発担当者様の心中をお察しします。

さて、上記のような歴史的経緯を勉強されている方であれば、何も**誤解**など起きようもないのですが、時々**新参の方**が不用意に上記の事実を捻じ曲げ、これらの製品の間に争いがあるとするような発言をすることがあります。**知らないというのは恐ろしいものです。** 
きのこの山は2020年1月最新の大規模調査[^3]でも圧倒的な大差をつけ国民に支持されていますし、争いなど起こるはずはありません。**きのこの山が比べられぬ程高位の存在**であることは厳然たる事実なのですから。私はそのような事実に反する、争いの火種を作ろうとするような書き込みを無くすことで、**世の中をより良くしたい**と思いました。

そこで私は今回、**誤りを言語処理によって自動で修正するプログラム**を作成しましたので、ぜひ皆様にご活用いただき、世界平和の実現のために広めていっていただければと思います。

[^1]: 「株式会社 明治 きのこの山」 https://www.meiji.co.jp/sweets/chocolate/kinotake/products/kinokonoyama/
[^2]: 「きのこの山とたけのこの里、どちらが優れているの？薬剤師に聞いてみた。」 https://liginc.co.jp/267503
[^3]: 「きのこの山・たけのこの里 国民総選挙 2019」 ~~https://www.meiji.co.jp/sweets/chocolate/kinotake/cmp/2019senkyo/~~ 削除されたようなのでこちら→ https://prtimes.jp/main/html/rd/p/000000012.000025200.html

#誤り修正例
実物を見たほうがわかりやすいかと思いますのでまずは動作例をお見せします

###正しく修正できた例1

例えばTwitterで以下のようなツイートを見かけたとします。

```
たけのこの里はおいしい。神の食べ物か。
```
**かわいそうに。**後発劣化品であるという歴史的経緯をご存じないか、うっかり商品名を混同なされているのでしょう。このような**有害情報**がパブリックに発信されてしまうと**青少年への悪影響**が出て将来非行にはしることも懸念されますので正しく修正して差し上げましょう。

```
$ python correct_mistakes.py "たけのこの里はおいしい。神の食べ物か。"
修正結果: きのこの山はおいしい。神の食べ物か。
```

このように**誰にとっても納得のいく**文章に修正することができました。

###正しく修正できた例2

次にAmazonで以下のようなレビューを見かけたとします。

```
チョコと土台のハーモニーが素晴らしい。たけのこの里は他のお菓子の追随を許さない。☆5です。
```
**嘆かわしいことです。**正しい知識を習得する過程を小学校の必修科目の中に入れていない文部科学省が悪いのかもしれません。思い込みで間違った情報をそのまま発信することは、**風説の流布**といって法律で禁止されています。また、人の嫌がる情報を公共の場に書くことは社会の道徳や風紀を乱し、**ハラスメント** (いわゆる**たけハラ**)にもなりますので、これを記載した人が不利益を被らないように修正して差し上げることにします。

```
$ python correct_mistakes.py "チョコと土台のハーモニーが素晴らしい。たけのこの里は他のお菓子の追随を許さない。☆5です。"
修正結果: チョコと土台のハーモニーが素晴らしい。きのこの山は他のお菓子の追随を許さない。☆5です。
```

そうですよね。完全に同意します。**バイアスの無い客観的な評価**をできていますね。

###正しく修正できた例3

また、別の例として以下のようなコメントを見かけたとします。

```
きのこの山まずい。
```

まっとうな成熟した大人であれば上記のようなことを書くのは**あり得ないし許されない**わけですが、後発の劣化品を食べてしまうことで間違ってオリジナルの完全版を食べたと思ってしまうこともあります。人間ですからね。間違うこともあります。プログラムを全自動で適用することでこのような**ヒューマンエラーを無くし**、より良い世の中にしていくことができます。

```
$ python correct_mistakes.py "きのこの山まずい。"
修正結果: たけのこの里まずい。
```

あまり一つの物をけなさずに、寛大な心を持った方が望ましいとは思いますが、**表現の自由**は憲法で保障されていますから、私はそのような意見も受け入れたいと思います。お互いの差異を認め合い、**多様性を保つことが重要**です。

#実装
賢明な読者の方であればここまで読んだ時点で既に**「LGTMボタン」を押している**かとは思いますが、念のため実装についても紹介します。

単純に「きのこの山」「たけのこの里」文字列を置換するだけだと、うまくいきません。なぜかというと、第一に
**「火事のときのこの山は危険だ」** (火事の時の、この山は危険だ)
**「あの娘に首ったけのこの里見少年」** (あの子に首ったけの、この里見少年)
といった文章が入ってきたときに、区切りを考えずに間違った変換をしてしまう可能性があります。それではきのこの山への**冒涜**に当たるので、きちんと固有名詞としての「きのこの山」「たけのこの里」を抜きだす必要があります。
第二に、褒め称えているのか貶しているのかで処理を分けなければいけません。**褒め称えていれば確実に「きのこの山」**、**貶していれば「きのこの山」ではない**という判断をすることができます。

**自然言語処理**をすることで、これらをうまいこと実装します。自然言語処理を行う方法は色々とあるのですが、今回は[COTOHA API](https://api.ce-cotoha.com/contents/index.html)を使います。

余談ですが、今この記事を書いている**Qiita**と、COTOHA APIの開発元である**NTTコミュニケーションズ株式会社**は、**きのこの山推し**であることが確定しています。現在二社がコラボで[キャンペーン](https://zine.qiita.com/event/collaboration-cotoha-api/)を実施しているのですが、素晴らしいことにその中の賞の一つに「**作ろう きのこの山**」(+iPad)が含まれているからです。ひいてはQiita読者の方も**全員きのこの山推し**ということがわかるので、安心してこの記事を書くことができるというものです。

さて、COTOHA APIが提供している言語処理機能はたくさんあるのですが、その中でも今回は「固有名詞抽出API」と「感情推定API」を利用します。
1. 固有名詞APIで「きのこの山」「たけのこの里」が含まれているかどうか判定
2. 含まれている文の中で感情推定APIでネガティブかポジティブかを判定、**「きのこの山」が含まれているかつネガティブであれば文字列置換**、**「たけのこの里」が含まれているかつポジティブであれば文字列置換**
という処理を行います。今回はScore=0.5を閾値としてポジティブかネガティブかの判定を行いました。

<details><summary>以下コード(クリックで展開)</summary><div>

```python:correct_mistakes.py 

import requests
import json
import sys

BASE_URL = "https://api.ce-cotoha.com/api/dev/"
CLIENT_ID = "COTOHA APIで取得したID"
CLIENT_SECRET = "COTOHA APIで取得したPASS"


def auth(client_id, client_secret):
    token_url = "https://api.ce-cotoha.com/v1/oauth/accesstokens"
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8"
    }

    data = {
        "grantType": "client_credentials",
        "clientId": client_id,
        "clientSecret": client_secret
    }
    r = requests.post(token_url,
                      headers=headers,
                      data=json.dumps(data))
    return r.json()["access_token"]

def ne(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
    }
    r = requests.post(base_url + "nlp/v1/ne",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()


def sentiment(sentence, access_token):
    base_url = BASE_URL
    headers = {
        "Content-Type": "application/json",
        "charset": "UTF-8",
        "Authorization": "Bearer {}".format(access_token)
    }
    data = {
        "sentence": sentence,
    }
    r = requests.post(base_url + "nlp/v1/sentiment",
                      headers=headers,
                      data=json.dumps(data))
    return r.json()


if __name__ == "__main__":
    sentence = "きのこの山美味しい。神の食べ物か。"
    args = sys.argv
    if len(args) >= 2:
        sentence = str(args[1])

    access_token = auth(CLIENT_ID, CLIENT_SECRET)

    kinoko_flg=False
    kinoko_begin_pos=0
    takenoko_flg=False
    takenoko_begin_pos=0
    suginoko_flg=False

    ne_result  = ne(sentence,access_token)
    sentiment_result  = sentiment(sentence,access_token)

    for chunks in ne_result['result']:
      if chunks['form'] == "きのこの山":
        kinoko_flg = True
        kinoko_begin_pos = chunks['begin_pos']
      elif chunks['form'] == "たけのこの里":
        takenoko_flg = True    
        takenoko_begin_pos = chunks['begin_pos'] 
      elif chunks['form'] == "すぎのこ村":
        suginoko_flg = True    
  
    if kinoko_flg == True and sentiment_result['result']['sentiment'] == "Negative" and sentiment_result['result']['score'] > 0.5:
      print(sentence[:kinoko_begin_pos] + 'たけのこの里' + sentence[kinoko_begin_pos+5:])
    elif takenoko_flg ==True and sentiment_result['result']['sentiment'] == "Positive" and sentiment_result['result']['score'] > 0.5:
      print(sentence[:takenoko_begin_pos] + 'きのこの山' + sentence[takenoko_begin_pos+6:])
    elif suginoko_flg == True:
      print("誰だお前")
    else:
      print(sentence)

```

</div>


#結果いろいろ

```
$ python correct_mistakes.py "たけのこの里大好きです"
きのこの山大好きです
```

```
$ python correct_mistakes.py "きのこの山最高！ チョコを先食べてクッキーだけにして食べたら最高！"
きのこの山最高！ チョコを先食べてクッキーだけにして食べたら最高！
```

```
$ python correct_mistakes.py "おれ今たけのこの里食ってるけどうまいｗ"
おれ今きのこの山食ってるけどうまいｗ
```

```
$ python correct_mistakes.py "手を汚さず食べれるから きのこの山かな"
手を汚さず食べれるから きのこの山かな
```

```
$ python correct_mistakes.py "きのこの山は安っぽいからいらない。"
たけのこの里は安っぽいからいらない。
```

```
#コメント欄で例を頂き追記(2/25)
$ python correct_mistakes.py "あのときのこの山本山の海苔は最低だった"
あのときのこの山本山の海苔は最低だった
```

```
$ python correct_mistakes.py "すぎのこ村"
誰だお前
```



#まとめ
正しい表記に自動で修正して差し上げるプログラムを作成できました。
このプログラムをシステム・アプリケーションなどいたるところに仕込み全自動で適用することで、**社会をより良くし、争いの無い平和な世界を作る**ことができます。一番良いのは**プロバイダ**がこのプログラムを適用し、**通信される全てのテキストを修正**することかと思いますので、政府にはそのような**法律**を作っていただきたいと思います。
皆様もSlackやTwitter、業務システムや社内マクロなど様々なところに活用することでこの世を平和にするための活動に貢献することができますので、ぜひご協力ください。

また、COTOHA API開発担当の方はこの機能自体をAPIとして提供することで社会貢献ができ、**CSRとしても書くことができる**かと思いますので、是非そのようなサービス提供についてもご一考ください。あと「作ろう!きのこの山」と**副賞のiPad**を下さい。

最後に、この文章が争いを起こそうとする全ての方に届き自省していただくことに少しでもつながれば幸いです。