LoginSignup
8
6

More than 5 years have passed since last update.

AIに評価の高いワインを選んでもらう

Last updated at Posted at 2018-12-22

はじめに

はじめまして。
株式会社アドベンチャーのへっぽこサーバーサイドエンジニア、須藤と申します。

歳をとるにつれ、色々とやるやる詐欺が多くなってきてる中、今回、会社としてAdventカレンダーをやろうということで、

  • 発信側として技術ブログを書いてみる
  • AIに触れてみる

というやるやる詐欺リスト消化のため、そして、来たるクリスマス、誰も選んでくれないので、表題のプログラムを作成して癒されたいと思います。
(直前で他のメンバーとお題が被って焦りましたが、私の方はより初級なのできっと大丈夫。ヘーキヘーキ。区別できてる:frowning2:)

少々長いし文字も多いのですが、ご容赦ください:bow:

ちなみに他のリストは
・Railsさわる
・Vue.jsさわる
・部屋掃除する
などです。

AIについて

ここ数年で一気に広まりましたね。「そろそろうちも導入するか」とお偉方から言われてる企業もあるかと思います。
かつてのクラウドと同じように、細かいことはわからないが、とりあえず導入しとけば全て解決、みたいに思われてる節も見受けられますが。。

比較的最近から賑わったAIですが、実は随分昔、幼少期に身近なところで触れていました。

そう、国民的RPG、ドラゴンクエストの4作目、ドラクエ4(FC版)です。

総勢8人の中で、コマンド選択できるのは勇者ただ1人で、仲間は学習型AIだったのです。

例えば、クリフトは初見の敵に対して、効かないにもかかわらず勝手にザラキを唱えますが、
二度目はそれを学習して別行動を取っていたようです。へぇぇ。(記憶にない)
そうとは知らず、クリフト使えねぇ、やっぱミネアに、って人多いかとおm(略)

ドラクエ4は今から28年前に発売なので、そう思うとゲーム業界のエンジニアって、やはりすごいなぁと感じます。

まずはPython触ってみる

AIに最適な言語だとPythonが出てきますが、以前会社の人に「なんでPythonなの?」と
聞かれ、調べたことがあります。ざっくり言うと

  1. とある人がちょっと計算に便利なライブラリを作った
  2. とある企業がとても計算に強いパッケージを作った
  3. オープンソースなので開発コミュニティが賑わった

PythonはPHPと同じスクリプト言語で、元々計算向けに作ったわけじゃないそうなので
1.の人の好きな言語がたまたまPythonだったから、というのは過言ではないと思います。

言語の流行り廃りと一緒ですね。風前の灯火だったRubyがRailsで爆発的に伸びたとか、
JavascriptがGoogle Mapで一気に使える子認識されたとか。
そのときすごい技術力を持った人の好み、ゴリ押し、など、背景を調べるとちょっとおもしろいです。

Pythonの機械学習ライブラリで有名なところは以下があります。(下のほうが新し目)
・NumPy
・SciPy
・matplotlib
・pandas
・scikit-learn

とりあえず、上記の中から「scikit-learn」を使って何か簡単なものを作ってみます。
(サイキット・ラーンです。寿司キットではないです。)
Python学習のど定番ライブラリということなので、入門に良いですね。

開発環境にはエディター・実行環境を1つにまとめた便利ツール「Jupyter Notebook」を使います。
そして上記のライブラリ群と、Python本体をまとめた「Anaconda」をインストールしときます。
※ インストール工程は割愛。毎度のことながら、すんなりとはいきませんでした:frowning2:
※ すんなりいかなかったので、Python本体、ライブラリを個別に入れました、、

お題はそうですね。させるほどではないけど、AND演算子の動きを機械学習させてみます。
AND演算子はビット演算の1つで 全部1のときだけ1が返る、あれです。

まず手始めに誤った学習データを与えてみます。
Pythonで記述。セミコロンがないと、、なんか、、そわそわします。

# ライブラリのインポート
from sklearn.svm import LinearSVC
from sklearn.metrics import accuracy_score

## 学習データ用意
# パターン
learn_data = [[0,0], [0,0]]
# 覚えてほしい結果(カンマごとに上の一次元目の配列に対応)
learn_result = [0, 1]

# 使用アルゴリズム(今回はLinearSVC)
# SVMアルゴリズムは2クラス分類(0,1など)用だそうです。
clf = LinearSVC()

# パターンと覚えてほしい結果を食べさせて学習させてみる
clf.fit(learn_data, learn_result)

