部分DL笔记

P17 Vectorization

  • 什么是Vectorization?

    • 在Logistic Regression中,我们需要去计算
      $$
      z = w^Tx + b\
      w =
      \begin{bmatrix}
      . \
      . \
      .
      \end{bmatrix}_{n
      1}
      x =
      \begin{bmatrix}
      . \
      . \
      .
      \end{bmatrix}_{n*1},w\in R^{n_x}
      $$
      如果有很多特征,那么他们就是非常大的向量,所以w和x都是R内的nX维向量
  • 在Python或Numpy中,使用z = np.dot(w,x)+b来计算上面的式子,并且比使用for loop来计算快得

P18 More vectorization examples

上一节课学习了如何向量化,如何使用内置函数,避免使用显式for loop,能不使用它就不要去使用。这样可以使得程序运行速度显著加快,让我们再看几个例子。

让v向量转化为u向量

$$
v = \begin{bmatrix}
v_1 \
.\
.\
.\
v_n
\end{bmatrix},
u = \begin{bmatrix}
e^{v_1} \
.\
.\
.\
e^{v_n}
\end{bmatrix}
$$

  • 一种非向量化的实现
1
2
3
4
5
import numpy as np

u = np.zeros((n,1))
for i in range (n):
u[i] = math.exp(v[i])

注释:np.zeros()返回来一个给定形状和类型的用0填充的数组

zeros(shape, dtype=float, order=‘C’)
shape:形状
dtype:数据类型,可选参数,默认numpy.float64
order:可选参数,c代表与c语言类似,行优先;F代表列优先

1
2
3
4
5
import numpy as np

print(np.zeros((2,5)))
# [[0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0.]]
  • 利用numpy实现
1
2
3
4
import numpy as np

u = np.zeros((n,1))
u = np.exp(v)

numpy的其他方法

需要是np.array()创建的vector

  • np.log():返回对所有元素取对数之后的向量

  • np.abs():返回对所有元素取绝对值之后的向量

  • np.maximum():返回向量中所有元素和0相比的最大值

  • v**2:返回对所有元素取平方之后的向量

  • 1/v:返回对所有元素取倒数之后的向量

  • np.dot():函数主要功能有两个——向量点积和矩阵乘法

    • 向量点积:如果处理的是一维数组,则得到的是两数组的內积。

      例1:

      1
      2
      3
      4
      5
      6
      import numpy as np
      x=np.array([0,1,2,3,4])#等价于:x=np.arange(0,5)
      y=x[::-1]
      print x
      print y
      print np.dot(x,y)

      输出:

      1
      2
      3
      [0 1 2 3 4]
      [4 3 2 1 0]
      10

      例2:

      1
      2
      3
      4
      5
      6
      import numpy as np
      x=np.arange(0,5)
      y=np.random.randint(0,10,5)
      print x
      print y
      print np.dot(x,y)

      输出:

      1
      2
      3
      [0 1 2 3 4]
      [5 1 0 9 2]
      36
    • 如果是二维数组(矩阵)之间的运算,则得到的是矩阵积

      1.np.dot(x, y), 当x为二维矩阵,y为一维向量,这时y会转换一维矩阵进行计算

      首先,我们来看一下一维向量和一位矩阵的不同

      例3:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      import numpy as np
      x=np.arange(0,5)
      # 0,10,是随机数的方位,size=(5,1),也就是5维矩阵,且每一维元素数为1个
      y=np.random.randint(0,10,size=(5,1))
      print x
      print y
      # 查看矩阵或者数组的维数
      print "x.shape:"+str(x.shape)
      print "y.shape"+str(y.shape)
      print np.dot(x,y)

      输出:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      [0 1 2 3 4]
      [[3]
      [7]
      [2]
      [8]
      [1]]
      x.shape:(5,)
      y.shape(5, 1)
      [39]

      可以看出一维向量的shape是(5, ), 而一维矩阵的shape是(5, 1), 若两个参数x和y中有一个是矩阵时(包括一维矩阵),dot便进行矩阵乘法运算,同时若有个参数为向量,会自动转换为一维矩阵进行计算。

      2.np.dot(x, y)中,x、y都是二维矩阵,进行矩阵积计算
      np.dot(x, y)两个二维矩阵满足第一个矩阵的列数与第二个矩阵的行数相同,那么可以进行矩阵的乘法,即矩阵积
      例4:

      1
      2
      3
      4
      5
      6
      7
      8
      import numpy as np
      x=np.arange(0,6).reshape(2,3)
      y=np.random.randint(0,10,size=(3,2))
      print x
      print y
      print "x.shape:"+str(x.shape)
      print "y.shape"+str(y.shape)
      print np.dot(x,y)

      输出:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      [[0 1 2]
      [3 4 5]]
      [[7 5]
      [0 7]
      [6 2]]
      x.shape:(2, 3)
      y.shape(3, 2)
      [[12 11]
      [51 53]]

      注意:矩阵积计算不遵循交换律,np.dot(x,y) 和 np.dot(y,x) 得到的结果是不一样的。

