深度学习入门之PyTorch 本文章绝大部分参考这位大佬的笔记非常实用,记录加复习
第一章 深度学习介绍 1.1 人工智能 Artificial Intelligence,人工智能,也称机器智能人工智能的分类
(1)弱人工智能:擅长单方面(已实现)
(2)强人工智能:类似人类级别(现阶段)
(3)超人工智能:全方面胜过人类(未实现)
1.2 数据挖掘,机器学习和深度学习 1.2.1 数据挖掘 KDD(knowledge discovery in database),从数据中获取有意义的信息
1.2.2 机器学习 机器学习是实现人工智能的一种途径,设计多门学科大致分为五大类
(1)监督学习:从给定的训练数据集中学习出一个函数,训练集中的目标由人标注,常见算法包括回归和分类
(2)无监督学习,训练集没有人为标注,常见算法如聚类
(3)半监督学习:介于监督学习和无监督学习之间
(4)迁移学习:将已经训练好的模型参数迁移到新的模型来帮助新模型训练数据集
(5)增强学习:通过观察周围环境来学习
1.2.3 深度学习 机器学习的一个分支, 通过模拟人脑来实现数据特征的提取
常见网络结构:DNN,CNN,RNN,GAN等等
第二章 深度学习框架 2.1 深度学习框架介绍 Tensorflow(Google开源的基于C++开发的数学计算软件)
Caffe
Theano
Torch(支持动态图)
MXNet
2.2 PyTorch介绍 2.2.1 什么是PyTorch Python优先的深度学习框架,支持GPU加速和动态神经网络
2.2.2 为什么使用PyTorch 啥也不会就学过PyTorch
PyTorch通过一种反向自动求导的技术,可以让你零延迟地改变神经网络
线性、直观、易于使用
代码简洁直观,底层代码友好,框架环境简单好搭建,总之强烈推荐
2.3 配置PyTorch深度学习环境 2.3.1 操作系统 主流操作系统均可Windows、Linux、Max
2.3.2 Python开发者环境 Anaconda吹爆!!简单安装,随意切换Python环境
2.3.3 PyTorch安装 官网 或者Anaconda
CPU或者GPU
CUDA,CuDnn
第三章 多层全连接神经网络 3.1 PyTorch基础 3.1.1 Tensor张量 Tensor相当于多维的矩阵
Tensor的数据类型有:torch.FloatTensor
(32位浮点型)、torch.DoubleTensor
(64位浮点型)、torch.ShortTensor
(16位整型)、
torch.IntTensor
(32位整型)、torch.LongTensor
(64位整型)
导入Torch
1 2 from __future__ import print_functionimport torch
创建一个大小为5x3未初始化的矩阵
1 2 x = torch.empty(5 , 3 ) print (x)
创建一个随机初始化的矩阵
1 2 3 4 5 6 7 x = torch.rand(5 , 3 ) print (x)x = torch.randn(5 , 3 ) print (x)
创建一个零矩阵,且数据类型为Long
1 x = torch.zeros(5 , 3 , dtype = torch.long)
直接创建数据结构张量
1 2 x = torch.tensor([5 , 3 ]) print (x)
创建一个全为1的矩阵,且数据类型为Double
1 2 3 4 5 x = torch.ones(5 , 3 ) print (x)x = x.new_ones(5 , 3 , dtype = torch.double) print (x)
根据已有Tensor创建新的Tensor,且除非提供新值,将重用所给张量的属性
1 2 3 4 5 x = x.new_ones(5 , 3 , dtype = torch.double) print (x)x = torch.randn_like(x, dtype = torch.float ) print (x)
获取张量形状
torch.Size
本质上还是tuple, 所以支持tuple的一切操作
1 2 print (x.shape)print (x.size())
和numpy的相互转换
1 2 3 4 5 6 7 8 print (x)numpy_x = x.numpy() print (numpy_x)torch_x = torch.from_numpy(numpy_x) print (torch_x)
绝对值
1 2 3 4 5 a = torch.randn(5 , 3 ) print (a)b = torch.abs (a) print (b)
运算 ,例如加法
形式一
1 2 y = torch.rand(5 , 3 ) print (x + y)
形式二
形式三
1 2 3 result = torch.empty(5 , 3 ) torch.add(x, y, out = result) print (result)
形式四
warning 在任何一个in_place改变张量的操作后面都固定一个_。例如:x.copy(y)、x.t(y),更改x需要后跟短-
裁剪 :如果在上下边界内则不变,否则大于上边界值,则改为上边界值,小于下边界值,则改为下边界值
1 2 3 4 5 a = torch.randn(2 , 3 ) print (a)b = torch.clamp(a, -0.1 , 0.1 ) print (b)
除法
1 2 3 4 5 6 7 8 a = torch.randn(2 , 3 ) b = torch,randn(2 , 3 ) c = torch.div(a, b) d = torch.div(c, 10 ) print (a)print (b)print (c)print (d)
warning 加法add,乘积mul,除法div,求幂pow,矩阵乘法mm,矩阵向量乘法mv
改变张量形状
1 2 3 4 x = torch.randn(4 , 4 ) y = x.view(16 ) z = x.view(-1 , 8 ) print (x.size(), y.size(), z.size())
对只含一个元素的Tensor,可以使用.item() 来得到数值
1 2 3 X = torch.randn(1 ) print (x)print (x.item())
使用GPU
1 2 3 4 5 6 7 if torch.cuda.is_available(): device = torch.device("cuda" ) y = torch.ones_like(x, device=device) x = x.to(device) z = x + y print (z) print (z.to("CPU" , torch.double))
3.1.2 Variable变量 Autograd:自动求导
创建一个张量并设置requires_grad=True用来追踪其计算历史
1 2 3 x = torch.ones(5 , 3 , requires_grad=True ) print (x)
对这个张量做一次运算
1 2 3 4 5 6 7 8 y = x + 2 print (y)print (y.grad_fn)z = y * y * 3 out = z.mean() print (z, out)
.requires_grad(…)改变了现有张量的requires_grad标志。如果没有指定的话,默认输入的标志是False。
1 2 3 4 5 6 7 a = torch.randn(2 , 2 ) a = ((a * 3 ) / (a - 1 )) print (a.requires_grad)a.requires_grad_(True ) print (a.requires_grad)b = (a * a).sum () print (b.grad_fn)
梯度
1 2 3 4 5 6 7 8 x = torch.ones(2 , 2 , requires_grad = True ) y = x + 2 z = y * y * 3 out = z.mean() out.backward() print (x.grad)
雅可比矩阵
数学上,若有向量值函数$y=f(x)$,那么y相当于对x的梯度是一个雅可比矩阵(下面是一个latex数学公式)
1 2 3 4 5 J=\begin{bmatrix} \frac{\partial y_1}{\partial x_1} &\cdots& \frac{\partial y_1}{\partial x_n} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_m}{\partial x_1} &\cdots& \frac{\partial y_m}{\partial x_n} \end{bmatrix}
通常来说,torch.autograd是计算雅可比向量积的一个引擎。也就是说,给定任意向量v,计算乘积$v^T*J$。如果v恰好是一个标量函数$l=g(y)$的导数,即$(\frac {\partial l } {\partial y_1 }···\frac {\partial l } {\partial y_m })^T$,那么根据链式法则
,雅可比向量积应该是l
对x
的导数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 J^T·v=\begin{bmatrix} \frac{\partial y_1}{\partial x_1} &\cdots& \frac{\partial y_m}{\partial x_1} \\ \vdots & \ddots & \vdots \\ \frac{\partial y_1}{\partial x_n} &\cdots& \frac{\partial y_m}{\partial x_n} \end{bmatrix} \begin{bmatrix} \frac{\partial l}{\partial y_1}\\ \cdots\\ \frac{\partial l}{\partial y_m} \end{bmatrix}= \begin{bmatrix} \frac{\partial l}{\partial x_1}\\ \cdots\\ \frac{\partial l}{\partial x_n} \end{bmatrix}
(注意:行向量的$vT*J$也可以被视作列向量的$J T*v$)
雅可比向量积的这一特性使得将外部梯度输入到具有非标量输出的模型中变得非常方便
1 2 3 4 5 6 x=torch.randn(3 ,requires_grad=True ) y=x*2 while y.data.norm() <1000 : y=y*2 print (y)
在这种情况下,y不再是标量。torch.autograd不能直接计算完整的雅可比矩阵,但是如果我们只是想要雅可比向量积,只需要将这个向量作为参数传给backward
1 2 3 4 v = torch.tensor([0.1 , 1.0 , 0.0001 ], dtype=torch.float ) y.backward(v) print (x.grad)
也可以通过将代码块包装在with torch.no_grad():中,来阻止autograd跟踪设置了.requires_grad=True的张量的历史纪录
1 2 3 4 5 print (x.requires_grad)print ((x ** 2 ).requires_grad)with torch.no_grad(): print ((x ** 2 ).requires_grad)
Variable
Variable和Tensor的区别:Variable会被放入到计算图中,然后进行向前传播(前馈),反向传播(反馈),自动求导,Variable是在torch.autograd.Varable中
1 2 3 4 5 6 7 8 9 10 11 12 from torch.autograd import Variablex = Variable(torch.Tensor([1 ]), requires_grad = True ) w = Variable(torch.Tensor([1 ]), requires_grad = True ) b = Variable(torch.Tensor([1 ]), requires_grad = True ) y = w * x + b y.backward() print (x.grad)print (w.grad)print (b.grad)
搭建一个简单的神经网络
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 batch_n = 100 hidden_layer = 100 input_data = 1000 output_data = 10 x = torch.randn(batch_n, input_data) y = torch.randn(batch_n, output_data) w1 = torch.randn(input_data, hidden_layer) w2 = torch.randn(hidden_data, output_data) epoch_n = 20 learning_rate = 1e-6 for epoch_n range (epoch_n): h1 = x.mm(w1) h1 = h1.clamp(min = 0 ) y_pred = h1.mm(w2)
使用Variable搭建一个自动计算梯度的神经网络
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 from torch.autograd import Variablebatch_n = 100 hidden_layer = 100 input_data = 1000 output_data = 10 x = Variable(torch.randn(batch_n, input_data), requires_grad = False ) y = Variable(torch.randn(batch_n, output_data), requires_grad = False ) w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad = True ) w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad = True ) epoch_n = 20 learning_rate = 1e-6 for epoch_n in range (epoch_n): y_pred = x.mm(w1).clamp(min = 0 ).mm(w2) loss = (y_pred - y).pow (2 ).sum () print ("Epoch:{},loss:{:.4f}" .format (epoch, loss)) loss.backward() w1.data -= learning_rate * w1.grad_data w2.data -= learning_rate * w2.grad_data w1.grad.data.zero_() w2.grad.data.zero_()
使用nn.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 from torch.autograd import Varablebatch_n = 100 hidden_layer = 100 input_data = 1000 output_data = 10 class Model (torch.nn.Moudle): def __init__ (self ): super (Moudle, self).__init__() def forward (self, input_n, w1, w2 ): x = torch.mm(input_n, w1) x = torch.clamp(x, min = 0 ) x = torch.mm(x, w2) return x def backward (self ): pass model = Model() x=Variable(torch.randn(batch_n,input_data),requires_grad = False ) y=Variable(torch.randn(batch_n,output_data),requires_grad = False ) w1=Variable(torch.randn(input_data,hidden_layer),requires_grad = True ) w2=Variable(torch.randn(hidden_layer,output_data),requires_grad = True ) epoch_n = 20 learning_rate = 1e-6 for epoch in range (epoch_n): y_pred = model(x, w1, w2) loss = (y_pred-y).pow (2 ).sum () print ("Epoch:{},Loss:{:.4f}" .format (epoch,loss)) loss.backward() w1.data -= learning_rate * w1.grad.data w2.data -= learning_rate * w2.grad.data w1.grad.data.zero_() w2.grad.data.zero_()
3.1.3 Dataset数据集 torch.utils.data.Dataset是代表这一数据的抽象类,可以自己定义数据类继承和重写这个抽象类,只需要定义__len__
和__getitem__
函数即可
1 2 3 4 5 6 7 8 9 10 11 12 13 from torch.utils.data import Datasetclass myDataset (Dataset ): def __init__ (self, csv_file, txt_file, root_dir, other_file ) self.csv_data = pd.read_csv(csv_file) with open (txt_file, 'r' ) as f: data_list=f.readlines() self.txt_data = data_list self.root_dir = root_dir def __len__ (self ): return len (self.csv_data) def __getitem__ (self, idx ): data = (self.csv_data[idx],self.txt_data[idx]) return data
通过上面的方式,可以定义需要的数据类,可以通过迭代的方法取得每一个数据,但是这样很难实现取batch,shuffle或者多线程去读取数据,所以Pytorch中提供了torch.utils.data.DataLoader来定义一个新迭代器
3.1.4 nn.Moudle模组 所有的层结构和损失函数均来自torch.nn
1 2 3 4 5 6 7 8 9 import torch.nn as nnclass net_name (nn.Moudle): def __init__ (self, other_arguments ): super (net_name, self).__init__() self.conv1 = nn.Conv2d(in_channels,out_channels, kernel_size) def forward (self, x ): x = self.conv1(x) return x
一个神经网络的典型训练过程如下:
(1)定义包含一些可学习参数(或者叫权重)的神经网络
(2)在输入数据集上迭代
(3)通过网络处理输入
(4)计算loss(输出和正确答案的距离
)
(5)将梯度反向传播给网络的参数
(6)更新网络的权重,一般使用一个简单的规则:weight = weight - learning_rate * gradient
使用torch.nn内的序列
容器Sequential
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 batch_n = 100 hidden_layer = 100 input_data = 1000 output_data = 10 models_1 = torch.nn.Sequential( torch.nn.Linear(input_data, hidden_layer), torch.nn.ReLu(), torch.nn.Linear(hidden_layer, output_data) ) from collections import OrderedDictmodels_2 = torch.nn.Sequential(OrderedDict([ ("Line1" , torch.nn.Linear(input_data, hidden_layer)), ("ReLu1" , torch.nn.ReLu()), ("Line2" , torch.nn.Linear(hidden_layer, output_data)) ])) print (model_1)print (model_2)
使用nn.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 imoprt torch import torch.nn as nnimport torch.nn.functional as Fclass Net (nn.Moudle): def __init__ (self ): super (Net, self).__init__() self.conv1 = nn.Conv2d(1 , 6 , 5 ) self.conv2 = nn.Conv2d(6 , 16 , 5 ) self.fc1 = nn.Linear(16 * 5 * 5 , 120 ) self.fc2 = nn.Linear(120 , 84 ) self.fc3 = nn.Linear(84 , 10 ) def forward (self, x ): x = F.max_pool2d(F.relu(self.conv1(x)), (2 , 2 )) x = F.max_pool2d(F.relu(self.conv2(x)), 2 ) x = x.view(-1 , self.num_flat_features(x)) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x def num_flat_features (self, x ): size = x.size()[1 :] num_features = 1 for s in size: num_features *= s return num_features net = Net() print (net)
3.1.5 torch.optim优化 优化算法分为两大类:
(1)一阶优化算法
使用各个参数的梯度值来更新参数,最常用的是梯度下降
。梯度下降的功能是通过寻找最小值,控制方差,更新模型参数,最终使模型收敛,网络的参数更新公式 $$ \theta = \theta - \eta \times\frac {\partial J(\theta) } {\partial \theta} $$ 其中$\eta$是学习率,$\frac {\partial J(\theta) } {\partial \theta}$是函数的梯度
(2)二阶优化算法
二阶优化算法使用了二阶导数(Hessian方法)来最小化或最大化损失函数,主要是基于牛顿法
1 optimizer=torch.optim.SGD(model.parameters(),lr=0.01 ,momentum=0.9 )
3.1.6 模型的保存和加载 保存
1 2 3 4 torch.save(model, path) torch.save(model.state_dict(), path)
加载
1 2 3 4 load_model= torch.load(path) model.load_state_dict(torch.load(path))