# テストデータを与えてみる(全パターン)
test_data = [[0,0], [1,0], [0,1], [1,1]]
test_result = clf.predict(test_data)

# 結果出力
print(test_data, "の結果:", test_result)

# 正解もわかるので正解率も出してみる
expect_result = [0, 0, 0, 1]
print("正解率 = ", accuracy_score(expect_result, test_result) * 100, "%")

AIが出した答え: [1 1 1 1]
正解率 = 25.0 %

はい。見事にアホな子に育ちました。
そもそも[0,0]のときに、0にもなる、1にもなるという学習をさせてるので
回答が不安定です。何回か実行すると、たまに0を返すので正解率75%にもなります。

次は学習データをきちんとさせてみます。

## 学習データ用意
# パターン
learn_data = [[0,0], [1,0], [0,1], [1,1]]
# 覚えてほしい結果
learn_result = [0, 0, 0, 1]

学習データだけ変えてみました。他のコードは全く一緒です。
結果は、、

AIが出した答え: [0 0 0 1]
正解率 = 100.0 %

ほぼカンニングみたいですが、当然何回やっても100%です。

このように、今はライブラリや環境が充実しているので、手軽に試せます。

本題

ではいよいよワイン選びをしてみます。
書籍と睨めっこしてなんとなくわかったのが、機械学習で重要なのは主に以下4点でしょうか。
1.学習データの用意
2.テストデータの用意
3.1と2の答えの用意
4.アルゴリズムの選定

1は言わずともわかりますね。
2は学習がうまくいっているか確認するためのデータです。自信があるならいらないです。
3は1と2が何をもって正しいとするか、の値です。機械学習ではこれを「ラベル」と呼びます。
4は正解率に大きく作用します。ワイン選びに前回の2クラス分類は使いづらそうですね。

3について、正解は人によって様々です。
ワインデータで言えば 「フランスのシャトー・マルゴー 2005年は98点」というデータの場合、
仮に点数をラベルとするなら、「フランスのシャトー・マルゴー 2005年」がデータで「98点」がラベル、ということになります。
上記を学習させて、同じようなテストデータを与えたら、98点!と算出してくれたら優秀な子ということになります。

テストデータにもラベルが必要な理由は、AIが出した点数と、ラベルで定義している点数を比較して
正解率を出すためです。これによって信頼できるAIかどうかが判断できるわけですね。
90%超えてたら世に公開してもいいんじゃないでしょうか。

そんなわけでまずは学習データとテストデータを用意します。
初めは神の雫に出てきたワインと、そのときのキャラクターのお、、おぉぉ、、な表現を点数化してリストにしようと思いましたが、いまいち点数化しづらいのでやめました。

スクリーンショット 2018-12-21 1.07.55.png

理想としては銘柄、産地、ヴィンテージ(年代)、パーカーポイント(※)がリストになっていると
非常にありがたかったのですが、散々探してみましたが、全然見当たらず、どこかの英語サイトで
$100払ってやっと見れそうな感じでしたので、過程でみつけたamazonを使うことにします。
※ロバート・パーカーJrというワイン評論家のポイント。めっちゃ市場価格に影響がでる

amazonでワインを検索したら左側にスコア絞り込みがあることに気づきました。
これを5段階評価として、各評価別に5本ずつぐらい抜粋していきます。
80-84 → 1、85-89 → 2 という具合で、1が一番低いということにします。
一覧.png
このリストの各詳細ページを見ると
詳細.png
こんな欄がありますので、これの必要な部分をExcelに転記していきます。
。。。これ、地味に大変です。
たまに産地とかヴィンテージ(年代)がなかったりするので、
なかったらamazon取扱開始年を入れちゃいました。

んで、できたのがこちら

