一、 引言(Introduction)

1.1 什么是机器学习?

​ 第一个机器学习的定义来自于Arthur Samuel。他定义机器学习为,在进行特定编程的情况下,给予计算机学习能力的领域。Samuel的定义可以回溯到50年代,他编写了一个西洋棋程序。这程序神奇之处在于,编程者自己并不是个下棋高手。但因为他太菜了,于是就通过编程,让西洋棋程序自己跟自己下了上万盘棋。通过观察哪种布局(棋盘位置)会赢,哪种布局会输,久而久之,这西洋棋程序明白了什么是好的布局,什么样是坏的布局。然后就牛逼大发了,程序通过学习后,玩西洋棋的水平超过了Samuel。这绝对是令人注目的成果。

1.2 监督学习

监督学习(supervised learning)指的就是我们给学习算法一个数据集,这个数据集含有“正确答案”的标签,机器通过学习输入数据到标签的映射,可预测接下来输入的无标签数据。

image-20230913151635791

上图很好地展示了监督学习过程,通过学习输入 X 和所需输出标签 Y 的数据对实现数据预测,即:“从正确答案中学习”,现实中机器学习的大部分的使用场景都是监督学习,如下图所示:

image-20230913151829672

Email客户端中,你点击“垃圾邮件”按钮,报告某些Email为垃圾邮件,不会影响别的邮件。基于被标记为垃圾的邮件,您的电子邮件程序能更好地学习如何过滤垃圾邮件;输入音频文件及其对应的音频文本,机器即可学习如何将语音转化为文本。总结来看,监督学习就是通过大量已经得知映射关系的数据集中,寻找映射逻辑并学习的过程。

监督学习可以分为两种,分别是回归问题(Regression)与分类问题(Classification)。

image-20230913153848528

回归问题

假如你现在收集到关于房价与房屋面积的数据,你把这些数据画出来,看起来是这个样子:横轴表示房子的面积,单位是平方英尺,纵轴表示房价,单位是千美元。那基于这组数据,假如你有一个朋友,他有一套750平方英尺房子,现在他希望把房子卖掉,他想知道这房子能卖多少钱。

那么关于这个问题,机器学习算法将会怎么帮助你呢?

img

应用学习算法拟合一条直线,根据这条线我们可以推测出这套房子可能卖多少钱。也可能还有更好的,比如我们不用直线拟合这些数据,用二次方程去拟合可能效果会更好。根据二次方程的曲线,我们可以从这个点推测出,这套房子能卖接近。这些都是学习算法里面很好的例子。

可以看出,监督学习指的就是我们给学习算法一个数据集。这个数据集由“正确答案”组成。在房价的例子中,我们给了一系列房子的数据,我们给定数据集中每个样本的正确价格,即它们实际的售价然后运用学习算法,算出更多的正确答案。比如你朋友那个新房子的价格。用术语来讲,这叫做回归问题。我们试着推测出一个连续值的结果,即房子的价格。

回归问题:在监督学习中,我们给学习算法一个数据集,比如一系列房子的数据,给定数据集中每个样本的正确价格,即它们实际的售价然后运用学习算法,算出更多的答案,我们需要估算一个连续值的结果,这属于回归问题

分类问题

假设现在需要构建机器学习模型, 现在有这样的数据集:横轴表示乳腺癌肿瘤的大小,纵轴1代表乳腺癌恶性,0代表乳腺癌良性,如下图所示:

image-20230913153252377

或者是给定的数据集有超过两种变量输入,学习算法必须决定如何通过这些数据来模拟一条合适的边界线帮助医生诊断。

image-20230913153602338

机器学习的问题就在于,估算出肿瘤是恶性的或是良性的概率,属于分类问题

分类问题:指的是我们试着推测出离散的输出值:0或1良性或恶性,而事实上在分类问题中,输出可能不止两个值。

1.3 无监督学习

image-20230913154044869

相较于监督学习,无监督学习的数据集没有任何的标签,不知道数据点是什么,也不知道如何处理。无监督学习算法可能会把这些数据分成多个不同的,叫做聚类算法

image-20230913154656788

互联网上每天都有无数条新闻,如何将这么多的新闻分组组成关联的新闻就成为了聚类算法需要解决的问题,例如Google新闻中关于一个专题的报道,该报道的标题可以看出相近词使用频率很高,因此聚类算法将其划分为一类。

image-20230913155103251

又例如人的DNA序列,通过输入很多人的某段DNA序列(一列代表一个人),聚类算法可以将DNA序列划分为多种类型或是不同的组。

关于无监督学习,没有提前告知算法一些信息,比如,这是第一类的人,那些是第二类的人,还有第三类,等等。我们只是告诉机器这是有一堆数据,我不知道数据里面有什么,我不知道谁是什么类型,我甚至不知道人们有哪些不同的类型,这些类型又是什么。但你能自动地找到数据中的结构吗?要机器自动地聚类那些个体到各个类,我没法提前知道哪些是哪些。因为我们没有给算法正确答案来回应数据集中的数据,所以这就是无监督学习。

应用

  • 基因学的理解应用
  • 社交网络分析
  • 组织大型计算机集群
  • 细分市场
  • 新闻事件分类

二、单变量线性回归(Linear Regression with One Variable)

2.1 模型表示

刚刚介绍了监督学习中的回归问题,单变量线性回归问题指的是将学习过程中控制机器学习的数据集只有一个自变量。有如下这样一个训练集,特征为房子的大小,因变量是房价。那么对于一个新的房子的大小,我们如何根据历史的数据来预测出来该房子的价格呢?

image-20230913202412591

可以看出,单变量线性回归模型中,模型是一条直线,通过输入房子大小根据得到的模型即可推断出该房子的售价。现在我们将训练集(Training Set)用下面表格的方式展示:

image-20230913202747055

一些标注如下:

$m$ 代表训练集中实例的数量

$x$ 代表特征或者输入变量

$y$ 代表目标变量或者输出变量

$(x,y)$ 代表训练集中的实例

$(x^i,y^i)$ 代表第 $i$ 个观察实例

$h$ 代表学习算法的解决方案或函数(function)也称为假设(hypothesis)、模型(Model)

image-20230913211442052

上述就是一个监督学习算法的工作方式,将训练集(training set)投喂给机器学习算法后得到模型 f ,通过输入特征 x 到模型中,模型就可以预测可能得目标值 image-20230913213149751 (读作 y hat),需要区别 y 与 y hat,y 通常指代训练集中的目标变量,而y hat指代预测的目标变量。

一种可能的表达方式为:$h_{\theta}(x) = \theta_0 + \theta_1x$,因为只含有一个特征/输入变量,因此这样的问题叫作单变量线性回归问题。

2.2 代价函数

image-20230913213711862

假设有上述的训练集,$m$ 代表了训练集中实例的数量,例如$m = 47$,对样本进行线性回归预测,可以得到这样的线性函数:$f_{w,b} = wx + b$

$w$ 与 $b$ 可以称作参数(Parameters)或是权重(Weights),即线性函数的斜率与在y轴上的截距。$w$ 与 $b$ 参数值的选择决定了预测的精确度,不同的权重产生不同的模型,模型对于预测精确程度的判定标准称作代价函数

image-20230913214148823

$y_{hat} ^{i} - y_i$ 的值称作建模误差(modeling error),不同的模型对于代价函数的选择可能不同,但是在单变量线性回归预测中最常使用的代价函数是平方差代价函数:

image-20230913214448235

也可以写作:

image-20230913215400114

显然为了找到预测最精确的模型,需要模型参数带入到代价函数能使其$J(w,b)$值最小,即$minimizeJ(w,b)$

2.3 代价函数的直观理解

绘制一个等高线图,三个坐标分别为$w$和$b$ 和 $J(w,b)$:

image-20230913220154623

则可以看出在三维空间中存在一个使得代价函数最小的点。另一种形式是等高线图,在取到代价最低点的参数时,模型预测准确度最高 。

image-20230913225228044

通过这些图形,我希望你能更好地理解这些代价函数所表达的值是什么样的,它们对应的假设是什么样的,以及什么样的假设对应的点,更接近于代价函数$J$的最小值。

2.4 梯度下降

梯度下降是一个用来求函数最小值的算法,我们将使用梯度下降算法来求出代价函数$J(w,b)$ 的最小值。

梯度下降背后思想:随机选择一个参数组合$(w_1,b_1)$,计算代价函数,然后我们寻找下一个能让代价函数值$J(w,b)$下降最多的参数组合,循环往复最终可以得到一个局部最小值(local minimum),选择不同的初始参数组合,可能会找到不同的局部最小值。

image-20230914120810107

想象自己站在一座山的一点,在剃度下降算法中,我们需要旋转360度看看周围,根据判断选择下降最快的方向迈出一小步,随后再次旋转360度看看周围,再选择一次方向再迈出一小步。重复上述操作最终就会到达局部最低点的位置。

批量梯度下降(batch gradient descent)算法的公式为:

image-20230914121113026

其中$\alpha$称作学习率(learning rate),它决定了我们沿着能让代价函数下降程度最大的方向向下迈出的步子有多大,在批量梯度下降中,我们每一次都同时让所有的参数减去学习速率乘以代价函数的导数,流程可以简述为:

image-20230914121230185

注意:$w$与$b$的更新是同步的,也就是说需要中间变量$tmp_w$与$tmp_b$存储运算结果。

2.5 梯度下降的直观理解

对于梯度下降算法的理解,不妨将二元函数$J(w,b)$的$b$参数值固定,假设其为0,那么对于$w$参数的梯度下降算法公式为:$w = w-\alpha\frac{\partial}{\partial w}J(w)$,函数图为下面的形式:

image-20230914122530053

对于$\frac{\partial}{\partial w}J(w)$ 即在某一点$w = w_0$处的斜率,$\alpha$ 的取指大于 0,因此可以分为下面两种情况讨论:

image-20230914122814808
  1. $w_0$点位于局部最低点右侧,此时$\frac{\partial}{\partial w}J(w_0)$的值大于0,因此根据梯度下降算法$w = w-\alpha\frac{\partial}{\partial w}J(w)$ ,有$w_新 <w_0$。
  2. 同理如果$w_0$点位于局部最低点左侧,$\frac{\partial}{\partial w}J(w_0)$的值小于0,$w_新 >w_0$

2.6 学习率$\alpha$

梯度下降算法$w = w-\alpha\frac{\partial}{\partial w}J(w)$中,学习率$\alpha$即下降中每一次下降的步长,下面讨论如果$\alpha$太大或是太小会出现什么情况:

  • 如果$\alpha$太大:

梯度下降法可能会越过最低点,甚至可能无法收敛,下一次迭代又移动了一大步,越过一次,又越过一次,一次次越过最低点,直到你发现实际上离最低点越来越远,所以,如果太大,它会导致无法收敛,甚至发散。

image-20230914124310650
  • 如果$\alpha$太小:

如果太小了,即我的学习速率太小,结果就是只能这样像小宝宝一样一点点地挪动,去努力接近最低点,这样就需要很多步才能到达最低点。

对于另一种情况,如果说$(w_0,b_0)$ 初始化就在最低点,那么此时该点的偏导均为零,梯度下降算法的更新不会改变任何参数的值,来看下面这个例子:

image-20230914124712149

越靠近局部最低点时,该点的偏导数就越小,直到取到局部最低点时偏导数为 0 ,也就是说:$w$与$b$参数偏移幅度在每一次偏移后都会缩小,直到偏移幅度缩小为零。

2.7 梯度下降的线性回归

如果将梯度下降函数中的偏导数求出来,即梯度下降函数可以转换为下面形式:

image-20230914125307454

推倒过程如下:

image-20230914125555039

则算法改写为:

image-20230914125514822

三、多变量线性回归(Linear Regression with Multiple Variables)

3.1 多维特征

目前为止,我们探讨了单变量/特征的回归模型,现在我们对房价模型增加更多的特征,例如房间数楼层等,构成一个含有多个变量的模型,模型中的特征为$(x_1,x_2,x_3,x_4)$。

image-20230917150441348

  • $n$代表特征的数量
  • $x^{(i)}$代表第 个训练实例,是特征矩阵中的第行,是一个向量vector)。

例如上图的$x^{(2)} =
\begin{bmatrix}
1416\
3\
2\
40\
\end{bmatrix}$

  • $x^{(i)}_j$代表特征矩阵中第$i$行的第$j$个特征,也就是第$i$个训练实例的第$j$个特征。

因此多变量线性回归模型可以写作下面的形式:

image-20230917151323629

不妨定义$X=
\begin{bmatrix}
x_1\
x_2\
x_3\
x_4\
\end{bmatrix}$,$W= \begin{bmatrix}
w_1\
w_2\
w_3\
w_4\
\end{bmatrix}$,因此$f_{w,b}=W \cdot X + b$,$W^T$代表$W$矩阵的转置。

注意:这里的$W^T \cdot X$ 是点乘运算而不是矩阵乘法,因为向量是一维矩阵,使用numpy.dot()既可以实现点乘,也可以实现矩阵乘法。

3.2 多变量梯度下降

与单变量线性回归类似,在多变量线性回归中,我们也构建一个代价函数,则这个代价函数是所有建模误差的平方和,即:

$J(w_1,w_2,….,w_n)=\frac{1}{2m}\sum_{i=1}^m(f_{w,b}(x^{(i)})-y^{(i)})$

我们的目标和单变量线性回归问题中一样,是要找出使得代价函数最小的一系列参数。 多变量线性回归的批量梯度下降算法为:

image-20230917155037373

求导之后:

image-20230917155510153

3.3 特征缩放

在我们面对多维特征问题的时候,我们要保证这些特征都具有相近的尺度,这将帮助梯度下降算法更快地收敛。

以房价问题为例,假设我们使用两个特征,房屋的尺寸和房间的数量,尺寸的值为 0-2000平方英尺,而房间数量的值则是0-5,以两个参数分别为横纵坐标,绘制代价函数的等高线图能,看出图像会显得很扁,梯度下降算法需要非常多次的迭代才能收敛。

image-20230917164105795

可以看到,数据集分布图中,数据可能分布的范围是一个及其扁的矩形,而该模型的代价函数分布图也是一个椭圆,解决的方法是尝试将所有特征的尺度都尽量缩放到-1到1之间。

img

最简单的方法是令:$x_n=\frac{x_n-u_n}{\sigma_n}$,其中 $u_n$是平均值,是$\sigma_n$标准差。也就是将数据集化为(0,1)正态分布。

3.4 学习率的选择

梯度下降算法收敛所需要的迭代次数根据模型的不同而不同,我们不能提前预知,我们可以绘制迭代次数和代价函数的图表来观测算法在何时趋于收敛。

img

也有一些自动测试是否收敛的方法,例如将代价函数的变化值与某个阀值(例如0.001)进行比较,但通常看上面这样的图表更好。

梯度下降算法的每次迭代受到学习率的影响,如果学习率过小,则达到收敛所需的迭代次数会非常高;如果学习率过大,每次迭代可能不会减小代价函数,可能会越过局部最小值导致无法收敛。

通常可以考虑尝试些学习率:$α=0.01,0.03,0.1,0.3,1,3,1$

3.5 特征工程与多项式回归

例如在房价预测问题中:

image-20230918125259342

$x_1$代表房屋的宽度,$x_2$代表房屋的纵向深度,$x_3=x_1\cdot x_2$代表房屋的使用面积,那么能得到预测模型:

$ f_ {W,b} ( \overrightarrow {x} )= w_ {1} x_ {1} + w_ {3} x_ {2} + w_ {3} x_ {3} + b$

创建一个新的特征值$x_3$就是所谓的特征工程的案例,可以利用对问题的知识或者直觉来设计新的特征值,该特征值通常是通过转换货组合问题的原始特征值来使得学习算法能作出更为准确的预测。

image-20230918125831344

但有时线性回归问题不能解决我们遇到的问题,有时候需要使用曲线来适应我们的数据

例如二次方模型: $ f_ {W,b} $ (x)= $ w_ {1} $ x+ $ w_ {2} $ $ x^ {2} $ + $ w_ {3} $ $ x^ {3} $ +b

例如三次方模型: $ f_ {W,b} $ ( $ \overrightarrow {x} $ )= $ w_ {1} $ $ x_ {1} $ + $ w_ {3} $ $ x_ {2} $ + $ w_ {3} $ $ x_ {3} $ +b

或者是根 号模型: $ f_ {w,b} $ (x)= $ w_ {1} $ x+ $ w_ {2} $ $ \sqrt {x} $ + b

通常我们需要先观察数据然后再决定准备尝试怎样的模型。

四、逻辑回归(Logistic Regression)

4.1 分类问题

在监督学习中,分类问题预测的变量$y$是离散值,尝试预测的是结果是否属于某一个类(例如正确或错误)。

常见分类问题的例子有:判断一封电子邮件是否是垃圾邮件;判断一次金融交易是否是欺诈;之前我们也谈到了肿瘤分类问题的例子,区别一个肿瘤是恶性的还是良性的。

image-20230918134202567

如果有下列关于肿瘤大小与是否是恶性肿瘤的数据集,如果仍使用线性回归来预测就会得到下面的这个模型:

image-20230918134439442

我们可以定义一个阈值(threshold)来判断肿瘤是否为恶性(图中为0.5),如果预测结果 $ \widehat {y} < 0.5$ 则被定义为良性肿瘤,否则为恶性,这个预测模型看似是可靠的。但是考虑这一种情况:

image-20230918134821125

如果数据集新加入数据使得预测模型 $ f_ {w,b} $$ (x)=wx+b$ 改变为绿色直线,那么此时原本数据集中应当被判定为恶性肿瘤的部分数据,会被新模型判定为良性,这显然是不合理的。

我们会学习逻辑回归算法(Logistic Regression)来进行对分类问题的预测,它的输出值永远在0到 1 之间。

4.2 逻辑回归

image-20230918154339069

逻辑回归模型(如右图所示),其函数式:**$g(z)=$ $ \frac {1}{1+e^ {-z}} $ $0<g(z)<1$**

对于逻辑回归模型可以理解为**$z = w \cdot x +b $** ,得到的$z$值带入到$g(z)$中:

image-20230918155217610

