摘要

本日志记录我初次用pytorch搭建卷积神经网络,参考了很多别人的经验和代码,主要是根据https://blog.csdn.net/jining11/article/details/89114502这篇博客,按照博客上的内容一步步弄下来的,加强了我对CNN的理解,并明白了如何使用torch搭建神经网络。(在这里十分建议看懂之后在自己写代码,而不是直接ctrl+c/ctrl+v)

CNN

卷积神经网络之前学习的时候就接触过,在图像识别领域用的比较频繁。之前上数字图像处理课程的时候,最后一个实验是识别麻将和交通标志。如果使用卷积神经网络进行训练识别,会要比简单的对比准确率高。为了熟悉CNN。特意从csdn上找了一篇博客进行仿写。博客链接: 原博客地址

数据集介绍

CIFAR-10数据集可以在官网下载链接: link不过网速很慢,我会附上我下载好的python版本。CIFAR中50000个作为train数据,10000作为test数据。这些图片中不存在类的重叠。更加详细的介绍上链接。官网介绍的很详细。
CIFAR有10个类别,60000个32*32像素的图片,如下图为部分展示

代码实现

代码可以分为搭建神经网络,写fit函数,读取数据集合3个部分,下面将分别介绍

读取数据集合

关于数据读取之后,转换成torch的张量形式,和超参数的选择代码。


cifar10_mean = (0.49,0.48,0.45)
cifar10_std = (0.25,0.24,0.26)

#define transform operations
train_transform = transforms.Compose([
    #data augmentation
    transforms.Pad(4),
    transforms.RandomHorizontalFlip(),
    transforms.RandomCrop(32),
    
    transforms.ToTensor(),
    transforms.Normalize(cifar10_mean,cifar10_std)
    ])

test_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(cifar10_mean,cifar10_std)
    ])


#load data读取数据
train_dataset = torchvision.datasets.CIFAR10(root='./data/',
                                             train=True,
                                             transform=train_transform,
                                             download=True)
test_dataset = torchvision.datasets.CIFAR10(root='./data',
                                            train=False,
                                            transform=test_transform,
                                            )
#将训练集分为校验和训练
num_train = int(0.9*len(train_dataset))
num_val = int(0.1*len(train_dataset))
train_dataset,val_dataset=random_split(train_dataset,[num_train,num_val])
train_loader = Data.DataLoader(dataset=train_dataset,
                                batch_size=100,
                                shuffle=True)
val_loader = Data.DataLoader(dataset=val_dataset,
                             batch_size=100,
                             shuffle=True)
test_loader = Data.DataLoader(dataset=test_dataset,
                              batch_size=100,
                              shuffle=True)

""" 超参数选择 epochs:迭代次数 image_size:图片大小 num_classes分类数 lr:学习率 """
num_epochs = 10
image_size=32
num_classes=10
lr = 0.01

搭建CNN网络

CNN分为卷积–>池化–>再卷积–>再池化–>全连接层组成。由于输入是3通道的32*32像素的图片,因此第一个卷积层为3输入,我看的那篇博客使用的是16输出,代表16个隐层。(原理未知)。再加上归一化函数BatchNorm2d(16)。归一化函数原理见链接: link.后接上激活函数nn.ReLU()。和池化层。这里使用的是最大池化。通过2个这样的过程最后连接上全连接层。总体代码如下:

""" 搭建MyCNN网络 """
class MyCNN(nn.Module):
    def __init__(self,image_size,num_classes):
        super(MyCNN,self).__init__()
        self.conv1=nn.Sequential(
            nn.Conv2d(in_channels=3,out_channels=16,kernel_size=3,stride=1,padding=1),
            nn.BatchNorm2d(16),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2,stride=2),
            )
        self.conv2=nn.Sequential(
            nn.Conv2d(in_channels=16,out_channels=32,kernel_size=3,stride=1,padding=1),
            nn.BatchNorm2d(32),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2,stride=2),
            )
        
        self.fc=nn.Linear(32*(image_size//4)*(image_size//4),num_classes)
    def forward(self, x):
        """ Parameters ---------- x : N*3*image_size*image_size Returns:N*num_classes ------- """
        x=self.conv1(x)
        x=self.conv2(x)
        x=x.view(x.size(0),-1)
        output=self.fc(x)
        return output
    def save_model(model,save_path):
        torch.save(model.state_dict(),save_path)

fit函数

fit函数分为train过程和val过程,采用校验集的方法对数据进行训练。代码如下:

""" 训练评估模型 """
def fit(model,train_loader,test_loader,num_epochs,optimizer,device):
    loss_function=nn.CrossEntropyLoss()
    model.to(device)
    loss_function.to(device)
    
    losses=[]
    accs=[]
    
    for epoch in range(num_epochs):
        print('Epoch {}/{}:'.format(epoch + 1, num_epochs))
        "训练模型"
        loss = train(model,train_loader,loss_function,optimizer,device)
        losses.append(loss)
        "评估模型"
        accuracy=evaluate(model, test_loader, device)
        accs.append(accuracy)
    
    show_cruve(losses,"train loss in every epoch")
    show_cruve(accs,"test accuracy")
    
    
""" 训练模型 """
def train(model,train_loader,loss_function,optimizer,device):
    total_loss = 0
    for i,(images,targets) in enumerate(train_loader):
        images=images.to(device)
        targets=targets.to(device)
        #前向传播
        output = model(images)
        loss = loss_function(output,targets)
        #反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        total_loss +=loss.item()
        #打印损失
        if (i+1)%100==0:
            print("step[{}/{}]Train Loss:{:.4f}".format(i+1,len(train_loader),loss.item()))
    return total_loss/len(train_loader)
    
""" 评估模型 """
def evaluate(model,test_loader,device):
    model.eval()
    with torch.no_grad():
        correct = 0
        total = 0
        
        for i,(images,targets) in enumerate(test_loader):
            images = images.to(device)
            targets = targets.to(device)
            
            output = model(images)
            _,predicted=torch.max(output.data,dim=1)
            correct += (predicted==targets).sum().item()
            total += targets.size(0)
            
        accuracy = correct/total
        print('Accuracy on Test Set:{:.4f}%'.format(100*accuracy))
        return accuracy

""" 显示损失曲线 """  
def show_cruve(ys,title):
    x = np.array(range(len(ys)))
    y = np.array(ys)
    plt.plot(x,y,c='b')
    plt.axis()
    plt.title("{} curve".format(title))
    plt.xlabel('epochs')
    plt.ylabel('{}'.format(title))
    plt.show()  

总结

由于只是进一步熟悉神经网络的搭建,所以并未在改善模型上下功夫,通过自己动手搭建模型。加深了对神经网络和其应用的理解。完整代码和CAFAR数据由于太大不能上传,需要的话再说。