name year country area color price star point
イエローテイル 2012 オーストラリア その他 916 4 1
デリカート 2015 アメリカ カリフォルニア 2208 0 1
クロード・デュガ 2014 フランス ブルゴーニュ 6159 0 1
シャトー・クーフラン 2006 フランス ボルドー 4860 0 1
イエローテイル・シラーズ 2009 オーストラリア その他 916 4.5 2
ベリンジャー・ファウンダース・エステート 2015 アメリカ カリフォルニア 2450 5 2
モンテス・アルファ 2012 チリ その他 2138 3.5 2
コロンビア・クレスト・グランド・エステーツ 2014 アメリカ ワシントン 2830 0 2
エヴォディア 2012 スペイン カラタユー 1287 4 3
マルケス・デ・リスカル・ティント・レセルバ 2012 スペイン リオハ 2242 3.5 3
ラ・キュベ・ミティーク 2009 フランス フランス 1399 3.5 3
ルーチェ・デッラ・ヴィーテ・ルチェンテ 2013 イタリア トスカーナ 4268 0 3
ヌマンシア 2010 スペイン トロ 5810 5 4
レイク・ブリーズ 2013 オーストラリア ラングホーン・クリーク 3456 0 4
ドッグ・リッジ 2013 オーストラリア マクラーレン・ヴェイル 5832 0 4
バッカ ロッサ 2012 イタリア ラツィオ 4133 5 4
ペンフォールズ g3 2018 オーストラリア その他 324000 0 5
ギガル・コート・ロティ・ ラ・ランドンヌ 2015 フランス その他 44094 0 5
シャプティエ・エルミタージュ・ル・メアル・ルージュ 2009 フランス その他 39774 0 5
ペンフォールズ・グランジ 2015 オーストラリア その他 108000 0 5

一番重要なのは一番右のpointです。これをラベルに使います。
あとstartは★の数です。とりあえずいれときました。

んん、、全然知らんワインばっかり、、もっと素人でもわかる有名どころも欲しかったなぁ、、
しかもいきなり評価1が4本しかなかったので、各評価左上から4本ずつ転記してみました。

なんか確実に評価5の価格が全てをぶち壊している気がします、、
そもそも学習データにするには数が少なすぎますが、この際良しとします。

コードを書いていきます。

# 色んなライブラリ読み込み
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier 
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
import pandas as pd

# データ読み込み
wine = pd.read_csv("ワインリスト.csv", sep=",", encoding="utf-8")

# データをラベルとデータに分離
y = wine["point"]
x = wine.drop("point", axis=1)

# 文字列は学習できないっぽいので数値に
x["name"], _ = pd.Series(x["name"]).factorize()
x["country"], _ = pd.Series(x["country"]).factorize()
x["area"], _ = pd.Series(x["area"]).factorize()
x["color"], _ = pd.Series(x["color"]).factorize()

# 学習用とテスト用に分割(便利関数!)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)

# モグモグさせる
model = RandomForestClassifier()
model.fit(x_train, y_train)

# テストデータを評価してみる
y_pred = model.predict(x_test)

# 正解率を見てみる
print("正解率=", accuracy_score(y_test, y_pred))

便利関数で20個の学習データのうち、2割(4個)をテストデータとして切り離します。

そして、今回は「ランダムフォレスト」というアルゴリズムを使っていきます。
二分木みたいなツリー構造をいっぱい作って、多数決で決めるらしいです。
中身はよくわかりませんが、精度は良いみたいです。

最初CSVをそのまま分類器にかけたら型が違うみたいなエラーが出たので
途中で全部数値化してます。文字列ダメみたいです。

では正解率を見てみます。

正解率= 50.0 %

。。。ちょっと学習データが足りなすぎたのか、一か八かな子が出来上がりました。
何度か実行すると25%〜100%まで変動し、体感で平均50%ぐらいだったので50%とします。
本来AIとしてはダメダメですが、アホな子でも可愛い我が子なので、この子に判定してもらいます。

どうせならりんなみたいに名前でもつけてみようかな。
クリスマス由来にしようかと思いましたが、思いつかなかったので、正月由来で「おもち」にします。

どこの中から選んでもらうかなと色々検索してみましたが、楽天にしてみました。
学習データがamazonで、探すデータが楽天というのもまたオツです。
★評価があるのも都合がよいです。

意図せず学習データが赤ワインだらけになってしまったので、楽天の赤ワインランキングから選んでもらおうと思います。 数が多いので1位〜50位ぐらいまでにしときます。
セットものや、銘柄・年代が被っているものは飛ばします。
楽天.png

また転記作業をはじめます。スクレイピングでやれよ、とかはご勘弁ください。

