今天是国庆节,首先祝福一下我们伟大的祖国,也希望自己以后在这么好的大环境下有好的发展。
在世界范围的机器学习的热度图来看,中国对机器学习热度完全不逊于美国,可能还略胜一筹。有这么好大环境,而且人工智能是大势所趋,所以静下来心来学习学习机器学习。
学习方法
我们面对一些比较难,当然并不是难离谱的知识,学起来很容就会放弃。我想机器学习对多数数学基础相对薄弱的人,很容易就会放弃。因为机器学习既有深度又有广度。不过这里给出一点个人学习经验,首先需要有兴趣,有了兴趣才能够坚持,然后我们要多看,第一遍看不懂没啥再看一遍再听一遍,看的多了自然也就是感觉,有了感觉我们自己深入弄懂这些知识点,最后就是要多鼓励自己。
目标
通过一起学习,从小白成长为可以运用神经网络自己做一些项目。深度神经网发展今天,有许多框架(例如 TensorFlow mxnet 和 keras )供我们选择使用,机器学习也逐步得到应用和普及。真真切切走到我们生活中,我们也能亲身体会到深度学习给我们带来的便利。
准备
需要了解一些线性代数和高等数学知识,学过就行这样应该捡起来不难,不用系统学习,现用现学吧。不会涉及到特别高深难懂知识,所以有点心理准备。
主流框架
有了这些框架我们就可以轻松地搭建出模型,也就是设计出神经网络。我们就像玩乐高一样随意选择和组合搭建出神经网络,通过来调试各种参数,调整隐藏层顺序和组合来得到较高精准率,当然学习好基础知识和了解其实现的背后原理是必要,这样我们才可以轻松调整参数来得的预期的模型。
- TensorFlow
- MXNet
- Keras
在分享中实例主要是以上 3 三种来演示,不过大家知道除此之外当下流行框架还很多,例如Caffe、Torch 等。
神经元结构
神经元组成
- 细胞核
- 树突
- 细胞体
- 轴突
-
神经末梢
树突接收其他神经元发送信息,处理后通过神经末梢将信息传递给其他神经元。神经元网络就是受到生物神经网络的启迪而来。接下来我们将这个神经元抽闲出来深度网络的最基本单元。
这么简单的结构可能用处不大,但是大数力量让我们看到神经网络的威力,这是这样上亿上千万的简单结构组合在一起让神经网络今天大放异彩。
输入层(input layer)为数据输入,输出层(output layer)
在开始之前我们整理意思路,无论是线性回归问题还是深度神经网络中,都会一个一般步骤来解决问题。这里我把这些一般步骤列出来后,以后会根据这些步骤中可能运用技术进行简单的讲解来帮助大家对机器学习整个流程有所了解。
- 定义模型,也就是根据问题给出一个函数集合,我们这组函数是什么样,复杂些可以理解为我们神经网络结构
- 进行训练通过损失函数来评估模型
- 根据评估,选择优化算法来更新我们模型参数
基本术语(基本技术)
欠拟合和过拟合
我们通过训练的模型,然后通过对比模型得到预期值和实际值之间差距大小来评估模型好坏,那么为什么�会有误差呢?误差是从哪里来,只有搞懂这些才能够帮助我们调整参数来得到好的模型。
假设期望值(也就是实际值)我们用 表示真实函数(也就是数据背后规律),我们需要通过训练来的得到一个比较接近 ,我们用 来表示。
概率与统计
我们知道 是所有数据的平均值,而我们计算其中样本数据,也就是全体数据一部分的数据是
我们数据是否分散是和我们训练数据集大小有关的。这个很好理解数据量越大我们样本的并均值 m 就越接近真实均值
是估计值会散步 周围
通过图我们不难看出误差来源两件事,第一个就是我们估测值点中心值是否偏离靶心(Bias),而第二个是我们这些估计值分散程度(Variance),也就是训练稳定性。
偏差(Bias)
可能会造成欠拟合,即使用大量数据进行训练也无法消除 和期望值 之间差距。所以比较简单的模型会有比较大的偏差值(Bias)。
我们的模型就是函数集合,如果是比较简单模型的这个函数集合是比较紧凑覆盖范围比较小,不过复杂的模型,这个函数集合会覆盖比较大的范围。
- 简单模型会有比较大偏差但是较好稳定性
- 复杂模型而与简单模型正好相反
那么今天如果你的误差来自于偏差(bias)那么就是欠拟合,如果你的误差来自于稳定性就来自于过拟合。
只有了解我们模型错误来源才能够更好地解决问题。那么怎么判断是我们误差是由什么原因导致的呢?
- 如果你的模型无法拟合你的训练集,你就需要考虑是是否是欠拟合,对于这种情况就需要我们重新设计模型,添加更多特征或者增加模型的复杂度来解决欠拟合问题。
- 如果你的模型在训练集上表现不错,而在测试集表现和训练集上表现有差距就需要考虑过拟合的原因,如果是过拟合我们就需要通过以下手段来解决过拟合问题
- 增加数据,其实增加数据是最好也是最有效方法,同时也是最难的方法,在图像处理相关的神经网络我们是有一些技巧来增加数据。
- 正则化(后面会细讲)
损失函数(代价函数) —整理中
回归和分类问题是经典有监督学习(有关有监督学习大家可以理解为有训练数据,也就是我们通过训练集数据告诉机器怎么情况下应该怎么做以及什么是好的)。我们现在接触到模型都是一种预测模型,预测是那种分类以及预测一个数字。无论什么模型我们都将估计值和实际值(以后文章也叫期望值)进行比较来得到一个误差值。我们在整个训练过程中最重要的就是通过调整参数来减少这个误差。损失函数就是定义我们如何计算估计值和实际值之间差距。
对于损失函数,这里并没有通用方法,会根据实际情况来采用不同损失函数。
有关信息熵概念
我们都知道在计算机里发送信息是字节为单位,字节等于 0 或者 1,但是我们发送信息并不一定都是有用信息,当然我们希望接收到有用信息而非无用信息。在信息论中当我们传递一个字节的信息能够减少接收器两倍的不确定性。
世界杯,比方说有两支球队比赛,根据概率论每个球队赢得概率都是一样,事实上不是这样的,假设都是 50% 的概率。但是预测系统告知某一队将赢得比赛,那么我们的不确定性将减少 2 倍,
如果是 8 只球队呢?我们的不确定性除以 8 也就是 ,如果我们预测某一队将赢得比赛那么通过公式进行计算也就是 ,具有三个字节的信息,这里大家发挥想象,那么不确定性就会减少倍数(8)
如果一只队伍赢得比赛概率为 25% 而另一只为 75% 如果概率低
这就是信息熵的概念,
交叉熵的公式如下
H(p,q) =
概率
梯度下降
最小化
初始化
不断更新
- 红色区域表示损失函数比较大
- 蓝色区域表示损失函数比较小
不断迭代过程就是求导,求导后就会找到一个方向,朝着梯度下降方向移动来更新
问题根据选取初始值可能会走到局部极小值而不是全局最小值。
- 表示学习率
- 表示初始值参数
- 表示损失函数 L对于参数 w 取导数
当切线为负,我们就增加 w 值像右移动 是学习速度,也就是移动步长。知道走到微分为 0 时候我们停止前进,所以我们就会思考梯度下降不一定会知道最小值。
上面考虑是一个参数情况,接下来我们来考虑 2 参数
这个线就是等高线的法线方向,然后更新参数,每一次根据偏微分来移动点来获取下一次参数 w 和 b
总结
我们关注可以调整参数这里就是学习率
- 梯度下降是同步更新
- 学习率不能太小也能太大
- 学习率太大,每一步太长就会出现在函数谷底附近进行震荡而无法达到最小值
- 代价函数式突函数,非凸函数有许多局部极小值
反向转播
这是一个比较难于理解的部分,不过如果你不理解似乎也不会影响你使用机器学习,不过只有很好理解什么是反向传播才能够真正理解在模型中是如何优化参数的。
坐下来想一想,我们通过损失函数可以查看出估计值与期望值之间差距来评估当前参数好坏。知道了我们参数(也就是函数)好坏就是为了优化函数,我们优化参数具体工作就是更新参数。更新那些参数以及更新幅度是如何操作的呢。
我们需要通过损失函数对每一个参数的导数,简单有效方法就是反向求导。
求导链式法则
我们先回忆一下什么是链式求导,这是在大学学过的知识。
- 第一种情况
当 x 发生变化就会影响到 y ,那么当 y 发生变化就会影响 z,那么也就是 x 间接通过 y 来影响 z。
- 第二种情况
这里我们看到当 s 发生变化就会影响到 x 和 y,而 x 和 y 发生变化就影响 z ,也可以这样看 s 通过 x 和 y 两条路径可以到达 z。
我们对 求导也就是 是每一
在图中当我们对 z 对 进行偏微分得到值就是 z 输入 的值。
正向传播是分简单的,我们接下来反向传播就比较麻烦
这里选择不同的微分就不一样。
反向传播算法的思路
- 先计算向前传播
所以这里应用第二种情况,这个也比较好理解,我们 a 到 l 有两条途径,我们分别对其进行取导数后去和。
如果我们方向思考认为 和 都是已知的反向取导数
好有了上面的灵感,我们就可以从输出来反向一步一步进行推导出
在中间过程也就是隐藏尝试
欠拟合(Underfitting)
也就是函数的复杂度不够无法
过拟合(Overfitting)
在训练集表示很好,因为参数过多所以函数精确拟合到训练集每一个数据,也就是函数记住了每一个数据,也就是对于简单问题我们选择复制模型。
正则化
所谓正则化就是为了解决过拟合的问题,我们除了让误差取决于之前估计值和期望值之间的差,还取决于参数和的大小,有两种形式。
我们知道在函数
上面方程来看高阶的 对输出值影响最大,所以值对输出值影响比较大。通过正则化我们削减了 对函数影响力。这里添加 就是为让我们认为可以参与到其中对其进行调整和控制。
总结
- 防止过拟合
- 减少特征
- 增加数据量,增加数据量也就是用更复杂的数据来匹配复杂模型
- 正则化(Regularized)
- dropout(随后深入讲解)
激励函数
回归问题
线性回归可以应用在股票预测,房价预测,无人车驾驶、还就就是推荐系统,淘宝或者京东,用户A购买商品 B 可能性也是。李宏毅老师是以预测宝可梦进化为例来讲的线性回归,所谓线性回归就是可以暂时理解根据以往经验(数据)总结出规律来预测未来。
再简化一些问题,其实
抛出问题
今天我们要解决一个问题就是通过训练集数据来找到一个函数(上面函数),其实这个应该对于我们在简单不过了,学多人在小学时候就会通过带入数据来找到 x 权重 3。正式因为这是简单问题,我们自然就会解决而忽略解决问题过程。今天的神经网络也就是模拟我们人类是如何学习的。
当然我们也会遇到相对更复杂问题,
今天我们是通过学习来找到这么方程然后,用方程来预测一些值,那么我们如何通过学习来找到这个函数呢。这也就是今天我们今天主要要学习的。
1.定义模型
什么是模型,所谓模型我们认为是一组函数的集合,所谓函数集也称函数族,并不是形状各异天马行空的函数,而是设计出具有一定样式,只是参数 w 和 b 不同而已,我们要做工作就是在函数集中找出最优函数(最好的 w 和 b)来拟合我们真实函数。所以第一步定义模型就是定义函数大概什么样式,几元几次函数呀。线性问题我们通常会定义为如下形式
也可以表示为这种形式
在这个函数的参数 w 和 b 可能是任意数值,理论上根据 w 和 b 不同,函数数量是无穷。
不过为了简化问题我们也会将 w 和 b 限定在一定范围来减少运算。
这是一个线性模型,我们需要进行初步筛选,为什么说这是线性函数,这是因为函数可以写出这种形式
所以就是线性函数。这里 w 叫做权重 b 偏移值。
我们需要使用上标表示一个类,用下标表示该类的某个属性。
评估
评估我们函数好坏,这里定义一个损失函数(用L表示),损失函数是接受我们一个模型一个函数作为参数,那么学过函数编程都知道这就是所谓高阶函数,我们f(x) 是 L的参数。这个函数输出就是通过数值表示这个函数有多好,和多不好。
所以引入损失函数(Loss Function) 也有人使用 Cost Function ,损失函数也会根据具体情况而不同
因为我们这里 f(x) 是 b 和 w 决定的,所以我们就可以用 L(b,w)
- 是真实值,我们现在函数计算出值与真实值做差后平方,来计算每一个样本的差值,然后在取平方后求和。
其实我们要弄清两个问题
- 第一个问题是优化谁:我们需要不但更新权重 w 和偏差值 b 来找最优函数
- 第二个问题是怎么优化:通过缩小预期值和真实值的误差来优化。
优化
这就是一个如何找到最优解的方程,我们知道求解这个方程就可以得到我们想要最优函数,也就是我们想要的 w 和 b。我们可以根据大学学过知识来解这个问题。
梯度下降(大名鼎鼎)
梯度下降是用于处理一切可微分的方程,我们需要在曲线上找到最低点,也就是导数为 0 位置,不过导数为 0 位置不一定是最低点,随后案例我们就会遇到这种情况。
- 随机找到一位置
我们在曲线随意找一点作为起始点,然后在这点进行求导得到关于曲线这一点的切线。
当切线为负,我们就增加 w 值像右移动 是学习速度,也就是移动步长。知道走到微分为 0 时候我们停止前进,所以我们就会思考梯度下降不一定会知道最小值。
上面考虑是一个参数情况,接下来我们来考虑 2 参数
这个线就是等高线的法线方向,然后更新参数,每一次根据偏微分来移动点来获取下一次参数 w 和 b
问题点
$$
L(w,b) = \sum_{n=1}^{10}( \hat{y}^n - (b + w \cdot x_in))2
$$
线性回归实例
来一个线性回归问题实例,在开始之前我们先明确一些问题,也就是我们要训练模型几个步骤,在李宏毅教授的课程中他提到会分为建立模型(也就是函数集合),然后就是定义损失函数,最后根据损失函数进行优化找到最优的函数。
不过我们对于简单的神经网,个人总结了一点经验,首先是准备好训练和测试数据集,然后处理这些数据以便训练,然后就是定义输入变量,因为都是矩阵和向量乘法,在设计神经网网络时候我们需要考虑结构以及矩阵的维度和矩阵元素类型,这样运算可以顺利进行。然后就是定义损失函数,接下来是定义优化找到让损失函数最小的那个方程也就是我们最优解,然后用最优解方程来测试我们测试数据,来评估我们模型精确度。
准备数据
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
learning_rate = 0.01
epochs = 200
n_samples = 30
train_x = np.linspace(0,20,n_samples)
train_y = 3 * train_x + 4* np.random.rand(n_samples)
plt.plot(train_x,train_y,'o')
plt.show()
plt.plot(train_x,train_y,'o')
plt.plot(train_x, 3 * train_x)
plt.show()
创建变量
X = tf.placeholder(tf.float32)
Y = tf.placeholder(tf.float32)
W = tf.Variable(np.random.randn,name='weights')
B = tf.Variable(np.random.randn,name='bias')
定义计算图
定义损失函数
然后定义评估函数也就是我们提到损失函数
cost = tf.reduce_sum((pred - Y) ** 2) / (2 * n_samples)
用计算出估计值(pred) 减去实际值,也就是估计值到真实值的距离然后平方来去掉负数后取均值。
优化
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)
我们优化就是需要让损失函数最小,所以最小化 cost 函数的参数就是最好的参数
训练
init = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init)
for epoch in range(epochs):
for x, y in zip(train_x,train_y):
sess.run(optimizer,feed_dict={X:x,Y:y})
if not epoch % 20:
c = sess.run(cost,feed_dict={X:train_x,Y:train_y})
w = sess.run(W)
b = sess.run(B)
print(f'epoch:{epoch:04d} c={c:.4f} w={w:.4f} b={b:.4f}')
设计好我们计算图后,就可以开始进行计算了,我们所有计算图都需要放到
验证结果
weight = sess.run(W)
bias = sess.run(B)
plt.plot(train_x,train_y,'o')
plt.plot(train_x,weight * train_x + bias)
plt.show()
epoch:0000 c=78.2851 w=1.8528 b=1.4544
epoch:0020 c=7.5476 w=2.8999 b=1.4118
epoch:0040 c=7.4682 w=2.9079 b=1.2879
epoch:0060 c=7.3967 w=2.9155 b=1.1703
epoch:0080 c=7.3324 w=2.9226 b=1.0587
epoch:0100 c=7.2745 w=2.9295 b=0.9528
epoch:0120 c=7.2224 w=2.9359 b=0.8522
epoch:0140 c=7.1755 w=2.9421 b=0.7568
epoch:0160 c=7.1334 w=2.9479 b=0.6662
epoch:0180 c=7.0954 w=2.9535 b=0.5802