1. Why create tensors?
torch.Size`` returns a tuple type, so it supports all operations of the tuple type.
Any operation ending with ``_`` will replace the original variable with the result. For example: ``x.copy_(y)``, ``x.t_()``, will change ``x``.
torch.view : can change the dimension and size of the tensor
If you have a tensor with only one element, use .item() to get the value of the Python data type
Operations related to tensors https://pytorch.org/docs/stable/torch.html
a=torch.ones(5)
b=a.numpy()
a and b share a memory address
Tensor can be moved to any device using the .to method
# is_available 函数判断是否有cuda可以使用
# ``torch.device``将张量移动到指定的设备中
if torch.cuda.is_available():
device = torch.device("cuda") # a CUDA 设备对象
y = torch.ones_like(x, device=device) # 直接从GPU创建张量
x = x.to(device) # 或者直接使用``.to("cuda")``将张量移动到cuda中
z = x + y
print(z)
print(z.to("cpu", torch.double)) # ``.to`` 也会对变量的类型做更改
tensor([0.7632], device='cuda:0')
tensor([0.7632], dtype=torch.float64)
Back Propagation Algorithm (BP)
Automatic differentiation is actually the programming implementation of the differential chain rule
The purpose of the neural network is to find a set of w (weights) to minimize the E (error term), which is the difference between the simulated value and the known actual value
In essence, it is an optimal solution problem - using the gradient descent method
Update formula:
One time as an example, update one by one in the reverse order—w5 first, then w6, then w1, w2, w3, w4, which is called completing a gradient descent.
The core of the problem is to find the pair of error terms
Partial derivative of w - the chain rule
If .requires_grad is set to True, then all operations on the tensor will be tracked
Dynamic Computational Graph
图中的每个点轮廓框是一个变量,紫色矩形框是一个操作。
每个变量对象都有几个成员,其中一些成员是:
Data:它是一个变量持有的数据。 x持有一个1x1张量,其值等于1.0,而 y持有2.0。 z持有两个的乘积,即2.0。
requires_grad:这个成员(如果为true)开始跟踪所有的操作历史,并形成一个用于梯度计算的向后图。对于任意张量 a,可以按如下方式对其进行原地处理:a.requires_grad_(True)。
grad: grad保存梯度值。如果requires_grad 为False,它将持有一个None值。即使requires_grad 为真,它也将持有一个None值,除非从其他节点调用.backward()函数。例如,如果你对 out关于 x计算梯度,调用out.backward(),则x.grad的值为 ∂out/∂x。
grad_fn:这是用来计算梯度的向后函数。
is_leaf:如果:
它被一些函数显式地初始化,比如x = torch.tensor(1.0)或x = torch.randn(1, 1)(基本上是本文开头讨论的所有张量初始化方法)。
它是在张量的操作之后创建的,所有张量都有requires_grad = False。
它是通过对某个张量调用.detach()方法创建的。
在调用backward()时,只计算requires_grad和is_leaf同时为真的节点的梯度。
Backward()函数
Backward函数实际上是通过传递参数(默认情况下是1x1单位张量)来计算梯度的,它通过Backward图一直到每个叶节点,每个叶节点都可以从调用的根张量追溯到叶节点。然后将计算出的梯度存储在每个叶节点的.grad中。 请记住,在正向传递过程中已经动态生成了后向图。 backward函数 仅使用已生成的图形计算梯度,并将其存储在叶节点中。
2,计算z对x的梯度后,x的requires_grad为什么变成了true?
x = torch.tensor([0.0, 2.0, 8.0], requires_grad = True)
y = torch.tensor([5.0 , 1.0 , 7.0], requires_grad = True)
z = x * y
z.backward(torch.FloatTensor([1.0, 1.0, 1.0])
3,为什么会报错?
雅可比矩阵
l是标量损失,这种计算雅可比矩阵并将其与向量 v相乘的方法使PyTorch能够轻松地为非标量输出提供外部梯度
X = [x1,x2,…,xn](假设这是某个机器学习模型的权值)
X经过一些运算形成一个向量 Y
Y = f(X) = [y1, y2,…,ym]
感觉思想还是链式求导,只不过用矩阵、向量的形式表示出来
文献https://cs231n.github.io/optimization-2/#intro
值得一提的是
import torch
x = torch.ones(2,2, requires_grad=True)
x=x+2
z = x*x*3
out=z.mean()
#print(z,out)
print(x.is_leaf) #false
print(x.requires_grad) #true
print(z.is_leaf) #false
print(z.requires_grad) #false
print(out.is_leaf) #false
print(out.requires_grad) #false
out.backward()
print(x.grad) #报错,因为x不是叶子,原本x是叶子,但x=x+2后就不再是叶子了
4,为什么正常情况下输出是4.5而不是18
有疑问的是这张图
SGD随机梯度下降
基本思想是在每一次迭代中,随机选择一个样本作为训练集,计算该样本的梯度,并利用该梯度更新模型参数。由于每次迭代仅考虑一个样本,因此SGD算法具有较快的速度和较小的计算开销,但也容易陷入局部最优解
params:需要优化的参数列表。
lr:学习率,控制参数更新的步长。
momentum:动量因子,控制参数更新的方向。
weight_decay:权重衰减系数,控制模型复杂度。
nesterov:是否使用Nesterov动量。
dampening:动量抑制因子,防止动量过大
import torch
import torch.optim as optim
# 定义模型参数
w = torch.randn(2, requires_grad=True)
b = torch.randn(1, requires_grad=True)
# 定义优化器
optimizer = optim.SGD([w, b], lr=0.1)
# 定义损失函数
loss_fn = torch.nn.MSELoss()
# 训练模型
for i in range(100):
x = torch.randn(2)
y = 2*x[0] - 3*x[1] + 1
# 前向传播
y_pred = torch.dot(w, x) + b
# 计算损失函数
loss = loss_fn(y_pred, y)
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 清空梯度
optimizer.zero_grad()
# 打印损失函数
if (i+1) % 10 == 0:
print('Epoch [{}/{}], Loss: {:.4f}'.format(i+1, 100, loss.item()))
Momentum
在Momentum更新中,权重参数的更新方向不仅取决于当前的梯度,还取决于之前的梯度方向。这个算法的主要思想是利用梯度方向的指数加权平均来更新权重参数
m = β * m + (1 - β) * g
g是本次计算的梯度,β介于0~1之间
AdaGrad
s = s + g^2
lr = α / sqrt(s + epsilon)
w = w - lr * g
AdaGrad算法的主要特点是使用了一个梯度平方和s,这个平方和可以看作是之前所有梯度的累加和。由于每个维度的梯度平方和不同,因此每个维度的学习率也不同,从而可以自适应地调整学习率。这种方法可以有效地降低学习率在训练过程中的震荡,同时可以对每个维度的权重参数进行更加细致的调整,从而提高模型的性能
RMSProp
Adam
神经网络
训练模型:
定义包含一些可学习的参数(或者叫权重)神经网络模型;
在数据集上迭代;
通过神经网络处理输入;
计算损失(输出结果和正确值的差值大小);
将梯度反向传播回网络的参数;
更新网络的参数。
CNN卷积神经网络
线性函数(得分函数)——> y=kx+b
n个像素点有着n个不同的权重,得到每个筛选类别的得分(不同类别权重在像素中分布也不一样)
每个类别的权重分布向量组成一个矩阵,称为k
每个类别的偏重分量组成一个向量,称为b
至于权重分量,一开始是随机的,之后动态更新调整。
回归
深度学习里的回归(regression)指的是一种从输入到输出的映射,它将输入数据映射到一个连续的输出值。在深度学习中,回归任务通常是指给定一个输入,通过神经网络学习一个函数,以预测一个实数或实向量输出
损失函数——衡量得分函数评价是否合理的函数
(上图是交叉熵损失函数)
(root mean squared error)均方根误差
Softmax分类器
将该类别的得分转化为属于该类别的概率值
前向传播——得到损失值
反向传播(梯度下降)——更新权重k,即更新模型
所谓前向与反向,我的理解是梯度下降时需要对函数求导,求导的链式法则是从后到前的,因此是反向,而正常函数是正向进行的,称之为前向
梯度下降
寻找什么样的参数能使目标函数达到极值点
input layer:输入数据的特征数 / 输入数据的列数
hidden layer 1:扩充特征数 /扩充输入数据的列数,“神经元”
不妨举一个例子,输入数据只有一个,特征数有三个,1x3的输入矩阵,扩充成1x4的矩阵,需要乘一个由四组权重分布合并成的3x4矩阵 参考torch.linear()函数
激励函数
神经网络中激励函数的作用通俗上讲就是将多个线性输入转换为非线性的关系。如果不使用激励函数的话,神经网络的每层都只是做线性变换,即使是多层输入叠加后也还是线性变换。通过激励函数引入非线性因素后,使神经网络的表示能力更强了
有时候每步矩阵计算后往往会有一个非线性变换的额外处理,称为激活函数(Sigmoid,Relu,Tanh)
sigmoid会产生梯度消失的情况。(将整个复数范围转换成0到1的实数)
Sigmoid函数:将输入压缩到0到1之间的范围内。它在早期的神经网络中被广泛使用,但现在很少用于隐藏层,因为它容易出现梯度消失的问题。
ReLU函数:ReLU函数(Rectified Linear Unit)是一种简单的非线性函数,它将负数输入转换为零,
将正数输入保留不变。它在现代深度学习中被广泛使用,因为它易于计算,并且在训练期间可以缓解梯度消失的问题。
LeakyReLU函数:LeakyReLU函数是ReLU的一种扩展形式,
它在负数输入上返回一个小的斜率而不是零。这样可以使得神经元在负数输入时也能够产生梯度,从而加速训练。
Tanh
什么样的权重参数才是最适合的!!!
神经网络的强大之处是有非常多的参数
惩罚力度,参数个数(隐层神经元个数)对结果有一定影响
数据初始化——标准化
参数初始化——随机
由于神经网络比较负责,过拟合风险较大,为了消减神经网络复杂性,使用DROP-OUT
DROP-OUT
在每层神经元随机杀死部分神经元,每次训练杀死的神经元不同
卷积层
输入数据不再是一列特征,而是一张图,直接对图进行特征提取
输入层——一张图
卷积层——大概举例子,5x5的图像如果以3x3的比例分切,卷积就是重复使用部分区域,5x5的图像能分成9个3x3的区域,构成一个3x3的特征图。
池化层(汇聚层)——
全连接层——
一维卷积运算
w是滤波器向量中的元素,x是输入信号序列中的元素,y是输出特征序列中的元素
下层是输入的信号序列,与滤波器一起经过卷积运算得到上层的原信号序列的特征,
但特征的具体代表着什么目前没有头绪?
上图看作隐藏层
深度学习中的隐藏层是指位于输入层和输出层之间的一层或多层神经元,其作用是对输入数据进行非线性变换,从而提取数据的特征,增强模型的表达能力。隐藏层的数量、大小和激活函数等超参数是需要调整和优化的重要参数
二纬卷积运算
卷积运算通俗来说,若运算因子w是m*n的矩阵,
则将会在原信号序列矩阵中得到若干m*n的矩阵,与运算因子w的元素翻转后
对应位置相乘后相加的总和就是特征矩阵在该位置元素。
(值得一提的是,为了避免翻转操作使过程繁琐,可以修改公式,称为互相关或者不翻转卷积)
下面的图代表着步长不为1,填充0的情况
一个困境,如下所示:全连接时,两层之间连接十分复杂,于是使用卷积代替
池化层
池化层(汇聚层)
减少神经元个数,避免过拟合
有两类——最大池化,平均池化
不能避免得造成信息损失
循环神经网络——RNN
LSTMRNN
LSTM (Long Short-Term Memory)是一种递归神经网络(RNN),它是为了解决传统RNN中梯度消失和梯度爆炸的问题而提出的。它是由Hochreiter和Schmidhuber在1997年提出的。
LSTM通过添加一些特殊的单元(称为LSTM单元)来扩展传统RNN,从而使网络能够处理长序列数据并且保持长期记忆。这些单元具有控制门(input gate、output gate和forget gate),它们可以控制数据何时进入单元、何时输出数据以及何时遗忘过去的信息。
LSTM单元由三个主要组件组成:输入门(input gate)、遗忘门(forget gate)和输出门(output gate)。输入门控制着新的信息何时进入LSTM单元,遗忘门控制着哪些信息被遗忘,而输出门控制着何时输出当前的LSTM单元状态。这些门是通过一些特殊的神经网络层来实现的,这些层会根据当前输入的值和上一个时间步的状态计算门的开启程度。
通过这些门的控制,LSTM可以选择性地保留或遗忘过去的信息,同时将新的信息融合到现有的信息中
Autoencoder
自编码 非监督学习
自编码器(autoencoder)是一种用于无监督学习的神经网络,用于学习高效的数据表示。它由一个编码器和一个解码器组成,二者协同工作以学习输入数据的压缩表示。
编码器将输入数据转换为较低维度的表示,也称为潜在编码。然后,解码器接收潜在编码,并尝试尽可能精确地重构原始输入数据。自编码器的目标是学习输入数据的压缩表示,以保留尽可能多的重要特征,同时舍弃噪声和其他不重要的细节。
自编码器可用于各种任务,如降维、去噪和图像或音频生成。它们已成功应用于各种领域,包括计算机视觉、自然语言处理和异常检测
DQN
GAN
实战1——搭建一个简单的神经网络
快速搭建
net2 = torch.nn.Sequential(
torch.nn.Linear(1, 10),#第一层
torch.nn.ReLU(),
torch.nn.Linear(10, 1)#第二层
)
保存提取
torch.manual_seed(1) # reproducible
# 假数据
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # x data (tensor), shape=(100, 1)
y = x.pow(2) + 0.2*torch.rand(x.size()) # noisy y data (tensor), shape=(100, 1)
def save():
# 建网络
net1 = torch.nn.Sequential(
torch.nn.Linear(1, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
optimizer = torch.optim.SGD(net1.parameters(), lr=0.5)
loss_func = torch.nn.MSELoss()
# 训练
for t in range(100):
prediction = net1(x)
loss = loss_func(prediction, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
我们有两种方法保存
torch.save(net1, 'net.pkl') # 保存整个网络
torch.save(net1.state_dict(), 'net_params.pkl') # 只保存网络中的参数 (速度快, 占内存少)
提取网络
def restore_net():
# restore entire net1 to net2
net2 = torch.load('net.pkl')
prediction = net2(x)
只提取网络参数
def restore_params():
# 新建 net3
net3 = torch.nn.Sequential(
torch.nn.Linear(1, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
# 将保存的参数复制到 net3
net3.load_state_dict(torch.load('net_params.pkl'))
prediction = net3(x)
显示结果
# 保存 net1 (1. 整个网络, 2. 只有参数)
save()
# 提取整个网络
restore_net()
# 提取网络参数, 复制到新网络
restore_params()
批数据训练
import torch
import torch.utils.data as Data
torch.manual_seed(1) # reproducible
x = torch.linspace(1, 10, 10) # x data (torch tensor)
y = torch.linspace(10, 1, 10) # y data (torch tensor)
# 先转换成 torch 能识别的 Dataset
torch_dataset = Data.TensorDataset(x, y)
# 把 dataset 放入 DataLoader
loader = Data.DataLoader(
dataset=torch_dataset, # torch TensorDataset format
batch_size=5, # mini batch size
shuffle=True, # 要不要打乱数据 (打乱比较好)num_workers=2, # 多线程来读数据
)
print(torch_dataset)
print("start")
print(x)
print(y)
for epoch in range(3): # 训练所有!整套!数据 3 次
for step, (batch_x, batch_y) in enumerate(loader): # 每一步 loader 释放一小批数据用来学习
print('Epoch: ', epoch, '| Step: ', step, '| batch x: ', batch_x.numpy(), '| batch y: ', batch_y.numpy())
<torch.utils.data.dataset.TensorDataset object at 0x121203160>
start
tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.])
tensor([10., 9., 8., 7., 6., 5., 4., 3., 2., 1.])
Epoch: 0 | Step: 0 | batch x: [5. 3. 1. 7. 9.] | batch y: [ 6. 8. 10. 4. 2.]
Epoch: 0 | Step: 1 | batch x: [ 8. 10. 2. 6. 4.] | batch y: [3. 1. 9. 5. 7.]
Epoch: 1 | Step: 0 | batch x: [5. 9. 2. 6. 1.] | batch y: [ 6. 2. 9. 5. 10.]
Epoch: 1 | Step: 1 | batch x: [ 3. 4. 7. 10. 8.] | batch y: [8. 7. 4. 1. 3.]
Epoch: 2 | Step: 0 | batch x: [6. 9. 4. 8. 7.] | batch y: [5. 2. 7. 3. 4.]
Epoch: 2 | Step: 1 | batch x: [10. 3. 2. 1. 5.] | batch y: [ 1. 8. 9. 10. 6.]
加速
optimizer优化器
from typing import List, Any
import torch
import torch.utils.data as Data
import torch.nn.functional as F
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
LR = 0.01
BATCH_SIZE = 8
EPOCH = 1
# fake dataset
x = torch.unsqueeze(torch.linspace(0, 1, 16), dim=1)
y = x.pow(2) + 0.1*torch.normal(torch.zeros(*x.size()))
# plot dataset
# plt.scatter(x.numpy(), y.numpy())
# plt.show()
# 使用上节内容提到的 data loader
torch_dataset = Data.TensorDataset(x, y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True, )
# 默认的 network 形式
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(1, 20) # hidden layer
self.predict = torch.nn.Linear(20, 1) # output layer
def forward(self, x):
x = F.relu(self.hidden(x)) # activation function for hidden layer
x = self.predict(x) # linear output
return x
# 为每个优化器创建一个 net
net_SGD = Net()
net_Momentum = Net()
net_RMSprop = Net()
net_Adam = Net()
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
# different optimizers
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.8)
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]
loss_func = torch.nn.MSELoss()
losses_his = [[], [], [], []] # 记录 training 时不同神经网络的 loss
for epoch in range(EPOCH):
print('Epoch: ', epoch)
for step, (b_x, b_y) in enumerate(loader):
# 对每个优化器, 优化属于他的神经网络
for net, opt, l_his in zip(nets, optimizers, losses_his):
output = net(b_x) # get output for every net
loss = loss_func(output, b_y) # compute loss for every net
opt.zero_grad() # clear gradients for next train
loss.backward() # backpropagation, compute gradients
opt.step() # apply gradients
l_his.append(loss.item()) # loss recoder
labels = ['SGD', 'Momentum', 'RMSprop', 'Adam']
print(x.shape)
print(len(losses_his),len(losses_his[0]),losses_his)
for i, l_his in enumerate(losses_his):
plt.plot(l_his, label=labels[i])
print("i:",i,"size:",len(l_his),"l_his:",l_his)
plt.legend (loc='best')
plt.xlabel('Steps')
plt.ylabel('Loss')
plt.ylim((0, 0.2))
plt.show()
值得一提的是,第63行代码较难理解,建议运行学习
手写数据的分类
torchvision.datasets.MNIST()
是一个PyTorch中的函数,用于加载手写数字识别数据集MNIST。该函数返回一个Dataset对象,其中包含用于训练和测试的图像和标签
import torch
import torchvision
# 加载MNIST训练数据集
train_dataset = torchvision.datasets.MNIST(root='./data',
train=True,
transform=torchvision.transforms.ToTensor(),
download=True)
# 加载MNIST测试数据集
test_dataset = torchvision.datasets.MNIST(root='./data',
train=False,
transform=torchvision.transforms.ToTensor(),
download=True)
root:数据集存储路径,默认为当前目录。
train:True表示加载训练数据集,False表示加载测试数据集,默认为True。
download:True表示如果本地不存在数据集则下载数据集,默认为False。
transform:数据预处理的变换,可以通过 torchvision.transforms模块进行定义,例如 transforms.ToTensor()将数据转换为Tensor对象
返回的数据集包含以下特征
data:一个大小为 [N,1,28,28]的Tensor对象,其中N是数据集大小,1表示灰度图像通道数,28x28表示图像的宽度和高度。
targets:一个大小为 [N]的Tensor对象,表示每个图像的标签
自编码
import torch
import torch.nn as nn
import torch.utils.data as Data
import torchvision
# 超参数
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
EPOCH = 10
BATCH_SIZE = 16
LR = 0.005
DOWNLOAD_MNIST = True # 下过数据的话, 就可以设置成 False
N_TEST_IMG = 5 # 到时候显示 5张图片看效果, 如上图一
# Mnist digits dataset
train_data = torchvision.datasets.MNIST(
root='./mnist/',
train=True, # this is training data
transform=torchvision.transforms.ToTensor(), # Converts a PIL.Image or numpy.ndarray to
# torch.FloatTensor of shape (C x H x W) and normalize in the range [0.0, 1.0]
download=DOWNLOAD_MNIST, # download it if you don't have it
)
train_loader=Data.DataLoader(dataset=train_data,shuffle=True,batch_size=BATCH_SIZE,)
print(1)
class AutoEncoder(nn.Module):
def __init__(self):
super(AutoEncoder, self).__init__()
# 压缩
self.encoder = nn.Sequential(
nn.Linear(28*28, 128),
nn.Tanh(),
nn.Linear(128, 64),
nn.Tanh(),
nn.Linear(64, 12),
nn.Tanh(),
nn.Linear(12, 3), # 压缩成3个特征, 进行 3D 图像可视化
)
# 解压
self.decoder = nn.Sequential(
nn.Linear(3, 12),
nn.Tanh(),
nn.Linear(12, 64),
nn.Tanh(),
nn.Linear(64, 128),
nn.Tanh(),
nn.Linear(128, 28*28),
nn.Sigmoid(), # 激励函数让输出值在 (0, 1)
)
def forward(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return encoded, decoded
print(2)
autoencoder = AutoEncoder()
optimizer = torch.optim.Adam(autoencoder.parameters(), lr=LR)
loss_func = nn.MSELoss()
for epoch in range(EPOCH):
print("run")
for step, (x, b_label) in enumerate(train_loader):
x = x.numpy().copy()
x = torch.from_numpy(x)
b_label = b_label.numpy().copy()
b_label = torch.from_numpy(b_label)
b_x = x.view(-1, 28*28) # batch x, shape (batch, 28*28)
b_y = x.view(-1, 28*28) # batch y, shape (batch, 28*28)
encoded, decoded = autoencoder(b_x)
loss = loss_func(decoded, b_y) # mean square error
optimizer.zero_grad() # clear gradients for this training step
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
# 要观看的数据
print(3)
view_data = train_data.data[:200].view(-1, 28*28).type(torch.FloatTensor)/255.
encoded_data, _ = autoencoder(view_data) # 提取压缩的特征值
# fig = plt.figure(2)
# ax = Axes3D(fig)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 3D 图
# x, y, z 的数据值
X = encoded_data.data[:, 0].numpy()
Y = encoded_data.data[:, 1].numpy()
Z = encoded_data.data[:, 2].numpy()
values = train_data.targets[:200].numpy() # 标签值
for x, y, z, s in zip(X, Y, Z, values):
c1 = cm.rainbow(int(255*s/9)) # 上色
#ax.scatter(x,y,z,c='red')
ax.text(x, y, z, s, backgroundcolor=c1) # 标位子
children = ax.get_children()
ax.set_xlim(X.min(), X.max())
ax.set_ylim(Y.min(), Y.max())
ax.set_zlim(Z.min(), Z.max())
plt.show()
GAN-对抗神经网络
"""
View more, visit my tutorial page: https://mofanpy.com/tutorials/
My Youtube Channel: https://www.youtube.com/user/MorvanZhou
Dependencies:
torch: 0.4
numpy
matplotlib
"""
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
# torch.manual_seed(1) # reproducible
# np.random.seed(1)
# Hyper Parameters
BATCH_SIZE = 64
LR_G = 0.0001 # learning rate for generator
LR_D = 0.0001 # learning rate for discriminator
N_IDEAS = 5 # think of this as number of ideas for generating an art work (Generator)
ART_COMPONENTS = 15 # it could be total point G can draw in the canvas
PAINT_POINTS = np.vstack([np.linspace(-1, 1, ART_COMPONENTS) for _ in range(BATCH_SIZE)])
# show our beautiful painting range
# plt.plot(PAINT_POINTS[0], 2 * np.power(PAINT_POINTS[0], 2) + 1, c='#74BCFF', lw=3, label='upper bound')
# plt.plot(PAINT_POINTS[0], 1 * np.power(PAINT_POINTS[0], 2) + 0, c='#FF9359', lw=3, label='lower bound')
# plt.legend(loc='upper right')
# plt.show()
def artist_works(): # painting from the famous artist (real target)
a = np.random.uniform(1, 2, size=BATCH_SIZE)[:, np.newaxis]
paintings = a * np.power(PAINT_POINTS, 2) + (a - 1)
paintings = torch.from_numpy(paintings).float()
return paintings
G = nn.Sequential( # Generator
nn.Linear(N_IDEAS, 128), # random ideas (could from normal distribution)
nn.ReLU(),
nn.Linear(128, ART_COMPONENTS), # making a painting from these random ideas
)
D = nn.Sequential( # Discriminator
nn.Linear(ART_COMPONENTS, 128), # receive art work either from the famous artist or a newbie like G
nn.ReLU(),
nn.Linear(128, 1),
nn.Sigmoid(), # tell the probability that the art work is made by artist
)
#总的来说,这个神经网络的作用是对给定的艺术品向量进行分类,判断它是真实的艺术品还是伪造的艺术品,以帮助GANs生成器生成更加逼真的艺术品
opt_D = torch.optim.Adam(D.parameters(), lr=LR_D)
opt_G = torch.optim.Adam(G.parameters(), lr=LR_G)
plt.ion() # something about continuous plotting
for step in range(10000):
artist_paintings = artist_works() # real painting from artist(64,15) 原图
G_ideas = torch.randn(BATCH_SIZE, N_IDEAS, requires_grad=True) # random ideas\n (64,5)
G_paintings = G(G_ideas) # fake painting from G (random ideas) (64,15) 伪图
prob_artist1 = D(G_paintings) # D try to reduce this prob (64,1)
G_loss = torch.mean(torch.log(1. - prob_artist1))
#更新参数,使伪造的更不容易被筛选出来
opt_G.zero_grad()
G_loss.backward()
opt_G.step()
prob_artist0 = D(artist_paintings) # D try to increase this prob (64,1) 希望增加
prob_artist1 = D(G_paintings.detach()) # D try to reduce this prob (64,1) 希望减少
D_loss = - torch.mean(torch.log(prob_artist0) + torch.log(1. - prob_artist1))
#该损失函数的最小化将导致鉴别器学习如何区分真实和伪造数据,并提供梯度信号用于生成器的训练。
opt_D.zero_grad()
D_loss.backward(retain_graph=True) # reusing computational graph
opt_D.step()
if step % 50 == 0: # plotting
plt.cla()
plt.plot(PAINT_POINTS[0], G_paintings.data.numpy()[0], c='#4AD631', lw=3, label='Generated painting', )
plt.plot(PAINT_POINTS[0], 2 * np.power(PAINT_POINTS[0], 2) + 1, c='#74BCFF', lw=3, label='upper bound')
plt.plot(PAINT_POINTS[0], 1 * np.power(PAINT_POINTS[0], 2) + 0, c='#FF9359', lw=3, label='lower bound')
plt.text(-.5, 2.3, 'D accuracy=%.2f (0.5 for D to converge)' % prob_artist0.data.numpy().mean(),
fontdict={'size': 13})
plt.text(-.5, 2, 'D score= %.2f (-1.38 for G to converge)' % -D_loss.data.numpy(), fontdict={'size': 13})
plt.ylim((0, 3));
plt.legend(loc='upper right', fontsize=10);
plt.draw();
plt.pause(0.01)
plt.ioff()
plt.show()
动态调整学习率
动态调整学习率的目的是在训练过程中逐渐降低学习率,以便更好地让模型逼近最优解,提高模型的训练效果和准确率。学习率较大时,模型在参数空间中可能会跳过最优解,而学习率较小时,模型在更新参数时可能会停滞在局部最优解而无法跳出。因此,动态调整学习率可以在训练过程中自适应地调整学习率大小,让模型更好地收敛到最优解
批标准化
torch函数
torch.unsqueeze()
它的作用是在指定维度上增加一个维度,将维数增加 1。具体来说,就是将一个形状为 (a, b) 的张量在第 dim 维上增加一个维度,变为形状为 (a, 1, b) 的张量
torch.manual_seed(seed)
是PyTorch中的一个函数,用于设置生成随机数的种子,以确保每次运行代码时生成的随机数是一样的。这在调试和复现实验结果时非常有用。
该函数的参数seed是一个整数,可以是任意整数。通常建议将其设置为一个固定的值,例如1。
当设置了随机数种子后,每次运行程序生成的随机数序列都是一样的。这在训练神经网络时非常重要,因为每次初始化参数时,随机数种子都需要相同,以确保初始化的参数是一样的,从而保证模型的可复现性
torch.linspace()
用于生成一维张量的函数,用于生成一段区间上等间隔的数值。
torch.linspace(start, end, steps=100, dtype=None, layout=torch.strided, device=None, requires_grad=False) → Tensor
start:起始值。
end:终止值。
steps:生成数值的个数,默认值为 100。
dtype:输出的数据类型,默认为 None,表示使用默认的数据类型。可选的数据类型有 torch.float32、 torch.float64、 torch.int32 等。
layout:生成张量的布局,默认为 torch.strided。
device:生成张量所在的设备,默认为 None,表示使用默认设备。
requires_grad:是否需要计算梯度,默认为 False
torch.linear()
torch.nn.Linear(in_features: int, out_features: int, bias: bool = True)
in_features:输入特征的数量。
out_features:输出特征的数量。
bias:是否使用偏置,默认为 True
torch.max()
是 PyTorch 中的一个函数,用于返回输入张量中所有元素的最大值
import torch
a = torch.tensor([[1, 2, 3], [4, 5, 6]])
max_values, max_indices = torch.max(a, dim=1)
print(max_values) # 输出:tensor([3, 6])
print(max_indices) # 输出:tensor([2, 2])
在这个例子中,我们有一个 2x3 的张量 a,它包含两行三列的整数。我们使用 torch.max() 函数在维度 1 上计算最大值,也就是每行中的最大值,输出结果为一个大小为 2 的张量 max_values 和一个大小为 2 的张量 max_indices,其中 max_values 包含每行中的最大值, max_indices 包含每行中最大值的索引位置。
net.parameters()
是一个方法,它返回一个包含模型所有可学习参数的迭代器。这些可学习参数是模型在训练过程中需要不断更新的参数,比如卷积神经网络的卷积核和全连接层的权重等。这些参数通常被初始化为随机值,并在训练过程中不断更新以最小化损失函数。
通过调用net.parameters()方法,我们可以将这些可学习参数传递给优化器,例如PyTorch提供的SGD(随机梯度下降)优化器,用于更新模型参数
torch.ones()
函数用于创建一个指定形状(shape)的张量(tensor),并将所有元素的值设置为1
torch.normal()
是 PyTorch 中用于生成正态分布随机数的函数
torch.normal(mean, std, out=None)
mean:正态分布的均值。
std:正态分布的标准差。
out:输出张量,可选参数
torch.cat()
是一个用于沿着指定维度连接张量的 PyTorch 函数
torch.cat(tensors, dim=0, out=None)
tensors:需要连接的张量序列,可迭代对象或者是一个张量列表。
dim:指定在哪个维度上进行连接,缺省值为 0。
out:可选参数,输出张量
import torch
x1 = torch.tensor([[1, 2, 3], [4, 5, 6]])
x2 = torch.tensor([[7, 8, 9], [10, 11, 12]])
x3 = torch.tensor([[13, 14, 15], [16, 17, 18]])
y = torch.cat((x1, x2, x3), dim=0)
print(y)
输出:tensor([[ 1, 2, 3],
[ 4, 5, 6],
[ 7, 8, 9],
[10, 11, 12],
[13, 14, 15],
[16, 17, 18]])
plt.scatter()
绘制散点图
plt.text()
在图上贴标签
torch.nn.CrossEntropyLoss()
分类任务中的损失函数
loss_func = torch.nn.CrossEntropyLoss()
loss = loss_func(output, target)
例如,预测值是[0.1,,0.2,,0.7]而实际标签值是[0,0,1]
此函数就计算二者的误差
Data.DataLoader()
PyTorch中的一个数据加载器,用于将数据集加载到模型中进行训练或测试。它可以自动对数据集进行切片、洗牌和批处理,并且在每个迭代周期内提供多线程数据加载和异步GPU操作等功能。
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None,
batch_sampler=None, num_workers=0, collate_fn=None,
pin_memory=False, drop_last=False, timeout=0,
worker_init_fn=None, *, prefetch_factor=2,
persistent_workers=False)
dataset:数据集,可以是自定义的数据集类,也可以是PyTorch自带的 torch.utils.data.Dataset类的子类。
batch_size:每个批次的数据量,默认为1。
shuffle:是否打乱数据集,默认为 False。
sampler:数据样本采样器,用于定义数据集的采样策略,如果定义了 sampler,那么 shuffle必须为 False。
num_workers:用于数据加载的子进程数,默认为0,即在主进程中加载数据。
collate_fn:用于将数据样本列表转换为批次张量的函数,默认为使用 torch.stack()函数堆叠数据样本。
pin_memory:是否将数据张量存储在锁页内存中加速数据读取,默认为 False。
drop_last:如果数据集大小不能被批次大小整除,是否舍弃最后一批数据,默认为 False。
timeout:数据加载器在等待数据时的超时时间,默认为0。
worker_init_fn:在数据加载子进程启动时初始化的函数。
prefetch_factor:每个数据加载器缓存的批次数量的倍数,默认为2。
persistent_workers:是否启用数据加载子进程的持久化工作进程模式,默认为 False
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
test_loader = DataLoader(test_dataset, batch_size=64, shuffle=False)
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader):
# 训练模型
for images, labels in test_loader:
# 测试模型
在训练过程中,train_loader会自动迭代训练集中的所有数据样本,并将它们分成大小为64的批次。测试过程同理,只不过数据集是test_dataset
enumerate 是 Python 内置函数之一,用于在迭代对象(如列表,元组等)上迭代时,同时获取当前元素和元素在迭代对象中的索引。
在这段代码中, enumerate(train_loader) 可以将 train_loader 对象转换为包含元组 (batch_index, (images, labels)) 的可迭代对象。其中 batch_index 是一个整数,表示当前批次在数据集中的索引,而 (images, labels) 则是对应的图像和标签。
通过这种方式,我们可以轻松地访问当前批次的图像和标签,并使用它们来训练模型。例如,可以在循环中调用模型的训练函数,将当前批次的图像和标签传递给模型进行训练。同时,还可以使用 enumerate 获得当前批次在数据集中的索引,方便在模型训练过程中打印日志或保存模型等操作
Data.TensorDataset()
是 PyTorch 中的一个数据集类,用于处理张量数据。TensorDataset 可以将一个或多个张量作为数据集,并将它们分别作为训练样本和对应的标签。它的主要作用是将数据集打包成一个数据集对象,这样就可以轻松地使用 PyTorch 中的 DataLoader 对象来处理数据集。
nn.Conv2d()
nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
in_channels:输入数据的通道数。对于 RGB 彩色图像,它的通道数为 3;对于灰度图像,它的通道数为 1。
out_channels:输出数据的通道数,也即 卷积核的数量,控制着卷积层的输出维度。
kernel_size:卷积核的大小,可以是一个整数或者一个二元组。当它是一个整数时,表示卷积核的宽和高相等;当它是一个二元组时,第一个元素表示卷积核的宽,第二个元素表示卷积核的高。
stride:卷积核的步长,可以是一个整数或者一个二元组。当它是一个整数时,表示卷积核在水平和垂直方向上的步长相等;当它是一个二元组时,第一个元 素表示水平方向上的步长,第二个元素表示垂直方向上的步长。
padding:输入数据的填充大小,可以是一个整数或者一个二元组。当它是一个整数时,表示在输入数据的四周各填充相同数量的元素;当它是一个二元组时,第一个元素表示在水平方向上的填充数量,第二个元素表示在垂直方向上的填充数量。
dilation:卷积核的扩张率,可以是一个整数或者一个二元组。当它是一个整数时,表示卷积核在水平和垂直方向上的扩张率相等;当它是一个二元组时,第一个元素表示水平方向上的扩张率,第二个元素表示垂直方向上的扩张率。
groups:输入和输出之间连接的组数。默认值为 1,表示普通的卷积操作。
bias:是否包括偏置项。默认为 True。
padding_mode:填充方式。可以取 "zeros"(默认值)、"reflect" 或 "replicate"
nn.MaxPool2d()
是PyTorch中用于进行2D最大池化操作的函数。其使用方法如下
nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
各参数的含义为:
kernel_size:池化窗口的大小,可以是一个整数或一个元组(宽度,高度)。
stride:池化操作的步长,可以是一个整数或一个元组(横向步长,纵向步长)。默认为 kernel_size。
padding:输入的边界填充大小。默认为0,即不填充。
dilation:池化窗口中每个元素之间的跨度。默认为1。
return_indices:是否返回最大值的索引,默认为False。
ceil_mode:当设置为True时,会使用ceil而不是floor来计算输出形状。默认为False
例子
import torch.nn as nn
import torch
# 创建一个随机输入张量
input_tensor = torch.randn(1, 3, 10, 10)
# 使用nn.MaxPool2d()对张量进行最大池化
maxpool = nn.MaxPool2d(kernel_size=2, stride=2)
output_tensor = maxpool(input_tensor)
# 输出结果的形状
print(output_tensor.shape)
输出结果是1*3*5*5
nn.CrossEntropyLoss()
用于计算交叉熵损失的 PyTorch 中的损失函数。它通常用于多分类任务,其中预测值是一个类别分布,表示为一个概率向量,目标是一个类别的索引。
criterion = nn.CrossEntropyLoss()
loss = criterion(output, target)
其中 output 是模型预测的结果,形状为 (batch_size, num_classes), target 是实际标签的索引,形状为 (batch_size,)。 loss 是一个标量,表示模型预测和实际标签之间的交叉熵损失。
该函数的具体实现方式是先将输出进行 softmax 处理,然后计算 softmax 输出和实际标签之间的交叉熵损失。如果你需要在计算交叉熵损失时自定义权重或忽略某些类别,则可以通过指定 weight 和 ignore_index 参数来实现。
import torch
import torch.nn as nn
# 定义模型输出和目标
output = torch.tensor([[0.1, 0.2, 0.7], [0.9, 0.05, 0.05], [0.1, 0.2, 0.7]])
target = torch.tensor([2, 0, 2])
# 计算交叉熵损失
criterion = nn.CrossEntropyLoss()
loss = criterion(output, target)
print(loss)
tensor(0.9040)
import torch
import torch.nn as nn
# 定义模型输出和目标
output = torch.tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
target = torch.tensor([0, 1, 2])
# 计算交叉熵损失
criterion = nn.CrossEntropyLoss()
loss = criterion(output, target)
print(loss)
view(x.size(0), -1)
这行代码通常在PyTorch中使用,用于重新塑造张量 x的形状。
x.view返回一个与输入张量具有相同数据但形状不同的新张量。在这种情况下, x.size(0)是 x第一维中的元素数,而 -1是一个占位符,告诉PyTorch根据原始张量的大小和第一维的大小推断出第二维的大小。
因此,代码行 x = x.view(x.size(0), -1)将 x重新塑造为一个具有新形状的张量,其中元素数量相同,但维数不同。第一维将具有与之前相同的大小,而第二维将根据原始张量的大小和第一维的大小由PyTorch自动推断。
这行代码的目的通常是将张量 x重新塑造为可以通过神经网络中的线性层的输入。通过将 x展平为2D张量,可以将其乘以线性层的权重矩阵,这需要2D输入
torch.max()[1].data.numpy().squeeze()
pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
具体来说,这段代码可以分为以下几个步骤:
torch.max(test_output, 1):该函数返回 test_output 沿着第1个维度(即列)的最大值,以及该最大值所在的位置。例如,如果 test_output 是一个 m*n的矩阵,则返回一个长度为 m 的元组,其中第 i 个元素为 (max_val, max_idx),其中 max_val 是 test_output[i] 中的最大值,max_idx 是 test_output[i] 中最大值的下标。这里的第二个参数 1 表示沿着第1个维度进行操作,即按列求最大值。
[1]:由于 torch.max 函数返回一个元组,我们只需要其中的第二个元素,即最大值所在的位置,因此使用 [1] 将其取出。
.data.numpy():将返回的位置张量转换为 NumPy 数组。
.squeeze():如果 test_output 的维度为 2,那么 torch.max 函数返回的位置张量会包含一个额外的维度,长度为1,这里使用 .squeeze() 将其压缩掉,以便将其用作标量或一维数组。
最终,pred_y 将得到一个一维的 NumPy 数组,其中每个元素都是对应的预测标签