吴恩达机器学习笔记(二)

第三十三课:正规方程(区别于迭代方法的直接解法)

对于某些线性回归问题,正规方程会给我们更好的方法来求得参数θ的最优值。不同于梯度下降法的迭代解法,正规方程提供了一种求θ的解析解法,可以直接一次性的求解θ的最优值。

吴恩达机器学习笔记(二)_第1张图片

常规而言,通过解析法求最小值的时候,一般是求导然后令导数等于零。但是由于这里的θ包含很多不同的特征,所以本质上来说也是只要求偏导然后置零即可。

吴恩达机器学习笔记(二)_第2张图片

m:是训练样本数量;n:特征变量数,其实是n + 1,因为添加了额外的特征变量X0。最后如果你用矩阵X和向量y来来表示θ,这样就得到能够使得代价函数最小化的θ。

吴恩达机器学习笔记(二)_第3张图片

具体如何构建“设计矩阵X”和向量y:

吴恩达机器学习笔记(二)_第4张图片

具体的计算过程:

吴恩达机器学习笔记(二)_第5张图片

注意:在正规方程中,不需要像梯度下降法那样进行特征缩放。

何时使用梯度下降法,何时使用正规方程法(优缺点):

吴恩达机器学习笔记(二)_第6张图片

具体而言,只要特征变量的数目并不大,正规方程是一个很好的计算参数θ的替代方法,只要特称变量的数量小于一万,通常使用正规方程法而不是梯度下降法。但正规方程算法不适用于分类算法中的logistic回归算法等复杂算法,我们不得不回归到梯度下降法。因此梯度下降法是一个很好的方法,既可以用在有大量特征变量的线性回归问题,也可以用在之后课程中的其他算法上。但是对于线性回归这个特定的模型,正规方程法是一个比梯度下降法更快的替代算法。

第三十四课:正规方程在矩阵不可逆情况下的解决方法
吴恩达机器学习笔记(二)_第7张图片

Octave中pinv()求解的是伪逆,即及时矩阵不可逆也可以求解出其逆矩阵。线性代数进阶知识:两个特征是不可以用一个线性方程联系起来的,因为这样的话矩阵X'X将是不可逆的。

矩阵不可逆大体分为两种情况:
1.包含多余的特征值
2.m

所以:如果你发现矩阵X'X是奇异矩阵,或者是不可逆的,首先看特征里是否有一些多余的特征,线性相关的或者互为线性函数,如果的确有多余的特征,可以删除其中一个,将解决不可逆的问题,如果特征里没有多余的,就检查是否有过多的特征,如果特征数量实在太多,如果少一些不影响的话,删除一些特征,或者考虑使用正规化的方法。但是如果你在Octave中使用pinv()函数,那么就不会出现这种情况,因为函数求解的是伪逆,及时X‘X不可逆,算法也能正确运行。

吴恩达机器学习笔记(二)_第8张图片
第三十八课:Octave基本操作

数学运算

逻辑运算
~=:表示不等于而不是!=
&&:与
||:或
异或:xor()
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。

PS('>> ');:更改提示符样式

变量
a = 3
a = 3;(后面加分号可以阻止打印输出)
对于更复杂的输出,有一个disp()命令表示显示
disp(sprintf('2 decimals: %0.2f', a)):disp():输出打印,%0.2f:保留两位小数,sprintf():将括号内的字符转换为字符串
format long:会让字符串显示默认的位数
format short:默认输出打印少量的小数点后位数

向量和矩阵
三行两列:A = [1 2; 3 4; 5 6]
分号的作用:矩阵换行到下一行/或者输入分号后可以回车换行
C = 1:0.1:2:表示C是一组值,从1开始,步长是0.1,增加到2,C将变成一个行向量,变成一个1X11的矩阵
还有一些生成矩阵的方法:ones(2, 3):用来生成一个2X3的矩阵,且矩阵中的所有元素都为1
c = 2 * ones(2, 3):23元素值都为1矩阵
w = zeros(1, 3):1 * 3零矩阵
w = rand(3, 3):随机3
3矩阵,且值都介于0和1之间
高斯随机变量和正态分布的随机变量:rand(1, 3):元素值服从高斯分布的1 * 3矩阵(均值为0,标准差或者方差为1)
w = -6 + sqrt(10) * (randn(1, 10000)):生成一个有一万个元素的矩阵(根号10乘以高斯随机变量)
hist(w):对随机变量w绘制出的直方图
hist(w, 50):绘制有更多竖条的直方图:得到的就是一个均值为-6的高斯分布直方图。这个高斯随机变量的方差为10,标准差为根号10,约等于3.1
生成矩阵的特殊命令:eye():表示生成单位矩阵
I = eye(6)
help eye:输出单位矩阵的帮助函数
Q:退出帮助

