渋谷駅前で働くデータサイエンティストのブログ

元祖「六本木で働くデータサイエンティスト」です / 道玄坂→銀座→東京→六本木→渋谷駅前

ヒトの直感的理解は単変量モデルまで、直感を超えたければ多変量モデルへ

ちょっと前に「ワインの味わいとデータサイエンス」というお題で話してきたわけですが。



実は「単変量モデルという名の還元主義」vs.「多変量モデルに基づくデータサイエンス」というテーマを一貫して置いていたのですが、あまりそこにスポットライトが当たることはなかったなぁという印象を壇上では抱いていたのでした。ということで、ここでは改めてブログ記事の形で少し詳細に論じてみようかと思います。


なお今回使うデータは面倒なので先にRワークスペースとしてGitHubに置いてあります。DLしてきてお手元のワーキングディレクトリに置いてロードしておきましょう。



ここでは基本的に赤ワインのデータを見たいので、"winequality_red_blog.RData"を使います。白ワインのデータが別にありますが、こちらは以下のアプローチをお読みになった後で皆さん自身で試してもらえればと思います。


定量的に科学するということ=還元主義?


そもそも元ネタにした『新しいワインの科学』という本では、一貫して「ワインのブドウ栽培・醸造テイスティングを科学に基づいて良くしていきたい」という話が語られています。


新しいワインの科学

新しいワインの科学


で、その中でも3番目のテイスティングに関する章では、ワインが持つ様々な要素の味わいについてどのような化学物質が関わり、どのような作用がヒトの味覚系にもたらされ、どのような主観的な体験につながるかという話が展開されています。例えばその第3章の中では、ワインの味わいとその印象に影響する化学物質として以下の通り列挙されています。


また、ワインの基本的な香りを作る成分として20種類の化合物が知られるそうですが、ブドウの果実自体に含まれるのはそのうちたった1種類だけで、残り19種類は酵母によって生成されるのだそうです。さらにこれに間接的にワインの香りに寄与する16種類の化合物が知られ、その上極めて低濃度でもワインの風味に強い影響を与える「インパクト化合物」と呼ばれる化合物群が10種類程度知られていると書かれています。おまけに、最近の研究ではそれら揮発性成分のみならず、それ単体では何の作用も及ぼさない不揮発性化合物すらワインの風味に寄与するという報告があるそうです。


本書の中では、これらの化合物がそれぞれどのような香りや味わい、はたまた別種のヒトの主体的感覚に寄与するかが詳細に述べられています。その目的するところは言うまでもなく「ワインの成分を見ればそのワインが美味いかどうかが分かる」というモデルの構築を目指す、ということだと言い切って良いでしょう。


極端な話を言えば、例えばロバート・パーカーJr.氏に飲んでもらわなくとも、Wine Spectator誌の評論家に味わってもらわなくても、これらの化学物質の測定値の大小さえ分かれば自動的に美味いワインか否かが分かる、ということになるわけです。そんなことができれば革命的だ。。。というのはワインの愛好家の方々なら容易にピンと来るお話でしょう。世の中には新しく醸されたワインであったり、はたまた無名のドメーヌで作られた稀少なワインであったり、それがゆえに有名どころによるテイスティングも行われず、スポットライトが当たらないまま消えていくワインが沢山あります。それらのワインを単に化学的測定値のみで美味いか否かを評価できれば、こんなに良い話はないでしょう。


しかしながら、一方で同じ章および「おわりに」の中ではこんなことが指摘されています。

この種の研究からは、ワインをもっと全体的な視点で見ることの重要性が浮き彫りになる。ワインの風味を還元主義的に研究しようとすると、ワインを構成成分に分解して個々の成分を別個に調べるしかない。しかし、ワインの風味を研究する分野が成熟するにつれ、そういうやり方では限界があるということに研究者たちは気づき始めた。(p.387)


「みんなはとかく物事を単純化しようとしていましたね。個々の要素を全体から切り離して、それぞれについて個別に着目して、この物質には何々が重要だ、なぜならそれが存在すると閾値を超えるから、といった話をするわけです。でも実際には、何らかの操作をすれば同時にいくつものことに影響を与えるのですから、ひとつの物質だけを取り出して個別の閾値を語っても意味がないと思うんです」(pp.387-8)


品質とは、システム全体がもつ特徴なのである。ワインのいろいろな要素が組み合わさることで、ワインを味わうという感覚が経験できる。これは、個々の要素をばらばらに調べていてもわからない。(p.399)


