第四周作业:卷积神经网络(Part2)

一、现代卷积神经网络

 

1、深度卷积神经网络Alexnet

Alexnet对于lenet的主要改进:

1、卷积网络更深更大

2、用了丢弃法解决过拟合问题

3、激活函数由sigmoid换成了relu函数,减缓梯度消失

4、池化层改用maxpooling,更大的池化窗口

5、新增加了3层卷积层,更多的输出通道,因为分类类别增加了

6、数据增强:卷积对于位置和光亮等比较敏感,所以在输入的时候就进行数据增强,对图片进行变种,例如切割部分,提高光亮等。

2、使用块的网络VGG

卷积神经网络深度更大,卷积核越小时精度更好,由此产生了VGG,是Alexnet的变体,由3X3卷积构成卷积块(n层m通道),2X2最大池化层,使得输出高宽都减半。

VGG架构:

多个VGG块后面接全连接层,不同次数的重复块得到不同架构VGG-16,VGG-19等等,不同的卷积块个数和超参数可以得到不同复杂度的变种。

3、网络中的网络NiN

 NiN 的想法是在每个像素位置(针对每个高度和宽度)应用一个全连接层。 如果我们将权重连接到每个空间位置,我们可以将其视为 1×1卷积层,或作为在每个像素位置上独立作用的全连接层。 从另一个角度看,即将空间维度中的每个像素视为单个样本,将通道维度视为不同特征。

 NiN 和 AlexNet 之间的一个显著区别是 NiN 完全取消了全连接层。 相反,NiN 使用一个 NiN块,其输出通道数等于标签类别的数量。最后放一个 全局平均汇聚层(global average pooling layer),生成一个多元逻辑向量(logits)。NiN 设计的一个优点是,它显著减少了模型所需参数的数量。然而,在实践中,这种设计有时会增加训练模型的时间。

 全局池化会根据需要产生神经元,神经元个数可控,可调。而flatten方式就是一个硬链接,无法在flatten的时候调整链接数目。全局均值池化输出最常见的做法是把每个通道feature map输出一个神经元(均值结果输出)

总结:

  • NiN使用由一个卷积层和多个 1×1卷积层组成的块。该块可以在卷积神经网络中使用,以允许更多的每像素非线性。
  • NiN去除了容易造成过拟合的全连接层,将它们替换为全局平均汇聚层(即在所有位置上进行求和)。该汇聚层通道数量为所需的输出数量。

  • 移除全连接层可减少过拟合,同时显著减少NiN的参数。

4、含并行连结的网络Googlenet 

Googlenet将各种卷积层超参数并行组合来降低模型的参数,以降低模型的复杂度

 在GoogLeNet中,基本的卷积块被称为Inception块。Inception块由四条并行路径组成。 前三条路径使用窗口大小为 1×1、3×3和 5×5的卷积层,从不同空间大小中提取信息。 中间的两条路径在输入上执行 1×1卷积,以减少通道数,从而降低模型的复杂性。 第四条路径使用 3×3最大汇聚层,然后使用 1×1 卷积层来改变通道数。 这四条路径都使用合适的填充来使输入与输出的高和宽一致,最后我们将每条线路的输出在通道维度上连结,并构成Inception块的输出。在Inception块中,通常调整的超参数是每层输出通道的数量。

 5、批量归一化

原因:对于典型的多层感知机或卷积神经网络。当我们训练时,中间层中的变量(例如,多层感知机中的仿射变换输出)可能具有更广的变化范围:不论是沿着从输入到输出的层,跨同一层中的单元,或是随着时间的推移,模型参数的随着训练更新变幻莫测。 批量归一化的发明者非正式地假设,这些变量分布中的这种偏移可能会阻碍网络的收敛。 直观地说,我们可能会猜想,如果一个层的可变值是另一层的 100 倍,这可能需要对学习率进行补偿调整。更深层的网络很复杂,容易过拟合。 这意味着正则化变得更加重要。

解决方法:

批量归一化应用于单个可选层(也可以应用到所有层),其原理如下:在每次训练迭代中,我们首先归一化输入,即通过减去其均值并除以其标准差,其中两者均基于当前小批量处理。 接下来,我们应用比例系数和比例偏移。 正是由于这个基于批量统计的标准化,才有了批量归一化的名称。请注意,如果我们尝试使用大小为 1 的小批量应用批量归一化,我们将无法学到任何东西。 这是因为在减去均值之后,每个隐藏单元将为 0。 所以,只有使用足够大的小批量,批量归一化这种方法才是有效且稳定的。 请注意,在应用批量归一化时,批量大小的选择可能比没有批量归一化时更重要。从形式上来说,用 x∈B表示一个来自小批量 B的输入,批量归一化 BN根据以下表达式转换 x:

批量归一化可以使模型更快的收敛和减少过拟合(有正则化的作用)

 此外,批量归一化在训练和预测时候的参数是不一样的,将训练是学习到的均值和方差的移动平均值保存下来在模型预测的时候使用

