深度学习系列教程-如何向量化人工智能算法

朋友们,如需转载请标明出处公众号:jack床长

新手请点击公众号里的“历史文章”从序言看起,否则你可能看不懂本篇文章

在讲人工智能之前,第一段我先讲讲其它的,讲一些我想讲的,讲一些大家需要知道的,讲一些对大家的人生有帮助的。这一段我们来讲讲结婚。找结婚对象要趁早啊。因为要找到一个适合与自己过一辈子的人,很难的。有可能几年十年,你都遇不到一个。所以不要天天宅在家里面,多出去玩一玩结交朋友。有些人说处对象会花费精力影响事业,这个道理完全不通。你自己反思反思你因为空虚寂寞而导致的心情郁闷,以及看小黄片和自慰浪费的精力,或者出去不停重新找炮友哄骗她们所花的时间而对你事业的影响是有多么的严重。适合结婚的对象是不会影响你的事业的,反而会给你起到帮助的作用;如果那个对象是需要你花很多精力去哄骗去追的话,那TA不是适合结婚的对象。

好,进入正题,本篇文章将一步一步地教你如何向量化人工智能算法。是有点难理解的哦,做好心理准备吧。

下面我先给出了还未进行向量化的人工智能算法。本篇文章中的算法是用伪代码来呈现的,不要太在意语法,明白它们的意思就行了。我先给大家解释一下这个算法,然后再一步一步地将它改为向量化形式。为了方便你们理解,我将各部分分成了不同颜色。(如果你看不懂下面的内容,那么请你复习一下我前面的文章,可以从1.2.1开始)

for i = 1 to m:

temp = 0

for j = 1 to n:

temp += wj* xj(i)

z(i)= temp + b

a(i)=σ(z(i)) = 1 / (1 + e-z(i))

J += -(y(i)*log(a(i)) + (1-y(i))*log(1-a(i)))

dz(i)= a(i)– y(i)

for j = 1 to n:

dwj+= xj(i)* dz(i)

db += dz(i)

J = J/m,db = db / m,

for j = 1 to n:

dwj= dwj/ m

红色的for循环表示遍历所有训练样本。通过前面的文章大家知道要训练一个神经网络往往需要很多个样本;比如我们要训练识别猫的神经网络,那么一张猫的图片就是一个训练样本。

绿色的for循环表示遍历所有的输入特征。我们知道,一个样本往往会有很多个特征,一张猫的图片的每一个像素的每一个颜色强度值就是一个特征。(例如下图中的x1就是一个特征,w1就是它对应的权重)

蓝色的for循环也是遍历所有的特征,因为每一个特征都有一个对应的权重,每一个权重都有一个对应的偏导数。

后面除以m是求平均值,因为每一个样本训练得出的参数都不一样,所以通过求平均值的方法就能得出满足最广大样本的参数(就像我国人民每人的需求标准其实都不一样,而国家无法百分之百满足每个人的需求,只能满足最广大人民的需求)。既然在训练神经网络时,这一套参数能够满足最广大的训练样本,那么,在使用神经网络时,这套参数也就能最大程度的适用于新的待预测样本,也就是预测得会更准确。

下面我们来一步一步地进行向量化去除这些for循环。由于屏幕长度有限,所以为了大家后面能更好的对比向量化代码和非向量化代码,我在这里再次把非向量化代码粘贴出来。

for i = 1 to m:

temp = 0

for j = 1 to n:

temp += wj* xj(i)

z(i)= temp + b

a(i)=σ(z(i)) = 1 / (1 + e-z(i))

J += -(y(i)*log(a(i)) + (1-y(i))*log(1-a(i)))

dz(i)= a(i)– y(i)

for j = 1 to n:

dwj+= xj(i)* dz(i)

db += dz(i)

J = J/m,db = db / m,

for j = 1 to n:

dwj= dwj/ m

绿色代码可以向量化成下面的形式。

z(i)= np.dot(w.t,x(i)) + b

w.t表示将w进行转置,(矩阵转置是指将矩阵的行和列对应互换,即之前的行变成列,之前的列变成行)w之前是一个列向量,经过转置之后就变成了一个行向量。x(i)是一个列向量。套入之前文章中我们学到的向量相乘知识可知这行向量化代码等价于上面的for循环。

蓝色for循环可以向量化成下面的形式。

dw += x(i)* dz(i)

这里x(i)是一个向量,dz(i)是一个单独的数值,他们之间是如何进行运算的呢?这里就要向大家介绍一下python的广播化技术。当遇到两个不同维度的对象进行运算时,python会自动的通过复制元素来使两个操作对象的维度相同。如下图所示。

