Alex' Blog

记录生活和工作

0%

基本概念

强化学习任务一般通过马尔可夫决策过程(Markov Decision Process, MDP)来描述。

A Markov decision process is a 4-tuple$(S,A,P_a,R_a)$, where

  • $S$ is a finite set of states,
  • $A$ is the finite set of actions available from state $S$,
  • $P_a(s,s^{’})$ is the transfer probability.
  • $R_a(s, s^{‘})$ is the immediate reward from state to state s’.

而机器学习的任务便是不断尝试,学习得到一个新的策略(policy)$\pi$。基于这个策略,在不同的状态下选择不同的动作action。有两种表示方法,一种作为函数,一种作为概率表示,表示选择动作的概率。

目标便是长期奖励最大化的策略。常用的是T步累积奖赏。$E[1/T * \sum_{t=1} ^{T} {r_t}]$,或者加上一个累积折扣,即:

$E[\sum_{t=0} ^{+\infty} \gamma^t{r_t}]$

K臂赌博机(K armed bandit)

这是最简单的场景,因为每一步都独立。即只需要最大化单步奖赏。这个情景下,一个动作的奖赏,是一个概率分布,无法通过一次工作得到它的期望奖赏。

这里分为两个阶段,在计算每个臂的期望奖赏时,可以采用仅探索,在知道每个臂的期望奖赏时,采用仅利用,去摇哪个期望奖赏最大的臂。单独使用这两种策略都无法实现奖励的最大化,这时候需要结合两种方式来完成。

$\epsilon$-贪心

算法对两种方法取折中,即按照概率确定选用哪个策略,再进行摇臂。有一个优化就是,前期更倾向于探索,后期更倾向于利用,即根据步数修改$\epsilon$。

softmax

算法基于期望奖赏,分配摇臂的概率,按照概率选择哪个臂。摇臂概率大分布按照Boltzmann分布。
$$
P(k) = \frac{e^\frac{Q(k)}{\tau}}{\sum e^{\frac{Q(i)}{\tau}}}
$$

二者对比

具体选择哪个取决于具体应用,也与选择的参数有关系,例如$\tau$, 越小,则趋向于仅利用

有模型学习

假设,MDP过程的四元组已知,便认为是模型已知,即机器已经对环境建模。对于这种情况,成为有模型学习。

策略评估

在模型已知的情况下,策略$\pi$的累积奖赏便可以估计得到。

$V^\pi(x)$表示,在x的初始状态下,基于$\pi$策略的累积奖赏。称为状态值函数(state value funciton)

$Q^\pi(x, a)$表示 x作为初始状态,执行动作a后,带来的累积奖赏。称为:状态动作值函数。(state action value function)

根据累积奖赏的两种定义,可以得到:
$$
V_T^\pi(x) = E_\pi[1/T \ * \sum_{t=1}^{T}r_t|x_0 = x], \ \ \ \ \ T步累积奖赏
$$

$$
V_T^\pi(x) = E_\pi[\sum_{t=0}^{+\infty}\gamma^tr_{t+1}|x_0 = x], \ \ \ \ \ \gamma 折扣累积奖赏
$$

状态动作值函数为:
$$
Q_T^\pi(x,a) = E_\pi[1/T \ * \sum_{t=1}^{T}r_t|x_0 = x, a_0=a], \ \ \ \ \ T步累积奖赏
$$

$$
Q_T^\pi(x,a) = E_\pi[\sum_{t=0}^{+\infty}\gamma^tr_{t+1}|x_0 = x,a_0=a], \ \ \ \ \ \gamma 折扣累积奖赏
$$

基于马尔可夫性质,可以将公示写为递归的形式。
$$
V_T^\pi(x) = E_\pi[1/T \ * \sum_{t=1}^{T}r_t|x_0 = x] \
= \sum\pi(x, a)* \sum_{x\in X}(P_{x\rightarrow x^{‘}} * (\frac{1}{T} * R_{x\rightarrow x{‘}} + \frac{T-1}{T}* V_T^\pi(x^{‘})))
$$
同理,$\gamma$折扣累积奖赏也有如下的累积递归公式。
$$
V_T^\pi(x) = \sum\pi(x, a)* \sum_{x\in X}(P_{x\rightarrow x^{‘}} * (R_{x\rightarrow x{‘}} + \gamma * V_T^\pi(x^{‘})))
$$
可以看到,只有当状态转移矩阵P和奖赏矩阵R已知的前提下,才能完全展开。

