COCO数据集方面:
COCO数据集现在有3种标注类型:
1, object instances(目标实例),
2, object keypoints(目标上的关键点),
3, image captions(看图说话)
使用JSON文件存储。每种类型又包含了训练和验证,所以共6个JSON文件。
1,整体JSON文件格式
数据集中的instances_train2017.json、instances_val2017.json这两个文件就是这种格式
从头至尾按照顺序分为以下段落:
"info": info,
"licenses": [license],
"images": [image],
"annotations": [annotation],
"categories": [category]
其中,info、licenses、images这三个结构体/类型,在不同的JSON文件中这三个类型是一样的,定义是共享的。
不共享的是annotation和category这两种结构体,他们在不同类型的JSON文件中是不一样的。
images数组、annotations数组、categories数组的元素数量是相等的,等于图片的数量。
annotations字段是包含多个annotation实例的一个数组,annotation类型本身又包含了一系列的字段。
segmentation格式取决于这个实例是一个单个的对象(即iscrowd=0,将使用polygons格式)还是一组对象(即iscrowd=1,将使用RLE格式)。如下所示:
annotation{
"id": int,
"image_id": int,
"category_id": int,
"segmentation": RLE or [polygon],
"area": float,
"bbox": [x,y,width,height],
"iscrowd": 0 or 1,
}
categories是一个包含多个category实例的数组,而category结构体描述如下:
{
"id": int,
"name": str,
"supercategory": str,
}
1,整体JSON文件格式
数据集中的person_keypoints_train2017.json、person_keypoints_val2017.json这两个文件就是这种格式。
Object Keypoint这种格式的文件从头至尾按照顺序分为以下段落,看起来和Object Instance一样啊:
{
"info": info,
"licenses": [license],
"images": [image],
"annotations": [annotation],
"categories": [category]
}
其中,info、licenses、images这三个结构体/类型 在第一节中已经说了,在不同的JSON文件中这三个类型是一样的,定义是共享的。
不共享的是annotation和category这两种结构体,他们在不同类型的JSON文件中是不一样的。
images数组和annotations数组的元素数量是相等的,等于图片的数量。
2,annotations字段
这个类型中的annotation结构体包含了Object Instance中annotation结构体的所有字段,再加上2个额外的字段。
新增的keypoints是一个长度为3*k的数组,其中k是category中keypoints的总数量。每一个keypoint是一个长度为3的数组,第一和第二个元素分别是x和y坐标值,第三个元素是个标志位v,v为0时表示这个关键点没有标注(这种情况下x=y=v=0),v为1时表示这个关键点标注了但是不可见(被遮挡了),v为2时表示这个关键点标注了同时也可见。
num_keypoints表示这个目标上被标注的关键点的数量(v>0),比较小的目标上可能就无法标注关键点。
annotation{
"keypoints": [x1,y1,v1,...],
"num_keypoints": int,
"id": int,
"image_id": int,
"category_id": int,
"segmentation": RLE or [polygon],
"area": float,
"bbox": [x,y,width,height],
"iscrowd": 0 or 1,
}
3,categories字段
最后,对于每一个category结构体,相比Object Instance中的category新增了2个额外的字段,
keypoints是一个长度为k的数组,包含了每个关键点的名字;
skeleton定义了各个关键点之间的连接性(比如人的左手腕和左肘就是连接的,但是左手腕和右手腕就不是)。目前,COCO的keypoints只标注了person category (分类为人)
{
"id": int,
"name": str,
"supercategory": str,
"keypoints": [str],
"skeleton": [edge]
}
pytorch方面:
1:PyTorch是什么?
这是一个基于Python的科学计算包,其旨在服务两类场合:
替代numpy发挥GPU潜能
一个提供了高度灵活性和效率的深度学习实验性平台
2:Numpy桥
将Torch的Tensor和numpy的array相互转换简直就是洒洒水啦。注意Torch的Tensor和numpy的array会共享他们的存储空间,修改一个会导致另外的一个也被修改。
第一题:Tensor的五种基本数据类型:
32位浮点型:torch.FloatTensor。 (默认)
64位浮点型:torch.DoubleTensor。
16位整型:torch.ShortTensor。
32位整型:torch.IntTensor。
64位整型:torch.LongTensor。
除以上数字类型外,还有 byte和chart型
第二题:基本操作
1:生成标量 a = torch.tensor() tensor转化为Python数据 a.item()
2:tensor转化为numpy array a.numpy() 反之 torch.from_numpy(a)
3:torch.view 改变张量的维度和大小 和Numpy的reshape类似
a = torch.rand(2,2)
a.view(1,4)
4:沿着行取最大值
X = torch.randn(2,2)
max_value, max_idx = torch.max(x, dim=1)
Print(max_value, max_idx)
5:每行x求和
Sum_x = torch.sum(x, dim=1)
Print(sum_x)
第三题:设备之间的转换
1: .cuda的方法将tensor移动到gpu
gpu_a = cpu_a.cuda()
2: .cpu的方法将tensor移动到cpu
cpu_b = gpu_a.cpu()
3:多GPU的情况下 .to方法确定使用哪个设备
device = torch.device(“cuda”if torch.cuda.is_available() else “cpu”)
Print(device)
gpu_b = cpu_b.to(device) # 将tensor传送到设备
第四题:autograd 自动求导
1:autograd.Variable 这是这个包中最核心的类。 它包装了一个Tensor,并且几乎支持所有的定义在其上的操作。一旦完成了你的运算,你可以调用 .backward()来自动计算出所有的梯度。
通过属性 .data 来访问原始的tensor,而关于这一Variable的梯度则集中于 .grad 属性中。
2:Variable 和 Function 二者相互联系并且构建了一个描述整个运算过程的无环图。每个Variable拥有一个 .creator 属性,其引用了一个创建Variable的 Function。(除了用户创建的Variable其 creator 部分是 None)。
如果你想要进行求导计算,你可以在Variable上调用.backward()。 如果Variable是一个标量(例如它包含一个单元素数据),你无需对backward()指定任何参数,然而如果它有更多的元素,你需要指定一个和tensor的形状想匹配的grad_output参数。
使用 torch.nn 包可以进行神经网络的构建。
现在你对autograd有了初步的了解,而nn建立在autograd的基础上来进行模型的定义和微分。
nn.Module中包含着神经网络的层,同时forward(input)方法能够将output进行返回。
神经网络训练过程:
注意: torch.nn 只接受小批量的数据
整个torch.nn包只接受那种小批量样本的数据,而非单个样本。 例如,nn.Conv2d能够结构一个四维的TensornSamples x nChannels x Height x Width。
如果你拿的是单个样本,使用input.unsqueeze(0)来加一个假维度就可以了。
第六题:数据处理
通常来讲,当你处理图像,声音,文本,视频时需要使用python中其他独立的包来将他们转换为numpy中的数组,之后再转换为torch.*Tensor。
Pytorch图像数据的读取
适用于不是分类问题,或者标签不是简单的文件名的映射
首先:定义一个Dataset的派生类,并且对数据的处理、数据增强之类的都需要自己定义,这些定义的时候利用__call_()就可以了
第一步:定义一个Dataset的派生类,这个派生类目标是重载两个魔法方法 __ len __ (),__ getitem__()
加载包
import os
import glob
import cv2
import numpy as np
from torchvision import transforms as T
from torch.utils.data import Dataset, DataLoader
所以无论是自定义的dataset,或是 ImageFolder得到的dataset,因其都继承自utils.data.Dataset, 故以上方法两种方法都有。
#数据读取方式
transform = T.Compose([
T.Resize(224),
T.CenterCrop(224),
T.RandomHorizontalFlip(),
T.RandomSizedCrop(224),
T.ToTensor(),#将图片从0-255变为0-1
T.Normalize(mean=[0.5,0.5,0.5],std=[0.5,0.5,0.5])#标准化到[-1,1]
])
# 相当于一个派生类
class Test_Data(Dataset):
def __init__(self,data_root,mask_root,transforms=None):
data_image = glob.glob(data_root+'/*.jpg')
self.data_image = data_image
mask_image = glob.glob(mask_root+'/*.jpg')
self.mask_image = mask_image
self.transforms = transforms
# getitem则是定义了迭代返回一个数据集样本,返回值可以是包含训练样本和标签的list,也可以是字典,输入索引,返回对应的图片与标签,根据这个不同后面的用法也回不太一样(无非就是索引是数字还是key的区别)
def __getitem__(self, index):
data_image_path = self.data_image[index]
mask_image_path = self.mask_image[index]
image_data = cv2.imread(data_image_path,-1)
mask_data = cv2.imread(mask_image_path,-1)
if self.transforms:
image_data = self.transforms(image_data)
mask_data = self.transforms(mask_data)
return image_data,mask_data
# 读取出保存整个数据集的表格,然后len就是返回了数据集的数目
def __len__(self):
return len(self.data_image)
Dataset一般还会要求输入对数据集的操作,
Dataset=Test_Data(data_root='../../test_image/37_simple/0001/data',mask_root='../../test_image/37_simple/0001/mask')
#第一种调用,不常用
for data,mask in dataset:
print(data.shape,mask.shape)
下面两种应该在训练过程中更加好:
test_data_loader = DataLoader(dataset=dataset, num_workers=1, batch_size=1, pin_memory=True, shuffle=True,drop_last=True)
#第1种
# for i,data in enumerate(test_data_loader,0):
# print(data[0].shape,'..')
# print(data[1].shape,'...')
#第2种
for data_batch,mask_batch in test_data_loader:
print(data_batch.size(),mask_batch.size)
注意事项
nn.CrossEntropyLoss()这个损失函数是个大坑,它是softmax + 归一化,所以使用这个损失函数的时候模型最后就不要再加softmax了,不然会发现自己的损失就那几个值,也降不下去
输入模型的 input图像,格式为(batch_size,Nc,H,W)的四维矩阵