不用框架入门与进阶深度学习(3)−线性单元、梯度下降与回归任务

本文主要是介绍不用框架入门与进阶深度学习(3)−线性单元、梯度下降与回归任务,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自公众号-AI圈终身学习。
一个人是孤独的,一群人是强大的,欢迎关注。


写在前面

前文我们介绍了简单有效的感知器,用它实现了一个线性分类器,并在鸢尾花数据集上取得不错的效果。您肯定懂得AI领域做分类任务的基本原理了。

如果您觉得迷迷糊糊也很正常,因为我们没有讲感知器的更新规则算法-梯度下降。

如果单单讲个梯度下降,您可能会看睡着。所以,我们一起来动手做一个AI领域的基础任务-回归任务。相信学完本文您就能懂线性单元和AI领域回归任务的基本原理了。

本文在整个文集中是承上启下的核心章节,会讲解目标函数、优化算法等。他们组成了通吃整个机器学习的基本套路,非常重要。希望您能坚持下来。

一、回归任务

考虑这样一个简单问题,假如我有几个朋友在互联网公司做AI工程师,他们的工龄与月薪关系如下:

朋友工龄月薪
A555000
B323000
C876000
D1.418000
E10.1114000

我们画个图看看:

横坐标为工龄,纵坐标为月薪,我们可以看到有非常明显的线性增长关系。这也比较符合直觉,因为我们都知道一般来讲,工龄越长,工资越高,俗称资深互联网老兵。因此我们现在的问题是:

如何根据这些数据,判断一个工龄为9年的AI工程师,月薪是多少呢?

当然现实中影响因素会更复杂,可能有学历、公司、职级等因素影响。当然如果我们有这些数据,预测的结果会更加精准,但是现在我们可以先这样简化一下。这种输入x,预测一个不确定值y,就是回归问题。

那么问题来了,因为我们上文提到的感知器始终只能输出两个值,0或者1。所以这种问题怎么办呢?这个时候我们把感知器的激活函数修改成线性函数即可,这就是线性单元。

二、线性单元

线性单元结构和感知器完全一样,唯一不同的地方只是修改了激活函数部分。

我们回忆一下用以分类任务的感知器的激活函数是阶跃函数:

我们把它改成线性函数即为线性单元:

(2) f ( x ) = x f(x) = x \tag{2} f(x)=x(2)

这样我们的输出就不局限在0,1,而是一个实数了。接下来我们看看,输入五位朋友的工龄和薪水,模型是如何学习权重的。

三、线性单元的正向传播

我们已经知道,感知器和线性单元的输出结果都可以用这个公式表示:
(2) y ^ = f ( w ⋅ x + b ) \widehat{y} = f(w\cdot x+b) \tag{2} y =f(wx+b)(2)

本任务中共有5条数据,但是只有一个特征-工龄,权重个数与特征数量相等,所以我们只有一个权重和一个偏置项 w 1 、 b w_1、b w1b,因此线性单元输出为:

(3) y ^ = f ( w ⋅ x + b ) = w 1 ⋅ x 1 + b \begin{aligned} \widehat{y}&=f(w\cdot x+b) \\ &=w_1\cdot x_1+b \end{aligned} \tag{3} y =f(wx+b)=w1x1+b(3)

我们不可能直接得到最优的 w 1 和 偏 置 项 b w_1和偏置项b w1b,一般我们的权重会初始化为0。但是此时预测的工资 y ^ \widehat{y} y 为0,与真实的工资y相差甚远。因此现在我们最关心的问题是,如何取到合适的 w 1 和 b w_1和b w1b值?

四、线性单元的权重更新

现在我们已经有了初始的输出值 y ^ \widehat{y} y 以及真实的实际值 y y y,人们常用下面的公式表示 y ^ 与 y \widehat{y}与y y y的接近度:
(4) e = 1 2 ( y − y ^ ) 2 = 1 2 ( y − w 1 ⋅ x 1 − b ) 2 \begin{aligned} e&=\cfrac{1}{2}(y-\widehat{y})^2 \\ &=\cfrac{1}{2}(y-w_1\cdot x_1-b)^2 \end{aligned} \tag{4} e=21(yy )2=21(yw1x1b)2(4)

e e e是5条数据中1条样本的误差,我们叫做单个样本的误差。其中 x 1 为 工 龄 , y 为 月 薪 x_1为工龄,y为月薪 x1y,是已知值,所以我们只需要学习到 w 1 和 b w_1和b w1b使得预测的工资与真实工资尽可能接近。也就是使得 e = 1 2 ( y − y ^ ) 2 e=\cfrac{1}{2}(y-\widehat{y})^2 e=21(yy )2尽可能小,那么就达到我们的目的了。

所以现在我们的问题变成了求解这个方程的最小值,这就需要用到梯度下降优化算法。

4.1 梯度下降算法

