Mrli
别装作很努力,
因为结局不会陪你演戏。
Contacts:
QQ博客园

深度学习+Pytorch学习笔记

2021/11/28 机器学习 读书笔记
Word count: 8,377 | Reading time: 37min

在课程设计实验周里总共读了3本书, 《深度学习算法实践》、《深度学习框架PyTorch快速开发与实战》、《PyTorch深度学习入门》。 其中《深度学习算法实践》、《深度学习框架PyTorch快速开发与实战》比较失望,质量比较差;《PyTorch深度学习入门》的代码质量高一点,但需要理论辅助,由于事先看了李宏毅老师的深度学习,因此还算容易入手。

《深度学习框架PyTorch快速开发与实战》

网上关于《深度学习框架PyTorch快速开发与实战》的评价:

关于作者

  • 作者一共三个,在封面内侧有简单的介绍, 三个作者的简绍里没有“深度学习”相关的内容(请注意书名).单从这本书的作者介绍来说,这几个作者是很不专业的,换言之,这几个人可能未必够资格写这么一本书(就事论事,没有任何不尊重人的意思)
  • 代码质量差到离谱
    • PyTorch版本问题(这个是技术相关书籍都会遇到的问题,可以接受)
    • 代码不全,照着书上的代码敲下来,会发现莫名其妙就少了一部分

我第一本代码书看的就是这本, 当时觉得还行, 但在看完《PyTorch深度学习入门》后打算也敲下这本书的代码,结果发现给的样例代码很多都跑不过,并且初看时对每个函数的详细介绍现在看来可能是因为没东西写了,内容非常重复。

▲.以后选书还是得看下作者是否相关专业

常用距离公式

(1)闵可夫斯基距离
闵可夫斯基距离(Mlinkowski distance)是衡量数值点之间距离的一种非常常见的方法。
闵可夫斯基距离定义为:(i=1nxiyip)1/p\left(\sum_{i=1}^{n}\left|x_{i}-y_{i}\right|^{p}\right)^{1 / p}

该距离最常用的p是2和1,前者是欧几里得距离(Euclidean distance),后者是曼哈顿距离(Manhattan distance)。当p趋近于无穷大时,闵可夫斯基距离转化成切比雪夫距离(Chebyshev distance):limp(i=1nxiyip)1p=maxi=1nxiyi\lim _{p \rightarrow \infty}\left(\sum_{i=1}^{n}\left|x_{i}-y_{i}\right| p\right)^{\frac{1}{p}}=\max _{i=1}^{n}\left|x_{i}-y_{i}\right|

(2)马氏距离(Mahalanobis distance)是由印度统计学家马哈拉诺比斯(PC.Mahalanobis)提出的,表示数据的协方差距离。它是一种有效地计算两个未知样本集的相似度的方法。与欧氏距离不同的是它考虑各种特性之间的联系(例如:一条关于身高的信息会带来一条关于体重的信息,因为两者是有关联的。),并且是尺度无关的(Scale-invariant),即独立于测量尺度。对于一个均值为u,协方差矩阵为的多变量向量,其马氏距离为(x-u)Z(-1)(xu)。

(3)余弦相似度,又称为余弦相似性。通过计算两个向量的夹角余弦值来评估它们的相似度。
假设向量a、b的坐标分别为(×1,y1)、(×2,y2)。则:cosθ=abab\cos \theta=\frac{a \bullet b}{\|a\|\|b\|}

数据标准化处理

数据标准化(归一化)处理,是为了消除指标之间的量纲影响,以解决数据指标之间的可比性。原始数据经过数据标准化处理后,各指标处于同一数量级,适合进行综合对比评价。标准化就是一种对样本数据在不同维度上进行一个伸缩变化,也就是不改变原始数据的信息(分布)。这样的好处就是在进行特征提取时,忽略掉不同特征之间的一个度量,而保留样本在各个维度上的信息(分布)。

a.Min-Max标准化(Min-Max Normalization)也称为离差标准化,是对原始数据的线性变换,使结果值映射到[0, 1]之间。转换函数如下:X=XminmaxminX^{*}=\frac{X-\min }{\max -\min }。这种方法有个缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义。

b.Z-score标准化方法
这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。经过处理的数据符合标准正态分布,即均值为0,标准差为1,转化函数为X*= \frac{X-μ}{σ},其中u为所有样本数据的均值,σ为所有样本数据的标准差。

正则化

为了防止过拟合现象,我们加入了正则化项,常用的有 L1范数和 L2范数。
常用的向量的范数如下。
L0范数:||x||0为x向量各个非零元素的个数。
L1范数:||x||1为x向量各个元素绝对值之和,也叫“稀疏规则算子”(Lasso Regularization)。
L2范数:||x||2为 x 向量各个元素平方和的1/2次方,L2范数又称Euclidean范数或者Frobenius范数。在回归里面,有人把有它的回归叫“岭回归”(Ridge Regression),有人也叫它“权值衰减(Weight Decay)”。
Lp范数:||x||为x向量各个元素绝对值p次方和的1/p次方 […]

