记:最近学习上有些迷茫,明明有那么多书确不知道该学些什么,好像陷入了死循环一样。好多东西都学得似懂非懂,迷迷糊糊,还是决定开始写一些笔记,加深自己的理解。。。(持续更吧)
一、张量(tensor)问题:(tensor相当于numpy中的ndarray,numpy的替代品)
1、创建一个没有初始化的矩阵:
x=torch.empty(2,3)
print(x)
############result#################
tensor([[0.0000e+00, 0.0000e+00, 1.4013e-45],
[0.0000e+00, 1.4013e-45, 0.0000e+00]])
2、创建一个随机初始化的矩阵:
x=torch.rand(2,3)
print(x)
#################################
tensor([[0.1981, 0.1740, 0.3273],
[0.7940, 0.1450, 0.5020]])
3、构造一个填满0且数据类型为long的矩阵:
x=torch.zeros(2,3,dtype=torch.long)
print(x)
######################################
tensor([[0, 0, 0],
[0, 0, 0]])
4、直接从数据构造张量:
x=torch.tensor([1,2,3,4,5])
print(x)
###############################
tensor([1, 2, 3, 4, 5])
5、根据已有的tensor创建新的tensor:
x=x.new_ones(5,3,dtype=torch.double)
print(x)
y=torch.randn_like(x,dtype=torch.float)
print(y)
print(y.size())
###########################################
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[-1.0281, 1.3334, 0.4002],
[ 0.3715, 0.5247, 1.4494],
[ 0.8633, 0.3223, -0.6000],
[ 1.0817, -0.5652, 1.4704],
[-1.9426, -2.1161, 0.5766]])
torch.Size([5, 3])
6、张量的运算(以加法为例):
1)x+y
2)torch.add(x,y)
3)给定一个输出张量作为参数:
result=torch.empty(5,3)
torch.add(x,y,out=empty)
print(result)
4)原位/原地操作:(注意其后要固定一个—)
x=torch.ones(5)
y=x.add_(1)#此时x,y都会变成2
若x=torch.ones(5)
y=x.add(1)#此时x还是1,而y是2
5)改变其形状:torch.view()#相当于numpy中的reshape()
6)若是仅包含一个元素的tensor,可以使用x.item()来得到对应的python数值
7、将torch的tensor与numpy数组相互转换:
#将tensor转化成numpy数组
a=torch.ones(5)#tensor([1.,1.,1.,1.,1.])
b=a.numpy()#[1. 1. 1. 1. 1.]
a.add_(1)#则a和b中的元素都会变成b
#将numpy数组转化成tensor
a=np.ones(5)
b=torch.from_numpy(a)
np.add(a,1,out=a)
print(a)#[2. 2. 2. 2. 2.]
print(b)#tensor([2.,2.,2.,2.,2.],dtype=torch.float64)
二、Autograd自动求导问题
1、将tensor对象的属性requires_grad设置为True时将会自动追踪该张量对象的操作,可以通过backward()来自动计算整个计算图中的所有梯度,并且保存到grad属性中。使用requeres_grad_()可以改变现有张量的requires_grad属性。
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)
"""
False
True
<SumBackward0 object at 0x000002B0A7EBD048>
"""
2、如果不想张量的操作被追踪,可以使用with torch.no_grad()将代码块封装在其中,一般在测试模型的时候不需要反向传播计算梯度,所以在测试时可以把代码块写到with torch.no_grad()中。
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
"""
True
True
False
"""
3、每个张量都有grad_fn属性,如果该张量是用户手动创建的则grad_fn属性为None,否则该属性是引用了创建Tensor自身的Function。
import torch
#创建tensor,并且设置requires_grad为True
x=torch.ones(2,2,requires_grad=True)
print(x)
#对该张量做运算
y=x+2
print(y)
print(y.grad_fn)#因为y是计算结果,不是手动创建的,所以有该属性
"""
结果:
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
<AddBackward0 object at 0x000002B0A996A278>
"""
4、关于backward,如果Tensor是一个标量(只有一个元素)则不用给backward传送参数,否则要制定一个gradient参数(形状匹配的张量)
z=y*y*3
out=z.mean()#求均值
print(z,out)
#开始反向传播
out.backward()
print(x.grad)#因为前面的操作把out,x,y,z等都联系成了一整个计算图,类似于高数中的链式法则
#对out求反向传播,也会自动求出该计算图中其他张量的梯度,所以我们可以直接输出x的梯度
"""
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward1>)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
"""
注:关于梯度为4.5的来源如下图所示:
三、神经网络(使用torch.nn包构建神经网络)
1、torch.nn只支持小批量处理样本,不支持单个样本。nn.Module中包含了各个层以及一个forward(input)方法,且该方法返回的是output,神经网络的训练过程一般如下:
- 定义包含一些可学习参数(或者叫权重)的神经网络
- 在输入数据集上迭代
- 通过网络处理输入
- 计算损失(输出和正确答案的距离)
- 将梯度反向传播给网络的参数
- 更新网络的权重,一般使用一个简单的规则:
weight = weight - learning_rate * gradient
2、 只需要定义forward函数,backward函数在使用autograd时会自动定义,一个模型的参数可以通过net.parameters()来返回。注意在调用backward时要记得把net中所有参数的梯度清空,防止梯度累加,net.zero_grad()
3、损失函数:根据模型的需要来选择使用什么损失函数,损失函数接受一对(output, target)作为输入,计算一个值来估计网络的输出和目标值相差多少。例如,在pytorch中写法为:criterion=nn.MSELoss() loss=criterion(input,output)
4、更新权重:最简单的是随机梯度下降法,weight=weight-learning_rate*gradient。在使用神经网络时可能会使用不同的更新方法,例如SGD,Adam,RMSProp等,我们可以使用包torch.optim,这个包中实现了所有的更新方法,使用很方便。
import torch.optim as optim
# 创建优化器(optimizer)
optimizer = optim.SGD(net.parameters(), lr=0.01)
# 在训练的迭代中:
optimizer.zero_grad() # 清零梯度缓存
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # 更新参数
四、训练分类器(训练图片分类器)
注:对于视觉方面的torchvision包中有很多常用数据集的加载器(data loaders),例如Imagenet,CIRAR10,MNIST等,接下来的操作都是在CRIFA10数据集上进行的。CRIFA10包含了如下分类,分别是“飞机”,“汽车”,“鸟”,“猫”,“鹿”,“狗”,“青蛙”,“马”,“船”,“卡车”,图片大小为3*32*32,即三通道彩色图片,大小为32*32像素。
步骤如下:
##使用torchvision包加载数据集,并且进行标准化处理
##定义卷积神经网络
##定义损失函数
##利用训练数据训练网络
##利用测试数据测试网络
具体代码连接:https://pytorch.apachecn.org/docs/1.0/blitz_cifar10_tutorial.html
五、数据加载和处理
对于torchvision中包含的数据集我们可以直接加载进来使用,但是对于一个Excel或者一个csv文件中的数据我们需要用其他办法把数据加载进来,并进行相应的处理,本小节用到了scikit-image包(用于图形接口以及变换)和pandas包(便于处理CSV文件)。
1、pandas相关用法:(以下默认导入了包 import pandas as pd 和 import numpy as np)
df=pd.read_csv(r'E:\pytorch_work\data\faces\face_landmarks.csv'),使用绝对路径,添加r是为了防止斜杠/出现错误,比较方便处理,默认csv文件的首行为标题行。
df.head() #查看引入的csv文件的前5行数据
df["image_name"][:5] #查看指定列,后面跟的数据代表查看指定列的前5行数据
df["image_name"][:5].str[:6] #获取指定列的前5行数据的前6个字符串
df["img_n"]=df["image_name"][:10].str[:7] #将指定列的前10行数据的前7个字符串作为新列img_n插入到csv文件中
df.to_csv(r"E:\pytorch_work\data\faces\face_landmarks2.csv") #写入csv文件,重新打开文件如下图所示(已经保存操作后的数据):
2、iloc在csv文件中的一些用法:
df.iloc[0] #得到第1行的数据
df.iloc[4,0] #得到第5行第1列的数据名称
df.iloc[:,0] #得到第一列中所有的数据
df.iloc[3:,4:] #得到第4至m行,5至n列的所有数据
3、as_matrix()在csv文件中的作用:
读取csv或者Excel文件的数据后,在训练模型之前需要对数据进行数组转化,可能直接提取完就是数组形式,但很多时候取得的数据是DataFrame的形式,这个时候要转换成数组形式,可以使用as_matrix(),现在可能会出现警告(因为快被淘汰掉了),可以使用values进行转化,具体如下:
具体数据集代码连接:https://pytorch.apachecn.org/docs/1.0/data_loading_tutorial.html
4、数据集类Dataset class:
torch.utils.data.Dataset是一个代表数据集的抽象类,而我们自定义的数据集类应该继承自Dataset类并重新实现以下方法:
__len__:返回数据集的尺寸
__getitem__:用来获取一些索引数据,可以节省空间,只在用到图片的时候才读取,而不是一开始就把图片全部放进内存里。
#常见数据集类
#os.path.join()函数:连接两个或更多的路径名组件
#1.如果各组件名首字母不包含’/’,则函数会自动加上
#2.如果有一个组件是一个绝对路径,则在它之前的所有组件均会被舍弃
#3.如果最后一个组件为空,则生成的路径以一个’/’分隔符结尾
class FaceLandmarksDataset(Dataset):
def __init__(self,csv_file,root_dir,transform=None):
"""
csv_file(string)带注释的csv文件的路径
root_dir(string)包含所有图像的目录
transform(callable,optional)一个样本上的可用的可选变换
"""
self.landmarks_frame=pd.read_csv(csv_file)
self.root_dir=root_dir
self.transform=transform
def __len__(self):
return len(self.landmarks_frame)
def __getitem__(self,idx):
img_name=os.path.join(self.root_dir,self.landmarks_frame.iloc[idx,0])
image=io.imread(img_name)
landmarks=self.landmarks_frame.iloc[idx,1:]
landmarks=np.array([landmarks])
landmarks=landmarks.astype('float').reshape(-1,2)
sample={'image':image,'landmarks':landmarks}
if self.transform:
sample=self.transform(sample)
return sample
5、Transforms转换:
我们通常会对图片进行一些预处理,下面创建了三个转换,通过__call__方法把他们写成可调用的类的形式,之后我们不需要每次调用都传递一遍参数,
Rescale:缩放图片
RandomCrop:对图片进行随机剪裁,来增强数据
ToTensor:把numpy格式的图片转换为torch格式的图片
具体实现代码见连接:https://pytorch.apachecn.org/docs/1.0/data_loading_tutorial.html
6、torch.utils.data.DataLoader:
这个迭代器可以克服for循环的不足,实现批处理数据,打乱数据和使用多线程并行加载数据,
#DataLoader迭代器
dataloader=DataLoader(transformed_dataset,batch_size=4,shuffle=True,num_workers=0)
#辅助功能,批次显示图片
def show_landmarks_batch(sample_batched):
images_batch,landmarks_batch=
sample_batched['image'],sample_batched['landmarks']
print(images_batch)
print(landmarks_batch)
batch_size=len(images_batch)
im_size=images_batch.size(2)
grid_border_size=2
grid=utils.make_grid(images_batch)
plt.imshow(grid.numpy().transpose(1,2,0))#从torch转换为numpy
for i in range(batch_size):
plt.scatter(landmarks_batch[i,:,0].numpy()+i*im_size+(i+1)*grid_border_size,
landmarks_batch[i,:,1].numpy()+grid_border_size,
s=10,marker='.',c='r')
plt.title('batch from dataloader')
for i_batch, sample_batched in enumerate(dataloader):
print(i_batch,sample_batched['image'].size(),sample_batched['landmarks'].size())
if i_batch==3:
plt.figure()
show_landmarks_batch(sample_batched)
plt.axis('off')
plt.ioff()
plt.show()
break
torchvision中有一个更常用的数据集类ImageFolder,数据集规定以如下形式 构造:
在PIL.Image中也可以使用RandomHorizontalFlip,Scale来进行转换(transforms),那我们就可以按如下方式创建一个加载器:
import torch
from torchvision import transforms, datasets
data_transform = transforms.Compose([
transforms.RandomSizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
hymenoptera_dataset = datasets.ImageFolder(root='hymenoptera_data/train',
transform=data_transform)
dataset_loader = torch.utils.data.DataLoader(hymenoptera_dataset,
batch_size=4, shuffle=True,
num_workers=4)
六、迁移学习
一般来说数量足够大的数据集是很罕见的,所以一般不从头开始训练整个卷积网络,通常在非常大的数据集上预先训练好卷积卷积网络(例如ImageNet数据集),然后使用训练好的卷积网络对我们的目标任务进行初始化或使用固定特征提取器。下面是两种迁移学习的方式:
1、对卷积网络(convnet)进行微调:使用预训练网络初始化网络,而不是随机初始化,其余训练不变。
2、把卷积网络(convnet)作为固定的特征提取器:冻结除完全连接层之外的所有网络的权重,最后一个全连接层被替换为具有随机权重的新层,并且仅训练该层。
#导入相关包
"""
转移学习的两个主要场景:
(1)微调**Convnet**:使用预训练的网络(如在 imagenet 1000 上训练而来的网络)来初始化自己
的网络,而不是随机初始化。其他的训练步骤不变。
(2)将**Convnet**看成固定的特征提取器:首先固定ConvNet除了最后的全连接层外的其他所有
层。最后的全连接层被替换成一个新的随机 初始化的层,只有这个新的层会被训练[只有这层
参数会在反向传播时更新]
"""
#导入相关的包
from __future__ import print_function, division
import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
import copy
plt.ion() # interactive mode
#加载数据
#加载数据,训练一个网络来分类蚂蚁和蜜蜂,各有大约120张训练图,每个类别有75张验证图片
#训练集数据扩充和归一化,在验证集上仅需要归一化
#transforms.Pad(padding,fill=0),将给定的PIL.Image的所有边用给定的pad,value填充,padding:要填充多少像素,fill:可以给图片加边框
data_transforms={
'train':transforms.Compose([
transforms.RandomResizedCrop(224), #随机剪裁一个area然后在resize成给定的size大小
transforms.RandomHorizontalFlip(), #随机水平翻转,概率为0.5,即一半翻转,一半不翻转
transforms.ToTensor(),
transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])
]),
'val':transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224), #将图片从中心剪裁成224*224的大小,RandomCrop(size)是切割中心点的位置随机
transforms.ToTensor(),
transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])
]),
}
data_dir='data/hymenoptera_data'
image_datasets={x:datasets.ImageFolder(os.path.join(data_dir,x),data_transforms[x])
for x in ['train','val']}
dataloaders={x:torch.utils.data.DataLoader(image_datasets[x],batch_size=4,shuffle=True,num_workers=4)
for x in ['train','val']}
dataset_sizes={x:len(image_datasets[x]) for x in ['train','val']}
class_names=image_datasets['train'].classes #数据集中的不同类别
device=torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
#可视化部分图像数据,可做可不做, 只是看一下数据集的情况
#可视化部分图像数据
def imshow(inp,title=None):
inp=inp.numpy().transpose((1,2,0))
mean=np.array([0.485,0.456,0.406])
std=np.array([0.229,0.224,0.225])
inp=std*inp+mean
inp=np.clip(inp,0,1)#截取的意思,超出的部分就把它强置为边界部分。
plt.imshow(inp)
if title is not None:
plt.title(title)
plt.pause(0.001)
inputs,classes=next(iter(dataloaders['train']))
out=torchvision.utils.make_grid(inputs)
imshow(out,title=[class_names[x] for x in classes])
#定义用于训练的模型,把训练结果最好的模型保存起来
#训练模型
#参数scheduler是一个来自 torch.optim.lr_scheduler 的学习速率调整类的对象(LRscheduler object)。
"""
copy.deepcopy()是深复制方法,什么都是复制过来 创建独立内存空间。
不跟着原数据的改变而改变。在pytorch中,torch.nn.Module模块中
的state_dict变量存放训练过程中需要学习的权重和偏执系数。需要注意
的是torch.nn.Module模块中的state_dict只包含卷积层和全连接层的参
数,当网络中存在batchnorm时,torch.nn.Module模块中的state_dict也会存放batchnorm's running_mean
torch.optim模块中的Optimizer优化器对象也存在一个state_dict对象,此处的state_dict字典对象包含state和param_groups的字典对象,而param_groups key对应的value也是一个由学习率,动量等参数组成的一个字典对象。
"""
def train_model(model,criterion,optimizer,scheduler,num_epochs=25):
since=time.time()
best_model_wts=copy.deepcopy(model.state_dict())
best_acc=0.0
for epoch in range(num_epochs):
print('epoch{}/{}'.format(epoch,num_epochs-1))
print('-'*10)
#每一个epoch都包含了一个train和一个val
for phase in ['train','val']:
if phase=='train':
scheduler.step()
model.train()
else:
model.eval()
running_loss=0.0
running_corrects=0
#迭代数据
for inputs,labels in dataloaders[phase]:
inputs=inputs.to(device)
labels=labels.to(device)
optimizer.zero_grad()
#torch.max(a,0) 返回每一列中最大值的那个元素,且返回索引(返回最大元素在这一列的行索引).
#torch.max(a,1) 返回每一行中最大值的那个元素,且返回其索引(返回最大元素在这一行的列索引)
with torch.set_grad_enabled(phase=='train'):#phase == 'train')为True时求导,即只有训练集进行求导
outputs=model(inputs)
_,preds=torch.max(outputs,1)
loss=criterion(outputs,labels)
#向后求导只在训练阶段进行优化
if phase=='train':
loss.backward()
optimizer.step()
running_loss+=loss.item()*inputs.size(0)
#inputs.size(0)为4
running_corrects+=torch.sum(preds==labels.data)
epoch_loss=running_loss/dataset_sizes[phase]
epoch_acc=running_corrects.double()/dataset_sizes[phase]
print('{} loss: {:.4f} acc: {:.4f}'.format(phase,epoch_loss,epoch_acc))
#深度复制model
if phase=='val' and epoch_acc>best_acc:
best_acc=epoch_acc
best_model_wts=copy.deepcopy(model.state_dict())
print()
time_elapsed=time.time()-since
print('training complete in {:.0f}m {:.0f}s'.format(time_elapsed//60,time_elapsed%60))
print('best val acc:{:.4f}'.format(best_acc))
model.load_state_dict(best_model_wts)
return model
#可视化模型的预测结果,定义一个通用的可视化少量图片的函数
#可视化模型的预测结果,一个通用的展示少量预测图片的函数
def visualize_model(model,num_images=6):
was_training=model.training
model.eval()
images_so_far=0
fig=plt.figure()
with torch.no_grad():
for i,(inputs,labels) in enumerate(dataloaders['val']):
inputs=inputs.to(device)
labels=labels.to(device)
print("#######",inputs.size(0),inputs.size()[0])
outputs=model(inputs)
_,preds=torch.max(outputs,1)
for j in range(inputs.size()[0]):
images_so_far+=1
ax=plt.subplot(num_images//2,2,images_so_far)
ax.axis('off')
ax.set_title('predicted:{}'.format(class_names[preds[j]]))
imshow(inputs.cpu().data[j])
if images_so_far==num_images:
model.train(mode=was_training)
return
model.train(mode=was_training)
#使用第一种迁移方法,微调convnet,加载预训练模型并重置全连接层
#微调ConvNet,加载预训练模型并重置最终完全连接的图层
model_ft=models.resnet18(pretrained=True)#直接加载resnet18网络模型
num_ftrs=model_ft.fc.in_features#获取全连接层输入特征
print("全连接层输入特征:",num_ftrs)
model_ft.fc=nn.Linear(num_ftrs,2)#重置全连接层
print("重置后的全连接层:",model_ft.fc)
model_ft=model_ft.to(device)
criterion=nn.CrossEntropyLoss()
#观察所有参数都正在优化
optimizer_ft=optim.SGD(model_ft.parameters(),lr=0.001,momentum=0.9)
#每7个epochs衰减LR通过设置gamma=0.1,学习率更新,在每step_size个epoch,每组参数的学习率都以gramma倍衰减。
exp_lr_scheduler=lr_scheduler.StepLR(optimizer_ft,step_size=7,gamma=0.1)
#训练和评估模型
model_ft=train_model(model_ft,criterion,optimizer_ft,exp_lr_scheduler,num_epochs=25)
visualize_model(model_ft)
#使用第二种迁移方法,冻结除最后一层之外的所有网络,重置最终的全连接层
#ConvNet作为固定特征提取器,在这里,我们需要冻结除最后一层之外的所有网络。
#我们需要设置 requires_grad == False 冻结参数,以便在 backward() 中不计算梯度。
model_conv=torchvision.models.resnet18(pretrained=True)
for param in model_conv.parameters():
param.requeres_grad=False#冻结参数
num_ftrs=model_conv.fc.in_features#提取全连接层输入特征数
model_conv.fc=nn.Linear(num_ftrs,2)#重置全连接层,冻结原有参数,只对初始化的全连接层进行训练
model_conv=model_conv.to(device)
criterion=nn.CrossEntropyLoss()
optimizer_conv=optim.SGD(model_conv.fc.parameters(),lr=0.001,momentum=0.9)
exp_lr_scheduler=lr_scheduler.StepLR(optimizer_conv,step_size=7,gamma=0.1)
#训练和评估
model_conv=train_model(model_conv,criterion,optimizer_conv,exp_lr_scheduler,num_epochs=25)
#模型评估效果可视化
visualize_model(model_conv)
plt.ioff()
plt.show()
七、保存和加载模型
1、有关状态字典state_dict:
在上面的迁移学习方法中有用到state_dict,torch.nn.Module模型的可学习参数(权重和偏差)包含在模型的parameters中,state_dict是字典对象,它将每一层映射到其参数张量。但只有具有可学习参数的层(卷积层,线性层)的模型才具有state_dict这一项。
举个栗子:
# 定义一个模型
class TheModelClass(nn.Module):
def __init__(self):
super(TheModelClass, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
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 = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
# 初始化模型
model = TheModelClass()
# 初始化优化器
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
# 输出模型的state_dict
print("Model's state_dict:")
for param_tensor in model.state_dict():
print(param_tensor, "\t", model.state_dict()[param_tensor].size())
# 输出优化器的 state_dict
print("Optimizer's state_dict:")
for var_name in optimizer.state_dict():
print(var_name, "\t", optimizer.state_dict()[var_name])
"""
输出结果:
Model's state_dict:
conv1.weight torch.Size([6, 3, 5, 5])
conv1.bias torch.Size([6])
conv2.weight torch.Size([16, 6, 5, 5])
conv2.bias torch.Size([16])
fc1.weight torch.Size([120, 400])
fc1.bias torch.Size([120])
fc2.weight torch.Size([84, 120])
fc2.bias torch.Size([84])
fc3.weight torch.Size([10, 84])
fc3.bias torch.Size([10])
Optimizer's state_dict:
state {}
param_groups [{'lr': 0.001, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [4675713712, 4675713784, 4675714000, 4675714072, 4675714216, 4675714288, 4675714432, 4675714504, 4675714648, 4675714720]}]
"""
2、保存和加载state_dict:
####保存####
torch.save(model.state_dict(),PATH)
####加载####
model=TheModelClass(*args,**kwargs)
model.load_state_dict(torch.load(PATH))#load_state_dict()函数只接受字典对象,而不能是对象的路径,所以必须使用load反序列化参数
model.eval()
3、保存和加载完整的模型:
####保存####
torch.save(model,PATH)
####加载####
model=torch.load(PATH)
model.eval()
注意:以上两种方式在运行之前务必调用model.eval()来设置dropout和batch normalization层为评估层。