背景介绍

机器翻译在神经网络之后得到了长足的发展。随着国际化的趋势,机器翻译在加强国际交流中的作用日益明显。本文将介绍一下基于最新的神经网络翻译架构Transformer来完成双语翻译。

Transformer架构

结构图

Transformer架构图如下,总体可以分为两部分,编码器和解码器(Encoder和Decoder)。编码器和解码器分别有两种基本单元堆叠而成。基本单元又主要分为两种模块,Multi-Head Attention和Feed Forward Network。

###Multi-Head Attention

首先说一下Attention,可以简单用下面一个公式说明白:
$$
Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V
$$
这边采用$\sqrt{d_k}$进行缩放,因为当两个纬度增加时,向量的点基也会增大。

Multi Head Attention的过程就是先将Q,K,V映射到不同的子空间当中,再对不同的子空间做Attention,然后将结果联合起来,再通过一层全连接网络得到最后结果。
$$
MultiHead(Q,K,V) = Concat(head_1, head_2…)W^o
\\
where\ head_i = Attention(QW^q,KW^k,VW^v)
$$
其中, $W^q$,$W^k$,$W^v$的第二维度为各自初始维度d除以head数。

Position-wise Feed Forward Network

有点类似kernel size为1的卷积。对每个位置上的深度向量进行计算。

Position Encoding

由于Attention机制,不能捕捉位置信息,需要将位置信息编码到输入向量当中。有相对编码和绝对编码两种方式。

相对编码

相对编码是固定的,直接通过三角函数计算得到:
$$
PE(pos, 2i) = sin(pos/10000^{2i/d_{model}})
$$

$$
PE(pos, 2i+1)=cos(pos/10000^{2i/d_{model}})
$$

pos是位置编号,i是维度向量中的位置。之所以选择三角函数,因为对于PE(pos+k),可以很容易通过PE(pos) 线性变换得到。

绝对位置编码

绝对位置编码比较简单,直接随机初始化一个embedding矩阵,直接在训练当中学习得到。

二者比较

论文上说,基于机器翻译的实验来说,二者性能上几乎没有区别。然后,选择了相对位置编码,因为作者认为,模型可以在位置长度超出训练集case的情况下,得到一个比较合适的位置向量。

Mask机制

由于这边使用Attention,采用的是定长的结构,所以一般来说,会设一个最大长度,那么训练集当中会出现很多pad。期望的是在输入的时候,能够通过mask机制,将pad的向量在注意力机制中的权重为0。实现方式为修改attention权重矩阵,将mask的权重设置为一个超小的负数,使得算softmax时权重很小。

网络介绍

Bi-LSTM + CRF网络是曾经的STOA用于序列标注的模型,比如命名实体识别。接下来将详细介绍该模型。

LSTM

LSTM全称为Long Short Term Memory networks,是循环神经网络(RNN)添加三个门的改进版本。循环神经网络会维护一个状态(state),并将该状态作为输入,一步一步迭代下去。如下图所示,因为这样的结构,使得梯度计算的时候,会有连乘项,使得梯度产生一个累积效应,导致梯度消失和梯度爆炸。

img

LSTM网络,则是通过添加三个门,来缓解梯度消失和梯度爆炸的问题。

A LSTM neural network.

遗忘门:
$$
f_t = \sigma(h_{t-1}* W_f + X_tW_f + b_f)
$$
输入门:
$$
i_t = \sigma(h_{t-1}
W_i + X_tW_i + b_i)
$$
输出门:
$$
o_t =\sigma(h_{t-1}
W_o + X_tW_o + b_o)
$$
待更新:
$$
C = \tanh(h_{t-1}
W + X_t*W + b)
$$
更新:
$$
C_t = C_{t-1} * f_t + C * i_t
$$
输出:
$$
h_t = \tanh( C_t) * o_t
$$

变种