成本函数(优化函数)

在训练神经网络时,必须评估网络输出的正确性。众所周知,预期上正确的训练输出数据和实际的训练输出是可比拟的。成本函数便是测量实际和训练输出之间的差异。实际和预期输出之间的零成本将意味着训练神经网络成为可能。
BGD即Batch Gradient Descent。在训练中,每一步迭代都使用训练集的所有内容。也就是利用现有参数对训练集中的每一个输入生成一个估计输出,然后跟实际输出比较,统计所有误差,求平均以后得到平均误差,以此来作为更新参数的依据。
由于每一步都利用了训练集中的所有数据,因此当损失函数达到最小值以后,能够保证此时计算出的梯度为0,换句话说,就是能够收敛。因此,使用BGD时不需要逐渐减小学习速率。由于每一步都要使用所有数据,因此随着数据集的增大,运行速度会越来越慢。在批量梯度下降法中,因为每次都遍历了完整的训练集,其能保证结果为全局最优,但是也因为我们需要对每个参数求偏导,且在对每个参数求偏导的过程中还需要对训练集遍历一次,当训练集很大时,这个计算量是惊人的!
所以,为了提高速度,减少计算量,提出了SGD随机梯度下降的方法,该方法每次随机选取一个样本进行梯度计算,大大降低了计算成本。SGD全名Stochastic Gradient Descent,即随机梯度下降。即随机抽取一批样本,以此为根据来更新参数。随机梯度下降算法和批量梯度下降的不同点在于其梯度是根据随机选取的训练集样本来决定的,其每次对theta的更新,都是针对单个样本数据,并没有遍历完整的参数。当样本数据很大时,可能到迭代完成,也只不过遍历了样本中的一小部分。因此,其速度较快,但是其每次的优化方向不一定是全局最优的,但最终的结果是在全局最优解的附近。

Momentum Momentum
借用了物理中的动量概念,即前几次的梯度也会参与运算。
为了表示动量,引入了一个新的变量v(velocity)。v是之前的梯度的累加,但是每回合都有一定的衰减。前后梯度方向一致时,能够加速学习。前后梯度方向不一致时,能够抑制震荡。
Nesterov Momentum这是对之前的Momentum的一种改进,大概思路就是,先对参数进行估计,然后使用估计后的参数来计算误差。

AdaGrad AdaGrad
可以自动变更学习速率,只需要设定一个全局的学习速率,但是这并非是实际学习速率,实际的速率是与以往参数的模之和的开方成反比的。
它能够实现学习率的自动更改。如果这次梯度大,那么学习速率衰减就快一些;如果这次梯度小,那么学习速率衰减就慢一些。但仍然要设置一个变量。
经验表明,在普通算法中也许效果不错,但在深度学习中,深度过深时会造成训练提前结束。

RMSProp
RMSProp 通过引入一个衰减系数r,让r每回合都衰减一定比例,类似于Momentum中的做法。相比于AdaGrad,这种方法很好地解决了深度学习中过早结束的问题。

《PyTorch深度学习入门》

入门coding:

线性回归代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import torch
import matplotlib.pyplot as plt
%matplotlib inline

def Produce_X(x):
# 增加一维全1给bias
x0 = torch.ones( x.numpy().size)
# dim = 0, 添加行; dim = 1, 添加列
X = torch.stack( (x, x0), dim = 1)
return X


x = torch.Tensor([ 1.4, 5, 11, 16, 21 ])
y = torch.Tensor( [14.4, 29.6, 62, 85.5, 113.4])

X = Produce_X(x)

inputs = X
targets = y
w = torch.rand(2, requires_grad = True)
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
def draw(outputs, loss):
plt.cla()
plt.scatter( x.numpy(), y.numpy() )
plt.plot( x.numpy(), outputs.data.numpy(), 'r-', lw = 5)
plt.text(0.5, 0, 'loss=%s' % (loss.item()), fontdict = {
'size':20,
'color':'red'
})
plt.pause(0.05)

def train(epochs = 1, lr = 0.01):
for ep in range(epochs):
outputs = inputs.mv(w)
print(outputs.size())
print(targets.shape)
loss = (outputs - targets).pow(2).sum()
loss.backward()
w.data = w.data - lr * w.grad
w.grad.zero_()#更新完grad后, 必须清空grad, 否则会累加
# if epochs % 80 == 0:
# draw(outputs, loss)
return w, loss

w, loss = train(1, lr = 1e-4)
print("final loss:", loss.item())
print("weights:", w.data)

人工神经元-线性回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
import matplotlib.pyplot as plt