简化Logistic Regression代码

image-20211029161735554

image-20211029162353822

这里去掉了一个for loop,一个用来遍历特征的循环,以便加快计算,后面将学习如何去掉遍历样本的循环

P19 Vectorizing Logistic Regression

如何实现在Logistic Regression上面的向量化,以便可以同时处理整个训练集,来实现gradient descent的一步迭代,针对整个训练集的一步迭代,而不需要使用任何显式的for loop。
$$
z^{(i)} = w^Tx^{(i)} + b \
a^{(i)} = \sigma(z^{(i)})\
X = \begin{bmatrix}
.&.&&. \
.&.&&. \
x^{(1)}&x^{(2)}&…… &x^{(m)}\
.&.&&. \
.&.&&. \
\end{bmatrix}_{n_xm}\\
Z = [z{(1)}z{(2)},……z{(m)}] = w^TX+[b,……,b]_{1
m} = [w^TX^{(1)}+b,w^TX^{(2)}+b,……,w^TX^{(m)}+b]\\
A = [a^{(1)},a^{(2)},……,a^{(m)}] = \sigma(Z)
$$
使用numpy:

1
2
3
import numpy as np
Z = np.dot(w.T,x) + b
# 此处b会被自动扩展为一个1*m的行向量,在python中叫做广播(broadcasting)

image-20211029171514174

p20 Vectorizing Logistic Regression’s Gradient Computation

我们将学习如何向量化同时计算m个训练数据的梯度。

回顾:
$$
dz^{(1)} = a^{(1)} - y^{(1)}……dz^{(m)} = a^{(m)} - y^{(m)}\
dz = [dz^{(1)},……,dz^{(m)}]{1*m}
$$
我们已经描述了如何计算:
$$
a^{(i)} = \sigma(z^{(i)})\
X = \begin{bmatrix}
.&.&&. \
.&.&&. \
x^{(1)}&x^{(2)}&…… &x^{(m)}\
.&.&&. \
.&.&&. \
\end{bmatrix}
{n_xm}\\
Z = [z{(1)}z{(2)},……z{(m)}] = w^TX+[b,……,b]_{1
m} = [w^TX^{(1)}+b,w^TX^{(2)}+b,……,w^TX^{(m)}+b]\
A = [a^{(1)},a^{(2)},……,a^{(m)}] = \sigma(Z)\
Y = [y^{(1)},y^{(2)},……,y^{(m)}]{1*m}
$$
那么有:
$$
dZ = [dz^{(1)},dz^{(2)},……,dz^{(m)}]=
\A - Y = [a^{(1)}-y^{(1)},a^{(2)}-y^{(2)},……,a^{(m)}-y^{(m)}]
$$
虽然我们已经去掉了一个for loop(用于遍历每个样本的特征的循环)但是我们仍然还有一个遍历训练集的for loop,以w计算为例:
$$
dw = 0\
dw += x^{(1)}dz\
dw += x^{(2)}dz\
dw += x^{(3)}dz\……\
dw += x^{(m)}dz\
dw /= m\\
db = 0\
db += dz^{(1)}\
db += dz^{(2)}\
db += dz^{(3)}\……\
db += dz^{(m)}\
db /= m
$$
向量化实现:
$$
db = \frac{1}{m}\sum
{i=1}^mdz^{(i)}
$$

1
2
import numpy as mp
db = 1/m*np.sum(dZ)

$$
dw = \frac{1}{m}X(dZ)^T \= \frac{1}{m}\begin{bmatrix}
.&.&&. \
.&.&&. \
x^{(1)}&x^{(2)}&…… &x^{(m)}\
.&.&&. \
.&.&&. \
\end{bmatrix}_{n_x*m}\begin{bmatrix}
dz^(1) \
.\
.\
dz^{(m)}
\end{bmatrix}_{m
1}\=\frac{1}{m}[x^{(1)}dz^{(1)}+……+x^{(m)}dz^{(m)}]
$$

所以,综上,对于一次迭代应该有:
$$
Z = w^Tx+b=np.dot(w.T,X) + b\
A=\sigma(Z)\
dZ = A-Y\
dw = \frac{1}{m}XdZ^T\
db = \frac{1}{m}np*sum(dZ)\
w := w -\alpha dw \
b := b - \alpha db
$$
可以看到我们去掉了所有的for loop,但是,要由于要进行多次迭代,我们仍然需要使用for loop,即:for iteration in range(times)

p21 Python中的广播