对于逻辑回归模型的理解:若 $ f_ {W,b}(x_i) $ $=0.7$ 则$x_i$这个样本下, $ f_ {w,b}(x)=P(y=1|\overrightarrow {x},\overrightarrow {w},b) =70 % $ ,即样本是恶性肿瘤的概率为70%

4.3 决策边界

决策边界(Decision Boundary):指在分类问题中,用于划分不同类别的边界或界限。决策边界可以是一个超平面、曲线或其他形状,它将特征空间分成不同的区域,每个区域对应于一个特定的类别。

image-20230918161425838

在上述逻辑回归模型中,如果 $ f_ {w,b} $ (x) $ \geqslant $ $0.5$,则预测的结果为真,此时$g(z)\geqslant0.5$ ,那么$z\geqslant0$,即: $ \overrightarrow {w} $ $ \cdot $ $ \overrightarrow {x} $ $+b $ $\geqslant$ $ 0$。

同理不难退出,若预测结果为假,即:$ \overrightarrow {w} $ $ \cdot $ $ \overrightarrow {x} $ $+ b $ < $ 0$

因此对于预测模型总的$z$,有$z=0$的函数称作为决策边界,决策边界将预测为0或1的两部分区域分割开。

image-20230918162002408

例如上述模型中,$z=w_1x_1+w_2x_2+b$,此时决策边界为$x_1+x_2=3$

image-20230918162106553

而在该模型中,$z=w_1x_1^2+w_2x_2^2+b$,此时决策边界为$x_1^2+x_2^2=1$。

可以用非常复杂的模型来适应非常复杂形状的判定边界。

4.4 代价函数

对于线性回归模型,我们定义的代价函数是所有模型误差的平方和。理论上来说,我们也可以对逻辑回归模型沿用这个定义,但是问题在于,当我们将**$g(z)=$ $ \frac {1}{1+e^ {-z}} $ 带入到这样定义了的代价函数中时,我们得到的代价函数将是一个非凸函数(non-convexfunction**)。

image-20230918164549078

这意味着我们的代价函数有许多局部最小值,这将影响梯度下降算法寻找全局最小值。

重新定义逻辑归回模型的代价函数(cost function)如下,其中$L( f_ {w,b} ( x^ {(i)} ), y^ {(i)} ) $ 称为**损失函数(loss function) **。

image-20230918165531144

对于$ f_ {w,b}(x^{(i)}) $ 函数,其取值范围为:$(0,1)$,因此可以画出$L( f_ {w,b} ( x^ {(i)} ), y^ {(i)} ) $分布图:

8b97dcb78406bea24939d146292d4d1

下面分情况来讨论:

  1. 当$y^{(i)}==1时$:如果预测结果$ f_ {w,b}(x^{(i)}) $ 值为 1 ,此时预测成功,此时代价值为0;若$ f_ {w,b}(x^{(i)}) $ 值为 0,与真实情况不相符,此时代价值接近于无穷大
image-20230918165138641
  1. 当$y^{(i)}==0时$:如果预测结果$ f_ {w,b}(x^{(i)}) $ 值为 0 ,此时预测成功,此时代价值为0;若$ f_ {w,b}(x^{(i)}) $ 值为 1,与真实情况不相符,此时代价值接近于无穷大
image-20230918165356945

如果将代价函数从分段函数简化为一般函数,代价函数可以写作:

image-20230918170400097

4.5 实现梯度下降

image-20230918182314392

可以看出梯度下降的实现方法与线性回归预测相似,下面列举 $ \frac {\delta }{\delta w_j} J(w,b)$的证明过程:

a0517626b4c87344293e5905d44edfb

注意:$\overrightarrow{w}\cdot\overrightarrow{x}$是向量乘法,而$ \frac {\delta }{\delta w_j} \overrightarrow{w} \cdot \overrightarrow{x} $ 实际上是:$ \frac {\delta }{\delta w_j} (w_1\cdot x_1+w_2\cdot x_2 + …+w_j\cdot x_j+ … + w_n\cdot x_n) $,算出来的结果应该是是$x_j$

4.6 实训:训练逻辑回归模型

首先是数据导入部分,这里使用data[data['Admitted'].isin([1])]按照Admitted值的结果(0或1)将数据划分为两部分,用于数据的展示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 读取数据集
data = pd.read_csv('ex2data1.txt', header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
data.insert(0, 'num', 1)

# 划分数据集
positive = data[data['Admitted'].isin([1])]
negative = data[data['Admitted'].isin([0])]

# 获取数据集的数量和参数个数
col = data.shape[0]
n = data.shape[1]
X = data.iloc[:, 0:n - 1]
Y = data.iloc[:, n - 1: n]

X = np.array(X.values)
Y = np.array(Y.values)

接着是参数的设置:

1
2
3
4
# 初始化参数和设置学习率、迭代次数
theta = np.array([[0, 0, 0]])
alpha = 0.001
iters = 1000000

接着先定义sigmoid函数:

image-20230921205956574

1
2
def sigmoid(z):
return 1. / (1. + np.exp(-z))

定义代价函数:

image-20230921211451539
1
2
3
4
5
6
# 定义代价函数
def costFunction(theta, X, y):
# '@' 跟np.dot()类似,是对于np.array() 的矩阵乘法运算符
first = np.multiply(-y, np.log(sigmoid(X @ theta.T)))
second = np.multiply((1 - y), np.log(1 - sigmoid(X @ theta.T)))
return np.sum(first - second) / (len(X))

定义梯度下降函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 定义梯度下降函数
def gradient(theta, X, y, alpha, iters):
# 初始化代价数组
cost = np.zeros(iters)
# 参数数量,这里是 3
parameters = int(theta.ravel().shape[0])
# 创建全 0 的(3,1)矩阵存储theta改变后的值,另一种写法是:grad = np.zeros((parameters, 1))
grad = np.zeros(parameters).reshape(1, -1)

for i in range(iters):
error = sigmoid(np.dot(X, theta.T)) - y
cost[i] = costFunction(theta, X, y)
for j in range(parameters):
term = np.multiply(error, X[:, j].reshape(-1, 1))
grad[0, j] = theta[0, j] - ((alpha / len(X)) * np.sum(term))

theta = grad.copy()

return grad, cost

绘图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 绘制数据点和决策边界
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 8))
xx = np.linspace(data['Exam 1'].min(), data['Exam 1'].max(), 100)
yy = (-g[0, 0] - xx * g[0, 1])/ g[0, 2]
ax1.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Accepted')
ax1.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Rejected')
ax1.legend()
ax1.set_xlabel('Exam 1 Score')
ax1.set_ylabel('Exam 2 Score')
ax1.plot(xx, yy, 'r', label='Prediction')

# 绘制代价函数随迭代次数的变化
ax2.plot(np.arange(iters), cost, linewidth=2, color='red')
ax2.set_xlabel('Iterations')
ax2.set_ylabel('Cost')
ax2.set_title('Error vs. Training Epoch')

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 定义 sigmoid 函数
def sigmoid(z):
return 1. / (1. + np.exp(-z))

# 定义代价函数
def costFunction(theta, X, y):
# '@' 跟np.dot()类似,是对于np.array() 的矩阵乘法运算符
first = np.multiply(-y, np.log(sigmoid(X @ theta.T)))
second = np.multiply((1 - y), np.log(1 - sigmoid(X @ theta.T)))
return np.sum(first - second) / (len(X))

# 定义梯度下降函数
def gradient(theta, X, y, alpha, iters):
# 初始化代价数组
cost = np.zeros(iters)
# 参数数量,这里是 3
parameters = int(theta.ravel().shape[0])
# 创建全 0 的(3,1)矩阵存储theta改变后的值,另一种写法是:grad = np.zeros((parameters, 1))
grad = np.zeros(parameters).reshape(1, -1)

for i in range(iters):
error = sigmoid(np.dot(X, theta.T)) - y
cost[i] = costFunction(theta, X, y)
for j in range(parameters):
term = np.multiply(error, X[:, j].reshape(-1, 1))
grad[0, j] = theta[0, j] - ((alpha / len(X)) * np.sum(term))

theta = grad.copy()

return grad, cost

# 读取数据集
data = pd.read_csv('ex2data1.txt', header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
data.insert(0, 'num', 1)

# 将数据集分为正例和负例
positive = data[data['Admitted'].isin([1])]
negative = data[data['Admitted'].isin([0])]

# 获取数据集的数量和参数个数
col = data.shape[0]
n = data.shape[1]
X = data.iloc[:, 0:n - 1]
Y = data.iloc[:, n - 1: n]

X = np.array(X.values)
Y = np.array(Y.values)

# 初始化参数和设置学习率、迭代次数
theta = np.array([[0, 0, 0]])
alpha = 0.001
iters = 50000000

# 调用梯度下降函数进行训练
g, cost = gradient(theta, X, Y, alpha, iters)

# 绘制数据点和决策边界
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(24, 8))
xx = np.linspace(data['Exam 1'].min(), data['Exam 1'].max(), 100)
yy = (-g[0, 0] - xx * g[0, 1])/ g[0, 2]
ax1.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Accepted')
ax1.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Rejected')
ax1.legend()
ax1.set_xlabel('Exam 1 Score')
ax1.set_ylabel('Exam 2 Score')
ax1.plot(xx, yy, 'r', label='Prediction')

