深度学习12—VGG19实现

目录

VGG19实现

1.为数据打标签的generate_txt.py

2.对图像进行预处理的data_process.py

3.VGG19的网络构建代码net_VGG19.py

4.训练得到pth模型参数文件的get_pth_file.py

5.预测代码predict.py

6.预测VGG16与VGG19结果对比


VGG19实现

1.为数据打标签的generate_txt.py

这里的程序设计思想还是可以学一下的。

import os
from os import getcwd # 文件夹操作

# 写入数据集对应的文件夹
classes = ['cat','dog']
sets = ['train']

# 主程序执行
if __name__ == "__main__":
    wd = getcwd() # 获取当前工作目录

    # 说明:当前代码的目录关系是:sets-->subset(当前只有一个“train”)-->type_name(“train”下的关于类别的文件夹,比如“dog”)-->具体样本数据
    # 遍历sets中的每个文件夹,当前sets中只有"train"一个文件夹
    for subset in sets:
        list_file = open("cls_"+subset+".txt",'w')

        # 拿到每个子文件夹的目录
        path_subset = subset
        type_names = os.listdir(path_subset) # 拿到subset文件夹下的所有动物分类文件夹type_name,存到type_names列表中

        # 遍历subset中的每个文件夹type_name
        """
        它遍历名为type_names的列表中的每个元素。
        代码的目的是检查每个元素是否存在于名为classes的集合中。
        如果存在,代码会继续执行下一次循环,处理下一个元素;
        如果不存在,代码会跳过当前循环并继续执行下一次循环。
        """
        for type_name in type_names:
            if type_name not in classes:
                continue

            # 打标签
            type_id = classes.index(type_name) # 按type_name文件夹在classes文件夹中的索引,为type_name编号

            # 生成每个type_name文件夹的路径
            type_path = os.path.join(path_subset,type_name)
            photo_names = os.listdir(type_path) # 拿到type_name文件夹下的所有图片,组成一个列表phto_names

            # 处理每一张图片
            """
            这段代码的作用是遍历名为photos_name的列表中的每个元素,
            并根据文件名的扩展名来过滤文件。代码会判断文件的扩展名是否为.jpg、.png或.jpeg,
            如果不是这些扩展名之一,则跳过当前文件的处理。对于符合条件的文件,
            代码会将其写入到名为list_file的文件中,并写入文件的类别ID和路径信息。
            """
            for photo_name in photo_names:

                """
                这一行代码使用os.path.splitext()函数将文件名photo_name分成文件名部分和扩展名部分,
                并将扩展名赋值给变量postfix。下划线_表示不使用文件名部分,只关注扩展名。
                """
                _,postfit = os.path.splitext(photo_name) # #该函数用于分离文件名与拓展名

                # 如果拓展名不在如下的列表中,则跳过当前循环;如果在,则继续
                if postfit not in ['.jpg','.png','.jpeg']:
                    continue

                # 将文件的类别ID和完整路径信息写入到名为list_file的文件中
                photo_path = os.path.join(type_path,photo_name)
                # print(wd) # C:\Users\ZARD\PycharmProjects\pythonProject\AAA_FX\revise_VGG19
                # 如上可知,wd为该项目的路径
                list_file.write(str(type_id)+';'+'%s/%s'%(wd,photo_path))
                list_file.write('\n') # 这一行代码写入一个换行符,将下一个文件的记录写入到新的一行

        list_file.close()

2.对图像进行预处理的data_process.py

对数据做一些基本操作,可根据实际需求进行更改。

import cv2
import numpy as np
import torch.utils.data as data
from PIL import  Image

def preprocess_input(x):
    x/=127.5
    x-=1.
    return x
def cvtColor(image):
    if len(np.shape(image))==3 and np.shape(image)[-2]==3:
        return image
    else:
        image=image.convert('RGB')
        return image


