《深度学习入门》笔记
此文档已更新完毕
你可以通过右下角的齿轮切换夜间模式和阅读模式
《深度学习入门:基于Python的理论与实现》由日本作者斋藤康毅撰写,2020年3月经人民邮电出版社引入中国出版,是一部面向初学者的深度学习技术专著 。该书以Python3为核心工具,旨在通过数学理论与代码实践结合的方式,帮助读者系统性掌握深度学习基础知识。
Markdown学习
文档
数学
- 乘法 $x\times y $
- 除法 $y\div x $
- 上标 $x^2 $
- 下标
- 分数
- 开方
- 积分 $$
- 极限
- 块公式格式 ,\displaystyle可以让行内公式显示多行,如果不用这个指令,显示出来的公式会被压缩成单行。
- 积分
Anaconda学习
conda操作
- conda信息
1
conda info
环境操作
- 新建环境
1
2
3conda create -n [环境名] python=[版本]
conda create -n myenv
conda create -n myenv python=3.11.4 - 激活环境
1
2conda activate [环境名]
conda activate myenv - 退出环境
1
conda deactivate
- 查看已创建的环境
1
conda env list
- 卸载环境
-n其实就是–name
-v其实就是–version1
2conda env remove -n [name]
conda env remove -n myenv
包操作
- 安装包(在环境中)
1
2
3
4conda install [name]
conda install numpy
conda install pandas=1.3.5
conda install jupyter matplotlib scikit-learn (用空格分隔) - 更新包(在环境中)
1
2conda update numpy
conda update --all - 卸载包(在环境中)
1
2conda uninstall [name]
conda uninstall numpy - 查看已下载的包
全局环境就查看pkgs的包
激活环境就查看当前环境的包1
2conda list
conda list -n myenv - 搜索包
1
conda search numpy
- 清除包
此命令可清除未使用的pkgs中的包,即清理缓存
–all可以清理缓存1
2conda clean --packages
conda clean --all
Python学习
Python本体
- 类的操作
创建类的方法的时候,在各方法中的第一个参数self(表示自己)是python的一个特色
特殊的方法__init__叫做构造函数,会在生成类的实例时被调用一次1
2
3
4
5
6
7
8
9
10
11
12
13
14class Man:
def __init__(self, name):
self.name = name
print("Initialized!")
def hello(self):
print("Hello "+self.name+"!")
def goodbye(self):
print("Goodbye "+self.name+"!")
m = Man("David")
m.hello()
m.goodbye()
此处定义了新类Man,并用Man生成了实例m
类Man的构造函数会接收参数name,然后会用这个参数初始化实例变量self.name。实例变量是存储在各个实例的变量。可以在self后面添加属性名来生成或访问实例变量
1
2
m.age = 21
print(m.age)
可以新增实例的属性,而不会报错
numpy学习
- numpy数组
- 可以使用
np.array([列表])来新建数组,类为numpy.ndarray
数组内数据只能为同一个类型,而python的list可以有多个类型
内含一个浮点数则为浮点型数组 - 同化定理: 往浮点数数组里插入整形数,整形数会变为浮点数
往整型数数组里插入浮点数,浮点数会被截断100.9->1001
2
3
4
5
6import numpy as np
x = np.array([1,2.2,3,4])
y = x / 2
print(type(x))
>><class 'numpy.ndarray'>
- 数组的运算
- element-wise运算
1
2
3
4x = np.array([1,2,3])
y = np.array([4,5,6])
z = x + y
-
数组类型转换
使用.astype()方法可以改变数组类型,但不改变原数组的类型1
2
3
4
5
6
7
8import numpy as np
x = np.array([1,2,3,4,5])
y = x.astype(float)
z = x.astype(bool)
print(x.dtype,y.dtype,z.dtype)
>int64 float64 bool共同改变定理: 只有当数组内全部元素都改变时,数组的数据类型才会改变
-
广播运算
当向量和行列矩阵与矩阵做运算时,会被广播
有可能会适配不了1
2
3
4
5
6
7
8
9import numpy as np
a = np.array([[1,2],[3,4]])
b = np.array([10,20])
c = a*b
print(c)
>[[10 40]
[30 80]]1
2
3arr1 = np.arange(3).reshape((3,1))
arr2 = np.ones((3,5))
arr3 = arr1*arr2 -
N维数组
1
2
3
4
5
6
7
8
9
10
11import numpy as np
a = np.array([[1,2],[3,4]])
print(type(a))
print(a.shape)
print(a.dtype)
><class 'numpy.ndarray'>
>(2, 2)
>int64 -
reshape方法
reshape方法可以用于同维数组改变形状,也可以用于改变维度1
2
3
4arr1 = np.arange(10)
arr2 = arr1.reshape(5,2)
arr3 = arr2.reshape(-1)
print(arr1,arr2,arr3) -
arange函数
arange是指array_range,可以用来创建递增的数组1
2
3arr1 = np.arange(10.0) #0.,1.,2.,3.,4.,5.,6.,7.,8.,9.
arr2 = np.arange(10,20) #10,11,12,13,14,15,16,17,18,19
arr2 = np.arange(1,21,2) #1,3,5,7,9,11,13,15,17,19 -
ones和zeros函数
shape的class为tuple(元组)
注意ones和zeros生成的数组都为浮点数数组1
2arr1 = np.ones(4)
arr2 = np.zeros((1,2)) -
随机数组
1
2
3
4
5arr1 = np.random.random((2,5)) #参数是形状,生成0-1均匀分布的浮点型数组。可以通过线性变换来实现任意范围
arr2 = np.random.randint(10,100,(2,5))#范围和形状,这个用来生成整数的随机数组
arr3 = np.random.normal(0,1,(2,3))#均值,标准差,形状,正态分布
arr4 = np.random.randn((2,3))#形状,标准正态分布(即均值=0,标准差=1的正态分布)
arr5 = np.random.choice([1,2,3,4],size = 3,replace = False)#replace为false时不会重复出现 -
矩阵切片
用这个方法可以获得子式1
2
3
4
5
6
7arr1 = np.arange(1,21).reshape(4,5)
arr2 = arr1[1:3,1:-1]#行切片,列切片
arr3 = arr1[::3,::2]#跳跃采样
arr4 = arr1[1,:]#采样第一行,列全都要,等于arr1[1],并且输出是一维数组
arr5 = arr1[,2] #注意,这是错误的,正确的采样列的方法参考下面两个
arr6 = arr1[:,2]#输出的是一维数组
arr7 = arr1[:,2:3] #可以写两个冒号,也可以写一个,此时就输出的是矩阵了注意:数组切片仅是视图 修改arr2-arr7时,原数组arr1也会被修改
-
copy方法
1
2arr1 = arange(10)
arr2 = arr1[:3].copy()copy会创建新数组,这时修改arr2就不会修改arr1了
-
数组相等仅为绑定
1
2arr1 = arange(10)
arr2 = arr1此时修改arr2,arr1会跟着变
如果不想跟着变,可以使用copy方法1
2arr1 = arange(10)
arr2 = arr1.copy() -
矩阵的转置
转置方法仅仅对 矩阵(二维数组) 有效1
2
3arr1 = np.arange(1,4)
arr2 = arr1.reshape((1,-1))
arr3 = arr2.T -
矩阵的翻转
使用np.flipud()和np.fliplr()进行翻转操作
对于一维数组,只能使用np.flipud() -
数组的拼接
可以使用np.concatenate([arr1,arr2,...])函数进行数组的拼接1
2
3
4
5arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([[7,8,9],[10,11,12]])
arr3 = np.concatenate([arr1,arr2]) #默认axis=0,此时变为4行3列数组
arr4 = np.concatenate([arr1,arr2],axis = 1) #此时变为2行6列数组 1,2,3,7,8,9矩阵和向量不能拼接,必须将向量升维
-
数组分割
1
2
3
4
5
6
7arr = np.arange(10,100,10)
arr1,arr2,arr3 = arr.split(arr,[2,6])#第二个参数为分割点
#会被分割为:arr[0],arr[1]
#arr[2],arr[3],arr[4],arr[5]
#arr[6],arr[7],arr[8] -
矩阵乘积
混有向量的时候,结果一定为向量
使用np.dot()方法- 两个向量相乘 在numpy中是合法的,最后会输出标量,由于混有向量,因此输出为一个向量,而不是(5,1)*(1,5)=(5,5)的矩阵
1
2
3a = np.arange(5)
b = np.arange(5)
c = np.dot(a,b) #class:numpy.int64- 向量和矩阵相乘
1
2
3a = np.arange(5) #1x5
b = np.arange(21).reshape(5,4) #5x4
c = np.dot(a,b) #1x4- 矩阵相乘
正常运算即可
-
数学函数
- 绝对值函数:
np.abs(),参数可以是多维数组 - 三角函数:
np.sin()np.cos()np.tan() - 指数函数:
np.exp() - 对数函数:
np.log()np.log2()np.log10()
1
2
3
4x = arange(0,10.1,0.1)
y1 = np.log(x) #y1 = ln(x)
y2 = np.log(x)/np.log(2) #y2=log2(x) 换底公式
y3 = np.log(x)/np.log(10) #y3=log10(x) - 绝对值函数:
-
聚合函数
np.max()
1
2
3
4arr = np.random.random((2,3)) #arr为(2,3)的0-1随机数组
arr1 = np.max(arr,axis = 0) #arr1为(1,3)
arr2 = np.max(arr,axis = 1) #arr2为(1,2)
arr3 = np.max(arr) #arr3 为标量- 求和函数
np.sum()求积函数np.prod()
1
2
3
4arr = np.arange(10).reshape(2,5)
arr1 = np.sum(arr,axis = 0) #(1,5)
arr2 = np.sum(arr,axis = 1) #(1,2)
arr3 = np.sum(arr) #标量- 均值函数
np.mean()标准差函数np.std() - 如果有缺失值,可以在函数名前加nan,np.nansum(),可以忽略缺失值
-
布尔型数组
可用运算符>,>=,==,!=,<,<=,&,|,~ 。当布尔型数组作为同纬度的数组的索引时,可以用来筛选数组,结果为一维向量- 当
np.sum()用在布尔型数组上时,作用为统计True的数量
1
2
3arr = np.random.normal(0,1,10000)
result = np.sum(np.abs(arr) < 1)
#其中np.abs可以换成(arr>-1)&(arr<1)np.any():只要布尔型数组有一个True,就返回True
1
2
3arr1 = np.arange(1,10)
arr2 = np.flipud(arr1)
print(np.any(arr1 == arr2))np.all():只有布尔型数组所有元素都是True,返回True
- 当
-
np.where()实际上也利用了布尔型数组1
2
3arr = np.random.normal(500,70,1000)
print(np.where(arr>650)[0])
print(np.where(arr == np.max(arr))[0])#输出是一个元组,第一个元素是位置列表,第二个元素数据类型 -
np.argmax() 可以获取数组的最大值的索引
-
花式索引
1
2
3arr1 = np.arange(1,17).reshape((4,-1))
print(arr1[[0,1],[0,1]]) #输出为一维向量
Matplotlib学习
- 基本的画图
1
2
3
4
5
6
7
8
9
10
11import numpy as np
import matplotlib.pyplot as plt
date_list = [1,2,3,4]
stock1 = [4,8,2,6]
stock2 = [10,12,5,3]
Fig1 = plt.figure()
plt.plot(date_list,stock1)
plt.plot(date_list,stock2)
plt.show()
深度学习
感知机
- AND门、OR门和NAND门
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
29def AND(x1,x2):
x = np.array([x1,x2])
w = np.array([0.5,0.5])
b = -0.7
tmp = x*w + b
if tmp <= 0:
return 0
elif tmp > 0 :
return 1
def OR(x1,x2):
x = np.array([x1,x2])
w = np.array([0.5,0.5])
b = -0.3
tmp = x*w + b
if tmp <= 0:
return 0
elif tmp > 0 :
return 1
def NAND(x1,x2):
x = np.array([x1,x2])
w = np.array([-0.5,-0.5])
b = 0.7
tmp = x*w + b
if tmp <= 0:
return 0
elif tmp > 0 :
return 1 - XOR门
1
2
3
4
5def XOR(x1,x2):
s1 = NAND(x1,x2)
s2 = OR(x1,x2)
y = AND(s1,s2)
return y
神经网络
一般而言,输出层的激活函数:回归问题用恒等函数,分类问题用softmax函数
-
sigmoid函数
sigmoid激活函数是一个平滑的曲线,形状如下图
特性为输入小时,输出接近0。输入大时,输出接近1。且值域为(0,1)

