大家好,这里是你们的第一个作业,即使你之前没有用过python,这个作业也会帮助你熟悉接下来会用到的功能
让我们开始吧!
iPython Notebook是嵌入在网页中的交互式编码环境。 你将在此类中使用iPython笔记本。 你只需要在### START CODE HERE ###和### END CODE HERE ###注释之间编写代码。 编写代码后,通过按“ SHIFT” +“ ENTER”或单击笔记本上部栏中的“Run Cell”来运行该单元块。
我们通常会在注释中说明“(≈X行代码)”,以告诉你需要编写多少行代码。 当然这只是一个粗略的估计,当你的代码更长或更短时,也不用在意。
练习:运行下面的两个单元格中将test设为“ Hello World”,并输出“ Hello World”。
test = "Hello World"
print("test: " + test)
test: Hello World
Numpy是Python中主要的科学计算包。它由一个大型社区维护。在本练习中,你将学习一些关键的numpy函数,例如np.exp,np.log和np.reshape。你需要知道如何使用这些函数去完成将来的练习。
练习:构建一个返回实数x的sigmoid的函数。将math.exp(x)用于指数函数。
提示:
s i g m o i d ( x ) = 1 1 + e − x sigmoid(x)=\frac {1}{1+e^{-x}} sigmoid(x)=1+e−x1有时也称为逻辑函数。它是一种非线性函数,即可用于机器学习(逻辑回归),也能用于深度学习。
要引用特定程序包的函数,可以使用package_name.function()对其进行调用。运行下面的代码查看带有math.exp()的示例。
import math
def basic_sigmoid(x):
s = 1 / (1 + math.exp(-x))
return s
basic_sigmoid(3)
0.9525741268224334
因为函数的输入是实数,所以我们很少在深度学习中使用“math”库。 而深度学习中主要使用的是矩阵和向量,因此numpy更为实用。
x = [1, 2, 3]
basic_sigmoid(x)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_9164\2766938880.py in
1 x = [1, 2, 3]
----> 2 basic_sigmoid(x)
~\AppData\Local\Temp\ipykernel_9164\905362836.py in basic_sigmoid(x)
2
3 def basic_sigmoid(x):
----> 4 s = 1 / (1 + math.exp(-x))
5 return s
TypeError: bad operand type for unary -: 'list'
如果 x = ( x 1 , x 2 , . . . , x n ) x=(x_1,x_2,...,x_n) x=(x1,x2,...,xn)是行向量,则 n p . e x p ( x ) np.exp(x) np.exp(x)会将指数函数应用于x的每个元素。
因此,输入为: n p . e x p ( x ) = ( e 1 x , e 2 x , . . . , e n x ) np.exp(x)=(e^x_1,e^x_2,...,e^x_n) np.exp(x)=(e1x,e2x,...,enx)
import numpy as np
x = np.array([1, 2, 3])
print(np.exp(x))
[ 2.71828183 7.3890561 20.08553692]
如果x是向量,则 s = x + 3 s=x+3 s=x+3或 s = 1 x s=\frac {1}{x} s=x1之类的Python运算将输出与x维度大小相同的向量s。
x = np.array([1, 2, 3])
print(x + 3)
[4 5 6]
练习:使用numpy实现sigmoid函数。
说明:x可以是实数,向量或矩阵。 我们在numpy中使用的表示向量、矩阵等的数据结构称为numpy数组。现阶段你只需了解这些就已足够。
s i g m o i d ( x ) = s i g m o i d ( x 1 x 2 . . . x n ) = ( 1 1 + e − x 1 1 1 + e − x 2 . . . 1 1 + e − x n ) sigmoid(x)=sigmoid\left( \begin{matrix} x_1 \\ x_2 \\ ... \\ x_n \end{matrix} \right)=\left( \begin{matrix} \frac {1}{1+e^{-x_1}} \\ \frac {1}{1+e^{-x_2}} \\ ... \\ \frac {1}{1+e^{-x_n}} \end{matrix} \right) sigmoid(x)=sigmoid⎝ ⎛x1x2...xn⎠ ⎞=⎝ ⎛1+e−x111+e−x21...1+e−xn1⎠ ⎞
For x ∈ R n x \in R^n x∈Rn
import numpy as np
def sigmoid(x):
s = 1 / (1 + np.exp(-x))
return s
x = np.array([1, 2, 3])
sigmoid(x)
array([0.73105858, 0.88079708, 0.95257413])
正如你在教程中所看到的,我们需要计算梯度来使用反向传播优化损失函数。 让我们开始编写第一个梯度函数吧。
练习:创建函数sigmoid_grad()计算sigmoid函数相对于其输入x的梯度。 公式为:
s i g m o i d _ d e r i v a t i v e ( x ) = δ ′ = δ ( x ) ( 1 − δ ( x ) ) sigmoid\_derivative(x)=\delta^\prime=\delta(x)(1-\delta(x)) sigmoid_derivative(x)=δ′=δ(x)(1−δ(x))
我们通常分两步编写此函数代码:
1.将s设为x的sigmoid。 你可能会发现sigmoid(x)函数很方便。
2.计算 δ ′ ( x ) = s ( 1 − s ) \delta^\prime(x)=s(1-s) δ′(x)=s(1−s)
def sigmoid_derivative(x):
s = sigmoid(x)
ds = s * (1 - s)
return ds
x = np.array([1, 2, 3])
print ("sigmoid_derivative(x) = " + str(sigmoid_derivative(x)))
sigmoid_derivative(x) = [0.19661193 0.10499359 0.04517666]
深度学习中两个常用的numpy函数是np.shape和np.reshape()。
例如,在计算机科学中,图像由shape为 ( l e n g t h , h e i g h t , d e p t h = 3 ) (length,height,depth = 3) (length,height,depth=3)的3D数组表示。但是,当你读取图像作为算法的输入时,会将其转换为维度为 ( l e n g t h ∗ h e i g h t ∗ 3 , 1 ) (length * height * 3, 1) (length∗height∗3,1)的向量。换句话说,将3D阵列"展开"或重塑为1D向量。
练习:实现image2vector() ,该输入采用维度为(length, height, 3)的输入,并返回维度为 ( l e n g t h ∗ h e i g h t ∗ 3 , 1 ) (length * height * 3, 1) (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
def image2vector(image):
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]]
我们在机器学习和深度学习中使用的另一种常见技术是对数据进行标准化。 由于归一化后梯度下降的收敛速度更快,通常会表现出更好的效果。 通过归一化,也就是将x更改为 x ∣ ∣ x ∣ ∣ \frac {x}{||x||} ∣∣x∣∣x(将x的每个行向量除以其范数)。
例如:
x = [ 0 3 4 2 6 4 ] x= \begin{bmatrix} 0&3&4 \\ 2&6&4 \end{bmatrix} x=[023644]
then
∣ ∣ x ∣ ∣ = n p . l i n a l g . n o r m ( x , a x i s = 1 , k e e p d i m s = T r u e ) = [ 5 = 0 2 + 3 2 + 4 2 56 = 2 2 + 6 2 + 4 2 ] ||x||=np.linalg.norm(x,axis=1,keepdims=True)= \begin{bmatrix} 5=\sqrt{0^2+3^2+4^2} \\ \sqrt{56}=\sqrt{2^2+6^2+4^2} \end{bmatrix} ∣∣x∣∣=np.linalg.norm(x,axis=1,keepdims=True)=[5=02+32+4256=22+62+42]
and
x n o r m a l i z e d = x ∣ ∣ x ∣ ∣ = [ 0 3 5 4 5 2 56 6 56 4 56 ] x_normalized=\frac {x}{||x||}= \begin{bmatrix} 0&\frac {3}{5}&\frac {4}{5} \\ \frac {2}{\sqrt{56}}&\frac {6}{\sqrt{56}}&\frac {4}{\sqrt{56}} \end{bmatrix} xnormalized=∣∣x∣∣x=[05625356654564]
请注意,你可以划分不同大小的矩阵,以获得更好的效果:这称为broadcasting,我们将在第5部分中学习它。
练习:执行 normalizeRows()来标准化矩阵的行。 将此函数应用于输入矩阵x之后,x的每一行应为单位长度(即长度为1)向量。
def normalizeRows(x):
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,我们现在将讨论它!
在numpy中要理解的一个非常重要的概念是“广播”。 这对于在不同形状的数组之间执行数学运算非常有用。
练习: 使用numpy实现softmax函数。 你可以将softmax理解为算法需要对两个或多个类进行分类时使用的标准化函数。 你将在本专业的第二门课中了解有关softmax的更多信息。
s o r t m a x ( x ) = s o f t m a x ( [ x 1 x 2 . . . x n ] ) = [ e 1 x ∑ j e x j e 2 x ∑ j e x j . . . e n x ∑ j e x j ] sortmax(x)=softmax( \begin{bmatrix} x_1&x_2&...&x_n \end{bmatrix} )= \begin{bmatrix} \frac{e^x_1}{\sum_je^xj}&\frac{e^x_2}{\sum_je^xj}&...&\frac{e^x_n}{\sum_je^xj} \end {bmatrix} sortmax(x)=softmax([x1x2...xn])=[∑jexje1x∑jexje2x...∑jexjenx]
def softmax(x):
x_exp = np.exp(x)
x_sum = np.sum(x_exp, axis = 1, keepdims = True)
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有了很好的理解,并实现了一些将在深度学习中用到的功能。
你需要记住的内容:
在深度学习中,通常需要处理非常大的数据集。 因此,非计算最佳函数可能会成为算法中的巨大瓶颈,并可能使模型运行一段时间。 为了确保代码的高效计算,我们将使用向量化。 例如,尝试区分点/外部/元素乘积之间的区别。
import time
x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]
### CLASSIC DOT PRODUCT OF VECTORS IMPLEMENTATION ###
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 ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### CLASSIC OUTER PRODUCT IMPLEMENTATION ###
tic = time.process_time()
outer = np.zeros((len(x1),len(x2))) # we create a len(x1)*len(x2) matrix with only zeros
for i in range(len(x1)):
for j in range(len(x2)):
outer[i,j] = x1[i]*x2[j]
toc = time.process_time()
print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### CLASSIC ELEMENTWISE IMPLEMENTATION ###
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 ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### CLASSIC GENERAL DOT PRODUCT IMPLEMENTATION ###
W = np.random.rand(3,len(x1)) # Random 3*len(x1) numpy array
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 ----- Computation time = " + str(1000*(toc - tic)) + "ms")
dot = 278
----- Computation time = 0.0ms
outer = [[81. 18. 18. 81. 0. 81. 18. 45. 0. 0. 81. 18. 45. 0. 0.]
[18. 4. 4. 18. 0. 18. 4. 10. 0. 0. 18. 4. 10. 0. 0.]
[45. 10. 10. 45. 0. 45. 10. 25. 0. 0. 45. 10. 25. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[63. 14. 14. 63. 0. 63. 14. 35. 0. 0. 63. 14. 35. 0. 0.]
[45. 10. 10. 45. 0. 45. 10. 25. 0. 0. 45. 10. 25. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[81. 18. 18. 81. 0. 81. 18. 45. 0. 0. 81. 18. 45. 0. 0.]
[18. 4. 4. 18. 0. 18. 4. 10. 0. 0. 18. 4. 10. 0. 0.]
[45. 10. 10. 45. 0. 45. 10. 25. 0. 0. 45. 10. 25. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
----- Computation time = 0.0ms
elementwise multiplication = [81. 4. 10. 0. 0. 63. 10. 0. 0. 0. 81. 4. 25. 0. 0.]
----- Computation time = 0.0ms
gdot = [27.21704242 27.00032077 26.69452292]
----- Computation time = 0.0ms
x1 = [9, 2, 5, 0, 0, 7, 5, 0, 0, 0, 9, 2, 5, 0, 0]
x2 = [9, 2, 2, 9, 0, 9, 2, 5, 0, 0, 9, 2, 5, 0, 0]
### VECTORIZED DOT PRODUCT OF VECTORS ###
tic = time.process_time()
dot = np.dot(x1,x2)
toc = time.process_time()
print ("dot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### VECTORIZED OUTER PRODUCT ###
tic = time.process_time()
outer = np.outer(x1,x2)
toc = time.process_time()
print ("outer = " + str(outer) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### VECTORIZED ELEMENTWISE MULTIPLICATION ###
tic = time.process_time()
mul = np.multiply(x1,x2)
toc = time.process_time()
print ("elementwise multiplication = " + str(mul) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
### VECTORIZED GENERAL DOT PRODUCT ###
tic = time.process_time()
dot = np.dot(W,x1)
toc = time.process_time()
print ("gdot = " + str(dot) + "\n ----- Computation time = " + str(1000*(toc - tic)) + "ms")
dot = 278
----- Computation time = 0.0ms
outer = [[81 18 18 81 0 81 18 45 0 0 81 18 45 0 0]
[18 4 4 18 0 18 4 10 0 0 18 4 10 0 0]
[45 10 10 45 0 45 10 25 0 0 45 10 25 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[63 14 14 63 0 63 14 35 0 0 63 14 35 0 0]
[45 10 10 45 0 45 10 25 0 0 45 10 25 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[81 18 18 81 0 81 18 45 0 0 81 18 45 0 0]
[18 4 4 18 0 18 4 10 0 0 18 4 10 0 0]
[45 10 10 45 0 45 10 25 0 0 45 10 25 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
----- Computation time = 0.0ms
elementwise multiplication = [81 4 10 0 0 63 10 0 0 0 81 4 25 0 0]
----- Computation time = 0.0ms
gdot = [27.21704242 27.00032077 26.69452292]
----- Computation time = 0.0ms
你可能注意到了,向量化的实现更加简洁高效。 对于更大的向量/矩阵,运行时间的差异变得更大。
注意 不同于np.multiply()和* 操作符(相当于Matlab / Octave中的 .*)执行逐元素的乘法,np.dot()执行的是矩阵-矩阵或矩阵向量乘法,
练习:实现L1损失函数的Numpy向量化版本。 我们会发现函数abs(x)(x的绝对值)很有用。
提示:
def L1(yhat, y):
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()函数更好用。 提醒一下,如果 x = [ x 1 , x 2 , . . . , x n ] x=\begin{bmatrix}x_1,x_2,...,x_n\end{bmatrix} x=[x1,x2,...,xn],则np.dot(x,x)= ∑ j = 0 n x j 2 \sum_{j=0}^{n}x_j^2 ∑j=0nxj2。
def L2(yhat, y):
loss = np.dot((y - yhat), (y - yhat).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
你需要记住的内容:
def L2(yhat, y):
loss = np.dot((y - yhat), (y - yhat).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
你需要记住的内容: