目录
Numpy基础
1、使用numpy构建基本函数
1.1、sigmoid function和np.exp()
有时也称为逻辑函数。它是一种非线性函数,即可用于机器学习(逻辑回归),也能用于深度学习。
要引用特定程序包的函数,可以使用package_name.function()对其进行调用。运行下面的代码查看带有math.exp()的示例。
import math
def basic_sigoid(x):
return 1/(1+math.exp(-x))
print(basic_sigoid(3) )
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(x))
x = np.array([1,2,3])
print(sigmoid(x))
# [0.26894142 0.11920292 0.04742587]
因为函数的输入是实数,所以我们很少在深度学习中使用“math”库。 而深度学习中主要使用的是矩阵和向量,因此numpy更为实用。
如果是行向量,则
会将指数函数应用于x的每个元素。 因此,输出为:
def sigmoid_derivative(x):
# 计算S型函数相对于其输入x的梯度(也称为斜率或导数)。 您可以将S型函数的输出存储到变量中,然后使用它来计算梯度。
s = sigmoid(x)
ds = s *(1-s)
# ds - 计算出的梯度
return ds
x =np.array([1,2,3])
print("sigmoid_derivative="+ str(sigmoid_derivative(x)))
# sigmoid_derivative=[0.19661193 0.10499359 0.04517666]
1.2、 Sigmoid gradient
练习:创建函数sigmoid_grad()计算sigmoid函数相对于其输入x的梯度。 公式为:
def sigmoid_derivative(x):
# 计算S型函数相对于其输入x的梯度(也称为斜率或导数)。 您可以将S型函数的输出存储到变量中,然后使用它来计算梯度。
s = sigmoid(x)
ds = s *(1-s)
# ds - 计算出的梯度
return ds
x =np.array([1,2,3])
print("sigmoid_derivative="+ str(sigmoid_derivative(x)))
# sigmoid_derivative=[0.19661193 0.10499359 0.04517666]
1.3、重塑数组
深度学习中两个常用的numpy函数是np.shape和np.reshape()。
-X.shape用于获取矩阵/向量X的shape(维度)。
-X.reshape(...)用于将X重塑为其他尺寸。
例如,在计算机科学中,图像由shape为(length,height,depth=3)的3D数组表示。但是,当你读取图像作为算法的输入时,会将其转换为维度为(length∗height∗3,1)的向量。换句话说,将3D阵列“展开”或重塑为1D向量。
练习:实现image2vector()
,该输入采用维度为(length, height, 3)的输入,并返回维度为(length*height*3, 1)的向量。例如,如果你想将形为(a,b,c)的数组v重塑为维度为(a*b, 3)的向量,则可以执行以下操作:
v = v.reshape((v.shape[0]*v.shape[1], v.shape[2])) # v.shape[0] = a ; v.shape[1] = b ; v.shape[2] = c
-请不要将图像的尺寸硬编码为常数。而是通过image.shape [0]等来查找所需的数量。
def image2vector(image):
# image - 形状(长度,高度,深度)的数字数组
# v - 形状的向量(长 * 高 * 深,1)
v = image.reshape(image.shape[0]*image.shape[1]*image.shape[2],1)
return v
image = np.array([[[ 0.67826139, 0.29380381],
[ 0.90714982, 0.52835647],
[ 0.4215251 , 0.45017551]],
[[ 0.92814219, 0.96677647],
[ 0.85304703, 0.52351845],
[ 0.19981397, 0.27417313]],
[[ 0.60659855, 0.00533165],
[ 0.10820313, 0.49978937],
[ 0.34144279, 0.94630077]]])
print("image2vector(image) = " + str(image2vector(image)))
# image2vector(image) = [[0.67826139]
# [0.29380381]
# [0.90714982]
# [0.52835647]
# [0.4215251 ]
# [0.45017551]
# [0.92814219]
# [0.96677647]
# [0.85304703]
# [0.52351845]
# [0.19981397]
# [0.27417313]
# [0.60659855]
# [0.00533165]
# [0.10820313]
# [0.49978937]
# [0.34144279]
# [0.94630077]]
1.4、 行标准化
我们在机器学习和深度学习中使用的另一种常见技术是对数据进行标准化。 由于归一化后梯度下降的收敛速度更快,通常会表现出更好的效果。 通过归一化,也就是将x更改为(将x的每个行向量除以其范数)。
例如: 则 :
并且:
请注意,你可以划分不同大小的矩阵,以获得更好的效果:这称为broadcasting,我们将在第5部分中学习它。
1.4.1 、 np.linalg.norm()
1、linalg=linear(线性)+algebra(代数),norm则表示范数。
x_norm=np.linalg.norm(x, ord=None, axis=None, keepdims=False)
①x: 表示矩阵(也可以是一维)
②ord:范数类型
矩阵的范数:
ord=1:列和的最大值
ord=2:|λE-ATA|=0,求特征值,然后求最大特征值得算术平方根(matlab在线版,计算ans=ATA,[x,y]=eig(ans),sqrt(y),x是特征向量,y是特征值)
ord=∞:行和的最大值
ord=None:默认情况下,是求整体的矩阵元素平方和,再开根号。(没仔细看,以为默认情况下就是矩阵的二范数,修正一下,默认情况下是求整个矩阵元素平方和再开根号)
③axis:处理类型
axis=1表示按行向量处理,求多个行向量的范数
axis=0表示按列向量处理,求多个列向量的范数
axis=None表示矩阵范数。
④keepding:是否保持矩阵的二维特性
True表示保持矩阵的二维特性,False相反
向量的范数:
练习:执行 normalizeRows()来标准化矩阵的行。 将此函数应用于输入矩阵x之后,x的每一行应为单位长度(即长度为1)向量。
def normalizeRows(x):
# 实现一个对矩阵x的每一行进行规范化(具有单位长度)的函数。
# 输入: x - 形状为(n,m)的numpy矩阵
# 输出:x - 规范化(按行)的numpy矩阵。
x_norm = np.linalg.norm(x,axis=1,keepdims=True)
x=x/x_norm
return x
x = np.array([
[0, 3, 4],
[1, 6, 4]])
print("normalizeRows(x) = " + str(normalizeRows(x)))
# normalizeRows(x) = [[0. 0.6 0.8 ]
# [0.13736056 0.82416338 0.54944226]]
注意:
在normalizeRows()中,你可以尝试print查看 x_norm和x的维度,然后重新运行练习cell。 你会发现它们具有不同的w维度。 鉴于x_norm采用x的每一行的范数,这是正常的。 因此,x_norm具有相同的行数,但只有1列。 那么,当你将x除以x_norm时,它是如何工作的? 这就是所谓的广播broadcasting,我们现在将讨论它!
1.5、广播和softmax函数
在numpy中要理解的一个非常重要的概念是“广播”。 这对于在不同形状的数组之间执行数学运算非常有用。 有关广播的完整详细信息,你可以阅读官方的broadcasting documentation.
练习: 使用numpy实现softmax函数。 你可以将softmax理解为算法需要对两个或多个类进行分类时使用的标准化函数。 你将在本专业的第二门课中了解有关softmax的更多信息。
操作指南:
对于矩阵 是表示矩阵中第i行,第j列 的元素x 我们可以得到:
# 1.5- 广播和softmax函数
def softmax(x):
# 计算输入x的每一行的softmax。 用于行向量以及形状(n,m)的矩阵
# x - 形状为(n,m)的numpy矩阵
# 返回值: s - numpy矩阵,等于形状(n,m)的x的softmax
# 将exp()逐个元素应用于x。使用np.exp(...)。
x_exp = np.exp(x)
# 创建一个向量x_sum,该向量求和x_exp的每一行。使用np.sum(...,axis = 1,keepdims = True)。
x_sum = np.sum(x_exp,axis=1,keepdims=True)
# 通过将x_exp除以x_sum来计算softmax(x)。它应该自动使用numpy广播。
s = x_exp / x_sum
return s
x = np.array([
[9, 2, 5, 0, 0],
[7, 5, 0, 0 ,0]])
print("softmax(x) = " + str(softmax(x)))
# softmax(x) = [[9.80897665e-01 8.94462891e-04 1.79657674e-02 1.21052389e-04
# 1.21052389e-04]
# [8.78679856e-01 1.18916387e-01 8.01252314e-04 8.01252314e-04
# 8.01252314e-04]]
注意:
-如果你在上方输出 x_exp,x_sum和s的维度并重新运行练习单元,则会看到x_sum的纬度为(2,1),而x_exp和s的维度为(2,5)。 x_exp/x_sum 可以使用python广播。
恭喜你! 你现在已经对python numpy有了很好的理解,并实现了一些将在深度学习中用到的功能。
需要记住的内容:
-np.exp(x)适用于任何np.array x并将指数函数应用于每个坐标
-sigmoid函数及其梯度
-image2vector通常用于深度学习
-np.reshape被广泛使用。 保持矩阵/向量尺寸不变有助于我们消除许多错误。
-numpy具有高效的内置功能
-broadcasting非常有用
2、向量化
在深度学习中,通常需要处理非常大的数据集。 因此,非计算最佳函数可能会成为算法中的巨大瓶颈,并可能使模型运行一段时间。 为了确保代码的高效计算,我们将使用向量化。 例如,尝试区分点/外部/元素乘积之间的区别。
普通数乘与向量化后效率对比:
import time
x1 = np.random.rand(1000000)
x2 = np.random.rand(1000000)
# 矢量实施的经典点积
tic = time.process_time()
dot = 0
for i in range(len(x1)):
dot += x1[i]*x2[i]
toc = time.process_time()
print("dot="+str(dot)+"\n------dot所用时间="+str((toc-tic)*1000)+"ms")
tic = time.process_time()
dot = np.dot(x1,x2)
toc = time.process_time()
print ("dot = " + str(dot) + "\n ----- dot向量化后所耗时间 = " + str(1000*(toc - tic)) + "ms")
x1 = np.random.rand(10000)
x2 = np.random.rand(10000)
tic = time.process_time()
#创建大小为(len(x1),len(x2)) 的零矩阵
size = len(x1)
outer = np.zeros((size,size))
for i in range(size):
for j in range(size):
outer[i,j] = x1[i]*x2[j]
toc = time.process_time()
print("outer="+str(outer)+"\n----outer所耗时间="+str(1000*(toc-tic))+"ms")
tic = time.process_time()
outer = np.outer(x1,x2)
toc = time.process_time()
print("outer="+str(outer)+"\n-----outer向量化后所耗时间=" + str(1000*(toc - tic)) + "ms" )
x1 = np.random.rand(1000000)
x2 = np.random.rand(1000000)
tic = time.process_time()
mul = np.zeros(len(x1))
for i in range(len(x1)):
mul[i] = x1[i]*x2[i]
toc = time.process_time()
print ("elementwise multiplication = " + str(mul) + "\n ----- elementwise所耗时间 = " + str(1000*(toc - tic)) + "ms")
tic = time.process_time()
mul = np.multiply(x1,x2)
toc = time.process_time()
print("mul="+str(mul)+"\n-----elementwise向量化后所耗时间=" + str(1000*(toc - tic)) + "ms" )
W = np.random.rand(3,len(x1))
tic = time.process_time()
gdot = np.zeros(W.shape[0])
for i in range(W.shape[0]):
for j in range(len(x1)):
gdot[i] += W[i,j]*x1[j]
toc = time.process_time()
print ("gdot = " + str(gdot) + "\n ----- gdot所耗时间 = " + str(1000*(toc - tic)) + "ms")
tic = time.process_time()
dot = np.multiply(W,x1)
toc = time.process_time()
print("gdot="+str(dot)+"\n-----gdot向量化后所耗时间=" + str(1000*(toc - tic)) + "ms" )
# dot=250084.76006018423
# ------dot所用时间=750.0ms
# dot = 250084.76006018213
# ----- dot向量化后所耗时间 = 31.25ms
# outer=[[0.02071126 0.09555269 0.14442706 ... 0.11184553 0.08170454 0.13806498]
# [0.08205103 0.37854758 0.57217136 ... 0.44309433 0.32368589 0.54696697]
# [0.05765745 0.26600627 0.40206616 ... 0.31136343 0.22745483 0.38435497]
# ...
# [0.0781874 0.36072247 0.54522887 ... 0.42222984 0.30844412 0.5212113 ]
# [0.09412523 0.43425265 0.6563691 ... 0.50829777 0.3713178 0.62745576]
# [0.08109653 0.37414392 0.56551528 ... 0.43793981 0.31992044 0.54060409]]
# ----outer所耗时间=73968.75ms
# outer=[[0.02071126 0.09555269 0.14442706 ... 0.11184553 0.08170454 0.13806498]
# [0.08205103 0.37854758 0.57217136 ... 0.44309433 0.32368589 0.54696697]
# [0.05765745 0.26600627 0.40206616 ... 0.31136343 0.22745483 0.38435497]
# ...
# [0.0781874 0.36072247 0.54522887 ... 0.42222984 0.30844412 0.5212113 ]
# [0.09412523 0.43425265 0.6563691 ... 0.50829777 0.3713178 0.62745576]
# [0.08109653 0.37414392 0.56551528 ... 0.43793981 0.31992044 0.54060409]]
# -----outer向量化后所耗时间=484.375ms
# elementwise multiplication = [0.03193241 0.06713531 0.5331545 ... 0.00178877 0.68138161 0.07755435]
# ----- elementwise所耗时间 = 796.875ms
# mul=[0.03193241 0.06713531 0.5331545 ... 0.00178877 0.68138161 0.07755435]
# -----elementwise向量化后所耗时间=0.0ms
# gdot = [250033.57823197 249767.3662673 249430.81196528]
# ----- gdot所耗时间 = 3093.75ms
# gdot=[[3.44156976e-02 1.74716384e-01 8.92561385e-02 ... 3.02648090e-04
# 2.17920834e-01 3.53817508e-01]
# [1.84960864e-02 1.78308842e-01 5.91351208e-01 ... 2.92963298e-03
# 5.09580532e-01 3.10458708e-01]
# [1.89789143e-02 3.41281921e-01 2.54714634e-01 ... 1.31974216e-02
# 4.39869158e-01 1.22641198e-01]]
# -----gdot向量化后所耗时间=15.625ms
你可能注意到了,向量化的实现更加简洁高效。 对于更大的向量/矩阵,运行时间的差异变得更大。
注意 不同于np.multiply()
和*
操作符(相当于Matlab / Octave中的 .*
)执行逐元素的乘法,np.dot()
执行的是矩阵-矩阵或矩阵向量乘法,
2.1实现L1和L2损失函数
练习:实现L1损失函数的Numpy向量化版本。 我们会发现函数abs(x)(x的绝对值)很有用。
提示:
-损失函数用于评估模型的性能。 损失越大,预测(y^) 与真实值(y)的差异也就越大。 在深度学习中,我们使用诸如Gradient Descent之类的优化算法来训练模型并最大程度地降低成本。
-L1损失函数定义为:
def L1(yhat,y):
# yhat - 大小为m的向量(预测的标签)
# y - 大小为m的向量(真标签)
# 返回值: loss - 上面定义的L1损失函数的值
loss = np.sum(np.abs(y - yhat))
return loss
yhat = np.array([.9, 0.2, 0.1, .4, .9])
y = np.array([1, 0, 0, 1, 1])
print("L1 = " + str(L1(yhat,y)))
# L1 = 1.1
练习:实现L2损失函数的Numpy向量化版本。 有好几种方法可以实现L2损失函数,但是还是np.dot()函数更好用。 提醒一下,如果,则
np.dot(x,x)
= 。
L2损失函数定义为:
def L2(yhat, y):
"""
yhat-大小为m的向量(预测的标签)
y-大小为m的向量(真标签)
返回值: loss-上面定义的L2损失函数的值
"""
loss = np.dot((y - yhat), (y - yhat).T) # .T表示转置
return loss
yhat = np.array([.9, 0.2, 0.1, .4, .9])
y = np.array([1, 0, 0, 1, 1])
print("L2 = " + str(L2(yhat,y)))
# L2 = 0.43
祝贺你完成此教程练习。 我们希望这个小小的热身运动可以帮助你以后的工作,那将更加令人兴奋和有趣!
你需要记住的内容:
-向量化在深度学习中非常重要, 它保证了计算的效率和清晰度。
-了解L1和L2损失函数。
-掌握诸多numpy函数,例如np.sum,np.dot,np.multiply,np.maximum等。