# 绘制代价函数随迭代次数的变化
ax2.plot(np.arange(iters), cost, linewidth=2, color='red')
ax2.set_xlabel('Iterations')
ax2.set_ylabel('Cost')
ax2.set_title('Error vs. Training Epoch')
plt.show()

结果:

image-20230921213408516

五、正则化

5.1 过拟合

过拟合的问题就是指我们有非常多的特征,通过学习得到的模型能够非常好地适应训练集(代价函数可能几乎为0),但是推广到新的数据集上效果会非常的差。
下面以回归问题举例:
img

  • 图一模型不适合数据集,无法正确模拟数据,这种情况称之为欠拟合(underfit)或是高偏差(high bias)
  • 图二模型刚好合适,其泛化能力最好
  • 图三模型参数过多,若使用图三模型进行预测偏差会很大,这种情况称之为过拟合(overfit)或是高方差(high variance)

过拟合的情况在分类问题中同样会出现,例如:

img

那么应该如何防止过拟合呢?下面给出三种方法:

  1. 收集更多的数据。
  2. 选择合适的特征。
  3. 正则化。

5.2 正则化

正则化(regularization)的目标是保留尽可能多的特征值,但是每一个特征值对应的参数应当是最小的。

为了实现正则化,将参数的大小作为代价函数的考虑标准之一,因此代价函数改写为下面这种形式:

img

  • img代表训练集中样本数量
  • img表示参数代价率,这个值大于0
  • img代表参数img的数量(一般不考虑 img)

加号左侧为原来的平方差代价,加号右侧为正则化代价值。

对于img值的选择需要额外留意,如果该值选择过大,学习后各参数参数img都会是很小的值(假想为0),那么此时模拟出来的模型会是一条直线,此时模型欠拟合;如果该值选择过小(假想为0),那么此时正则化代价值可以忽略不计,此时模型过拟合。

5.3 线性回归正则化

线性回归正则化代价方程:

img

梯度下降算法:

img

img项合并在一起:img不难发现正则化是通过 img 来一步一步缩小img的值。

5.4 逻辑回归正则化

跟线性回归的类似,直接贴图:

img

5.5 实训:训练逻辑回归正则化模型

介绍

此次实训的目标是搭建一个 $x_1,x_2$ 的多项式模型来进行逻辑回归预测,为了防止训练后的模型出现过拟合情况需要加入正则化约束是的参数的权重都尽可能的小。下面是部分数据以及数据的分布图:

image-20230922213307303image-20230922213332681

读取数据

首先是数据导入部分,跟之前一样,这里使用data[data['Admitted'].isin([1])]按照Admitted值的结果(0或1)将数据划分为两部分,用于数据的展示:

1
2
3
4
5
6
# 读取数据集
data = pd.read_csv('ex2data1.txt', header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
data.insert(3, 'Ones', 1)
# 划分数据集
positive = data[data['Admitted'].isin([1])]
negative = data[data['Admitted'].isin([0])]

处理数据

相较于线性逻辑回归,多项式逻辑回归中还需要 $x_1,x_2$ 其他次方来组成多项式模型,这里使用一个 $for$ 循环分别将$x_1^2x_2^0$、$x_1x_2$、$x_1^3x_2$ 等等参数也存如data中,处理完毕后data中的数据集:

image-20230922215800827
1
2
3
4
5
6
7
8
9
10
11
# 定义多项式分割次数
degree = 5
x1 = data['Exam 1']
x2 = data['Exam 2']

for i in range(1, degree):
for j in range(0, i):
data['F' + str(i) + str(j)] = np.power(x1, i - j) * np.power(x2, j)

data.drop('Exam 1', axis=1, inplace=True)
data.drop('Exam 2', axis=1, inplace=True)

设置参数

1
2
3
4
5
6
# 初始化参数和设置学习率、迭代次数
theta = np.random.randn(1, X.shape[1])
alpha = 0.1
iters = 100000
# 学习率
learningRate = 1

代价函数

1
2
3
4
5
6
# 定义代价函数
def costFunction(theta, X, y, learningRate):
first = np.multiply(-y, np.log(sigmoid(X @ theta.T)))
second = np.multiply((1 - y), np.log(1 - sigmoid(X @ theta.T)))
reg = (learningRate / (2 * len(X)) * np.sum(np.power(theta[:, 1: theta.shape[1]], 2)))
return np.sum(first - second) / (len(X)) + reg

梯度下降函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 定义梯度下降函数
def gradient(theta, X, y, alpha, iters, learningRate):
cost = np.zeros(iters)
parameters = int(theta.ravel().shape[0])
grad = np.zeros(parameters).reshape(1, -1)

for i in range(iters):
error = sigmoid(np.dot(X, theta.T)) - y
cost[i] = costFunction(theta, X, y, learningRate)
for j in range(parameters):
term = np.multiply(error, X[:, j].reshape(-1, 1))
if j == 0:
grad[0, j] = theta[0, j] - (alpha / len(X)) * np.sum(term)
else:
grad[0, j] = theta[0, j] - (alpha / len(X)) * (np.sum(term) + (learningRate * theta[0, j]))
theta = grad

# 绘制进度条
progress = (i + 1) / iters
bar_length = 30
filled_length = int(bar_length * progress)
bar = '#' * filled_length + '-' * (bar_length - filled_length)
percentage = progress * 100
print(f'Progress: [{bar}] {percentage:.1f}%', end='\r')

return theta, cost

计算 z 值的函数
$$
z=\overrightarrow x \cdot \overrightarrow w + b
$$

1
2
3
4
5
6
7
8
def functionZ(degree, x1, x2, theta):
sum = 0
t = 1
for i in range(1, degree):
for j in range(0, i):
sum += np.power(x1, i - j) * np.power(x2, j) * theta[0, t]
t = t + 1
return sum + theta[0, 0]

计算预测准确性

1
2
3
4
5
6
7
8
9
10
11
12
def predict(theta, X):
# 使用训练好的参数预测标签
probability = sigmoid(X @ theta.T)
predictions = (probability >= 0.5).astype(int)
return predictions

# X : 输出多项式的多项式版训练集
def accuracy(theta, X, y):
# 计算模型预测的准确度
predictions = predict(theta, X)
accuracy = np.mean(predictions == y) * 100
return accuracy

绘图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 绘制判决边界
x1 = np.linspace(-1, 1, 1000)
x2 = np.linspace(-1, 1, 1000)

x1, x2 = np.meshgrid(x1, x2)
z = functionZ(degree, x1, x2, theta)

ax2.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Accepted')
ax2.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Rejected')
ax2.set_xlabel('Exam 1 Score')
ax2.set_ylabel('Exam 2 Score')
ax2.legend()
ax2.set_title('Decision Boundary')
ax2.contour(x1, x2, z, levels=[0], colors='r', linewidths=2)
plt.grid(True)
plt.show()
image-20230922224959197

六、神经网络

6.1 概述

我们之前学的,无论是线性回归还是逻辑回归都有这样一个缺点,即:当特征太多时,计算的负荷会非常大。

下面是一个例子:

img

使用非线性的多项式项,能够帮助我们建立更好的分类模型。假设我们有非常多的特征,例如大于100个变量,我们希望用这100个特征来构建一个非线性的多项式模型,结果将是数量非常惊人的特征组合,即便我们只采用两两特征的组合,我们也会有接近5000个组合而成的特征。这对于一般的逻辑回归来说需要计算的特征太多了。

假设我们希望训练一个模型来识别视觉对象(例如识别一张图片上是否是一辆汽车),我们怎样才能这么做呢?一种方法是我们利用很多汽车的图片和很多非汽车的图片,然后利用这些图片上一个个像素的值(饱和度或亮度)来作为特征。

假如我们只选用灰度图片,每个像素则只有一个值(而非 RGB值),我们可以选取图片上的两个不同位置上的两个像素,然后训练一个逻辑回归算法利用这两个像素的值来判断图片上是否是汽车:

img

假使我们采用的都是50x50像素的小图片,并且我们将所有的像素视为特征,则会有 2500个特征,如果我们要进一步将两两特征组合构成一个多项式模型,则会有约个(接近3百万个)特征。普通的逻辑回归模型,不能有效地处理这么多的特征,这时候我们需要神经网络。

神经网络模型

为了构建神经网络模型,我们需要首先思考大脑中的神经网络是怎样的?每一个神经元都可以被认为是一个处理单元/神经核(processing unit/Nucleus),它含有许多输入/树突(input/Dendrite),并且有一个输出/轴突(output/Axon)。神经网络是大量神经元相互链接并通过电脉冲来交流的一个网络。

img

这里是一条连接到输入神经,或者连接另一个神经元树突的神经,接下来这个神经元接收这条消息,做一些计算,它有可能会反过来将在轴突上的自己的消息传给其他神经元。这就是所有人类思考的模型:我们的神经元把自己的收到的消息进行计算,并向其他神经元传递消息。这也是我们的感觉和肌肉运转的原理。如果你想活动一块肌肉,就会触发一个神经元给你的肌肉发送脉冲,并引起你的肌肉收缩。如果一些感官:比如说眼睛想要给大脑传递一个消息,那么它就像这样发送电脉冲给大脑的。

img

神经网络模型建立在很多神经元之上,每一个神经元又是一个个学习模型。这些神经元(也叫激活单元,activation unit)采纳一些特征作为输出,并且根据本身的模型提供一个输出。下图是一个以逻辑回归模型作为自身学习模型的神经元示例,在神经网络中,参数又可被成为权重(weight)。

假设现在需要预测一件商品可能成为爆款的概率,现在有下面几个参数可能影响这个结果:价格(price)、运费(shipping cost)、营销(marketing)和材料(material)。进一步来看,价格+运费决定着用户支付能力(affordablity),营销决定着用户对该商品的注意力(awarness),价格+材料决定着用户对商品的感知质量(perceived quality),这三个属性无法通过数据输入得到,可以通过逻辑回归来预测得到,因此设计三个模型分别预测这三个属性值,而这三个属性值最终可以预测商品成为爆款的概率,因此再设计一个逻辑回归模型输入这三个属性最终可以预测成为爆款的概率。对于这四个模型,可以假想为四个神经元,现在设计如下四个神经元的网络:

img

图其中price,shipping cost,marketing,material 是输入单元(input units),将原始数据输入给它们。 第二列的神经元负责将数据进行处理,像这样在图中呈一列的神经元我们将其称为层(layer)。s第一层神经元将数据加工后呈递到下一层。

神经网络模型是许多逻辑单元按照不同层级组织起来的网络,每一层的输出变量都是下一层的输入变量。第一层称为输入层(Input Layer),最后一层称为输出层(Output Layer),中间一层成为隐藏层(Hidden Layers)

img

如果将输入的数据看作是向量img, 那么经过隐藏层可以得到新向量img, 最终将 img 输入输出层即可以得到结果。

  • 下面是关于 hidden layer 的一些举例:

img

神经网络中的网络层

下面是一个拥有一个输入层、隐藏层、输出层的四神经元神经网络,我们首先将目光聚焦于隐藏层 layer 1。

img

隐藏层由三个神经元组成,每个神经元拥有一个逻辑回归模型,其参数用img来表示。这一层是位于神经网络的第img层,为了表示参数所在的层数,参数均在右上角添加 img来表示所在的层数,例如img用来表示第一层第一个神经元参数。同样的用img来表示第一层输出的向量。

同样的,接下来来看layer 2 输出层:

img

可以看到 layer 1 的输出向量img成为了layer 2 的输入数据项,第二层只有一个神经元而其参数为:img

最终作为layer 2 的输出向量(只有一个数值)img 只需要判断该值是否大于$0.5$ 即可判断商品是否可能成为爆款。

不难总结出一般式:

image-20230921111013311

$\overrightarrow a^{[l - 1]}$ 称为 $l$ 层的激活因子(Activation value of layer $l$) ,也就是说:$\overrightarrow a^{[l - 1]}$ 作为输入参数会被带入到 $l$ 层的模型中运算。因此输入层输入的数据可以看作是 $\overrightarrow a^{[0]}$ ,其作用于第一个隐藏层 $ layer1$。

在神经网络中,这种通过将输入数据从输入层传递到输出层,逐层计算和传递数据的过程称为网络层的正向传播(Forward Propagation)。在这个过程中,每一层的神经元接收上一层的输出,并将其作为输入进行计算,最终产生输出。

Tensorflow实现简单网络

训练模型时可以分为下面几步:

  1. 制定模型函数,确保模型能够很好地切合训练集及其预测的目标。
  2. 确定成本函数(lost function)和损失函数(cost function)
  3. 梯度下降算法计算模型

这三步在Tensorflow中的对应图如下:

img

第一步:创建网络架构

img

Dense()函数用于创建神经网络模型中的一层,units = 25表示这一层中拥有25个神经元,activation = 'sigmoid'表示激活函数使用 sigmoid 函数。

第二步:损失和成本函数

img

model.compile(loss = ) 输入属性loss的值可以制定不同的损失函数,loss = BinaryCrossentropy()指定为逻辑回归损失函数,loss = MeanSquaredError())指定为线性回归平方差损失函数。

