我因为写了一部人们把它和《禅与摩托车维修艺术》相比较的书而感到甚受恭维。我希望拙作(《时间简史》)和本书一样使人们觉得,他们不必自处于伟大的智慧及哲学的问题之外。——by霍金

在历经了多个早起的清晨和国庆回家路上的几个小时之后,也_勉强_算是走完了这一次的肖陶扩,这是一本看书名不知所云但是却发人深省的书,主要讲述了作者和儿子在一次穿越美国的长途摩托车旅行中发生的故事以及作者的思考和感受.在看到霍金对这本书的评价之后我才豁然:我们生活中处处充满了哲学,但是当我们听到哲学这个词的时候却会将其束之高阁,但是《禅》这本书却从旅行和摩托车修理的角度,将作者自己对哲学的思考和理解很好的讲述给了读者听,这是很了不起的.

骑摩托车旅游和其他的方式完全不同。坐在汽车里你只是被局限在一个小空间之内,因为已经习惯了,你意识不到从车窗向外看风景和看电视差不多。你只是个被动的观众,景物只能呆板地从窗外飞驰而过。 骑摩托车可就不同了。它没有什么车窗玻璃在面前阻挡你的视野,你会感到自己和大自然紧密地结合在了一起。你就处在景致之中,而不再是观众,你能感受到那种身临其境的震撼。脚下飞驰而过的是实实在在的水泥公路,和你走过的土地没有两样。它结结实实地躺在那儿,虽然因为车速快而显得模糊,但是你可以随时停车,及时感受它的存在,让那份踏实感深深印在你的脑海中。

我们不再是观众而是紧密地和作者的思想结合在了一起,深深的印在了我的脑海中.

在看这本书的过程中,我的脑海中就有一个词一直存在着:物我两忘,个人觉得这和作者想要表述的良质是比较接近的.虽然这里讲的禅和中国人对禅的定义是非常不一样的,但是从根本上来说,都是对于哲学的讨论.禅与摩托车可以对应于哲学中的物体和自我与世界的二元对立,而作者在书中着重讨论的也是古典和浪漫如何才能够有机的结合在一起,比如,在修理摩托车上.也就是说:如何将物体和自我,主观和客观统一起来,达到物我两忘的境界.

在讲到修理摩托车的时候:

在修理机器这方面,如果你的自我太强,往往无法把工作做好。因为你总是会被愚弄,很容易犯错,所以修理人员自大的个性对他颇为不利。如果你认识很多技术人员,我想你会同意我的观点,他们往往相当谦虚而且安静。当然,也有例外。不过即使他们起初无法保持安静和谦逊,长久工作下来,也会变成这样的个性。同时,他们还具有高度的警戒心,专注而又懂得怀疑,不会以自我为中心。

以我的理解,所谓的物我两忘或者修理摩托车的禅,就是要抛弃那一堆冰冷的说明书和零件分解,而将其视为一个整体,需要的是对于摩托车的整体的把握,在某个零件受损的时候,会冷静的从摩托车的角度来考虑这个问题,而不是发现一个零件坏了,就努力试图将其从摩托车上拆下来换一个新的上去,因为这样没有考虑到每一个零件和摩托车本体的联系,而只是将其视为一个零件,将问题视为一个任务去完成,很有可能这个零件的问题是由别的部位导致的,也有可能在你只想要拆除它的时候,因为你的目光只停留在这个零件上而造成了别的问题.修理一辆摩托车,需要的是掌握它的全部,不仅仅是所有的技术细节和零件,还有对车的_感性的或者浪漫的认识_,或者说,我们对这辆车的一生的每一个状态都很了解.

一旦真正地投入了工作之中,就可以说是在关心自己的工作,这就是关心的真正意义——对自己手中的工作产生了认同感。当一个人产生这种认同的时候,他就会看到关系的另一面——良质。

