【每月分享】 201912

[TOC]

这里记录过去一个月,我看到、想到值得分享的东西,每周六滚动更新。

0x02. Org-mode Workflow

国外一名 CS 学生的 org mode workflow 教程,包括 GTD 和 Zettelkasten 两个主要的部分,分别对应时间管理和知识管理,是一份很好的参考资料。

0x01. HHKB 更新

HHKB 好久之后终于更新了!不过价格也变得更贵……还是很喜欢自己 18 年买的 HHKB BT 版。不过在使用 Emacs 之后,出现没有方向键的烦恼。之前通过映射 Ctrl + HJKL 替代方向键,然后和 Emacs 的一些快捷键冲突……等买一个新的机械键盘。

0x00. My GTD Workflow (2019 ver.) - Yiming Chen #gtd

很少看到国人用英文写的 GTD 相关文章,年初自己也想按 Workflow 这种形式写一篇,不过一直拖到现在都没有完成。

  • 对任务设置优先级:A B C
  • 如何设置任务优先级,对目标进行分解
    • 每年一月份设定年度目标
    • 每月一号根据年度目标设定月度目标
    • 每周日根据月度目标设定每周目标
    • 每天早上设定当天目标
  • 任务安排优先级和截止日期后,可以使用四象限法则。
  • 回顾技巧
    • 追求 100% 完成,可以接受 70%。
    • 一个任务多次延迟之后,考虑是否还是重要。
    • 如果任务还是重要,对任务进行拆分。

2019 年软硬件指北

呼吸不止,折腾不停。记录在过去的一年,自己选择的软件和硬件。去年写指南并不能指南,所以今年直接写成指北。

硬件更新

iPhone XR 和 Apple Watch Series 4

iPhone XR 刚出来的时候,一直被吐槽是大边框。不过随着在电商网站上不断降价,越来越被当成是无边框手机……在忍受不了使用多年 iPhone 6 的卡顿,以及很难脱离 iOS 生态的现实。终于在苏宁上下单 (Product)read(Product)^{read} 版的 XR。经过半年多的使用,这部手机实用但是不出彩。

Apple Watch 圆环图

购买 Apple Watch Series 4(AW) 理由很简单:在去公司健身房锻炼的时候,希望有一个可以记录运动数据的设备。事实 AW 自带的运动软件很不错,可以满足我的运动记录需求。但是例如 Keep 之类的第三方 App 适配不好。另外,AW 很好扩展 iPhone 和 Mac 的使用,比如可以解锁 Mac、查看 iPhone 上的信息等等。到头来,AW 还只是一块需要一天一充的电子表。

Nintendo Switch

Nintendo Switch(NS) 是任天堂在 2017 年推出的,集掌机和主机于一体的游戏机。买 NS 的理由也很简单,Mac 上游戏太少,我需要一个设备玩游戏。更深层次的来说,感觉自己的反应太慢,想通过玩游戏来锻炼快速决策能力。

简单统计一下,我在 NS 上花费的时间大概有 200 多小时,购入游戏也花费上千元……反应能力不知道有没有上去,但是享受到游戏的快乐。

任天堂就是世界的主宰

Nintendo Switch 目前可以选的有 Nintendo Switch、Nintendo Switch 续航版、Nintendo Switch Lite。

Kindle Oasis 2

大概是 5 月末,通过公司内部闲置群出了使用 5 年多的 Kindle PaperWhite 2 后,从淘宝上购入美版 Kindle Oasis 2 。可惜的是,1 个月不到的时间,亚马逊推出 Kindle Oasis 3 ……

kindle oasis 2 和 kindle 2 对比图

和 KPW2 相比,KO2 主要带来一下几个方面的提升:

  1. 7寸屏幕,更高的分辨率。看的更多,看的更清晰,更加逼近纸书的感觉。
  2. 不对称设计,电池集中在一边,握持比较舒服。
  3. 金属机身。前几代 kindle 都是亚马逊祖传的类肤质塑料机身,很容易沾上油脂,这一代采用金属机身,看起来更加富有科技感。毕竟当年小米用上金属边框的时候,都敢去吹一块钢板的艺术之旅。
  4. 两个翻页实体按键,按起来比较有安全感。

上面说这么多,kindle 主要功能还是看书。这几年,很多 kindle 电子书分享站都由于版权问题陆续关闭,优质的资源比较难下载。不过,去年自己办信用卡时,领了一年的 Kinle U 会员(今年又领到一年的会员),在中亚上借阅很多本小说。从体验上来说,KU 会员不能实现全场自由借,而且大部分书籍都只是滥竽充数。一对比微信读书会员就是十分实惠,多期待微信读书可以出电子书阅读器吧。

软件实践

18 年开始,着手准备构建自己的数字化系统。19 年在前面的基础上,进行了很多迁移。

信息管理

11 月份看到一句话:input 做的越多,知识管理越差。这个很好形容我之前的状态,在印象笔记中囤积待看的剪藏、OF 里面有很多想写的主题、MWeb 遗留大量没有写完的文章。

年中的时候,想把自己写的一些笔记更好的管理起来。最初想到的是搭建 wiki ,实现知识的网状化连接。不过市面上常用的一些个人 wiki 方案都不是很满意。最终选择 hexo 搭配一个 wiki 主题 Wikitten。另外,后来了解到有一种基于纯文本的知识管理方案:zettelkasten。感兴趣的可以去看一下。

写日记是这么多年以来坚持的一件小事情。之前一直是在笔记本上写,后来慢慢的尝试通过印象笔记来写。2月份,订阅 Day One ,开始尝试迁移到它上面去。作为一个专业的软件,体验真的比之前的方式不知道好多少。Day One 上也有很多数据统计,多少可以拿来得瑟用。另外,自己干的一件事情就是把印象笔记中的日记慢慢转移到 Day one 。写在笔记本上的日记,也被我拍成一张又一张的照片,只不过这个迁移起来比较麻烦。

Day one 按年统计图

任务管理

这个问题一直是一个大坑,花费很多时间在多个软件中试来试去。在现在这个时间点,自己开始选择混合使用 OmniFocus 和 Org mode。具体怎么搭配使用,等再坚持几个月再出来分享。不过说回来,任务管理的关键不在于软件,而在于执行。

其他实践

下面这一些今年自己做的选择,都有一个共同的特点:从商业软件到开源项目。很多人选着使用的开源项目的出发点在于害怕商业公司无休止的使用个人隐私数据,而吸引我的主要是自由软件自由开放的精神。

从 MoneyWiz 到 Beancount

MoneyWiz 是在少数派上了解到记账软件,Setapp 中可以免费使用。和国内那些整天搞社区和卖理财的记账软件相比,只是纯粹的一个记账软件。Beancount 是无意中从BYvoid文章中了解的一款纯文本记账软件。最大的优点是扩展性强。在使用过程中,搭配一些简单的脚本,可以实现每月底花一个小时就能把这个月的开销记录明白。

Beancount fava

从 1Password 到 KeePass

之前看过一个结论:密码破解的难度主要在于长度而不是复杂度。所以借助密码软件辅助记忆密码是不二之选。1Password 是在去年感恩节活动中获得的长达一年的免费体验。快要到期前,没有选择转向订阅(今年感恩节活动依然是新用户
长度一年的免费使用),反而是选择开源的 KeePass。KeePass 在不同的平台上有多个客户端可以选择,目前我主要用的是 MacPass 和奇密。KeePass 中所有的密码数据都保存在一个文件中,跨平台使用只需要简单同步这个文件。

从搜狗输入法到鼠须管

网上关于搜狗输入法的声讨一直不绝于耳,我也长时间忍受搜狗动不动给你跳出来的斗图功能提示。在花费一番力气,配置鼠须管后,彻底删除搜狗,详见 「Rime 鼠须管」小鹤双拼配置指南 | 算法花园。另外 Mac 上自带的输入法的体验也没有那么差。

博客上和这个主题相关的文章:


Standford CS231n 2017 课程部分总结

去年学习这门做的部分笔记,现在分享出来。
笔记格式有些问题,持续整理中。

Table of contents

Course Info

01. Introduction to CNN for visual recognition

  • 视觉地出现促进了物种竞争。
  • ImageNet 是由李飞飞维护的一个大型图像数据集。
  • 自从 2012 年 CNN 出现之后,图像分类的错误率大幅度下降。 神经网络的深度也从 7 层增加到 2015 年的 152 层。截止到目前,机器分类准确率已经超过人类,所以 ImageNet 也不再举办相关比赛。
  • CNN 在 1998 年就被提出,但是这几年才流行开来。主要原因有:1) 硬件发展,并行计算速度提到 2)大规模带标签的数据集。
  • Gola: Understand how to write from scratch, debug and train convolutional neural networks.