课时39:移动数据

如何把数据加载到Octave中?
怎么把数据存入矩阵?
如何对矩阵进行操作?
如何保存计算结果?
如何移动这些数据并且操作它们?

size(A):返回矩阵的大小
实际上size()所返回的是一个1X2的矩阵,所以如果令sz = size(A),sz就是一个1X2的矩阵,它的第一个元素是3,第二个元素是2。sz的两个元素包含了矩阵A的维度。
size(A, 1):将返回A矩阵的第一维度的大小,也就是A矩阵的行数
size(A, 2):将返回A矩阵的列数
length():将返回最大维度的大小

如何在文件系统中加载和查找数据:
当我们打开Octave,我们通常已经在一个默认路径中,它是Octave的安装路径
pwd:显示出当前路径(Octave的当前所在的路径)
cd + 路径:把当前路径更改为新路径
ls:将列出当前路径下的文件列表
load featuresX.dat:加载featuresX.dat文件/或者写成load('featureX.dat')
在Octave中使用单引号可以表示字符串
who:能显示出Octave中的所有变量,即当前Octave在内存中储存的所有变量
输入featuresX,就会显示featuresX中的所有数据
whos:能显示更详细的变量信息,不仅列出所有变量,还列出了变量的维度,还会显示它们占用了多少字节的内存空间以及它们的数据类型
clear():删除某个变量
v = pricey(1:10):把pricey的前是个变量赋给v
如果想把这些数据存入硬盘:使用save hello.mat v命令,这将使变量v保存为一个名为hello.mat的文件
clear:可以直接删除内存空间内的所有变量
save hello.mat v:将数据储存为了二进制的格式,或者说是更压缩的二进制格式,如果v的数据量很大,那么它被压缩的程度也更大,它的占用空间也会更小,如果你想把数据储存为能被人类看懂的形式,可以输入save hello.txt v -ascii:这样就会把数据储存为文本文档或者说用ascii编码的文档。

如何操作数据:
索引:

A(3, 2)    #将索引到A的3, 2位置的元素,通常我们把3, 2写成下标的形式
A(2, :)    #获取第二行的所有元素(冒号表示该行或该列的所有元素)
A(:, 2)    #将返回A的第二列的所有元素
A([1 3], :)    #得到A中第一索引为1和3的所有元素,即A的第一行和第三行中所有列的元素

索引复制操作:

A(:, 2) = [10; 11; 12]    #将A的第二列重新赋值
A = [A , [100, 101, 102]];    #在A的右边附加了一列新的列变量
A(:)    #将A中的所有元素放入一个单独的列向量
C = [A B]    #表示把A、B两个矩阵结合到一起,矩阵A在左边,矩阵B在右边
C = [A; B]    #这里的分号表示把分号后面的矩阵放到前一个矩阵的下面(分号的意思就是换到下一行)

此外[A B] = [A, B],这两种写法的结果是相同的

通过以上操作,掌握如何构造矩阵,如何快速的组合矩阵,将矩阵取出,并将它们组合成更大的矩阵(组合矩阵、移动数据)

快速移动数据,包括加载和储存向量和矩阵,加载和存储数据,把矩阵组合成更大的矩阵,用索引来载入和选择矩阵中某个特定的元素。

第四十课:计算数据

计算两个矩阵的乘积:

A * C    #一个3X2矩阵乘以一个2X2的矩阵,得到一个3X2矩阵(矩阵相乘)
A .*B    #将A中的各个元素与B中对应的元素相乘(元素对应相乘)(一个3X2矩阵乘以一个3X2矩阵,对应元素相乘)
这个点号一般用于表示元素的运算
A .^ 2     #表示对A中的元素进行平方运算
1 ./ v    #表示求v的对应元素的倒数
log(v)    #表示对v中所有的元素进行求倒数运算
exp(v)    #表示以e为底,以v中元素为指数的幂运算
abs(v)    #表示求v中所有元素的绝对值
-v    #求v中所有元素的相反数(等价于-1 * v)

