VGG-16卷积神经网络实现

VGGNet在ILSVRC2014比赛中获得了分类项目第二名和定位项目第一名,以其强大的泛化能力和简洁的结构著称。该网络使用统一的3x3卷积核和2x2最大池化层,广泛应用于图像特征提取。本文详细介绍了VGGNet的训练过程、网络结构和实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

VGG简介

  • ILSVRC2014比赛分类项目第2名(第1名是GoogLeNet)和定位项目第1名。
  • 拓展性很强:迁移到其他图片数据上的泛化性非常好。
  • 结构简洁:整个网络都使用了同样大小的卷积核尺寸(3X3)和最大池化尺寸(2X2),到目前为止VGGNet依然经常被用来提取图像特征。

网络结构图

在这里插入图片描述
VGGNet训练时使用了4块Geforce GTX Titan GPU并行计算,速度比单块GPU快了3.75倍。每个网络耗时2~3周才可以训练完。(数据库ImageNet)

完整代码

from datetime import datetime
import tensorflow as tf
import math
import time

def conv_op(input_op,name,kh,kw,n_out,dh,dw,p): 
    # 卷积核 (kh,kw,n_out)-高、宽、通道数
    # 步长 (dh,dw)
    # p 参数列表
    n_in = input_op.get_shape()[-1].value #获取输入的通道数
    
    with tf.name_scope(name) as  scope:
        kernel = tf.get_variable(scope+'w',
                                shape=[kh,kw,n_in,n_out],
                                dtype=tf.float32,
                                initializer=tf.contrib.layers.xavier_initializer_conv2d())
        conv = tf.nn.conv2d(input_op,kernel,(1,dh,dw,1),padding="SAME")
        bias_init_val = tf.constant(0.0,shape=[n_out],dtype=tf.float32)
        biases = tf.Variable(bias_init_val,trainable=True,name='b')
        z = tf.nn.bias_add(conv,biases)
        activation = tf.nn.relu(z,name=scope)
        p += [kernel,biases]
    return activation

def fc_op(input_op,name,n_out,p):
    n_in = input_op.get_shape()[-1].value
    
    with tf.name_scope(name) as scope:
        kernel = tf.get_variable(scope+"w",
                                shape=[n_in,n_out],
                                dtype=tf.float32,
                                initializer=tf.contrib.layers.xavier_initializer())
        biases = tf.Variable(tf.constant(0.1,shape=[n_out],dtype=tf.float32),name='b')
        activation = tf.nn.relu_layer(input_op,kernel,biases,name=scope)
        p += [kernel,biases]
        return activation

def mpool_op(input_op,name,kh,kw,dh,dw):
    return tf.nn.max_pool(input_op,
                         ksize=[1,kh,kw,1],
                         strides=[1,dh,dw,1],
                         padding='SAME',
                         name=name)