这是这本书中最能发我深思的地方了,而这本书作为计算机的必读书之一,我觉得最有意义的也就在于此,虽然科技的发展不仅仅只在计算机上,但是CS确实成为了现在科技发展最瞩目的地方之一,而作为一个CS的学生,在开始的阶段就读到这样的书,是我的幸运,它指引我去寻找理解世界的方式和获得内心平静的方法,而作为一个程序员,更不应只停留在技术细节和代码的世界中,我们需要的是浪漫抑或者说是编写软件的禅,它也让我理解了,什么是的,也就是,什么是良质的,我们应该如何去做技术,如何让做出的东西中有良质.

几分钟之后,我们顺着这条路骑到了山顶,然后又笔直地往山谷落下。一路风景十分优美。我觉得这个山谷和美国其他的山谷完全不同。往南边一点就是所有葡萄美酒的产地。山坡像波浪一样起伏,呈现出优美的曲线,而路也是蜿蜒曲折。我们的身体和车子缓缓地顺着山路向下走,同时向路边倾斜过去,几乎可以碰到树叶和树枝。高山地区的岩石和枞树远远落在身后,在我们周围是平缓的山坡和葡萄树,还有许多紫色和红色的花朵。从山谷冒出了浓郁的雾气,那是森林的气息和花香融合在了一起。在遥远的那一端,则是看不到但可以微微嗅得到的海洋气息。
…… 人只要活着就会发生不愉快的事和不幸的事。但是我现在有一种以前没有过的感觉,这种感觉并不只停留在表面,而是深入内里:我们赢了。情况正在慢慢好起来。我们几乎可以这样期待。

是的,情况正在慢慢好起来,我觉得一本书的价值不仅在于作者倾授了多少他的观点给你,而是在于阅读完这本书之后,能不能在你的脑海中留下作者思考的痕迹或者说潜意识,能够在遇到别的情况的时候,用别人(作者)的方式去思考问题从而获得答案,如果说读完一本书之后能够对其_久久不能忘怀_的话,那么这本书的价值就超出了其文字所能承载的了.

番外

在看到王垠的什么是“对用户友好”的时候,我觉得对用户友好这一点就像是需要结合理性的和感性的认识的一个例子,就在这里记录一下,可能这就是做一个好软件的禅吧.

爱因斯坦说:“Any intelligent fool can make things bigger and more complex… It takes a touch of genius - and a lot of courage to move in the opposite direction.”

确实,大师做的事情是都是简单的,道不远人,也就是霍金说的:

不必自处于伟大的智慧及哲学的问题之外

此言得之,而我们也不应该身处一个好的软件之外,不应该在使用它的时候还需要看一大堆的说明书和教程,好的软件,应该是傻瓜式的,也就是KISS原则.

很多程序员都会注意到这些机器界面的抽象,让使用者尽量少的接触到实现细节。可是他们却往往忽视了人和机器之间的界面。也许他们没有忽视它,但是他们却用非常不一样的设计思想来考虑这个问题。他们没有真正把人当成这个系统的一部分,没有像对待其它机器模块一样,提供具有良好抽象的界面给人。

一个良好的界面不应该是这样的。它给予用户的界面,应该只有一些简单的设定。用户应该用同样的方法来设置所有程序的所有参数,因为它们只不过是一个从变量到值的映射(map)。至于系统要在什么地方存储这些设定,如何找到它们,具体的格式,用户根本不应该知道。

所以我们看到,“对用户不友好”的背后,其实是程序设计的不合理使得它们缺少抽象,而不是用户的问题。

也就是说,虽然开发者每天都在讲抽象,但是却没有将用户也抽象进整个的系统中,或者说将用户和整个系统剥离了,使得用户所接触到的是冷冰冰的操作手册和一堆配置信息,而《禅》中告诉我们,操作手册是没用的,或者说写操作手册给用户看本身就不是一个的行为.

还记得邹欣在《构建之法》中讲的一个例子:微软会征集用户来测试office软件,而让程序员在单向玻璃后面看用户的操作,当用户对某一个操作很疑惑或者找不到某个功能的位置的时候,程序员在后面都会很着急,因为这个功能似乎就在那里,但是用户就是没有去点击它.当然测试结束之后团队是很羞愧的,因为自己为很好的用户交互逻辑,到了用户那里变成了反逻辑或者很难的操作.