02. Image classification

  • 图像由一大堆没有规律的数字组成,无法直观的进行分类,所以存在语义鸿沟。分类的挑战有:视角变化、大小变化、形变、遮挡、光照条件、背景干扰、类内差异。
  • Data-Driven Approach
    • Collect a dataset of images and labels
    • Use Machine Learning to train a classifier
    • Evaluate the classifier on new images
  • 图像分类流程:输入、学习、评估
  • 图像分类数据集:CIFAR-10,这个数据集包含了60000张32X32的小图像。每张图像都有10种分类标签中的一种。这60000张图像被分为包含50000张图像的训练集和包含10000张图像的测试集。
  • 一种直观的图像分类算法:K-nearest neighbor(knn)
    • 为每一张需要预测的图片找到距离最近的 k 张训练集中的图片,然后选着在这 k 张图片中出现次数最多的标签做为预测图片的标签(多数表决)。
    • 训练过程:记录所有的数据和标签 O(1){O(1)}
    • 预测过程:预测给定图片的标签 O(n){O(n)}
    • Hyperparameters:k and the distance Metric
    • Distance Metric
      • L1 distance(Manhattan Distance)
      • L2 distance(Euclidean Distance)
    • knn 缺点
      • Very slow at test time
      • Distance metrics on pixels are not informative
    • 反例:下面四张图片的 L2 距离相同
      • -w622
  • Hyperparameters: choices about the algorithm that we set ranther than learn
  • 留一法 Setting Hyperparameters by Cross-validation:
    • 将数据划分为 f 个集合以及一个 test 集合,数据划分中药保证数据集的分布一致。
    • 给定超参数,利用 f-1 个集合对算法进行训练,在剩下的一个集合中测试训练效果,重复这一个过程,直到所有的集合都当过测试集。
    • 选择在训练集中平均表现最好的超参数。
  • Linear classification: Y = wX + b
    • b 为 bias,调节模型对结果的偏好
    • 通过最小化损失函数来,来确定 w 和 b 的值。
  • Linear SVM: classifier is an option for solving the image classification problem, but the curse of dimensions makes it stop improving at some point. @todo
  • Logistics Regression: 无法解决非线性的图像数据

03. Loss function and optimization

  • 通过 Loss function 评估参数质量
    • 比如 $$L=\frac{1}{N}\sum_iL_i\left(f\left(x_i,W\right),y_i\right)$$
  • Multiclass SVM loss 多分类支持向量机损失函数
    • Li=jyjmax(0,sjsyi+1)L_i=\sum_{j \neq y_j}\max\left(0,s_j-s_{y_i}+1\right)

    • 这种损失函数被称为合页损失 Hinge loss
    • SVM 的损失函数要求正确类别的分类分数要比其他类别的高出一个边界值。
    • L2-SVM 中使用平方折叶损失函数$$\max(0,-)^2$$能更强烈地惩罚过界的边界值。但是选择使用哪一个损失函数需要通过实验结果来判断。
    • 举例
      • 根据上面的公式计算:$$L = \max(0,437.9-(-96.8)) + \max(0,61.95-(-96.8))=695.45$$
      • 猫的分类得分在三个类别中不是最高得,所以我们需要继续优化。
  • Suppose that we found a W such that L = 0. Is this W unique?
    • No! 2W is also has L = 0!
  • Regularization: 正则化,向某一些特定的权值 W 添加惩罚,防止权值过大,减轻模型的复杂度,提高泛化能力,也避免在数据集中过拟合现象。
    • L=1NiLi(f(xi,W),yi)+λR(W)L=\frac{1}{N}\sum_iL_i\left(f\left(x_i,W\right),y_i\right) + \lambda R(W)

    • R 正则项 $$\lambda$$ 正则化参数
  • 常用正则化方法
    • L2$$\begin{matrix} R(W)=\sum_{k}\sum_l W^2_{k,l} \end{matrix}$$
    • L1$$\begin{matrix} R(W)=\sum_{k}\sum_l \left\vert W_{k,l} \right\vert \end{matrix}$$
    • Elastic net(L1 + L2): $$\begin{matrix} R(W)=\sum_{k}\sum_l \beta W^2_{k,l} + \left\vert W_{k,l} \right\vert \end{matrix}$$
    • Dropout
    • Batch normalization
    • etc
  • L2 惩罚倾向于更小更分散的权重向量,L1 倾向于稀疏项。
  • Softmax function:
    • fj(z)=esiesjf_j(z)=\frac{e^{s_i}}{\sum e^{s_j}}

    • 该分类器将输出向量 f 中的评分值解释为没有归一化的对数概率,通过归一化之后,所有概率之和为1。
    • Loss 也称交叉熵损失 cross-entropy loss $$L_i = - \log\left(\frac{e^{s_i}}{\sum e^{s_j}}\right)$$
1
2
3
4
5
f = np.array([123, 456, 789]) # 例子中有3个分类,每个评分的数值都很大
p = np.exp(f) / np.sum(np.exp(f)) # 不妙:数值问题,可能导致数值爆炸
# 那么将f中的值平移到最大值为0:
f -= np.max(f) # f becomes [-666, -333, 0]
p = np.exp(f) / np.sum(np.exp(f)) # 现在OK了,将给出正确结果
  • SVM 和 Softmax 比较
    1. 评分,SVM 的损失函数鼓励正确的分类的分值比其他分类的分值高出一个边界值。
    2. 对数概率,Softmax 鼓励正确的分类归一化后的对数概率提高。
    3. Softmax 永远不会满意,SVM 超过边界值就满意了。
  • Optimization:最优化过程
    • Follow the slope
  • 梯度是函数的斜率的一般化表达,它不是一个值,而是一个向量,它是各个维度的斜率组成的向量。
    • Numerical gradient: Approximate, slow, easy to write. (But its useful in debugging.)
    • Analytic gradient: Exact, Fast, Error-prone. (Always used in practice)
    • 实际应用中使用分析梯度法,但可以用数值梯度法去检查分析梯度法的正确性。
  • 利用梯度优化参数的过程:W = W - learning_rate * W_grad
  • learning_rate 被称为是学习率,是一个比较重要的超参数
  • Stochastic Gradient Descent SGD 随机梯度下降法
    • 每次使用一小部分的数据进行梯度计算,这样可以加快计算的速度。
    • 每个批量中只有1个数据样本,则被称为随机梯度下降(在线梯度下降)
  • 图像分类任务中三大关键部分:
    1. 评分函数
    2. 损失函数:量化某个具体参数 W{W} 的质量
    3. 最优化:寻找能使得损失函数值最小化的参数 W{W} 的过程

04. Introduction to Neural network

  • 反向传播:在已知损失函数 L{L} 的基础上,如何计算导数WL{\nabla _WL}
  • 计算图
    • 由于计算神经网络中某些函数的梯度很困难,所以引入计算图的概念简化运算。
    • 在计算图中,对应函数所有的变量转换成为计算图的输入,运算符号变成图中的一个节点(门单元)。
  • 反向传播:从尾部开始,根据链式法则递归地向前计算梯度,一直到网络的输入端。
    • -w1107
    • 绿色是正向传播,红色是反向传播。
  • 对于计算图中的每一个节点,我们需要计算这个节点上的局部梯度,之后根据链式法则反向传递梯度。
  • Sigmoid 函数:f(w,x)=11+e(w0x0+w1x1+w2){f(w,x)=\frac{1}{1+e^{-(w_0x_0+w_1x_1+w_2)}}}
    • 对于门单元 1x{\frac{1}{x}},求导的结果是 1x2{-\frac{1}{x^2}},输入为 1.37,梯度返回值为 1.00,所以这一步中的梯度是 (11.372)1.00=0.53{(\frac{-1}{1.37^2})*1.00=-0.53}
    • 模块化思想:对 σ(x)=11+ex{\sigma(x)=\frac{1}{1+e^{-x}}} 求导的结果是 (1σ(x))σ(x){(1-\sigma(x))\sigma(x)}。如果 sigmoid 表达式输入值为 1.0 时,则前向传播中的结果是 0.73。根据求导结果计算可得局部梯度是 (10.73)0.73=0.2{(1-0.73)*0.73=0.2}
  • Modularized implementation: forward/backwar API
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MultuplyGate(object):
"""
x,y are scalars
"""
def forward(x,y):
z = x*y
self.x = x # Cache
self.y = y # Cache
# We cache x and y because we know that the derivatives contains them.
return z
def backward(dz):
dx = self.y * dz #self.y is dx
dy = self.x * dz
return [dx, dy]
  • 深度学习框架中会实现的门单元:Multiplication、Max、Plus、Minus、Sigmoid、Convolution
  • 常用计算单元
    • **加法门单元:**把输出的梯度相等地分发给它所有的输入,这一行为与输入值在前向传播时的值无关。
    • **取最大值门单元:**将梯度转给前向传播中值最大的那个输入,其余输入的值为0。
    • **乘法门单元:**等值缩放。局部梯度就是输入值,但是需要相互交换,然后根据链式法则乘以输出值得梯度。
  • Neural NetWorks
    • (Before) Linear score function $$f = Wx$$
    • (Now) 2-layer Neural NetWork $$f=W_2\max(0,W_1x)$$
    • ReLU $$\max(0,x)$$ 是激活函数,如果不使用激活函数,神经网络只是线性模型的组合,无法拟合非线性情况。
    • 神经网络是更复杂的模型的基础组件