广播是一种可以让python代码执行得更快的手段,本节介绍python中的广播是如何运作的。

  • 计算每种食物中,来自carbon的calorie所占的比例,但是不使用for loop

    • 用一行代码对各列求和,可得到四种不同食物的卡路里总量、
    • 第二行代码让每一列都除以对应的和
    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
    import numpy as np

    # 例子1:
    A = np.array([[56.0, 0.0, 4.4, 68.0],
    [1.2, 104.0, 52.0, 8.0],
    [1.8, 135.0, 99.0, 0.9]])
    print(A)

    calorie = A.sum(axis=0)
    # axis 代表每列竖直相加,如果希望水平方向相加,可以令axis = 1
    print(calorie)

    percentage = 100 * A / calorie.reshape(1, 4)
    # 使用矩阵A,将它除以1x4的矩阵,就得到百分比矩阵
    # 这里可以省略reshape,由于它的算法复杂度为O(1),成本很低,所以能写就写,以确保矩阵的正确性
    # A为3x4矩阵,将他除以一个1x4的矩阵,这似乎是不可行的
    print(percentage)

    # 例子2:
    B = np.array([1, 2, 3, 4])
    B += 10
    print(B) # [11 12 13 14]

    # 例子3:
    C = np.array([[2, 0, 35, 5], [54, 54, 4545, 5]])
    print(B + C)
    # [[ 13 12 48 19]
    # [ 65 66 4558 19]]
    # 将一个m*n的矩阵加上一个1*n的矩阵,python会将矩阵复制m次,将它变成一个m*n的矩阵之后再相加

    D = np.array([100, 100]).reshape(2, -1)
    print(C + D)
    # [[ 102 100 135 105]
    # [ 154 154 4645 105]]
    # 这里水平复制了三次
  • python广播中的一些通用规则:

image-20211029200114345

numpy中reshape函数的三种常见相关用法

  • numpy.arange(n).reshape(a, b) 依次生成n个自然数,并且以a行b列的数组形式显示
1
2
3
4
5
np.arange(16).reshape(2,8) #生成16个自然数,以2行8列的形式显示

# Out:
# array([[ 0, 1, 2, 3, 4, 5, 6, 7],
# [ 8, 9, 10, 11, 12, 13, 14, 15]])
  • mat (or array).reshape(c, -1) 必须是矩阵格式或者数组格式,才能使用 .reshape(c, -1) 函数, 表示将此矩阵或者数组重组,以 c行d列的形式表示
1
2
3
arr.shape    # (a,b)
arr.reshape(m,-1) #改变维度为m行、d列 (-1表示列数自动计算,d= a*b /m
arr.reshape(-1,m) #改变维度为d行、m列 (-1表示行数自动计算,d= a*b /m )

-1的作用就在此**: 自动计算d:d=数组或者矩阵里面所有的元素个数/c**, d必须是整数,不然报错)

(reshape(-1, m)即列数固定,行数需要计算)

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
arr=np.arange(16).reshape(2,8)
arr
'''
out:
array([[ 0, 1, 2, 3, 4, 5, 6, 7],
[ 8, 9, 10, 11, 12, 13, 14, 15]])
'''
arr.reshape(4,-1) #将arr变成4行的格式,列数自动计算的(c=4, d=16/4=4)
'''
out:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
'''
arr.reshape(8,-1) #将arr变成8行的格式,列数自动计算的(c=8, d=16/8=2)
'''
out:
array([[ 0, 1],
[ 2, 3],
[ 4, 5],
[ 6, 7],
[ 8, 9],
[10, 11],
[12, 13],
[14, 15]])
'''
arr.reshape(10,-1) #将arr变成10行的格式,列数自动计算的(c=10, d=16/10=1.6 != Int)
'''
out:
ValueError: cannot reshape array of size 16 into shape (10,newaxis)
'''
  • numpy.arange(a,b,c) 从 数字a起, 步长为c, 到b结束,生成array
  • numpy.arange(a,b,c).reshape(m,n) :将array的维度变为m 行 n列。
1
2
3
4
5
6
7
8
9
np.arange(1,12,2)#间隔2生成数组,范围在1到12之间
# Out: array([ 1, 3, 5, 7, 9, 11])
np.arange(1,12,2).reshape(3,2)
'''
Out:
array([[ 1, 3],
[ 5, 7],
[ 9, 11]])
'''

P22 A note on python / numpy vectors

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
import numpy as np

a = np.random.randn(5)
# 生成5个随机的高斯变量,存储在数组a中
print(a.shape)
# (5,),这种结构就是所谓的Python秩为1的数组,它既不是行向量,也不是列向量
# m × n矩阵的秩最大为m和n中的较小者

print(a.T) # a和a的转置看起来相同
print(np.dot(a, a.T)) # 4.676296983566586


# 建议不要使用上述a这种形式的数据结构编写代码,推荐使用以下方式构建向量
a = np.random.randn(5, 1)
print(a)
# [[ 1.06285306]
# [ 0.09125329]
# [-1.03121605]
# [-0.28563524]
# [ 1.55272952]]
print(a.shape) # (5, 1)
print(a.T)
# [[ 1.06285306 0.09125329 -1.03121605 -0.28563524 1.55272952]]
print(np.dot(a, a.T))

# 或者你也可以使用reshape()来重构向量
# assert(a.shape == (5,2))断言,可以用来检查向量是否

p23 Jupyter_ipython(略)

p24 Explanation of logistic regression cost funcion