6、残差网络resnet

resnet提出原因:

 因此,只有当较复杂的函数类包含较小的函数类时,我们才能确保提高它们的性能。 对于深度神经网络,如果我们能将新添加的层训练成 恒等映射(identity function) f(x)=x,新模型和原模型将同样有效。 同时,由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。

在数学里,恒等函数为一无任何作用的函数:它总是传回和其引数相同的值。换句话说,恒等函数为函数f(x) = x,输入等于输出。resnet对于训练恒等函数很有效。

resnet为什么有用的解释:(吴恩达的视频解释的更清楚)

关于残差的理解:

在线性拟合中的残差说的是数据点距离拟合直线的函数值的差,那么这里我们可以类比,这里的X就是我们的拟合的函数,而H(x)的就是具体的数据点,那么通过训练使的拟合的值加上F(x)的就得到具体数据点的值,因此这 F(x)的就是残差了,如下图:

 

 参考文章:深度学习 --- 深度残差网络详解ResNet_进击的菜鸟-CSDN博客_深度残差网络

  • 学习嵌套函数(nested function)是训练神经网络的理想情况。在深层神经网络中,学习另一层作为恒等映射(identity function)较容易(尽管这是一个极端情况)。

  • 残差映射可以更容易地学习同一函数,例如将权重层中的参数近似为零。

  • 利用残差块(residual blocks)可以训练出一个有效的深层神经网络:输入可以通过层间的残余连接更快地向前传播。

  • 残差网络(ResNet)对随后的深层神经网络设计产生了深远影响,无论是卷积类网络还是全连接类网络。

二、ResNet猫狗大战

import numpy as np
import matplotlib.pyplot as plt
import os
import torch
import torch.nn as nn
import torchvision
from torchvision import models,transforms,datasets
import time
import json
import shutil
from PIL import Image
import csv

# 判断是否存在GPU设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print('Using gpu: %s ' % torch.cuda.is_available())
!wget http://fenggao-image.stor.sinaapp.com/dogscats.zip #下载数据集并且解压
!unzip dogscats.zip
normalize = transforms.Normalize(mean=[0.485,0.456,0.406],std=[0.229,0.224,0.225])#在我们把数据导入模型进行训练的时候,我们首先要对数据进行标准化处理,为了保证网络可以良好的收敛
                                          #,在不清楚各个维度的相对重要程度之前,标准化使得输入的各个维度分布相近,从而允许我们在网络训练过程中
                                          #对各个维度“一视同仁”(即设置相同的学习率、正则项系数、权重初始化、以及激活函数)。
                                          #反过来,当我们使用全局相同的学习率、权重初始化、以及激活函数等网络设置时,方差更大的维度将获得更多的重视。
vgg_format = transforms.Compose([                        #transforms.Compose函数就是将transforms组合在一起;而每一个transforms都有自己的功能。最终只要使用定义好的train_transformer 就可以按照循序处理transforms的要求的。
      transforms.CenterCrop(224),
      transforms.ToTensor(),                        ##将PIL Image或者 ndarray 转换为tensor,并且归一化至[0-1]
      normalize,
])
data_dir = './dogscats'
dsets = {x:datasets.ImageFolder(os.path.join(data_dir,x),vgg_format) for x in ['train','valid']}      #dsets是个字典,里面有两个键,一个train,一个valid

dset_sizes = {x:len(dsets[x]) for x in ['train','valid']}
dset_classes = dsets['train'].classes
print(dsets['valid'].classes)       #用一个list保存类别名称
print(dsets['train'].class_to_idx)    #类别对应的索引,与不做任何转换返回的 target 对应
print(dsets['train'].imgs[:5])      #保存(img-path, class) tuple的 list
print('dset_sizes:',dset_sizes)
print(dset_classes)
loader_train = torch.utils.data.DataLoader(dsets['train'], batch_size=48, shuffle=True, num_workers=6)
loader_valid = torch.utils.data.DataLoader(dsets['valid'], batch_size=5, shuffle=False, num_workers=6)
count=1
for data in loader_valid:
  print(count,end='\n')
  if count == 1:
    inputs_try,labels_try = data
  count+=1
print(labels_try)
print(inputs_try.shape)
def imshow(inp,title = None):
  inp = inp.numpy().transpose((1,2,0))  #transpose()函数的作用就是调换数组的行列值的索引值,numpy()将torch.FloatTensor转换为numpy
  #plt.axis("off")  # 不显示坐标尺寸
  mean = np.array([0.485,0.456,0.406])
  std = np.array([0.229,0.224,0.225])
  inp = np.clip(std * inp + mean,0,1)
  plt.imshow(inp)
  if title is not None:
    plt.title(title)
  plt.pause(0.001)
out = torchvision.utils.make_grid(inputs_try)   #make_grid的作用是将若干幅图像拼成一幅图像。其中padding的作用就是子图像与子图像之间的pad有多宽。nrow是一行放入八个图片。
imshow(out,title=[dset_classes[x] for x in labels_try])

 用ResNet18并修改模型的全连接层