后面还有很多变种,比如将c也作为输入添加到各个门的计算中。

条件随机场 CRF

首先介绍一下马尔可夫随机场。如果联合概率分布 P(V) 满足成对、局部或全局马尔可夫性,就称此联合概率分布为概率无向图模型或马尔可夫随机场。我的理解具体就是通过边来表示依赖,如果局部的节点给定了,相当于从途中去掉之后,两部分节点属于断开的话,就是条件独立。

条件随机场,属于为概率图模型。是条件概率分布模型 P(Y|X) ,表示的是给定一组输入随机变量 X 的条件下另一组输出随机变量 Y 的马尔可夫随机场,也就是说 CRF 的特点是假设输出随机变量构成马尔可夫随机场。

下图说明了CRF与逻辑回归的的关系和区别,逻辑回归通过几个输入变量,确定一个输出。若是有多个输出,且输出之间存在依赖,则演化成了条件随机场。若依赖是一条直线, 则是线性链条件随机场。下图中,个人理解有一个地方不太对,就是线性链随机场那个子图,每个白圈的依赖不只是只有对应的黑圈,而是所有黑圈。正如第一幅逻辑回归那样。

img

特征函数

转移特征 $t_k(y_{i-1},y_i, x, i)$ 是定义在边上的特征函数(transition),依赖于当前位置 i 和前一位置 i-1 ;对应的权值为 $\lambda_k$ 。

状态特征$s_k(y_i,x,i)$是定义在节点上的特征函数(state),依赖于当前位置 i ;对应的权值为$\mu_k$。

一般来说,特征函数的取值为 1 或 0 ,当满足规定好的特征条件时取值为 1 ,否则为 0 。

安装步骤(mac)

基础环境

操作系统:Mac os

环境:Python3,JDK 8

安装编译工具

安装bazel;推荐使用homebrew安装

Note: 版本要求要低于0.26.1,所以在安装时候需要指定版本,选择合适的formula.

这里给出一个合适的地址:https://raw.githubusercontent.com/bazelbuild/homebrew-tap/c8a0ccc2a8b442d9887d88c6380f835f533ffd47/Formula/bazel.rb

1
brew install https://raw.githubusercontent.com/bazelbuild/homebrew-tap/c8a0ccc2a8b442d9887d88c6380f835f533ffd47/Formula/bazel.rb

下载源代码

1
2
git clone tensorflow
git checkout (branch you want to install)

安装相关依赖

1
2
3
pip install -U --user pip six numpy wheel setuptools mock 'future>=0.17.1'
pip install -U --user keras_applications==1.0.6 --no-deps
pip install -U --user keras_preprocessing==1.0.5 --no-deps

配置

1
./configure

这里大部分都选择默认的no,可以开启XLA优化

编译

1
bazel build //tensorflow/tools/pip_package:build_pip_package

接下来是漫长的等待,我的机器编译了一天(mbp)。

编译python包

1
./bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

得到一个whl包,通过pip安装即可将tensorflow安装至python环境。

什么是扩展?

能够被整合或者导入到其它python脚本中运行的代码,都可以称之为扩展。可以用python写一个package作为扩展,也可以通过C/C++或者JAVA等语言为python写扩展。

为什么需要扩展

有以下三个方面

  1. 满足新功能等需要
  2. 提升性能
  3. 增强代码私密性

一些用python实现不了的功能,需要通过非python代码实现,比如调用GPU等。

解释型语言的效率相比编译型速度会慢一些,在聚焦到核心代码后,通过扩展可以加速这部分代码。

同样,解释型语言暴露源代码,扩展可以免于暴露源代码,增加私密性。

扩展步骤

3个主要步骤

  1. 完成扩展到功能代码实现
  2. 利用样版包装代码
  3. 编译测试

第一步 完成功能代码实现

此时,可以通过编写main函数的方式,对代码功能性进行测试,保障代码正确性。这边,我给出一个简单的例子:

1
2
#include<stdio.h>
#include<stdlib.h>

第一章 什么是架构

架构的目标

以最小的成本构建和维护一个软件系统,满足业务需求(包括当前需求和未来需求)。

