仅仅是学习记录笔记,搬运了学习课程的ppt内容,本意不是抄袭!望大家不要误解!纯属学习记录笔记!!!!!!
一、梯度下降中的两个关键问题
1 找出梯度向量的方向和大小
2 让坐标点移动起来(进行一次迭代)
二、找出距离和方向:反向传播
1 反向传播的定义与价值
单层的反向传播计算已经很复杂了,双层的只会更复杂,所以我们通过链式法则来解决这个问题:
对于单层的神经网络来说,梯度计算过程如下:
对于双层的神经网络来说,梯度计算过程如下:
2 PyTorch实现反向传播
import torch
x = torch.tensor(1., requires_grad=True) #requires_grad表示允许对x进行梯度计算
y = x ** 2
print(torch.autograd.grad(y, x))
#求y对x求导在x=1上的值
#(tensor(2.),)
#导入库、数据、定义神经网络类,完成正向传播
#继承nn.Module类完成正向传播
import torch
import torch.nn as nn
from torch.nn import functional as F
#确定数据
torch.manual_seed(420)
X = torch.rand((500,20),dtype=torch.float32) * 100
y = torch.randint(low=0,high=3,size=(500,1),dtype=torch.float32)
#定义神经网路的架构
"""
注意:这是一个三分类的神经网络,因此我们需要调用的损失函数多分类交叉熵函数CEL
CEL类已经内置了sigmoid功能,因此我们需要修改一下网络架构,删除forward函数中输出层上的sigmoid
函数,并将最终的输出修改为zhat
"""
class Model(nn.Module):
def __init__(self,in_features=10,out_features=2):
super(Model,self).__init__() #super(请查找这个类的父类,请使用找到的父类替换现在
的类)
self.linear1 = nn.Linear(in_features,13,bias=True) #输入层不用写,这里是隐藏
层的第一层
self.linear2 = nn.Linear(13,8,bias=True)
self.output = nn.Linear(8,out_features,bias=True)
def forward(self, x):
z1 = self.linear1(x)
sigma1 = torch.relu(z1)
z2 = self.linear2(sigma1)
sigma2 = torch.sigmoid(z2)
z3 = self.output(sigma2)
#sigma3 = F.softmax(z3,dim=1)
return z3
input_ = X.shape[1] #特征的数目
output_ = len(y.unique()) #分类的数目
#实例化神经网络类
torch.manual_seed(420)
net = Model(in_features=input_, out_features=output_)
#前向传播
zhat = net.forward(X)
#定义损失函数
criterion = nn.CrossEntropyLoss()
#对打包好的CorssEnrtopyLoss而言,只需要输入zhat
loss = criterion(zhat,y.reshape(500).long())
loss
net.linear1.weight.grad #不会返回任何值
#反向传播,backward是任意损失函数类都可以调用的方法,对任意损失函数,backward都会求解其中全部
w的梯度
loss.backward()
net.linear1.weight.grad #返回相应的梯度
#与可以重复进行的正向传播不同,一次正向传播后,反向传播只能进行一次
#如果希望能够重复进行反向传播,可以在进行第一次反向传播的时候加上参数retain_graph
loss.backward(retain_graph=True)
loss.backward()
三、移动坐标点
1 走出第一步
#在这里,我们的数据是生成的随机数,为了让大家看出效果,所以我才设置了步长为10,正常不会使用这么
大的步长
#步长、学习率的英文是learning rate,所以常常简写为lr
lr = 10
dw = net.linear1.weight.grad
w = net.linear1.weight.data
#对任意w可以有
w -= lr * dw
2 从第一步到第二步:动量法Momentum
#恢复小步长
lr = 0.1
gamma = 0.9
dw = net.linear1.weight.grad
w = net.linear1.weight.data
v = torch.zeros(dw.shape[0],dw.shape[1])
#v要能够跟dw相减,因此必须和dw保持相同的结构,初始v为0,但后续v会越来越大
#对任意w可以有
v = gamma * v - lr * dw
w -= v
w
#不难发现,当加入gamma之后,即便是较小的步长,也可以让w发生变化
3 torch.optim实现带动量的梯度下降
#导入库、数据、定义神经网络类,完成正向传播
import torch
import torch.nn as nn
from torch.nn import functional as F
from torch import optim
#确定数据
torch.manual_seed(420)
X = torch.rand((500, 20), dtype=torch.float32)
y = torch.randint(low=0, high=2, size=(500, 1), dtype=torch.float32)
#确定数据、确定优先需要设置的值
lr = 0.1
gamma = 0.9
#定义神经网络的架构
class Model(nn.Module):
def __init__(self, input_features, output_features):
super(Model, self).__init__()
self.Linear1 = nn.Linear(input_features, 13, bias=True)
self.Linear2 = nn.Linear(13, 8, bias=True)
self.Output = nn.Linear(8, output_features)
def forward(self, x):
z1 = self.Linear1(x)
sigma1 = torch.relu(z1)
z2 = self.Linear2(sigma1)
sigma2 = torch.sigmoid(z2)
z3 = self.Output(sigma2)
#因为后面要用到多分类交叉损失熵,所以最后一层输出之后没有经过激活函数,到时候直接使用log_softmax即可
return z3
input_ = X.shape[1] #输入的是特征的数目,不是样本数量
#print(input_)
output_ = len(y.unique())
#实例化神经网络类
torch.manual_seed(420)
net = Model(input_features=input_, output_features=output_)
#print(net.parameters())
#查看神经网络的参数,包括多个神经网络层中的w和b
#定义损失函数
criterion = nn.CrossEntropyLoss()
#定义优化算法
opt = optim.SGD(net.parameters(), lr=lr, momentum=gamma)
#optim需要三个数据,我们要优化的参数,学习率和动量参数
#接下来我们进行一轮梯度下降
zhat = net.forward(X) #向前传播
loss = criterion(zhat, y.reshape(500).long()) #损失函数值
loss.backward() #向后传播
opt.step() #更新权重w,这一瞬间,坐标点就发生了变化,所有的梯度必须重新计算
opt.zero_grad()
四、开始迭代:batch_size与epoches
1 为什么要有小批量?
mini-batch SGD的优势是算法不会轻易陷入局部最优,由于每次梯度向量的方向都会发生巨大变化,因此一旦有机会,算法就能够跳出局部最优,走向全局最优(当然也有可能是跳出一个局部最优,走向另一个局部最优)。不过缺点是,需要的迭代次数变得不明。如果最开始就在全局最优的范围内,那可能只需要非常少的迭代次数就收敛,但是如果最开始落入了局部最优的范围,或全局最优与局部最优的差异很小,那可能需要花很长的时间、经过很多次迭代才能够收敛,毕竟不断改变的方向会让迭代的路线变得曲折。从整体来看,为了mini-batch SGD这“不会轻易被局部最优困住”的优点,我们在神经网络中使用它作为优化算法(或优化算法的基础)。当然,还有另一个流传更广、更为认知的理由支持我们使用minibatch SGD:mini-batch SGD可以提升神经网络的计算效率,让神经网络计算更快。
2 batch_size与epoches
在mini-batch SGD中,我们选择的批量batch含有的样本数被称为batch_size,批量尺寸,这个尺寸一定是小于数据量的某个正整数值。每次迭代之前,我们需要从数据集中抽取batch_size个数据用于训练。
重要概念:epoch,epoch是衡量训练数据被使用次数的单位,一个epoch表示优化算法将全部训练数据都使用了一次。它与梯度下降中的迭代次数有非常深的关系,我们常使用“完成1个epoch需要n次迭代“这样的语言。
3 TensorDataset与DataLoader
from torch.utils.data import TensorDataset
a = torch.randn(500, 2, 3)
b = torch.randn(500, 3, 4, 5)
c = torch.randn(500, 1)
#打包特征和标签的工具TensorDataset
print(TensorDataset(a, b, c)[0])#在第一个维度上将a,b,c张量进行拼接,拼接的一个准则是最外层的维度一定要一致
print(TensorDataset(a, c)[0])
当我们将数据打包成一个对象之后,我们需要使用划分小批量的功能DataLoader。DataLoader是处理训练前专用的功能,它可以接受任意形式的数组、张量作为输入,并把他们一次性转换为神经网络可以接入的tensor。
from torch.utils.data import DataLoader
import numpy as np
DataLoader(np.random.randn(5, 2))
for i in DataLoader(np.random.randn(5, 2)):
print(i)
break
#tensor([[-0.5758, 1.0728]], dtype=torch.float64) 因为numpy生成array数组的时候,对于浮点型来说,优先生成float64
for i in DataLoader(torch.randn(5, 2, dtype=torch.float32)):
print(i.dtype)
break
#torch.float32
data = DataLoader(torch.randn(500, 2), batch_size=120, shuffle=True, drop_last=True)
#drop_last如果设置为true的话,当最后一个batch样本不够组成一个batch_size时,就会抛掉最后一个batch
#设置为False,则会保留一个较小的batch,默认情况下是False
#查看一下生成的对象
for i in data:
print(i.shape)
'''
torch.Size([120, 2])
torch.Size([120, 2])
torch.Size([120, 2])
torch.Size([120, 2])
'''
对于小批量随机梯度下降而言,我们一般这样使用TensorDataset与DataLoader:
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
torch.manual_seed(420)
X = torch.rand((50000, 20), dtype=torch.float32)
y = torch.randint(low=0, high=3, size=(50000, 1), dtype=torch.float32)
epochs = 4
bs = 4000
data = TensorDataset(X, y)
batchdata = DataLoader(data, batch_size=bs, shuffle=True)
print(len(batchdata)) #13
#可以使用.datasets查看数据集相关的属性
print(batchdata.dataset)
#查看不到详细的内容,正常来说,batchdata会包括一个索引值,20个x特征值和1个y标签值
#<torch.utils.data.dataset.TensorDataset object at 0x0000022D23C1FA08>
print(enumerate(batchdata.dataset))
'''
(tensor([0.8054, 0.1990, 0.9759, 0.1028, 0.3475, 0.1554, 0.8856, 0.6876, 0.2506,
0.1133, 0.2105, 0.4035, 0.2448, 0.8644, 0.2896, 0.1729, 0.3458, 0.0117,
0.2572, 0.2272]),
tensor([2.]))
'''
#batchdata中第一个样本是20个特征及其标签值
#查看第一个样本的特征
#print(batchdata.dataset[0][0])
'''
tensor([0.8054, 0.1990, 0.9759, 0.1028, 0.3475, 0.1554, 0.8856, 0.6876, 0.2506,
0.1133, 0.2105, 0.4035, 0.2448, 0.8644, 0.2896, 0.1729, 0.3458, 0.0117,
0.2572, 0.2272])
'''
print(batchdata.dataset[0][1])
#tensor([2.])
#属性batch_size,查看batch_size是多少
print(batchdata.batch_size)
#4000
for batch_idx, (x,y) in enumerate(batchdata):
#sigma = net(x)
#loss = lossfn(sigma, y)
#loss.backward()
#opt.step()
#opt.zero_grad()
print(x.shape)
print(y.shape)
print(x,y)
if batch_idx == 2:
break #为了演示用,所以打断,在正常的循环里是不会打断的
除了自己生成数据之外,我们还可以将外部导入的数据放到TensorDataset与DataLoader里来使用:
#导入sklearn中的数据
from sklearn.datasets import load_breast_cancer as LBC
data = LBC()
X = torch.tensor(data.data, dtype=torch.float32)
y = torch.tensor(data.target, dtype=torch.float32)
data = TensorDataset(X, y)
batchdata = DataLoader(data, batch_size=5, shuffle=True)
'''
for x, y in batchdata:
print(x)
break
'''
#验证有效
导入外部数据,通过pandas导入
import pandas as pd
import numpy as np
data = pd.read_csv("creditcard.csv")
print(data.shape)
#(284807, 31)
x = torch.tensor(np.array(data.iloc[:, :-1]), dtype=torch.float32)
y = torch.tensor(np.array(data.iloc[:, -1]), dtype=torch.float32)
data = TensorDataset(x, y)
batchdata = DataLoader(data, batch_size=1000, shuffle=True)
for x, y in batchdata:
print(x)
break
#实验验证是可以的
五、在MINST-FASHION上实现神经网络的学习流程
导库+设置各种初始值
#1、导库,设置各种初始值
import torch
import torch.nn
from torch import optim
from torch import functional as F
from torch.utils.data import DataLoader
from torch.utils.data import TensorDataset
lr = 0.1
gamma = 0
epochs = 10
batch_size = 128
#2、导入数据,分割小批量
import torchvision
import torchvision.transforms as transforms
minst = torchvision.datasets.FashionMNIST(
root='/Users/gaoyuxing/Desktop/MINST-FASHION数据集',
train=True,
download=True, #如果电脑上不存在这个数据集可以允许下载
transform=transforms.ToTensor())
print(len(minst))
#60000 数据样本有6万个
#查看特征张量
print(minst.data)
#查看标签值
print(minst.targets)
#查看标签的类别
print(minst.classes)
#['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']一共九种类别
查看图像的模样
#查看图像的模样
import matplotlib.pyplot as plt
import numpy
#print(minst) #查看数据的介绍
'''
Dataset FashionMNIST
Number of datapoints: 60000
Root location: /Users/gaoyuxing/Desktop/MINST-FASHION数据集
Split: Train
StandardTransform
Transform: ToTensor()
'''
#print(minst[0])
#查看的结果显示每个样本包括两个内容,第一是特征张量,第二个是对应的标签
#print(minst[0][0]) #绘制图像的时候我们只选取其特征向量进行读取
#print(minst[0][0].shape)
#torch.Size([1, 28, 28]) 代表这个数据是有通道的,显示图像的时候我们需要改变其形状,转变为长和宽即可
plt.imshow(minst[0][0].view(28, 28).numpy()) #转变为array数组
plt.show()
第一个样本长得像是一只鞋,然后我们继续查看第二个样本
plt.imshow(minst[1][0].view(28, 28).numpy()) #转变为array数组
plt.show()
看起来是一个T恤
分割batch_size
#分割batch_size
batchdata = DataLoader(minst, batch_size=batch_size, shuffle=True)
#print(len(batchdata))
#60000/128=468.75 469
#查看会放入进行迭代的数据结构
for x, y in batchdata:
print(x.shape) #torch.Size([128, 1, 28, 28])
print(y.shape) #torch.Size([128])
break
#查看了数据的结构之后我们需要思考为了满足神经网络需要的数据形状,时候需要对现有的数据形状进行处理
#目前的数据是一个思维结构,128是索引,1,28,28特征的形状,接下来我们需要将特征拉平成128,1*28*28的结构,则输入层的神经元就是28*28个
print(minst.data[0]) #可以读取第一个样本的特征值
print(minst.data[0].shape) #torch.Size([28, 28]) #第一个样本的特征值形状
print(minst.data[0].numel()) #784可以得到这个特征张量中一共有多少元素,这个数量就是我们输入层的神经元的个数
定义神经网络的架构
#定义网络结构
class Model(nn.Module):
def __init__(self, input_feature, output_feature):
super(Model, self).__init__()
self.linear1 = nn.Linear(input_feature, 128, bias=True)
self.Output = nn.Linear(128, output_feature, bias=True)
def forward(self, x):
#外部输入的数据,我们一定要把特征拉平,每个样本的第2个维度是必须是28*28才对
x = x.view(-1, 28*28)
#-1是占位符,意思是要求pytorch自己计算这个位置上的维度值
z1 = self.linear1(x)
sigma1 = torch.relu(z1)
z2 = self.Output(sigma1)
sigma2 = F.log_softmax(z2, dim=1)
return sigma2
在这里介绍一下view(-1,*)的功能,当我们定义好一个维度的大小时候,为了简便,可以要求pytorch自动计算另一个维度的大小
x = torch.rand(30, 40)
x = x.view(-1, 20)
print(x.shape)
#torch.Size([60, 20])
定义训练函数
#定义训练函数
def fit(net, batchdata, lr, epochs, gamma):
criterion = nn.NLLLoss() #实力化损失函数
#有了损失函数之后就要进行梯度下降求解,我们这里用到的是小批量随机梯度下降
opt = optim.SGD(net.parameters(), lr=lr, momentum=gamma) #由于我们是对权重进行优化,所以我们当然需要网络参数,学习率和动量参数
samples = 0
correct = 0
'''
对于每一个epoch里面的每一个batch_size都要进行小批量梯度下降解
首先重新定义了y的形状
定义损失函数里面输入的预测值,这里根据损失函数使用的不同,输入的参数也不同
定义具体的损失函数
对损失函数进行反向传播
走出一步动量梯度
对原来的梯度值进行情况
'''
for epoch in range(epochs):
for batch_idx, (x, y) in enumerate(batchdata):
y = y.view(x.shape[0]) #我们在做交叉熵计算的时候y一定是一维张量
sigma = net.forward(x)
loss = criterion(sigma, y)
loss.backward() #反向传播
opt.step()
opt.zero_grad()
yhat = torch.max(sigma, 1)[1]
samples += x.shape[0]
correct += torch.sum(yhat == y)
if (batch_idx + 1) % 125 == 0 or batch_idx == len(batchdata) - 1:
print('Epoch{}:[{}/{}({:.0f}%)]\tLoss:{:.6f}\t Accuracy:{:.3f}'.format(epoch + 1,
samples,
len(batchdata.dataset) * epochs,
100 * samples / (len(batchdata.dataset) * epochs),
loss.data.item(),
float(correct * 100) / samples))
#进行训练与评估
torch.manual_seed(420)
net = Model(input_feature=input_, output_feature=output_)
fit(net, batchdata, lr=lr, epochs=epochs, gamma=gamma)
对torch.max()详细使用一下
import torch
a = torch.tensor([[.1, .4, .3, .5], [.2, .3, .6, .8]])
print(torch.max(a))
#tensor(0.8000)
#可以给出整个张量的最大值,但我们需要的内含的一维张量上的最大值
print(torch.max(a, 1))
'''
torch.return_types.max(
values=tensor([0.5000, 0.8000]),
indices=tensor([3, 3]))
'''
#然后我们可以看出来,这里会给出每个样本中的最大值
print(torch.max(a, 1)[1])
#得到每个最大值的索引——也就是对应着的标签值
#tensor([3, 3])