class DataGenerator(data.Dataset):
    def __init__(self,annotation_lines,inpt_shape,random=True):
        self.annotation_lines=annotation_lines
        self.input_shape=inpt_shape
        self.random=random

    def __len__(self):
        return len(self.annotation_lines)
    def __getitem__(self, index):
        annotation_path=self.annotation_lines[index].split(';')[1].split()[0]
        image=Image.open(annotation_path)
        image=self.get_random_data(image,self.input_shape,random=self.random)
        image=np.transpose(preprocess_input(np.array(image).astype(np.float32)),[2,0,1])
        y=int(self.annotation_lines[index].split(';')[0])
        return image,y
    def rand(self,a=0,b=1):
        return np.random.rand()*(b-a)+a

    def get_random_data(self,image,inpt_shape,jitter=.3,hue=.1,sat=1.5,val=1.5,random=True):

        image=cvtColor(image)
        iw,ih=image.size
        h,w=inpt_shape
        if not random:
            scale=min(w/iw,h/ih)
            nw=int(iw*scale)
            nh=int(ih*scale)
            dx=(w-nw)//2
            dy=(h-nh)//2

            image=image.resize((nw,nh),Image.BICUBIC)
            new_image=Image.new('RGB',(w,h),(128,128,128))

            new_image.paste(image,(dx,dy))
            image_data=np.array(new_image,np.float32)
            return image_data
        new_ar=w/h*self.rand(1-jitter,1+jitter)/self.rand(1-jitter,1+jitter)
        scale=self.rand(.75,1.25)
        if new_ar<1:
            nh=int(scale*h)
            nw=int(nh*new_ar)
        else:
            nw=int(scale*w)
            nh=int(nw/new_ar)
        image=image.resize((nw,nh),Image.BICUBIC)
        #将图像多余的部分加上灰条
        dx=int(self.rand(0,w-nw))
        dy=int(self.rand(0,h-nh))
        new_image=Image.new('RGB',(w,h),(128,128,128))
        new_image.paste(image,(dx,dy))
        image=new_image
        #翻转图像
        flip=self.rand()<.5
        if flip: image=image.transpose(Image.FLIP_LEFT_RIGHT)
        rotate=self.rand()<.5
        if rotate:
            angle=np.random.randint(-15,15)
            a,b=w/2,h/2
            M=cv2.getRotationMatrix2D((a,b),angle,1)
            image=cv2.warpAffine(np.array(image),M,(w,h),borderValue=[128,128,128])
        #色域扭曲
        hue=self.rand(-hue,hue)
        sat=self.rand(1,sat) if self.rand()<.5 else 1/self.rand(1,sat)
        val=self.rand(1,val) if self.rand()<.5 else 1/self.rand(1,val)
        x=cv2.cvtColor(np.array(image,np.float32)/255,cv2.COLOR_RGB2HSV)#颜色空间转换
        x[...,1]*=sat
        x[...,2]*=val
        x[x[:,:,0]>360,0]=360
        x[:,:,1:][x[:,:,1:]>1]=1
        x[x<0]=0
        image_data=cv2.cvtColor(x,cv2.COLOR_HSV2RGB)*255
        return image_data

3.VGG19的网络构建代码net_VGG19.py

其实该代码可以直接去torch的官网下,而且如果想改动VGG网络结构,只需微调一下,就可以实现其代码了。

在torch官网能够下载到一些预训练的模型:

https://pytorch.org/vision/stable/models/vgg.html

比如如下的VGG16:

逐条解析该程序:

导包和下载网络权重:

import torch
import torch.nn as nn
​
model_urls = {
    "vgg19":  "https://download.pytorch.org/models/vgg19-dcbb9e9d.pth",
}#权重下载网址,该地址在torch官网上可下载

VGG网络的类:

class VGG(nn.Module):
    def __init__(self, features, num_classes = 1000, init_weights = True, dropout = 0.5):
        #继承
        super(VGG,self).__init__()
        self.features = features
        self.avgpool = nn.AdaptiveAvgPool2d((7, 7)) # AdaptiveAvgPool2d使处于不同大小的图片也能进行分类
        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=dropout),  # 完成4096的全连接
            nn.Linear(4096, num_classes), #对 num_classes的分类
        )
        if init_weights:
            for m in self.modules():
                if isinstance(m, nn.Conv2d):
                    nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")
                    if m.bias is not None:
                        nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.BatchNorm2d):
                    nn.init.constant_(m.weight, 1)
                    nn.init.constant_(m.bias, 0)
                elif isinstance(m, nn.Linear):
                    nn.init.normal_(m.weight, 0, 0.01)
                    nn.init.constant_(m.bias, 0)
​
    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x

网络结构设置:

def make_layers(cfg, batch_norm = False): # make_layers对输入的cfg进行循环
    layers = []
    in_channels = 3
    for v in cfg:
        if v == "M": # 对cfg进行输入循环,取第一个v
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)] # 把输入图像进行缩小
        else:
            #v = cast(int, v)
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            if batch_norm:
                layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)]
            else:
                layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    return nn.Sequential(*layers)