def Produce_X(x):
x0 = torch.ones(x.numpy().size) #用ones产生初始值为1,大小与x相同的向量
X = torch.stack((x,x0),dim=1) #stack函数将两个向量拼合
return X


x = torch.linspace(-3,3,100000)#用linspace产生(-3,3)区间内的100000个点
X = Produce_X(x)
y = x +1.2*torch.rand(x.size())#假设真实函数是y=x,我们在上面增加一些误差,更加符合实际情况
w = torch.rand(2) #定义权重w的变量

# 注意这边的x是x, 而不是X, X是神经网络的输入, 被加上了bias的
plt.scatter(x.numpy(),y.numpy(),s=0.001)
plt.show()
1
2
3
4
5
6
7
8
9
10
11
CUDA = torch.cuda.is_available()
if CUDA:
inputs = X.cuda()
targets = Y.cuda()
w = w.cuda()
w.requires_grad = True
else:
inputs = X
targets = Y
w = w
w.requires_grad = Truue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def draw(outputs, loss):
if CUDA:
outputs = outputs.cpu()
plt.cla()
plt.scatter( x.numpy(), y.numpy() )
plt.plot( x.numpy(), outputs.data.numpy(), 'r-', lw = 5)
plt.text(0.5, 0, 'loss=%s' % (loss.item()), fontdict = {
'size':20,
'color':'red'
})
plt.pause(0.05)

def train(epochs = 1, lr = 0.01):
for ep in range(epochs):
outputs = inputs.mv(w)
loss = (outputs - targets).pow(2).sum()
loss.backward()
w.data = w.data - lr * w.grad
w.grad.zero_()#更新完grad后, 必须清空grad, 否则会累加
# if epochs % 80 == 0:
# draw(outputs, loss)
return w, loss
1
2
3
4
5
6
7
8
9
from time import perf_counter
start = perf_counter()
w, loss = train(1000, lr = 1e-4)
finish = perf_counter()
t = finish - start
print("计算时间:", t)
print("final loss:", loss.item())
print("w:", w.data)
print(type(loss))

人工神经元-线性回归-module

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import torch
import matplotlib.pyplot as plt
from torch import nn,optim
from time import perf_counter
import numpy as np
from typing import Any

x = torch.unsqueeze(torch.linspace(-3, 3, 1000), dim = 1)
y = x + 1.2 * torch.rand(x.size())

class LR(nn.Module):
def __init__(self):
super(LR, self).__init__()
self.liner = nn.Linear(1, 1)

def forward(self, x: Any):
out = self.liner(x)
return out


#如果支持CUDA,则采用CUDA加速
CUDA = torch.cuda.is_available()

if CUDA:
LR_model = LR().cuda()
inputs = x.cuda()
target = y.cuda()
else:
LR_model = LR()
inputs = x
target = y


criterion = nn.MSELoss()
optimizer = optim.SGD(LR_model.parameters(), lr=1e-4)


def draw(output, loss):
if CUDA:
output = output.cpu()
plt.cla()
plt.scatter(x.numpy(), y.numpy(), marker='.')
plt.plot(x.numpy(), output.data.numpy(), 'r-', lw=5)
plt.text(0.5, 0, 'Loss=%.8s' % (loss.item()), fontdict={'size': 20, 'color': 'red'})
plt.pause(0.005)


def train(model, criterion, optimizer, epochs):
for ep in range(epochs):
output = model(inputs)
loss = criterion(output, target)
optimizer.zero_grad()# 清空权重的grad
loss.backward()# 计算梯度
optimizer.step()# 利用梯度更新权重

if ep % 80 == 0:
draw(output, loss)

return model, loss


start = perf_counter()
LR_model,loss = train(LR_model,criterion,optimizer,10000)
finish = perf_counter()
time = finish-start
print("计算时间:%s" % time)
print("final loss:",loss.item())
print("weights:",list(LR_model.parameters()))

非线性回归

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import torch
import matplotlib.pyplot as plt
from torch import nn, optim
from time import perf_counter
import numpy as np
from typing import Any


def draw(output, loss):
if CUDA:
output = output.cpu()
plt.cla()
plt.scatter(x.numpy(), y.numpy(), marker='.')
plt.plot(x.numpy(), output.data.numpy(), 'r-', lw=5)
plt.text(-2, -10, 'Loss=%.6s' % (loss.item()), fontdict={'size': 20, 'color': 'red'})
plt.pause(0.005)

x = torch.unsqueeze(torch.linspace(-3, 3, 1000), dim=1)
y = x.pow(3) + 0.3 * torch.rand(x.size())

plt.scatter(x.numpy(), y.numpy())
plt.show()


class Net(nn.Module):
def __init__(self, input_feature, num_hidden, outpus):
super(Net, self).__init__()
self.hidden = nn.Linear(input_feature, num_hidden)
self.out = nn.Linear(num_hidden, outpus)

