week5 8月4日

微调

     (1)在源数据集(例如 ImageNet 数据集)上预训练一个神经网络模型,即源模型

     (2)创建一个新的神经网络模型,即目标模型。它复制源模型上除了输出层外的所有模型设计及其参数。我们假设这些模型参数包含了源数据集上学习到的知识,且这些知识同样适用于目标数据集。我们还假设源模型的输出层跟源数据集的标签紧密相关,因此在目标模型中不予采用。

     (3)为目标模型添加一个输出大小为目标数据集类别个数的输出层,并随机初始化该层的模型参数。

     (4)在目标数据集(例如椅子数据集)上训练目标模型。我们将从头训练输出层,而其余层的参数都是基于源模型的参数微调得到的

为什么要微调

卷积神经网络的核心是:

(1)浅层卷积层提取基础特征,比如边缘,轮廓等基础特征。

(2)深层卷积层提取抽象特征,比如整个脸型。

(3)全连接层根据特征组合进行评分分类

普通预训练模型的特点是:用了大型数据集做训练,已经具备了提取浅层基础特征和深层抽象特征的能力。

不做微调:

(1)从头开始训练,需要大量的数据,计算时间和计算资源。

(2)存在模型不收敛,参数不够优化,准确率低,模型泛化能力低,容易过拟合等风险。

使用微调:有效避免了上述可能存在的问题。

 什么情况下使用微调
(1) 要使用的数据集和预训练模型的数据集相似。如果不太相似,比如你用的预训练的参数是自然景物的图片,你却要做人脸的识别,效果可能就没有那么好了,因为人脸的特征和自然景物的特征提取是不同的,所以相应的参数训练后也是不同的。

(2) 自己搭建或者使用的CNN模型正确率太低

(3)数据集相似,但数据集数量太少。 (4)计算资源太少。

只有源数据集几十倍或者几百倍于目标数据集,才有效果,不然重新训练一个神经网络即可 

正样本:属于目标的样本

负样本:不属于目标的样本

代码:

%matplotlib inline
import os 
import torch
import torchvision
from torch import nn
from d2l import torch as d2l

#加载数据集
#@save
d2l.DATA_HUB['hotdog'] = (d2l.DATA_URL+'hotdog.zip','fba480ffa8aa7e0febbb511d181409f899b9baa5')
data_dir=d2l.download_extract('hotdog')

train_imgs=torchvision.datasets.ImageFolder(os.path.join(data_dir,'train'))
test_imgs=torchvision.datasets.ImageFolder(os.path.join(data_dir,'test'))

#展示样本,前8个是正类样本 后8个是负类样本
hotdogs=[train_imgs[i][0] for i in range(8)]
not_hotdogs=[train_imgs[-i-1][0] for i in range(8)]
d2l.show_images(hotdogs+not_hotdogs,2,8,scale=1.4);

#进行数据增广
normalize=torchvision.transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])
#使用rgb通道的均值和标准差,以标准化每个通道
train_augs=torchvision.transforms.Compose([
torchvision.transforms.RandomResizedCrop(224),torchvision.transforms.RandomHorizontalFlip(),torchvision.transforms.ToTensor(),normalize])
#随机裁剪大小成224*224的图片 水平翻转 
test_augs=torchvision.transforms.Compose([
torchvision.transforms.Resize([256,256]),torchvision.transforms.CenterCrop(224),torchvision.transforms.ToTensor(),normalize])
#缩放为256*256,裁剪中央224*224大小
#定义预训练模型
pretrained_net=torchvision.models.resnet18(pretrained=True)

#展示模型顶层(输出层)
pretrained_net.fc

#定义目标模型
finetune_net=torchvision.models.resnet18(pretrained=True)
finetune_net.fc=nn.Linear(finetune_net.fc.in_features,2)
nn.init.xavier_uniform_(finetune_net.fc.weight)

