OmniFocus的几个主要概念

文章中所有的内容都是以OmniFocus 3为例。

  • 文件夹:OmniFocus中任务的组织结构最顶级一层,但只是用于分类,本身没有什么属性。
  • 项目:顾名思义,用于定义我们生活和工作中大大小小的项目,项目本身也有属性设置,可设置标签、其内任务的关系(并行、串行、单个),截止日期等。项目下可包含多个任务。
  • 任务:OmniFocus中承载事件的最小颗粒。因为OmniFocus支持任务无限嵌套,所以当多个任务在一个任务下时该任务就成为一个任务组。
  • 任务组:任务组本身也是一个任务,但是它又是其他任务的父任务,所以任务组有子任务关系(并行、串行、单个)的设置。
  • 标签:任务、项目身上都可以设置一到多个标签。标签可以嵌套,并且标签支持设置地理位置,并且有根据进入,走出两个行为和距离范围设置通知。
  • 透视:根据各种条件筛选任务的视图。比如查看有标签“办公室”,即将截至的任务。条件可以按照包含任意、全部包含和不包含进行组合。然后还可以设置筛选出来任务的排列、分组方式。

任务系统

我参照John Z. Sonmez的看板+番茄钟的系统,将其调整为OmniFocus+番茄钟的系统。

系统设置

  • 在截止日期这一项,将即将截止日期的表示设置为今天。
  • 今天和Watch这一项,设置为自定义透视的今日(该透视下面会讲到)。

构建系统的主要标签

  • 仪式:周期性重复的任务会设置该标签。
  • 地点:标明某个任务能在何种场所进行。
    • 办公室
    • 通勤中
  • 状态:进行任务时需要投入的专注度。
    • 集中精力
    • 放松
  • 跟踪:需要跟踪的任务会设置该标签,比如安排下去的任务需要在某个时间点知道完成的结果。
  • 采购:需要购买东西的任务会设置该标签。
  • 星期:规划一周任务时设置的标签。
    • 周一
    • 周二
    • 周三
    • 周四
    • 周五
    • 周六
    • 周日
  • 二分钟:能在很短时间内完成的任务会设置该标签。
閱讀全文 »

这篇笔记主要介绍梯度下降法,梯度下降不是机器学习专属的算法,它是一种基于搜索的最优化方法,也就是通过不断的搜索然后找到损失函数的最小值。像上篇笔记中使用正规方程解实现多元线性回归,基于$X_b\theta$这个模型我们可以推导出$\theta$的数学解,但是很多模型是推导不出数学解的,所以就需要梯度下降法来搜索出最优解。

梯度下降法概念

我们来看看在二维坐标里的一个曲线方程:

纵坐标表示损失函数L的值,横坐标表示系数$\theta$。每一个$\theta$的值都会对应一个损失函数L的值,我们希望损失函数收敛,既找到一个$\theta$的值,使损失函数L的值最小。

在曲线上定义一点A,对应一个$\theta$值,一个损失函数L的值,要判断点A是否是损失函数L的最小值,既求该点的导数,在第一篇笔记中我解释过,点A的导数就是直线M的斜率,直线M是点A的切线,所以导数描述了一个函数在某一点附近的变化率,并且导数大于零时,函数在区间内单调递增,导数小于零时函数在区间内单调递减。所以$\frac {d L}{d \theta}$表示损失函数L增大的变化率,$-\frac {d L}{d \theta}$表示损失函数L减小的变化率。

再在曲线上定义一点B,在点A的下方,B点的$\theta$值就是A点的$\theta$值加上让损失函数L递减的变化率$-\eta \frac {d L}{d \theta}$,$\eta$称为步长,既B点在$-\frac {d L}{d \theta}$变化率的基础下移动了多少距离,在机器学习中$\eta$这个值也称为学习率。

同理还可以再求得点C,然后看是否是损失函数的L的最小值。所以梯度下降法就是基于损失函数在某一点的变化率$-\frac {d L}{d \theta}$,以及寻找下一个点的步长$\eta$,不停的找到下一个点,然后判断该点处的损失函数值是否为最小值的过程。$-\eta \frac {d L}{d \theta}$就称为梯度。

在第一篇笔记中将极值的时候提到过,当曲线或者曲面很复杂时,会有多个驻点,既局部极值,所以如果运行一次梯度下降法寻找损失函数极值的话很有可能找到的只是局部极小值点。所以在实际运用中我们需要多次运行算法,随机化初始点,然后进行比较找到真正的全局极小值点,所以初始点的位置是梯度下降法的一个超参数。

不过在线性回归的场景中,我们的损失函数$\sum_{i=1}^m(y^{(i)}-\hat y^{(i)})^2$是有唯一极小值的,所以暂时不需要多次执行算法搜寻全局极值。

閱讀全文 »

