一.深度学习的介绍
深度学习是机器学习(手动进行特征提取)的分支,是一种以人工神经网络为架构,对数据进行特征学习(自动进行特征提取)的算法。深度学习应用场景多样,自行百度搜索。
1.区别
a.1.机器学习需要有人工特征提取的过程;
2.机器学习数据少
b.1.深度学习每有人工特征提取的过程,特征提取的过程可以通过深度神经网络自动完成
2.深度学习需要大量的训练数据集,会有更高的效果,深度学习训练深度神经网络需要大量的算力,因为其中有更多的参数
2.框架
使用facebook出的pytorch(语法和python相同),使用动态计算,代码调试更简单。
二.神经网络的介绍
人工神经网络,简称神经网络,模仿生物神经网络的结构和功能的数学模型,用与对函数进行估计或近似。(应用例如机器视觉和语言识别)
1.神经元(最小的神经网络结构,神经网络结构基础单元,相互连接组成神经网络)
生物神经网络作用机理请参考高中生物教程
2.单层神经网络
3.感知机(两层神经网络)
作用:把n维向量空间用一个超平面分割成两部分,给定一个输入向量,超平面可以判断这个向量位于超平面的那一边。简单的二分类模型,给定阈值,判断数据属于哪一部分。
4.多层神经网络
输入层和特征数量差不多,输出层和分类类别数量差不多
全连接层:第n层和第n-1层中神经元两两之间都有链接
5.激活函数
所以需要激活函数,重要作用,增强模型的非线性分割能力,提高模型稳健性(拟合更多的不同的数据,效果更好),缓解梯度消失问题,加速模型收敛(模型训练得更快)等
系统:函数,f,模型,f(x)=y
线性则有f(x1+x2)=y1+y2;f(kx1)=ky1
像素点(随意组合)-->高级特征-->更高级特征(知道得出结果)
三.pytorch安装方法(自行搜索最新安装方法)
四.张量Tensor
1.各种数值数据称为张量
1.0阶张量:标量,常数
2.1阶张量:向量
3.2阶张量:矩阵
4.3阶张量
5.n阶张量
2.张量的创建方法
1.torch.tensor(list)使用python中的列表和序列创建tensor,或者使用numpy中的数组创建
2.torch.empty()/zeros/ones
3.torch.rand()/randint()/randn()
3.张量的常用方法和属性
1.获取tensor()中的数据(当tensor中只有一个数据可用时)tensor.item()
2.转化为numpy数组 z.numpy()
3.获取tensor形状,tensor.size(),括号里填入数据表示获取某一个维度的形状
4.(数据拉伸成一整条,然后按照每块大小切分)形状改变,tensor.view(),浅拷贝,不赋值的话不会改变
5,获取维数 tensor.dim
6.tensor.max(),tensor.min(),tensor,std()
7.(拉成一整条,如(3,2,4),先分3块,每块切成2部分,再每块切成4部分)转置 tensor.transpose()(括号里写两个维度,转置这两个维度),tensor.permute()(口号里可以写多个维度,转置多个维度)
8.获取值以及复制如 tensor[1,3]获取第一行第三列的值,可对其赋值tensor[1,3]=100;
9,tensor 的切片tensor[0,:,:]获取第一维度的值
4.tensore的数据类型
1.获取tensor的数据类型:tensor.dtype
2.创建数据的时候指定类型 torch.tensor(array,dtype) torch.ones(array,dtype)
3.类型的修改 tensor.float()/long()/int()
4.torch.tensor和torch.Tensor的区别
torch.Tensor和torch.FloatTensor一样,传入数字时表示生成tensor的形状,内容随机填充,当传入列表或可迭代对象时,才表示创建一个内容已知的tensor,torch.tensor为创建tensor的方法
5.tensor和tensor 相加 直接x+y 或者torch.add(x,y)或者x.add(y),x.add_(y)(对x进行修改,把y加到x上)带有下划线的方法意思就是进行就地修改
6.tensor和数字进行运算 x+10
7.CUDA中的tensor
CUDA是NVIDIA推出的运算平台,CUDA是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能解决复杂的计算问题。torch.cuda这个模块增加了对CUDA tensor 的支持,能够在cpu和gpu上使用相同的方法操作tensor。通过.to方法能够把tensor转移到另一个设备(比如从cpu转到gpu)
#device=torch.device("cuda:0") if torch.cuda.is_availble() else "cpu") (当电脑支持CUDA,字符串是CUDA,否则是CPU,还可指定是哪块PU)
实例化一个device,每个tensor“to”以下就好了
a,实例化device:torch.device=torch.device("cuda:0") if torch.cuda.is_availble() else "cpu")
b.tensor.to(device)#把tensor转化为CUDA持的tensor,或者cpu支持的tensor支
五.梯度下降和反向传播
梯度就是一个向量,导数+变化最快的方向(学习前进的方向)
1.梯度下降
a.算出梯度
b.w=w-aw'使得loss最小
、
反向传播,从后往前计算每一层的梯度
六.pythorch完成线性回归
对于pythorch中的一个tensor,如果设置它的属性.requires_gard为True,那么它将会追踪(保存)对该张量的所有操作。或者可以理解为,这个tensor是一个参数,后续会被计算梯度,更新该参数。tensor的所有操作都会记录在grand_fn
注意:为了防止跟踪历史记录(和使用内存),可以将代码块包装在with torch.no_grad():中。在评估模型时特别有用,因为模型可能具有requires_grad=True的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算
1.梯度计算 对于out而言,我们可以使用backward方法来进行反向传播,计算梯度out.backward(),此时便能够求出导数,调用x.grad获取导数值。注意在输出为一个标量的情况下,我们可以调用输出tensor 的backword()方法,但是在数据是一个向量的时候,调用backword()的时候还需要传入其他参数。但是很多时候我们的损失函数是一个标量,所以不在深究。loss.backward()就是根据损失函数。对参数(requires_grad=Ture)的计算他的梯度,并且把她累加保存到x.grad,此时还并为更新其梯度(所以每次进行反向传播需要先把梯度置为零)
2.注意点:a.tensor.data:在tensor的require_grad=False,tensro.data和tensor等价;require_grad=Ture时,tensor.data仅仅是获取tensor 中的数据,不带grad等属性
b.torch.numpy():require_grad=Ture不能够直接转换,需要使用tensor.detach().numpy()
七.线性回归实现
1.准备数据
2.计算预测值
3.计算损失,把梯度设置为零,进行反向传播
4.更新参数
以上属于是手动实现线性回归,接下来讲如何使用pytorchAPI来帮助我们完成线性回归
七.Pytorch完成模型常用API(应用程序编程接口 是用于在软件应用程序之间以形式化的方式来回传递数据的接口)
1.nn.Moudl是torch.nn提供的一个类,是pytorch中我们自定义网络的一个基类,在这个类中定义了很多有用的方法,让我们在继承这个类定义网络的时候非常简单。torch.nn是神经网络工具箱,该工具箱建立于Autogard(主要有自动求导和梯度反向传播功能),提供了网络搭建的模组,优化器等一系列功能
在python中,类是一种用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例,即类的具体对象。类具有以下几个关键特性:
1.继承:一个类可以从另一个类继承字段和方法,这允许代码重用和扩展已有的功能
2.封装:类隐藏了内部实现的细节,只暴露必要的接口给外部使用,增加了代码的安全性和可维护性
3.多态:允许使用共同的接口来处理不同类型的对象,提高了代码的灵活性和可扩展性
类的定义通常包括属性和方法。属性用于存储对象的状态信息,而方法则定义了对象可以执行的操作。例如,一个Person类可能包含name、age等属性,以及walk()、speak()等方法。这些属性和方法共同定义了Person对象的行为和特征。
在自定义网络时,有两个方法需要特别注意:
1.__init__需要调用super方法,继承父类的属性和方法
2.forward方法必须实现,用来定义我们的网络的向前计算,我们得告诉我们的模型,从输入x到输出这个结果是怎么算的,如在__init__里定义一个w,一个b,然后就可以得到out。nn.Linear 可以帮助我们实现一个线性模型,中间需要我们传入两个参数,一个参数是输入的feature,一个参数是输出的feature.实例化之后,我们就可以在forward里调用了。实列化forward里传入x,意味着对于我们这样一个实例来讲,它是具有call方法的。就是我们调用Linear这个实例,其实调用的是Linear的call方法。Linear call方法其实调用的是这里的call 方法,传入了一个x进来其实调用的是这里Linear里面的forward方法.Linear是一个简单的线性模型,它其实我们自己定义的Lr模型是一样的,它里面也有__init__结构,forward 结构,是一样的。所以对Linear进行调用传入x的时候其实就像我们 实例化这个LR一样。通过这种方式我们就能完成自己的这样一个线性模型了,就是这样一个类。对这样一个类,要用肯定要进行实例化。进行实例化之后,和我们用nn.Linear它实例一样,我们直接传入x。调用它得到的一个结果就是forward的output,forward的输出。这样就把我们的模型给定义好了。在我们调用nn.Linear,我们不需要自己去定义一个w和b了。我们前面在手动完成线性回归,我们要自动定义w和b,还需要把.requires_gard设为Ture,表示它是一个参数,后面要更新这样一个参数值,计算梯度。nn.Linear已经帮我们定义好了w和b,所以我们只用调用就行了,不需要做额外的事情。实例化之后,直接传入数据,调用的其实是forward,得到output,其实就是predict.
注意:1.nn.Linear为torch 预定义好的线性模型,也被称为全链接层,传入的参数为输入的数量,输出的数量(in_features,out_features),是不算(batch_size的列数)
2.nn.Moudle定义了__call__方法,即Lr的实例,能够直接被传入参数调用,实际上调用的是forward方法并传入参数
2,优化器类
优化器类(optimizer),可以理解为torch为我们封装的用来进行更新参数的方法,比如常见的随机梯度下降。对于我们参数进行更新的,对于我们参数进行优化的,让他取得投射值,最终得到我们的模型
优化器类都是由torch,optim提供的,例如
1.torch.optim.SGD(参数,学习率)
2.torch.optim.Adam(参数,学习率)
注意:1.参数可以使用model.parameters()获取,获取模型中所有requires_grad=Ture的参数
2.优化器类的使用方法
1.实例化
2.所有参数的梯度,将其值置为0
3.反向传播计算梯度
4.更新参数值
3.损失函数
torch中也预测了很多损失函数
1.均方误差:nn.MseLoss(),常用语分类问题
2.交叉熵损失:nn.CrissEntropyLoss(),常用语逻辑回归
使用方法
4.使用API完成线性回归
import torch
import torch.nn as nn
from torch.optim import SGD
#0.准备数据
x=torch.rand([500,1])#帮助我们随机准备一些数据
y_ture=3*x+0.8
#1.定义模型
class MyLinear(nn.Module):
def __init__(self)
super(MyLinear,self).__init__()#继承父类
self.linear = nn.linear(1,1)
def forward(self,x):
out = self.linear(x)
return out
#2.模型实例化,优化器类实例化,Loss实例化
my_linear=MyLinear()
optimizer = SGD(my_linear.parameters,0.001)
loss_fn = nn.MSEloss()
#3.循环进行梯度下降,参数的更新
for i in range(2000)
#得到预测值
y_predict = my_linear(x)
loss=loss_fn(y_predict,y_true)
#梯度置为0
optimizer.zero_grad()
#反向传播
loss.backward()
#参数更新
optimizer.step()
if i%50 == 0:
params = list(my_linear.parameters())
print(loss.item(),params[0].item,params[1].item)#打印损失和参数
模型评估
model.eval()#设置模型为评估模式,即预测模式,改变my_linear.train()为false 或者可以使用model.train(model=True)
predict = model(x)
predict = predict.data.numpy()
在当前的线性回归中上述并无区别,但是在其他的一些模型中,训练的参数和预测的参数会不同,到时候就需要告诉程序我们是在进行训练还是预测,比如模型中存在Dropout,BatchNorm的时候
5.在GPU上运行代码
1.tensor转化为CUDA支持的tensor,model转化为CUDA支持的model
在GPU上使用API完成线性回归
import torch
import torch.nn as nn
from torch.optim import SGD
#定义一个device对象
torch.device("cuda" if torch.cuda.is_available() else "cpu")
#0.准备数据
x=torch.rand([500,1]).to(device)#帮助我们随机准备一些数据
y_ture=3*x+0.8
#1.定义模型
class MyLinear(nn.Module):
def __init__(self)
super(MyLinear,self).__init__()#继承父类
self.linear = nn.linear(1,1)
def forward(self,x):
out = self.linear(x)
return out
#2.模型实例化,优化器类实例化,Loss实例化
my_linear=MyLinear().to(device)
optimizer = SGD(my_linear.parameters,0.001)
loss_fn = nn.MSEloss()
#3.循环进行梯度下降,参数的更新
for i in range(2000)
#得到预测值
y_predict = my_linear(x)
loss=loss_fn(y_predict,y_true)
#梯度置为0
optimizer.zero_grad()
#反向传播
loss.backward()
#参数更新
optimizer.step()
if i%50 == 0:
params = list(my_linear.parameters())
print(loss.item(),params[0].item,params[1].item)#打印损失和参数
八.常见的优化算法介绍
1.梯度下降算法(BGD)
每次迭代都需要把所有样本都送入,这样的好处是每次迭代都顾及了全部的样本,做的是全局最优化
2.随机梯度下降(SGD)
随机地从样本中抽出一个样本进行梯度的更新
针对梯度下降算法训练速度过慢的缺点,提出了随机梯度下降算法,随机梯度下降算法是从样本中随机抽出一组,训练后按梯度更新一次,然后再抽取一组,再更新一次,在样本量及其大的情况下,可能不用训练完所有的样本就可以获得一个损失值在可接受的范围之内的模型了。
3.小批量梯度下降(MBGD)
找一波数据计算梯度,使用均值更新参数
4.动量法
对梯度进行平滑处理,防止振幅过大
5.AdaGrad
相当于是对学习率进行操作,越到后面学习率越小
6.RMSProp
7.Adam
九.Pytorch中的数据加载
1.模型中使用数据加载器的目的
深度学习中,数据量巨大,不可能一次性在模型中进行向前的计算和反向传播,经常我们会对整个数据进行随机的打乱顺序,把数据处理成一个个的batch,同时还会对数据进行预处理。
2.数据集类
a.dataset基类介绍
在torch中提供了数据集的基类torch.utils.data.Dataset,继承这个基类,我们能够非常快速的实现对数据的加载
可知,我们需要在自定义的数据集类中继承Dstaset类,同时还需要实现两个方法:
1.__len__方法,能够实现通过全局的len()方法获取其中的元素个数
2.__getitem__方法,能够通过传入索引的方式获取数据,例如通过dataset[i]获取其中的第i条数据
3.add方法,把两个数据集合并到一起
import torch
from torch.utils.data import Dataset
data_path=r#后加文件路径
#完成数据集类
class MyDataset(Dataset):
def __init__(self):
self.lines=open(data_path).readlines()
def __getitem__(self,index):
#获取索引对应位置的一条数据
return self.lines[index]
def __len__(self):
#返回数据的总数量
return len(self.lines)
#使用
my_dataset=MyDataset()
print(my_dataset[0])
print(len(my_dataset))
b.数据加载器类
通过数据加载器类我们实现对数据的迭代,对数据的一个批处理,对数据的打乱,甚至使用多线程来同时加载我们的数据
在pytorch中 torch.utils.data.DataLoader提供了上述的所用方法
import torch
from torch.utils.data import Dataset,DataLoader
data_path=r#后加文件路径
#完成数据集类
class MyDataset(Dataset):
def __init__(self):
self.lines=open(data_path).readlines()
def __getitem__(self,index):
#获取索引对应位置的一条数据
return self.lines[index]
def __len__(self):
#返回数据的总数量
return len(self.lines)
my_dataset=MyDataset()
data_loader=DataLoader(dataset=my_dataset,batch_size=2,shuffle=Ture)
#使用
#print(my_dataset[0])
#print(len(my_dataset))
c.Pytorch自带的数据集
下面我们以Mnist手写数字为例,看看pytorch如何加载其中自带的数据集
使用方法和之前一样:
1.准备好Dataset实例
2.把dataset交给dataloder打乱顺序,组成batch
import torchvision
from torchvision.datasets import MINST
#实例化
minist=MINIST(root="./data",train=Ture,download=Ture)
print(minist)#描述对里面的数据量,数据集的切分,位置,是否有对数据和target的目标值的一些处理
d. MNist数据的处理API
a.torchvision.transforms.ToTensor()
把image对象或者(h,w,c)转化为(c,h,w)
b.torchvision.transforms.Normalize(mean,std)
均值和标准差的形状和通道数相同
c.torchvision.transforms.Compose(transforms)
传入list,数据经过list中的每个方法挨个进行处理
使用Pytorch实现手写数字识别
流程:
1.准备数据,这些需要准备DataLoader
2.构建模型,这里可以使用torch构造一个深层的神经网络
分类模型损失函数应该用交叉熵损失
3.知道Pytorch中torchvision.transforms中常见图形处理函数的使用
4.知道如何训练模型和如何评估模型
import torchvision
from torchvision.datasets import MINST
#实例化
minist=MINIST(root="./data",train=Ture,download=Ture)
print(minist)#描述对里面的数据量,数据集的切分,位置,是否有对数据和target的目标值的一些处理
#使用pytorch完成手写数字的识别
import torch'
import numpy as np
import os
from torch.utils.data import DataLoader
from torchvision.datasets import MINIST#手写数据
from torchvision.transforms inport Compose,ToTensor,Normalize#MINIST数据处理的API
import torch.nn as nn
from torch.optim import Adam #导入优化器类
import torch.nn.functional as F
BATCH_SIZE=128
#1.准备数据集
def get_dataloader(train=True):
transform_fn =Copose([ToTensor(),Normalize(mean=(0.1307,),std=(0.3081,))])
dataset=MINIST(root="./data",train=train,transform=transform_fn)
data_loader=DataLoader(dataset,batch_size=BATCH_SIZE,shuffle=Ture)
return data_loader
#2.构建模型
class MnistModel(nn.Module):
def __init__(self):
super(MnistModel,self).__init__()
self.fc1==nn.Linear(1*28*28,28)
self.fc2==nn.Linear(28,10)
def forward(self,input)
#1.形状修改
input.view([input.size[0],1*28*28])
#2.全连接操作,进行矩阵乘法
x = self.fc1(x)
#3.进行激活函数的处理,形状没有变化
x=F.relu(x)
#4.输出层
out = self.fc2(x)
return F.log_softmax(out)
model = MnistModel()
optimizer = Adam(model.parameters,lr=0.001)
if os.path.exits("./model/model.pk1")
model.load_state_dict(torch.load("./model/model.pk1"))
optimizer.load_state_dict(torch.load("./model/optimizer.pk1"))
#实现训练过程
def train(epcoh)#把数据从前往后跑一遍称为一轮
data_loader= get_dataloader()
for index,(input,traget) in enumerate(data_loader):
optimizer.zero_grad()
output = model(input) #调用模型,得到预测值
loss = F.nll_loss(output,target) #得到损失
loss.backward() #反向传播
optimizer.step()#梯度的更新
#模型的保存
torch.save(model.state_dict(),"./model/model.pk1")
torch.save(optimizer.state_dict(),"./model/optimizer.pk1")
if __name__=='_main_':
for i in range(3):#训练三轮
train(i)
#模型的评估
def test():
loss_list=[]
acc_list=[]
test_dataloader = get_dataloader(train=False)
for index,(input,traget) in enumerate(data_loader):
with torch.no.grad:
output = model(input)
cur_loss = F.nll_loss(output,target)
loss_list.append(cur_loss)
#计算准确率
#output [batch_size,10] target:[bstch_size]
pred= output.max(dim=-1)[-1]
cur_acc=pred.eq(traget).float().mean()
acc_list.append(cur_acc)
print("平均准确率,平均损失率:",np.mean(acc_list),np.mean(loss_list))
e.模型中数据的形状
1.原始输入数据的形状:[batch,1,28,28].要经过第一个全连接层进行处理,全连接层所做的是矩阵乘法,四阶矩阵很明显没有办法进行矩阵乘法,所以我们要把它变形,变成二阶矩阵。第一个全连接层的输出形状:[batch_size,28],这里的28是个人设定的,你也可以设置为别的。激活函数不会修改数据的形状,第二个全连接层的输出形状:[batch_size,10],因为手写数字由10个类别
f.模型的保存和加载
torch.save(model.state_dict(),"./model/model.pk1")#保存模型参数
torch.save(optimizer.state_dict(),"./model/optimizer.pk1")#保存优化去参数
#模型的加载
model.load_state_dict(torch.load("./model/model.pk1"))
optimizer.load_state_dict(torch.load("./model/optimizer.pk1"))
g.模型的评估
模型的评估与训练过程相似,但是:
1.不需要计算梯度
2.需要收集损失和准确率,用来计算平均损失和平均准确率
3.损失的计算和训练时损失的计算方法相同
4.准确率的计算
九.循环神经网络
1.文本的tokenization
tokenization就是通常所说的分词,分出的每一个词语我们把它称为token
常见的分词工具很多,比如 Jieba分词
中英文的分词方法
把句子转化为词语
把句子转化为单个字
2.N-garm表示方法
一种帮助我们准备文本特征的方法,前面我们说可以把一个词作为特征,也可以把一个字作为特征,还可以把连续的若干个词语作为特征
3.向量化
因为文本不能直接被模型计算,所以需要将其转化为向量
把文本转化为向量有两种方法
1.转化为one-hot编码,它相当于是每一个token使用一个长度为N(我们训练集的语料)的向量表示,N表示词典的数量。使用稀疏的向量表示文本,占用空间多
2.转化为word embedding
word embedding 使用了浮点型的稠密矩阵来表示token。根据词典的大小,我们的向量通常使用不同的维度,例如:100,256,300等。其中向量中的每一个值是一个超参数,其初始值时随机生成的,之后会在训练的过程中进行学习而获得
我们会把所有的文本转化为向量,把句子用向量来表示
但是在这中间,我们会把taken使用数字来表示,再把数字使用向量来表示
API:torch.nn.Embedding(词典数量,embeeding的维度)
形状的变化:[bach_size,seq_len]--->[bach_size,seq_len,embedding_dim]
4.文本情感分类
1.数据集的准备
2.模型
3.训练
4.评估
5.循环神经网络的介绍
在普通的神经网络中,信息的传递是单向的,这种限制虽然使得网络变得更容易学习,但在一定程度上也削弱了神经网络模型的能力。特别是在很多现实任务中,网络的输出不仅和当前时刻的输入相关,也和其过去一段时间的输出相关。此外,普通网络难以处理时序数据,比如视频,语音,文本等,时序数据的长度一般是不固定的,而前馈神经网络要求输入和输出的维数都是固定的,不能任意改变。因此,当处理这一类和时序相关的问题时,就需要一种能力更强的模型。循环神经网络(RNN)是一类具有短期记忆能力的神经网络。在循环神经网络中,神经元不但可以接受其他神经元的信息,也可以接受自身的信息,形成具有环路的网络结构。换句话说:神经元的输出可以在下一个时间步直接作用到自身(作为输入)
*RNN类型介绍
a,one-to-one:图像分类
b.one-to-many:图像转文字
c.many-to-one:文本分类
d.异步的many-to-many:文本翻译
e.同步的many-to-many:视频分类
*LSTM和GRU
RNN中存在长期依赖性问题,随着间隔的增加可能会导致真实的预测值对结果的影响变得非常小,而无法非常好地进行预测。LSTM不仅让我们的模型具有短期记忆的能力还能学习到长期的依赖的一些信息
GRU是一种LSTM的变形版本,它将遗忘和输入门合并成一个更新门。它还合并了单元状态和隐藏状态,并进行了一些其他更改,由于他的模型比标准LSTM模型简单,所以越来越受欢迎
双向LSTM,可能需要预测的词语和后面的内容也相关,该机制能够让模型不仅能够从前往后的具有记忆,还需要从后往前需要记忆