#定义训练函数
def train_fine_tuning(net,learning_rate,batch_size=128,epochs=5,param_group=True):
    train_iter = torch.utils.data.DataLoader(dataset=torchvision.datasets.ImageFolder(os.path.join(data_dir,'train'),transform=train_augs),
                                             batch_size=batch_size,shuffle=True)
    test_iter = torch.utils.data.DataLoader(dataset=torchvision.datasets.ImageFolder(os.path.join(data_dir,'test'),transform=test_augs),
                                            batch_size=batch_size,shuffle=False)
    #reduction='none'表示直接求出loss,然后再求和,得出样本总loss大小,不是求样本平均loss,因此loss值会与样本数有直接线性关系,从而当batch_size批量样本数目变多时,一般都会增加学习率。样本平均loss一般与样本数目关系不大,当batch_size批量样本数目变多时,一般学习率不会增加
    devices = d2l.try_all_gpus()
    loss = nn.CrossEntropyLoss(reduction='none')
    
    if param_group:
        #param_group=True表示使用微调
        #表示除最后一层外前面所有层的参数使用微调,最后一层参数学习率是前面所有层参数学习率的10倍
        param_conv2d = [param for name,param in net.named_parameters() if name not in ['fc.weight','fc.bias']]
        #如果不是最后一层fc的话,直接用预训练模型
        optim = torch.optim.SGD([{'params':param_conv2d},
                                 {'params':net.fc.parameters(),
                                 'lr':learning_rate*10}],
                                lr=learning_rate,weight_decay=0.001)
    else:
        # param_group=False表示不使用微调,直接使用当前数据集对目标模型从零开始进行训练
        optim = torch.optim.SGD(net.parameters(),lr=learning_rate,weight_decay=0.001)

    d2l.train_ch13(net,train_iter,test_iter,loss,optim,epochs,devices)



#开始训练
#使用预训练的模型对目标模型进行训练,使用较小的学习率5e-5
train_fine_tuning(finetune_net,learning_rate=5e-5)

训练结果


物体检测的边缘框

x[m,n]是通过numpy库引用数组或矩阵中的某一段数据集的一种写法,

m代表第m维,n代表m维中取第几段特征数据。

通常用法:

x[:,n]或者x[n,:]

x[:,n]表示在全部数组(维)中取第n个数据,直观来说,x[:,n]就是取所有集合的第n个数据, 

%matplotlib inline
import torch
from d2l import torch as d2l

#获取图片
d2l.set_figsize()
img=d2l.plt.imread("C:\\Users\\13930\\Pictures\\Saved Pictures\\猫狗.jpg")
d2l.plt.imshow(img)

#@save
def box_corner_to_center(boxes):
    #从(左下,右下)转换到(中间,宽度,高度)
    x1,y1,x2,y2=boxes[:,0],boxes[:,1],boxes[:,2],boxes[:,3]
    cx=(x1+x2)/2
    cy=(y1+y2)/2
    w=x2-x1#宽
    h=y2-y1#高
    boxes=torch.stack((cx,cy,w,h),axis=-1)
    #(cx,cy)是中心点的坐标
    return boxes

#@save
def box_center_to_corner(boxes):
    #从(中间,宽度,高度)转换到(左上,右下)
    cx,cy,w,h=boxes[:,0],boxes[:,1],boxes[:,2],boxes[:,3]
    x1=cx-0.5*w
    y1=cy-0.5*h
    x2=cx+0.5*w
    y2=cy+0.5*h
    boxes=torch.stack((x1,y1,x2,y2),axis=-1)
    #(x1,y1)是左上左边,(x2,y2)是右下坐标
    return boxes

#边缘框位置(左上和右下)
dog_bbox,cat_bbox=[50.0,45.0,150.0,300.0],[130.0,100.0,290.0,300.0]

#根据给定的坐标点,生成边框的样子
#@save
def bbox_to_rect(bbox,color):
    return d2l.plt.Rectangle(
    xy=(bbox[0],bbox[1]),width=bbox[2]-bbox[0],height=bbox[3]-bbox[1],fill=False,edgecolor=color,linewidth=2)

#描绘边框
fig=d2l.plt.imshow(img)
fig.axes.add_patch(bbox_to_rect(dog_bbox,'blue'))
fig.axes.add_patch(bbox_to_rect(cat_bbox,'red'))

描绘结果 

 加载一个小的数据集,里面有香蕉的边框位置

import torch
import os
import pandas as pd
import torchvision
import d2l.torch

#@save
d2l.torch.DATA_HUB['banana-detection'] = (
    d2l.torch.DATA_URL + 'banana-detection.zip',
    '5de26c8fce5ccdea9f91267273464dc968d20d72')