第三步:梯度下降

img

使用函数model.fit(X, y epochs =)来执行梯度下降算法,tensorflow汇使用逆向传播的方式计算损失函数的偏导数,这些都会自动执行。

6.2 正向传播与反向传播

正向传播

正向传播(forward propagation):沿着从输入层到输出层的顺序,依次计算并存储神经网络的中间变量。

首先拿一个简单的三层神经网络来举例,如下:

在这里插入图片描述

每个神经元由两部分组成,第一部分(e)是输入值权重系数乘积的,第二部分($f(e)$)是一个激活函数(非线性函数)的输出, $y=f(e)$即为某个神经元的输出,如下:

在这里插入图片描述

下面是正向传播过程:

在这里插入图片描述
在这里插入图片描述 在这里插入图片描述

到这里为止,神经网络的前向传播已经完成,最后输出的y就是本次前向传播神经网络计算出来的结果(预测结果),但这个预测结果不一定是正确的,要和真实的标签(z)相比较,计算预测结果和真实标签的误差($δ$ ):

如下:

在这里插入图片描述

下面开始计算每个神经元的误差($ \delta$):

在这里插入图片描述

如果传播的误差来自少数几个神经元,则这些误差会求和计算,图示如下:

在这里插入图片描述

注意:上述误差计算过程不会真实存在,而是模拟这个过程帮助你弄明白神经网络中神经元误差生成流程用于接下来反向传播的求导。

反向传播

反向传播(Backpropagation):一种用于计算神经网络中参数梯度的方法,它是训练神经网络的核心算法之一。通过反向传播,可以根据成本函数的梯度信息来更新神经网络的权重和偏置,从而使网络能够逐渐优化并提高性能。

反向传播的过程可以分为以下步骤:

  1. 正向传播:首先,使用输入数据进行前向传播,将数据从输入层逐层传递到输出层。在每一层中,通过将权重和偏置与输入相乘并应用激活函数来计算每个神经元的输出。
  2. 计算成本函数:根据模型的预测结果和真实值,计算成本函数(也称为损失函数)的值,用于度量预测结果与真实值之间的差异。
  3. 反向传播误差:从输出层开始,计算成本函数对每个参数的梯度。通过使用链式法则,将误差从输出层反向传播回网络的每一层。在每一层中,根据当前层的误差和权重,计算上一层的误差。
  4. 计算梯度:根据反向传播的误差,计算每个参数(权重和偏置)的梯度。梯度表示成本函数关于参数的变化率,它指示了在参数空间中应该朝着哪个方向更新以减小成本函数的值。
  5. 参数更新:使用优化算法(如梯度下降法),根据计算得到的梯度信息来更新神经网络的参数。通过反复迭代这个过程,不断调整参数,使得成本函数的值逐渐减小,从而优化模型。

还是刚刚的例子,下面开始利用反向传播的误差,计算各个神经元(权重)的导数,开始反向传播修改权重,修改的方式与梯度下降类似:
$$
w_i^, = w_i - \alpha \frac{\partial J_{total}}{\partial w_i}
$$
参数说明如下:

  • $w_i$ :一个参数(权重)
  • $\alpha$ :某个神经元学习率
  • $ J_{total}$ :整个神经网络输出结果代价函数

下面模拟反向传播过程:

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

到此为止,整个网络的前向,反向传播和权重更新已经完成。

正向、反向传播举例说明

首先明确,“正向传播”求损失,“反向传播”回传误差。同时,神经网络每层的每个神经元都可以根据误差信号修正每层的权重

BP算法,也叫$\delta $算法,下面以3层的感知机为例进行举例讲解:

在这里插入图片描述

下面是前向(前馈)运算(激活函数为sigmoid,误差函数为平方差):

在这里插入图片描述
  • $net_{h_1}$ 可以理解为$z_1 = \overrightarrow w\cdot \overrightarrow x + b$
  • $out_{h_{1}}$ 可以理解为$f(z_1) = sigmoid(z_1)$

下面是反向传播(求网络误差对各个权重参数的梯度):

先来求最简单的,求神经网络误差$E_{total}$对$$w_5$$的导数。首先明确这是一个“链式求导”过程,要求误差$E_{total}$对$$w_5$$的导数,需要先求误差$E_{total}$对$$out_{o1}$$的导数,再求$out_o1$对$net_o1$的导数,最后再求$net_{o1}$对$w_5$的导数,经过这个链式法则,我们就可以求出误差$E_{total}$对$$w_5$$的导数(偏导),如下图所示:

在这里插入图片描述

导数(梯度)已经计算出来了,下面就是反向传播与参数更新过程

在这里插入图片描述

如果要想求**$E_{total}$对$$w_1$$的导数**,误差$E_{total}$对$$w_1$$的导数的求导路径不止一条,这会稍微复杂一点,但换汤不换药,计算过程如下所示:

在这里插入图片描述

6.3 激活函数

激活函数:其实就是存在于单个神经元中的模型函数。

ReLU激活函数

先给出ReLU激活函数的定 义:$z=\overrightarrow x \cdot \overrightarrow w + b$, $g(z)=m a x(0,z)$ReLU函数与线性回归激活函数类似,不过当imgimg。ReLU函数通常用于处理参数值为正数的情况,就比如还是之前的模型:

img

这里的 img代表用户的注意程度,之前我们使用的是 sigmoid 激活函数只能是 0 或 1,但是现实中的注意力程度应该是一个非负数(比如 0~100),而线性逻辑回归可能会输出负数不适合,因此可以使用img激活函数。

下面👇是三种激活函数图像对比:

img

如何选择激活函数

image-20230922203301087

以上述的模型为例,在上述模型中存在一个输入层、两个隐藏层和一个输出层,下面来分别讨论:

  • 对于输出层的激活函数:这取决于该神经网络的目标
    • 例如做一个二分类问题毫无疑问使用sigmoid激活函数(分类)
    • 如果做一个预测问题且预测值为正负值使用线性逻辑回归预测(预测明天股票的涨跌)
    • 如果预测的的值为非零数那么使用ReLU激活函数(预测房屋的价格)
  • 对于隐藏层的激活函数:通常使用ReLU激活函数,下面介绍原因
    1. ReLU相较于sigmoid激活函数,其运算速度更快。sigmoid函数需要取幂取反,而ReLU函数只需要计算一次最大值。
    2. sigmoid激活函数在$x -> {+-} \infty$ 时会变得扁平,这会导致梯度下降地很慢。

综合上述观点:隐藏层的激活函数通常选择ReLU,输出层的激活函数通常按需选择。下面是模型的TensorFlow构造代码:

image-20230922204654592

不要连续地使用线性回归激活函数

定义一个网络使用了两个隐藏层神经元并且每一个神经元都是使用线性回归激活函数,如下图所示:

image-20230922204853671

现在假定输入单元为:$x$,那么有:

image-20230922205032188

可以看到最终输出结果$\overrightarrow a ^{[2]} = w\cdot x + b$ ,这与一层线性回归模型的网络输出结果一致,因此当连续地使用线性回归激活函数时,效果仅仅只有一层,无法处理复杂的问题。

6.4 多分类问题

概述

很多情况下,我们希望机器能够学习从而进行两种类别以上的分类,例如手写数据集的识别要求机器能够识别 0 ~ 9 的数据,在多分类问题中,决策边界可能如下图所示:

image-20230924102220905

Softmax函数

Softmax是逻辑回归算法的一种推广,主要解决的就是多分类问题,下面先回顾下二分类分类问题:

image-20230924104230133

根据逻辑回归的sigmoid函数,我们不难得到取到该分类的概率$a_1$,那么没有取到该分类的概率就是$1-a_1$。

现在将分类的数量增加到 4 个 ,每一个分类的sigmoid函数可以定义为 $g(z_i) = g(\overrightarrow w_i \cdot \overrightarrow x + b_i)$,那么定义Softmax函数为如下形式:

image-20230924104944469

按照 4 个分类的情况举例:

image-20230924105014207

讨论:

  1. 为什么Softmax函数中要使用指数函数?

    答:主要原因是为了引入非线性,使得模型能够更好地捕捉类别之间的差异。指数函数具有快速增长的特性,它可以将输入的值映射到更大的范围,从而增强了差异性。另外,指数函数的性质也有助于处理数值稳定性的问题。在计算Softmax函数时,指数函数的使用可以防止数值溢出或下溢,因为指数函数的输出范围是正实数,可以有效地处理较大或较小的数值。

  2. Softmax函数中使用sigmoid函数得到的值还是取到该类别的概率吗?

    在多分类问题中,Sigmoid函数通常不直接用于表示每个分类的概率,可以理解为该分类的权重值,而Softmax函数就是通过这个权重值得到该分类的概率。

Softmax 在神经网络作用于输出层,Softmax 输出层中神经单元的数量与需要识别的分类数量$n$相同。Softmax输出层会输出取得不同类别的概率,如下图所示:

image-20230924113227329


Softmax函数代价函数

在神经网络反向传播中,要求一个损失函数,这个损失函数其实表示的是真实值与网络的估计值的误差,知道误差了,才能知道怎样去修改网络中的权重。

损失函数可以有很多形式,这里用的是交叉熵函数,主要是由于这个求导结果比较简单,易于计算,并且交叉熵解决某些损失函数学习缓慢的问题。交叉熵的函数是这样的:

image-20230925225525834

成本函数求和去平均得到代价函数:

$$
J(\overrightarrow w ,b) = \frac{1}{m}\sum_{i=1}^{m}L
$$
下面使用一个 3 分类的数据集来进行介绍:

img

推广到一般形式:

img


Softmax实现梯度下降

梯度下降与线性回归、逻辑回归类似:

img

举一个例子:

img

因此分为两种情况讨论:

  1. 样本真实标签与 img 参数所属标签相同,则分子会存在img参数
  2. 样本真实标签与 img参数所属标签不同,则分子不会存在img参数

推理前先整理几个数据:
$$
z_i = \sum_jw_{ij}x_{ij} + b
$$

$$
a_i = \frac{e^{z_i}}{\sum_te^{z_t}}
$$

$$
Loss(L) = -\sum_iy_iIna_i
$$

根据梯度下降公式:
$$
w_{ij} = w_{ij} - \alpha \frac{\partial L}{\partial w_{ij}}
$$
而根据复合函数的求导法则:
$$
\frac{\partial L}{\partial w_{ij}} = \frac{\partial L}{\partial a_k} \cdot \frac{\partial a_k}{\partial z_i} \cdot \frac{\partial z_i}{\partial w_{ij}}(k是样本真实标签,i 是参数 w 所属的标签)
$$
有个人可能有疑问了,这里为什么是$a_k$而不是$a_i$,这里我想了近三个小时,终于得出结论:对于某个训练集的样本来说,其损失函数的值与样本真实标签 $k$ 有关。当$k!=i$ 时,此时softmax模型中$a_k=\frac{e^{z_k}}{\sum_te^{z_t}}$ 中分子部分不存在 $w_{ij}$ 的项,只有当$k==i$ 时才存在。也就是说:softmax公式分母包含了所有神经元的输出,而对于分母是否包含$w_{ij}$ 就是分别讨论了。

首先求出不需要讨论的部分:
$$
\frac{\partial L}{\partial a_k} = -\frac{1}{a_k}
$$

$$
\frac{\partial z_i}{\partial w_{ij}} = x_{ij}
$$

下面分类讨论:

  1. 当$i == k$ 时:

$$
\frac{\partial a_k}{\partial z_i} = a_k(1-a_k)
$$

​ 此时就有:
$$
\frac{\partial L}{\partial w_{ij}} = (a_k - 1) \cdot x_{ij}
$$

  1. 当$i != k$ 时:

$$
\frac{\partial a_k}{\partial z_i} = -a_ka_i
$$

​ 此时就有:
$$
\frac{\partial L}{\partial w_{ij}} = a_i\cdot x_{ij}
$$
总结一下就有:
$$
\frac{\partial L}{\partial w_{ij}} =
\begin{cases}
(a_i - 1) \cdot x_{ij},,i = k\
a_i\cdot x_{ij} ,,i \neq k\
\end{cases} (i为w参数所属标签,k为样本真实标签)
$$


Softmax改进实现

先看看下面这段代码:

image-20230924120855354

为什么(2/10000)(1 + 1/10000) - (1 - 1/10000) 的结果会不同?是因为在(1 + 1/10000) - (1 - 1/10000)计算机会会对 (1 + 1/10000)(1 - 1/10000) 进行两次舍入操作,最后两式相减还会进行一次舍入。本来舍入操作就会产生误差,将误差带入式子后会产生更大的误差,因此在计算中要减少中间变量的产生,直接计算能够减小误差。

逻辑回归模型的激活函数与代价函数如下:

image-20230924121312044

根据刚才的结论,想要误差足够小就需要中间变量 $a$ 能够直接带入损失函数的式子中:

image-20230924121428403

如果想要用TensorFlow来实现上述优化过程,需要更改逻辑回归模型的两处代码:

  1. 指定输出层为线性回归(liner)
  2. 指定损失函数中的属性from_logits值为true

image-20230924121636415

那样输出层的输出结果不就是线性回归的输出结果了吗?

并不是这样的,from_logits = true 表示模型输出是未经过概率变换的logits,指定 loss =BinaryCrossEntropy 后TensorFlow还是会根据损失函数将线性回归的输出结果转换为逻辑回归sigmoid函数输出取该分类的概率。


现在将视野拉回softmax函数,该函数的代价函数可以写为下面这种形式(假定分类数量为10):

image-20230924122213061

那么对softmax函数的优化代码为:

image-20230924122437094

6.5 梯度下降的优化(Adam 算法)

Adam(Adaptive Moment Estimation)是一种常用的优化算法,用于在深度学习中更新神经网络的参数。它结合了动量法和自适应学习率的思想,被广泛应用于各种深度学习模型的训练过程中。

d26b98f9fe591ca457030699dadd259

Adam 算法核心思想是对学习率$\alpha$ 的偏差修正:如果$\alpha$ 过于小(下降速度很慢),则增大$\alpha$ 的值;如果 $\alpha$ 值过于大(出现代价值增大),则减小 $\alpha$ 值。

在TensorFlow中使用Adam算法优化梯度下降过程:

b7aa81a313b49e09a20f682dcd28750

6.6 神经网络中常见的网络层类型

