机器学习之线性回归-AndrewNg学习笔记

来源:互联网 发布:围棋打谱软件下载 编辑:程序博客网 时间:2024/05/22 12:41

监督学习



从讨论监督学习问题的例子出发,假设我们有某个地区住房面积和相应房价的数据集合。对于这样的给定的数据, 我们的目的是要利用已有的信息,来对房价建立预测模型。即对于给定的房屋信息(房屋面积)预测其房价。


把这些数据在图上表示:


为了方便以后的使用,我们首先定义一些符号标记。我们使用x(i)表示输入变量(或者是特征),使用y(i)表示我们将要预测的输出变量或者说是目标变量(或者叫做标记)。(x(i), y(i))被称作是一个训练样本,训练样本的集合称作训练集。

在这里给出监督学习的比较正式的定义:对于给定的训练集,我们的目标是要学习到函数h,使得h(x)能够尽量准确的预测目标变量y。

当所要预测的目标变量y是连续的时候,这类学习问题为回归问题。当y仅仅能取一些离散值时称之为分类问题。

线性回归



假设收集到的有关房价的数据如下(增加了一个特征:卧室的个数):


为了运行监督学习算法,首先要决定的是如何在计算机中表示假设函数h。在这里我们决定把房价表示为特征的线性函数:

其中θ为参数(或者可以理解为权重),依照惯例,x0 = 1,可以得到向量表示的形式:

其中n是特征的个数。

现在对于给定的训练集合,问题是我们要如何选取(学习)参数θ。一个合理的想法是使得h(x)的值尽量逼近真实标记y(起码在给定的训练集合上努力做到这一点)。我们定义如下的代价函数(它描述的是在任一θ下,h(x)和相应的y的接近程度),代价函数如下:(那么现在我们的目标就是要使得如下的cost function的值最小)

LMS算法:



我们的目标是找到使得J(θ)最小的参数向量θ,为了达到这样的目的,我们需要使用一种搜索算法:给定初始的θ值,并且不断改变θ的值是的J(θ)更小直到θ收敛。在这里我们使用的梯度下降算法:从初始的θ值开始按照以下的形式不断更新:

(note:对所有的θ要同时更新)。alpha称为学习速率或者步长。这个算法的基本思想是:要选择θ使得J(θ)达到最小,那么我每次沿着使J下降最快的方向走一步,而这个下降最快的方向就是梯度的方向。接下来是要进行偏导的求解。首先考虑只有一个样本的情况:

那么对于单个数据样本,我们得到的更新规则如下:

将上式称为LMS(least mean squares)更新规则。将其推广到多个样本的情形得到以下的求解方法:

这就是原始的代价函数的梯度下降算法。需要注意的是:该算法在更新每一个θ时,都要遍历整个训练集。所以该算法也叫做批梯度下降算法。(需要说明的是,尽管梯度下降算法一般会受到局部最小值的影响,但我们在求解线性回归时做面临的最优化问题仅有一个全局最优解)。


上图是一个二次函数的等高线示意图,同时也表明了梯度下降从初始值(48,30)的计算轨迹,图中由直线连起来的X表明了θ的梯度下降轨迹。


梯度下降算法的C语言代码实现(输入数据并不是大神吴恩达所用的数据,因为没有下载到)