我想取出v,并对v中的每个元素都加1,其中一个方法就是,构造一个3X1的元素全部为1的向量,然后将这个向量与v相加
v + ones(length(v), 1)
length(v) = 3,ones(length(v), 1)就构造了一个和v大小相同的元素全为1的矩阵
另一个更简单的方法就是:v + 1

转置:A‘

(A')'    #A转置的转置
a = [1 15 2 0.5]
val = max(a)    #这将返回a中最大的元素
[val, ind] = max(a)    # 这将返回两个值,val是a中最大的元素,ind是a中该元素的索引
但是输入max(A)    #需要注意,如果A是一个矩阵,这将会得到每一列的最大值
a < 3    #将a中所有元素与3进行比较,根据比较结果来返回真和假
find(a < 3)    #将会找到a中所有小于3的元素,并返回它们的索引
A = magic(3)    #会返回名为幻方的矩阵,

幻方具有一种特殊的数学性质,它任意行、列和对角线中的元素加起来都等于相同的值,这是经过数学构造的结果

[r, c] = find(A >= 7)    #找出A中大于等于7的元素,rc分别表示它们的所在行列

求和函数

sum(a)    #就会得到a中所有元素的和
prod(a)    #就会得到a中所有元素的积
floor(a)    #会对a中元素向下取整
ceil(a)    #会对a中元素向上取整
rand(3)    #得到一个3X3的随机矩阵
max(rand(3), rand(3))    #它会得到由两个3X3的随机矩阵中较大的那些元素组成
max(A, [], 1)    #得到每一列的最大值,这里的1表示从A的第一维度去取值
max(A, [], 2)    #得到每一行的最大值

这种方法能求得每行或每列的最值
如果只是单纯的想知道A中所有元素的最大值:max(max(A))
或者先把A变成向量输入max(A(:)),相当于先把A变成向量,然后求这个向量的最大元素

sum(A, 1)    #就可以得到每一列的总和
sum(A, 2)    #就可以得到每一行的总和

确保幻方矩阵的对角线相加和也相等,可以构造一个单位矩阵eye(9),然后输入A .* eye(9),将这两个矩阵对应元素相乘,除了对角线外的元素与0相乘都会变成0,相当于输入sum(sum(A .* eye(9))), 就能得到A的对角线的元素之和。
同理:输入sum(sum(A .* flipud(eye(9)))):flipud()表示把矩阵垂直翻,就可以对另一条对角线求和

伪逆矩阵

pinv(A)
temp = pinv(A)
temp * A    #得到的就是单位矩阵
第四十一课:数据绘制

通常情况下,绘制数据或学习算法所有的输出可以判断输出值是否收敛以及如何改进你的学习算法,Octave有非常简单的程序来生成大量不同的图。

plot(t, y1);

快速绘制t对应y1的函数图
输入不同的plot()函数将会替代之前的plot()图像,如果想保留两个图像,输入

hold on;

hold on可以让Octave在旧的图像上面绘制新的图像,注意要用不同的颜色表示两个曲线可以输入plot(t, y2, 'r');

xlebel('time'); ylebel('value')    #可以加上横纵坐标轴的标签
legend('sin', 'cos')    #来在图像上绘制图例表示两条曲线的内容
title('my plot')    #在图像顶部显示这幅图的标题
print -dpng 'my plot.png'    #将保存图像
cd "路径";print -dpng 'my plot.png'    #将保存图像到指定路径
close#    关闭当前图像
figure(1); plot(t, y1);
figure(2); plot(t, y2);
subplot(1, 2, 1); 
#它将图像分为一个1 * 2的格子,也就是前两个参数的意义,然后它使用第一个格子,也就是最后一个参数1的意思

plot(t, y1);    #将会在左边的第一个格子中显示sin图像
subplot(1, 2, 2);
plot(t, y2);    #图二将会显示在右边
axis([0.5 1 -1 1])    #也就是设置了右图x轴和y轴的范围
clf;     #命令可以清除一副图像

可视化矩阵:

A = magic(5); imagesc(A)    #将绘制一个5*5的矩阵,将矩阵绘制成一个5*5的彩色格图,不同的颜色对应A矩阵中的不同值
imagesc(A), colorbar, colormap gray;    #生成一个颜色图像,一个灰度分布图,并在右边加入一个颜色条,这个颜色条显示了不同深浅的颜色所对应的值。

例:imagesc(magic(15)), colorbar, colormap gray;:来生成一副15*15方阵数值的灰度示意图

a = 1, b = 2, c = 3:三个命令一个接一个执行
a = 1; b = 2; c = 3:如果用分号代替逗号,不会输出任何东西
逗号连接命令或者函数调用是一种更便捷的命令,来将多条命令写在同一行中。

第四十二课:控制语句(for while if语句)

如何书写控制语句
如何定义和使用函数

v = zeros(10, 1),
for i = 1 : 10;
    v(i) = 2 ^ i;
end;

注意这里的末尾分号,不然它会分别运行十遍命令
另一种方式:

indices == 1:10;
for i = indices,
    disp(i);
end;

while循环:

i = 1;
while i <= 5,
    v(i) = 100;
    i = i + 1;
end;

break语句:

i = 1;
while true,
    v(i) = 999;
    i = i + 1;
    if i == 6,
        break;
    end;
end;

即和Python语句一样,当满足条件时,跳出循环,注意两个end,分别技术while语句和if语句。

v(1) = 2;
if v(1) == 1,
    disp('The value is one');
elseif v(1) == 2,
    disp('The value is two');
else,
    disp('The value is not one or two.');
end; 

exit/quit命令都可以退出Octave

定义和调用函数:
首先要在Octave环境下创建一个文件名为函数名.m的文件,Octave会在这个文件中查找你要使用的函数,可以用notepad打开编辑函数,如何在Octave里面定义函数?

function y = squareThisNumber(x)
y = x^2;

第一行告诉Octave我想返回一个值,将它存放于变量y里,另外这个函数有一个自变量x,第二行告知定义的函数主体是y = x^2

pwd
cd'文件路径'
squareThisNumber(5)

此外还有个高级功能可以设置搜索路径,可以修改Octave的搜索路径

addpath('函数所在路径')
cd '其他路径'
squareThisNumber(5)

此外Octave允许你定义一个函数,能够返回多个值

function [y1, y2] = squareAndCubeThisNumber(x)
y1 = x^2;
y2 = x^3;

在Octave中输入

[a, b] = squareAndCubeThisNumber(5)

Ok接下来看一个复杂一点的函数:
比方说我有一个数据集,数据点如下:

吴恩达机器学习笔记(二)_第9张图片

数据点为(1,1)(2,2)(3,3),我想做的是定义一个Octave函数,来计算代价函数J(θ),不同θ值所对应的代价函数值J。
首先把数据放到Octave中:

X = [1 1; 1 2;1 3];      #设计矩阵X
#第一列表示X0,第二列表示三个训练样本的x值
y = [1; 2; 3];    #y轴对应值
theta = [0; 1];

在路径下提前设定好代价函数J(θ)文件:

function J = costFunctionJ(X, y, theta)
m = size(X, 1)
predictions = X * theta;
sqrErrors = (predictions - y) .^2;
J - 1/(2*m) * sum(sqrErrors);

接着在Octave中输入:

j = costFunctionJ(X, y, theta)

如果令theta = [0; 0];
那么计算j = 2.333
即(12+22+3^2)/(2*m) = 2.333
即第一个样本的平方误差+第2/3个样本的平方误差 / 训练样本数的两倍

第四十三课:矢量化

调用线性代数库而不是自己写命令去做那些函数库可以做的事,通常你会发现用内置算更有效,即运算速度更快,并且更好的利用你计算机里可能有的一些并行硬件系统。

这也意味着你可以使用更少的代码实现你需要的功能。实现的方式越简单,出错的可能性就更小:

吴恩达机器学习笔记(二)_第10张图片

如何用C++实现:

吴恩达机器学习笔记(二)_第11张图片

更为复杂的例子:

吴恩达机器学习笔记(二)_第12张图片

向量化的方法实现:

吴恩达机器学习笔记(二)_第13张图片

这一段超炫的,我觉得是把右边的求和公式仔细去拆开以后发现,是向量乘以实数相加的形式,进而转化为线性代数运算,这样会利用现有的线性代数库加快计算速度,简化计算过程,即使特征向量有很多,要知道这要比for循环分别迭代求解在速度和内存占用上优化多了。高效!

我觉得这不仅仅是Octave,我觉得任何编程语言可能都需要这种解题思路。

你可能感兴趣的:(吴恩达机器学习笔记(二))