def forward(self, x):
x = nn.functional.relu(self.hidden(x))
x = self.out(x)
return x

# 如果支持CUDA,则采用CUDA加速
CUDA = torch.cuda.is_available()

if CUDA:
net = Net(input_feature= 1, num_hidden= 20, outpus= 1).cuda()
inputs = x.cuda()
target = y.cuda()
else:
net = Net(input_feature= 1, num_hidden= 20, outpus= 1)
inputs = x
target = y

criterion = nn.MSELoss()
optimizer = optim.SGD(net.parameters(), lr=0.01)

def train(model, criterion, optimizer, epochs):
for ep in range(epochs):
output = model(inputs)
loss = criterion(output, target)

optimizer.zero_grad()
loss.backward()
optimizer.step()

if ep % 80 == 0:
draw(output, loss)
return model, loss

net, loss = train(net, criterion, optimizer, 1)
print(type(loss))
print("final loss:", loss.item())

分类问题

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import torch
import matplotlib.pyplot as plt
from torch import nn, optim
from time import perf_counter
import numpy as np


def draw(output):
if CUDA:
output=output.cpu()
plt.cla()
# torch.max[0]是取max的值, [1]是取索引值
output = torch.max((output), 1)[1]
pred_y = output.data.numpy().squeeze()
target_y = y.numpy()
plt.scatter(x.numpy()[:, 0], x.numpy()[:, 1], c=pred_y, s=10, lw=0, cmap='RdYlGn')
accuracy = sum(pred_y == target_y)/1000.0
plt.text(1.5, -4, 'Accuracy=%s' % (accuracy), fontdict={'size': 20, 'color': 'red'})
plt.pause(0.1)



N = 500
cluster = torch.ones(N, 2)
# mean, std的正态分布
data0 = torch.normal(4 * cluster, 2)
data1 = torch.normal(-4 * cluster, 2)
label0 = torch.zeros(N)
label1 = torch.ones(N)

x = torch.cat((data0, data1), ).type(torch.FloatTensor)
y = torch.cat((label0, label1), ).type(torch.LongTensor)
plt.scatter(x.numpy()[:, 0], x.numpy()[:, 1], c = y, s=10, lw=0, cmap='RdYlGn')
plt.show()

class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.linear = nn.Linear(2, 2)

def forward(self, x):
x = self.linear(x)
x = torch.sigmoid(x)
return x

CUDA = torch.cuda.is_available()
if CUDA:
net = Net().cuda()
inputs = x.cuda()
targets = y.cuda()
else:
net = Net()
inputs = x
targets = y


def train(model, criterion, optimizer, epochs):
for ep in range(epochs):
outputs = model(inputs)
loss = criterion(outputs, targets)

optimizer.zero_grad()
loss.backward()
optimizer.step()

if ep % 80 == 0:
draw(outputs)

# return model, loss


optimizer = optim.SGD(net.parameters(), lr=0.02)
criterion = nn.CrossEntropyLoss()
train(net, criterion, optimizer, 1000)
  • torch.randn(5,2)是标准正态分布取(5,2)数
  • data0 = torch.normal(4 * cluster, 2) , 第一个参数是mean, 第二个是std, 维度根据传入的tensor参数决定
  • torch.norm() 是求范数

多分类

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import torch
from torch import nn, optim
import torch.nn.functional as F
import matplotlib.pyplot as plt

def draw(output):
if CUDA:
output=output.cpu()
plt.cla()
output = torch.max((output), 1)[1]
pred_y = output.data.numpy().squeeze()
target_y = y.numpy()
plt.scatter(x.numpy()[:, 0], x.numpy()[:, 1], c=pred_y, s=10, lw=0, cmap='RdYlGn')
accuracy = sum(pred_y == target_y)/1500.0
plt.text(0.5, -6, 'Accuracy=%.6s' % (accuracy), fontdict={'size': 20, 'color': 'red'})
plt.pause(0.1)

N = 500
cluster = torch.ones(N, 2) # 2是两个维度: x, y
data0 = torch.normal(4*cluster, 2)
data1 = torch.normal(-4*cluster, 1)
data2 = torch.normal(-8*cluster, 1)
label0 = torch.zeros(N)
label1 = torch.ones(N)
label2 = label1 * 2


x = torch.cat((data0, data1, data2), ).type(torch.FloatTensor)
y = torch.cat((label0, label1, label2), ).type(torch.LongTensor)

plt.scatter(x.numpy()[:, 0], x.numpy()[:, 1], c=y.numpy(), s=10, lw=0, cmap='RdYlGn')
plt.show()


class Net(nn.Module):
def __init__(self, input_feature, num_hidden, outputs):
super(Net, self).__init__()
self.hidden = nn.Linear(input_feature, num_hidden)
self.out = nn.Linear(num_hidden, outputs)

def forward(self, x ):
x = F.relu( self.hidden(x) )
x = F.softmax(self.out(x))
return x