常见问题

  1. 忽视好的设计的作用,导致后续维护成本持续增长。
  2. 容忍糟糕的代码,为了临时上线。

Notes

慢但是稳,是成功的秘诀。要跑的快,先要跑得稳。

第二章 两个价值维度

软件的价值

  1. 行为价值(满足业务需求的行为)
  2. 架构价值(软件灵活性,适应后续的需求变更和增加)

研发人员的职责

业务肯定认为行为价值更重要,但是软件开发人员应该重视架构价值,因为一个不可变更或者不可维护的软件系统的价值会最终变成0,而一个维护行好的软件系统会持续产生价值。

业务部门并不对系统的架构价值负责,所以研发人员需要自己平衡好架构价值,并为此负责,与其他部门协调。

第三章 编程范式

结构化编程

结构化编程对程序控制权的直接转移进行了限制和规范。

限制goto

面向对象编程

对程序控制权的间接转移进行了限制和规范

限制函数指针的使用(通过多态)

函数式编程

对程序的赋值进行了限制和规范

不可变性

Note

三个编程范式都是限制程序员行为。

第四章 结构化编程

通过结构化编程,将软件拆分为一个个可推导的单元,通过测试证明这些程序是无法证伪的,则认为程序正确。

按功能性降解拆分是软件设计的最佳实践之一。

第五章 面向对象编程

三大特性

  1. 封装
  2. 继承
  3. 多态

三个特性并不是面向对象编程的创新,之前就通过其他方式实现,但是需要人为遵守规定。面向对象编程消除了人工遵守的必要,通过语言层面实现多态,更简单和安全。

多态的作用

  1. 与具体实现无关
  2. 依赖反转(引入接口)

通过控制组件的依赖,让高层次策略组件和底层实现组件可以相分离,实现独立部署。

第六章 函数式编程

通过变量的不可变性,充分利用多线程的好处,同时避免了变量更改导致的竞争问题。

如果计算和存储无限制的话,不可变性在程序设计中说可行的,例如git版本管理。

在实际情况中,一般说将可变性隔离起来,将不可变量和可变量隔离到不同的组件当中,用合适的机制(锁,事务)保护可变量。

第七章 单一职责原则

定义

一个软件模块,只对某一类行为负责,只对一类客户负责。

Note

单一职责原则主要用于界定函数和类之间的关系,同时,在更高的层面上,比如组件层面,有一个类似的原则,共同闭包原则,在软件架构层面,用于界定架构的边界。

第八章 开闭原则

定义

易于扩展,抗拒修改

Note

高阶组件不因为低阶组件更改而影响

业务逻辑是核心抽象

第九章 里氏替换

定义

所有引用基类的地方必须能透明地使用其子类的对象。

子类可以扩展父类的功能,但不能改变父类原有的功能。

Note

违反LSP原则,系统会不得不增加很多复杂的应对机制。比如各种if else语句

第十章 接口隔离原则

定义

不应该强迫客户依赖于它们不用的方法。

任何让用户依赖他们不需要的东西都是有害的。

Note

不必要的功能代码,导致不必要的重新部署和意外的错误。

第十一章 依赖反转原则

定义

使得高层次的模块不依赖于低层次的模块的实现细节,依赖关系被颠倒(反转),从而使得低层次模块依赖于高层次模块的需求抽象。

规定

  1. 高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。
  2. 抽象接口不应该依赖于具体实现。而具体实现则应该依赖于抽象接口。

Note

接口比实现更稳定

不可避免地依赖具体实现,比如操作系统,但是这部分很少改动。

要重点关注经常变动的部分

使得代码依赖是单向的

第十二章 组件

定义

组件是最小部署单元

问题:virtualenv activate 后失效

问题描述

Virtualenv 经常用于python环境的隔离,但有时候执行了activate命令,终端显示已经进入对应环境(终端前面会显示环境名称)。此时,执行python会发现并没有使用对应环境下的python解释器,而是调用的系统的python解释器。

导致原因

执行source activate 其实是加载activate这个脚本,打开脚本可以看到

1
2
3
4
5
6
7
VIRTUAL_ENV="/Users/someone/Documents/python_env/python3"
export VIRTUAL_ENV

_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"

export PATH