这篇笔记主要介绍线性回归算法,对在第一篇笔记中介绍过的线性回归算法进行实现。kNN算法主要解决的是分类问题,并且它的结果不具备良好的解释性。线性回归算法主要解决回归问题,它的结果具有良好的可解释性,和kNN算法的介绍过程一样,线性回归算法也蕴含了机器学习中很多重要的思想,并且它是许多强大的非线性模型的基础。

简单线性回归

在第一篇笔记中,我们举过房屋面积大小和价格例子,将其绘制在二维坐标图上,横轴表示房屋面积,纵轴表示房屋价格,这里样本特征数据只有一个,那就是房屋面积,而在kNN算法的分类问题中,二维坐标图上横纵轴表示的都是样本特征数据,这是一个比较明显的区别。如果线性回归问题要在图中表示两种样本特征数据的话就需要三维空间坐标来表示。我们一般将只有一种样本特征数据的线性回归问题称为简单线性回归问题。

回顾线性回归

线性回归其实就是寻找一条直线,最大程度的拟合样本特征和样本输出标记之间的关系。

我们来看上面这张图,大家知道直线的方程是$y=ax+b$,那么点A肯定是在一条直线上,该条直线方程为$y^{(i)}=ax^{(i)}+b$,那么点A的横轴值为$x^{(i)}$,既是A的样本特征值,纵轴值为$y^{(i)}$,既是A的样本输出值。我们假设图中的红线就是拟合直线,方程为$\hat y^{(i)}=ax^{(i)}+b$,也就是将$x^{(i)}$代入这条红线,会得到一个预测的纵轴值$\hat y^{(i)}$。我们希望真值$y^{(i)}$和预测值$\hat y^{(i)}$的差值越小,说明我们的拟合直线拟合的越好。

因为差值有正有负,为了保证都是正数,所以将差值进行平方,之所以不用绝对值,是为了方便求导数。

方程求导的知识可参阅 《机器学习笔记一之机器学习定义、导数、最小二乘》

$$ (y^{(i)} - \hat y^{(i)})^2 $$

将所有样本特征都考虑到,既将所有真值和预测值的差值求和:

$$ \sum_{i=1}^m(y^{(i)} - \hat y^{(i)})^2 $$

将$ax^{(i)}+b$代入上面的公式就得到:

$$\sum_{i=1}^m(y^{(i)} - ax^{(i)}-b)^2$$

上面的公式我们称为损失函数(Loss Function),损失函数值越小,我们的拟合直线越好。在Loss函数中,$a$和$b$是变量,所以我们要做的就是找到使Loss函数值最小的$a$和$b$。这个套路是近乎所有参数学习算法常用的套路,既通过分析问题,确定问题的损失函数,通过最优化损失函数获得机器学习的模型。像线性回归、多项式回归、逻辑回归、SVM、神经网络等都是这个套路。

閱讀全文 »

上一篇笔记主要介绍了NumPy,Matplotlib和Scikit Learn中Datasets三个库的用法,以及基于欧拉定理的kNN算法的基本实现。这一篇笔记的主要内容是通过PyCharm封装kNN算法并且在Jupyter Notebook中调用,以及计算器算法的封装规范,kNN的k值如何计算,如何使用Scikit Learn中的kNN算法,还有机器学习算法中的一些主要概念,比如训练数据集、测试数据集,分类准确度,超参数,数据归一化。另外会具体用代码实现第一篇笔记中介绍过的线性回归算法。

封装kNN算法

上一篇笔记中我们对kNN算法在Jupyter Notebook中进行了实现,但是想要复用这个算法就很不方便,所以我们来看看如何在PyCharm中封装算法,并且在Jupyter Notebook中进行调用。

PyCharm的配置这里我就不再累赘,如图所示,我们创建了一个Python文件kNN.py,然后定义了kNNClassify方法,该方法有4个参数,分别是kNN算法的k值,训练样本特征数据集XTrain,训练样本类别数据集yTrain,预测特征数据集x。该方法中的实现和在Jupyter Notebook中实现的一模一样,只不过加了三个断言,让方法的健壮性更好一点。我们给出N维欧拉定理

$$ \sqrt {\sum_{i=1}^n(x_i^{(a)}-x_i^{(b)})^2} $$

# kNN.py
import numpy as np
from math import sqrt
from collections import Counter

def kNNClassify(k, XTrain, yTrain, x):

assert 1 <= k <= XTrain.shape[0], "k 的取值范围不正确"
assert XTrain.shape[0] == yTrain.shape[0], "训练样本数据行数应该与训练结果集行数相同"
assert XTrain.shape[1] == x.shape[0], "训练样本数据特性个数应该与被预测数据特性个数相同"

distances = [sqrt(np.sum((xTrain - x) ** 2)) for xTrain in XTrain]
nearest = np.argsort(distances)