def inference_op(input_op,keep_prob):
    p=[]
    conv1_1 = conv_op(input_op,name='conv1_1',kh=3,kw=3,n_out=64,dh=1,dw=1,p=p)
    conv1_2 = conv_op(conv1_1,name='con1_2',kh=3,kw=3,n_out=64,dh=1,dw=1,p=p)
    pool1 = mpool_op(conv1_2,name='pool1',kh=2,kw=2,dh=2,dw=2)
    
    conv2_1 = conv_op(pool1,name='conv2_1',kh=3,kw=3,n_out=128,dh=1,dw=1,p=p)
    conv2_2 = conv_op(conv2_1,name='con2_2',kh=3,kw=3,n_out=128,dh=1,dw=1,p=p)
    pool2 = mpool_op(conv2_2,name='pool2',kh=2,kw=2,dh=2,dw=2)

    conv3_1 = conv_op(pool2,name='conv3_1',kh=3,kw=3,n_out=256,dh=1,dw=1,p=p)
    conv3_2 = conv_op(conv3_1,name='con3_2',kh=3,kw=3,n_out=256,dh=1,dw=1,p=p)
    conv3_2 = conv_op(conv3_1,name='con3_2',kh=3,kw=3,n_out=256,dh=1,dw=1,p=p)       
    pool3 = mpool_op(conv3_2,name='pool3',kh=2,kw=2,dh=2,dw=2)

    conv4_1 = conv_op(pool3,name='conv4_1',kh=3,kw=3,n_out=512,dh=1,dw=1,p=p)
    conv4_2 = conv_op(conv4_1,name='con4_2',kh=3,kw=3,n_out=512,dh=1,dw=1,p=p)
    conv4_2 = conv_op(conv4_1,name='con4_2',kh=3,kw=3,n_out=512,dh=1,dw=1,p=p)       
    pool4 = mpool_op(conv4_2,name='pool4',kh=2,kw=2,dh=2,dw=2)

    conv5_1 = conv_op(pool4,name='conv5_1',kh=3,kw=3,n_out=512,dh=1,dw=1,p=p)
    conv5_2 = conv_op(conv5_1,name='con5_2',kh=3,kw=3,n_out=512,dh=1,dw=1,p=p)
    conv5_2 = conv_op(conv5_1,name='con5_2',kh=3,kw=3,n_out=512,dh=1,dw=1,p=p)       
    pool5 = mpool_op(conv5_2,name='pool5',kh=2,kw=2,dh=2,dw=2)

    shp = pool5.get_shape()
    # shp.shape ---  [batch_size,h,w,d] 0:batch_size 1:高 2:宽 3:通道
    flattened_shape = shp[1].value * shp[2].value * shp[3].value
    reshl = tf.reshape(pool5,[-1,flattened_shape],name='reshl')
    # 隐含节点数4096
    fc6 = fc_op(reshl,name='fc6',n_out=4096,p=p)
    fc6_drop = tf.nn.dropout(fc6,keep_prob,name='fc6_drop')
    #fc7
    fc7 = fc_op(fc6_drop,name='fc7',n_out=4096,p=p)
    fc7_drop = tf.nn.dropout(fc7,keep_prob,name='fc_drop')
    # fc8
    fc8 = fc_op(fc7_drop,name='fc8',n_out=1000,p=p)
    # softmax
    softmax = tf.nn.softmax(fc8)
    predictions = tf.argmax(softmax,1)
    return predictions,softmax,fc8,p

def time_tensorflow_run(session,target,feed,info_string):    
    # 预热
    num_step_burn_in = 10
    total_duration = 0.0
    total_duration_squared = 0.0
    for i in range(num_step_burn_in + num_batches):
        start_time = time.time()
        _ = session.run(target,feed_dict=feed)
        duration = time.time() - start_time
        if i >= num_step_burn_in:
            if not i % 10:
                print('%s: step : %d, duration = %.3f'
                     % (datetime.now(),i-num_step_burn_in,duration))

            total_duration += duration
            total_duration_squared += duration*duration
    mn = total_duration / num_batches
    vr = total_duration_squared / num_batches - mn*mn
    sd = math.sqrt(vr)
    print('%s:%s across %d steps, %.3f +/- %.3f sec / batch' %
            (datetime.now(),info_string,num_batches,mn,sd))

def run_benchmark():
    with tf.Graph().as_default():
        image_size = 224
        images = tf.Variable(tf.random_normal([batch_size,
                                              image_size,
                                              image_size,
                                              3],
                                             dtype=tf.float32,
                                             stddev=1e-1))
        keep_prob = tf.placeholder(tf.float32)
        predictions,softmax,fc8,p = inference_op(images,keep_prob)
        init = tf.global_variables_initializer()
        sess = tf.Session()
        sess.run(init)

        time_tensorflow_run(sess,predictions,{keep_prob:1.0},'Forward')
        # objective = tf.nn.l2_loss(fc8)
        # grad = tf.gradients(objective,p)
        # time_tensorflow_run(sess,grad,{keep_prob:0.5},'Backward')

batch_size = 32
num_batches = 100
run_benchmark()

运行结果

GPU:Quadro p2000 5GB
在这里插入图片描述