​
​
cfgs = {
    "VGG19": [64, 64, "M", 128, 128, "M", 256, 256, 256, 256, "M", 512, 512, 512, 512, "M", 512, 512, 512, 512, "M"],
}
# 这部分代码比较讲究,可参考B站视频,地址贴在下面了
def vgg19(pretrained=False, progress=True,num_classes=2):
    model = VGG(make_layers(cfgs["VGG19"]))
    if pretrained:
        from torch.hub import load_state_dict_from_url
        state_dict = load_state_dict_from_url(model_urls['vgg19'],model_dir='./model' ,progress=progress)#预训练模型地址
        model.load_state_dict(state_dict)
    if num_classes != 1000:
        model.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),  # 随机删除一部分不合格
            nn.Linear(4096, 4096),
            nn.ReLU(True),  # 防止过拟合
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes),
        )
    return model
if __name__ == '__main__':
    in_data = torch.ones(1, 3, 224, 224)
    net = vgg19(pretrained=False, progress=True, num_classes=2)
    out = net(in_data)
    print(out)

4.训练得到pth模型参数文件的get_pth_file.py

生成训练集和测试集:

'''数据集'''
annotation_path = 'cls_train.txt' # 读取数据集生成的文件
with open(annotation_path,'r') as f:
    lines = f.readlines() # 拿到所有图片数据的地址,lines的数据类型是一个列表,其中存下了所有图片地址
    #print(type(lines)) # <class 'list'>
    
import numpy as np
np.random.seed(10101) # 函数用于生成指定随机数
np.random.shuffle(lines) # 数据打乱
num_val = int(len(lines)*0.2) # 用做测试的数据数量
#print(num_val) #-->266
num_train = len(lines)-num_val # 训练的数据的数量
#输入图像大小
input_shape=[224,224]   #导入图像大小
# 生成数据
from AAA_FX.revise_VGG19.data_process import DataGenerator
​
train_data = DataGenerator(lines[:num_train],input_shape,True)
val_data = DataGenerator(lines[num_train:],input_shape,False)
​
val_len=len(val_data)
print(val_len)#返回测试集长度

加载数据:

# 取黑盒子工具
"""加载数据"""
from torch.utils.data import DataLoader#工具取黑盒子,用函数来提取数据集中的数据(小批次)

gen_train=DataLoader(train_data,batch_size=4)#训练集batch_size读取小样本,规定每次取多少样本
gen_test=DataLoader(val_data,batch_size=4)#测试集读取小样本

构建网络:

'''构建网络'''
from net_VGG19 import vgg19

device=torch.device('cuda'if torch.cuda.is_available() else "cpu")#电脑主机的选择
net=vgg19(True, progress=True,num_classes=2)#定于分类的类别
net.to(device)

选择优化器和学习率的调整方法:

'''选择优化器和学习率的调整方法'''
lr=0.0001#定义学习率
optim=torch.optim.Adam(net.parameters(),lr=lr)#导入网络和学习率
sculer=torch.optim.lr_scheduler.StepLR(optim,step_size=1)#步长为1的读取

训练:

'''训练'''
epochs=20#读取数据次数,每次读取顺序方式不同
for epoch in range(epochs):
    total_train=0 #定义总损失
    for data in gen_train:
        img,label=data
        with torch.no_grad():
            img =img.to(device)
            label=label.to(device)
        optim.zero_grad()
        output=net(img)
        train_loss=nn.CrossEntropyLoss()(output,label).to(device)
        train_loss.backward()#反向传播
        optim.step()#优化器更新
        total_train+=train_loss #损失相加
    sculer.step()
    total_test=0#总损失
    total_accuracy=0#总精度
    for data in gen_test:
        img,label =data #图片转数据
        with torch.no_grad():
            img=img.to(device)
            label=label.to(device)
            optim.zero_grad()#梯度清零
            out=net(img)#投入网络
            test_loss=nn.CrossEntropyLoss()(out,label).to(device)
            total_test+=test_loss#测试损失,无反向传播
            accuracy=((out.argmax(1)==label).sum()).clone().detach().cpu().numpy()#正确预测的总和比测试集的长度,即预测正确的精度
            total_accuracy+=accuracy
    print("训练集上的损失:{}".format(total_train))
    print("测试集上的损失:{}".format(total_test))
    print("测试集上的精度:{:.1%}".format(total_accuracy/val_len))#百分数精度,正确预测的总和比测试集的长度

    torch.save(net.state_dict(),"DogandCat{}.pth".format(epoch+1))
    print("模型已保存")