这其实都从不同的角度在思考软件和人的关系,在编写软件的过程中程序员知道代码需要抽象,也都知道抽象的好处,但是在和用户的交互这一层上,这样的抽象却被剥离了,缺失了,不合理的菜单布局和操作逻辑比比皆是.

对于开发者来说,如何做出对用户友好的软件就是需要开发软件的禅的地方了.

主键

  • 总是应该定义主键
  • 主键要求:
    1. 不更新主键的值
    2. 不重用主键的值
    3. 不在主键中使用可能会更改的值

连接

  • 连接数据库: mysql -u{username} -p{password}
  • 选择表: USE tables;
  • 查看表/数据库: SHOW tables/databases;
  • 查看列: SHOW COLUMNS FROM tables;

检索数据

  • 单列: SELECT col FROM tables;
  • 多列: SELECT col1, col2 FROM tables;
  • 所有列: SELECT * FROM tables;
  • 检索不同的行: SELECT DISTINCT col FROM tables;
  • 限制结果: SELECT col FROM tables LIMIT 1,1

注意:

  1. 检索出来的数据都是未排序的,并且原始数据没有格式
  2. 一般不要使用*通配符,指出需要检索的列提高性能
  3. 通配符用处:检索未知列
  4. 不能部分使用DISTINCT
  5. LIMIT第一个参数为开始位置,第二个为检索的行数,开始位置从0开始

排序检索数据

  • 单列排序: SELECT col FROM table ORDER BY col
  • 多列排序: SELECT col1,col2 FROM table ORDER BY col1,col2
  • 指定排序方向: SELECT col FROM table ORDER BY col DESC
  • 多列指定方向: SELECT col1, col2 FROM table ORDER BY col1 DESC, col2

注意:

  1. 如果不明确排序顺序,不应假定检索出来的数据有顺序
  2. 多列时排序关键字有主次顺序之分
  3. MySQL默认为升序排序
  4. 如果想要对多个列降序排序,需要对每个列指定DESC关键字
  5. 位置: FROM ORDER BY LIMIT

过滤数据

  • 检查单个值: SELECT col FROM table WHERE col = ?
  • 检查不匹配: SELECT col FROM table WHERE col <> ?
  • 检查范围值: SELECT col FROM table WHERE col < ?
  • 空值检查: SELECT col FROM table WHERE col IS NULL
  • 组合WHERE子句:
    1. AND操作符: SELECT col FROM table WHERE col1 = ? AND col2 = ?
    2. OR操作符: SELECT col FROM table WHERE col1 = ? OR col2 = ?
    3. IN操作符: SELECT col FROM table WHERE col IN (val1, val2)
    4. NOT操作符: SELECT col FROM table WHERE col NOT IN (val1, val2)

注意:

  1. 数据检索和过滤应在数据库中完成,以提高应用的性能和减少带宽消耗
  2. AND和OR计算次序:AND会被优先计算,优先级需要使用圆括号来提升
  3. IN的功能和OR相当,但是IN的执行更快且可以包含其他SELECT语句建立子句

用通配符过滤数据

  • 百分号(%)通配符: SELECT col FROM table WHERE col LIKE 'xx%'
  • 下划线(_)通配符: SELECT col FROM table WHERE col LIKE '_ xx'

注意:

  1. 尾空格会干扰通配符匹配
  2. %不能匹配NULL的值
  3. 下划线只匹配单个字符
  4. 不要过度使用通配符

使用正则表达式过滤

  • SELECT col FROM table WHERE col REGEXP 'reg'
  • 匹配字符类
  • 定位符


注意:

  1. MySQL只支持正则的一个很小的子集
  2. 区分大小写需要在REGEXP后加BINARY关键字

计算字段

  • 拼接字段: SELECT Concat(col1, 'str', col2, 'str2') FROM table
  • 使用别名: SELECT col AS name FROM table
  • 执行算术计算: SELECT col*col2 AS name FROM table

注意:

  1. 计算字段用于格式化检索出的数据
  2. LTrim()和RTrim()函数可以去除字段左右两边空格

数据处理函数

常用文本处理函数:


常用日期处理函数:

  • 检索时间: SELECT col FROM table WHERE Date(col2) = 'xxxx-xx-xx'
  • 检索时间段: SELECT col FROM table WHERE Date(col) BETWEEN 'xxxx-xx-xx' AND 'xxxx-xx-xx'