我们在大一的时候学过函数 y = f ( x ) y=f(x) y=f(x)的极值点就是它的导数 f ′ ( x ) = 0 f'(x)=0 f(x)=0的点。因此我们可以通过解方程 f ′ ( x ) = 0 f'(x)=0 f(x)=0得到函数的极值点 ( x , y ^ ) (x, \widehat{y}) (x,y )

同理可以得到方程 e = 1 2 ( y − y ^ ) 2 e=\cfrac{1}{2}(y-\widehat{y})^2 e=21(yy )2的极值点。但是计算机是不会解方程的,怎么办呢?这里就要用到梯度下降了:


如图, 横 坐 标 x 横坐标x x对应于我们任务的 w 1 或 者 b w_1或者b w1b,纵坐标 f ( x ) f(x) f(x)对应我们的 误 差 值 e 误差值e e。我们的初始点是图上的 x 0 x_0 x0,然后每次迭代修改 横 坐 标 x 横坐标x x x 1 , x 2 , x 3 . . x_1,x_2,x_3.. x1,x2,x3..:

x = x 0 → x 1 → x 2 → x 3 → . . . x = x_0\rightarrow x_1 \rightarrow x_2 \rightarrow x3 \rightarrow ... x=x0x1x2x3...

你会发现,修改后的误差值 f ( x ) f(x) f(x)一直在减小。为什么会这样呢?我们先解释下梯度是什么。我们以图中的线 x 2 → x 3 x_2 \rightarrow x3 x2x3为例,梯度是一个向量,它指向函数值上升最快的方向。因此梯度下降就是梯度的反方向,当然就是指向函数值下降最快的方向。我们每次沿着这个方向乘以一个步长去修改 横 坐 标 x 横坐标x x的值,误差值 f ( x ) f(x) f(x)就会减小。

但是聪明的你会发现,这种方法很难走到函数的最小值点。因为我们的步长不可能刚刚好,如果步长过大,就会越过最小值点;如果步长过小,迭代会很慢而且容易陷入局部最小值再也出不来。步长又叫学习率,选择它是个技术活。

其实步长(学习率),我们在上一节的感知器中已经用过,就是更新规则里的 δ \delta δ但是记不起来也没关系。我们现在开始讲解,最后会对比学习率对收敛的影响。

这里我们用公式表示下梯度下降算法,不要怕,很简单的,我们以工龄对应的权重 w 1 w_1 w1为例:

w 1 n e w = w 1 o l d − δ ∇ e ( w 1 ) w_{1new} = w_{1old} - \delta \nabla e(w_1) w1new=w1oldδe(w1)

∇ \nabla 表示梯度, ∇ e ( w 1 ) 表 示 e ( w 1 ) \nabla e(w_1)表示e(w_1) e(w1)e(w1)的梯度,我们直接求导就行了:

KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ \nabla{e(\math…

学过高数的同学应该都能推,很简单。如果看不懂梯度这部分推导也没关系,把最后一个等式记住就行,不影响阅读。所以现在我们得到的工龄特征对应权重 w 1 w_1 w1公式更新公式如下:

w 1 n e w = w 1 o l d + δ ( y − y ^ ) x 1 w_{1new} = w_{1old} + \delta(y-\widehat{y})x_1 w1new=w1old+δ(yy )x1

同理我们可以得到 偏 置 项 b 偏置项b b的更新公式:

b n e w = b o l d + δ ( y − y ^ ) b_{new} = b_{old} + \delta(y-\widehat{y}) bnew=bold+δ(yy )

我们回顾下前两节的感知器更新规则:

Δ w i = δ ( y − y ^ ) x i w i = w i + Δ w i Δ b = δ ( y − y ^ ) b = b + Δ b \begin{aligned} \Delta w_i&=\delta (y- \widehat{y})x_i \\ w_i&=w_i + \Delta w_i\\ \Delta b&=\delta (y- \widehat{y}) \\ b&=b + \Delta b \end{aligned} ΔwiwiΔbb=δ(yy )xi=wi+Δwi=δ(yy )=b+Δb

他们是完全一样的,是不是感觉到满满的成就感。那么我们就来开始手撸代码吧。

五、手写线性单元预测工资

完整代码请参考GitHub: https://github.com/AIGroup-Z/deep-neural-network

5.1 训练数据准备

这里我们直接捏造前文表格中的数据,并捏造一个测试集:

朋友工龄月薪
A555000
B323000
C876000
D1.418000
E10.1114000
def get_training_dataset():'''训练集:捏造5个人的工作年限与对应的收入数据'''# 输入列表,每一项表示工作年限x_train = [[5], [3], [8], [1.4], [10.1]]# 期望的输出列表,月薪,注意要与输入一一对应y_train = [55000, 23000, 76000, 18000, 114000]return x_train, y_traindef get_test_dataset():'''测试集:捏造5个人的工作年限,用模型预测结果'''# 输入列表,每一项表示工作年限x_test = [[1], [2], [4.3], [6.7], [9]]return x_test
5.2 实现线性单元模型

我们总结下感知器和线性模型区别,看看两者哪些地方可以复用:


我们发现,感知器与线性单元除了激活函数完全相同,所以我们直接复用上一节感知器的代码就好了:

class LinearUnit(Perceptron):def __init__(self, input_feature_num, activation=None):self.activation = activation if activation else self.fPerceptron.__init__(self, input_feature_num, self.activation)def f(self, x):return x

真爽。这样我们的线性单元就完全实现了。考虑到有些读者没有连贯读,我这里贴一下上文感知器Perceptron的代码。已经看过的同学跳过就好:

class Perceptron(object):def __init__(self, input_feature_num, activation=None):self.activation = activation if activation else self.signself.w = [0.0] * input_feature_numself.b = 0.0def predict(self, x):'''预测输出函数:y_hat = f(wx + b)'''return self.activation(np.dot(self.w, x) + self.b)def sign(self, z):'''阶跃激活函数:sign(z) = 1 if z > 0sign(z) = 0 if z <= 0'''return int(z>0)def fit(self, x_train, y_train, iteration=10, learning_rate=0.1):# 训练函数for _ in range(iteration):for x, y in zip(x_train, y_train):y_hat = self.predict(x)self._update_weights(x, y, y_hat, learning_rate)print(self)def _update_weights(self, x, y, y_hat, learning_rate):# 权重更新, 对照公式查看delta = y - y_hatself.w = np.add(self.w,np.multiply(learning_rate * delta, x))self.b += learning_rate * deltadef __str__(self):return 'weights: {}\tbias: {}'.format(self.w, self.b)
5.3 模型训练-小试调参

回顾一下模型训练的流程:

准备数据->模型初始化->根据步长学习率更新权重

这里我们先用0.1的学习率看看迭代10轮后的模型:

x_train, y_train = get_training_dataset()
lu = LinearUnit(len(x_train[0]))
lu.fit(x_train, y_train, iteration=10, learning_rate=0.1)

我们看看每次迭代的权重变化:

注意这里的每次迭代结果,是把所有的5条数据都跑了一遍,其实每次迭代会更新5次权重,所以一共更新了50次权重。我们发现差不多在第4次迭代的时候,就收敛了,即后边的迭代weights和bias都不再变化了。满心欢喜,此时我们作个图看看模型和数据拟合得怎么样:

WTF??? 这偏得太多了吧。聪明的读者思考下,这是为什么呢?

这就是我们在梯度下降讲的步子太大问题,我们这次打印最后3轮所有权重更新的变化:

我们看到权重其实是在更新的,只是在这个学习率下,步子太大,损失函数会在极小值附近震荡,如图红色箭头部分:

现在我们降低学习率到0.01看看:

x_train, y_train = get_training_dataset()
lu = LinearUnit(len(x_train[0]))
lu.fit(x_train, y_train, iteration=10, learning_rate=0.1)

很棒,这就是我们差不多想要的模型了。

5.4 用模型去预测AI工程师工资
x_test = get_test_dataset()
prediction = []
for t in x_test:prediction.append(lu.predict(t))print('预测工作{}年的AI工程师\t月薪{}'.format(t, prediction[-1]))

我们画个图看看:

棒棒哒,工资这么高,加油干吧。

六、总结

本文为您介绍了适用于所有机器学习算法的基本套路,其实就只有两个部分:

  • 模型 从输入特征x预测输出y的函数f(x)
  • 目标函数 又叫损失函数,其取最值时的参数即为模型的最优参数。但是我们往往取不到最优值,只能取到局部最优值。

然后我们会用优化算法去取目标函数的最值。优化算法有很多,本文中的梯度下降只是一种。其实梯度下降算法主要有两种:

  • 批梯度下降算法(Batch Gradient Descent, BGD)
  • 随机梯度下降算法(Stochastic Gradient Descent, SGD)

他们的区别在于用多少条数据时,更新权重。聪明的你会发现,我们每遍历一条数据,就会更新一次权重,这就是随机梯度下降算法。其实起初大家都用批梯度下降算法,都是把样本当中的所有实际值和预测值的差值求和过后,再更新权重。但是这样计算代价太大,已经被淘汰了。另外有经验的同学知道,我们经常说的batch size设置成64,是两者的结合,就是用64条数据更新一次权重。

在机器学习中,算法并不重要,重要的是特征。比如本文中如果不止工龄这一种特征,还有公司、职级等,模型的性能会大大提升。而神经网络的优势在于可以自动学习提取哪些特征,降低人力成本,又能提高模型性能。

提前关注不迷路,敬请期待。

本文集暂定内容:

如果有任何问题。可以在公众号首页找到我们的组队学习群。

这篇关于不用框架入门与进阶深度学习(3)−线性单元、梯度下降与回归任务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092551

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学