另外还可以注意到,这里的相乘没有用到np.dot,dot执行的是矩阵相乘,而python的运算符*执行的是元素相乘,即x(i)的第一个元素与广播化后的dz(i)的第一个元素相乘,x(i)的第二个元素与dz(i)的第二个元素相乘…可知上面这行向量化代码取代了第二个for循环。

两个循环被取代后,代码就成了如下形式。

for i = 1 to m:

z(i)= np.dot(w.t,x(i)) + b

a(i)=σ(z(i)) = 1 / (1 + e-z(i))

J += -(y(i)*log(a(i)) + (1-y(i))*log(1-a(i)))

dz(i)= a(i)– y(i)

dw += x(i)* dz(i)

db += dz(i)

J = J/m,db = db / m,

dw = dw / m

目前我们已经消灭了两个循环了。最后我们来消灭最上面那个红色大循环。

红色for循环去掉后,绿色代码将变成如下形式

Z = np.dot(w.t, X) + b

注意这里是大写的X,小x(i)是一个列向量,表示一个样本对应的n个输入特征,大写X是由多个小x(i)组成的,是一个n*m的矩阵。结合之前文章中我说的向量相乘知识可得np.dot(wt,X)将得到一个1*m的行向量,这个向量中一个元素就是w.t行向量与一个样本的特征列向量的乘积。

黑色代码变成了如下形式。

A =σ(Z) = 1 / (1 + np.exp(-Z))

小z(i)变成了大Z向量后,np.exp会对向量中每一个元素进行e-z(i)运算。随后的运算中python会自动应用广播化技术而得出一个A向量,这个向量里面的一个元素就对应着一个样本的小a(i)。

橙色代码变成了如下形式。

J = np.sum(-(Y*np.log(A) + (1-Y)*np.log(1-A)))/ m

np.log会对向量里面的每一个元素进行log运算,经过广播化运算后,np.sum里面就是一个向量,向量里面的一个元素就是单独一个样本对应的损失。而np.sum会将向量中每一个元素累加起来,结果除以m之后就得到了成本(平均损失)。

灰色代码变成了如下形式

dZ = A – Y

它简单的执行了元素间的减法后得到一个行向量dZ,它的一个元素对应一个样本关联的dz

蓝色代码变成了如下形式

dw += np.dot(X,dZ.t) / m

X是一个n*m的矩阵,dZ.t是一个m*1的行向量,所以np.dot(X,dZ.t)将得到一个n*1的列向量。这行代码是如何取代之前代码的呢?这个取代过程是有一点巧妙的,下面我尝试着解释一下,大家要集中注意力哦。X的一列对应着一个样本的一组输入特征,每一行对应着各个样本的同一个位置的特征,例如第一行就对应着所有样本的第一个特征,第二行就对应着所有样本的第二个特征。而dZ.t是一个列向量,里面的一个元素就是一个样本对应的dz。所以当X第一行与dZ进行运算时,就相当于所有样本对应的第一个特征与其对应样本的dz相乘后再把每一个乘积结果累加起来,这就等价于了最外面那个红色循环。除了第一行会与dZ进行运算外,X的每一行都会与dZ进行运算,这就等价于了里面那个蓝色循环。然后通过广播化技术让得到的列向量的每一个元素除以一个m,就得到了平均值。最终就这样巧妙的把两个循环都去掉了。这一个取代过程是比较巧妙的,要自己多揣摩揣摩啊。

紫色代码变成了如下形式

db += np.sum(dZ) / m

它很简单,直接用一个np.sum将向量的元素进行累加就可以了。

最终整体代码变成了如下形式。

Z = np.dot(w.t, X) + b

A =σ(Z) = 1 / (1 + np.exp(-Z))

J = np.sum(-(Y*np.log(A) + (1-Y)*np.log(1-A)))/ m

dZ = A – Y

dw += np.dot(X,dZ.t) / m

db += np.sum(dZ) / m

是不是挺难理解的呢!但是必须要理解啊,这段代码是人工智能算法的核心,理解了它才能有助于你理解人工智能编程。一遍看不懂,就休息下,去看看岛国爱情动作片,冷静下来后,再看一遍,如此反复,不到精尽人亡不罢休!

喜欢我文章的朋友请帮我推广一下,我现在是辞职写教程。

  • 发表于:
  • 原文链接:https://kuaibao.qq.com/s/20180611G0QKQW00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券