上述脚本其实就是virtualenv 切换python环境的实现方式,通过在环境变量的最前面添加当前环境的python解释器的路径完成。需要注意的是,这个路径是在脚本中写死的,所以如果你将python环境的目录修改了,那么此时会将一个错误的python解释器路径添加到环境变量当中,当然也就找不到对应的解释器,于是系统会接着往下找,最后找到系统的解释器。

问题解决

将VIRTUAL_ENV这个环境变量设置为正确的目录路径。

线性模型

1.基本形式

线性模型的基本思想是通过属性或者特征的线性组合来预测的函数. 数学表达如下:

更通俗的向量表达为:

线性模型比较简单, 并且具有较好的解释性. 很多非线性模型实际上是通过将特征映射到高维空间当中或者通过引入层级结构来实现非线性模型.
经典的线性模型有: 线性回归, 逻辑回归.

2. 线性回归

基于已有数据集 D = {(x1, y1 ) …, (xm, ym )}, 其中x为向量, 表示特征集合. y为实数. 线性回归会取学习到一个线性模型, 使得根据特征向量x, 可以尽可能精确的预测y.

为了确定模型中的参数w, b. 我们首先需要找到一个模型的性能度量. 在回归任务当中, 比较常用的衡量指标是: 均方误差. 这样, 目标函数便是最小化均方误差, 也叫做最小二乘法.
均方误差具有较好的几何意义, 它对应了常用的欧几里得距离.

求解w和b的上述过程, 叫做 线性回归模型的最小二乘 “参数估计”. 由于上述函数是关于w和b的凸函数(这个地方我很好奇 为什么不是叫凹函数,从文字象形上来说 更像凹形状), 因为有这样的性质, 直接求导, 导数为0的点必是最优解, 也是上述函数的最小值点.

解得:

计算机中, 利用矩阵可以通过并行计算加速, 矩阵形式的表示的求解如下:
首先为了计算, 做一点变化, 首先在x的特征向量当中添加一个常数项 1, 这样可以让b 添加w中.即:

于是, 矩阵表示的目标函数为:

对w求导可以得到:

实际情况是, 由于样本数量较多, 并且 有误差的存在, XTX不是满秩矩阵, 这个时候可以通过引入正则化变量

最后说一下广义线性模型, 即在输出的结果中, 使之成为一个可微函数的的输入.

3.逻辑回归

i 模型表示

上述线性回归返回的是一个连续值, 那么在做进行分类任务的时候, 需要将连续值转换为0/1值.
可以想到的是单位阶跃函数, 和 sigmoid函数. 这里利用广义线性模型, 将求得的连续值作为可微函数输入.
由于单位阶跃函数不可微, 所以一般都会选择sigmoid函数, 将输出的值, 记作z 转换为0 -1 之间的连续值.
sigmoid函数可以表示为:

最后输出可以表示为:

ii 目标函数(损失函数)

经过转换后, 我们可以将y的值看作样本为正例的概率, 将1 - y 看作样本为负例的概率. 然后通过”极大似然法”来对参数w和b进行估计.

这里用theta将w和b 一起表示
单个样本的概率表示为:

极大似然函数为:

取对数后:

结果就是要最大话上面的公式, 最大化和最小化是等价的, 但一般都会选择转化为最小化. 也就是在上述公式前面添加负号.

iii 通过梯度下降求解

要使得最小化下面公式:

采用梯度下降的方式完成.
此时通过链式求导, 并且注意利用性质:
g(z)的导数为: g(z)*(1 - g(z))
手工求解如下(这个地方用到损失函数多求了一个平均):

其中, a为学习率, 当模型训练无法收敛时, 可以尝试将学习率调小

iiii 正则化

在目标函数中, 为了防止过拟合, 一般会加入正则项.
通常选用的正则项为L1正则和L2正则.
这两种正则化的区别是L1正则可以更容易产生稀疏解.

4.其它

逻辑回归是一种判别模型, 直接对P(y|x)建模. 生成式模型则是对数据的联合分布建模. 通过贝叶斯公式计算个类别的后验概率, 即:

事实上, 当P(x|y)的分布属于指数分布族时, 可以将生成式推导到判别式模型.