05. Convolutional neural networks (CNNs)

  • 这一轮浪潮的开端:AlxNet
  • 卷积神经网络
    • Fully Connected Layer 全连接层:这一层中所有的神经元链接在一起。
    • Convolution Layer:
      • 通过参数共享来控制参数的数量。Parameter sharing
      • Sparsity of connections
    • 卷积神经网络能学习到不同层次的输入信息
    • 常见的神经网络结构:INPUT -> [[CONV -> RELU]*N -> POOL?]*M -> [FC -> RELU]*K -> FC
    • 使用小的卷积核大小的优点:多个卷积层与非线性的激活层交替的结构,比单一卷积层的结构更能提取出深层的更好地特征。而且使用的参数也会更少
  • 计算卷积层输出
    • stride 是卷积核在移动时的步长
    • 通用公式 (N-F)/stride + 1
      • stride 1 => (7-3)/1 + 1 = 5
      • stride 2 => (7-3)/2 + 1 = 3
      • stride 3 => (7-3)/3 + 1 = 2.33
    • Zero pad the border: 用零填充所有的边界,保证输入输出图像大小相同,保留图像边缘信息,提高算法性能
      • 步长为 1 时,需要填充的边界计算公式:(F-1)/2
        • F = 3 => zero pad with 1
        • F = 5 => zero pad with 2
        • F = 7 => zero pad with 3
    • 计算例子
      • 输入大小 32*32*3 卷积大小 10 5*5 stride 1 pad 2
      • output 32*32*10
      • 每个 filter 的参数数量:5*5*3+1 =76 bias
      • 全部参数数量 76*10=760
  • 卷积常用超参数设置
    • 卷积使用小尺寸滤波器
    • 卷积核数量 K 一般为 2 的次方倍
    • 卷积核的空间尺寸 F
    • 步长 S
    • 零填充数量 P
  • Pooling layer
    • 降维,减少参数数量。在卷积层中不对数据做降采样
    • 卷积特征往往对应某个局部的特征,通过池化聚合这些局部特征为全局特征
  • Max pooling
    • 2*2 stride 2
    • 避免区域重叠
  • Average pooling