この先、ワインの科学を発展させて、ワインの品質に関する理解を深めようと思うなら、還元主義のみに頼った分析的なアプローチの足かせを解く必要がある。そして、全体論的な見方も組み合わせ、ワインを飲むという経験を理解しようとしなくてはならない。(p.401)


なるほど、確かにそれはその通りかもしれません。例えば経験的に言って、アルコール度数が高くても酸の弱いワインは前者がもたらすアドバンテージ以上に「粗い」という印象を飲む人に与えてしまいます。かといって、アルコール度数も酸も強いワインであっても、糖度が低ければ酸っぱいだけの美味くないワインになってしまいます。けれども、アルコール度数も高く酸も強く糖度も高いワインというのは、度を越せば失敗作の貴腐ワインのようになりかねません。


つまり、ひとつの指標だけを見て云々するのには限界がある、ということが言えるわけです。この辺の問題を指して、恐らく『新しいワインの科学』では「還元主義的な捉え方には限界がある」ということを言っているのだと思われます。


ヒトが直感的に理解できるのは単変量モデルまで(≒古典的還元主義)


上記の問題点について、冒頭でDLした赤ワインのデータを使ってもう少し紐解いてみましょう。なお、このデータセットについての説明が上記記事中のslideshareにもありますが、以下のような構成になっています。

  • fixed acidity:酒石酸濃度
  • volatile acidity:酢酸濃度
  • citric acid:クエン酸濃度
  • residual sugar:残糖濃度
  • chlorides:塩化ナトリウム濃度
  • free sulfur dioxide:遊離SO2濃度
  • total sulfur dioxide:総SO2濃度
  • density:密度
  • pH:pH(そのまんま)
  • sulphates:硫化カリウム濃度
  • alcohol:アルコール度数
  • quality:テイスティングスコア(目的変数)


例えばアルコール度数とワインテイスティングスコアとの関係性(ここでは相関)を見るために散布図を描き、そこに適当な近似直線を手で書き加えてやるとこうなります。


f:id:TJO:20151125203344p:plain


何となくですが、「アルコール度数が高いほどテイスティングスコアも高くなる」ことが見て取れるかと思います。つまり「アルコール度数が高いワインほど美味いはず!」というわけです。けれども、アルコール度数だけでテイスティングスコアを予測するのは無謀なんじゃないかなと。これは言わば単変量モデルということになるんですが、実際アルコール度数のみで回帰し、テストデータのテイスティングスコアを予測してみるとこうなります。

> alc.lm<-lm(quality~alcohol,d.train)
> table(d.test$quality,round(predict(alc.lm,newdata=d.test[,-12]),0))
   
     5  6  7
  3  0  1  0
  4  4  0  1
  5 52 16  0
  6 21 43  0
  7  1 18  1
  8  0  1  1
> (52+43+1)/160
[1] 0.6


Accuracyという意味ではほんの60%しか当たらないわけで、モデルの予測性能としては非常に貧弱ですね。。。また上のconfusion matrixを見ても分かるかと思いますが、実はアルコール度数が高いにもかかわらずテイスティングスコアの低いワインも実は混じっているわけです。これをアルコール度数だけに基づく予測では排除できません。


これは言い換えると、ヒトが直感的に「アルコール度数が高いほど美味いワインだ!」と思ったところで、それが当たるとは必ずしも限らない(6割しか当たらない)ということを示していることになります。


では、Wine Qualityデータに収録されているその他10種類の化合物濃度とテイスティングスコアとの相関を全て並べてみたら、どうなるでしょう?


f:id:TJO:20151118125955p:plain


もうぐちゃぐちゃですね(笑)。こんなものを見ても何も分かりはしません。けれども、これこそが『新しいワインの科学』で指摘されていた「還元主義的アプローチ」の限界であり、また「全体論的アプローチ」への移行を考えざるを得ない好例だとも言えると思います。では、どうしたら良いのか?と。


多変量モデルはヒトの直感を超えるが、より正確な予測を与える上に非線形な効果も分かる


ここまで来て、ようやくこのブログらしい本題に入ります。上の記事中のslideshareにもありますが、もちろんこれは統計モデリングなり機械学習なりに基づく多変量モデルを用いれば良いわけです。このブログの読者の皆さんであれば、多変量モデルを実践する手法が統計学機械学習ともに数多あることはご存知かと。例えばランダムフォレストでやるとこうなります。