1 全连接层(Dense layer type)

就是之前学习一直学习的网络层,它是最常见的网络层类型之一,也被称为全连接层或线性层。每个神经元与上一层的所有神经元相连,通过权重和偏置来进行线性变换,并通过激活函数将结果传递给下一层。

密集层的每个神经元都与上一层的所有神经元相连,每个连接都有一个权重。这意味着输入特征的每个维度都与输出特征的每个维度都有一个连接权重。神经元接收上一层的输入,对它们进行加权求和,并通过激活函数将结果传递给下一层。

image-20230925112223135

2 卷积层(Convolutional layer type)

主要用于处理图像和其他类似结构的数据。卷积层通过使用卷积核(过滤器)在输入数据上进行滑动窗口操作,提取局部特征,具体来说:每一个神经元输入的数据只是整体数据的一小部分,这样做能够加快训练速度,同时只需要更小的数据集支持,如果神经网络中有多个卷积层,则称其为卷积神经网络。

image-20230925112754854

3 池化层(Pooling layer type)

池化层(Pooling Layer)是深度神经网络中常用的一种层类型,用于减少特征图的空间尺寸,同时保留重要的特征信息。池化层通过对输入特征图的局部区域进行聚合操作,将其转换为更小的输出特征图。

七、关于机器学习的建议

7.1 评估假设Evaluating a Hypothesis

当学习的算法时候,考虑的是如何选择参数来使得训练误差最小化。在模型建立的过程中很容易遇到过拟合的问题,那么如何评估模型是否过拟合呢?

为了检验算法是否过拟合,将数据集分成训练集和测试集,通常是$7:3$的比例。关键点是训练集和测试集均要含有各种类型的数据,通常我们要对数据进行“洗牌”,然后再分成训练集和测试集。

image-20230925120749415

当我们在训练集上得到我们的学习模型之后,就需要使用测试集合来检验该模型:

  • 对于线性回归模型:利用测试数据计算代价函数$J_{test}$ 与 训练集计算代价函数$J_{train}$ 进行比较,如果$J_{train}$ 的值很低而$J_{test}$的值很高,就说明模型存在过拟合。注意这里的代价函数均不包含正则项。

    image-20230925121642975
  • 对于逻辑回归模型:

    • 方式一:与线性回归一样计算$J_{test}$ 与$J_{train}$ 进行比较

    • 方式二:在针对每个测试集样本计算误分类的比率,再求平均值

      image-20230925121559312

7.2 模型选择和交叉验证集

假设我们要在10个不同次数的二项式模型之间进行选择:

image-20230925133228457

现在能够知道的是$J_{t e s t}(w^{<5>},^{<5>})$ 是最小值,那么就能够选择$(w^{<5>},^{<5>})$ 作为模型了吗?可能存在训练集上过拟合并且测试集上代价函数最小的情况,为了准确评估模型的泛化能力并选择合适的模型,通常会使用交叉验证(Cross-validation)的方法,即:使用60%的数据作为训练集,使用 20%的数据作为交叉验证集,使用20%的数据作为测试集:

image-20230925134656524

下面介绍模型选择的具体细节:

  1. 针对每个二项式模型,使用**训练集来训练模型的参数(例如,权重w和偏置b),这就意味着对于每个模型会得到不同的参数**。
  2. 使用交叉验证集来评估每个模型的性能。代价可以根据任务的具体情况选择,例如使用均方误差(MSE)或对数损失(Log Loss)等。通过比较各个模型在交叉验证集上的代价,选择具有最低代价的模型
  3. 使用选择的最佳模型在**测试集上进行最终评估**。将测试集的数据应用于最佳模型,并计算模型在测试集上的代价或其他评估指标。这个步骤主要用于评估模型的泛化能力,即模型在未见过的数据上的表现。
  4. 如果在测试集上发现模型的泛化能力不佳,您可以尝试调整模型的超参数、增加更多的训练数据、应用特征选择也可以直接打乱数据集。然后,您可以重复之前的步骤,重新训练和评估模型,以获得更好的泛化能力。

7.3 参数对偏差和方差影响

多项式次数

如果一个算法的运行结果不是很理想,只有两种情况:要么偏差过大,要么方差过大。换句话就是说,要么出现欠拟合,要么出现过拟合。偏差指模型的拟合能力,方差指模型的泛化能力,越高均代表该模型这项能力越弱

image-20230925154106829

通过训练集和交叉验证集的代价函数误差和多项式的次数绘制在同张图中:

  • 高偏差(high bias):$J_{train}$值较高,且$J_{t r a i n} \approx J_{c v}$
  • 高方差(high variance):$J_{cv}>>J_{train}$ 且$J_{train}$ 值可能很低

正则化参数$\lambda$

正则化技术主要是为了解决过拟合的问题,通过向代价函数中添加参数$w_j$ 的项来保证参数的值都能尽可能地小。

image-20230925160602596

如上图所示:

  • 当$\lambda$ 值太大:模型图像趋近一条水平直线,此时偏差较大。
  • 当$\lambda$ 值太小:正则化无法起效,模型过拟合,方差较大。

下图是$\lambda$ 与$J_{train}$ 和 $J_{cv}$ 值的关系图:

image-20230925161147318

训练集大小(学习曲线)

使用学习曲线来判断某一个学习算法是否处于偏差、方差问题,其是将训练集误差和交叉验证集误差作为训练集样本数量$m$的函数绘制的图表

样本数量 $m$ 的变化对代价函数的影响对于模型拟合、高偏差和高方差这三种情况是不同的,下面我们来分开讨论:

情况1:模型拟合

image-20230925172248303

对于模型拟合的情况,当训练集大小越小时,相对容易能够得到非常小的训练误差$J_{train} $,然而预测交叉验证集中的数据时,因为训练量过小导致$J_{cv}$很大;随着训练集规模的增大,二次函数更难拟合所有训练示例导致$J_{train}$ 值增大,而$J_{cv}$ 不算减小,如下图所示:

image-20230925172627307

情况2:高偏差

image-20230925172705081

对于高偏差情况:使用简单的线性函数对于大量的训练集也不会产生太大的改变,也就是说$J_{train}$ 、$J_{cv}$ 最后会趋于平整。但需要注意的是:高偏差情况无论添加多少训练集,拟合能力$J_{cv}$ 永远都无法达到人类水平判别能力。如下图所示:

image-20230925173137824

情况3:高方差

image-20230925173314348

对于高方差情况:随着训练集的增多,$J_{train}$不断升高且$J_{cv}$ 不断下降,并且最终能够超越人类水平的判别能力,如下图所示:

image-20230925173638743

总结

解决高方差:

  1. 增大训练集样本的数量。
  2. 减少特征的数量。
  3. 增大正则化参数 $\lambda$ 的值

解决高偏差:

  1. 增多特征的数量。
  2. 减小正则化参数$\lambda$ 的值。
  3. 增多多项式的特性。

事实证明,构建复杂的神经网络只要通过合适的正则化就能做的比较小的模型更好,低偏差永远是我们需要解决的首要问题:

image-20230925180844711

八、决策树

8.1 决策树模型

简单的决策树

用猫分类问题来介绍决策树模型。这是一个二元分类问题,每个特征的值只能取两个离散值。

在这里插入图片描述

以下是决策树模型:

在这里插入图片描述
  • 根节点:树中最顶层的节点。
  • 决策节点:图中的椭圆节点。
  • 叶子节点:图中的矩形节点。

要根据决策树作出分类,首先从根节点开始,根据决策节点的值选择向左或者向右,最终做出分类。

构建决策树

在构建决策树的过程中,我们需要做出几个关键决定。

  1. 选择在每个节点上使用哪些特征进行拆分,原则就是最大化纯度(Purity),使每个节点中尽量都是cat或者not cat。

    在这里插入图片描述
  2. 何时停止拆分。

  • 节点只剩一个类别。
  • 树的深度达到最大深度(预先设置的参数),确保我们的决策树不会变得太大或太笨重,一定程度上防止过拟合。
  • 纯度的提升小于一个阈值(改善太小)。
  • 样本的数量小于一个阈值

熵与纯度

(entropy)是衡量一组数据纯度(是否不纯)的指标,定义 $p_1$是样本中猫的比例,则熵和 p1 的函数图像如下:

在这里插入图片描述

当两种类别的样本越接近一半数量的组合,不纯度越高,则熵越高。

熵函数的实际方程如下:

在这里插入图片描述

计算熵时采用的是以 2 为底而不是以 e 为底的对数,是为了让曲线的峰值等于 1,便于解释曲线的意义(杂质含量)。

8.2 构建决策树

拆分算法

按信息增益拆分

构建决策树时,在一个节点使用什么特征进行拆分取决于选择什么特征可以最大程度地减少熵

熵的减少成为信息增益(Information gain),下面介绍如何计算信息增益。

在这里插入图片描述

分别计算左右分支的熵之后,会发现每种分法有两个熵值,那么如何进行比较呢?如果一个高熵的节点上有很多样本,往往比一个高熵的节点上只有少数样本更糟糕,因为前者是一个很大的含有很多杂志的数据集,为了确保进入较多样本数的分支的杂质更低,所以通常计算一个以上的加权平均,然后使用根节点的熵减去这个加权平均,即求出了熵的减少,即信息增益,下图是信息增益计算的公式:

在这里插入图片描述

决定何时不进行拆分的标准之一就是如果熵减少的太小,增益太小,而拆分还会增加过拟合的风险,就停止拆分。

信息增益告诉我们如何选择一个特征来划分一个结点,以下是建立决策树的流程:

  1. 从根节点的所有样本开始。

  2. 计算所有可能的特征的信息增益,并选择信息增益最高的特征。

  3. 根据所选特征拆分数据集,并创建树的左右分支。

  4. 继续重复拆分过程,直到满足停止条件:

    • 当样本100%属于一个类别,即0熵。

    • 当分割节点时,将导致树超过最大深度

    • 从附加分割中获得的信息增益小于阈值

    • 当节点中的示例数低于阈值时

决策树是递归划分

在这里插入图片描述

关于如何选择树的最大深度,可以使用一些开源库中的参数,也可以使用交叉验证方法选择最佳参数。最大深度越大,也就像模型的多项式次数越多、模型越复杂,这可能会导致过拟合的现象出现。

按基尼指数拆分

基尼指数: 与熵类似,基尼系数也是衡量随机变量不纯度的一种方法。基尼系数越小,则表示变量纯度越高。

假设随机变量x有K个可能取值,其概率分布为$P(x=x_k)=P_k$ 则x的基尼指数为:
$$
G i n i(x)=\sum_{k=1}^{K}p_{k}(1-p_{k})=1-\sum_{k=1}^{K}p_{k}^{2}
$$
直观来说,$Gini(x)$反映了从数据集x中随机抽取两个样本,其类别标记不一致的概率,因此$Gini(x)$越小,则数据集D的纯度越高。

回到训练集中,如果将数据集D看做随机变量,数据集的特征A看做随机变量可能的取值,假设特征数量为K,样本属于特征K的数量为$C_K$,那么样本数据D的基尼指数为:
$$
G i n i(D)=\sum_{k=1}^{K}\frac{C_{K}}{|D|}(1-\frac{C_{K}}{|D|})=1-\sum_{k=1}^{K}(\frac{C_{K}}{|D|})^{2}
$$
那么在选择拆分时,同样根据每一个特征基尼指数的大小进行拆分,特征A的基尼指数为:
$$
G i n i(D,;;a_{i})=\frac{|C_{i}|}{|D|}G i n i(C_{i})+(1-\frac{|C_{i}|}{|D|})G i n i(D-C_{i});;;
$$

生成方式

独热编码One-hot

在之前的样例中,每个特征只有两个离散的取值(例如脸型是圆脸和非圆脸),那么当取值大于两个时,我们怎么构建决策树呢?此时就 需要使用 独热编码One-hot 编码。

在这里插入图片描述

当耳型特征有三个离散取值时,我们把它拆开,创建三个新的特征,每个特征只能取0-1两个可选值中的一个,三个特征中只有一个能取 1,因此叫做One-hot独热编码。
构建的决策树形状如下:

在这里插入图片描述

进一步,如果一个分类特征可以取 k 个值,那么我们将创建 k 个二进制特征来代替它。
这样每个特征就只能取两个离散的值,回到了之前的情况。
如上,我们构建了5个特征的列表,前三个是来自脸型的独热编码,将它输入到新的神经网络或逻辑回归模型,并尝试训练猫分类器。
One-hot 编码不仅仅适用于决策树,它也适用于神经网络,线性回归和逻辑回归(使用 0、1 对特征进行编码,作为输入)。

连续值特征

当我们面临一个具有连续值的特征时,通常对其拆分的方式是设置一个阈值,根据是否小于等于阈值进行拆分。

比如新增了一个特征-体重,是一组连续型的数值。选择不同的阈值并计算计算增益,选择带来的信息增益最大的那个阈值。

在这里插入图片描述

选择阈值时,一种常见的方法是,根据特征值大小对所有样例排序,取排序表中每两个连续点的中点值作为一种阈值选择,也就是说,当有 10 个样例时,会测试 9 个不同的阈值。

决策树剪枝

一个性能良好的决策树结构应该具有小的错误率和低的决策代价。

决策树的损失函数:$C_a(T) =\alpha|T|+\sum^{|T|}_{t=1}N_tH_t(T)$

image-20231011171920616 image-20231011171952356

回归树

如果之前的决策树是为了预测种类,那么构建回归树是为了预测一个数字,这与之前的逻辑、线性回归类似。举例介绍回归树——预测动物体重。

在这里插入图片描述

根据耳朵形状、面部形状、胡须来预测体重,因此是个回归问题。为回归问题建立好的决策树如下图:

在这里插入图片描述

当预测一个新的样例时,从根节点开始根据样例特征进行决策,直至根节点,然后输出该叶节点动物的体重的平均值。

在这里插入图片描述

选择特征划分样例时,不同于尽量减少熵的方法,回归问题中是尽量减少方差。

方差的减少 = 根结点方差-加权平均方差

8.3 树集合

引入树集合

使用单个决策树的缺点之一是该决策对数据中的微小变化高度敏感。
比如我们更改训练集中的一个样本,这可能会导致算法产生完全不同的分裂,产生完全不同的树,这也是过拟合的一种表现,通过使用多个决策树,并结合它们的预测结果,可以降低过拟合的风险,提高模型的泛化能力。

image-20231009180005333

例如上述案例:只需要改变一个样本,回归树根据信息增益得到的拆分方式从Ear Shape改变为Whiskers,继续递归得到的是一个完全不同的决策树。

为了使算法不那么敏感或者更健壮的解决方案是构建很多决策树,我们称之为树集合

在这里插入图片描述

当构建多个合理的决策树时,也就是树集合,对一个新的样例分别作出判断,然后投票决定最终结果。

例如上图所示:树集合中公有三个决策树,三个决策树对于新测试样本的预测结果分别是:’Cat’ 、’Not Cat’、 ‘Cat’,显然预测为’Cat’ 的数量更多,因此最终投票结果为’Cat’

构建树集合

有放回抽样是构建树集合的关键技术。
原始训练集中有 10 个动物,我们做 10 次有放回抽样,得到一个新的训练集,这与原始的训练集不同,并且有些样本重复。

image-20231009181122442

通过有放回抽样,就可以实现固定数量的样本生成大数量的树集合训练样本,假设固定样本数量为$n$,那树集合中树的数量最多为:$n!$。··

随机森林

随机森林比单个决策树具有更好的效果。
如何生成随机森林:

  1. 使用有放回抽样方法得到新的训练集。
  2. 训练决策树。
  3. 重复以上步骤,共训练B棵决策树。
    至于重复多少次,建议选择64~128中的数值,比如100。
  4. 给出新样例,让100棵树分别作出预测并投票。

事实证明,B设置的更大,不会对算法有坏处,但若超过了某个数值,会导致收益递减,若远大于100,算法不会获得更好的效果,而是会影响效率(可以想象很多树具有相同的结构)。

对该算法进行一点更改,优化每个结点的特征选择,使集合中的树与彼此变得不同,就变成了随机森林算法。
在这里插入图片描述
在每个节点上,当选择用于拆分的特征时,如果n个特征可用,则选择一个随机子集 $K$ 个特征,并允许算法仅从该特征子集中进行选择,往往用于具有较多特征的较大问题。
当n很大时,通常令$K=\sqrt{n}$​。

XGBoost

XGBoost(Extreme Gradient Boosting)是一种增强决策树算法,运行速度快,开源易于使用,且非常成功的赢得了很多机器学习比赛,并成功应用于很多商业应用中。
对生成树集合的算法进行了改进,除了第一次取样以外,与其以相等的概率从所有示例中选择实例,不如
优先选择以前训练过的树错误分类的示例
,也被称为**刻意练习**,思想是将注意力集中在容易出错的小部分。
但是怎么提高预测错误实例的概率是一个非常复杂的问题,XGBoost是一种普遍的方法。

在这里插入图片描述
XGBoost 优点如下:
在这里插入图片描述

  • 开源。
  • 快速高效实现
  • 具有默认分裂准则和正确选择何时停止分裂
  • 内置正则化以防止过度拟合
  • 竞赛中机器学习的高度竞争算法(如:Kaggle竞赛)

实际上,XGBoost为不同的训练实例分配了不同的选择方法,所以不需要生成和多组训练示例,这使它比随机采样更加高效。
在这里插入图片描述

使用XGBoost步骤:

  1. 导入库。
  2. 将模型初始化为XGBoost分类器/回归器。
  3. 训练模型。
  4. 预测。

8.4 何时使用决策树

选择何时使用决策树和神经网络:

决策树适合处理结构化数据(表格数据),并且训练速度快,小型决策树具有可解释性(人可以读懂它的原理);
在这里插入图片描述

神经网络

  • 适合处理任何类型数据,如图像,视频,音频,文本等非结构化数据或混合数据。
  • 但其速度比较慢,大型的网络需要更长的时间来训练。
  • 通常和迁移学习一起应用。
  • 当需要多个模型联合使用时,可以构建更强大的机器学习系统,使用神经网络也更容易一些(可以将其并联在一起同时训练,而决策树一次只能训练一棵树)。
    在这里插入图片描述