06. Training neural networks I

  • Activation functions 激活函数

    • 不使用激活函数,最后的输出会是输入的线性组合。利用激活函数对数据进行修正。
    • Sigmoid
      • 限制输出在 [0,1]区间内
      • firing rate
      • 二分类输出层激活函数
      • Problem
        • 梯度消失:x很大或者很小时,梯度很小,接近于0(考虑图像中的斜率。无法得到梯度反馈。
        • 输出不是 0 均值的数据,梯度更新效率低
        • exp is a bit compute expensive
    • tanh
      • 输出范围 [-1, 1]
      • 0 均值
      • x 很大时,依然没有梯度
      • f(x)=ezezez+ez{f(x)=\frac{e^{z}-e^{-z}}{e^{z}+e^{-z}}}
      • 1(tanh(x))2{1-(tanh(x))^2}
    • RELU rectified linear unit 线性修正单元
      • 一半空间梯度不会饱和,计算速度快,对结果又有精确的计算
      • 不是 0 均值
    • Leaky RELU
      • leaky_RELU(x) = max(0.01x, x)
      • 梯度不会消失
      • 需要学习参数
    • ELU
      • 比 ReLU 好用
      • 反激活机制
    • Maxout
      • maxout(x) = max(w1.Tx + b1, w2.Tx + b2)
      • 梯度不会消失
      • 增大参数数量
    • 激活函数选取经验
      • 使用 ReLU ,但要仔细选取学习率
      • 尝试使用 Leaky ReLU Maxout ELU
      • 使用 tanh 时,不要抱有太大的期望
      • 不要使用 sigmoid
  • 数据预处理 Data Preprocessing

    • 均值减法:对数据中每个独立特征减去平均值,从几何上来看是将数据云的中心都迁移到原点。
    • 归一化:将数据中的所有维度都归一化,使数值范围近似相等。但是在图像处理中,像素的数值范围几乎一致,所以不需要额外处理。
    1
    2
    X -= np.mean(X, axis = 1)
    X /= np.std(X, axis =1)
    • 图像归一化
      • Subtract the mean image AlexNet
        • mean image 32,32,3
      • Subtract per-channel mean VGGNet
        • mean along each channel = 3 numbers
      • 如果需要进行均值减法时,均值应该是从训练集中的图片平均值,然后训练集、验证集、测试集中的图像再减去这个平均值。
    • Weight Initialization
      • 全零初始化
        • 网络中的每个神经元都计算出相同的输出,然后它们就会在反向传播中计算出相同的梯度。神经元之间会从源头上对称。
      • Small random numbers
        • 初始化权值要非常接近 0 又不能等于 0。将权重初始化为很小的数值,以此来打破对称性
        • randn 函数是基于零均值和标准差的高斯分布的随机函数
        • W = 0.01 * np.random.rand(D,H)
        • 问题:一个神经网络的层中的权重值很小,那么在反向传播的时候就会计算出非常小的梯度。会减小反向传播中的“梯度信号”,在深度网络中就会出现问题。
      • Xavier initialization
        • W = np.random.rand(in, out) / np.sqrt(in)
        • 校准方差,解决输入数据量增长,随机初始化的神经元输出数据的分布中的方差也增大问题。
      • He initialization
        • W = np.random.rand(in, out) / np.sqrt(in/2)
    • Batch normalization
      • 保证输入到神经网络中的数据服从标准的高斯分布
      • 通过批量归一化可以加快训练的速度
      • 步骤
        • 首先计算每个特征的平均值和平方差
        • 通过减去平局值和除以方差对数据进行归一化
        • Result = gamma * normalizedX + beta
          • 对数据进行线性变换,相当于对数据分布进行一次移动,可以恢复数据之前的分布特征
      • BN 的好处
        • 加快训练速度
        • 可以使用更快的而学习率
        • 减少数据对初始化权值的敏感程度
        • 相当于进行一次正则化
      • BN 适用于卷积神经网络和常规的 DNN,在 RNN 和增强学习中表现不是很好
    • Babysitting the Learning Provess
    • Hyperparameter Optimization
      • Cross-validation 策略训练
      • 小范围内随机搜索

07. Training neural networks II

  • Optimization Algorithms:
    • SGD 的问题

      • x += - learning_rate * dx
      • 梯度在某一个方向下降速度快,在其他方向下降缓慢
      • 遇到局部最小值点,鞍点
    • mini-batches GD

      • Shuffling and Partitioning are the two steps required to build mini-batches
      • Powers of two are often chosen to be the mini-batch size, e.g., 16, 32, 64, 128.
    • SGD + Momentun

      • 动量更新:从物理学角度启发最优化问题
      • V[t+1] = rho * v[t] + dx; x[t+1] = x[t] - learningRate * V[t+1]
      • rho 被看做是动量,其物理意义与摩擦系数想类似,常取 0.9 或0.99
      • 和 momentun 项更新方向相同的可以快速更新。
      • 在 dx 中改变梯度方向后, rho 可以减少更新。momentun 能在相关方向加速 SGD,抑制震荡,加快收敛。
    • Nestrov momentum

      • v_prev = v; v = mu * v - learning_rate * dx; x += -mu * v_prev + (1 + mu) * v
    • AdaGrad

      • nt=nt1+gt2n_t=n_{t-1}+g^2_t
      • Δθt=ηnt+ϵ\Delta \theta _t = -\frac{\eta}{\sqrt{n_t+\epsilon}}
      • 下面根号中会递推形成一个约束项。前期这一项比较大,能够放大梯度。后期这一项比较小,能约束梯度。
      • gt 的平方累积会使梯度趋向于 0
    • RMSProp

      • RMS 均方根
      • 自适应学习率方法
      • 求梯度的平方和平均数:cache = decay_rate * cache + (1 - decay_rate) * dx**2
      • x += - learning_rate * dx / (sqrt(cache) + eps)
      • 依赖全局学习率
    • Adam

      • RMSProp + Momentum
      • It calculates an exponentially weighted average of past gradients, and stores it in variables vv (before bias correction) and vcorrectedv^{corrected} (with bias correction).
      • It calculates an exponentially weighted average of the squares of the past gradients, and stores it in variables ss (before bias correction) and scorrecteds^{corrected} (with bias correction).
      • 一阶到导数累积,二阶导数累积
      • It updates parameters in a direction based on combining information from “1” and “2”.
      • The update rule is, for l=1,...,Ll = 1, ..., L:

      {vdW[l]=β1vdW[l]+(1β1)JW[l]vdW[l]corrected=vdW[l]1(β1)tsdW[l]=β2sdW[l]+(1β2)(JW[l])2sdW[l]corrected=sdW[l]1(β1)tW[l]=W[l]αvdW[l]correctedsdW[l]corrected+ε\begin{cases} v_{dW^{[l]}} = \beta_1 v_{dW^{[l]}} + (1 - \beta_1) \frac{\partial \mathcal{J} }{ \partial W^{[l]} } \\ v^{corrected}_{dW^{[l]}} = \frac{v_{dW^{[l]}}}{1 - (\beta_1)^t} \\ s_{dW^{[l]}} = \beta_2 s_{dW^{[l]}} + (1 - \beta_2) (\frac{\partial \mathcal{J} }{\partial W^{[l]} })^2 \\ s^{corrected}_{dW^{[l]}} = \frac{s_{dW^{[l]}}}{1 - (\beta_1)^t} \\ W^{[l]} = W^{[l]} - \alpha \frac{v^{corrected}_{dW^{[l]}}}{\sqrt{s^{corrected}_{dW^{[l]}}} + \varepsilon} \end{cases}

      where:

      • t counts the number of steps taken of Adam
      • L is the number of layers
      • β1\beta_1 and β2\beta_2 are hyperparameters that control the two exponentially weighted averages.
      • α\alpha is the learning rate
      • ε\varepsilon is a very small number to avoid dividing by zero

      特点:

      • 适用于大数据集和高维空间。
      • 对不同的参数计算不同的自适应学习率。
    • Learning decay

      • 学习率随着训练变化,比如每一轮在前一轮的基础上减少一半。
      • 防止学习停止
    • Second order optimization

  • Regularization
    • Dropout
      • 每一轮中随机使部分神经元失活,减少模型对神经元的依赖,增强模型的鲁棒性。
  • Transfer learning
    • CNN 中的人脸识别,可以在大型的模型基础上利用少量的相关图像进行继续训练。

09. CNN architectures

  • 研究模型的方法:搞清楚每一层的输入和输出的大小关系。
  • LeNet - 5 [1998]
    • 60k 参数
    • 深度加深,图片大小减少,通道数量增加
    • ac: Sigmod/tanh
  • AlexNet [2012]
    • (227,227,3) (原文错误)
    • 60M 参数
    • LRN:局部响应归一化,之后很少使用
  • VGG - 16 [2015]
    • 138 M
    • 结构不复杂,相对一致,图像缩小比例和通道增加数量有规律
  • ZFNet [2013]
    • 在 AlexNet 的基础上修改
      • CONV1: change from (11 x 11 stride 4) to (7 x 7 stride 2)
      • CONV3,4,5: instead of 384, 384, 256 filters use 512, 1024, 512
  • VGG [2014]
    • 模型中只使用 3*3 conv:与 77 卷积有相同的感受野,而且可以将网络做得更深。比如每一层可以获取到原始图像的范围:第一层 33,第二层 55,第三层 77。
    • 前面的卷积层参数量很少,模型中大部分参数属于底部的全连接层。

  • GoogLeNet
    • 引入 Inception module
      • design a good local network topology (network within a network) and then stack these modules on top of each other
      • 该模块可以并行计算
      • conv 和 pool 层进行 padding,最后将结果 concat 在一起

Reset

  • ResNet
    • 目标:深层模型表现不应该差于浅层模型,解决随着网络加深,准确率下降的问题。
    • Y = (W2* RELU(W1x+b1) + b2) + X
    • 如果网络已经达到最优,继续加深网络,residual mapping会被设置为 0,一直保存网络最优的情况。

Reference


算法花园写作风格清单

李如一在 写作风格手册 中提到写作风格的作用是 「保持机构和组织内部的文体统一,提高沟通效率。」

本清单会持续更新,如果有相关的建议,可以在留言中告诉我。

算法花园定位为个人博客,也是我和这个世界沟通的窗口。为提高读者阅读体验,参考相关文章后,推出该清单统一网站文章的基础风格。

写作

  1. 减少形容词使用,尽可能删除 「的」和「了」。
  2. 给出引用图片及引文来源。
  3. 文章如果发布后大幅度修改,在末尾给出版本信息。
  4. 写完文章后,整体阅读一遍。

排版

  1. 中文、英文、数字中间加空格,数字与单位之间无需增加空格,全角标点与其他字符之间不加空格。链接前后增加空格用以区分。
  2. 不重复使用标点符号。
  3. 中文使用直角引号 「」以及『 』。
  4. 使用全角中文标点,数字使用半角字符。中文中出现英文部分,仍然使用中文标点。
  5. 遇到完整的英文整句、特殊名词,其內容使用半角标点。
  6. 专有名词使用正确的大小写,使用公认的缩写。
  7. todo 如何处理图片排版和命名。
  8. 使用英文命名文档,使用 - 来连接。为保证搜索引擎效果,尽量不要修改文档名称。
  9. 每篇文章开头添加简单介绍 <!--more-->
  10. 发布后,在网页中确认格式是否符合预期、链接能否点击以及图片能否展示。

ChangeLog

20191103: 第一版

参考


(WDR) Learning to Estimate the Travel Time

严重申明:本篇文章所有信息从论文、网络等公开渠道中获得,不会透露滴滴地图 ETA 任何实现方法。

这篇论文是滴滴时空数据组 2018 年在 KDD 上发表的关于在 ETA 领域应用深度学习的文章,里面提到模型和技巧大家都应该耳熟能详,最大亮点是工业界的创新。

简单介绍一下背景:ETA 是 Estimate Travel Time 的缩写,中文大概能翻译成到达时间估计。这个问题描述是:在某一个时刻,估计从 A 点到 B 点需要的时间。对于滴滴,关注的是司机开车把乘客从起点送到终点需要的时间。抽象出来 ETA 就是一个时间空间信息相关的回归问题。CTR 中常用的方法都可以在这里面尝试。

对于这个问题:文章首先提到一个最通用的方法 Route ETA:即在获得 A 点到 B 点路线的情况下,计算路线中每一段路的行驶时间,并且预估路口的等待时间。最终 ETA 由全部时间相加得到。这种方法实现起来很简单,也能拿到一些收益。但是仔细思考一下,没有考虑未来道路的通行状态变化情况以及路线的拓扑关系。针对这些问题,文章中提到滴滴内部也有利用 GBDT 或 FM 的方法解决 ETA 问题,不过没有仔细写实现的方法,我也不好继续分析下去。

评价指标

对于 ETA 问题来说,工业界和学术界常用的指标是 MAPE(mean absolute percentage error),yi{y_i} 是司机实际从 A 点到 B 点花费的时间,f(xi){f(x_i)} 是 ETA 模型估计出来的时间。得到计算公式如下:

minfi=1Nyif(xi)yi{min_f \sum_{i=1}^{N}\frac{|y_i - f(x_i)|}{y_i}}

多说一句,如果使用 GBDT 模型实现 ETA 时,这个损失函数的推导有点困难,全网也没有看见几个人推导过。

这个公式主要考虑预估时间偏差大小对用户感知体验的影响,目前我们更加关心极端 badcase 对用户的影响。

特征

  • 特征:
    • 空间特征:路线序列、道路等级、POI等
    • 时间特征:月份、星期、时间片等
    • 路况特征:道路的通行速度、拥堵程度
    • 个性化信息:司机特征、乘客特征(有「杀熟」风险)、车辆特征
    • 附近特征:天气、交通管制

模型

模型包含 3 个部分:

  • Wide Learning Models:Wide & Deep 这一部分使用的是 LR + 特征工程,希望模型能记忆一部分的模型。这篇论文中直接利用交叉积学习,减少手动特征工程。
  • Deep Neural Networks:对 sparse feature 做一次 Embedding,使用 3 层 MLP 和 ReLU 的网络。
  • Long-Short Term Memory:前两部分模块没用使用路线序列特征,所以这部分采用 LSTM 抽取路线特征。由于路线序列是不定长的,论文中的模型是使用最后一个隐藏状态,我们也可以把全部的隐藏状态 reduce_sum 输入到最后的模块。
  • Regressor: 将 3 个模型的输出综合起来,作为最后的 ETA 预估。MAPE 作为损失函数,利用 BP 训练模型。

WDR

上面模型中使用的特征分类:

  • Dense feature:行程级别的实数特征,比如起终点球面距离、起终点 GPS 坐标等。
  • Sparse feature:行程级别的离散特征,比如时间片编号、星期几、天气类型等。
  • Sequential feature:link 级别的特征,实数特征直接输入模型,而离散特征先做 embedding 再输入模型。注意,这里不再是每个行程一个特征向量,而是行程中每条 link 都有一个特征向量。比如,link 的长度、车道数、功能等级、实时通行速度等。

评估

包括两部分:离线评估和在线评估。

离线评估中取滴滴 2017 年北京前6个月的订单数据,分成两类 pickup (平台给司机分单后,司机开车去接乘客的过程)和 trip (司机接到乘客并前往目的地的过程)。具体数据集划分如下。

离线效果

离线使用 MAPE 来评价模型。在线评估时,为了更好的与用户体验挂钩,采用多个指标来衡量 ETA 的效果。包括:

  • APE20: absolute percentage error 小于 20% 的订单占比。(越大越好)
  • Badcase率:APE 大于 50% 或者 AE 大于 180s 的订单占比,定义为对用户造成巨大影响的情况。(越小越好)
  • 低估率:低估订单的比例。(越小越好)

离线结果如下图所示,说来汗颜 PTTE 和 TEMP 是什么算法我都不知道…… WD-MLP 指的是将 WDR 中的 R 部分换成 MLP 。最终 WDR 较 route-ETA 有巨大提升,而且 LSTM 引入的序列信息也在 pikcup 上提升了 0.75%。文章的最后还提出来,LSTM 也可以换成是 Attention,这样替换有什么优点和缺点留给大家思考。

pickup 和 trip 效果

在线实验结果如下图所示,滴滴 ETA MAPE 明显小于 com1、com2、com3 ,这三家地图公司具体是哪三家,大家也能猜到吧。

线上实验效果

ETA 服务工程架构

工程架构

从上面的图中可以看出 ETA 服务工程架构主要包括三个部分:

  • Data Aggregation:包括利用 Map Matching 将司机上传到平台的 GPS 对应到滴滴的 Map Info 中得到司机真实行驶过的路线信息,Order Context 指的是订单相关的信息,augmented Data 额外数据比如上文说的交通情况相关信息。
  • Offline Training:利用上一步得到的历史数据训练模型。这里可以值得一提的是,ETA 模型是和时间强相关的(节假日和工作日的数据分布明显不同),所以在文章中作者指出将拿出最新的一部分数据用来 fine-tune 训练出来的 WDR 模型。
  • Online Service:这里需要一个完整的模型服务系统,其他公司也有很多分享,所以原文没有多提。

FMA-ETA: Estimating Travel Time Entirely Based on FFN With Attention

模型架构

  • WDR 模型中 RNN 耗时长,探索基于 Attention 机制的模型
  • 对特征分组(multi-factor)去做 Attention 效果比多头要好
  • 实验结果分析这部分没有看懂

    The deep modules with attention achieve better results than WDR on MAE and RMSE metrics, which means attention mechanism can help to extract features and sole the long-range dependencies in long sequence.

  • 遗憾之处
    • 新模型预测时延减少,但是没有线上实验结果。
    • 暂时没有公开代码和数据集。

总结

从上面简单的介绍来看,ETA 可以使用 CTR 和 NLP 领域的很多技术,大有可为。最后,滴滴 ETA 团队持续招人中(社招、校招、日常实习等),感兴趣者快快和我联系。

说点题外话 你为什么从滴滴出行离职? - 知乎 中提到一点:

8.同年大跃进,在滴滴中高层的眼里,没有BAT。滴滴单量超淘宝指日可待,GAFA才是滴滴要赶超的对象。百度系,LinkedIn系,学院派,uber帮,联想系,MBB就算了,据说连藤校都混成了一个小圈子。。一个项目A team ,B team。一个ETA,投入了多少人力自相残杀?MAPE做到0%又如何?用户体验就爆表了吗?长期留存就高枕无忧了吗?风流总被雨打风吹去,滴滴是二龙山,三虫聚首?是不是正确的事情不知道,反正跟着公司大势所趋,升D10保平安。

参考


(FM) Factorization Machines

Factorization Machines(FM) 由日本 Osaka University 的 Steffen Rendle [1] 在 2010 年提出,是一种常用的因子机模型。

FM

假设现在有一个电影评分的任务,给定如下如所示的特征向量 x(包括用户名、当前在看的电影、已经打分的电影、时间特征、之前看的电影),预测用户对当前观看电影的评分。

电影评分

作者在线性回归模型的基础上,添加交叉项部分,用来自动组合二阶特征。

y^(x):=w0+i=1nwixi+i=1nj=i+1nvi,vjxixj\hat y(x):= w_0 + \sum_{i=1}^{n} w_ix_i + \sum_{i=1}^n \sum_{j=i+1}^n \left \langle v_i,v_j \right \rangle x_ix_j

其中交叉特征的权重由两个向量的点积得到,可以解决没有在模型中出现的特征组合权重问题,以及减少参数数量。

Wi,j=vi,vj=f=1kvi,fvj,fW_{i,j}=\left \langle v_i,v_j \right \rangle = \sum_{f=1}^kv_{i,f} \cdot v_{j,f}

通过下面的方法来化简交叉项权重计算,算法复杂度降到线性。

i=1nj=i+1nvi,vjxiyi=12f=1k((i=1nvi,fxi)2i=1nvi,f2xi2)\sum_{i=1}^n \sum_{j=i+1}^n \left \langle v_i,v_j \right \rangle x_iy_i = \frac{1}{2}\sum^k_{f=1} \left( \left(\sum_{i=1}^nv_{i,f}x_i \right)^2 - \sum^n_{i=1} v^2_{i,f} x_i^2 \right)

对交叉项部分的求导:

θy^(x)={1, if θ is w0xi, if θ is wixij=1nvj,fxjvi,fxi2,if θ is vi,f\frac{\partial}{\partial \theta} \hat y \left( x \right) = \begin{cases} 1, & \text{ if $\theta$ is $w_0$} \\ x_i, & \text{ if $\theta$ is ${w_i}$} \\ x_i\sum^n_{j=1} v_{j,f}x_j - v_{i,f}x_i^2, &\text{if $\theta$ is ${v_{i,f}}$} \end{cases}

其中 j=1nvj,fxj{\sum^n_{j=1} v_{j,f}x_j}xi{x_i} 无关,可以在计算导数前预处理出来。

FM vs SVM

对于经典的特征组合问题,不难想到使用 SVM 求解。Steffen 在论文中也多次将 FM 和 SVM 做对比。

在考虑 SVM 的 Polynomial kernel 为 K(x,z):=(x,z+1)2{K(\mathbf{x}, \mathbf{z}) :=(\langle\mathbf{x}, \mathbf{z}\rangle+ 1)^{2}},映射

ϕ(x):=(1,2x1,,2xn,x12,,xn22x1x2,,2x1xn,2x2x3,,2xn1xn)\begin{array}{l}{\phi(\mathbf{x}) :=\left(1, \sqrt{2} x_{1}, \ldots, \sqrt{2} x_{n}, x_{1}^{2}, \ldots, x_{n}^{2}\right.} {\sqrt{2} x_{1} x_{2}, \ldots, \sqrt{2} x_{1} x_{n}, \sqrt{2} x_{2} x_{3}, \ldots, \sqrt{2} x_{n-1} x_{n} )}\end{array}

SVM 的公式可以转化为:

y^(x)=w0+2i=1nwixi+i=1nwi,i(2)xi2+2i=1nj=i+1nwi,j(2)xixj\begin{aligned} \hat{y}(\mathbf{x})=w_{0}+\sqrt{2} \sum_{i=1}^{n} w_{i} x_{i}+\sum_{i=1}^{n} w_{i, i}^{(2)} x_{i}^{2} &+\sqrt{2} \sum_{i=1}^{n} \sum_{j=i+1}^{n} w_{i, j}^{(2)} x_{i} x_{j} \end{aligned}

论文中提到一句上面的公式中 wi{w_{i}}wi,i{w_{i,i}} 表达能力类似,我猜这也是为什么 FM 中没有自身交叉项的原因吧。

FM 相比于 SVM 有下面三个特点:

  1. SVM 中虽然也有特征交叉项,但是只能在样本中含有相对应的特征交叉数据时才能学习。但是 FM 能在数据稀疏的时候学习到交叉项的参数。
  2. SVM 问题无法直接求解,常用的方法是根据拉格朗日对偶性将原始问题转化为对偶问题。
  3. 在使用模型预测时,SVM 依赖部分训练数据(支持向量),FM 模型则没有这种依赖。

Rank

FM 用来做回归和分类都很好理解,简单写一下如何应用到排序任务中。以 pairwise 为例。假设排序结果有两个文档 xi{x_i}xj{x_j},显然用户点击文档有先后顺序,如果先点击 xi{x_i},记 label yij=1{y_{ij}=1},反之点击 xj{x_j},label yij=0{y_{ij}=0}。模型需要去预测 y^ij=sigmoid(y^iy^j){\hat y_{ij} = sigmoid(\hat y_i - \hat y_j)}

参考逻辑回归,用最大似然对参数进行估计,得到损失函数为 L=log(1+exp((y^(xi)y^(xj)){L=\log(1+\exp(-(\hat y(x_i)-\hat y(x_j))}。优化过程和前面提到类似。

NFM

NFM 和 AFM 两篇论文是同一个作者写的,所以文章的结构很相近。

FM 模型由于复杂度问题,一般只使用特征二阶交叉的形式,缺少对 higher-order 以及 non-liner 特征的交叉能力。NFM 尝试通过引入 NN 来解决这个问题。

NFM 的结构如下:第一项和第二项是线性回归,第三项是神经网络。神经网络中利用 FM 模型的二阶特征交叉结果做为输入,学习数据之间的高阶特征。与直接使用高阶 FM 模型相比,可以降低模型的训练复杂度,加快训练速度。

y^NFM(x)=w0+i=1nwixi+f(x)\hat{y}_{N F M}(\mathbf{x})=w_{0}+\sum_{i=1}^{n} w_{i} x_{i}+f(\mathbf{x})

NFM 的神经网络部分包含 4 层,分别是 Embedding Layer、Bi-Interaction Layer、Hidden Layers、Prediction Score。

NFM

  • Embedding Layer 层对输入的稀疏数据进行 Embedding 操作。最常见的 Embedding 操作是在一张权值表中进行 lookup ,论文中作者强调他们这一步会将 Input Feture Vector 中的值与 Embedding 向量相乘。
  • Bi-Interaction Layer 层是这篇论文的创新,对 embedding 之后的特征两两之间做 element-wise product,并将结果相加得到一个 k 维(Embeding 大小)向量。这一步相当于对特征的二阶交叉,与 FM 类似,这个公式也能进行化简:

fBI(Vx)=i=1nj=i+1nxivixjvj=12[(i=1nxivi)2i=1n(xivi)2]f_{B I}\left(\mathcal{V}_{x}\right)=\sum_{i=1}^{n} \sum_{j=i+1}^{n} x_{i} \mathbf{v}_{i} \odot x_{j} \mathbf{v}_{j} =\frac{1}{2}\left[\left(\sum_{i=1}^{n} x_{i} \mathbf{v}_{i}\right)^{2}-\sum_{i=1}^{n}\left(x_{i} \mathbf{v}_{i}\right)^{2}\right]

  • Hidden Layers 层利用常规的 DNN 学习高阶特征交叉
  • Prdiction Layer 层输出最终的结果:

y^NFM(x)=w0+i=1nwixi+hTσL(WL(σ1(W1fBI(Vx)+b1))+bL)\begin{aligned} \hat{y}_{N F M}(\mathbf{x}) &=w_{0}+\sum_{i=1}^{n} w_{i} x_{i} +\mathbf{h}^{T} \sigma_{L}\left(\mathbf{W}_{L}\left(\ldots \sigma_{1}\left(\mathbf{W}_{1} f_{B I}\left(\mathcal{V}_{x}\right)+\mathbf{b}_{1}\right) \ldots\right)+\mathbf{b}_{L}\right) \end{aligned}

实验结果:

AFM

AFM(Attentional Factorization Machine), 在 FM 的基础上将 Attention 机制引入到交叉项部分,用来区分不同特征组合的权重。

y^AFM(x)=w0+i=1nwixi+pTi=1nj=i+1naij(vivj)xixj\hat{y}_{A F M}(\mathbf{x})=w_{0}+\sum_{i=1}^{n} w_{i} x_{i}+\mathbf{p}^{T} \sum_{i=1}^{n} \sum_{j=i+1}^{n} a_{i j}\left(\mathbf{v}_{i} \odot \mathbf{v}_{j}\right) x_{i} x_{j}

单独看上面公式中的第三项结构:

  • Embedding Layer 与 NFM 里面的作用一样,转化特征。
  • Pair-wise Interaction Layer 是将特征两两交叉,如果对这一步的结果求和就是 FM 中的交叉项。
  • Attention 机制在 Attention-based Pooling 层引入。将 Pair-wise Interaction Layer 中的结果输入到 Attention Net 中,得到特征组合的 score aij{a_{i j}^{\prime} },然后利用 softmax 得到权重矩阵 aij{a_{ij}}

aij=hTReLU(W(vivj)xixj+b)aij=exp(aij)(i,j)Rxexp(aij)\begin{aligned} a_{i j}^{\prime} &=\mathbf{h}^{T} \operatorname{Re} L U\left(\mathbf{W}\left(\mathbf{v}_{i} \odot \mathbf{v}_{j}\right) x_{i} x_{j}+\mathbf{b}\right) \\ a_{i j} &=\frac{\exp \left(a_{i j}^{\prime}\right)}{\sum_{(i, j) \in \mathcal{R}_{x}} \exp \left(a_{i j}^{\prime}\right)} \end{aligned}

  • 最后将 Pair-wise Interaction Layer 中的二阶交叉结果和权重矩阵对应相乘求和得到 AFM 的交叉项。

和前一节的实验结果对比,AFM 效果比 NFM 要差一些。这大概就能说明为什么论文中提到 NFM,但是最后没有把 NFM 的结果贴出来,实在是机智。又回到,发论文是需要方法有创新,还是一味追求 state-of-the-art。

参考资料


(Wide&Deep) Wide & Deep Learning for Recommender Systems

背景

这是一篇推荐系统相关的论文,场景是谷歌 Play Store 的 App 推荐。文章开头,作者点明推荐系统需要解决的两个能力: memorization 和 generalization。

memorization 指的是学习数据中出现过的组合特征能力。最常使用的算法是 Logistic Regression,简单、粗暴、可解释性强,而且会人工对特征进行交叉,从而提升效果。但是,对于在训练数据中没有出现过的特征就无能为力。

generalization 指的是通过泛化出现过特征从解释新出现特征的能力。常用的是将高维稀疏的特征转换为低维稠密 embedding 向量,然后使用 fm 或 dnn 等算法。与 LR 相比,减少特征工程的投入,而且对没有出现过的组合有较强的解释能力。但是当遇到的用户有非常小众独特的爱好时(对应输入的数据非常稀疏和高秩),模型会过度推荐。

综合前文 ,作者提出一种新的模型 Wide & Deep。

模型

从文章题目中顾名思义,Wide & Deep 是融合 Wide Models 和 Deep Models 得到,下图形象地展示出来。

Wide & Deep Models

Wide Component 是由一个常见的广义线性模型:y=wTx+b{y=w^Tx+b}。其中输入的特征向量 x{x} 包括两种类型:原始输入特征(raw input features)和组合特征(transformed features)。

常用的组合特征公式如下:

ϕk(x)=i=1dxicki,cki{0,1}{\phi_k(x)=\prod_{i=1}^dx_i^{c_{ki}},c_{ki}\in\{0,1\}}

cki{c_{ki}} 代表对于第k个组合特征是否包含第i个特征。xi{x_i}是布尔变量,代表第i个特征是否出现。例如对于组合特征 AND(gender=female, language=en) 当且仅当 x 满足(“gender=female” and “language=en”)时,ϕk(x)=1{\phi_k(x)=1}

Deep Component 是一个标准的前馈神经网络,每一个层的形式诸如:a(l+1)=f(W(l)a(l)+b(l)){a^{(l+1)}=f(W^{(l)}a^{(l)} + b^{(l)})}。对于输入中的 categorical feature 需要先转化成低维稠密的 embedding 向量,再和其他特征一起喂到神经网络中。

对于这种由基础模型组合得到的新模型,常用的训练形式有两种:joint training 和 ensemble。ensemble 指的是,不同的模型单独训练,且不共享信息(比如梯度)。只有在预测时根据不同模型的结果,得到最终的结果。相反,joint training 将不同的模型结果放在同一个损失函数中进行优化。因此,ensmble 要且模型独立预测时就有有些的表现,一般而言模型会比较大。由于 joint training 训练方式的限制,每个模型需要由不同的侧重。对于 Wide&Deep 模型来说,wide 部分只需要处理 Deep 在低阶组合特征学习的不足,所以可以使用简单的结果,最终完美使用 joint traing。

预测时,会将 Wide 和 Deep 的输出加权得到结果。在训练时,使用 logistic loss function 做为损失函数。模型优化时,利用 mini-batch stochastic optimization 将梯度信息传到 Wide 和 Deep 部分。然后,Wide 部分通过 FTRL + L1 优化,Deep 部分通过 AdaGrad 优化。

实验

本篇论文选择的实验场景是谷歌 app 商店的应用推荐,根据用户相关的历史信息,推荐最有可能会下载的 App。

使用的模型如下:
Wide & Deep model structure for apps recommendation.

一些细节:

  • 对于出现超过一定次数的 categorical feature,ID 化后放入到模型中。
  • Continuous real-valued features 通过 cumulative distribution function 归一化到 [0, 1] 区间。
  • categorical feature 由 32 维 embedding 向量组成,最终的输入到 Deep 部分的向量大概在 1200 维。
  • 每天在前一天 embedding 和模型的基础上进行增量更新。

实验结果:

实验结果

Wide & Deep 模型相对于其他两个模型毫无疑问有提升。但结果中也一个反常的现象:单独使用 Deep 模型离线 AUC 指标比单独使用 Wide 模型差,但是线上对比实验时却有较大的提升。论文中作者用了一句:线下实验中的特征是固定的,线上实验会遇到很多没有出现过的特征组合,Deep 相对于 Wide 有更好的模型泛化能力,所以会有反常现象。由于笔者工作中不关注 AUC,也没有办法继续分析。

总结

作者从推荐系统的的 memorization 和 generalization 入手,设计出新的算法框架。通过线上和线下实验实验,证明 Deep 和 Wide 联合是必须的且有效的。最终也在自己的业务场景带来提升。

Reference


Best of iPhone 2019 软件清单

一直想做一个推荐软件的系列文章,不过完成 2017 年的iPhone软件清单 后就没有动力……很多时候在思考,自己为什么一定要使用 iPhone?iOS 中的软件正是最好的答案,让每一个人享受科技带来的快乐。

这一篇文章和 2017 年的形式一样,删除常用的软件,推荐一些我认为有趣的软件。自从 iOS 12 中引入屏幕时间,今年的推荐顺序就按照屏幕时间中的排序。

目前使用设备:iPhone XR & Apple Watch Series 4

  • Inoreader:RSS 订阅服务商以及 RSS 阅读器。随着这两年对推荐算法相关的新闻阅读器批判,RSS 大有一股复新之势。可惜的是在去年年底左右,inoreader 中每个免费帐号只能关注 150 个订阅源。目前,我通过使用多个帐号来临时解决。
  • 脉脉:职场社交软件。有匿名区以及公司圈,定期查看公司内部的吐槽和爆料。用 Leader 的话来说,整天操 M6 的心。
  • Keep/Nike Running/健身记录:锻炼相关 App 集合。Keep 和 Apple Watch 的配合太差,很多数据不能同步。自带的健身记录展示数据不够详细。Nike Running 不太用。
  • Overcast:免费且功能强大的播客软件,良心到只有少量的播客相关广告。喜欢智能播放和人声增强功能。最近,也支持章节播放功能。
  • AutoSleep:配合 Apple Watch 实现睡眠监控功能。原来大致为读取 Apple Watch 写入健康中的数据判断是否入睡以及睡眠质量。由于硬件的限制,很多时候还是不太准确。不过我也和网上说的一样,每天起来需要看这软件中的评分,判断昨天睡的如何……
  • Kindle:管它微信阅读如何火爆,不忘初心依旧选择 Kindle。配合 Kindle Unlimted,可以阅读很多好书。
  • Google 相册:免费强大的照片管理软件,允许谷歌优化照片质量的后,可以无限存储。
  • Quantumult:梯子软件,美区下载,比小火箭好用。
  • OmniFoucs:GTD 软件,居然使用的正版……
  • Anki:多年之后,我的摘抄终于有了存放之地。
  • Day One:日记软件,喜欢里面的去年今天功能,陆续把日记导入中。
  • Pocket:稍后读。
  • 1 Password:密码软件,全平台通用。
  • 熊猫吃短信:垃圾短信过滤,可以自己配置规则把公司的报警短信给屏蔽了。
  • Ulysses:Markdown 软件,iOS 版也包含在 SetApp 订阅。

在与 2017 版相比,有两个趋势:1. 减少很多与学习强相关的软件。 2. 越来越多的付费或者订阅制软件。期待明年。


Practical Lessons from Predicting Clicks on Ads at Facebook(gbdt + lr)

**主题:**Facebook 2014 年发表的广告点击预测文章。最主要是提出经典 GBDT+LR 模型,可以自动实现特征工程,效果好比于人肉搜索。另外,文章中还给出一个 online learning 的工程框架。

问题:

  • GBDT 如何处理大量 id 类特征
    • 广告类对于 user id 的处理:利用出现的频率以及转化率来代替
    • id 特征放在 lr 中处理。
  • GBDT+LR 和 RF+LR 的区别
    • 选出能明显区分正负样本的特征的变换方式,转换成 one hot 有意义
    • RF + LR 可以并行训练,但是 RF 中得到的区分度不高

收获:

  • 数据支撑去做决策,收获和实验数量成正比。
  • CTR click through rate,点击率
  • 评价指标:
    • Normalized Entropy:越小模型越好
    • Calibration:预测点击数除以真实点击数
    • AUC 正样本出现在负样本前面的概率。
  • 数据新鲜度:模型天级训练比周级训练在 NE 下降 1%。
  • GBDT 和 LR 模型采用不同的更新频率,解决训练耗时不同。但是 GBDT 重新训练之后,LR 必须要重新训练。

网络:

GBDT + LR

利用 GBDT 模型进行自动特征组合和筛选,然后根据样本落在哪棵树哪个叶子生成一个 feature vector 输入到 LR 模型中。这种方法的有点在于两个模型在训练过程从是独立,不需要进行联合训练。

GBDT 由多棵 CART 树组成,每一个节点按贪心分裂。最终生成的树包含多层,相当于一个特征组合的过程。根据规则,样本一定会落在一个叶子节点上,将这个叶子节点记为1,其他节点设为0,得到一个向量。比如下图中有两棵树,第一棵树有三个叶子节点,第二棵树有两个叶子节点。如果一个样本落在第一棵树的第二个叶子,将它编码成 [0, 1, 0]。在第二棵树落到第一个叶子,编码成 [1, 0]。所以,输入到 LR 模型中的向量就是 [0, 1, 0, 1, 0]

Online Learning

文章中提到的 Online Learning 包括三个部分:

  • Joiner 将每次广告展示结果(特征)是否用户点击(标签) join 在一起形成一个完成的训练数据;
  • Trainer 定时根据一个 small batch 的数据训练一个模型;
  • Ranker 利用上一个模块得到模型预测用户点击。

注意的点:

  • waiting window time:给用户展示广告之后,我们只能知道用户点击的广告,也就是模型中的正样本。负样本需要设置一个等待时间来判断,即超过某一个时间没有观测到用户点击某一个广告,就认为这是一个负样本。另外设置这个时间也是一个技术活,时间过短导致click没有及时join到样本上,时间太长数据实时性差以及有存储的压力。最后,无论如何都会有一些数据缺失,为了避免累积误差,需要定期重新训练整个模型。
  • request ID:人家的模型是分布式架构的,需要使用 request ID 来匹配每次展示给用户的结果以及click。为了实现快速匹配,使用 HashQueue 来保存结果。
  • 监控:避免发生意向不到的结果,导致业务损失。我们的实时模型也在上线前空跑了好久。

实验:

有无 GBDT 特征对比

训练两个 LR 模型,一个模型输入样本经过 GBDT 得到的特征,另外一个不输入。混合模型比单独 LR 或 Tree

学习率选择

5 种学习率,前三个每一个特征设置一个学习率,最后两种全局学习率。

结果:应该给每一个特征设置一个不同的学习率,而且学习率应该随着轮次缓慢衰减。

GBDT 参数相关实验

  • 前面的树会带来大量的收益,但是树越多训练越慢。
  • 特征重要程度,累加不同树上某个特征的得分减少贡献。
  • 两种特征:
    • 上下文,冷启动的时候比较重要,与数据新鲜度有关。
    • 历史史特征,权重比较大,关键在于长时间积累。

采样

训练数据大多,需要进行采样。

  • uniform subsampling :无差别采样。使用 10 % 的样本,NE 减少 1 %

  • negative down subsampling :对负样本进行下采样。但不是负采样率越低越好,比如下面的图中0.0250就可能是解决了正负样本不平衡问题。最后的CTR指标结果需要重新进行一次映射。

Reference


「Rime 鼠须管」小鹤双拼配置指南

引言

如何将汉字输入到计算机中是一个编码有关的问题,目前市面上主流的方案包括音码、形码、音形码。和大多数人一样,之前我一直使用全拼,而且得益于 NLP 技术发展,使用搜狗输入法搭配云词库,输入效率可以媲美五笔输入法。

但是今天要和大家分享,是从年初开始使用的全新音码输入方案——小鹤双拼。最初关于双拼的概念来自李笑来《把时间当作朋友》:

在很长的一段时间里,我常言之凿凿地对同学们说“练习打字完全是浪费时间。”我当时的逻辑是这样的。首先,我认为王码五笔字型输入法是给打字员用的。为什么要学它?难道你将来想要当个打字员?我总觉得五笔字型知识一种抄写输入法,因为用他输入时只能边看边打。而对真正创造内容的人来说,先用纸和笔写出来再录入电脑,还有比这更荒诞的事情吗?学习拆字学法已经很累人了,还要练什么指法,见鬼。更不用说这种所谓的输入法对思考的干扰——不仅要把字拆开再输入,还要按照莫名其妙的方法拆字。其次,盲打。我现在不是盲打,只是两根手指输入速度就已经很快了(至少比手写快)。

这样看来,我还有必要学习什么五笔字型和盲打吗?

在我有了这些定见很久之后,发生了一件事情。

那是在1997年,我25岁。当时互联网除了聊天室和论坛,几乎没有什么实际的应用。适逢windows捆绑了哈尔滨工业大学开发的“微软拼音输入法1.0”,某天下午,当我在网上和一位永远都不会知道是谁的女生放肆地聊了两个小时之后,突然发现自己竟已无师自通地学会了所谓的“盲打”了!在这之后的一段时间里,我身边甚至很多人羡慕我打字的速度。为了让自己的打字速度再快一点,我索性花了差不多20分钟,把原本默认的“全拼输入”改成了“”双拼输入“。而这还远远不够。后来,我增设了”慢放模糊音“(不区分z/zh、c/ch、s⇧),又把打字速度提高了一些。这时我第一次意识到‘有些认识,哪怕是简单的常识,也需要亲身经历后才能真正体会”。只有拥有无与伦比的打字速度,才会体会打字速度快的好处。

打字速度提升后,我发现自己不再讨厌在读书的时候做笔记了,因为在键盘上敲字相对于笔写字来说轻松太多。我开始大段地纪录感悟,有时甚至干脆整篇摘抄原文!

李笑来思考的问题正是如何利用最小的代价快速的输入文字。这里代价包括两个方面,输入方案的学习成本以及输入文字的速度。简单分析一下,主流输入法需要用户在键盘上敲击一些字符,然后映射到汉字。就是说输入文字的速度和两个因数有关:敲击键盘的次数(对应计算机中的码长)以及重码的字符数量(对于拼音输入法来说,这里指的是每个拼音对应多少的字)。

从这两个指标来说,五笔应该是接近输入法的极限,五次敲击键盘肯定能选定你需要的汉字。但是学习五笔需要记忆大量的说是有序其实没有太多规律的字根,学习曲线不是一般的陡峭。几年前,自己尝试跟着网上的视频教程学习,最后还是太复杂而放弃。

这时候要介绍一下双拼输入法,它是一种基于全拼的激进改良版拼音输入法。简单来说,它将一个汉字的拼音分成声母和韵母两个部分,输入一个拼音只需要按两个键(减少键盘的敲击次数但是没有减少重码率)。比如对于“拼音”两个字来说,全拼需要输入 pinyin 六个字符,换成是双拼,只需要输入 4 个字符 pbyb。具体介绍可以看 做少数派中的少数派:双拼输入快速入门

由于双拼有声母和韵母,需要先从键盘上进行一次映射,所以有很多的输入方案。我自己入门使用的是小鹤双拼,也推荐大家使用这个方案。主要原因有两点:声母和韵母全部放在字母键上(微软双拼中要用到 ; 键,以及 iOS12 和 MacOS 中自带的输入法都支持这个方案(国内那几个流氓输入法更不用提)。

下图是一张小鹤双拼的输入法键位图,其中黑色的字符代表是键盘上的什么键,棕色的代表这个键表示声母时是什么,蓝色的代表这个键表示韵母时是什么。学习双拼的过程也很简单,拼音本身就会,无非是熟悉键位。从我自己的角度来说,每天抽出几分看一下键位图,再在 双拼练习 @ BlueSky (相关的介绍可以看:快速上手双拼,可以尝试这个练习平台 - 少数派)网站上练习 5 分钟,一周后可以完全脱离键位图来打字,之后就是孰能生巧的过程。

说回来当你学习了这么强大的内功之后,自然需要神兵来辅助你输入。在饱受国内的流氓输入法侵害之后(比如输入到一半给你跳一个什么斗图功能提示),我遇到今天的主角 RIME | 中州韻輸入法引擎,它是由佛振开发的一种开源输入框架,业内人士称之为「神级输入法」。上一个有类似拉风的称号的软件还是「神级编辑器」—— Vim。Rime 有趣的一点是在不同的平台上有不同的名字,包括 Linux 上的「中州韵」,Win 上的「小狼毫」以及 Mac 上的「鼠须管」。稍微有文化的人可以反应过来,后面两个正是两种不同的毛笔名字。

简单总结一下为什么要使用鼠须管:一是安全,不会出现什么输入法读取你个人信息更甚者是密码发送到服务器,也不知道他们用来机器学习什么;二是配置全平台同步,解决多台设备的输入法配置问题;三是快,不会出现输入法跟不上我的打字速度而导致思路中断的情况。

安装

下載及安裝 | RIME | 中州韻輸入法引擎 主页,你可以找到对应不同平台的安装方法。

对于 MacOS 来说,最简单的方式莫过于在终端中输入 brew cask install squirrel 安装软件本体。如何你想使用 rime/plum: 東風破可以在终端中输入 curl -fsSL https://git.io/rime-install | bash -s -- :preset double-pinyin 。其中 preset double-pinyin 指定下载时默认包括小鹤双拼输入方案。

配置

防止配置时候出现各种意想不到的情况,首先推荐阅读官方文档 CustomizationGuide · rime/home Wiki

Rime 的配置文件默认放在 ~/Library/Rime,而且是一种扩展 yaml 文件。默认的文件名为 .schema.yaml,比如小鹤双拼相关的默认配置在 double_pinyin_flpy.schema.yaml 中。如果我们自己想添加一些设置,推荐写在以.custom.yaml 结尾的新文件中,比如 double_pinyin_flypy.custom.yaml

default.custom.yaml

这个文件写一些全局的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
patch:
switcher:
caption: 〔方案选单〕
hotkeys: Control+grave
# 候选词数量
menu:
page_size: 9
# 使用的输入方案
schema_list:
- schema: luna_pinyin_simp
- schema: luna_pinyin
- schema: double_pinyin_flypy
# 输入法中英文状态快捷键
ascii_composer/switch_key:
Caps_Lock: commit_code
Control_L: noop
Control_R: noop
# 按下左 shift 英文字符直接上屏,不需要再次回车,输入法保持英文状态
Shift_L: noop
Shift_R: noop
# 在一些软件中默认使用英文输入状态
app_options:
com.apple.finder: &a
ascii_mode: true
no_inline: true
com.googlecode.iterm2: *a
com.alfredapp.Alfred: *a
com.runningwithcrayons.Alfred-2: *a
org.vim.MacVim: *a
com.apple.Terminal: *a

最后在修改配置时,可以查阅 Rime_collections/Rime_description.md at master · LEOYoon-Tsaw/Rime_collections 寻找相关信息。

installation.yaml

配置文件多平台同步相关文件,sync_dir 指定同步文件夹的位置,配合例如坚果云之类的软件实现备份同步。

1
2
3
4
5
6
7
8
distribution_code_name: Squirrel
distribution_name: "鼠鬚管"
distribution_version: 0.11.0
install_time: "Sun Dec 23 23:42:01 2018"
installation_id: "mac_didi"
sync_dir: "/Users/didi/Documents/rime_sync"
rime_version: 1.4.0
update_time: "Mon Jun 3 07:18:30 2019"

自由输入法RIME简明配置指南 - 少数派 中了解到,rime 个人词典双向同步,用户配置单向同步。另外需要把配置文件上传到 git 中,方便不同设备使用。

double_pinyin_flypy.custom.yaml

小鹤双拼相关的自用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
patch:
# 引用 `symbols.custom` 文件里面的符号
# 'punctuator/import_preset': symbols.custom
'recognizer/patterns/punct': "^/([a-z]+|[0-9])$"
# 載入朙月拼音擴充詞庫
"translator/dictionary": ryen
# 更改‘西文’为‘英文’,‘增广’为‘扩展集’
punctuator:
import_preset: symbols.custom
half_shape:
"#": "#"
"`": "`"
"~": "~"
"@": "@"
"=": "="
"/": ["/", "÷"]
'\': ["、", '\']
"'": {pair: ["「", "」"]}
"[": ["【", "["]
"]": ["】", "]"]
"$": ["¥", "$", "€", "£", "¢", "¤"]
"<": ["《", "〈", "«", "<"]
">": ["》", "〉", "»", ">"]
switches:
- name: ascii_mode
reset: 0
states: ["中文", "英文"]
- name: full_shape
states: ["半角", "全角"]
- name: zh_simp
reset: 1
states: ["漢字","汉字"]
- name: ascii_punct
states: [",。", ",."]
- name: extended_charset #生僻字开关
states: ["通用", "扩展集"]
- name: show_emoji # 该项为表情输入,具体内容可见下文中 [关于表情输入] 部分
reset: 1
states: [ "🈚️️\uFE0E", "🈶️️\uFE0F" ]
# 输入双拼码的时候不转化为全拼码
translator/preedit_format: {}
simplifier:
option_name: zh_simp
# 分号上屏二候选词;引号上屏三候选词
"key_binder/bindings":
- { when: has_menu, accept: semicolon, send: 2 }
- { when: has_menu, accept: apostrophe, send: 3 }
- { when: paging, accept: bracketleft, send: Page_Up }
- { when: has_menu, accept: bracketright, send: Page_Down }

squirrel.custom.yaml

自定义皮肤相关文件

1
2
3
4
5
6
7
8
9
10
11
patch:
style:
color_scheme: psionics
horizontal: true
inline_preedit: true
candidate_format: "%c\u2005%@ \u2005" # 用 1/6 em 空格 U+2005 来控制编号 %c 和候选词 %@ 前后的空间。
font_point: 16 # 候选文字大小
label_font_point: 14 # 候选编号大小
corner_radius: 5 # 候选条圆角
border_height: 0 # 窗口边界高度,大于圆角半径才生效
border_width: 0 # 窗口边界宽度,大于圆角半径才生效

最后,我的配置在 xiang578/rime 同步。

调试

Debug 是折腾 rime 不得不面对的一步,主要方式是查看 rime 部署的时产生的 log 文件。对于鼠鬚管而言, log 文件保存在 $TMPDIR/rime.squirrel.* 中。

  • 在命令行中输入 echo $TMPDIR 获得路径
  • 将地址输入到 「访达-前往-前往文件夹…」中跳转
  • 右键点击 rime.squirrel.WARNING 文件,选择显示原身,并使用文本编辑器打开

文件格式类似于下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Log file created at: 2019/06/07 23:31:54
Running on machine: didideMiBook-Pro.local
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
W0607 23:31:54.549346 162086912 config_compiler.cc:391] inaccessible node: punctuation.custom:/patch
W0607 23:31:54.554167 162086912 config_compiler.cc:391] inaccessible node: key_bindings.custom:/patch
W0607 23:31:54.560144 162086912 deployment_tasks.cc:179] schema list not defined.
W0607 23:36:13.133517 161013760 config_compiler.cc:391] inaccessible node: punctuation.custom:/patch
W0607 23:36:13.135843 161013760 config_compiler.cc:391] inaccessible node: key_bindings.custom:/patch
W0607 23:36:13.149406 161013760 config_data.cc:62] nonexistent config file '/Users/didi/Library/Rime/luna_pinyin_simp.custom.yaml'.
W0607 23:36:13.154920 161013760 config_compiler.cc:391] inaccessible node: punctuation.custom:/patch
W0607 23:36:13.156643 161013760 config_compiler.cc:391] inaccessible node: key_bindings.custom:/patch
W0607 23:36:13.331344 161013760 config_compiler.cc:391] inaccessible node: key_bindings.custom:/patch
W0607 23:36:13.333066 161013760 config_compiler.cc:391] inaccessible node: punctuation.custom:/patch
W0607 23:36:13.668072 161013760 config_data.cc:62] nonexistent config file '/Users/didi/Library/Rime/stroke.custom.yaml'.
W0607 23:36:13.670761 161013760 config_compiler.cc:391] inaccessible node: key_bindings.custom:/patch
W0607 23:36:13.672724 161013760 config_compiler.cc:391] inaccessible node: punctuation.custom:/patch
W0607 23:36:14.281919 161013760 config_compiler.cc:391] inaccessible node: punctuation.custom:/patch
W0607 23:36:14.283246 161013760 config_compiler.cc:391] inaccessible node: key_bindings.custom:/patch

按右 shift 切换输入法

之前使用搜狗输入法时,特别喜欢的一个功能:按右 shift 切换输入法的输入状态,实现暂时切换到英文状态。Rime 作者在 使用 Control 鍵切換中西文,上屏已輸入的編碼;令 Caps Lock 改變字母的大小寫 中提到一种方案。例如下面:

1
2
3
4
5
6
7
8
patch:
ascii_composer/good_old_caps_lock: true
ascii_composer/switch_key:
Caps_Lock: commit_code
Shift_L: noop
Shift_R: commit_code
Control_L: commit_code
Control_R: commit_code

然而这样修改完成之后,不论按哪个 Shift 键,都会切换到英文输入状态。看前面那个网页下面作者与其他人的讨论中发现,鼠须管无法区分 Shift 键。

网上查了一下,简单实现的方法是通过 karabiner 软件来改键。详细步骤可以参考 禁用 Squirrel 英文模式,使用左侧 Shift 切换中英 · rime/squirrel Wiki

Reference