由于研究项目需要,这周开始学习机器学习有关的知识,接下来尽量坚持每周写一次博客,总结一下自己的学习内容,方便以后查阅。如果自己笔记中有错误的地方,还请大家能够帮忙纠正,共同进步!
主要学习内容
- 主要是参考吴恩达的机器学习视频并进行了拓展学习,在这期间主要学习了机器学习的基本概念,并学习了监督学习中线性回归,主要包括两种回归算法-最小二乘法和局部加权线性回归算法。
- 主要是参考吴恩达的机器学习视频并进行了拓展学习,在这期间主要学习了机器学习的基本概念,并学习了监督学习中线性回归,主要包括两种回归算法-最小二乘法和局部加权线性回归算法。
参考资源
目录
- 2017.10.09-10.11
- 主要学习了机器学习的基本概念,学习了最小二乘算法
1 机器学习
- 参考视频:吴恩达机器学习视频
参考博文:
笔记中所用到的符号表示的意义
- m: 训练样本的数目
- n: 特征变量数
- x’s:输入变量/特征值
- y’s:输出变量/目标值
- (x,y): 表示一个样本
- x(i): 表示第i个输入样本
1.1 机器学习概述
- 通常可以被划分为三大种类,取决于可从训练系统获得的信号量或者反馈的属性。包括:
- 监督学习(Supervised learning): 回归(regression)和分类(classification),给定数据集和结果集,通过对新数据进行回归或者分类,来预测新的数据属于给定结果集中的哪一种。
- 处理回归问题常用的算法包括:线性回归,普通最小二乘回归(Ordinary Least Squares Regression),逐步回归(Stepwise Regression),多元自适应回归样条(Multivariate Adaptive Regression Splines)。
- 处理分类问题的常用算法包括:逻辑回归(工业界常用),支持向量机,随机森林,朴素贝叶斯(NLP中常用),深度神经网络(视频、图片、语音等多媒体数据中使用)。
监督学习一般使用两种类型的目标变量:标称型和数值型
标称型:标称型目标变量的结果只在有限目标集中取值,如真与假(标称型目标变量主要用于分类)
数值型:数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 (数值型目标变量主要用于回归分析)
无监督学习(Unsupervised learning): 聚类分析,只给定数据集,没有结果集,由学习算法聚类出结果集。
- 处理聚类问题的常用算法包括:K均值(K-means),基于密度聚类,LDA等等。
- 降维的常用算法包括:主成分分析(PCA),奇异值分解(SVD)等
- 推荐系统的常用算法:协同过滤算法
- 模型融合(model ensemble)和提升(boosting)的算法包括:bagging,adaboost,GBDT,GBRT。
- 其他重要算法:EM算法等等
参考资料:
1.2 回归
回归指的是对于训练数据集{xi,yi},其中,yi是连续值。用过学习,找到函数fθ(x),使得:
此时,为了度量找到的函数的优劣,设计了度量的函数,称为损失函数(代价函数):
注:上公式中的h(x),就是函数f(x),一般f(x)称为模型函数,模型函数有:
- 1. 线性模型
- 线性模型一把采用如下形式:
- 线性模型一把采用如下形式:
其中Ø是基函数,一般有多项式形式和三角多项式形式两种:
(1)
(2)
- 2.核模型
- 线性模型中,多项式或三角多项式等基函数与训练样本{(xi,yi)}ni=1是毫不相关的。而核模型在进行基函数的设计时会使用输入样本{xi}ni=1。
- 核模型一般采用如下形式:
其中K(x,xj)是基函数,也称为核函数,一般选择最多的是高斯核函数:
其中||.||表示2范数,h和c分别对应于高斯核函数的带宽与均值
* 3. 层级模型
- 前面两种模型都是与参数相关的线性模型,层级模型是非线性模型中的一种,特别是在人工神经网络领域。
- 层级模型一般采用如下形式:
- 层级模型一般采用如下形式:
其中Ø是基函数,一般采用S型函数或者高斯函数(1为S型,2为高斯型):
其中S型函数模拟的是人类脑细胞的输入输出函数,因此使用S型函数的层级模型也经常称为人工神经网络模型
2.3 最小二乘法(Least Squares)
最小二乘法(又称最小平方法)是对模型的输出fθ(x)和训练集输出{yi}ni=1的平方误差:
为最小值时的参数θ进行学习
求解方法包括:
- 随机梯度下降算法(Stochastic Gradient Descent)
- 标准方程法(The Normal Equations)
随机梯度下降算法(Stochastic Gradient Descent)
概念:梯度下降法是一个最优化算法,通常也称为最速下降法。最速下降法是求解无约束优化问题最简单和最古老的方法之一,虽然现在已经不具有实用性,但是许多有效算法都是以它为基础进行改进和修正而得到的。最速下降法是用负梯度方向为搜索方向的,最速下降法越接近目标值,步长越小,前进越慢。
-
梯度的概念
- 梯度是一个矢量,在单变量中,梯度就是导数。在多变量中,梯度就是各个方向上的偏导数,梯度一词有时也用于斜度,也就是一个曲面沿着给定方向的倾斜程度。梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即 函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模)。
- 算法实现:
- := 表示赋值
- = 表示逻辑判断
repeat until convergence:{
}
参数更新方式:
{
}
算法细节说明
1. α 表示步长,步长太长容易陷入局部最优解,步长太短增加计算时间
2. 必须同时更新θ0和新θ1,然后存入temp0和temp1中,然后再更新θ0和新θ1
* 注解:该微分就是求梯度,梯度是一个向量,这个向量的每一项都是一个整体,不能先下降X的偏导,再下降y的偏导,所以必须先计算之后,在最后面才更新值
- 偏导化简如下
n = 50; % 训练样本
N = 1000; % 输入样本数
x = linspace(-3,3,n)'; % 所有样本的第一个特征值
X = linspace(-3,3,N)';
pix = pi*x;
y = sin(pix)./(pix)+0.1*x+0.05*randn(n,1);
p(:,1) = ones(n,1); % 设计矩阵
P(:,1) = ones(N,1);
t0 = randn(31,1);
alpha = 0.01; % 步长
% 采用线性模型,基函数采用三角基
for j = 1:15
p(:,2*j) = sin(x*j/2); p(:,2*j+1) = cos(x*j/2); % 30个特征值,共50个样本
P(:,2*j) = sin(X*j/2); P(:,2*j+1) = cos(X*j/2);
end
count = 1;
while 1
% 这是矩阵运算,每次都是一次性计算所有值
t = t0 - alpha * p' * (p*t0-y);
if norm(t0-t) < 0.000001 || count > n*N
break
end
% 统一更新所有 θ
t0 = t;
count = count + 1;
sprintf('count ========== %d',count)
end
F = P * t; % 对输入样本进行训练,与训练样本无关了
figure(2);
clf;
hold on
axis([-3,3,-0.5,1.2]);
plot(X,F,'g-');
plot(x,y,'bo');
- 运行结果如图:
可以看出其实际效果还是相当不错的(没有完全收敛)
* 梯度缩放
为了使函数收敛更快,可以将特征值进行缩放,一般缩放的原则是:
- 标准方程法(The Normal Equations)
标准方程法就是直接对函数进行求导得到
θ = (X TX) -1X Ty 证明方法见讲义 notes1
求θ有两种方法,一种是根据如上的方法 θ = (X TX) -1X Ty,另外一种就是直接通过 X*θ=y求出θ = x -1y 两种方法对比图:
n = 50; % 训练样本
N = 1000; % 输入样本数
x = linspace(-3,3,n)'; % 所有样本的第一个特征值
X = linspace(-3,3,N)';
pix = pi*x;
y = sin(pix)./(pix)+0.1*x+0.05*randn(n,1);
p(:,1) = ones(n,1); % 设计矩阵
P(:,1) = ones(N,1);
for j=1:15
% 选择线性模型,三角多项式作为基函数
p(:,2*j) = sin(x*j/2); p(:,2*j+1) = cos(x*j/2); % 30个特征值,共50个样本
P(:,2*j) = sin(X*j/2); P(:,2*j+1) = cos(X*j/2);
end
t = p\y;
t1 = (p'*p)\p'*y;
F = P*t;
F1 = P*t1;
figure(2);
clf;
hold on
axis([-3,3,-0.5,1.2]);
plot(X,F,'g-');
plot(X,F1,'r-')
plot(x,y,'bo');
legend('t=p\y','t1 = (p''*p)\p''*y','data');
- 梯度下降法&&标准方程法特点
梯度下降法 | 标准方程法 |
---|---|
需要选择α | 不需要选择α |
需要很多次迭代 | 不需要迭代 |
当n很大时,仍然可以用 | 需要计算(XTX)-1,当n很大时,计算量非常大,当特征值超过一万,就可以考虑使用梯度下降法了 |
* 以上三种处理的对比如图:
可以看出效果 红色>=黑色>绿色,梯度下降法迭代了50001次,资源消耗大,对于小数据优先考虑标准方程法
标准方程法求解线性回归方程,python代码
- x是一维的,也就是特征值为1,所以h=θ0+θ1x
# 数据来源于机器学习实战第八章
# LMS:least mean squares
from numpy import *
import matplotlib.pyplot as plt
def loadDataSet(fileName):
numFeat = len(open(fileName).readline().split('\t')) - 1 # 计算特征值n
dataMat = []
labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr = []
# strip是去除数据开始和末尾的空格
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
dataMat.append(lineArr)
labelMat.append(float(curLine[-1]))
return dataMat,labelMat
def standRegres(xArr,yArr):
xMat = mat(xArr)
yMat = mat(yArr).T
# 标准方程法 w = (x.T*x)^-1x.Ty
xTx = xMat.T * xMat
if linalg.det(xTx) == 0.0: # 行列式等于0,不存在逆矩阵,不过可以使用linalg.pinv()
print("This matrix is singular,cannot do inverse")
return
theta = xTx.I * xMat.T * yMat
return theta
def plotTest(xMat,yMat,xHat,yHat):
# flatten使列矩阵变成行矩阵,.A是是matrix变为array
# plt.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0].flatten().A[0])
plt.scatter(xMat,yMat)
plt.hold(True)
plt.plot(xHat,yHat,'r')
plt.show()
def main():
xArr, yArr = loadDataSet('ex0.txt')
xMat = mat(xArr)
yMat = mat(yArr)
# print(xArr[0:2])
theta = standRegres(xArr,yArr)
# print(theta)
# main函数画图,如果先排序要做如下处理
xCopy = xMat.copy() # 排序不影响结果,应为theta是根据训练数据训练得来的,与输入数据没关系
xCopy.sort(0)
yHat = xCopy*theta
plotTest(xMat[:,1],yMat,xCopy[:,1],yHat)
print(corrcoef(yHat.T,yMat))
if __name__ == '__main__':
main()
- 运行结果如下
- 区分所建模型好坏可以通过计算预测值yHat和真实值y序列的匹配程度,就是计算两个序列的相关系数。python的numpy中提供了相关系数的计算方法:corrcoef(yEstimate,yActual)。
print(corrcoef(yHat.T,yMat))
- 输出结果:
[[ 1 0.98647356]
[ 0.98647356 1 ]] **对角线上的数据表示yMat和yMat自己匹配的程度,为1,是最完美的,斜对角表示yHat和yMat的匹配程度是0.98**
- 参考资源
- 2017.10.11-10.13
- 主要学习了局部加权线性回归算法,并学习了交叉验证的原理
2.4 局部加权线性回归(Locally Weighted Linear Regression,LWLR)
- 普通的线性回归算法,得到的参数矩阵(theta是根据少量数据训练后得到的参数矩阵θ,他是固定的、有限的,对于对于未来数据进行预测的时候不会改,这叫做参数学习算法(parametric learning algorithm)。而局部加权线性回归算法在计算每个点的权重时,会考虑所有的输入数据,虽然计算量增加了,但是预测更加接近实际值,在一般称为无参学习算法(non-parametric learning algorithm)
- 该算法解出回归系数θ的形式如下:
LWLR算法采用的“核”函数最常用的就是高斯核模型,高斯核对应的权重如下:
其中||x(i)-x||是求范数,||x||=sqrt(x*xT)。这里的k就是前面所述核模型的h,表示高斯核模型的带宽。是唯一需要确定的值。
- python代码如下:
# LMS:least mean squares
from numpy import *
import matplotlib.pyplot as plt
def lwlr(testPoint,xArr,yArr,k=1.0):
xMat = mat(xArr); yMat = mat(yArr).T
# 每个点都会有一个权重值,每次预测一个点的权重值都要考虑全部的数据
m = shape(xMat)[0] # 得到样本个数
weights = mat(eye(m)) # 得到一个m*m的单位矩阵
for j in range(m):
diffMat = testPoint - xMat[j,:]
# 更新该行的权重,每一行是一个样本,权重值一样
# 核函数选择高斯核函数,范数计算:||x||=sqrt(x*x.T)
weights[j,j] = exp(diffMat*diffMat.T/(-2*k**2))
xTx = xMat.T*weights*xMat
if linalg.det(xTx) == 0.0:
print("This matrix is singular,cannot do inverse")
return
theta = xTx.I * xMat.T * weights * yMat
return testPoint * theta # 得到一个预测值
def lwlrArray(testArr,xArr,yArr,k):
m = shape(testArr)[0]
yHat = zeros(m)
for i in range(m):
yHat[i] = lwlr(testArr[i],xArr,yArr,k)
return yHat
def loadDataSet(fileName):
numFeat = len(open(fileName).readline().split('\t')) - 1 # 计算特征值n
dataMat = []
labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr = []
# strip是去除数据开始和末尾的空格
curLine = line.strip().split('\t')
for i in range(numFeat):
lineArr.append(float(curLine[i]))
dataMat.append(lineArr)
labelMat.append(float(curLine[-1]))
return dataMat,labelMat
def plotTest(xMat,yMat,xHat,yHat,k):
# flatten使列矩阵变成行矩阵,.A是是matrix变为array
# plt.scatter(xMat[:,1].flatten().A[0],yMat.T[:,0].flatten().A[0])
plt.scatter(xMat,yMat)
plt.hold(True)
plt.plot(xHat,yHat,'r')
plt.legend('k=%f'%k)
plt.show()
def main_lwlr():
xArr,yArr = loadDataSet('ex0.txt')
xMat = mat(xArr)
yMat = mat(yArr)
# print("yArr[5]=%f"%yArr[5])
# print("预测值为:")
# yHat_i = lwlr(xArr[5],xArr,yArr,k=0.01)
# print(yHat_i)
# 需要对xMat排序
srtInd = xMat[:,1].argsort(0) # 按列排序,0表示按列排序,1表示按行排列
# xSort = xMat[srtInd][:,0,:] # xMat排序后得到的是三维矩阵,不好理解
xSort = xMat[srtInd,1]
k = 0.01
yHat = lwlrArray(xArr,xArr,yArr,k)
plotTest(xMat[:,1],yMat,xSort[:,1],yHat[srtInd],k) # # 因为采用高斯核模型,与输入数据有关,所以yHat要做相应的调整
if __name__ == '__main__':
main_lwlr()
- 上面代码给定了k=0.01,得到的拟合曲线为:
- k值的选取对拟合结果有很大的影响,因为k值是高斯核模型的带宽,带宽越宽,那么每个点的权值就更加接近,拟合效果近似直线,带宽越小,每个点的权重值相差较大,容易出现过拟合的现象,这在实际的场景中需要对参数进行调节。
- 下面取k=1.0,k=0.01,k=0.003分别进行拟合对比:
- 可以看出k=0.01时拟合效果最好,k=1.0时和普通最小二乘法是一样的,k=0.003时出现了过拟合的现象。
- 局部加权平均和最小二乘算法的比较
LSM | LWLR |
---|---|
Fit θ to minimize sum(y(i)-θTx(i))2 | Fit θ to minimize sum[(w(i))(y(i)-θTx(i))2] |
output θTx | output θTx |
- 介绍了以上两种方法来找出最佳的拟合直线,接下来将用这些技术来预测鲍鱼的年龄
数据来源:机器学习实战第八章
print("训练数据预测")
xArr,yArr = loadDataSet('abalone.txt')
# 对训练数据进行预测,误差结果计算
yHat01 = lwlrArray(xArr[0:99],xArr[0:99],yArr[0:99],0.1)
yHat1 = lwlrArray(xArr[0:99],xArr[0:99],yArr[0:99],1.0)
yHat10 = lwlrArray(xArr[0:99],xArr[0:99],yArr[0:99],10.0)
print("k=0.1时,误差代价为:%f"%rssError(yArr[0:99],yHat01.T))
print("k=1.0时,误差代价为:%f"%rssError(yArr[0:99],yHat1.T))
print("k=10.0时,误差代价为:%f"%rssError(yArr[0:99],yHat10.T))
# 可以看出对训练数据预测,k越小误差越小
print('='*50)
print("新数据预测")
# 对新的数据进行预测,误差结果计算
yHat01 = lwlrArray(xArr[100:199],xArr[0:99],yArr[0:99],0.1)
yHat1 = lwlrArray(xArr[100:199],xArr[0:99],yArr[0:99],1.0)
yHat10 = lwlrArray(xArr[100:199],xArr[0:99],yArr[0:99],10.0)
print("k=0.1时,误差代价为:%f"%rssError(yArr[100:199],yHat01.T))
print("k=1.0时,误差代价为:%f"%rssError(yArr[100:199],yHat1.T))
print("k=10.0时,误差代价为:%f"%rssError(yArr[100:199],yHat10.T))
# 对于新数据,k越小反而误差越大,这是因为造成了过拟合现象,而
# 预测模型的好坏就应该根据对新数据的预测效果来判断
print('='*50)
print("最小二乘法对新数据进行预测")
ws = standRegres(xArr[0:99],yArr[0:99])
yHat = mat(xArr[100:199]) * ws
print("最小二乘法误差代价为:%f"%rssError(yArr[100:199],yHat.T.A))
- 输出结果为:
数据来源 | 条件 | 误差代价 |
---|---|---|
训练数据预测 | k=0.1 | 56.795728 |
训练数据预测 | k=1.0 | 429.890562 |
训练数据预测 | k=10.0 | 549.118171 |
新数据预测 | k=0.1 | 21537.640485 |
新数据预测 | k=1.0 | 573.526144 |
新数据预测 | k=10.0 | 517.571191 |
最小二乘法新数据预测 | 518.636315 |
* 从上述结果可以知道,对于新数据,核大小k=10时误差代价最小,简单的线性回归达到了与局部加权线性回归类似的效果,这些表明必须在未知数据上比较效果才能选取最佳模型。上例中k=10也不一定是最优的结果,如果要选择更好的方法,可以采用交叉验证的方法。
- 模型选择步骤
- 准备模型的候选M1,…,Mk。
- 对各个模型M1,…,Mk求解其学习结果f(1),…,f(k)
- 对各学习结果f(1),…,f(k)的泛化误差G(1),…,G(2)进行评价。
- 选择泛化误差最小的模型为最终模型
泛化误差是指对未知的测试输入样本的输出所做的预测的误差。在对泛化误差的的评价过程中,泛化误差并不是训练样本的误差,实际中经常使用的是交叉验证法。
- 交叉验证法(Cross Validation)
在交叉验证法中,把训练样本的一部分拿出来作为测试样本,不将其用于学习,而只是用来评价最终学习结果的泛化误差。
交叉学习算法流程
- 把训练样本{(xi,yi)}ni=1随机划分成m个集合(大小基本相同)
- 选取其中的m-1个作为训练样本进行训练,求解其结果fi。
- 循环选择训练样本集对测试样本集Ti 进行学习,求每个学习结果集产生的代价误差。
这里Ti 表示集合包含的训练样本的个数。另外sign(y)表示y值的符号函数:
- 对各个泛化误差的评估值Gi 进行平均,得到最终的泛化误差的评估值G。
- 现在在最小二乘法(采用标准方程法)和局部权重线性回归(高斯核,k=10)对鲍鱼的年龄预测误差进行交叉验证,代码如下:
# 分成五组,每组50个样本
# 标准回归
xArr,yArr = loadDataSet('abalone.txt')
N = 50 # 样本数
n = 8 # 特征值
m = 5 # 分成5组
k = 10 # 核大小
ws = mat(zeros((n,m-1)))
G = mat(zeros((m-1,1)))
for i in range(m-1):
ws[:,i] = standRegres(xArr[i*N:i*N+N],yArr[i*N:i*N+N])
fi = mat(xArr[N*(m-1):N*m])*ws[:,i]
G[i] = 1/N*rssError(yArr[N*(m-1):N*m],fi.T.A)
print("标准方程法预测值误差:%f"%(G.sum()/(m-1)))
# 局部权重线性回归
lG = mat(zeros((m-1,1)))
for i in range(m-1):
yHat = lwlrArray(xArr[N*(m-1):N*m],xArr[i*N:i*N+N],yArr[i*N:i*N+N],k)
lG[i] = 1/N*rssError(yArr[N*(m-1):N*m],yHat.T)
print("局部权重线性回归预测值误差:%f"%(lG.sum()/(m-1)))
- 运行结果如下:经过交叉验证,可以看出k=10的局部加权线性回归算法和标准最小二乘法的泛化误差大小相差不多,从而得出两着之间的拟合效果类似
标准方程法预测值误差:6.085034
局部权重线性回归预测值误差:6.083830