name year country area color price star
シャトー・レスタージュ・シモン 1996 フランス ボルドー 4298 4
バローロ・リゼルヴァ・コルダーナ 2009 イタリア ピエモンテ 3348 4
バローロ・リゼルヴァ・コルダーナ 2010 イタリア ピエモンテ 3488 0
ルモワスネ・ニュイ・サン・ジョルジュ 1979 フランス ブルゴーニュ 21500 0
ラ・ヴィ 2016 ルーマニア ムンテニア 1096 0
バローロ・テルツェット 2014 イタリア ピエモンテ 2041 0
シャトー・ラ・クロワ・サン・ジョルジュ 2014 フランス ボルドー 8046 0
ボデガス・アルト・モンカヨ 2016 スペイン その他 6458 0
プリミティーヴォ・ディ・マンドゥーリア 2017 イタリア モンテポルツィオ・カトーネ 1231 4
ブルゴーニュ・ルージュ・クーヴァン・デ・ジャコバン 2015 フランス ブルゴーニュ 2030 0
オーパス・ワン 2014 アメリカ カリフォルニア 43800 0
シャノン・ヴィンヤード・マウント・バレット・メルロー 2013 南アフリカ エルギン 6026 5
ランゲ・ネッビオーロ・カーサ・ヴィニコラ・ニコレッロ 2002 イタリア ピエモンテ 1760 4
アナベラ・プラチナム・オークヴィル・ナパ・ヴァレー・カベルネ・ソーヴィニヨン 2015 アメリカ カリフォルニア 4147 0
サンタ・ヘレナ・アルパカ・カベルネ・メルロー 2015 チリ セントラル・ヴァレー 6098 4
アンティノリ・ティニャネロ 2015 イタリア トスカーナ 8964 0
シャトー・ラ・トゥール・フィジャック 2014 フランス ボルドー 7430 0
シャトー・ディッサン 2013 フランス ボルドー 5918 0
ドメーヌ・ド・ベレーヌ・ヴォルネイ・レ・グラン・ポワゾ 2016 フランス ブルゴーニュ 9590 0
バルコ・ヴィエホ・カベルネ・ソーヴィニヨン 2017 チリ コルチャグア・ヴァレー 532 4
ワイ・バイ・ヨシキ・カベルネ・ソーヴィニヨン・カリフォルニア 2016 アメリカ カリフォルニア 5940 4.5
コレッツィオーネ・チンクアンタ・+2・NV・サン・マルツァーノ 2016 イタリア プーリア 2646 4.5
ナパ・ハイランズ・カベルネ・ソーヴィニヨン・ナパ・ヴァレー 2015 アメリカ カリフォルニア 4838 3.5
シックス・エイト・ナインナパ・ヴァレー・レッド 2016 アメリカ カリフォルニア 2350 5
テッレ・デル・バローロ・バローロ 2012 イタリア ピエモンテ 3650 2.5
バスタルド 2008 スペイン モンテレイ 10800 0
フランシスカン・ナパヴァレー・カベルネソーヴィニヨン 2015 アメリカ カリフォルニア 2862 4.5
ドメーヌ・ヴァンサン・ルグー・ブルゴーニュ・オート・コート・ド・ニュイ・ルージュ レ・ボー・モン・リュソ 2014 フランス ブルゴーニュ 4838 4
メルキュレ・プルミエ・クリュ・クロ・デ ミグラン 2016 フランス ブルゴーニュ 3866 4
ロベール・シュヴィヨン ニュイ・サン・ジョルジュ・1er・レ・プリュリエ 2016 フランス ブルゴーニュ 12744 0
タトール・プリミティーヴォ・サレント 2017 イタリア プーリア 2214 4.5
ロッソ・トスカーナ・NV・テヌーテ・ロセッティ 2017 イタリア トスカーナ 1144 4.5
シャトー・モンペラ・ルージュ 2015 フランス ボルドー 1980 5
シャルム・シャンベルタン 2016 フランス ブルゴーニュ 53460 0
ビオンディ・サンティ ブルネッロ・ディ・モンタルチーノ・アンナータ 2007 イタリア トスカーナ 18252 5
イ・ムリ・ネグロアマーロ 2017 イタリア プーリア 1328 4.5
スターク・コンデ・スリー・パインズ・カベルネ・ソーヴィニヨン 2015 南アフリカ ヨンカースフッ ク・ヴァ レー 4395 5
コスタセラ・アマローネ・デッラ・ヴァルポリチェッラ・クラシコ 2012 イタリア ヴェネト 5994 4
シャトー・ラフィット 2015 フランス ボルドー 3542 3.5
シャトー・ラグランジュ 2014 フランス ボルドー 4700 0

40個で力尽きました、、
でも!素晴らしい!素晴らしすぎるよ楽天ランキング!!

年代、産地、値段がすばらしくバラついていて、まさに実験にうってつけです。
スコアさえあれば、こちらを学習データにしたかったぐらいです。

ではこれを先程のプログラムに組み込みます。
ちなみにラベルを1〜5の5段階で渡しているので、結果も5段階で評価されます。
最初の1回目は5のものを全て選び、再度それを評価、点数の最も高いものをリスト化、、、
を最後の1つになるまで繰り返して選出します。
ラベルを100点満点形式で出せば一発で選出できたかもしれませんが、まあ仕方ないです。

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier 
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
import pandas as pd