#include<stdio.h>#include<stdlib.h>#include<math.h>#include<string.h>int feature_size;int data_size;char filename[20];double** feature;double* theta;double* theta_new;void readData();void gradientDescent();int main(int argc, char** argv){if( argc != 4 ){printf("1.feature_size:\n""2.data_size\n""3.filename\n");exit(0);}feature_size = atoi(argv[1]);data_size = atoi(argv[2]);strcat(filename, argv[3]);printf("%d,%d,%s\n", feature_size, data_size,filename);readData();gradientDescent();return 0;}void readData(){FILE* fread;if( NULL == (fread = fopen(filename,"r")) ){printf("open file error");exit(0);}if( !(feature = (double**)malloc(sizeof(double*) * data_size)) ){printf("feature** malloc error");exit(0);}int i;for( i = 0; i < data_size; i++ ){if( !(feature[i] = (double*)malloc(sizeof(double) * (feature_size + 2))) ){printf("feature[%d]* malloc error", i);exit(0);}}int j;for( i = 0; i < data_size; i++ ){feature[i][0] = 1.0;for( j = 1; j <= feature_size + 1; j++ ){if( 1 != fscanf(fread, " %lf ", &feature[i][j]) ){printf("fscanf error %d %d", i, j);exit(0);}}}for( i = 0; i < data_size; i++ ){for( j = 0; j <= feature_size + 1; j++ ){printf("%lf\t", feature[i][j]);}printf("\n");}}void gradientDescent(){if( !(theta = (double*)malloc(sizeof(double) * (feature_size + 1))) ){printf("theta* malloc error");exit(0);}if( !(theta_new = (double*)malloc(sizeof(double) * (feature_size + 1))) ){printf("theta_new* malloc error");exit(0);}int i, j, k, conv;double diff, sum, alpha = 0.0000001;for( i = 0; i <= feature_size; i++ )theta[i] = theta_new[i] = 0.0;while(1){for( i = 0; i <= feature_size; i++ ){sum = 0.0;for( j = 0; j < data_size; j++ ){diff = 0.0;for( k = 0; k <= feature_size; k++ ){diff += feature[j][k] * theta[k];}diff = feature[j][feature_size + 1] - diff;diff *= feature[j][i];sum += diff;}theta_new[i] = theta[i] + alpha * sum;}for( i = 0; i <= feature_size; i++ )printf("%lf ", theta_new[i]);printf("\n");for( conv = 1, i = 0; i <= feature_size; i++ ){if( fabs(theta_new[i] - theta[i]) > 0.0002 ){conv = 0;break;}}if( 1 == conv ){break;}else{for( i = 0; i <= feature_size; i++ ){theta[i] = theta_new[i];}}}for( i = 0; i <= feature_size; i++ )printf("%lf ", theta[i]);printf("\n");}


(拟合情况如上图,颜色最身的为最后的结果,可以看到一开始的时候几乎没有什么拟合的功能)上面介绍的是批梯度下降算法,下面介绍另外一种

随机梯度下降算法

也叫做增量梯度下降算法,算法描述如下:


在该算法中我们遍历样本集合中的每一个样本,每次使用仅仅一个样本去更新所有的θ值。然而批梯度下降算法在更新一个θ时不得不扫描整个训练集。通常随机梯度下降算法比批梯度下降算法要更快的收敛于接近θ的结果,尽管有可能永远不会收敛而是在最优值附近摆动。而实际上这已经够用了,所以当训练集很大的时候,随机梯度下降算法往往是更好的选择。

以下是C语言实现的随机梯度下降算法

