论文阅读与视频学习

ResNet(Deep Residual Learning for Image Recognition):

提出了Residual Learning的概念,通过添加残差连接(shortcut connection)来解决深层网络中梯度消失和模型退化问题。

resnet50与vit结合的代码_人工智能

 

Residual Learning的核心思想是学习残差函数,即将网络的输出与输入之间的差值学习为模型的优化目标,从而使模型可以更容易地学习恒等映射。

在ResNet中,使用了“残差块”(Residual Block),每个块由两个或三个卷积层组成,其中包含了跳跃连接(shortcut connection)。

通过堆叠多个残差块,可以构建非常深的神经网络,例如ResNet-50、ResNet-101等。

ResNet的结构简洁高效,有效解决了深层网络训练中的梯度消失和模型退化问题,成为了后续深度学习模型的基础。

ResNeXt(Aggregated Residual Transformations for Deep Neural Networks):

论文地址:https://arxiv.org/abs/1611.05431

 ResNeXt相对于ResNet主要的改变就是block块的修改。

resnet50与vit结合的代码_人工智能_02

ResNeXt是在ResNet的基础上进一步发展的模型,提出了“分组卷积”(grouped convolution)的概念,以进一步提升模型的性能。

在分组卷积中,将输入通道分成若干个组,每个组进行独立的卷积操作,然后将各个组的输出进行拼接。这样可以增加模型的宽度而不增加模型的深度,从而提高模型的表达能力。

ResNeXt的结构比较灵活,可以通过调整分组数来控制模型的性能和计算量。通常,较大的分组数可以带来更好的性能,但也会增加计算复杂度。

ResNeXt在多个图像分类和目标检测任务上都取得了优秀的表现,证明了分组卷积的有效性和泛化能力。

代码作业

数据预处理

编写dataloader文件对数据集进行处理。猫的标签为0狗的标签为1,因为训练集的标签都保存在图片的名称中需要对文件名进行处理。定义了正则表达式来获取狗还是猫以及序号。

if self.mode == 'train':            # 训练集模式下,需要提取图片的路径和标签
            dir = dir + '/train/'           # 训练集路径在"dir"/train/
            for file in os.listdir(dir):    # 遍历dir文件夹
                self.list_img.append(dir + file)        # 将图片路径和文件名添加至image list
                self.data_size += 1                     # 数据集增1
                
                # 定义正则表达式
                pattern = r'^(.*?)_(\d+)\.jpg$'
                
                # 使用正则表达式匹配字符串
                match = re.match(pattern, file)
                if match.group(1) == 'cat':
                    self.list_label.append(0)         # 图片为猫,label为0
                else:
                    self.list_label.append(1)

网络结构的搭建

类似LeNet的网络结构

class likeLeNet(nn.Module):
    def __init__(self):
        super(likeLeNet, self).__init__()
        net = nn.Sequential(
            nn.Conv2d(3, 6, kernel_size=3, stride=2,padding=1),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(6, 16, kernel_size=3,stride=2,padding=1), 
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Flatten(),
            nn.Linear(16 * 14 * 14, 784), 
            nn.ReLU(),
            nn.Linear(784, 98), 
            nn.ReLU(),
            nn.Linear(98, 2))
        self.net=net
    def forward(self, x):
        out=self.net(x)
        return out

类似ResNet的网络结构

class base_resnet(nn.Module):
    def __init__(self):
        super(base_resnet, self).__init__()
        self.model = models.resnet18(pretrained=False)
        #self.model.load_state_dict(torch.load('./model/resnet50-19c8e357.pth'))
        num_classes = 2  
        in_features = self.model.fc.in_features  # 获取当前全连接层的输入特征数
        self.model.fc = torch.nn.Linear(in_features, num_classes)  # 替换全连接层
 
    def forward(self, x):
        return self.model(x)

网络训练