注意:

  1. 总是应该使用4位数的年份

数值处理函数:

汇总数据

聚集函数

注意:

  1. 使用COUNT(*)计数包括NULL值,COUNT(col)则不包含NULL值

分组数据

  • 创建分组: SELECT col FROM table GROUP BY col
  • HAVING过滤分组: SELECT col, COUNT(*) FROM table GROUP BY col HAVING COUNT(*) > 2

注意:

  1. GROUP BY子句可以包含任意数目的列
  2. 建立分组时,指定的所有列都一起计算
  3. GROUP BY子句中列出的每个列都必须是检索列或有效表达式
  4. 除计算语句外, SELECT的每个列都需要在GROUP BY中给出
  5. 如果分组中有NULL值,将NULL作为一个分组返回
  6. GROUP BY子句必须出现在WHERE之后,ORDER BY之前
  7. WHERE在分组之前过滤,HAVING在分组之后过滤
  8. GROUP BY之后应该

使用子查询

  • 利用子查询进行过滤: SELECT col FROM table1 WHERE col IN (SELECT col2 FROM table2 WHERE col2=?)
  • 作为计算字段: SELECT col,(SELECT COUNT(*) FROM table2 WHERE col3=col4) FROM table1

注意:

  1. 列必须匹配,子查询SELECT和WHERE应保持相同的列
  2. 使用子查询时需要考虑效率和性能

联结表

  • 创建联结: SELECT col1, col2 FROM table1, table2 WHERE table1.col=table2.col
  • 内部联结: SELECT col1, col2 FROM table1 INNER JOIN table2 ON table1.col=table2.col
  • 外部联结: SELECT col1, col2 FROM table1 LEFT OUTER JOIN table2 ON table1.col=table2.col

注意:

  1. 在引用的列名可能出现二义性时需要使用完全限定列名
  2. 没有WHERE语句限定的结果将返回笛卡尔积
  3. WHERE为内部联结,是等值联结
  4. 可以使用表别名缩短sql语句和重复使用一张表

组合查询

  • 使用UNION: SELECT col FROM table1 WHERE col2=? UNION SELECT col FROM table1 WHERE col3=?

注意:

  1. UNION必须由两条或以上的SELECT组成
  2. UNION中的每个查询必须包含相同的列,表达式或聚集函数
  3. 列数据必须兼容
  4. UNION默认会去重,取消去重使用UNION ALL查询
  5. UNION只能使用一条排序语句出现在最后一个SELECT之后

全文本搜索

  • SELECT col FROM table WHERE Match(col) Against('text')
  • 对结果排序: SELECT col,Match(col) Against('text') AS rank FROM table

使用视图

  • CREATE VIEW view1 AS SELECT col FROM table

优点

  • 解决了特征稀疏的问题,能在非常非常稀疏数据的情况下进行预估
  • 解决了特征组合的问题
  • FM是一个通用的模型,适用于大部分的场景
  • 训练速度快,线性复杂度

开始

先附上视频的地址:机器学习算法讲堂(3):FM算法 Factorization Machine

我们首先从线性回归解法的角度来看一个特征组合的问题,如果现在数据涉及到训练集中的两个特征,那么LR就要写成:
$f(x)=w_0+\sum_{i=1}^nw_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^nw_{ij}x_ix_j$

可以看到在这个方程中,随着特征选取的数量变多,特征组合之后的复杂度是特征数量个N相乘,这是在训练过程中不能接受的,并且线性回归的方法只能在$w_{ij}x_ix_j$不为0的情况下进行训练,对于稀疏的数据来说训练是不充分的.而FM解决的就是LR复杂度过高和对稀疏数据训练不充分的缺点.

FM的思路就是将W分解为$vv^T$,这也就是FM名字的由来,等于是因式分解W.接下来是FM的推导的过程:
首先引入辅助变量$v_i=(v_{i1},v_{i2},\cdots,v_{ik})^T\in\mathbb{R}^k,i=1,2,3\ldots,n$