CUDA = torch.cuda.is_available()
if CUDA:
net = Net(input_feature=2, num_hidden=20,outputs=3).cuda()
inputs = x.cuda()
target = y.cuda()
else:
net = Net(input_feature=2, num_hidden=20,outputs=3)
inputs = x
target = y

optimizer = optim.SGD(net.parameters(), lr=0.02)
criterion = nn.CrossEntropyLoss()


def train(model, criterion, optimizer, epochs):
for ep in range(epochs):
outputs = model(inputs)
loss = criterion(outputs, target)

optimizer.zero_grad()
loss.backward()
optimizer.step()

if ep % 80 == 0:
draw(outputs)

train(net, criterion, optimizer, 10000)

LeNet-MNIST手写识别

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# coding=utf-8
import os
import torch
from torch import nn, optim
import torch.nn.functional as F
from torchvision import datasets, transforms
# 加载torchvision, 是专门做图形处理的一个库, datasets为加载数据集, transforms做图形预处理
# torch.utils.data.DataLoader做数据加载

transform = transforms.Compose([
transforms.ToTensor(), # 将数据转成tensor对象
transforms.Normalize((0.1307,), (0.3081,)) # 将数据做归一化处理
])

# 下载并存储路径, 加载训练集or测试集、是否自动下载、指定数据预处理方式
trainset = datasets.MNIST('data', train=True, download=True, transform=transform)
testset = datasets.MNIST('data', train=False, download=True, transform=transform)
# 下载下来的raw文件夹包含了数据和标签,processsed文件夹中为处理后的数据

class LeNet(nn.Module):
# 定义Net的初始化函数,本函数定义了神经网络的基本结构
def __init__(self):
# 继承父类的初始化方法,即先运行nn.Module的初始化函数
super(LeNet, self).__init__()
# C1卷积层:输入1张灰度图片,输出6张特征图,卷积核5x5
self.c1 = nn.Conv2d(1, 6, (5, 5))
# C3卷积层:输入6张特征图,输出16张特征图,卷积核5x5
self.c3 = nn.Conv2d(6, 16, 5)
# 全连接层S4->C5:从S4到C5是全连接,S4层中16*4*4个节点全连接到C5层的120个节点上
self.fc1 = nn.Linear(16 * 4 * 4, 120)
# 全连接层C5->F6:C5层的120个节点全连接到F6的84个节点上
self.fc2 = nn.Linear(120, 84)
# 全连接层F6->OUTPUT:F6层的84个节点全连接到OUTPUT层的10个节点上,10个节点的输出代表着0到9的不同分值。
self.fc3 = nn.Linear(84, 10)

# 定义向前传播函数
def forward(self, x):
# 输入的灰度图片x经过c1的卷积之后得到6张特征图,然后使用relu函数,增强网络的非线性拟合能力,接着使用2x2窗口的最大池化,然后更新到x
x = F.max_pool2d(F.relu(self.c1(x)), 2)
# 输入x经过c3的卷积之后由原来的6张特征图变成16张特征图,经过relu函数,并使用最大池化后将结果更新到x
x = F.max_pool2d(F.relu(self.c3(x)), 2)
# 使用view函数将张量x(S4)变形成一维向量形式,总特征数不变,**为全连接层做准备**
x = x.view(-1, self.num_flat_features(x))
# 输入S4经过全连接层fc1,再经过relu,更新到x
x = F.relu(self.fc1(x))
# 输入C5经过全连接层fc2,再经过relu,更新到x
x = F.relu(self.fc2(x))
# 输入F6经过全连接层fc3,更新到x
x = self.fc3(x)
return x

# 计算张量x的总特征量
def num_flat_features(self, x):
# 由于默认批量输入,第零维度的batch剔除
size = x.size()[1:]
num_features = 1
for s in size:
num_features *= s
return num_features

CUDA = torch.cuda.is_available()
if CUDA:
lenet = LeNet().cuda()
else:
lenet = LeNet()

criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(lenet.parameters(), lr=0.001, momentum=0.9)

# 用数据加载工具来加载训练数据, batch_size表示一次性加载的数据量, shuffle表示遍历不同批次数据时打乱顺序, num_workers表示用n个子进程来加载数据
# 后来把workers改成2还是出错,直到最后把workers改成0才不出错。电脑不行啊!当报错时应该考虑一下是不是设置的太大或者不支持多线程进行操作。
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=0)
testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=0)

def train(model, criterion, optimizer, epochs=1):
for epoch in range(epochs):
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
inputs, labels = data
if CUDA:
inputs, labels = inputs.cuda(), labels.cuda()
optimizer.zero_grad()
outputs = model(inputs)

loss = criterion(outputs, labels)
loss.backward()
optimizer.step()

running_loss += loss.item()