> library(randomForest)

# チューニングする
> tuneRF(d.train[,-12],as.factor(d.train[,12]),doBest=T)
mtry = 3  OOB error = 30.65% 
Searching left ...
mtry = 2 	OOB error = 31.2% 
-0.01814059 0.05 
Searching right ...
mtry = 6 	OOB error = 31.62% 
-0.03174603 0.05 

Call:
 randomForest(x = x, y = y, mtry = res[which.min(res[, 2]), 1]) 
               Type of random forest: classification
                     Number of trees: 500
No. of variables tried at each split: 3

        OOB estimate of  error rate: 29.26%
Confusion matrix:
  3 4   5   6  7 8 class.error
3 0 1   7   1  0 0   1.0000000
4 1 0  28  18  1 0   1.0000000
5 0 1 499 110  3 0   0.1859706
6 0 0 121 426 27 0   0.2578397
7 0 0   7  81 91 0   0.4916201
8 0 0   0   8  6 2   0.8750000

# mtryは3がベスト

# qualityをfactor型に直した上でランダムフォレストでモデル推定
> d.train.factor.rf<-randomForest(as.factor(quality)~.,d.train,mtry=3)

# confusion matrixを出してみる
> table(d.test$quality,predict(d.train.factor.rf,newdata=d.test[,-12]))
   
     3  4  5  6  7  8
  3  0  0  1  0  0  0
  4  0  0  4  1  0  0
  5  0  0 58 10  0  0
  6  0  1 12 48  3  0
  7  0  0  1  4 15  0
  8  0  0  0  0  2  0
> (58+48+15)/160
[1] 0.75625 # accuracy


結果的に75%以上のaccuracyを叩き出すことが出来ました。これだけワインテイスティングのスコアが当たれば、それなりに有用と言えるんじゃないでしょうか。ちなみに変数重要度を出してみるとこんな感じになります。

> importance(d.train.factor.rf)
                     MeanDecreaseGini
fixed.acidity                69.60611
volatile.acidity             97.57508
citric.acid                  68.37856
residual.sugar               65.64702
chlorides                    75.12833
free.sulfur.dioxide          61.07482
total.sulfur.dioxide         94.10219
density                      87.32159
pH                           69.21815
sulphates                   102.46872
alcohol                     133.72922


アルコールが特に重要で、次に硫化カリウム、そして酢酸濃度の順に重要だということが分かります。ただしこれらの値が高低どちらであればテイスティングスコアの向上に寄与するかは分からないので、非線形性を無視して線形回帰モデルを推定し、パラメータ(偏回帰係数)を求めるとこんな感じになります。

> summary(lm(quality~.,d.train))

Call:
lm(formula = quality ~ ., data = d.train)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.7256 -0.3538 -0.0470  0.4541  1.9565 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)          12.6307205 22.2671936   0.567 0.570644    
fixed.acidity         0.0130328  0.0270194   0.482 0.629633    
volatile.acidity     -1.1794967  0.1274792  -9.252  < 2e-16 ***
citric.acid          -0.1838340  0.1544325  -1.190 0.234093    
residual.sugar        0.0073885  0.0156617   0.472 0.637173    
chlorides            -1.6444079  0.4414953  -3.725 0.000203 ***
free.sulfur.dioxide   0.0048293  0.0022897   2.109 0.035103 *  
total.sulfur.dioxide -0.0030132  0.0007693  -3.917 9.40e-05 ***
density              -8.3935917 22.7154552  -0.370 0.711802    
pH                   -0.4270140  0.2005306  -2.129 0.033390 *  
sulphates             0.9683204  0.1226886   7.893 5.86e-15 ***
alcohol               0.2781216  0.0277798  10.012  < 2e-16 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6456 on 1427 degrees of freedom
Multiple R-squared:  0.3655,	Adjusted R-squared:  0.3606 
F-statistic: 74.73 on 11 and 1427 DF,  p-value: < 2.2e-16


アルコールと流化カリウムは高い方がテイスティングスコアが高くなり、逆に酢酸濃度は低い方がテイスティングスコアが高くなることが分かります。ところで今脇に置いておいた非線形性ですが、ランダムフォレストで目的変数をnumeric型にして回帰を選択した場合はpartialPlot関数を用いることで個々の化学測定指標とテイスティングスコアとの関係性をプロットして見ることができます。例えばアルコール、硫化カリウム、酢酸濃度、pHとテイスティングスコアとの関係性をプロットするとこうなります。