已知特征矩阵W为:
$$
\begin{pmatrix} w_{11} & w_{12} & w_{13} & \cdots & w_{1n} \\ w_{21} & w_{22} & w_{23} & \cdots & w_{2n} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ w_{n1} & w_{n2} & w_{n3} & \cdots & w_{nn} \\ \end{pmatrix}
$$
V矩阵为:
$$
\begin{pmatrix} v_{11} & v_{12} & v_{13} & \cdots & v_{1k} \\ v_{21} & v_{22} & v_{23} & \cdots & v_{2k} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ v_{n1} & v_{n2} & v_{n3} & \cdots & v_{nk} \\ \end{pmatrix}
$$
且有结论:当k足够大时,对于任意对称正定实矩阵$w\in\mathbb{R}^{n\times n}$,均存在实矩阵$v\in\mathbb{R}^{n\times k}$使得$w=vv^T$

这样一来,我们只要找到一个$vv^T=w$就可以解决这个问题了,而分解成$vv^T$的优势在于有效的降低了算法的复杂度,下面是对FM算法的求解:

根据上面的分解,可以将FM公式写为:
$$
y=w_0+\sum_{i=1}^nw_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^n<v_i,v_j>x_ix_j, <v_i,v_j>=\sum_{f=1}^kv_{i,f}\cdot v_{j,f}
$$
这里主要需要求解的就是最后一项:
$$
\begin{split} &\sum_{i=1}^{n-1}\sum_{j=i+1}^n<v_i,v_j>x_ix_j \\ =&\frac {1} {2}\sum_{i=1}^n\sum_{j=1}^n<v_i,v_j>-\frac {1} {2}\sum_{i=1}^n<v_i,v_i>x_ix_i \text{,把j化成i,需要减去重复的部分} \\ =&\frac {1} {2} \left( \sum_{i=1}^n\sum_{j=1}^n\sum_{f=1}^kv_{i,f}v_{j,f}x_ix_j-\sum_{i=1}^n\sum_{f=1}^kv_{i,f}v_{i,f}x_ix_i \right) \text{,把上面的等式代入进来} \\ =&\frac {1} {2} \sum_{f=1}^k \left( \left( \sum_{i=1}^nv_{i,f}x_i \right) \left( \sum_{j=1}^nv_{j,f}x_j \right)-\sum_{i=1}^nv_{i,f}^2x_i^2 \right) \text{,提出公共的求和部分} \\ =&\frac {1}{2} \sum_{f=1}^k \left( \left( \sum_{i=1}^nv_{i,f}x_i \right)^2-\sum_{i=1}^nv_{i,f}^2x_i^2 \right) \text{,前面2项除了下标的i,j不同之外是一样的,所以合并} \\ \end{split}
$$
我们可以看到公式化简到最后,复杂度就只和求和符号上的k,n相关了,所以复杂度可以简单的记为O(nk),但是这已经比之前的线性回归的解法复杂度低了很多了,这可以视为是线性的复杂度.

下面看一下FM的梯度的计算,我们对$w_0,w_i,v_i$分别求偏导可以写为:
$$
\frac {dy} {d\theta}= \begin{cases} 1, & \text{if $\theta=w_0$} \\ x_i, & \text{if $\theta=w_i$} \\ x_i\sum_{j=1,j\neq i}^nv_{j,f}x_j & \text{if $\theta=v_{i,f}$} \\ \end{cases}
$$
最后一个公式可以化为:
$$
x_i\sum_{j=1,j\neq i}^nv_{j,f}x_j = x_i\sum_{j=1}^nv_{j,f}x_j-v_{i,f}x_i^2
$$
我们可以看到这个梯度计算的公式中,计算复杂度也只和n相关也就是说:复杂度是O(n)!相比于简单的LR动不动的N方的复杂度来说,FM的复杂度可以说是相当的低了.

拓展

这里就只是先列举一下FM的一些拓展使用:

  • FFM
  • DeepFM
  • DCN
  • xDeepFM

下面就放几个图片吧(这次是自己截图放的图床)
DeepFM:
DeepFM
xDeepFM:
xDeepFM

贴一下论文的地址:
Factorization Machines

xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems

0%