if i % 1000 == 999:
print('[Epoch:%d, Batch:%5d] Loss: %.3f' % (epoch + 1, i + 1, running_loss / 1000))
running_loss = 0.0
print('Finished Training')


def test(testloader, model):
correct = 0
total = 0
for data in testloader:
images, labels = data
if CUDA:
images = images.cuda()
labels = labels.cuda()
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
print('Accuracy on the test set: %d %%' % (100 * correct / total))

def load_param(model, path):
if os.path.exists(path):
model.load_state_dict(torch.load(path))

def save_param(model, path):
torch.save(model.state_dict(), path)

load_param(lenet, 'model.pkl')
train(lenet, criterion, optimizer, epochs=2)
save_param(lenet, 'model.pkl')
test(testloader, lenet)

model.eval()是让model变成训练模式, 这主要是因为dropout和batch normalization的操作在训练和测试是不一样的

附录

Torch.size()/shape, numpy.narray.size/shape 区别

1
2
3
4
5
6
7
# a = np.array([[1,2,3],[2,1,3]])
# print(a.shape, a.size)
# (2, 3) 6

a = torch.Tensor([[1,2,3],[2,1,3]])
print(a.shape, a.size())
# torch.Size([2, 3]) torch.Size([2, 3])

unsqueeze用法

1
2
3
4
5
a = torch.linspace(-3, 3, 5)
print(a, a.size())

b = torch.unsqueeze(a, dim = 1)
print(b, b.size())

关于求微分->autograd->Variable