model = models.resnet18(pretrained=True)
model_new = model;
model_new.fc = nn.Linear(512,2,bias=True)
model_new = model_new.to(device)
print(model_new)
criterion = nn.CrossEntropyLoss()        #采用交叉熵损失函数

lr = 0.001                                # 学习率0.001,每10epoch *0.1

optimizer = torch.optim.SGD(model_new.parameters(), lr=lr, momentum=0.9, weight_decay=5e-4)
# 随机梯度下降,momentum加速学习,Weight decay防止过拟合
def val_model(model,dataloader,size):
    model.eval()
    predictions = np.zeros(size)
    all_classes = np.zeros(size)
    all_proba = np.zeros((size,2))
    i = 0
    running_loss = 0.0
    running_corrects = 0
    with torch.no_grad():
        for inputs,classes in dataloader:
            inputs = inputs.to(device)
            classes = classes.to(device)
            outputs = model(inputs)
            loss = criterion(outputs,classes)           
            _,preds = torch.max(outputs.data,1)
            running_loss += loss.data.item()
            running_corrects += torch.sum(preds == classes.data)
            i += len(classes)     
    epoch_loss = running_loss / size
    epoch_acc = running_corrects.data.item() / size
    return epoch_loss, epoch_acc 
def train_model(model,dataloader,size,epochs=1,optimizer=None):
    
    
    for epoch in range(epochs):
        model.train()
        
        running_loss = 0.0
        running_corrects = 0
        count = 0
        for inputs,classes in dataloader:#循环每次取一批量的图像与标签
            inputs = inputs.to(device)#将图像搬移至指定设备上
            classes = classes.to(device)#将标签搬移至指定设备上
            outputs = model(inputs)#将批量图像数据input输入网络模型model,得到输出批量预测数据outputs
            loss = criterion(outputs,classes)#计算批量预测标签outputs与批量真实标签classes之间的损失函数loss           
            optimizer = optimizer
            optimizer.zero_grad()#优化器的梯度清零
            loss.backward()
            optimizer.step()#优化器的梯度进行更新,训练所得参数也更新
            _,preds = torch.max(outputs.data,1)
            running_loss += loss.data.item()#将本批量损失函数loss加至训练损失函数running_loss累计中
            running_corrects += torch.sum(preds == classes.data)#将本批量预测正确的样本数加至累计预测正确样本数running_corrects中
            count += len(inputs)
        epoch_loss = running_loss / size
        epoch_acc = running_corrects.data.item() / size
        epoch_Valloss, epoch_Valacc = val_model(model,loader_valid,dset_sizes['valid'])
        print('epoch: ',epoch,' Loss: {:.5f} Acc: {:.5f} ValLoss: {:.5f} ValAcc: {:.5f}'.format(
                     epoch_loss, epoch_acc,epoch_Valloss,epoch_Valacc))
        scheduler.step()
        
        

scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)#学习率衰减

train_model(model_new,loader_train,size=dset_sizes['train'], epochs=20, 
            optimizer=optimizer)                                          # 模型训练

 结果展示

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
卷积神经网络(Convolutional Neural Network,CNN)是一种深度学习模型,广泛应用于图像处理和计算机视觉任务中。人脸对齐是指将输入的人脸图像进行标准化处理,使得人脸在图像中的位置和姿态保持一致,以便后续的人脸识别、表情分析等任务能够更准确地进行。 在Python中,可以使用多种库和框架来实现卷积神经网络和人脸对齐。以下是一个简单的示例代码,使用dlib库进行人脸对齐: ```python import dlib import cv2 import numpy as np # 加载dlib的人脸检测器和关键点检测器 detector = dlib.get_frontal_face_detector() predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat") # 加载输入图像 image = cv2.imread("input.jpg") # 将图像转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 使用dlib的人脸检测器检测人脸 faces = detector(gray) # 遍历检测到的人脸 for face in faces: # 使用关键点检测器检测人脸关键点 landmarks = predictor(gray, face) # 提取关键点坐标 points = np.zeros((68, 2), dtype=int) for i in range(68): points[i] = (landmarks.part(i).x, landmarks.part(i).y) # 计算人脸对齐矩阵 align_matrix = cv2.getAffineTransform(points[17:48], 96 * np.float32([ [0.375, 0.5625], [0.625, 0.5625], [0.5, 0.7500] ])) # 对人脸图像进行仿射变换 aligned_face = cv2.warpAffine(image, align_matrix, (96, 112)) # 显示对齐后的人脸图像 cv2.imshow("Aligned Face", aligned_face) cv2.waitKey(0) # 关闭窗口 cv2.destroyAllWindows() ``` 这段代码使用dlib库进行人脸检测和关键点检测,然后根据检测到的关键点计算人脸对齐矩阵,并对人脸图像进行仿射变换,最终显示对齐后的人脸图像。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值