#include<stdio.h>#include<stdlib.h>#include<math.h>#include<string.h>int feature_size;int data_size;char filename[20];double** feature;double* theta;double* theta_new;void readData();void StochasticGradientDescent();int main(int argc, char** argv){if( argc != 4 ){printf("1.feature_size:\n""2.data_size\n""3.filename\n");exit(0);}feature_size = atoi(argv[1]);data_size = atoi(argv[2]);strcat(filename, argv[3]);printf("%d,%d,%s\n", feature_size, data_size,filename);readData(); StochasticGradientDescent();return 0;}void readData(){FILE* fread;if( NULL == (fread = fopen(filename,"r")) ){printf("open file error");exit(0);}if( !(feature = (double**)malloc(sizeof(double*) * data_size)) ){printf("feature** malloc error");exit(0);}int i;for( i = 0; i < data_size; i++ ){if( !(feature[i] = (double*)malloc(sizeof(double) * (feature_size + 2))) ){printf("feature[%d]* malloc error", i);exit(0);}}int j;for( i = 0; i < data_size; i++ ){feature[i][0] = 1.0;for( j = 1; j <= feature_size + 1; j++ ){if( 1 != fscanf(fread, " %lf ", &feature[i][j]) ){printf("fscanf error %d %d", i, j);exit(0);}}}for( i = 0; i < data_size; i++ ){for( j = 0; j <= feature_size + 1; j++ ){printf("%lf\t", feature[i][j]);}printf("\n");}}void StochasticGradientDescent(){int i, j, k, conv;if( !(theta = (double*)malloc(sizeof(double) * (feature_size + 1))) ){printf("theta* malloc error");exit(0);}if( !(theta_new = (double*)malloc(sizeof(double) * (feature_size + 1))) ){printf("theta_new* malloc error");exit(0);}for( i = 0; i <= feature_size; i++ )theta[i] = theta_new[i] = 0.0;double diff, alpha = 0.00001;while(1){for( i = 0; i < data_size; i++ ){diff = 0.0;for( k = 0; k <= feature_size; k++ ){diff += theta[k] * feature[i][k];}diff -= feature[i][feature_size + 1];for( j = 0; j <= feature_size; j++ ){diff *= feature[i][j];theta_new[j] = theta[j] - alpha * diff;}}for( i = 0; i <= feature_size; i++ )printf("%lf ", theta_new[i]);printf("\n");for( conv = 1, i = 0; i <= feature_size; i++ ){if( fabs(theta_new[i] - theta[i]) > 0.0002 ){conv = 0;break;}}if( 1 == conv ){break;}else{for( i = 0; i <= feature_size; i++ ){theta[i] = theta_new[i];}}}}
下面同样是去拟合的结果,在和批梯度下降算法使用的相同的数据的情况下,该方法只迭代了4次就完成了



局部加权回归(LWR)

简单介绍下参数学习算法和非参数学习算法,参数学习算法是指参数个数固定的一类算法,非参数学习算法是指参数数量不固定的一类算法,局部加权回归算法是一种特定的非参数学习算法。

现在考虑根据x对y进行预测的问题,以下的三幅图中,


最左边的图对于数据给出的拟合结果是:

可以看到这些点确实不落在这条直线上,所以我们说拟合的不是很好。之后我们添加一个额外的特征x2,获得了一个稍微好点的拟合结果:

然而并不是这样加的特征越多就会拟合的越好。最右边的图中曲线拟合了一个5次多项式,尽管该拟合曲线经过了每一个点,但是这并不是一个好的拟合结果。最左边和最右边分别称为欠拟合和过拟合。

下面开始介绍LWR。

当面对回归问题时,如果是LR算法,我们需要找到θ使得下面的cost function最小,然后返回θTx就是我们给出的预测值。


但是对于LWR不是这样的。在LWR中,为了对查询点做出预测,我们只是考虑了查询点附近固定长度区域内的数据点(也就是只考虑了一部分的训练集合)。之后使用该区域内的点进行线性回归来拟合出一条直线。然后根据这条直线上相应的对查询点的预测值作为算法的返回值。

正式的定义如下:我们要找到使得如下式子最小的θ


其中是非负的权重值


通过观察可以得到,如果x(i)和查询点x非常的接近,那么wi就会非常的大(接近1),反之则会非常的小(接近于0).也就是说在线性回归时给距离要预测点远的数据点较小的权值,离的近的较大的权值。所以该方法通过加权实现了对邻近点的精确拟合,同时忽略了那些离得很远的点的贡献。

之前的线性回归算法是参数学习算法,因为该算法有固定的,有限个数的参数,一旦我们得到了参数,当进行以后的预测时就不再需要训练集合了。但是当时候LWR算法进行预测的时候,我们需要整个训练集合。


0 0
原创粉丝点击