### 使用VGG-16卷积神经网络实现犬类图像分类 为了使用预训练的VGG-16模型来执行犬类图像分类任务,可以按照以下方法构建解决方案。此过程涉及准备环境、加载预训练模型以及调整模型以适应特定的任务需求。 #### 准备工作 确保安装了必要的库版本: ```bash pip install torch==1.9.0 torchvision==0.10.0 ``` #### 加载并配置VGG-16模型 通过`torchvision.models`模块可以直接获取已经完成训练的VGG-16模型实例,并将其设置为评估模式以便后续操作[^2]。 ```python import torch from torchvision import models use_pretrained = True net = models.vgg16(pretrained=use_pretrained) net.eval() print(net) ``` 这段代码会打印出完整的VGG-16架构详情,帮助理解每一层的功能及其参数量大小。 #### 修改最后一层全连接层 由于原始VGG-16是在ImageNet上进行了预训练,默认情况下其最后的全连接层输出维度对应于该数据集中存在的1000个类别。对于仅需区分两类(例如只针对犬类或其他任何二元分类场景),应当修改这一部分使其更适合当前任务的需求。 ```python num_features = net.classifier[-1].in_features features = list(net.classifier.children())[:-1] # 去掉最后一个全连接层 fc_layer = torch.nn.Linear(num_features, 1) # 新增一层用于二分类 features.extend([torch.nn.ReLU(inplace=True), fc_layer]) net.classifier = torch.nn.Sequential(*features) if torch.cuda.is_available(): net = net.cuda() print("Modified Model:", net) ``` 这里创建了一个新的线性变换层作为最终预测器,它接收来自倒数第二层特征映射的数量作为输入,并产生单一数值输出表示属于某一类别的概率估计值;同时加入了激活函数ReLU以保持非线性的特性。 #### 数据预处理与增强 考虑到实际应用中的X光安检图像可能较为复杂且背景干扰较多,在进行图像识别之前通常还需要对输入样本做适当转换处理,比如裁剪、缩放、翻转等随机变化手段增加泛化能力[^1]。然而,对于简单的宠物照片而言,则只需遵循标准流程即可满足要求。 ```python from torchvision import transforms data_transforms = { 'train': transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), 'val': transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), } ``` 上述定义了一套适用于训练阶段和验证/测试阶段的不同图像预处理策略集合,包括尺寸调整、颜色标准化等一系列常规步骤,有助于提高模型性能表现。 #### 训练模型 有了经过改造后的基础框架之后就可以着手准备具体的训练环节了。这一步骤涉及到批量读取图片文件夹内的所有样本、分配至GPU设备加速计算速度等方面的工作细节。 ```python from torch.utils.data import DataLoader from torchvision.datasets import ImageFolder image_datasets = {x: ImageFolder(root=f'data/{x}', transform=data_transforms[x]) for x in ['train', 'val']} dataloaders = {x: DataLoader(image_datasets[x], batch_size=4, shuffle=(x=='train'), num_workers=4) for x in ['train', 'val']} device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') criterion = torch.nn.BCEWithLogitsLoss().to(device) optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, net.parameters()), lr=1e-4) for epoch in range(num_epochs): running_loss = 0.0 for phase in ['train', 'val']: if phase == 'train': model.train(True) else: model.train(False) for inputs, labels in dataloaders[phase]: optimizer.zero_grad() with torch.set_grad_enabled(phase == 'train'): outputs = net(inputs.to(device)) loss = criterion(outputs.squeeze(), labels.float().to(device)) if phase == 'train': loss.backward() optimizer.step() running_loss += loss.item() print(f"[Epoch {epoch+1}/{num_epochs}] Loss: {running_loss:.4f}") ``` 以上展示了基本的循环迭代逻辑,其中包含了切换不同运行状态(`train`, `eval`)、前向传播求解损失值、反向传播更新权重参数等内容。注意此处假设标签是以整型形式给出而输出则采用sigmoid压缩到区间内再比较差异度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值