def train():
    datafile = DVCD('train', dataset_dir)                                                           # 实例化一个数据集
    dataloader = DataLoader(datafile, batch_size=batch_size, shuffle=True, num_workers=workers, drop_last=True)     # 用PyTorch的DataLoader类封装,实现数据集顺序打乱,多线程读取,一次取多个数据等效果

    print('Dataset loaded! length of train set is {0}'.format(len(datafile)))

    # model = likeLeNet()                       # 实例化一个网络
    
    model=base_resnet()
    
    model = model.to(device)               
    # model = model
    # model = nn.DataParallel(model)
    model.train()                       

    optimizer = torch.optim.Adam(model.parameters(), lr=lr)         

    criterion = torch.nn.CrossEntropyLoss()                         

    cnt = 0             # 训练图片数量
    for epoch in range(nepoch):
        for img, label in dataloader:                                         
            img, label = img.to(device), label.to(device)           
            out = model(img)                                                  
            loss = criterion(out, torch.squeeze(label)) #这里的第二个参数应该是一个向量    
            loss.backward()                             
            optimizer.step()                            
            optimizer.zero_grad()                      
            cnt += 1

            print('Epoch:{0},Frame:{1}, train_loss {2}'.format(epoch, cnt*batch_size, loss/batch_size))          # 打印一个batch size的训练结果

    torch.save(model.state_dict(), '{0}/resnetmodel.pth'.format(model_cp))

结果提交

将最后的结果按照格式要求保存成CSV文件

#文件夹路径
folder_path = "data/test/"

# 获取文件夹下所有文件的名称
file_names = os.listdir(folder_path)

# 按照图片名称的数字进行排序
sorted_image_files = sorted(file_names, key=lambda x: int(x.split(".")[0]))

device = torch.device("cuda:0" if (torch.cuda.is_available()) else "cpu")

model_file = './snapshots/resnetmodel.pth'

#加载一下网络
model = base_resnet()                                      
model.to(device)                                       
model.load_state_dict(torch.load(model_file)) #模型加载已完成     
model.eval()                                       

results=[]
for file in sorted_image_files:
    #读取图片转换为tensor格式
    img = Image.open(folder_path + file)
    img_data = torch.unsqueeze(dataTransform(img).to(device),dim=0)
    out=model(img_data)
    # 使用torch.max()获取最大值和索引
    max_value, max_index = torch.max(out, dim=1)
    print(max_index.item())
    results.append([file.split(".")[0], max_index.item()])

with open('output2.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    # # Write the header row
    # writer.writerow(['File ID', 'Prediction'])
    # Write the data rows
    writer.writerows(results)

csv内容展示

resnet50与vit结合的代码_卷积_03

output1是类似于LeNet的测试结果,output2是类似于ResNet的测试结果。

resnet50与vit结合的代码_resnet50与vit结合的代码_04

本周思考内容

Residual learning 的基本原理?

Residual Learning引入了残差块(Residual Block),这是一种特殊的网络结构。在残差块中,输入通过一系列的卷积、激活函数等操作得到输出,然后将输出与输入相加(按元素相加),而不是简单地将输入映射为输出。这样,残差块实际上学习了输入的增量,而不是直接学习输入到输出的映射。
Batch Normailization 的原理,思考 BN、LN、IN 的主要区别。

Batch Normailization的原理:对于输入的数据D[N,C,H,W],BN会对D[N,1,H,W],D[N,2,H,W]......等子数据进行处理,先对每个子数据进行转换为标准正太分布,然后就是将其进行放缩和偏移到新的分布。

BN是在同一特征通道数的情况下所有的样本进行归一化,然后进行重新分布。

LN是在同一样本所有的通道数进行归一化,然后进行重新分布。

IN是每个样本的每一个通道单独进行归一化,然后进行重新分布。
为什么分组卷积可以提升准确率?即然分组卷积可以提升准确率,同时还能降低计算量,分组数量尽量多不行吗?

分组卷积假如分为5组,那就是一次卷积可以得到5个通道。可以理解为本来卷积一次需要很多参数的计算才能得到一个输出结果的一个数值,而分组卷积可以用更少的参数得到输出结果的值。

分组卷积通过将输入通道分成多个组,每个组之间进行独立的卷积操作,相当于在不同的子空间中学习特征,从而增加了网络的表达能力。所以可以提高准确率。

分组卷积中,每个分组内的卷积操作是独立的,因此分组过多可能导致每个分组的参数量较小,难以有效地共享信息,从而影响模型的表达能力。所以分组数量要适中。