1
2def sigmoid(x):
return 1 / (1 + np.exp(-x)) -
LeRU函数
1
2def ReLU(x):
return np.maximum(0,x) #np.maximum会比较并输出较大的那个值 -
一个2,3,2,2的三层神经网络的实现
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
28def init_network(): #初始化网络
network = {}
network['W1'] = np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]])
network['b1'] = np.array([0.1,0.2,0.3])
network['W2'] = np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]])
network['b2'] = np.array([0.1,0.2])
network['W3'] = np.array([[0.1,0.3],[0.2,0.4]])
network['b3'] = np.array([0.1,0.2])
return network
def forward(network,x): #前向函数
W1,W2,W3 = network['W1'],network['W2'],network['W3']
b1,b2,b3 = network['b1'],network['b2'],network['b3']
a1 = np.dot(x,W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1,W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2,W3) + b3
z3 = identity_function(a3)
y = z3
return y
network = init_network()
x = np.array([1,0.5])
y = forward(network,x)
print(y) -
softmax激活函数
1
2
3
4def softmax(x):
exp_x = np.exp(x)
sum_exp_x = np.sum(exp_x)
return (exp_x/sum_exp_x) -
改进softmax函数(防止溢出问题)
经过softmax后的数据总和为1,因此可以把softmax函数的输出解释为“概率”1
2
3
4
5def softmax(x):
c = np.max(x)
exp_x = np.exp(x - c)
sum_exp_x = np.sum(exp_x)
return (exp_x/sum_exp_x) -
损失函数
- 均方误差(Mean Squared Error)
1
2
3
4
5def mean_squared_error(y,t):
return 0.5 * np.sum((y-t)**2)
y = [0.2,0.3,0.5]
t = [0,0,1] #one-hot表示- 交叉熵误差(Cross Entropy Error)
1
2
3
4def cross_entropy_error(y,t):
delta = 1e-7
return -np.sum(t*np.log(y+delta))
#delta 防止np.log(0) = -inf1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#mini-batch的交叉熵误差
def cross_entropy_error_onehot(y,t):
delta = 1e-7
if y.ndim == 1:
y = y.reshape((1,-1)) #将一维向量变为二维的一行矩阵
t = t.reshape((1,-1))
batch_size = y.shape[0]
return -np.sum(t*np.log(y+delta)) / batch_size
def cross_entropy_error_notonehot(y,t):
delta = 1e-7
if y.ndim == 1:
y = y.reshape((1,-1))
t = t.reshape((1,-1))
batch_size = y.shape[0]
return -np.sum(np.log(y[np.arange(batch_size),t] + delta))/batch_size -
数值微分
1
2
3def numerical_diff(f,x):#使用中心差分
h = 1e-4
return (f(x+h)-f(x-h))/(2*h) -
数值梯度
1
2
3
4
5
6
7
8
9
10
11
12
13
14def numerical_grad(f,x):
h = 1e-4
x = x.astype(float)
#当x[i]=x_val+h时,由于int型数组不能接纳浮点数,所以需要将x改变为float型
grad = np.zeros(x.shape)
for i in range(x.size):
x_val = x[i]
x[i] = x_val + h
f_1 = f(x)
x[i] = x_val - h
f_2 = f(x)
x[i] = x_val
grad[i] = (f_1-f_2)/(2*h)
return grad -
梯度下降法
1
2
3
4
5
6
7
8def grad_desent(f,init_x,lr=0.01,step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_grad(f,x)
x = x - lr * grad
return x -
乘法层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class MulLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self,x,y):
self.x = x
self.y = y
return x*y
def backward(self,out_direct):
x_direct = out_direct*self.y
y_direct = out_direct*self.x
return x_direct,y_direct -
加法层
1
2
3
4
5
6
7
8
9
10
11
12class addLayer:
def __init__(self):
self.x = None
self.y = None
def forward(self,x,y):
self.x = x
self.y = y
return x+y
def backward(self,dout):
return dout,dout -
激活函数层
- ReLU函数
x,(x>0)\
0,(x<=0)\
\end{cases}
\frac{\partial h(x)}{\partial x}=\begin{cases}
1,(x>0)\
0,(x<=0)\
\end{cases}
1
2
3
4
5
6
7
8
9
10
11
12
13
14class ReLULayer:
def __init__(self):
self.mask = None
def forward(self,x):
self.mask = (x<=0)
out = x.copy()
out[self.mask] = 0
return out
def backward(self,dout):
dout[self.mask] = 0
return dout1
2
3
4
5
6
7
8
9
10
11
12class sigmoidLayer:
def __init__(self):
self.out = None
def forward(self,x):
out = 1 / (1 + np.exp(-x))
self.out = out
return out
def backward(self,dout):
dx = dout * self.out * (1.0 - self.out)
return dx1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Affine:
def __init__(self, W, b):
self.W = W
self.b = b
self.x = None
self.dW = None
self.db = None
def forward(self, x):
self.x = x
out = np.dot(self.x, self.W) + self.b
return out
def backward(self, dout):
dx = np.dot(dout, self.W.T)
self.dW = np.dot(self.x.T, dout)
self.db = np.sum(dout, axis = 0)
return dx1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class softmaxWithLoss:
def __init__(self):
self.loss = None
self.y = None
self.x = None
def forward(self,x,t):
self.t = t
self.y = softmax(x)
self.loss = cross_entropy_error_onehot(y,t)
return self.loss
def backward(self, dout = 1):
batch_size = self.t.shape[0]
dx = (self.y - self.t) / batch_size
return dx1
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
57class newTwoLayerNet:
def __init__(self,inputSize,outputSize,hiddenSize,weightInitStd = 0.1):
self.param = {}
self.param['W1'] = weightInitStd * np.random.randn((inputSize,hiddenSize))
self.param['b1'] = weightInitStd * np.zeros((hiddenSize))
self.param['W2'] = weightInitStd * np.random.randn((hiddenSize,outputSize))
self.param['b2'] = weightInitStd * np.zeros((outputSize))
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.param['W1'],self.param['b1'])
self.layers['Relu1'] = ReLULayer()
self.layers['Affine2'] = Affine(self.param['W2'],self.param['b2'])
self.lastLayer = softmaxWithLoss()
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x #没有经过lastLayer,及x为得分
def loss(self, x, t):
y = self.predict(x)
return self.lastLayer.forward(y, t)
def accuracy(self, x, t):
y = self.predict(x)
if y.ndim != 1:
y = np.argmax(y, axis = 1)
elif y.ndim == 1:
y = np.argmax(y)
if t.ndim != 1:
t = np.argmax(t, axis = 1)
elif t.ndim == 1:
t = np.argmax(t)
return np.sum(y == t) / float(x.shape[0])
def gradient(self, x, t):
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values()).reverse()
for layer in layers:
layer.backward(dout)
grads = {}
grads['W1'] = self.layers['Affine1'].dW
grads['b1'] = self.layers['Affine1'].db
grads['W2'] = self.layers['Affine2'].dW
grads['b2'] = self.layers['Affine2'].db
return grads
1
2
3
4
5
6
7
class SGD:
def __init__(self, lr = 0.1):
self.lr = lr
def update(self,params,grads):
for key in params.keys
params[key] -= self.lr * grads[key]
- Momentum法
Momentum是动量的意思表示要更新的权重参数,表示损失函数关于W的梯度,表示学习率。对应物理上的速度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Momentum:
def __init__(self, lr = 0.01, momentum = 0.9):
self.lr = lr
self.momentum = momentum
self.v = None
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
params[key] += self.v[key] - AdaGrad
Ada来自Adaptive,意为“适当的”,采用学习率衰减的思想。1
2
3
4
5
6
7
8
9
10
11
12
13class AdaGrad:
def __init__(self, lr = 0.01):
self.lr = lr
self.h = None
def update(self, params, grads):
if self.h is None:
for key,val in params.items():
self.h[key] = np.zeros_like(val)
for key in params.keys():
self.h[key] = self.h[key] + grads[key] * grads[key]
params[key] -= params[key] - self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
6.2 权重的初始值
- 使用Sigmoid激活函数
推荐使用Xavier初始值,使用标准差为的高斯分布 - 使用ReLU激活函数
推荐使用Kaiming He初始值,使用标准差为的高斯分布
6.3 Batch Normalization
Batch Norm就是以进行学习的Mini-Batch为单位,按Mini-Batch进行正规化。就是使数据分布的均值为0,方差为1的正规化。
这里对mini-batch的m个输入数据求均值和方差。
然后在对输入数据进行均值为0,方差为1的正规化。其中是一个微小值(比如1e-7),是为了防止出现除以0的情况。
6.4 正则化
解决过拟合这个问题
-
过拟合发生的主要原因:
- 模型拥有大量参数、表现力强
- 训练数据少
-
权值衰减
-
DropOut方法
6.5 超参数的验证
第七章 卷积神经网络
7.1 整体结构
7.2 卷积层
-
卷积运算
卷积运算相当于图像处理中的“滤波器运算”
7.3 池化层
7.4 卷积层和池化层的运用
- 基于im2col的展开
im2col是指image to column,就是从图像到一行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
30def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
"""
Parameters
----------
input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据
filter_h : 滤波器的高
filter_w : 滤波器的长
stride : 步幅
pad : 填充
Returns
-------
col : 2维数组
"""
N, C, H, W = input_data.shape
out_h = (H + 2*pad - filter_h)//stride + 1
out_w = (W + 2*pad - filter_w)//stride + 1
img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
for y in range(filter_h):
y_max = y + stride*out_h
for x in range(filter_w):
x_max = x + stride*out_w
col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]
col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
return col





