赞
踩
ISBN 978-7-121-32620-2
import numpy as np import torch #创建一个全是0的空Tensor或者取一个正态分布作为随机初始值 a = torch.zeros((3,2)) b = torch.randn((3,2)) print(a) print(b) #在Tensor和numpy.ndarray之间转换 numpy_b = b.numpy() c = np.array([[2,3],[4,5]]) torch_c = torch.from_numpy(c) float_torch_c = torch_c.float() #将Tensor放到GPU上 if torch.cuda.is_available(): a_cuda = a.cuda() print(a_cuda)
原本需要用Variable变量用于构建计算图进行反向传播,但新版torch似乎Tensor也有这些性质,不再需要专门的torch.autograd.Variable,PyTorch Variable与Tensor 【详解】
Variable变量有三个重要组成属性:data,grad和grad_fn。grad_fn表示的是得到这个Variable的操作,比如通过加减还是乘除来得到的。使用时要注意requires_grad,默认是False。
x = torch.autograd.Variable(torch.Tensor([1]),requires_grad = True)
a = torch.zeros((3,2))
a.requires_grad = True
y = x + a
y.backward()
如果对矩阵求导则不能直接用y.backward(),而是要传入参数声明。backward没有参数的时候,默认对标量求导,有参数的时候,相当是指定了后一层传过来的梯度,根据chain rule,乘即可。可以看到使用retain_graph,接着的求导会进行累加,相当是训练了多个batch,然后梯度进行了累加,然后再一次update参数,而不是每个batch都进行update。(来自Pytorch中backward函数)
dataiter = torch.utils.data.DataLoader(myDataset,batch_size=32,shuffle=True,collate_fn=default_collate)#collate_fn表示如何取样本
在pytorch里编写神经网络,所有的层结构和损失函数都来自于torch.nn,所有的模型结构都是从基类nn.Module继承的。
#使用模板
class net_name(nn.Module):
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
优化算法分为一阶优化算法和二阶优化算法(也叫Hessian方法,主要基于牛顿法),二阶导数计算成本高,故没有广泛使用。
#定义优化器,梯度归零——反向传播——通过梯度进行参数更新
optimizer = torch.optim.SGD(model.parameters(),lr=0.01,momentum=0.9)
optimizer.zero_grad()
loss.backward()
optimizer.step()
#一维线性回归的代码实现 import torch import numpy as np import torch.nn as nn #训练数据 x_train = torch.rand((10,1)) y_train = torch.rand((10,1)) #建立模型 class LinearRegression(nn.Module): def __init__(self): super(LinearRegression,self).__init__() self.linear = nn.Linear(1,1) def forward(self,x): x = self.linear(x) return x model = LinearRegression() #损失函数,使用均方误差函数 criterion = nn.MSELoss() #定义优化器 optimizer = torch.optim.SGD(model.parameters(),lr=1e-3) #模型训练 nums_epoch = 10 for epoch in range(nums_epoch): inputs = x_train target = y_train # inputs.requires_grad = True #out Tensor(10,1) out = model(inputs) loss = criterion(out,target) optimizer.zero_grad() loss.backward() optimizer.step() if (epoch+1) % 2 ==0: print(f'Epoch{epoch+1}/{nums_epoch},loss:{loss.data}') #模型测试 model.eval()#将模型变成测试模式,因为dropout和batchnormlization等层在训练和测试时是不一样的。 predict = model(x_train) #绘图 predict = predict.data.numpy() plt.plot(x_train.numpy(),y_train.numpy(),'ro',label='Original data') plt.plot(x_train.numpy(),predict,label='Fitting Line') plt.show()
#多项式回归模型 #参数方程 y=b+w1*x+w2*x^2+w3*x^3 #真实方程b=0.9,w1=0.5,w2=3,w3=2.4 import torch import torch.nn as nn from matplotlib import pyplot as plt #预处理数据 def make_data(x): '''将输入的数据变成[x,x^2,x^3],方便后续处理''' x = x.unsqueeze(1)#增加一个维度在1维度 4->(4,1) return torch.cat([x**i for i in range(1,4)],1) #将x分别2次方3次方,将三个矩阵在维度1上拼接 #定义真实函数 w_target = torch.Tensor([0.5,3,2.4]).unsqueeze(1)#3->(3,1) b_target = torch.Tensor([0.9]) def f(x): return x.mm(w_target)+b_target[0] #x.mm表示做矩阵乘法 #随机生成一些数据得到训练集 def get_batch(batch_size=4): random = torch.randn(batch_size) x = make_data(random) y = f(x) return x,y #定义多项式模型 class ploy_model(nn.Module): def __init__(self): super(ploy_model,self).__init__() self.ploy = nn.Linear(3,1) def forward(self,x): return self.ploy(x) model = ploy_model() criterion = nn.MSELoss() optimizer = torch.optim.SGD(model.parameters(),lr=1e-3) #训练模型 epoch = 0 while True: batch_x,batch_y = get_batch() output = model(batch_x) loss = criterion(output,batch_y) optimizer.zero_grad() loss.backward() optimizer.step() epoch = epoch+1 print_loss = loss.data if print_loss < 1e-3: break #模型测试 model.eval()#将模型变成测试模式,因为dropout和batchnormlization等层在训练和测试时是不一样的。 x_train,y_train = get_batch() predict = model(x_train) #绘图 predict = predict.data.numpy() plt.plot(x_train.numpy(),y_train.numpy(),'ro',label='Original data') plt.plot(x_train.numpy(),predict,label='Fitting Line') plt.show()
回归问题的预测结果是连续的,而分类问题的预测结果是离散的类别。
sigmoid函数 1 1 + e − x \frac{1}{1+e^{-x}} 1+e−x1
#二分类部分代码
out = model(x)
mask = out.ge(0.5).float()#判断输出结果如果>0.5就等于1,<0.5就等于0
correct = (mask==y).sum()
acc = correct.data/x.size[0]
一般来说logistic回归也可以处理多分类问题,但最常见的还是用于处理二分类问题上。
很大的负梯度经过RELU激活函数,更新参数之后会使得这个神经元不会对任何数据有激活现象?
一般而言,N层神经网络并不会吧输入层算进去,因此一个一层的神经网络是指没有隐藏层,只有输入输出层的网络结构,Logistic回归就是一个一层的神经网络。输出层一般是没有激活函数的,因为输出层通常表示一个类别的得分或者回归的一个实值的目标,所以输出层可以是任意的实数?
公式推导理论证明了梯度下降法的更新公式的有效性。
#多层全连接神经网络实现MNIST手写数字分类 import torch from torch import nn,optim from torch.utils.data import DataLoader from torchvision import datasets,transforms #三层全连接网络 class SimpleNet(nn.Module): def __init__(self,in_dim,hidden_1,hidden_2,out_dim): super(SimpleNet,self).__init__() self.layer1 = nn.Sequential(nn.Linear(in_dim,hidden_1),nn.BatchNorm1d(hidden_1),nn.ReLU(True))#nn.Sequential将网络的层组合在一起 self.layer2 = nn.Sequential(nn.Linear(hidden_1,hidden_2),nn.BatchNorm1d(hidden_2),nn.ReLU(True))#nn.BatchNorm1d批标准化,加快收敛速度 #批标准化一般放在全连接层的后面,激活函数的前面? self.layer3 = nn.Linear(hidden_2,out_dim)#最后一层输出层不能加激活函数 def forward(self,x): x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) return x #超参数 batch_size = 4 learning_rate = 1e-2 num_epoch = 20 #数据预处理 data_tf = transforms.Compose( [transforms.ToTensor(),#ToTensor()在转化过程中pytorch自动将图片标准化了,也就是说Tensor的范围在0~1 transforms.Normalize([0.5],[0.5])]#将图片转化到了-1~1之间 ) #读取数据集 train_dataset = datasets.MNIST( root='./data',train=True,transform=data_tf,download=True) test_dataset = datasets.MNIST( root='./data',train=False,transform=data_tf,download=True) train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True) test_loader = DataLoader(test_dataset,batch_size=batch_size,shuffle=False) #定义模型,损失函数,优化器 model = SimpleNet(in_dim=28*28,hidden_1=300,hidden_2=100,out_dim=10)#图片大小是28*28,一共0-9十个数字 if torch.cuda.is_available(): model = model.cuda() criterion = nn.CrossEntropyLoss()#交叉熵损失函数 optimizer = optim.SGD(model.parameters(),lr=learning_rate) #训练网络 for epoch in range(num_epoch): for data in train_loader: img,label = data img = img.view(img.size(0),-1)#(4,1,28,28)->(4,784) img.requires_grad = True out = model(img)#(4,10) loss = criterion(out,label)#一个数,是不是需要先取出10个中最大的那个作为预测结果再算loss? optimizer.zero_grad() loss.backward() optimizer.step() #模型测试 model.eval() eval_loss = 0 eval_acc = 0 for data in test_loader: img, label = data img = img.view(img.size(0), -1) out = model(img) loss = criterion(out,label) eval_loss += loss.data * label.size(0) _,pred = torch.max(out,1)#result, indices = torch.max(out, 1) num_correct = (pred==label).sum() eval_acc += num_correct.data[0] print(f'Test Loss:{eval_loss/len(test_dataset)}\nAcc:{eval_acc/len(test_dataset)}')
主要层结构有三个:卷积层、池化层、全连接层,其中卷积层和全连接层有参数,而激活层和池化层不含参数,这些参数通过梯度下降法来更新。
逐渐降低数据体的空间尺寸,这样能够减少网络中参数的数量,减少计算资源耗费,同时可以有效控制过拟合。
self.conv = nn.Conv2d(in_channels=,out_channels=,kernel_size=,stride=,padding=,dilation=,groups=,bias=)
#bias默认是True,dilation是空洞卷积的参数
#groups表示输出数据体深度上和输入数据体深度上的联系,默认为1,即所有的输出和所有的输入都是相关联的,
#如果groups=2表示输入的深度被分割成两份,输出的深度也被分割成两份,它们之间分别对应起来。
self.pool = nn.MaxPool2d(kernel_size=,stride=,padding=,dilation=,return_indices=,ceil_mode=)
#其中return_indices表示是否返回最大值所处的下标,默认为False,ceil_mode表示使用一些方格代替层结构,默认False
self.avgpool = nn.AvgPool2d(count_include_pad=)
#其中多的参数count_include_pad表示计算均值时是否包含零填充,默认True
#除此之外还有nn.LPPool2d和nn.AdaptiveAvgPool2d
#简单的多层卷积神经网络 import torch import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self): super(SimpleCNN,self).__init__() layer1 = nn.Sequential() layer1.add_module('conv1',nn.Conv2d(3,32,3,1,padding=1)) layer1.add_module('relu1',nn.ReLU(True)) layer1.add_module('pool1',nn.MaxPool2d(2,2)) self.layer1 = layer1 layer2 = nn.Sequential() layer2.add_module('conv2',nn.Conv2d(32,64,3,1,padding=1)) layer2.add_module('relu2',nn.ReLU(True)) layer2.add_module('pool2',nn.MaxPool2d(2,2)) self.layer2 = layer2 layer3 = nn.Sequential() layer3.add_module('conv3',nn.Conv2d(64,128,3,1,padding=1)) layer3.add_module('relu3',nn.ReLU(True)) layer3.add_module('pool3',nn.MaxPool2d(2,2)) self.layer3 = layer3 layer4 = nn.Sequential() layer4.add_module('fc1',nn.Linear(2048,512)) layer4.add_module('fc_relu1',nn.ReLU(True)) layer4.add_module('fc2',nn.Linear(512,64)) layer4.add_module('fc_relu2',nn.ReLU(True)) layer4.add_module('fc3',nn.Linear(64,10)) self.layer4 = layer4 def forward(self,x): x = self.layer1(x) x = self.layer2(x) x = self.layer3(x) fc_input = x.view(x.size(0),-1) fc_out = self.layer4(fc_input) return fc_out model = SimpleCNN() print(model)
#只提取模型的前两层
new_model = nn.Sequential(*list(model.children())[:2])
print(new_model)
#提取出模型中所有的卷积层
conv_model = nn.Sequential()
for layer in model.named_modules():
if isinstance(layer[1],nn.Conv2d):
conv_model.add_module(layer[0],layer[1])#报错,因为名称为layer1.conv1包含.
print(conv_model)
#children()会返回下一级模块的迭代器(layer1),而不会返回内部的东西(layer1.conv1)
#modules()会返回模型中所有模块的迭代器
#named_children(),named_modules()返回模快的迭代器及网络层的名字
for param in model.named_parameters():
print(param[0])
for m in model.modules():
if isinstance(m,nn.Conv2d):
nn.init.normal_(m.weight.data)#nn.init.normal 函数通过从具有指定均值和标准差的正态分布中采样值来初始化张量中的权重。这有助于打破权重的对称性,并防止神经网络在训练开始时陷入局部极小值。
nn.init.xavier_normal_(m.weight.data)#Xavier 正态初始化是一种权重初始化方案,旨��解决神经网络中的梯度消失和梯度爆炸问题。它通过将权重的方差初始化为 2 / (n_in + n_out) 来实现,其中 n_in 是输入特征的数量,n_out 是输出特征的数量。
nn.init.kaiming_normal_(m.weight.data)#Kaiming 正态初始化是一种权重初始化方案,旨在解决神经网络中的梯度消失和梯度爆炸问题。它通过将权重的方差初始化为 2 / (n_out * (kernel_size ** 2)) 来实现,其中 n_out 是输出特征的数量,kernel_size 是卷积核的大小。
m.bias.data.fill_(0)#fill_ 函数��张量中的所有元素替换为给定的值。在本例中,我们将偏差张量中的所有元素替换为 0。这通常在初始化神经网络或重置其权重时使用。
elif isinstance(m,nn.Linear):
m.weight.data.normal_()#m.weight.data.normal_() 是 PyTorch 中的一个函数,用于将模型 m 的权重张量中的所有元素替换为从标准正态分布中采样的值。
#named_parameters()给出网络层的名字和参数的迭代器
#paramters()会给出一个网络的全部参数的迭代器
#AlexNet网络 8层网络,11*11的滤波器 import torch import torch.nn as nn class AlexNet(nn.Module): def __init__(self,num_classes): super(AlexNet,self).__init__() self.features = nn.Sequential( nn.Conv2d(3,64,11,4,2), nn.ReLU(inplace=True), nn.MaxPool2d(3,2), nn.Conv2d(64,192,5,padding=2), nn.ReLU(True), nn.MaxPool2d(3,2), nn.Conv2d(192,384,3,padding=1), nn.ReLU(True), nn.Conv2d(384,256,3,padding=1), nn.ReLU(True), nn.Conv2d(256,256,3,padding=1), nn.ReLU(True), nn.MaxPool2d(3,2), ) self.classifier = nn.Sequential( nn.Dropout(), nn.Linear(256*6*6,4096), nn.ReLU(True), nn.Dropout(), nn.Linear(4096,4096), nn.ReLU(True), nn.Linear(4096,num_classes), ) def forward(self,x): x = self.features(x) x = x.view(x.size(0),256*6*6) x = self.classifier(x) return x
import torch import torch.nn as nn import torch.nn.init as init class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() # 定义模型的层 # ... def _initialize_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): init.kaiming_normal_(m.weight, mode='fan_out') if m.bias is not None: init.constant_(m.bias, 0) elif isinstance(m, nn.BatchNorm2d): init.constant_(m.weight, 1)#批归一化层(nn.BatchNorm2d)使用常量初始化(权重初始化为 1,偏差初始化为 0) init.constant_(m.bias, 0) elif isinstance(m, nn.Linear): init.xavier_normal_(m.weight) if m.bias is not None: init.constant_(m.bias, 0)
VGGNet有16~19层网络,只使用33的卷积滤波器和22的池化层
#GooLeNet 22层 import torch import torch.nn as nn import torch.nn.functional as F class BasicConv2d(nn.Module): def __init__(self,in_channels,out_channels,**kwargs): super(BasicConv2d,self).__init__() self.conv = nn.Conv2d(in_channels,out_channels,bias=False,**kwargs) self.bn = nn.BatchNorm2d(out_channels,eps=0.001) def forward(self,x): x = self.conv(x) x = self.bn(x) return F.relu(x,inplace = True)#nn.ReLU 是一个神经网络模块,而 F.relu 是一个函数。 #用途: nn.ReLU 用于创建具有 ReLU 激活的层,而 F.relu 用于对张量应用 ReLU 激活。 class Inception(nn.Module): def __init__(self,in_channels,pool_features): super(Inception,self).__init__() self.branch1x1 = BasicConv2d(in_channels,64,kernel_size=1) self.branch5x5_1 = BasicConv2d(in_channels,48,kernel_size=1) self.branch5x5_2 = BasicConv2d(48, 64, kernel_size=5,padding=2) self.branch3x3db1_1 = BasicConv2d(in_channels, 64, kernel_size=1) self.branch3x3db1_2 = BasicConv2d(64, 96, kernel_size=3,padding=1) self.branch3x3db1_3 = BasicConv2d(96, 96, kernel_size=3,padding=1) self.branch_pool = BasicConv2d(in_channels,pool_features,kernel_size=1) def forward(self,x): branch1x1 = self.branch1x1(x) branch5x5 = self.branch5x5_1(x) branch5x5 = self.branch5x5_2(branch5x5) branch3x3db1 = self.branch3x3db1_1(x) branch3x3db1 = self.branch3x3db1_2(branch3x3db1) branch3x3db1 = self.branch3x3db1_3(branch3x3db1) branch_pool = F.avg_pool2d(x,kernel_size=3,stride=1,padding=1) branch_pool = self.branch_pool(branch_pool) outputs = [branch1x1,branch5x5,branch3x3db1,branch_pool] return torch.cat(outputs,1)
#ResNet的残差模块 import torch import torch.nn as nn def conv3x3(in_planes,out_planes,stride=1): '''3x3 conv with padding=1''' return nn.Conv2d(in_planes,out_planes,kernel_size=3,stride=stride,padding=1,bias=False) class BasicBlock(nn.Module): def __init__(self,inplanes,planes,stride=1,downsample=None): super(BasicBlock,self).__init__() self.conv1 = conv3x3(inplanes,planes,stride) self.bn1 = nn.BatchNorm2d(planes) self.relu = nn.ReLU(True) self.conv2 = conv3x3(planes,planes) self.bn2 = nn.BatchNorm2d(planes) self.downsample = downsample#downsample 模块用于在残差块的输入和输出之间进行下采样,如果输入和输出的特征图大小不同的话 self.stride =stride def forward(self,x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out
上述模型在torchvision.model里面,同时大部分网络都有预训练好的参数。
问题:对于计算机来说,光照,物体变形(比如猫换个姿势),物体被遮蔽只呈现局部的信息,这些都会让计算机难以识别。
解决:针对这些问题,我们希望对原始图片进行增强,在一定程度上解决部分问题。
#数据增强
import torchvision.transforms as transforms
train_transform = transforms.Compose([
transforms.Scale(40),
transforms.RandomHorizontalFlip(),
transforms.RandomCrop(32),
transforms.ToTensor(),
transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
])
test_transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize([0.5,0.5,0.5],[0.5,0.5,0.5])
])
注意:只对训练图片进行图像增强,提高其泛化能力,对于测试集,仅对其中心化,不做其他的图像增强。
卷积神经网络相当于人类的视觉,但它并没有记忆能力,没办法根据以前的记忆来处理新的任务。
循环神经网络可以很好解决短时依赖问题,但对长时依赖问题的表现不好。
后面还有单词预测和词性判断的pytorch代码实现。CNN+RNN可以联合在一起完成图像描述任务。
#用全连接网络实现自动编码器 import torch.nn as nn class autoencoder(nn.Module): def __init__(self): super(autoencoder,self).__init__() self.encoder = nn.Sequential(#先对MNIST的数据集做transforms.Normalize()变化使得图片大小变为-1~1之间 nn.Linear(28*28,128), nn.ReLU(True), nn.Linear(128,64), nn.ReLU(True), nn.Linear(64,12), nn.ReLU(True), nn.Linear(12,3) ) self.decoder = nn.Sequential( nn.Linear(3,12), nn.ReLU(True), nn.Linear(12,64), nn.ReLU(True), nn.Linear(64,128), nn.ReLU(True), nn.Linear(128,28*28), nn.Tanh()#这个激活函数能够将最后的输出转换到-1~1之间,因为输入的图片已经变换到-1~1之间,这里输出必须和它对应 ) def forward(self,x): x = self.encoder(x) x = self.decoder(x) return x
#用卷积神经网络实现自动编码器 import torch.nn as nn class DCautoencoder(nn.Module): def __init__(self): super(DCautoencoder,self).__init__() self.encoder = nn.Sequential( nn.Conv2d(1,16,3,3,1), nn.ReLU(True), nn.MaxPool2d(2,2), nn.Conv2d(16,8,3,2,1), nn.ReLU(True), nn.MaxPool2d(2,1) ) self.decoder = nn.Sequential( nn.ConvTranspose2d(8,16,3,2),#ConvTranspose2d看作卷积的反操作,可以在某种意义上看成是反卷积 nn.ReLU(True), nn.ConvTranspose2d(16, 8, 5, 3,1), nn.ReLU(True), nn.ConvTranspose2d(8, 1, 2, 2, 1), nn.Tanh() ) def forward(self,x): x = self.encoder(x) x = self.decoder(x) return x
https://arxiv.org/pdf/1606.05908.pdf
第一部分生成模型和自动编码器的解码部分很像,第二部分对抗模型是其创新点,也是其与自动编码器最大的区别。
书里有代码实现及理论推导和一些改进方法及应用。
# 猫狗大战——迁移学习的三种方式 import torch.nn as nn import torch.optim as optim import torchvision.models as models #方法1——加载预训练权重,改全连接层为我们需要的层 img_classes = 7 transfer_model = models.resnet18(pretrained=True) dim_in = transfer_model.fc.in_features#dim_in=int(512) transfer_model.fc = nn.Linear(dim_in,img_classes) #方法2——固定卷积层参数,只更新全连接层参数 #在1的基础上固定参数即可 for param in transfer_model.parameters(): param.requires_grad = False optimizer = optim.SGD(transfer_model.fc.parameters(),lr=1e-3)#在优化器设置需要优化的参数只有全连接层的参数
方法3
这个很有意思,不更新网络参数而更新图片像素点来可视化每层网络到底学了什么特征。
https://arxiv.org/abs/1508.06576
图片内容的差异性用比较通过预训练网络提取的特征来代替直接比较图片;风格的差异性用Gram矩阵来定义
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。