之前书上提供的代码需要求grad的时都是把tensor的requires_grad属性手动置为True(requires_grad默认为False的), 而实际上需要求grad的tensor已经被封装成了可以自动求微分的Variable类型(from torch.autograd import Variable)`

1
2
3
4
5
6
7
8
9
10
11
CUDA = torch.cuda.is_available()
if CUDA:
inputs = X.cuda()
targets = Y.cuda()
w = w.cuda()
w.requires_grad = True
else:
inputs = X
targets = Y
w = w
w.requires_grad = True

(variable)的理解, pytorch中的变量variable有三个属性,分别是data表示变量中的具体值, grad表示这个变量反向传播的梯度,这个的计算方式下面有专门的一个演示程序, grad_fn表示是通过什么操作得到这个变量的例如( 加减乘除、卷积、反置卷积)

然后定义一个tensor(张量),以及将tensor(张量)转化成variable(变量)。之所以需要将tensor转化成variable是因为pytorch中tensor(张量)只能放在CPU上运算,而(variable)变量是可以用GPU进行加速计算的。 所以说这就是为什么pytorch加载图像的时候一般都会使用(variable)变量. 下面一段代码演示的是tensor和variable(变量)之间的转化。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 然后定义pytorch中的tensor 并将tensor转化成Variable的形式
x_tensor = torch.ones(3)
print('张量的类型以及具体值:\n', type(x_tensor), x_tensor)
x_var = Variable(x_tensor, requires_grad = True)
print('变量的类型以及具体的值:\n', type(x_var), x_var)

'''
张量的类型以及具体值:
<class 'torch.Tensor'> tensor([1., 1., 1.])
变量的类型以及具体的值:
<class 'torch.Tensor'> tensor([1., 1., 1.], requires_grad=True)
'''
# 可以看到这边多了一个requires_grad的属性

Tensor也可以通过x_tensor.data来获得值,因此比较下来两者的区别就是requires_grad为True、False的关系

摘自: https://blog.csdn.net/qq_41776781/article/details/93967961

《深度学习算法实践》

这本书应该是我大二见到过最多的书了, 但说实话看完也比较一般, 比较多介绍的是深度学习应用介绍。

《Pytorch深度学习实战:从新手小白到数据科学家》

这本书感觉也不错,即介绍了torch也介绍了不少pandas和sklearn

Pytorchæ·±åº¦å­¦ä¹ å®žæˆ˜ï¼šä»Žæ–°æ‰‹å°ç™½åˆ°æ•°æ®ç§‘å­¦å®¶

PyTorch的核心概念

本节主要介绍PyTorch的基本概念(如Tensor 和Variable)、自动微分和PyTorch的核心模块。

1.3.1PyTorch的基本概念

和其他深度学习框架一样,PyTorch在实现过程中也提出了自己的概念,包括张量,PyTorch中的张量用Tensor表示。初学者可能不知道张量是什么。其实,张量可以简单地理解为一个多维数组,类似于NumPy中的narray对象。
多维数组可以用相册来形象地解释。假设小米有相册A,相册A包含N张图片,每张图片的宽度为那、高度为H。由于是彩色图片,在计算机中用RGB三通道表示(所有颜色都可以用红、景、蓝三原色按照不同的比例调配出来,红、绿、蓝三通道不同的像素亮度值叠加在一起就呈现出不同的颜色,所以整张图片呈现出彩色),所以通道数为3。.
PyTorch采用四维数组表示这个相册,形如[N, W,H, C],其中C表示通道数。这种多维数组表示的好处可以从计算机组成原理说起,现代计算机都是多核多处理器的,支持多线程和多进程,非常适合矩库的并行计算,而且计算一批数据和计算一个数据的调度时间是差不多的,因此科学计算往往都是基于矩阵的计算,并且会指定一个适当的Batch。例如,PyTorch视觉处理中通常将Bah指定为4、128或256,这也是为了充分利用计算机资源而考虑的。基于张量的乘法运算如图18所示。
张量就是多维数组,并且提供了CPU设备和GPU设备的支持。如果机器上有GPU设备,就可以在Tr上调用cuda方法,将Tensor的数据加载到GPU设备中运行,提升运行速度。张最上提供了很多有用的方法,这些方法和Numpy中的方法类似,使用过NmPy的读者肯定会觉得这些方法既眼熟又亲切。

创健张量需要借助tensor提供的Tensor方法或from_numpy方法。理论上可以创建任意维度的多为数组,但最常用的Tensor补超过五维。

除了维度,还可以指定每个维度上的SIZE,如a = torch.Tensor(3, 4, 5, 8),表示创建了一个思维数组,因为Tensor有4个参数,每个参数表示对应维度的大小。因此,变量a可以表示Batch书为3、宽度为4。高度为5,通道数为8的特征图(Feature Map图像卷积运算产生的中间特征)

除了一二维以外,还有三维立方体:将多个二维矩阵叠加在一起就可以形成三维立方体。三维Tensor 常用于表示图片数据[width,height,channel]。四维多立方体:常见于批量的图片数据,如之前介细的相册就是多张图片的叠加,最常见的形式为[batch, width, height, channel]。五维:表示多个四维tensor相加,常见于视频数据,视频按帧数划分,如50fps(帧/s),表现形式为[frames, batch, width, height, channel]

Variable

PyTorch中另外一个和Tensor相关的概念是Variable变量。Variable是对Tensor的封装,是一种特殊的张量,位于torch.autograd自动微分模块中,是为实现自动微分而提出的一种特殊的数据结构。在PyTorch0.4.1之前的版本中,Variable的应用非常广泛。但是到PyTorch1.0之后,这个数据结构被标注为deprecated,表示已经过时,并且Variable中的grad和grad_fm等属性也被转移到Tensor中,这个改变进一步精简了接口。由于市面上还存在大量老版本的代码,所以本书也对Variable进行简单介绍。创建Variable需要借助 torch.autograd中提供的Variable方法,它接收Tensor类型的参数。下面通过 Float Tensor 创建 Variable。

算子有哪些

常见算子

PyTorch的核心模块

任何好的软件系统必定是经过精心设计和抽象的,PyTorch脱胎于2012年使用La语言编写的To山项目,该项目无论是计算架构设计还是扩展性都是一流的,特别是动态图更是让人耳目一新。
pyTorch将系统的实现抽象为不同的模块,最核心的模块是Tensor,它是整个动态图的基础,自动微分及反向传播都需要借助该模块。Torch为创建Tensor提供了多种方法,如torchrandn、torcth.randint等,Tensor上实现了大量的方法,如sum、argmin、argmax等。Tensor也是torch模块构建网络的核心,网络构建好之后通过网络的parameters方法可以获取网络的所有参数,并且可以通过遍历这些参数对象改变requires_grad 属性来达到冻结参数的目的,这在迁移学习中特别有用。torch.nn模块通过functional子模块提供了大量方法,如conv、ReLU、polling、softmax等。这些方法在网络的forward中调用,结果输出值由torch.nn.xxxLos损失函数计算损失。torch.nn模块中提供了很多可选的损失函数,如MSELoss、CrossEntropyLoss等,将损失传递给优化模块,torch.optim优化模块提供了大量的优化器,常见的有SGD、Adam等。优化器会根据损失计算并更新梯度,达到优化的目的,最终将更新 parameters。PyTorch的核心模块如图1.14所示。

pytorch核心模块

《Python深度学习基于Pytorch》

神经网络核心组件:

  • 层:神经网络的基本结构,将输入张量转换为输出张量
  • 模型:层构成的网络
  • 损失函数:参数学习的目标函数,通过最小化损失函数来学习各种参数
  • 优化器:如何使损失函数最小,这就设计优化器

1

多个层链接在一起构成一个模型或网络,输入数据通过这个模型转换为预测值,然后损失函数把预测值与真实值进行比较,得到损失值(损失值可以是距离、概率值等),该损失值用于衡量预测值与目标结果的匹配或相似程度,优化器利用损失值更新权重参数,从·而使损失值越来越小。这是一个循环过程,当损失值达到一个阀值或循环次数到达指定次数,循环结束。接下来利用PyTorch的nn工具箱,构建一个神经网络实例。nn中对这些组件都有现成包或类,可以直接使用,非常方便。

pytorch实现神经网络主要工具及相互关系

识别手写体步骤:

  1. 利用pytorch内置函数mnist下载数据
  2. 利用torchvision对数据进行预处理,调用torch.utils建立一个数据迭代器
  3. 可视化源数据
  4. 利用nn工具箱构建神经网络模型
  5. 实例化模型,并定义损失函数及优化器
  6. 训练模型
  7. 可视化结果

优化器

  • SGD
  • SGD带动量optim.SGD(model.parameters(), lr=lr, momentum=momentum)
  • adagrad
  • RMSProp(通过修改AdaGrad而来)
  • Adam(带动量项的RMSProp)

torchvision

更多见:40 实战 Kaggle 比赛:狗的品种识别(ImageNet Dogs)【动手学深度学习v2】-引入预训练网络

  • models

    • 预训练模型,如torchvision.models.resnet34(pretrained=True)
  • datasets

    • 数据集,如mnist,如datasets.MNIST
      • 对比tensorflow:(X_train, _), (_, _) = mnist.load_data()
    • ImageFolder,将指定目录下的数据以文件夹为界区分label

    注:配合data.DataLoader使用

  • transforms

    • 数据增强工具,Scale, RandomCrop、RandomResizedCrop, Pad, ColorJitter、RandomHorizontalFlip、ToTensor、CenterCrop
    • 聚合多个变换操作,Compose
  • utils

    • data.DataLoader

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        # 经典用法
        dataloader = torch.utils.data.DataLoader(
        datasets.MNIST(
        "../../data/mnist",
        train=True,
        download=True,
        transform=transforms.Compose(
        [transforms.Resize(opt.img_size), transforms.ToTensor(), transforms.Normalize([0.5], [0.5])]
        ),
        ),
        batch_size=opt.batch_size,
        shuffle=True,
        )
    • save_image

附录

保存模型断点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 存模型参数写法:
def save_checkpoint(self, folder='checkpoint', filename='checkpoint.pth.tar'):
filepath = os.path.join(folder, filename)
if not os.path.exists(folder):
print("Checkpoint Directory does not exist! Making directory {}".format(folder))
os.mkdir(folder)
else:
print("Checkpoint Directory exists! ")
torch.save({
'state_dict' : self.nnet.state_dict(),
}, filepath)

def load_checkpoint(self, folder='checkpoint', filename='checkpoint.pth.tar'):
filepath = os.path.join(folder, filename)
if not os.path.exists(filepath):
raise("No model in path {}".format(filepath))
map_location = None if args.cuda else 'cpu'
checkpoint = torch.load(filepath, map_location=map_location)
self.nnet.load_state_dict(checkpoint['state_dict'])
# 存整个模型
if j % 10 ==0 and loss < loss_value:
torch.save(model, "model.cpkt")
loss_value = loss

注意点: PIL能show的图片格式不能是tensor,只能是numpy的array,所以需要可视化的话得转换一下。

10分钟快速入门PyTorch (1)

https://zhuanlan.zhihu.com/p/26893755

Author: Mrli

Link: https://nymrli.top/2020/11/28/深度学习-Pytorch读书笔记/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
学会Latex写作
NextPost >
Linux服务器同步时钟
CATALOG
  1. 1. 《深度学习框架PyTorch快速开发与实战》
    1. 1.1. 关于作者
      1. 1.1.1. 常用距离公式
      2. 1.1.2. 数据标准化处理
      3. 1.1.3. 正则化
      4. 1.1.4. 成本函数(优化函数)
  2. 2. 《PyTorch深度学习入门》
    1. 2.1. 线性回归代码
    2. 2.2. 人工神经元-线性回归
      1. 2.2.1. 人工神经元-线性回归-module
    3. 2.3. 非线性回归
    4. 2.4. 分类问题
    5. 2.5. 多分类
    6. 2.6. LeNet-MNIST手写识别
    7. 2.7. 附录
      1. 2.7.1. Torch.size()/shape, numpy.narray.size/shape 区别
      2. 2.7.2. unsqueeze用法
      3. 2.7.3. 关于求微分->autograd->Variable
  3. 3. 《深度学习算法实践》
  4. 4. 《Pytorch深度学习实战:从新手小白到数据科学家》
    1. 4.1. PyTorch的核心概念
      1. 4.1.1. 1.3.1PyTorch的基本概念
      2. 4.1.2. Variable
      3. 4.1.3. 算子有哪些
    2. 4.2. PyTorch的核心模块
  5. 5. 《Python深度学习基于Pytorch》
    1. 5.1. 识别手写体步骤:
      1. 5.1.1. 优化器
    2. 5.2. torchvision
  6. 6. 附录
    1. 6.1. 保存模型断点:
    2. 6.2. 10分钟快速入门PyTorch (1)