tuple中的值是不可以改变的。但是如果tuple中的元素是list,那么list中的元素是可以改变的,但是只是list中的元素改变了,list还是没有改变的。在使用的过程中可以将tuple转换为list,后进行后续操作。
tuple_to_list = list(tuple_a)
一般在训练模型的时候,(或者类似的情况:需要重复前面的工作生成新的result表格或者权重),为了不将前面训练好的东西覆盖,一般在文件名后面增加时间后缀,这样每一次运行的时间戳都是不一样的,所以不会存在运行后前面的结果被覆盖的情况,只需要在前面加上下面,然后在后文中用就OK
import time
# 打印时间戳
start_time = time.time() # 计算1970到现在经过了多长时间,以秒为单位
start_time = time.strftime('%Y-%m-%d-%H-%M',time.localtime(start_time)) # 将以秒为单位的时间转换成自己定义的格式
save_weight_path = f'D:/Python/ConvNext/weights/con_model/con_model_{start_time}.pth'
csv_path = f'D:/Python/ConvNext/result/result_{start_time}.csv'
大家一定要注意自己逻辑运算符的使用,稍不留神就掉进去了:
for name, para in model.named_parameters():
print(name,('attention' not in name) or ('haed' not in name))
这句的意思是name中只要没有attention或者没有head都会输出TRUE,attention和head也会输出TRUE
是numpy中对数组排序的函数,返回数组中数值由小到大的index值,当然去负就是由大到小了。
image_pred[(-score).argsort()]
对张量降维
torch.squeeze(input, dim=None, out=None)
当dim没有设置时,默认为None,将输入张量形状中的1去除并返回,如果原始形状是(AB1C1D),输出的形状是(ABCD)。当给定dim时,那么squeeze只发生在给定的维度上 (并且该维度必须为1),例如:输入形状是(A1B),如果squeeze(input,0),则张量不会变,只有用squeeze(input,1)形状才会变为(A*B)
用于扩展张量维度 (1维变2维,2维变3维…),返回一个新的张量,对输入的既定位置插入维度1
torch.unsqueeze(input, dim, out=None)
# input(tensor): 输入张量
# dim(int):插入维度的索引
# out(tensor/optional):结果为张量
实现插值和上采样
可以简单的理解为任何可以让你的图像变成更高分辨率的技术,最简单的方法是重采样和插值。
torch.nn.functional.interpolate(input, size=None, scale_factor=None, mode='nearest', align_corners=None)
# input(tensor):输入张量
# size(int or Tuple[int] or Tuple[int, int] or Tuple[int, int, int]): 输出大小.
# scale_factor (float or Tuple[float]):指定输出为输入的多少倍数。如果输入为tuple,也要制定为tuple类型
# mode (str):可使用的上采样算法,有’nearest’, ‘linear’, ‘bilinear’, ‘bicubic’ , ‘trilinear’和’area’. 默认使用’nearest’
# align_corners (bool, optional) :如果align_corners=True,则对齐 input 和 output 的角点像素(corner pixels),保持在角点像素的值. 只会对 mode=linear, bilinear 和 trilinear 有作用. 默认是 False.
可用于重置大小的mode有:最近邻、线性(3D-only),、双线性, 双三次(bicubic,4D-only)和三线性(trilinear,5D-only)插值算法和area算法
view是改变tensor的形状,返回具有相同数据但大小不同的新张量。
计算两个矩阵之间的kl散度
loss = F.kl_div(input,label,reduction,name)
# input:输入张量
# label:输入张量
# reduction:(可选)可用类型为‘none’ | ‘batchmean’ | ‘mean’ | ‘sum’,‘none’表示无reduction,‘batchmean’ 表示输出的总和除以批大小,‘mean’ 表示所有输出的平均值,‘sum’表示输出的总和。
# name(可选)
注:第一个参数传入的是对数概率矩阵,第二个参数是概率矩阵。
KL散度具有不对称性,存在一个指导和被指导的关系,因此input和label之间的顺序需要确定。如果想要label知道input,第一个参数要传input,即被指导的放在前面,之后求相应的概率和对数概率就行
又是被自己笨到的一个晚上!大家在transformer的时候千万别忘了在transformer后面加上需要增强的图像变量,否则你的img就会变成这样,ndarray显示是空的,因为这或许会出现一些奇奇怪怪的error,比如tuple out of index
,我一晚上大部分时间都在这个问题上,虽然我调试了,但是忽视了前面的(),只看到了后面resize的大小是自己设定的大小,谁知,不尽人意,真就逻辑很重要,还有清晰的思维!!
# 错误写法
grade4_cam = transforms.Resize([int(r0*2) + 2, int(r0*2) + 2])
# 正确写法
grade4_cam = transforms.Resize([int(r0*2) + 2, int(r0*2) + 2])(grade4_cam)
劝大家细心!!
其中的scale参数可以设置为单个float数,表示缩放的倍数,也可以是float的tuple,表示将行列分开缩放
rescale=1./255
transform.rescale(img, 0.1) # 缩小为原来图片的0.1
transform.rescale(img, [0.5,0.25]) #缩小为原来图片行数一半,列数四分之一
transform.rescale(img, 2) #放大为原来图片大小的2倍
x = x.cuda(non_blocking=True)
一般见到的是x=x.cuda()
将Tensor加载到GPU上,设置non_blocking参数的原因:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
在训练模型的时候有的时候会遇到这种错误,其主要原因是代码中如果使用了多线程,必须要写在main()函数中!将所有代码写在main()中
if __name__=="__main__":
...
nw = min([os.cpu_count(), batchsize if batchsize > 1 else 0, 4]) # number of workers
tb_writer = SummaryWriter()
tags = ["train_loss", "train_acc", "val_loss", "val_acc", "learning_rate"]
#%% 准备train_dataloader和val_dataloader
#利用自定义类Mydataset创建对象train_dataset和train_dataloader
train_dataset = Mydataset(full = full_imgs_path,part = part_imgs_path,labels=labels,transform=data_transform['train'])
train_dataloader = torch.utils.data.DataLoader(train_dataset,
batch_size=batchsize,
shuffle=True,
num_workers=nw) #每次迭代时返回五个数据
原始代码
class Conv_con(nn.Module):
def __init__(self, num_classes):
super(Conv_con, self).__init__()
self.fctl = _FCtL(768,768)
self.classifier = nn.Sequential(
nn.Linear(768*7*7, 4096),
nn.ReLU(True),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Linear(4096,num_classes))
def forward(self, feature_x,feature_y):
features = self.fctl(feature_x,feature_y)
out = self.classifier(features)
return out
错误原因:卷积层的输入为四维[batch_size,channels,H,W] ,而全连接接受维度为2的输入,通常为[batch_size, size]。
改进代码
class Conv_con(nn.Module):
def __init__(self, num_classes):
super(Conv_con, self).__init__()
self.fctl = _FCtL(768,768)
self.classifier = nn.Sequential(
nn.Linear(768*7*7, 4096),
nn.ReLU(True),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Linear(4096,num_classes))
def forward(self, feature_x,feature_y):
features = self.fctl(feature_x,feature_y)
features = torch.flatten(features, 1)
out = self.classifier(features)
return out
解决方案
# 方法1
x.view(-1,7* 7* 1024)
# 方法2
x = torch.flatten(x,1) //拉成二维向量[batch_size, size]
最近想要结合两张图片的信息,进行训练,但是在dataset的过程中需要加载两张图片并数据增强。当时做的时候没有考虑到这一点——transform调用一次就会随机化参数,所以两张图片是两种不同的增强,所以已经丧失了两者之间的关键联系(空间信息)
温馨提醒:大家在写完dataset后,使用dataloader加载完成一定要检查数据的一致性!
lr_schedule有很多种“模式”,其中常用的三种:
torch.optim.lr_scheduler.StepLR(optimizer,step_size,gamma=0.1,last_epoch=-1,verbose=False)
torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1, last_epoch=-1, verbose=False)
torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1, verbose=False)
一般在每个epoch进行一次schedule的step!所以一般的格式是:
for epoch in range(epochs):
train_one_epoch()
evaluate()
lr_schedule.step()
有时在本地代码运行没用问题,但是在服务器上会出现差错,很大程度是代码中网络to(device)转换成cuda版本,但是输入图像没有转换,会出现这样的errorInput type (torch.FloatTensor) and weight type (torch.cuda.FloatTensor) should be the same
解决方法
将原本的输入.cuda()
,如:model(img.unsqueeze(0))
改为model(img.unsqueeze(0).cuda())
注:一般使用.cuda()默认将模型和变量加载到第一块device上,即如果有两块GPU(0,1),则直接加载到cuda0上面,如果前面的device设置为cuda1device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
,在代码运行期间就会出现两个数据或者变量不在同一个设备上(一个在cuda0一个在cuda1),因此为了避免这一类的问题,建议搭建不要使用.cuda()
,而是将代码写活,将device传过去使用.to(device)
这个问题我在之前的一篇中有讲过,但是最近训练模型发现又出现了新的问题。是什么呢?
原本的代码:
if freeze_layers == True:
for name, para in model.named_parameters():
if ("head" not in name) or ("attention" not in name):
para.requires_grad_(False)
else:
continue
原意是将除了head和attention相关的层解冻,其他层冻结,但是最后训练的结果acc只有50%,这一看就是有问题的。打印了各层的梯度情况:
所有层都是冻结的,所以前面的学个啥呢?啥都没学!所以大家擦亮双眼检查好自己的代码是不是真的达到了自己的预期。我改了逻辑以后打印出来的结果是:
if freeze_layers == True:
for name, para in model.named_parameters():
if ("head" in name) or ("attention" in name):
para.requires_grad_(True)
else:
para.requires_grad_(False)
刚刚又看了一下自己的逻辑部分错了,与或非弄错了,所以结果不对,在part1中第三个写了,大家注意逻辑运算!
element 0 of tensors does not require grad and does not have a grad_fn
解决方案:
这个错误主要出现在我调试过程中的loss计算后,training过程中,可以看出只有可导的参数在可以backward,但是我的loss之前是没有设置require——grad的,所以在训练过程中error了,只需要将代码改一下:
previous
loss = 0.9 * loss_each_sample + 0.1 * loss_time_series
loss.backward()
now
loss = 0.9 * loss_each_sample + 0.1 * loss_time_series
loss.requires_grad_(True)
loss.backward()
将torch中的数值控制在[min,max]之间,小于min的值赋值为min,大于max的值赋予max值,可以定义其中的任意一个,也可以两个都定义
torch.clamp(inter_rect_x2 - inter_rect_x1 + 1, min=0)
cv2.error: OpenCV(4.6.0) /io/opencv/modules/imgproc/src/resize.cpp:4052: error: (-215:Assertion failed) !ssize.empty() in function 'resize'
在模型的训练过程中出现这个问题,确定代码没有问题,因为在其他的数据上运行都没有问题,就是dataset中使用的数据在cv2.resize的时候出现了问题,但是检查数据没有问题,这个时候就寻求了帮助,在各种博客上看到各种各样的问题,自己都一一排查,但是没有解决,先说一下网上说的情况:
但是上面的解决方案,都没有解决的我的问题,还有一种说法是说图片质量问题使得图像变成了全零。于是我在dataset的forward函数中增加了打印当前加载图像的全路径,重新运行后,两次都在同一张图片上卡住了,所以最后的解决方案是:删了这张图片,现在一切正常
所以到底是什么问题呢?我查看了原来的图像,不存在质量问题,就很困惑,我猜:可能是中间裁剪之前定位的时候错了