def read_data_bananas(is_train=True):
    data_dir = d2l.torch.download_extract('banana-detection')
    print(data_dir)
    csv_fpath = os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'label.csv')
    csv_file = pd.read_csv(csv_fpath)
    csv_file = csv_file.set_index('img_name')
    images, targets = [], []
    for image_name, target in csv_file.iterrows():
        images.append(torchvision.io.read_image(
            path=os.path.join(data_dir, 'bananas_train' if is_train else 'bananas_val', 'images', f'{image_name}')))
        targets.append(list(target))
    return images, torch.tensor(targets).unsqueeze(1) / 256


images, targets = read_data_bananas()
targets[0]
targets.shape
"""一个用于加载香蕉检测数据集的自定义数据集"""


class BananasDataset(torch.utils.data.Dataset):
    def __init__(self, is_train):
        self.features, self.labels = read_data_bananas(is_train)

    def __getitem__(self, item):
        return (self.features[item].float(), self.labels[item])

    def __len__(self):
        return len(self.features)


"""加载香蕉检测数据集"""


def load_data_bananas(batch_size):
    train_iter = torch.utils.data.DataLoader(BananasDataset(is_train=True), batch_size, shuffle=True)
    val_iter = torch.utils.data.DataLoader(BananasDataset(is_train=False), batch_size, shuffle=False)
    return train_iter, val_iter


batch_size, edge_size = 32, 256
train_iter, valid_iter = load_data_bananas(batch_size)
batch = next(iter(train_iter))
batch[0].shape, batch[1].shape

#permute()函数可以同时多次交换tensor的维度,如:b = a.permute(0,2 ,1) 将a的维度索引1和维度索引2交换位置
imgs = (batch[0][0:10].permute(0,2,3,1))/255 #除以255是为了对图片中每一个像素进行标准化
axes = d2l.torch.show_images(imgs,2,5,scale=2)
for axe,label in zip(axes,batch[1][0:10]):
    d2l.torch.show_bboxes(axe,bboxes=[label[0][1:5]*256],colors=['w']) #label[0][1:5]*256乘256是因为加载数据集时bounding box边缘框除以256,256是图片的高和宽
x_range = torch.arange(0, 2)
y_range = torch.arange(0, 4)
x, y = torch.meshgrid(y_range, x_range)
print(x)
print(y)
x, y = torch.meshgrid(x_range, y_range)
print(x)
print(y)


锚框

生成大量锚框,找到还有关注物体的锚框,调整其边框位置,至真实的边框

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以帮你编写一个Python函数来实现这个功能。代码如下: ```python def week(m, d): # 判断输入期是否合法 if m < 1 or m > 12 or d < 1 or d > 31: return -1 if (m == 4 or m == 6 or m == 9 or m == 11) and d > 30: return -1 if m == 2: if (d > 29) or (d == 29 and (year % 4 != 0 or (year % 100 == 0 and year % 400 != 0))): return -1 # 计算该期是该年的第几天 days_in_month = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] day_of_year = days_in_month[m-1] + d if m > 2 and ((year % 4 == 0 and year % 100 != 0) or year % 400 == 0): day_of_year += 1 # 计算该期是该年的第几周 first_day = (2 - (year % 4)) % 7 + 1 # 确定该年11是星期几 if first_day == 1: first_week_days = 1 else: first_week_days = 8 - first_day week_of_year = (day_of_year - first_week_days)//7 + 1 return week_of_year # 输入一组期进行测试 dates = [(1, 1), (1, 5), (13, 1), (2, 29), (4, 31)] year = 2022 # 假设计算的年份是2022年 for m, d in dates: print(f"The week of {m}/{d} in {year} is {week(m, d)}") ``` 运行结果如下: ``` The week of 1/1 in 2022 is 1 The week of 1/5 in 2022 is 2 The week of 13/1 in 2022 is -1 The week of 2/29 in 2022 is -1 The week of 4/31 in 2022 is -1 ``` 其中,函数week(m, d)接受两个参数m和d,分别表示份和期。在函数中,首先判断输入期是否合法,如果不合法则返回-1。然后,计算该期是该年的第几天,进而计算该期是该年的第几周。最后,返回该期属于的周数。最后,我们输入一组期进行测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值