5.预测代码predict.py

导入图像:

from PIL import Image
test_pth='.\\train\cat\cat.6.jpg'#设置可以检测的图像
test=Image.open(test_pth)

处理图片:

'''处理图片'''
from torchvision import transforms

transform = transforms.Compose([transforms.Resize((224,224)),transforms.ToTensor()])
image=transform(test)

加载网络:

'''加载网络'''
device=torch.device("cuda" if torch.cuda.is_available() else "cpu")#CPU与GPU的选择
net =vgg19()#输入网络

model=torch.load("./DogandCat8.pth",map_location=device)#已训练完成的结果权重输入
net.load_state_dict(model)#模型导入
net.eval()#设置为推测模式
image=torch.reshape(image,(1,3,224,224))#四维图形,RGB三个通
with torch.no_grad():
    out=net(image)
out=F.softmax(out,dim=1)#softmax 函数确定范围
out=out.data.cpu().numpy()
print(out)
a=int(out.argmax(1))#输出最大值位置
plt.figure()
list=['Cat','Dog']
plt.suptitle("Classes:{}:{:.1%}".format(list[a],out[0,a]))#输出最大概率的道路类型
plt.imshow(test)
plt.show()

VGG16的道理是一样的,这里略。 

6.预测VGG16与VGG19结果对比

VGG19的预测结果:

VGG16的预测结果:

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: VGG19风格迁移代码是一种用于将图像的内容与风格进行分离,并将两者合成以创建新图像的算法。该算法基于深度卷积神经网络VGG19,它是一种经典的视觉感知模型,用于图像分类和识别任务。 在实现VGG19风格迁移代码时,我们需要进行以下步骤: 1. 导入相关的Python库和模块,例如tensorflow、opencv和numpy等。 2. 加载VGG19模型的权重文件,以便使用VGG19进行图像特征提取。 3. 定义图片的内容损失函数,该函数用于衡量生成图像与原始图像之间的内容相似度。 4. 定义图片的风格损失函数,该函数用于衡量生成图像与目标风格图像之间的风格相似度。 5. 定义总体损失函数,该函数将内容损失和风格损失加权组合在一起,以平衡两者的影响。 6. 使用优化算法,如梯度下降法,来最小化总体损失函数,从而更新生成图像的像素值。 7. 重复步骤6,直到生成的图像与原始图像在内容和风格上都达到满意的程度。 需要注意的是,VGG19风格迁移代码是一种较为复杂和计算密集的算法,可能需要较长的训练时间和高性能的计算设备。因此,在实际应用中,可以使用预训练的VGG19模型,以加快风格迁移的速度。 这就是VGG19风格迁移代码的一般步骤和流程。通过这种方法,我们可以将不同图像的内容与风格进行有机地融合,从而创造出独特且具有艺术感的图像。 ### 回答2: VGG19是一种深度卷积神经网络模型,经常被用于图像分类任务。而风格迁移是一种计算机视觉的技术,它可以将一幅图像的风格迁移到另一幅图像上,从而创造出具有新风格的图像。 VGG19风格迁移代码实现的基本原理如下: 1. 导入VGG19模型的权重参数,这些参数在预训练模型中已经通过大规模训练集进行了优化,可以提取出图像中的不同特征。 2. 加载待进行风格迁移的两个图像,一个是内容图像,一个是风格图像,通过读取图像的像素值进行处理。 3. 对内容图像和风格图像分别进行预处理,将图像缩放至合适的大小,并通过减去均值来进行归一化。 4. 将内容图像和风格图像输入到VGG19网络中,分别提取出内容特征和风格特征,这些特征通过网络的不同层来表示不同等级和抽象程度的特征信息。 5. 使用内容图像的特征与风格图像的特征计算损失函数,通过最小化这个损失函数来求解风格迁移的目标图像。 6. 通过梯度下降等优化算法,对目标图像进行迭代优化,不断更新图像的像素值,使得目标图像的内容与内容图像的特征相似,同时与风格图像的特征相匹配。 7. 最后得到的目标图像即为风格迁移后的图像。 这是简单概括了VGG19风格迁移代码的运行过程。实际使用时,还需要在代码中设置合适的超参数、学习率,以及选择不同层的特征来表示风格等。这是一个复杂的计算过程,需要一定的计算资源和训练时间。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Top Secret

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值