# 学習用データ読み込みます
wine = pd.read_csv("ワインリスト.csv", sep=",", encoding="utf-8")

# データをラベルとデータに分離
label = wine["point"]
data = wine.drop("point", axis=1)

# 文字列は学習できないっぽいので数値に
data["name"], _ = pd.Series(data["name"]).factorize()
data["country"], _ = pd.Series(data["country"]).factorize()
data["area"], _ = pd.Series(data["area"]).factorize()
data["color"], _ = pd.Series(data["color"]).factorize()

# 今回は学習とテストには分割せず、全てを学習にまわします
#x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)

# おもちにモグモグさせる
model = RandomForestClassifier()
model.fit(data, label)

# 選抜用のデータを読み込んで文字列を数値に
senbatu = pd.read_csv("楽天ランキング.csv", sep=",", encoding="utf-8")
senbatu["name"], _ = pd.Series(senbatu["name"]).factorize()
senbatu["country"], _ = pd.Series(senbatu["country"]).factorize()
senbatu["area"], _ = pd.Series(senbatu["area"]).factorize()
senbatu["color"], _ = pd.Series(senbatu["color"]).factorize()

# 評価してもらう
pred = model.predict(senbatu)

print(pred)

このような結果が出ました。
[1 1 1 1 1 1 2 2 2 2 3 3 2 3 4 4 4 5 2 3 5 2 5 3 2 4 2 2 2 2 2 2 2 5 4 2 4 4 2 2]
これは表の上からAIが5段階で出した点数です。なので40個数字が並んでます。

実は参考にした書籍には1つを選出するようなものがなく、色々悩んで、関数探したり、dumpしたりして、上記に気付きました。
これをExcelに貼り付けてスコア5を抜き出してみます。

name year country area color price star
シャトー・ディッサン 2013 フランス ボルドー 5918 0
ワイ・バイ・ヨシキ・カベルネ・ソーヴィニヨン・カリフォルニア 2016 アメリカ カリフォルニア 5940 4.5
ナパ・ハイランズ・カベルネ・ソーヴィニヨン・ナパ・ヴァレー 2015 アメリカ カリフォルニア 4838 3.5
シャルム・シャンベルタン 2016 フランス ブルゴーニュ 53460 0

意外にも4つまで絞られました。
平均的に価格が高めですね。1個ずば抜けてるし、、

たぶんこれは学習データのスコア5の値段が全部高額だったからでしょう。
それでも1万超えがいくつかあった中でこのラインは、なかなか良心的です。
オーパス・ワンはおもちの好みではなかったようです。

ではこの4つをさらに振るいにかけて最後の1つを決定させましょう。
栄えある第一位は、、!!
 
 
 
 
ワイ・バイ・ヨシキ・カベルネ・ソーヴィニヨン・カリフォルニア
2016 アメリカ カリフォルニア
 
 

ヨシキ、アメリカ、カリフォルニアでピンときた人もいるかもしれません。
そうです。これ、X-JAPANのYOSHIKIコラボワインです。
(楽天ランキング28位)

 imgrc0074351459.jpg

リスト作ってるときに、「なんだこれ、こんな色物入れるのどうしようかな。まあ選ばれないだろうし、いいか」と思って入れたんですが、まさかこれになるとは、、

せっかくなので購入してみようと思いましたが、
スクリーンショット 2018-12-21 0.15.02.png

なんと、、確かに知り合いにもファンがいるし、最早出せば売れるんだろうなぁ、、

ちなみに4つのスコアは上から順に「1 2 1 1」でした。。
それで良いのか、、おもちよ、、

とまあ良い具合にオチがついたところで今回はこれで終わりにしたいと思います。

とはいえ機械学習にはまだニューラルネットワークを使った深層学習、すなわちディープラーニングをはじめ、色々なアルゴリズムが存在していますし、勉強しないといけないことは山程ありますから先は長いですね。

当初、膨大な航空券売上情報を使って何かできないかなーと考えたりもしたのですが、
ラベルに相当する「何を持って正解とするか」というものが思いつきませんでした。

AIは魔法ではないので、技術だけじゃなく、学習データそのものもよく考えないと思うような結果は出ないことを、よく心に刻んておきましょう!

長々とお読みいただき、ありがとうございました:relaxed:

参考

すぐに使える! 業務で実践できる! Pythonによる AI・機械学習・深層学習アプリのつくり方
 

8
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6