topKy = [yTrain[i] for i in nearest[:k]]
votes = Counter(topKy)

return votes.most_common(1)[0][0]

这样我们就在PyCharm中封装好了kNN算法的方法,我们再来看看如何在Jupyter Notebook中调用封装好的方法呢,这就需要使用%run这个命令:

import numpy as np

raw_data_X = [[3.393533211, 2.331273381],
[3.110073483, 1.781539638],
[1.343808831, 3.368360954],
[3.582294042, 4.679179110],
[2.280362439, 2.866990263],
[7.423436942, 4.696522875],
[5.745051997, 3.533989803],
[9.172168622, 2.511101045],
[7.792783481, 3.424088941],
[7.939820817, 0.791637231]
]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]

XTrain = np.array(raw_data_X)
yTrain = np.array(raw_data_y)

x = np.array([8.093607318, 3.365731514])

# 使用%run命令可以引入Python文件,并可使用该Python文件中定义的属性和方法
%run ../pycharm/kNN.py
predicty = kNNClassify(6, XTrain, yTrain, x)
predicty
# 结果
1
閱讀全文 »

NumPy

NumPy是Python中的一个类库,它支持高阶维度数组(矩阵)的创建及各种操作、运算,是我们在机器学习中经常会使用的一个类库。这一节再看一些NumPy的矩阵用法。

numpy.random

NumPy也提供了生成随机数和随机元素数组的方法,我们来看一下:

# 生成从0到10之间的随机数
np.random.randint(0, 10)
# 结果
3

# 生成元素从0到10,一共4个随机元素的数组
np.random.randint(0, 10, size=4)
# 结果
array([4, 7, 8, 1])

# 生成元素随机从0到10,3行5列的矩阵
np.random.randint(0, 10, size=(3, 5))
# 结果
array([[6, 9, 7, 0, 9],
[7, 4, 8, 7, 8],
[4, 4, 9, 7, 2]])

如果我们希望每次使用随机方法生成的结果都是一样的,一般调试时候有这个需求,此时NumPy的random()方法也提供了方便简单的方式,既随机种子的概念:

# 生成随机矩阵前给定一个种子
np.random.seed(123)
# 然后生成随机矩阵
np.random.randint(0, 10, size=(4, 5))
# 结果
array([[2, 2, 6, 1, 3],
[9, 6, 1, 0, 1],
[9, 0, 0, 9, 3],
[4, 0, 0, 4, 1]])

# 再次生成随机矩阵时,只要传入相同的种子,就可以得到相同结果的矩阵
np.random.seed(123)
np.random.randint(0, 10, size=(4, 5))
# 结果
array([[2, 2, 6, 1, 3],
[9, 6, 1, 0, 1],
[9, 0, 0, 9, 3],
[4, 0, 0, 4, 1]])

# 默认范围是从0.0到1.0,返回值为float型
np.random.random()
# 结果
0.18249173045349998

# 传入的参数是数组的大小
np.random.random(10)
# 结果
array([ 0.17545176, 0.53155137, 0.53182759, 0.63440096, 0.84943179,
0.72445532, 0.61102351, 0.72244338, 0.32295891, 0.36178866])

# 创建4行5列,元素值的范围从0.0到1.0的矩阵
np.random.random((4, 5))
# 结果
array([[ 0.22826323, 0.29371405, 0.63097612, 0.09210494, 0.43370117],
[ 0.43086276, 0.4936851 , 0.42583029, 0.31226122, 0.42635131],
[ 0.89338916, 0.94416002, 0.50183668, 0.62395295, 0.1156184 ],
[ 0.31728548, 0.41482621, 0.86630916, 0.25045537, 0.48303426]])

指定均值和标准差生成随机数数组或矩阵

我们先来看看均值、方差、标准差的概念。均值很好理解,就是所有样本数据的平均值,描述了样本集合的中间点:

$$ \overline X=\frac{\sum_{i=1}^nX_i}n $$

方差是衡量样本点和样本期望值相差的度量值:

$$ S^2 = \frac{\sum_{i=1}^n(X_i-\overline X)^2} n $$

标准差描述的是样本集合的各个样本点到均值的距离之平均:

$$ S = \sqrt {\frac{\sum_{i=1}^n(X_i-\overline X)^2} n } $$

标准差也就是对方差开根号。举个例子,[0, 8, 12, 20][8, 9, 11, 12],两个集合的均值都是10,但显然两个集合的差别是很大的,计算两者的标准差,前者是8.3后者是1.8,显然后者较为集中,标准差描述的就是这种散布度或者叫做波动大小。综上,方差的意义在于描述随机变量稳定与波动、集中与分散的状况。标准差则体现随机变量取值与其期望值的偏差。

閱讀全文 »