这个是针对想冻结网络model里面的某些参数不参与训练时使用的。并且它是会返回一个新的tensor。
y = x.detach()
这种情况下,y和x共同一个内存,即一个修改另一个也会跟着改变。此时x还是可以正常求导。所以很多时候用的时候是x = x.detach()。
torch.cat不会增加新的维度,原来几个维度,还是几个维度
torch.stack会增加一个新的维度,让n维的tensor变成n+1维
x1_torch = torch.zeros(3,1)
y1_torch = torch.ones(3,1)
xy_1 = torch.cat([x1_torch, y1_torch], dim = 1)
xy_2 = torch.stack([x1_torch, y1_torch], dim=1)
xy_1的shape=(3,2)
xy_1 = tensor(
[[0., 1.],
[0., 1.],
[0., 1.]])
xy_2的shape= (3,2,1)
xy_2 = tensor(
[ [[0.],[1.]],
[[0.],[1.]],
[[0.], [1.]] ])
x = torch.Tensor([[1,2],[3,4]])
矩阵相乘 按照矩阵运算法则:
y1 = x @ x.T
y2 = x.matmul(x.T)
y1=y2=tensor(
[[ 5., 11.],
[11., 25.]])
元素之间相乘:
y3 = x * x.T
y4 = x.mul(x.T)
y3 = y4 = tensor(
[[ 1., 4.],
[ 9., 16.]])
与numpy数据交换:
Tensor --> numpy
x_numpy = x_torch.numpy()
numpy --> Tensor
x_torch = torch.from_numpy(x_numpy)
但是需要注意的是,这两个之间共享底层存储,所以更改一个会影响另一个。如果要想阻断之间的关系,可以在numpy数据后面加上 copy()。
pytorch的数据处理中最关键的就是这两个类
torch.utils.data.DataLoader
主要用于训练的迭代器,使用比较简单 train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
torch.utils.data.Dataset
做自己数据集的关键类,最关键的是继承类中一定要重载__init__
、__len__
、__getitem__
这三个函数。常用模板如下: class Dataset_name(Dataset):
def __init__(self, flag='train'):
assert flag in ['train', 'test', 'valid']
self.flag = flag
self.__load_data__()
def __getitem__(self, index):
pass
def __len__(self):
pass
def __load_data__(self, csv_paths: list):
pass
print(
"train_X.shape:{}\ntrain_Y.shape:{}\nvalid_X.shape:{}\nvalid_Y.shape:{}\n"
.format(self.train_X.shape, self.train_Y.shape, self.valid_X.shape, self.valid_Y.shape))
`
在训练之前,数据基本都需要进行数据增强,各种剪裁等数据变换,因此这个模块是必不可少的。
这是一个类,主要作用是把所有的数据处理集合成一个操作
Torchvision.transform
里面的方法组成的list
T = transforms.Compose([transforms.CenterCrop(10), transforms.ToTensor()])
ToTensor()
方法放到第一个。它调用定义的源码为: def __call__(self, img):
for t in self.transforms:
img = t(img)
return img
Tensor
类型。 transforms = torch.nn.Sequential(
transforms.CenterCrop(10),
transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
)
scripted_transforms = torch.jit.script(transforms)
因为图片的读取一般分为opencv和PIL,但都需要转为tensor
,或者互转,opencv中的存储方式为(h,w,c),但是tensor中一般都存储为(c,h,w),而pil也是一种特殊的格式;但是这一切都可以用ToTensor一键搞定。
#img_cv.shape = (448, 800, 3)
#img_pil.size = (800, 448)
cv2tensor = torchvision.transforms.ToTensor()(img_cv)
#cv2tensor.shape : torch.Size([3, 448, 800])
pil2tensor = torchvision.transforms.ToTensor()(img_pil)
#pil2tensor.shape : torch.Size([3, 448, 800])
搭建模型包括:
__init__
函数;forward
函数。PyTorch 把与深度学习模型搭建相关的全部类全部在 torch.nn 这个子模块中。
根据类的功能分类,常用的有如下几个部分:
torch.nn.Conv2d
torch.nn.MaxPool2d
torch.nn.Linear
torch.nn.Module
而在 torch.nn
下面还有一个子模块 torch.nn.functional
,基本上是 torch.nn
里对应类的函数,比如torch.nn.ReLU
的对应函数是 torch.nn.functional.relu
,这两者的功能一样,运行效率也不同,但是也有很大的区别:
inputs = torch.rand(64, 3, 244, 244)
conv = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, padding=1)
out = conv(inputs)
weight = torch.rand(64,3,3,3)
bias = torch.rand(64)
out = nn.functional.conv2d(inputs, weight, bias, padding=1)
所有自定义的神经网络都要继承 torch.nn.Module。定义单独的网络层在 init 函数中实现,把定义好的网络层拼接在一起在 forward 函数中实现。继承了nn.Moudle的模型类有两个重要的函数:parameters 存储了模型的权重;modules 存储了模型的结构。
这是一个序列容器,既可以放在模型外面单独构建一个模型,也可以放在模型里面成为模型的一个层。
# 单独成为一个模型
model1 = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
# 成为模型的一部分
class LeNetSequential(nn.Module):
def __init__(self, classes):
super(LeNetSequential, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 6, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, 5),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),)
self.classifier = nn.Sequential(
nn.Linear(16*5*5, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, classes),)
def forward(self, x):
x = self.features(x)
x = x.view(x.size()[0], -1)
x = self.classifier(x)
return x
放在模型里面的话,模型还是需要 init 和 forward 函数。
这样构建出来的模型的层没有名字:
>>> model2 = nn.Sequential(
... nn.Conv2d(1,20,5),
... nn.ReLU(),
... nn.Conv2d(20,64,5),
... nn.ReLU()
... )
>>> model2
Sequential(
(0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU()
(2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(3): ReLU()
)
为了方便区分不同的层,我们可以使用 collections 里的 OrderedDict 函数:
>>> from collections import OrderedDict
>>> model3 = nn.Sequential(OrderedDict([
... ('conv1', nn.Conv2d(1,20,5)),
... ('relu1', nn.ReLU()),
... ('conv2', nn.Conv2d(20,64,5)),
... ('relu2', nn.ReLU())
... ]))
>>> model3
Sequential(
(conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(relu1): ReLU()
(conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(relu2): ReLU()
)
将网络层存储进一个列表,可以使用列表生成式快速生成网络,生成的网络层可以被索引,也拥有列表的方法 append,extend 或 insert。提高搭建模型效率。
>>> class MyModule(nn.Module):
... def __init__(self):
... super(MyModule, self).__init__()
... self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)])
... self.linears.append(nn.Linear(10, 1)) # append
... def forward(self, x):
... for i, l in enumerate(self.linears):
... x = self.linears[i // 2](x) + l(x)
... return x
>>> myModeul = MyModule()
>>> myModeul
MyModule(
(linears): ModuleList(
(0): Linear(in_features=10, out_features=10, bias=True)
(1): Linear(in_features=10, out_features=10, bias=True)
(2): Linear(in_features=10, out_features=10, bias=True)
(3): Linear(in_features=10, out_features=10, bias=True)
(4): Linear(in_features=10, out_features=10, bias=True)
(5): Linear(in_features=10, out_features=10, bias=True)
(6): Linear(in_features=10, out_features=10, bias=True)
(7): Linear(in_features=10, out_features=10, bias=True)
(8): Linear(in_features=10, out_features=10, bias=True)
(9): Linear(in_features=10, out_features=10, bias=True)
(10): Linear(in_features=10, out_features=1, bias=True) # append 进的层
)
)
这个函数与上面的 torch.nn.Sequential(OrderedDict(…)) 的行为非常类似,并且拥有 keys,values,items,pop,update 等词典的方法:
>>> class MyDictDense(nn.Module):
... def __init__(self):
... super(MyDictDense, self).__init__()
... self.params = nn.ModuleDict({
... 'linear1': nn.Linear(512, 128),
... 'linear2': nn.Linear(128, 32)
... })
... self.params.update({'linear3': nn.Linear(32, 10)}) # 添加层
... def forward(self, x, choice='linear1'):
... return torch.mm(x, self.params[choice])
>>> net = MyDictDense()
>>> print(net)
MyDictDense(
(params): ModuleDict(
(linear1): Linear(in_features=512, out_features=128, bias=True)
(linear2): Linear(in_features=128, out_features=32, bias=True)
(linear3): Linear(in_features=32, out_features=10, bias=True)
)
)
>>> print(net.params.keys())
odict_keys(['linear1', 'linear2', 'linear3'])
>>> print(net.params.items())
odict_items([('linear1', Linear(in_features=512, out_features=128, bias=True)), ('linear2', Linear(in_features=128, out_features=32, bias=True)), ('linear3', Linear(in_features=32, out_features=10, bias=True))])
PyTorch 的 Autograd
目录:
浅谈 PyTorch 中的 tensor 及使用
目录:
这两天篇文章关于自动求导机制讲的太深入浅出了,完全不需要自己总结,直接照搬即可……
从上一章知道自动求导机制之后,其实模型训练基本就呼之欲出了。
所以常规的网络模型训练的模版如下:
model = Your_model().to(args.device)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(Your_model.parameters(),lr=args.learning_rate)
train_loss = []
valid_loss = []
train_epochs_loss = []
valid_epochs_loss = []
def train_loop(dataloader, model, loss_fn, optimizer):
size = len(dataloader.dataset)
for batch, (X, y) in enumerate(dataloader):
# Compute prediction and loss
pred = model(X)
loss = loss_fn(pred, y)
# Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step()
if batch % 100 == 0:
loss, current = loss.item(), batch * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
def test_loop(dataloader, model, loss_fn):
size = len(dataloader.dataset)
num_batches = len(dataloader)
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
pred = model(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= num_batches
correct /= size
print(f"Test Error: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
epochs = 10
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train_loop(train_dataloader, model, loss_fn, optimizer)
test_loop(test_dataloader, model, loss_fn)
print("Done!")
一般的训练只需要保存参数,这样更方便
model = models.vgg16(pretrained=True)
torch.save(model.state_dict(), 'model_weights.pth')
在读取这个参数时,必须先对模型进行实例化,才能load参数。
model = models.vgg16() # we do not specify pretrained=True, i.e. do not load default weights
model.load_state_dict(torch.load('model_weights.pth'))
model.eval()
#保存参数+网络结构
torch.save(model, 'model.pth')
#加载
model = torch.load('model.pth')
输入参数:
dataiter = iter(trainloader)
images, labels = dataiter.next()
img_grid = torchvision.utils.make_grid(images,nrow=2)
主要分为:实例化writer --> 写入数据 --> 浏览器查看
实例化writer
tensorboard在一个文件夹下,只能读取一个event文件,所以实例化的时候需要利用时间戳创建不同的文件夹,避免一个文件夹出现多个文件。
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter('./runs/' + time.strftime("%Y-%m-%d-%H_%M_%S",time.localtime(time.time())))
这样便会在当前目录下创建:
写入数据
最主要是写入loss这样的训练数据,然后也可以查看当前训练结果。
if (i + 1) % args.log_train_loss_interval == 0:
writer.add_scalar('loss', loss, step)
if (i + 1) % args.log_train_images_interval == 0:
writer.add_image('train_pred_pha', make_grid(pred_pha, nrow=5), step)
浏览器查看
进入到runs
目录,运行:
tensorboard --logdir=./
如果是在远程服务器上,本地打开cmd,执行:
ssh -L 16006:127.0.0.1:6006 [email protected]
登录进去之后,在浏览器输入:
http://127.0.0.1:16006/