当大数据进入厨房:让大数据教你做菜!
原文链接 | 2016-01-18 | 苏剑林
说在前面#

在空间侧边栏的笔者的自我介绍中,有一行是“厨房爱好者”,虽然笔者不怎么会做菜,但确实,厨房是我的一个爱好。当然,笔者的爱好很多,数学、物理、天文、计算机等,都喜欢,都想学,弄到多而不精。在之前的文章中也已经提到过,数据挖掘也是我的一个爱好,而当数据挖掘跟厨房这两个爱好相遇了,会有什么有趣的结果吗?
笔者正是做了这样一个事情:从美食中国的家常菜目录下面,写了个简单的爬虫,抓取了一批菜谱数据下来,进行简单的数据分析。(在此对美食中国表示衷心感谢。选择美食中国的原因是它的数据比较规范。)数据分析在我目前公司的高性能服务器做,分析起来特别舒服~~
这里共收集了18209个菜谱,共包含了9700种食材(包括主料、辅料、调料,部分可能由于命名不规范等原因会重复)。当然,这个数据量相对于很多领域的大数据标准来说,实在不值一提。但是在大数据极少涉及的厨房,应该算是比较多的了。
简单的统计#
最简单可以做的,就是对材料进行统计分析。大家猜一下,出现最多的什么呢?
不用对美食有什么研究,读者都应该可以想到,出现最多的肯定是盐!盐常被称为“百味之首”,很少有菜是不放盐的。接下来是料酒,再接下来是生抽,都是调味料、配料等,从中也可以看出,中国菜是非常讲究用料的,各种配料让人满目林立。主菜中,土豆出现在28位,五花肉出现在38位,等等。
盐 11200
料酒 4601
生抽 4413
姜 3671
葱 2854
鸡精 2579
白糖 2440
糖 2303
油 2297
蒜 2058
鸡蛋 1924
酱油 1883
老抽 1625
胡椒粉 1619
花椒 1571
胡萝卜 1324
……
Word2Vec的结果#
把每个菜谱看成一个分好词的句子,我们可以用这个“语料库”来训练一个Word2Vec模型,看看能得到什么有趣的结果?(就算没得到结果也不要紧,反正是探索嘛。)整个训练过程居然很快,几乎不到一秒钟。
简单对不了解的读者科普一下,Word2Vec是一个可以将词语转化为实数向量的模型,词语只有转化为数字后,才能交给计算机处理。而Word2Vec得到的向量还有一些特殊性质,比如两个词向量的余弦值,代表着两个词的相似程度。
Word2Vec的模型训练出来后,首先可以做的是比较两个词语的相似度,有些结果是很普通的,比如
pd.Series(model.most_similar(u’五花肉’))
0 (排骨, 0.882662177086)
1 (带皮五花肉, 0.866969347)
2 (干豆角, 0.864805340767)
3 (鹌鹑蛋, 0.850470840931)
4 (酸菜, 0.842567443848)
5 (鸭腿, 0.841659963131)
6 (三黄鸡, 0.837065219879)
7 (老卤汤, 0.828875720501)
8 (鸡珍, 0.827436089516)
9 (鲫鱼, 0.826281666756)
然而会有些让人意外的结果,比如:
pd.Series(model.most_similar(u’鸡肉’))
0 (玉米, 0.939546108246)
1 (茶树菇, 0.914446234703)
2 (甜玉米, 0.888315618038)
3 (鲜虾, 0.88096922636)
4 (蟹味菇, 0.870144784451)
5 (红萝卜, 0.86743336916)
6 (意面, 0.864846467972)
7 (丘比沙拉汁, 0.860477805138)
8 (猪里脊, 0.85995388031)
9 (白蘑菇, 0.855247914791)
这里显然“鸡肉”跟“玉米”居然高度相似!这表明,它们之间肯定有不得不说的故事。
背后的原因在哪里呢?Word2Vec模型的原理在于词的共现,因此,这个现象的原因可能是:******1、玉米和鸡肉经常放在一起煮;2、玉米和鸡肉经常分别和相似的菜料一起煮。事实上稍加观察,就会发现两者均有,主要是它们经常用来熬汤,配料也类似**:
含有鸡肉的菜谱(部分):
140 [鸡肉, 玛咖, 枸杞, 红枣, 桂圆, 莲子, 姜]
144 [鸡肉, 二荆条, 植物油, 老姜, 大蒜, 干辣椒, 花椒, 盐, 料酒, 生抽, 白糖]
267 [鸡肉, 土豆, 青椒, 洋葱, 面粉, 姜蒜, 小辣椒, 老抽, 白糖, 生抽, 花椒大料…
313 [鸡肉, 啤酒, 土豆, 花椒, 八角, 小辣椒, 姜蒜, 生抽, 老抽]
520 [鸡肉, 面包糠, 鸡蛋, 糯米粉, 生抽, 蚝油, 盐, 胡椒粉]
961 [胡瓜, 鸡肉, 虾干, 红枣, 姜, 八角, 葱]
1005 [糯米, 鸡肉, 虾仁, 姜丝, 葱花, 生抽, 蚝油, 胡椒粉, 淀粉]
1095 [鸡肉, 鸡蛋清, 面粉, 生姜, 大蒜, 料酒, 盐, 黑胡椒]
1178 [鸡肉, 牛奶, 盐, 胡椒粉, 蒜粉, 低筋面粉, 淀粉, 冰水, 花生碎, 食用油, 麦…
1551 [鸡肉, 口蘑, 洋葱, 干辣椒, 葱, 姜, 蒜, 花椒, 冰糖, 盐]含有玉米的菜谱(部分):
106 [鸡翅, 排骨2~3块, 盐, 姜, 芹菜, 料酒, 胡萝卜, 玉米, 红枣, 洋参片, 虫…
172 [山药, 玉米, 龙骨, 盐, 姜]
316 [猪骨, 玉米, 铁棍准山, 红枣]
441 [玉米, 番茄, 豆腐, 植物油, 老姜, 大葱, 花椒, 盐, 牛肉粉]
450 [山药, 排骨, 胡萝卜, 生姜, 料酒, 十三香, 枸杞, 玉米, 葱段, 八角, 精盐]
483 [红萝卜, 南瓜, 西芹, 玉米, 西兰花, 松子]
485 [莲藕, 胡萝卜, 香菇, 花生, 红枣, 玉米, 姜片]
509 [排骨, 玉米, 杏鲍菇, 枸杞, 香叶, 味精, 盐]
789 [鸡翅, 金针菇, 香菇, 白菇, 鸡腿菇, 滑子菇, 干贝, 虫草花, 玉米, 盐, 姜,…
828 [肉丁, 玉米, 胡萝卜, 鱼露, 生抽, 食盐, 花生油]
看来我们的玩意还确实产生了一些有趣的结果,单从我们的经验可能不容易发现“鸡肉”跟“玉米”的相关性,然而通过数据挖掘,只要有了足够多的数据,均可以发现一些有趣的结果。类似的结果还有:牛肉跟鱿鱼的相似性达到了96%,跟土豆的相似性有91%,等等!大家可以看着下面的数据,试图解释一下牛肉跟鱿鱼的结果:
含有牛肉的菜谱(部分):
46 [牛肉, 胡萝卜, 香葱, 咖喱粉, 盐, 椰浆, 土豆, 洋葱, 姜, 韩国酱油, 泰式鱼露]
70 [牛肉, 木耳, 胡萝卜, 红彩椒, 青椒, 葱花, 姜丝, 蒜末, 花生油, 郫县豆瓣酱,…
148 [牛肉, 青椒, 洋葱, 姜蒜, 花椒, 香叶, 八角, 老抽, 生抽, 孜然粉, 胡椒粉]
272 [牛肉, 毛芋, 八角, 香叶, 桂皮, 花椒, 姜, 辣椒, 冰糖, 葱, 盐, 料酒, 生抽]
290 [牛肉, 胡萝卜, 洋葱, 红酒, 肉汤, 盐, 胡椒粉, 番茄酱, 黄油, 面粉, 月桂叶…
404 [牛肉, 香叶, 八角, 花椒, 姜蒜, 蚝油, 生抽, 盐, 鸡精]
433 [豆芽, 牛肉, 葱, 青椒, 蚝油, 盐]
452 [牛肉, 白砂糖, 花椒, 酱, 盐, 胡萝卜, 酱油, 八角, 蒜, 料酒]
455 [牛肉, 干黄酱, 十三香, 肉桂粉, 八角粉, 花椒粉, 姜粉, 盐, 老汤]
534 [土豆, 牛肉, 盐, 料酒, 生抽, 大蒜, 生姜, 香菜]含有鱿鱼的菜谱(部分):
187 [鱿鱼, 排骨酱, 糖, 耗油, 洋葱, 辣椒, 甜椒]
284 [鱿鱼, 甜椒]
374 [鱿鱼, 洋葱, 白芝麻, 蒜蓉辣酱, 葱, 姜, 香葱, 烧烤酱]
996 [洋葱, 丝瓜, 鱿鱼, 油, 盐, 酱油, 白糖, 料酒, 耗油]
1468 [鱿鱼, 圆葱, 生菜, 一品鲜酱油, 蒜蓉辣椒酱, 糖, 蚝油, 料酒, 盐, 鸡精]
1502 [鱿鱼, 青红辣椒, 姜蒜, 盐, 白糖, 生抽, 淀粉, 花椒粒]
1577 [鱿鱼, 彩椒, 黄豆酱, 姜]
1619 [虾, 白贝, 鱿鱼, 草菇, 东阴功酱, 鲜柠檬叶, 鱼露, 椰浆, 糖]
1796 [蛏子, 鱿鱼, 韭菜, 白胡椒粉, 料酒, 盐, 姜]
1798 [鱿鱼, 孜然, 盐, 花生油]
Apriori关联规则#
另一个可能有意义的尝试是挖掘关联规则,由于数据量不大,这里简单地使用Apriori算法。

在挖掘规则之前,首先对数据进行一下预处理:1、把盐去掉,因为盐数目太大,如果不去掉,挖掘出来的规则很多都包含盐的,那无外乎就是告诉我们“做菜记得放盐”,这是没有什么意义的规则;2、去掉只出现一次的材料,这些材料信息量太少,基本不会出现在规则中,如果不去掉,会增加计算量。
这样处理之后,如果要求支持度(规则的出现比例)为0.01,置信度(规则的可信度)为0.8,那么得到以下规则:
规则 支持度 置信度
料酒–葱–蒜–姜 0.019935 0.912060
糖–葱–蒜–姜 0.011203 0.879310
料酒–花椒–葱–姜 0.010544 0.872727
料酒–老抽–葱–姜 0.011643 0.868852
八角–葱–姜 0.016695 0.858757
料酒–糖–葱–姜 0.013345 0.846690
料酒–生抽–蒜–姜 0.013345 0.840830
生抽–葱–蒜–姜 0.012741 0.840580
老抽–蒜–姜 0.013290 0.831615
花椒–葱–姜 0.019221 0.825472
料酒–蒜–姜 0.032511 0.821082
料酒–生抽–葱–姜 0.019057 0.816471
老抽–葱–姜 0.017903 0.808933
料酒–葱–姜 0.050799 0.805749
它们的意思是:出现了料酒、葱、蒜,那么也要把姜放下去;出现了糖、葱、蒜,也要记得放姜;等等。这些规则都以姜结束,告诉我们什么时候需要放姜,在做菜中,这些规则是比较有意义的(当然是对于新手来说)。这个规则也说明,中国菜中,姜是一个很重要的配料。
可以稍微放宽一点条件,试图挖掘到更多的规则。将置信度要求降低到0.7,得到
规则 支持度 置信度
葱–蒜–姜 0.038497 0.799316
桂皮–香叶–八角 0.018013 0.782816
冰糖–桂皮–八角 0.010160 0.780591
花椒–蒜–姜 0.014169 0.779456
生抽–香叶–八角 0.010874 0.770428
料酒–桂皮–八角 0.014938 0.764045
桂皮–八角 0.031633 0.761905
桂皮–花椒–八角 0.015267 0.761644
老抽–香叶–八角 0.011423 0.759124
胡椒粉–葱–姜 0.014498 0.758621
桂皮–老抽–八角 0.013070 0.757962
糖–葱–姜 0.022297 0.753247
桂皮–生抽–八角 0.011148 0.751852
料酒–香叶–八角 0.012411 0.750831
花椒–香叶–八角 0.014608 0.745098
葱–醋–姜 0.011203 0.744526
冰糖–生抽–老抽 0.010929 0.742537
香叶–八角 0.028777 0.738028
姜–桂皮–八角 0.011917 0.735593
淀粉–葱–姜 0.011038 0.730909
姜–香叶–八角 0.010215 0.723735
姜–糖–蒜–葱 0.011203 0.720848
糖–蒜–姜 0.015542 0.712846
生抽–葱–姜 0.032402 0.704898
葱–酱油–姜 0.017244 0.700893
如果说关于姜的规则还是过于普通,那么这时候得到的规则应该就更加有意义了,比如说“桂皮–香叶–八角”、“冰糖–桂皮–八角”、“桂皮–生抽–八角”等等,这些组合应该就是卤味相关的配方,这些配方对于一般的厨房爱好者来说不一定清楚,但是通过关联规则,可以将它们挖掘出来。
还有一些置信度更高、支持度稍低的规则:
规则 支持度 置信度
料酒–老抽–葱–蒜–姜 0.005602 0.953271
料酒–花椒–葱–蒜–姜 0.005547 0.952830
料酒–糖–葱–蒜–姜 0.007634 0.952055
料酒–桂皮–葱–姜 0.005711 0.936937
料酒–生抽–葱–蒜–姜 0.007743 0.921569
桂皮–花椒–葱–姜 0.005217 0.913462
料酒–白糖–蒜–姜 0.005931 0.805970
料酒–葱–姜 0.050799 0.805749
冰糖–老抽–香叶–八角 0.005162 0.803419
这是更加详细精准的调料配方。要注意,这是计算机自动挖掘出来的结果,大厨是我们的计算机。
最后#
本文试图将自己感兴趣的两个爱好——数据挖掘和厨房——结合起来,得到一些看上去比较有趣的结果。事实上是得到了一些看上去有趣的结果,当然,里边也包含了一些笔者对厨房的认识,但是否真的有趣,还需要读者来评价。这样的挖掘本质上是文本挖掘,或者可以归结为自然语言处理的领域,从本文可以看到,所用的方法基本也是自然语言处理的方法,而了解的读者就会知道,自然语言处理领域的难度在于特征的构建——即怎么用数字来表达一个词语。本文做了一个尝试,但是由于数据量不够等原因,其中的结论不一定准确,这个尝试不算特别成功,但是过程是具有参考性的。也许更多的数据,能够增加研究结果的价值。从直觉来看,这种挖掘是有意义的,我们可以从一个普通的领域中,挖掘出一些我们不知道的信息,也许这些信息我们都知道,只是未曾留意过。但计算机帮助我们发现了它,从而能够让我们正视它,或者更好地利用它。数据挖掘可以帮助我们更好地生活。的确,数据挖掘技术应该平民化、大众化,因为我们的生活才是我们最重要的数据来源。
最后分享所爬取到的数据:菜谱数据.zip