# numeric型でチューニング
> tuneRF(d.train[,-12],d.train[,12],doBest=T)
mtry = 3  OOB error = 0.3407737 
Searching left ...
mtry = 2 	OOB error = 0.3548686 
-0.04136142 0.05 
Searching right ...
mtry = 6 	OOB error = 0.3396589 
0.003271477 0.05 

Call:
 randomForest(x = x, y = y, mtry = res[which.min(res[, 2]), 1]) 
               Type of random forest: regression
                     Number of trees: 500
No. of variables tried at each split: 6

          Mean of squared residuals: 0.3225541
                    % Var explained: 50.49
# mtryは6がベスト

# ランダムフォレスト回帰
> d.train.numeric.rf<-randomForest(quality~.,d.train,mtry=6)
> table(d.test$quality,round(predict(d.train.numeric.rf,newdata=d.test[,-12]),0))
   
     5  6  7
  3  1  0  0
  4  3  2  0
  5 56 12  0
  6 16 45  3
  7  1  9 10
  8  0  0  2
> (56+45+10)/160
[1] 0.69375 # ややaccuracyは下がった
> par(mfrow=c(2,2))
> partialPlot(d.train.numeric.rf,d.train,x.var=alcohol)
> partialPlot(d.train.numeric.rf,d.train,x.var=sulphates)
> partialPlot(d.train.numeric.rf,d.train,x.var=volatile.acidity)
> partialPlot(d.train.numeric.rf,d.train,x.var=pH)

f:id:TJO:20151125211551p:plain


これを見ると、アルコールと硫化カリウムの濃度はある程度高くなるとサチってそれ以上テイスティングスコアの向上に貢献しなくなり、逆に酢酸濃度はほぼ線形に低くなれば低くなるほどテイスティングスコアの向上に貢献することが分かります。そして面白いのがpH。低い方が(≒酸味が強い方が)テイスティングスコアが高くなる一方、あちこちに凸凹した非線形な部分があることが見て取れます。これをヒトの感覚だけで突き止めるのは難しいかもしれません。


このように、多変量モデルを用いることでヒトの直感的理解を超えた「全体論」的な特徴を捉えることができるというのが今回の取り組みで示したかったことです。『新しいワインの科学』が執筆された時点ではまだ著者にはそのアイデアはなかったのかもしれませんが、このWine QualityというデータセットUCI ML repositoryに公開されていることからも分かるように、今後は多変量モデルによるアプローチが普及していくのではないかなと思っています。


実は多変量モデルこそがヒトの暗黙知としての「学習」に対応するのかも?


少し前に某所で話題になったんですが、ワインテイスティングだけでなく例えばファッションなんかにもそういう部分はあるのではないかと思っています。それこそ「青いスカーフがお洒落」とか「アンクル丈のショートブーツが可愛い」とか(超適当)、個々の要素に着目した流行というのはいつの時代にもあるわけですが、全体のコーディネートとなると結構流行の域を超えて「センス」の領域に入るような印象があるわけです。


おそらくですが、Wine AdvocateやWine Spectatorのテイスティングスコアもそういうものなのでしょう。特に前者であればかつてはロバート・パーカーJr.氏の「センス」が反映されたものであったし、そうでなくても大半のその手のテイスティングスコアは熟練したテイスティングの専門家・評論家たちが自身の長年の経験から来る「センス」に基づいて算出したものであるはずです。しかも、それらは基本的には多くのワイン消費者・愛好家から有用なものとして受け入れられ、また支持されます*1。そして、そういった「センス」とは何か?と問われた時に必ず引き合いに出されるのが「バランス」の語であるのも、また無理からぬことかと。


でも実はその「バランス」というのが、案外多変量に基づいた機械ならぬヒトが学習した結果なのだとしたら? そう考えると、面白い議論が別にできるのではないかとちょっと思ってます。Wine Advocateが「バランス」と表現しているものが、実際には多変量ベクトルの何かしらの形を表しているのだとすれば?そしてそれが実はヒトも暗黙のうちに学習できるのだとすれば?もしそうであるのならどのように説明されるべきなのか?。。。色々な推論ができそうです。

*1:その手のスコアリングに全く賛同しない人が少なくないことももちろん知ってますが