[自用]吴恩达机器学习笔记
自己水平很菜 不少的知识点在听课的时候就有点一头雾水 记下来也免不了会有很多错误。 这份笔记主要是留着自己需要的时候翻出来看一下 如果发现了问题欢迎批评指正! 谢谢!
机器学习
简介
机器学习
-
机器学习定义
-
在没有明确设置的情况下 使计算机具有学习能力的研究领域
-
"A computer program is said to learn from experience E with respect to some task T and some performance measure P, if its performance on T, as measured by P, improves with experience E."
-
-
机器学习应用
- 垃圾邮件过滤
- 对抗式学习
- 图像语音识别
- 自动驾驶
- ......
-
机器学习分类
- 监督学习 -> 教电脑做某件事
- 无监督学习 -> 让电脑自己学习
- 强化学习 etc.
监督学习
-
例一 回归问题
对于一个散点图 你可以直线拟合:
也可以二次函数拟合:
显然二次函数拟合的更好
这一个过程其实就是监督学习算法的一个很好的例子
-
监督学习
- 给定一个数据集 里面的是"正确的答案" (即数据集中给出的散点数据) 算法的目的是给出更多的正确答案
- 对于上面的这个例子 有个更专业的称呼 —— 回归问题 (设法预测连续值的属性)
-
例二 分类问题
这些数据记录的 对于不同大小的肿瘤 他们是良性(0) 还是恶性(1) 要做的是预测其中一个大小是良性还是恶性
或者有两个变量
这些都是分类问题
无监督学习
-
对于监督学习来说数据是这样的 有标注
而无监督学习的数据将会没有标注没有标签
所以这个时候我们所需要做的一般是把数据分成一簇一簇的 也就是聚类算法
平日里的新闻集 使用的就是聚类算法 上万篇新闻中将相关的数篇分为一类
-
无监督学习
- 对于给定的无标签数据 尝试将其中有联系部分进行分类
-
鸡尾酒会算法
- 简单鸡尾酒会算法模型 两个麦克风离两个人分别不一样的远近 两个麦克风都录到了两人混在一起的声音 但是通过聚类算法就可以把两个人的不同响度的声音分离开来
然而实现这个很nb的功能只需要一行代码:
吴恩达老师说这需要Octave来帮忙了
模型
常用符号
m
训练集中数据条数x
输入y
输出(x,y)
表示一条训练数据 (有输入有输出)(x^(i),y^(i))
表示第i条训练数据 并不是x的i次方的意思!
线性回归模型 / 梯度下降法
h(x)=θ_0+θ_1x
即将h(x)拟合成线性函数的样子
要取得最好结果 即希望h(x)和真实的y接近 也就是我们希望下图右边式子最小
这也就引出了我们的代价函数
代价函数
也被叫做误差平方代价函数
如何寻找最拟合的θ\_0
θ\_1
?
Batch梯度下降法
// - batch 成批 batch file 批处理文件
-
先考虑简单算法 假设θ_0为0 更改θ_1的不同的值 绘制出代价函数关于θ_1 的函数
选择θ_1是 1 0.5 0 之后就可以画出如下函数图
-
如果把θ_0加入考虑 那么就要有两个变量了 最终的代价函数应该在三维坐标系中表示成这样
将其拍到平面上 那就是这样
最低点就是拟合的最好的(θ_0,θ_1) 离得越远也就拟合的越差
- 对于一般情况的梯度下降法 那就是有很多个θ 基本思路也就是固定多个θ 让一个θ动 然后慢慢找到最小的J值
为方便表示 以后J后面就不放太多参数 只放两个
找一个点 环顾四周 找去哪里能下山 找到往前走一步 继续环顾四周找下山 然后走一步 由此往复
这样就能找到最低点 但是不同的起点 很有可能找到不同的终点 如图所示
数学表示就是这样
其中α
是学习速率 在这里也可以理解为步长 也就是下山的时候一步多大 显然步子越大下山下的越快但是最后结果越不容易精确
符号:=
的意思是赋值 因为在数学里一个单独的等号是判断相等 前面加个冒号就是编程中常用的赋值
这个算法的精髓在于同时更新θ_0
和θ_1
左下的代码是正确的同步更新的方法 右下是错误代码
- 下面用单个变量来讲解梯度下降法的原理
这就是里面导数项∂/∂θ
的用处 让你向着最低点前进
这就是学习速率的大小对结果的关系 如果太大就会产生下面的情况 有可能越来越远
虽然步子太大会导致θ跑太远以至于直接跳过最低点 我们却不需要重新更新学习速率α
因为:
导数越趋近于最低点就越小 所以步长也就自动的越来越小了
此算法简单且有效 一个字妙
应用梯度下降法求解代价函数
原线性回归模型是这样:
进行代价函数的微积分的计算 可以得到下面的式子
带回原来的代价函数就有:
然后进行应用之后 这就是最终不断梯度下降的直观结果:
这种梯度下降法也被称作 "Batch"梯度下降法 因为它遍历了所有数据的J值然后进行求和平均
接下来会讲解一个更加通用的梯度下降算法
矩阵多元梯度下降法
在介绍算法之前要先学习一下矩阵相关运算
先说一下有关矩阵乘法 对于方程式的计算 可以写成下面这种矩阵乘法的形式 要注意左边系数矩阵中多出来的一列1 非常关键
然后先前那个房价的例子就能写成如下形式:
对于矩阵的逆矩阵(inverse) 在Octave中就很简单一行代码即可:
pinv(A) //Pseudo-inverse
考虑到10^-15小到忽略不计 最后的ans毫无疑问就是个单位矩阵
没有逆矩阵的矩阵被称作 奇异矩阵 或者 退化矩阵 (其实就是行列式为0的矩阵)
而
pinv()
其实是伪逆矩阵的意思 因为真正的逆矩阵只有方阵才有 伪逆矩阵就可以做到:
转置矩阵比较简单 就是翻转过来
B_ij=A_ji
在Octave中 转置就是在右上方加一撇就好
A'
好了下面进入矩阵梯度下降法的介绍
这是各项已知数据的表格 我们首要的就是要把计算式子翻译成矩阵语言
矩阵的形式就给了我们一个研究多元线性回归的方法
多元线性回归的方程于是乎就要扩展成为:
对于有些不同数据的范围不同的情况 比如下图的2000和5 差的太大就会导致画出来的等高线太细长 梯度下降就很容易跑过头 所以这时候就需要特征缩放
全缩放成1就好辣
当然 平时的时候我们一般希望缩放结果在区间[-1,1]
内 接近也可以
当然 这个特征缩放也有更加高级的操作——均值归一化
当你想确认代价函数最后是否已经收敛到所需要的值 就可以用下面这个函数 如果是这样收敛的样子 那就拿下了
想要找到合适的学习率 可以将学习率3倍3倍往中间插入使得α
降低 这样可以快速的找到心仪的学习率
对于多元梯度下降的模拟 结合数据的样式 自己选择合适的多项式 然后代入上面的多元梯度下降公式即可
但这个时候就要注意特征缩放 因为一旦进行平方三次方 数据范围就被撑的很大很大
正规方程法 / 非梯度下降法
梯度下降法运用迭代不停地接近最小值 而正规方程法可以一步解出最小值点
用微积分的知识也就是 对于每一个θ全都求偏导使其为0 然后解方程组
让我们先把系数啥的表示成矩阵形式
这张图片下面的公式 就是我们梦寐以求的最低点θ
对于这个方程 吴恩达老师并没打算给出解释 那就死记吧
要注意的是 这个正规方程不需要特征收缩 同样也不需要学习速率α
梯度下降法 V.S. 正规方程法
考虑到时间复杂度的问题 样本量m不大的时候可以使用正规方程 而m较大比如上万时 最好使用梯度下降法迭代出最终结果
分类问题
- 二分类和多分类
-
如果把我们上面学到的线性回归应用于分类问题会怎么样?
对于一堆数据 我们要看从哪个点开始从0变成了1
参照上图你会发现 似乎有搞头 线性回归找到拟合的直线 然后截取直线上纵坐标为0.5的点 这样一来就是左边的看作是0 右边的看作是1 好有道理
BUT!!! 如果出现离群点 就是离大部队比较远的一个点 就会大大影响线性回归拟合直线的准确度 如下图的蓝色线
一下子就把0.5这个点往右偏了好多 大大影响了分类结果的准确度
因此线性回归最好不要用于分类问题
- 所以最好使用下面讲述的 sigmoid/logistic 回归
sigmoid/logistic 回归
- 目前这个是针对二分类 也就是0和1
总的来说 就是预测出来的θ可能值大于0.5那就归为1 小于0.5就归为0
决策边界
在这里面 尤其是在这张图里面 那就是x=0就是我们的决策边界 对于下面这个例子:
这个多参数的二分类 显然图中的玫红色分界线就是我们的决策边界 他的决策条件是g函数中的那个式子是否大于0
所以不同的问题所需要的决策条件和决策边界肯定都会有区别的 当然也可能会有下面这种决策边界
这就要强调一点: 决策边界不是你这个数据集所具有的属性 而是你这个θ参数值确定好之后就随之确定好的东西 有没有数据都无关
这句话的意思就是 下面我们就要知道 如何寻找能让决策边界拟合我们的数据集的θ值
寻找θ值
寻找θ值之前我们需要定义一个很重要的东西 那就是我们的代价函数 一个好的代价函数可以帮助我们快速的收敛到要找的值 有一个很好的代价函数是这样的:
这个分段函数总的来说其实就是当0和1两个点都趋近于无穷 也就是代价无穷大
给他赋予一个含义 那就是
y=1的时候 我们说代价函数是-log(h(x))
就是图上的蓝色线 当x->0的时候代价无穷大
也就是 客观事实是y=1你的肿瘤是恶性的 但我们非要说x=0也就是你的肿瘤是良性的 这属于不可饶恕的错误 我们要用非常非常大的代价来惩罚这个算法 也就是无穷代价
同理 y=0 也就是明明是个良性肿瘤 但你非要x=1说是恶性的 这也是不可饶恕 必须无穷代价来惩罚
前方高能! 这个分段函数细想一下 其实可以合并
下面展示这个优美的合并思路:
其实这个思路用到了统计学中的极大似然法 而且这个式子还有另一个好处就是他是凸函数 方便我们梯度下降
牢记这个式子 这个就是二分类的代价函数!
其实有了式子之后 接下来就是求最小值 也就是梯度下降
然而对于梯度下降 有一个高级优化算法可以处理大量样本量的情况
高级优化
有一些更高级的算法 例如BFGS L-BFGS等 目前他们太高级了暂时不需要
高级数值计算将会学习到这些算法
言归正传 普通的求最小值如果梯度下降迭代的话那其实就是两个大步骤 一个求偏导数 一个同时更新θ值
这个在Octave中可以做到
右上角是每次迭代所要执行的函数内容 下面的内容就是如何做到不断的迭代函数
-
第一行
optimset
中的'GrandObj','on'
的意思是梯度参数打开 意思是这个算法要提供一个梯度'MaxIter','100'
的意思是设置最大迭代次数是100 -
第二行就是设置梯度下降的起始点
-
第三行
fminunc
意思是 函数在无约束条件下的最小值 不过内嵌了高级优化的函数 他会自动选择好的算法不用我们烦心 然后他就会帮我们找到costFunction的最小值其中
@costFunction
是函数指针指向我们的迭代函数 后面一个是起始值 最后那个就是他所需要的设置参数 -
返回值
optTheta
就是收敛到最后的θ值是啥functionVal
就是在那个θ值的时候函数值是多少而
exitFlag
比较高级 可以理解为是否收敛 最后如果是1的话那就是成功收敛 θ值就是我们要找的东西
诶 上面说的都是梯度下降的事情 那怎么把这个东西给用到我们的 logistic回归
呢
其实就是修改一下迭代函数就好了
多元分类
现实生活中 显然也会有很多分类问题不只是只有两个 而是多个种类的分类
一个容易想到的思路就是 把图中这个三分类问题转化为三个二分类问题 分别二分类 之后再聚合到一起
过拟合问题
什么是过度拟合
对于这个住房价格数据的三种拟合曲线
-
图一 直接拟合成了直线 但是很显然价格越到后面越平缓 而不是一直升高 所以第一种是欠拟合(Underfit) 我们先入为主的以为要拟合成直线 导致后面直接高出 所以这种情况也可以称作**高偏差(High Bios) **
-
图二拟合的很好
-
图三 用了四次项去拟合 看似每个点都在拟合曲线上 但显然这不是一个好的拟合 这就是所说的过拟合(Overfit) 而且上下波浪起伏 因此也可以称为高方差(High Variance)
从理论上而言 有多少个数据 用多少项的多项式就可以完全把数据全部完美精准的放到曲线上 但这显然不会是一个好的拟合思路
升级一下
显然第三个也是过拟合
他千方百计的想把已有的数据全部精准的纳入自己的范围中 但这样反而失去了之后泛化(generate)的准确度
过拟合常常出现在 参数很多样本很少的情况下 解决方法:
-
主要在参数方面
- 人工筛选哪些参数是有用的 哪些参数是冗余可以舍去的
- 模型选择算法 来自动选择哪一种模型是参数最合适的
-
正则化(Regularization)
也许每个参数都是或多或少有用 所以保留所有参数 但是减少参数的量级或者是大小
正则化
本来一个二次函数就可以拟合的很好 但你非要四次函数 那么后面多出来的两个参数我们如何惩罚?
有一个简单粗暴而且有效的方法:
很乐 在代价函数里直接给你加上一个很大的系数 也就是你后面两项将会有很大的代价 那么如果想要代价小 必然是后面两项尽可能的小 这也就从一个四次函数降成了二次函数
这就是正则化背后的思想: 更简单的假设 带来更平滑的拟合
这是特殊情况 一般情况呢? 100个参数我们怎么一个个的判断哪些参数不需要呢?
所以一般情况的正则化 需要我们直接给所有的参数都套上一个大数字 也就是下面这个式子
这个后面加上的一个小式子就是精髓
当然这就带来了另一个问题:
正则参数λ的设置
如果我们λ设置的太大 那就对有用参数的惩罚也太大了 这样一来 θi
全都趋于0 房屋价格函数只剩个常数项了 显然不对 λ太大就会导致偏见性太强
线性回归梯度下降的正则化
对于之前的线性回归梯度下降方程 我们使用正则化的话就是这样
注意几点:
- 对于
θ0
我们不需要正则化 因为它对应的是常数项 所以上图特地把j=0的情况给去掉了 - 正则化其实也就是给
j>=1
的所有项后面跟上了一个λ/m*θ_j
这个东西从括号中抽出来再和前面的合并就会有一个有趣的系数:1-αλ/m
这个系数是略小于1的一个玩意儿 也就是0.99级别 所以其实我们所谓的正则化就是把原来的迭代函数中θj给乘上了一个近似是0.99的一个玩意儿 (听起来很不靠谱 其实有效而且有专业名词——缩小平方范数)
线性回归正规方程的正则化
还记得原先的正规方程是啥吗
原先就没给出为什么是这个式子 那么正则化后的式子也直接死记吧!
还好它very的好记
在那个逆矩阵中添加一个n+1阶的主对角矩阵 除了对角线上的元素其他都是0 对角线记得第一个也要放0 这意味着θ_0
不需要正则化
这个还有个好处 加上这个矩阵之后可以证明新矩阵一定可逆 正好就避免了奇异矩阵的情况
Logistic回归的正则化
还记得二分类问题中的Logistic回归吗 类似于线性回归直接在后面填上一个θ^2
项 这个也差不多
如图 在末尾填上一个有关θ平方的单项式就好 这就是我们的正则化参数
然后就一项一项代入之前的式子就好
什么式子? Octave代码里fminunc
那个函数有关的式子
(详见logistic回归中的高级优化)
这个在Octave中可以做到
右上角是每次迭代所要执行的函数内容 下面的内容就是如何做到不断的迭代函数
第一行
optimset
中的'GrandObj','on'
的意思是梯度参数打开 意思是这个算法要提供一个梯度
'MaxIter','100'
的意思是设置最大迭代次数是100第二行就是设置梯度下降的起始点
第三行
fminunc
意思是 函数在无约束条件下的最小值 不过内嵌了高级优化的函数 他会自动选择好的算法不用我们烦心 然后他就会帮我们找到costFunction的最小值其中
@costFunction
是函数指针指向我们的迭代函数 后面一个是起始值 最后那个就是他所需要的设置参数返回值
optTheta
就是收敛到最后的θ值是啥functionVal
就是在那个θ值的时候函数值是多少而
exitFlag
比较高级 可以理解为是否收敛 最后如果是1的话那就是成功收敛 θ值就是我们要找的东西诶 上面说的都是梯度下降的事情 那怎么把这个东西给用到我们的
logistic回归
呢其实就是修改一下迭代函数就好了
神经网络 / 非线性假设
问题背景:
很多时候 我们的参数是很多的
不同的参数x组合能产生很多很多种单项式 例如光秃秃的θ1x1
也有很恶心的θi x1^3 x3x4x5^4
这种式子
说明如果按照之前普通的尝试线性多项式去思考的话 他们的特征空间将会急剧的膨胀
所以我们需要思考有没有非线性方法来避开多参数导致的特征空间膨胀过大问题
实际问题:
我们如何让电脑知道一张车的图片中 展示的是一辆车?
现在的解决思路: 用一个学习算法 喂给他两组数据 一组是车一组不是车 然后算法训练出来一个分类器 用这个分类器之后去区分哪个是车哪个不是
先从小处入手 我们在所有图片上找两个像素点 在坐标系中将两个像素点的值以坐标的形式呈现出来
显然不同的图片会有着不同的坐标 将是车的图片用+号表示 不是的用-号
我们以此类推 多标记一些 然后我们就发现这就变成了刚刚我们谈论的二分类问题:
但是哪怕图片很小 也会有很多很多像素点 50*50糊成shit一样的图片也会有2500个像素点 两两排列组合那就是上百万个特征 显然我们之前学习的二分类知识在这里难以派上用场
- 这里其实也告诉我们 Logistic回归法适用于n比较小的时候
那该怎么办? 主角登场:
神经网络
什么是神经网络
第一个very的离谱
摄像机将图像转化为电信号 通过电极片传导刺激舌头 舌头接收信号反馈给大脑 大脑处理成图像
这就可以让盲人获得"视觉"
这也太他妈帅了
"神经"网络:
这是真正的神经 每个神经元接收到信号 处理之后传递给下一个
有点像Qt的信号和槽 ?
下面就是我们神经网络模型的一个简单单个神经元模型
这就是简单版本的 hθ(x)
就是图中那个公式 其中θ矩阵被称为权重 h函数被称为激活函数
有时候你也可以添上x0 作为偏置单元 它有时候会有些用
一个神经网络可能会有很多层 第一层就是输入层 最后一层就是输出层 中间就是隐藏层 顾名思义
每一层每个元素的表示 右上角括号数字就是第几层 右下角角标就是这层第几个元素/激活项
大写的大Θ
右上角括号数字(j)
意味着 这是从第j层到第j+1层的权重矩阵 他控制着每层映射的结果
- 其中 Θ(1) 就是第1层第2层中间那些箭头 所代表的计算
其实很容易可以看出来 g函数里面的东西其实就是矩阵乘法
所以可以变成下面图片中右边形式:
从第一层 一层层的向着输出层传播 这就是前向传播
其实并非所有神经网络都是一个结构 比如下面:
这就是神经网络不同的架构(Architectures)
不同的架构当然会得出不一样的结论 也会适用于不一样的问题
下面来个实例:
还是二分类问题 我们如何做到区分右边图片中的数据? 先让我们尝试分类左边
左边有个简单的规律: 他们的正负和横纵坐标的异或同或值有关
抓住这个规律我们来进行构造
- 输入层有x1 x2 还有一个偏置激励元 +1
- 我们假设设置参数分别是 -30 +20 +20 然后代入数据后算出z值
- 再代入
g(z)
得到之后的结果 (记住g函数是那个有关自然指数倒数的那个函数)
如图
然后你会发现这个小神经网络其实实现了逻辑与的功能
再看一个
参数调整为-10 20 20之后 就实现了类似逻辑或的功能
参数调整为10 -20 就实现了类似逻辑非的功能
把这几个基础逻辑功能放在一起 就能实现我们所需要的功能
比如我们要实现同或 那么如下:
有一说一 这个例子太有数字电路内味了
神经网络多分类问题
-
假如图中的神经网络是一个判断 图中是人是车是摩是卡 的一个神经网络
那么输入图片之后将会输出一个矩阵 其中4个数值 哪个是1就意味着这张图中神经网络识别的是什么
为什么不把矩阵转换成一个数字? 也就是输出1 2 3 4 来表示图中是什么?
这就很显然没有考虑到一个问题 如果图中都有呢? 那怎么表示? 说明这个思路的可拓展性就没有返回一个矩阵来得高
-
L
指神经网络层数s_i
指第i层有多少个激活元
神经网络代价函数
还记得Logistic回归的代价函数吗
"稍微"改动一下 就是神经网络的代价函数
- 第一项就是把原来的y换成了y_k 因为有很多层的y hθ也换成了hΘ_k 因为对应每一层的输出
- 第二项原来就是每一个权重平方 这个也是 只不过多了每一层都要计算
所以看上去很哈人 本质上还是没怎么变
ok 代价函数有了 我们弄出来代价函数 就是为了求他的最小值 怎么求呢?
这需要下面介绍的这个算法:
反向传播算法 Backpropagation Algorithm
手头上现在有个棘手的问题: 如何求解神经网络那个长长的代价函数的最小值
解决此事的算法叫做反向传播算法 顾名思义那就是反着来干 先让我们复习一下正向传播是什么样:
那什么叫反向传播呢
-
δ
指的是偏差(Error) 可以理解为这个点现在的值 跟他本来应该等于的值 之间的差 -
下面就是计算每一层偏差项的过程
-
- 先拿到最后一层的偏差项
- 然后每往前一层 偏差项都跟后面一层的偏差项有关
- 最后遍历结束 拿到每一个偏差项 代入最下面蓝色公式中求出代价函数的导数并求出极值
-
因为从后往前 所以被称为反向传播算法
-
进一步理解反向传播
-
看上图 我们在这之中引入了一个
cost
函数 他扮演一个类似于方差的角色 可以近似地看成(h-y)^2
这种偏差值的感觉 反映了准确程度 -
注意右侧的两个
δ
式子 会发现他们都与后面一个偏差项有关 因此要从后往前推导 也就是反向传播
神经网络求解代价函数完整过程
下面就是完整的计算过程&伪代码 very very的重要
- 先设置每个
Δ_ij
都是0 这个在之后求解代价函数导数的时候会有用 - 然后就是一个for循环 意思是从第一层一直到最后一层
- 在这里面 先执行正向传播算法 算出每一层的结果
- 之后执行反向传播算法 计算每一层的偏差
δ
- 最后将
Δ_ij
更新为红色式子
- for循环结束后 根据j来更新
D_ij
如果是0那么对应的是偏差项本身 所以不需要加上标准化项 - 最后弄出来的这个
D_ij
就是我们所要求的代价函数的偏导数
一些思想
展开参数
我们之前学过了Octave里面有个函数叫fminunc
可以用来求梯度
但是到了神经网络这边 返回gradient表示一个梯度就不够了 因为神经网络有好多好多梯度
所以我们需要研究如何把这么多矩阵取出来 展开成向量以此来方便计算
-
方法: 把他们全部塞进一个向量里 这会导致那个向量非常的细长
之后要再分开的时候就用
reshape
函数 第一个是元素列表 后面是横10列11 -
然后就可以把这么多个矩阵变成一个向量 放进
fminunc
函数辣
梯度检测
反向传播有时候会出现一个bug 看起来程序运行也没啥问题 梯度也是在慢慢减小 但是最后得到的结果会比没bug的时候误差高出一个数量级
这个时候就需要梯度检测
这个公式可以给出J(θ)在θ处的导数近似数值
那么偏导数就是这样:
代码如下:
-
如果这样计算出来的导数跟我们反向传播算出来的东西很接近 那么我们就可以确信反向传播没bug 放心大胆的去用
-
注意!!!
这个公式法非常耗时 他只是用来检验反向传播没有问题 所以用一次就好!
一旦确认反向传播没问题就要把它关掉了 不然他将会非常非常的耗时
随机初始化
对于优化函数fminunc()
函数 它里面的 initialTheta
参数 一般来说是需要给一个初始值的
那能随便给吗?? 比如我们给一个全是0的矩阵进去?
如果给0矩阵 让我们看看下面的分析
简单来说 这就会导致这个神经网络将计算不出来任何有趣的结果 因为你的参数全都没变过
解决方法就是随机
将权重随机初始化为一个接近0的大小在[-ε,ε]
之间的数 然后再进行反向传播
神经网络完整过程
Step1.选择结构
- 输入层: 多少x取决于你要输入多少个
- 隐藏层:
- 一般来说 隐藏单元越多 效果越好 不过多了也会加大计算量 这个自己取舍
- 通常每一个隐藏层都是一样大小的隐藏单元数量
- 隐藏层的选择很有学问 后面接触到的话会提及
- 输出层: 你要分为多少类 就有多少输出单元
选择好结构之后 要初始化权重(随机初始化 详见:随机初始化)
Step2.前向传播
前向传播算法 找到每一个x对应的hΘ(x)
以及最后的y
Step3.计算代价函数
用代码计算出来代价函数
Step4.反向传播
利用计算出来的代价函数 执行反向传播算法 计算出所有的偏导数项
这里初学者可以直接用for循环 它适用于比较浅层的网络 但如果是深层的网络就最好用向量化的方法了 以此提高计算效率
Step5.梯度检查
利用前文提到的梯度检查来进行代码的验证 如果结果没啥问题那就停用梯度检查 因为他计算太慢了 用一次检验就行
Step6.fminunc()
利用优化函数 计算出每一个偏导数的值
完整流程
如何优化或调整模型
性能评估 / 机器学习诊断法
并非拟合结果跟数据越接近 拟合的就越好(过拟合问题)
所以有一个常用的方法:
把数据集 按照7:3的比例分为两部分 7份作为训练数据 3份作为测试数据
评估误差时:
下面的err()
函数 就是0/1错误分类度量函数 如果error那就是1 之后把test集中每个点的error加在一起来评估好坏
模型选择问题
我们现在一般选择模型的流程:
这个流程走下来选出来的拟合函数 可能是对于已知数据的很好拟合 但对于新的数据 他真的泛化能力好吗??
所以我们需要有一个能够评估假设模型的泛化能力的方法
还记得上面性能评估的时候我们干了什么吗? 我们把数据集分为7:3的部分 7份是训练集 我们用这个来拟合出我们的函数 3份是测试集 我们来测试这个拟合结果的效果如何
在这里由于我们还要评估假设模型的泛化能力 所以有一个新的分类方法:
622 -> 60% 20% 20%
其中60%是训练集 20%是测试集 重要的是剩下来的20%: 交叉验证集 Cross Validation
这样的话 流程就是 先用训练集训练出各个模型的参数θ 接着在调整参数的时候 用交叉验证集 **算出方差值 判断是否过拟合 ** 接着用测试集定出最小的泛化误差
这样就能初步的避免掉过优化的问题 总之 训练集用来训练 交叉测试集用来筛选出方差最小的那个模型 最后测试集用来评估泛化能力
诊断偏差与误差
偏差值大 就是欠拟合 ; 方差值大 就是过拟合
这两种问题的解决方法截然不同 所以我们要判断出来 出的是哪一种问题
吴恩达老师有一张非常形象的图像解释了偏差和方差的关系
-
随着选择模型的多项式次数增大 肯定是越能拟合到训练集的数据 也就是偏差越来越小
-
一开始的时候 通过交叉验证集算出的方差肯定大 不过多项式增大之后就能发现慢慢会变小一些 但你这个时候如果不加束缚继续增大多项式次数 肯定会过拟合 方差再次越来越大
如何区分? 这张图其实已经提供思路了:
正则化偏差
还记得之前线性回归的时候怎么正则化的吗? 我们给后面添上了一个参数平方求和项 目的就是通过这个压缩每一个参数的值 避免出现参数太大
这里同样也可以 用正则化压缩参数的值避免出现过拟合
主要就是最开始加了个正则化参数 后面不用加(因为后面主要是验证)
- 第一步选出来你要测试的λ 推荐从很小的数字开始 每一个跟上一个差两倍
- 对于每一个λ 算出带正则化的代价函数 并且算出最后的参数θ
- 对于每一个已经算出θ的模型 用交叉验证集 算出他们的方差 从中挑出方差最小的那个模型 作为我们的最终结果(注意这里算方差的时候不要正则化 因为正则化只是让你减小参数用的 这里算方差是一个验证的过程)
- 最后用测试集 代入计算 评估这个模型的泛化能力
观察这个曲线图 会发现正好跟上面那个相反 细想一下确实如此 λ越小 也就是参数的个数越没有束缚 也就越可能过拟合低偏差 之后λ如果太大 那么参数就被压缩的很小了 所以会有欠拟合的情况
学习曲线
当训练集的样本增多的时候 显然就越来越难以拟合到一个优秀的曲线 所以训练集的偏差会增大
但是 训练集样本越来越多 最后做出来的曲线也就越来越拟合 所以会有更好的泛化能力
也就是说交叉验证集的方差会越来越小
如下
这个有大用处:
-
当你处于高偏差时(欠拟合) 你会意识到哪怕你再喂更多的数据进去 我们的训练集偏差和交叉验证方差 也就差不多这样了
这就意味着 高偏差状态下 就不要浪费时间收集更多数据了 因为没啥卵用 -
当处于高方差时(过拟合) 我们画出训练集和交叉验证集的代价函数 会发现大致规律会比较像上面的那个 但要注意: 过拟合的情况 对于训练集将会拟合的很好 而交叉验证集将会表现得很差 所以这个时候有个显著的特征就是 两者之间差别很大 线条之间有一个巨大的鸿沟
但是这个时候 我们继续增大训练集 通过正则化我们可以逐步逐步缩小两者之间的距离 最终得到一个还不错的结果 也就是说高方差的时候 继续喂数据是对改进算法来说有用的
-
总之
高偏差 / 欠拟合 -> 别想着喂数据了 没啥用 想着怎么增加特征
高方差 / 过拟合 -> 喂数据有用 多喂点 顺便再调整调整正则化参数λ的值也可以
总结: 如何调整模型
对于神经网络也是如此
如果你不确定该有多少个隐含层 有多少个激活元
那么你就用622 多试几个模型 看看最后哪个模型对交叉训练集结果最好 然后再用测试集去验证一下这个模型的泛化能力
机器学习系统设计
实例: 如何区分垃圾邮件和有用邮件??
想法: 垃圾邮件和有用邮件 主要就是其中有一些关键词不一样 我们找一些关键词 来判断他们是否在邮件中出现 由此得到一个向量 最后分析这个向量来得出邮件是否是垃圾邮件
想法很简单 如何改进算法有很多种思路:
那么我们该花时间在哪一个上面呢?
这个时候要记住一个思想: 如何优化一个算法 并非起步阶段要考虑的问题 过早优化有时候事倍功半
初步阶段当然也有自己的优化方法:
初步阶段 当然我们的模型对已有的数据不会拟合的太好 那么我们就来分析那些被我们错误分类的邮件 我们来看一看他们有什么特征导致了被我们错误分类 从而在算法中关注这一特征
如何系统的知道这个特征我们有没有用?
思路也很简单 先不选这个特征找到错误率 再把这个特征选上 找到错误率 比较一下就知道该不该选了
不对称分类的误差评估
还记得我们之前说过的癌症例子吗 但现实生活中关于癌症的数据集可不像分红球黄球这样可能五五开的情况 很有可能出现1000人中只有5个是真有癌症的阳性 也就是99.5%的阴性率
这个时候 你拿着这1000人的数据 辛辛苦苦训练出来一个算法 一看准确度99.3% 好像还不错
结果呢 另外有一个算法 这个算法无论给什么输入 只返回阴性 看起来非常非常蠢
但是呢 这个蠢逼算法 对于这1000人 却有着99.5%的准确率
这还不是问题的全部 另一个问题在于: 既然如此 如果你费尽心思的把原来那个99.3%的算法 优化成了准确率99.5%的算法 看似好像是精确度提高了 但你又怎么知道新优化出来的算法其实只是变成了那个蠢逼算法呢?
这个时候就要用到一个天才思路:
精确度和召回率
我们先把所有的真实数据 以及我们的预测数据 合在一起分成四类:
如果真实情况跟预测情况相同的 那就是真阳和真阴
如果不同 真实是阳我们预测成阴 说明这个是假阴 反过来 真实是阴我们非说人家是阳 这个就是假阳
围绕着这四组数据我们计算下面两个参数
-
精确度: 真阳除以所有预测的阳
算出来的东西就是 如果你被预测成阳了 你有多大的概率是真的阳性
即 判断这个算法预测的准不准
-
召回率: 真阳除以所有真实的阳
算出来的东西就是 在所有真实的阳里面 你这个算法涵盖了其中多少的阳性
即 判断这个算法预测的全不全
这个时候再鞭尸那个蠢逼算法 会发现他所有的阳性全都漏掉了 也就是召回率为0 就不是一个好的算法
天才思路
准确度和召回率的权衡
但是我们再来想两个情形:
-
癌症的治疗太痛苦 秉着"宁可放过不可杀错"的原则 医院决定预测结果是70%的可能性往上我们再去判断阳性 这样算法说一个病人是阳性的时候就有很大概率真的是阳性 但是同时这也会漏掉很多算法判断概率不足70%但是实际还真是阳性的人
也就是说 高准确度 低召回率
-
癌症的后果太吓人 秉着"宁可杀错不可放过"的原则 医院决定结果是30%以上的通通判断是癌症 显然这个时候就有很多真的癌症的人被发现了出来 涵盖率很广 但同时也拉了很多无辜的阴性人下水
也就是说 低准确度 高召回率
所以我们就需要在准确度和召回率之间寻找一个权衡
一下子想到直接把两个加起来取平均
但是这样的平均值就容易让之前那个蠢逼算法 也就是高准确度但是召回率为零的那个蠢逼算法 成为漏网之鱼
所以这里取平均值并不是简单平均 而是调和平均值 也就是下图中的F值 之后会经常用到
支持向量机
支持向量机 Support Vector Machine
[没学 被我跳过了XD ]
无监督学习
之前的监督学习其实就是 对于已有标签的数据进行操作
而无监督就是对于没有标签的数据之间 进行诸如寻找结构等操作
显然他也有很多的应用
让我们回到之前的那个散点图上来
如果我们想让计算机知道这个散点图有两个簇 我们该使用什么样的算法呢?
K-Means均值算法
在散点图上 我们随机点上两个点
这两个点就是聚类中心 (当然还不是最终的) 下面我们使用K均值算法
K均值算法是一个迭代算法 他会做两件事情
-
- 簇分配
-
- 移动聚类中心
什么是簇分配?
就是对于散点图上每一个点 我们求出他们离这两个聚类中心中的哪个更近 算出来之后我们就把这个点归到这个聚类中心一边
什么是移动聚类中心?
我们把图上所有的点都标记好了之后 我们接下来对于每一个聚类 我们求出他们所有点的平均值 将求出来的平均点作为下一个聚类中心
一直迭代下去 到了最后 就没有点再被更改位置 我们的聚类中心就找到了
如图:
很天才的想法
伪代码也很简单
关于k均值算法 他也有个属于自己的代价函数J()
那就是失真函数
有了这个失真函数 回到刚刚那个迭代过程就会发现 :
第一步 簇分配 其实就是在找到每个点的c值 使得失真函数最小(聚类中心不动)
第二步 移动聚类中心 其实就是在找到每一个μ值 使得失真函数最小(每个点的聚类分配情况不动)
如何初始化聚类中心
我们如果任意的初始化聚类中心 其实是有可能得到比较烂的结果的
解决方法: 随机初始化 并且多随机几次 每个随机初始值最后都能找到它们对应的最小的失真函数值 最后把这些失真函数值汇总再比较 找到最小的那个
如何选择聚类的数量
通常的做法: "肘部法则"
对于数据集 我们采用各种不同的K值 也就是尝试多种聚类数量的情况 并汇总成学习曲线
看曲线会发现有一个类似于"肘部"的东西 我们就去"肘关节"那个点
当然 如果最后是像右图那样比较平滑的曲线那你就抓瞎了
所以手工选择聚类数量 有时候也是必要的
毕竟无监督意味着无标签 对于无标签的图 你要让机器自动判断该分成几类实在有点困难
数据压缩 / 主成分分析
其实这个问题比较普遍 经常会有你想出来两个特征 结果一看这两个特征互相之间是一种比较简单的线性关系 那其实两个特征都用同一个特征来表示就好了 这也就是特征压缩
上图是二维压缩成一维 其实三维也可以压缩成二维 下图就是一个三维"云" 但是他的点大多数都落在一个平面内 所以我们可以把它压缩成二维
这个其实能帮助我们实现一些很有用的东西
比如下面这个国家的各项数据 假设有50项数据 我们可以画个50维向量 但这有谁能看得懂呢 所以最好的做法就是我们把50维的数据压缩为二维 放到平面上 以此实现可视化
主成分分析
初步的主成分分析 就是找到一条线段使得所有的数据投影到这个线段上 所有点与投影点之间的偏差最小
也就是点到直线的距离之和最小
专业表述:
找到一个向量 使得所有点投影到这个向量所在的直线形成的偏差最小
有一说一 这看上去和线性回归好像啊
但他们俩确实有两个地方有巨大差异
- 线性回归也是平方之和最小 但是那个平方之和是垂直着来画的 看的是真实y值与拟合y值之间的差 在主成分分析这里是点到直线的距离
- 线性回归是有x值和y值之分的 而在主成分分析这里不存在 所有的x值都是相等地位的
主成分分析算法
一般第一步是均值标准化 把每一个数减去他们的平均数 就能得到在0附近的数据
下面的操作就比较玄学了
-
svd()
奇异值分解 Singular Value Decomposition奇异值分解在某些方面与对称矩阵或Hermite矩阵基于特征向量的对角化类似。然而这两种矩阵分解尽管有其相关性,但还是有明显的不同。谱分析的基础是对称阵特征向量的分解,而奇异值分解则是谱分析理论在任意矩阵上的推广。
假设M是一个m×n阶矩阵,其中的元素全部属于域 K,也就是实数域或复数域。如此则存在一个分解使得
其中U是m×m阶酉矩阵;Σ是半正定m×n阶对角矩阵;而V*,即V的共轭转置,是n×n阶酉矩阵。这样的分解就称作M的奇异值分解。Σ对角线上的元素Σi,其中Σi即为M的奇异值。
常见的做法是为了奇异值由大而小排列。如此Σ便能由M唯一确定了。(虽然U和V仍然不能确定)直观的解释
在矩阵M的奇异值分解中
·U的列(columns)组成一套对M的正交"输入"或"分析"的基向量。这些向量是MM*的特征向量。
·V的列(columns)组成一套对M的正交"输出"的基向量。这些向量是M*M的特征向量。
·Σ对角线上的元素是奇异值,可视为是在输入与输出间进行的标量的"膨胀控制"。这些是MM及MM的奇异值,并与U和V的列向量相对应。
有丶看不懂 没事会用就行
-
关于其中的k 其实就是你想从n维到k维 换句话说 "主成分分析" 这个k就是你想保留多少个主成分
-
之后 把得到的U矩阵中 前k列拿出来(从n维到k维 这个k随你定) 转置之后乘以x向量 就得到了我们想要压缩到的z向量
下面一个问题来了 我们怎么确定我们要保留多少的主成分特征呢?
也就是我们怎么确定k值呢?
可以用下面这个公式来判断我们是否找到了一个不错的k值
这是一个评估函数 用来评估按照目前的k值 你压缩之后的数据是否还好用
这就引出了第一种确定k值的思路 一个个试 一直到找到了这个评估函数的值低于阈值的那个k
但是 还记得我们刚刚用的那个svd()
吗 他返回了一堆东西 在这其中有个东西可以直接拿来用!
下图左边就是我们刚刚想到的一个个k值去试 右边就是通过刚刚svd()
返回的S矩阵去直接解出评估函数的值
你会发现这个就是一次求svd()
直接就能求出任意k的结果 wtf
压缩重现
那我们压缩过去了 我们又该怎么取回原来的数据呢?
记住 原来从x到z只是左乘了U_reduce
矩阵的转置 那么从z返回到x就是做成这个玩意儿的逆就好辣
PCA应用
主要就是数据有大量特征的时候 就可以用到PCA来简化数据 加快之后算法的运算
但是 "特征数量过大 我们需要缩小特征" 这听着耳不耳熟呢
跟我们之前学过的 过拟合问题好像啊!
但在这里就一定要记好: 对于过拟合问题 千万不要用PCA去优化 正则化比PCA好得多!
因为在PCA的眼里 所有的数据都只是没有含义的数字而已 他这样一合并压缩 很有可能就压缩掉了有用的数据 所以别用在过拟合问题里面
异常检测
问题背景: 就是一堆正常的在一块儿的数据点里面 你忽然注意到了远离大部队的一个数据点 那个数据点就是一个异常 我们需要知道如何把他检测出来
基本思路:
对数据点进行建模 越是中心的点就越可能是正常数据 离得越远的点就越可能是异常
也就是对每个点是不是正常数据得出一个概率值 如果概率低于这个阈值 那么就可能是异常点
它的应用十分广泛 可以检测异常用户 异常主机 也可以检测cpu异常线程之类的很多东西
高斯分布 / 正态分布
他是一个概率函数 所以函数下面部分的面积积分一定是1 所以你改变σ
的宽窄 肯定会影响图像的高低
如何计算μ
和σ
的值? 一个公式就行
异常检测算法
下图是求解 密度估计 的算法 (其实就是求这一个数据的每一个特征 在这个特征属于的高斯分布中的概率 最后相乘 就能得到这个数据点ok的概率)
在m个数据集中 将向量x的第一个特征x_i
在所有向量的第一个特征形成的高斯分布中的概率求出来 再把第二个特征的高斯分布像这样求出来 一直求到最后第n个特征的高斯分布 最后累乘
最后得到的东西就是这个向量x在整个数据集中 属于ok部分的概率
如果概率低于你设置的阈值 那就是异常
总结:
例子:
这里有个小细节:
如果你发现你的数据出来之后里高斯分布差的有点远(下图左下) 那么你可以尝试一些变换函数(log()等等) 强行把他们变得像是高斯分布一样
那么我们如何构建一个异常检测的应用呢?
-
第一步肯定还是训练模型
先是找到一堆数据 然后开始喂模型
还是一个经典的622 其中给cv一点坏数据 给test一点坏数据 让他们去判断
为什么训练模型的时候只能用好的数据呢?
因为好的数据大伙都在一块儿 坏的数据各有各的坏法
-
第二步 那就是要开始评估模型了
由于异常这个东西肯定不是常见的 所以我们的数据集肯定是一个偏斜类
还记得之前讲偏斜类的时候我们如何评估一个模型的好坏的吗? 精确度和召回率 以及F值
-
第三步 调整
ε
如何调整? 不同的
ε
对应着不同的结果 对应着不同的F值 找到最大F值对应的那个ε
即可
异常检测 v.s. 监督学习
这个异常检测是你知道一些数据是错误的然后喂给模型
这个监督学习也是你知道一些数据是1然后喂给模型
那这两个有什么不同呢?
-
样本量不同
异常检测异常检测 肯定是有了异常才会需要检测 而异常异常 他的名字就注定了他不常见 会是样本量很少的情况 而监督学习只是标记不同 并不存在谁多谁少的问题 所以监督学习的正样本数量不会少
e.g. 航空发动机出故障 -> 异常检测 垃圾邮件 -> 监督学习
-
正样本特征方面
对于异常检测的异常样本记住一句话: 合格的样本千篇一律,异常的样本各有各的错法 所以对于那些异常来说 决定他们是异常的特征可能千奇百怪 发动机坏了可能有各方各面坏掉的原因 螺丝掉了里面裂开了等等等等
而监督学习的正样本 一般情况下都是由于存在固定的内部联系 他们才会成为正样本的 比如垃圾邮件 他们之间肯定都有些恶意推销 骚扰违法之类的东西 才使得他们变成了垃圾邮件
多元高斯分布 / 多元正态分布
目前的算法看起来好像还不错 但存在着一个致命的问题
让我们看左图的那个绿色异常 他的两个特征x1,x2 在右边的两个高斯分布图中看起来好像都没有什么问题 但是他们俩放到一起显然就出问题了
举个例子 你喝酒没问题 你开车没问题 你又喝酒又开车那问题可大了去了
换句话说 我们现在的异常检测算法认为 下图中紫色圈圈里面的都没问题 然而事实上只有蓝色圈才是对的
这个其实是我们用的高斯分布的锅 我们用的只是一维的 太简单了 针对目前这个问题 我们肯定要想着怎么运用多元高斯分布 / 多元正态分布
这个具体公式太复杂了 用到的时候查一下就行
其中 μ
和Σ
用下面的式子来得到:
下面是这个公式能做到的效果
多元正态分布改进异常检测算法
跟原来差不多 主要就是公式变了
原异常检测 v.s. 多元高斯分布异常检测
多元肯定效果更好更精准一些 但是多元有几个致命的问题:
- 如果特征数量较大 那么多元高斯分布中协方差矩阵
Σ
将会very的大 所以这个时候没法用多元 - 只有当样本的数量大于特征数量(m>n)的时候才可以用多元 而且最好是十倍以上 不然效果会比较差
另外 如果你在用多元高斯分布的时候发现协方差矩阵Σ
不可逆 一般来说要么就是你的m小于了n 也就是你数据太少特征太多 要么就是你这里面存在冗余的特征 俩特征在矩阵上线性相关的那种 也会导致协方差矩阵不可逆
推荐系统
这个现在太常见了 每个购物网站视频网站最能吸引用户的方法就是这个推荐系统 所谓个性化推送就是如此
用电影评分举例
- 注意这里面
r(i,j)
和y^(i,j)
的含义 跟平时反过来 意味着j有没有评价i
我们现在的目的是要做到 我们根据用户已经评价过的那些电影 来推断出某一部电影 这个用户会打多少分
图片中的那个 θ^T * x
就是能求出来我们所需要的评分
θ
向量是啥? 是这个用户 各个类别电影的喜爱程度
x
是啥? 是这个电影 各个类别元素的占比程度 (比如一个电影有10%的爱情元素 90%的动作元素 懂的都懂)
(不过目前这个θ和x怎么来的暂且按下不表)
所以这个公式还是很显然的
用户口味+电影成分 推测 用户评分
思路还是好想的 直接用口味与这部电影的成分做内积 然后跟实际的评分作差平方 就得到了我们的代价函数
由于里面的m
是个常数 所以可以约掉
这求出来的是一个用户的情况 如果是多个用户那就是下图中的公式二
然后梯度下降慢慢训练就完事了:
用户口味 + 用户评分 推测 电影成分
看到上面的表格 我们会注意到一个非常有意思的东西: x
在上面表格中 他代表着一个电影占了多少的爱情 占了多少的动作
这个就像tag一样 肯定每个电影都有 你是动作电影 他是科幻电影
但是像x这种细致入微的 精确到占比的数据 我们又该怎么获得呢?
这个时候 如果我们有用户的类型喜爱程度向量θ 还有最终他们的评分
我们就可以用θ' * x = r
这个公式来倒着推出来x
协同过滤
当你有了评分和成分 你就要去推测用户口味
当你有了评分和口味之后 你又能去推测电影成分
这是一个互相不断优化的过程 也就是协同过滤
那如果我们把它们合起来呢 ? 有没有一种算法可以不这样迭代 而是一次性求出结果呢??
Look this.
第三个公式 就是我们所需要的东西
换成向量形式肯定会更好一些
实际应用: 有了每一个电影的x值 也就是每个电影的特征向量 那么我们就可以定义两个电影之间的"相似" 接下来就可以有目的的给用户推荐符合口味的相似的电影了
均值归一化
这个均值归一化听起来很nb 但其实还是很简单的做法 不过他的确能帮助我们省一些事情
问题背景:
如果有一个新用户进入了网站 他对所有的电影都还没打过分 我们也都还不知道他的口味是什么 也就是他的打分那里全都是问号 这时候我们该如何他会打多少分?
其实第一个想到的就是把这个电影的平均值赋给他 ok 这个想法当然没有任何问题
但其实这个地方的问题在于 每个人肯定不会每一部电影都看过 难道我们要一个个遍历然后找到那些没评过分的问号地方 然后一个个赋值平均值吗?
所以这里的均值归一化其实是解决了这个问题
就和之前高斯回归的时候我们把所有的点都减去了平均值一样 先把所有评分减去平均值 在之后要协同过滤的时候就再把平均值加上来 这样就自动的每个点都起始有了个平均值作为基础 不用一个个去赋值了 (emmm 其实听起来有点没啥含金量 不过也还是很不错的一个小细节)
大规模机器学习
- 如果我们有一个一个亿大小的数据集 我们不妨先取出其中1000个进行训练 然后观察学习曲线
左边情况是高方差情况 这种情况下继续加大样本量是有作用
如果是右边情况那就是高偏差情况 这个时候想要优化就不应该提高样本量 而是应该添加特征了
在处理大数据中 有一些思想和算法也是很好的
随机梯度下降
还记得线性回归里面的梯度下降吗
这个是原来的梯度下降 也可以叫做簇梯度下降 批量梯度下降 以为是同时批量更新θ(下图左边)
而我们马上要提的随机梯度下降将在处理大数据时会更高效
-
第一步 随机打乱所有数据
-
第二步 也还是repeat 但相比之前那个算法repeat的内容 随机梯度下降将求和式子改成了for循环 每一个for循环里面的那个参数1/m也去掉了
这样做的好处就是 我们没有了求和号 也就没有了非得全部遍历一下的要求 不然如果有一亿个数据 我们还得先把一亿个数据全部遍历
注意图像紫色路径 随机梯度下降就是这样的一个随机法子 虽然没之前走的那么优美 但每走一步不但要快得多 而且大体上依旧是朝着最小值那个点去的 所以不影响使用
这个思想还是有意思的 用下降走过的步长去交换代码的时间和空间
现在问题来了 我们怎么得到一个好的α值呢?
下图中的公式一使我们之前的公式 但是我们注意到这里面有一个求和符号 所以肯定也不适用于大数据情况
那么我们可以参照随机梯度下降的思路 将每一个参数的代价函数转换为公式二
画出cost函数 由于我们没有精确求解 所以图像会有很多的噪声
图三你调整多少个点平均取一个数的话 是有可能得到一条缓慢下降的曲线 这说明其实算法问题不大 只是下降的缓慢了一些
而图四这种上升的就意味着你必须要降低你的α值了 不然就会发散
你也可以加一些小优化: 比如让α随时间慢慢减小 这样就不怕越来越大了
也是一个不错的优化思路
Mini-Batch梯度下降
其实这个就是分成一小块一小块
这是个介于随机梯度下降和批量梯度下降之间的算法
在线学习
问题背景:
用户输入流不断 源源不断的有新的用户涌入 他们会下达很多很多指令
在这里用快递举例 用户到了驿站 要寄给目的地一个快递 我们作为物流公司 该如何收取用户的快递费 让用户既感觉不是很贵下次还来 我们又能赚到票子呢?
这个时候就可以用到在线学习算法
-
什么是在线算法? 之前的所有算法都是你有了一个固定的数据集 单后通过这个固定的数据集去训练你的模型
而在线算法非常适用于一些用户量非常大的网站 而在线算法就可以实现实时训练模型 进来一个数据就出来一个结果
-
具体来说跟之前logistic回归没有什么大区别 进来一个用户数据 就用用户的(x,y) 更新θ 还是那个公式
主要就是取消了(i)上标 因为在这里我们不需要矩阵来存储一个个的用户 而是进来一个用户训练好之后就丢一个
也许快递收费这个问题有丶偏理我们的日常 但是接下来的这个例子将会无比贴合
这就是商品推荐算法中的 点击率预测算法(predicted CTR(Clicked-through Rate)) 淘宝B站抖音 每天都用这个赖以生存
-
实现想法就是:
先给用户推送搜索的相关联的东西 然后根据列出来的商品 用户点击的不同情况 来进一步运算出这个用户的口味情况 进而再推荐出更加符合用户需求的商品
MapReduce
本质和思想非常简单 就是把一个数据集分给多台电脑来做 然后每个电脑算出来的结果汇总给主程序
或者同一台电脑 把数据拆开来交给多个处理器运行也是可以的
其实就是分布式计算的思想 在现在已经非常的常见
不过这里要注意 你分开算的东西 最后是要真的可以加在一起
如果主程序不是这种求和的公式 那你要想想是不是真的可以分开算了
实例: 照片OCR
OCR 光学字符识别 Optical Character Recognition
基本步骤:
- 第一步 文字定位 找到照片中文字部位
- 第二步 文字分离并拆分 把找出来的文字部分提取出来并且拆成单个字符
- 第三步 单个字符识别 把分开来的单个字符一个个进行识别
第一步 文字检测定位
其实就是像行人检测一样
对于行人检测来说
我们用的是滑动窗口法 因为要检测人类 所以框框的长宽都差不多 定下来长宽之后 我们从一个比较小的矩形就开始遍历
遍历从左往右从上往下进行窗口的滑动 步长一般是4-8像素
然后一个大小的处理完之后 就把窗口变大一些 继续遍历
然后 不同大小的框遍历完之后 我们就有了几个留下来的候选框
好了 有了这个思路铺垫 我们来看怎么检测文字
首先我们也是要塞给模型一些正数据和负数据
接着我们滑动窗口遍历这张照片 找寻哪些地方是文字
-
上图 左下角 就是滑动窗口遍历出来的 识别出字母的地方 用亮白色格子表示
由于这些格子有很多是识别错误的 所以我们进行两步操作 把真正的文字部分识别开来
- 第一步 放大像素 把亮白色部分全部放大 便于识别
- 第二步 把细长矩形部分给取出来 剩下的丢掉 因为这些才可能真的是文本内容 其他的大概率都是误识别的
第二步 文字分割
找到文字中两个字母中间的地方 那就是我们可以分开字母的地方
第三步 文字识别
就是正常的机器学习算法了
如何获得大量数据
自制
字体数据 可以知道对应的结果之后 从字体网站上下载对应的替代字体 生成一个结果一样但是题面不一样的图像
也可以自己扭曲或者添加噪声
声音数据 可以在原来的数据中添加不同的噪音 以此生成新的数据
收集
- 小组讨论头脑风暴如何收集 自己收集 或者"众包"交给别人
总结
Octave有关操作
基础表示
-
==
判断等于~=
判断不等 -
&&
和AND||
或OR -
disp()
将括号内的东西显示出来 -
sprintf()
可以在括号内实现类似c语言的printf格式语法 只是这个命令是返回一个字符串 没有自动显示的功能 因此一般都和上面的disp()
搭配使用 即disp(sprintf("...",...))
-
矩阵
[xx,xx;xx,xx]
分号表示换行 -
start : step : end
即起始值:步长:终点值
这将会连续创造一堆值 从起点值开始按照步长一直增加到终点值e.g.
v=1:0.2:2
此时v=1 1.2 1.4 1.6 1.8 2
-
矩阵
-
eye(n)
为n阶单位矩阵ones(a,b)
为大小a*b的全1矩阵(就是全是1)zeros(a,b)
就是全是0 -
rand(a,b)
为a*b矩阵 但里面的元素全是随机小数(0-1) -
size(A)
给出A 的大小 会给出多少行多少列 -
v=A(start:end)
将矩阵A中第start到第end的内容放在v里面 -
A(m,n)
即提取出第m行第n列的元素 (相当于C++中二维数组的A[m][n]):
表示全部这个不仅仅是提取 更可以理解成一个c++中的引用 因为还可以赋值
-
复杂索引
A([1 3],:)
前面的[1,3]
表示第一行和第三行 -
添加列
A = [A,[COLUMN VECTOR]]
在A的右边添加一列向量这个其实在暗示我们 可以用
[A,B]
/[A B]
这样的式子把AB两个矩阵结合在一块儿 A左B右那么如果是
[A;B]
那就是A上B下 因为分号是换行 -
A(:)
特殊句法 意思是将A中所有的元素变成一条列向量
-
-
help xxx
关于xxx的帮助文档 -
who
列出目前Octave中的所有数据whos
会列出的更加详细clear xxx
会将名称为xxx的变量给删除clear
单个的clear就是删除全部变量 -
文件
-
load name
/load('name')
将名字为name的文件数据给读取 -
save name xxx
将变量xxx给保存为名称为name的文件 但这样存储的是二进制形式save name xxx -ascii
这样也是保存 但是形式是ASCII码 这样就能看懂了 -
cd 'path'
将文件存储路径变更为path (很常用)
-
运算
-
矩阵
-
A * B
矩阵相乘 这个很简单 但是...A .* B
这个.*
的意思是把A和B中对应位置的每个元素进行两两相乘这也暗示我们 这个点
.
一般会用于元素和元素之间的运算以此类推
A .^2
就是把A中每个元素进行平方1 ./ A
就是每个元素进行倒数如果是
log()
,exp()
,abs()
这些函数 就不用加点了log(A)
就是把A中每个元素取log -
A + B
顾名思义是两矩阵相加 但是...A + 1
这个就是A中每个元素都+1 很方便 -
max(v)
min(v)
向量v中的最大最小元素在获取的时候可以这么获取:
[val,ind]=max(v)
这个时候就会返回最大值的val以及最大值的下标索引ind very的高级
要注意这里是向量而不是矩阵! 如果用矩阵的话 会输出每一列的最大值
-
v < 3
将向量v中每个元素与3比较 返回结果 -
magic(n)
生成一个n*n的幻方 每行每列每对角线加起来相同没啥用 但是你可以快速的得到一个矩阵
-
sum(v)
prod(v)
就是所有元素求和求积 -
floor(i)
向下取整ceil(i)
向上取整 -
之前说过
rand(n)
是快速得到n*n的随机小数矩阵有一个有趣的做法:
max(rand(n),rand(n))
两个随机矩阵中对应位置较大的那个这样得到的矩阵每个位置都会普遍较大一些
-
容易看出 1表示一列一列看 2表示一行一行看
-
作图
-
plot(a,b,color)
以a为横轴 b为纵轴 画出函数b(a) 用颜色是color的线 -
hold on
此语句的意思是 保留刚刚的作图 直接在上面叠加显示要画的新函数 -
xlabel()
ylabel()
就是给横纵坐标加上标签legend()
在右上方加上图例title()
给整张图加上标题 -
print -format 'name.png'
将图片以format形式保存为名为name的文件 -
subslot()
将画纸分出几个小格子 不同的格子画上不同的函数 -
axis([x1,x2,y1,y2])
将x轴范围设置为[x1,x2], y轴范围设置为[y1,y2] -
clf
清除画布 -
imagesc(A)
将矩阵表示成颜色块画出来imagesc(A),colorbar, colormap gray
第一个画矩阵 第二个表示显示颜色对应的数值条 第三个表示用灰色来画矩阵
控制语句
-
for i=1:10,v(i)=2^i
就是一个简单的for循环语句 -
简单的while代码例:
里面的
end
一定不要忘记 起到类似右半边括号的作用 表示结束 if和while都需要它 -
elseif()
-
有一个很nb的操作
你可以把你这个函数的内容放进记事本里 然后让Octave读取这个文件 这样你就可以灵活应用你构造的函数了
注意看代码 使用的时候要更改一下路径 这样Octave才能找到你放函数的文件在哪里
但前提是: 文件名和函数名一致 毕竟他不会每个文件都翻一遍
当然 这里用了
cd 'path'
这样子做是更改搜索路径更好的做法是添加搜索路径 这样原来的路径就不会被你篡改
代码:
addpath('path')
这就比cd方便实用的多 -
Octave的函数可以返回多个值(以矩阵的形式) 因此可以实现这样的功能:
-
前置知识拿下 现在我们可以实现代价函数了
然后接着尝试实现线性回归梯度下降