天天看点

DL代码参考

文章目录

    • AlexNet实现
    • VGG16-Keras实现

AlexNet实现

在2012年ImageNet竞赛中,来自多伦多大学的Hinton教授带着他的学生Alex Krizhevsky惊艳了世界,他们构建的神经网络模型以超过第二名10.9个百分点的绝对优势一举夺冠。 而这种新型网络结构以第一作者Alex名字进行命名,被称为AlexNet。那么为什么AlexNet在那个时候会有如此巨大的优势呢?

DL代码参考

AlexNet中包含了几个比较新的技术点,也首次在CNN中成功应用了ReLU、Dropout和LRN等Trick。同时AlexNet也使用了GPU进行运算加速。

AlexNet将LeNet的思想发扬光大,把CNN的基本原理应用到了很深很宽的网络中。AlexNet主要使用到的新技术点如下:

(1)成功使用ReLU作为CNN的激活函数,并验证其效果在较深的网络超过了Sigmoid,成功解决了Sigmoid在网络较深时的梯度弥散问题。虽然ReLU激活函数在很久之前就被提出了,但是直到AlexNet的出现才将其发扬光大。

(2)训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽有单独的论文论述,但是AlexNet将其实用化,通过实践证实了它的效果。在AlexNet中主要是最后几个全连接层使用了Dropout。

(3)在CNN中使用重叠的最大池化。此前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间会有重叠和覆盖,提升了特征的丰富性。

(4)提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。

(5)使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时大量的矩阵运算。AlexNet使用了两块GTX 580 GPU进行训练,单个GTX 580只有3GB显存,这限制了可训练的网络的最大规模。因此作者将AlexNet分布在两个GPU上,在每个GPU的显存中储存一半的神经元的参数。因为GPU之间通信方便,可以互相访问显存,而不需要通过主机内存,所以同时使用多块GPU也是非常高效的。同时,AlexNet的设计让GPU之间的通信只在网络的某些层进行,控制了通信的性能损耗。

(6)数据增强,随机地从256256的原始图像中截取224224大小的区域(以及水平翻转的镜像),相当于增加了2*(256-224)^2=2048倍的数据量。如果没有数据增强,仅靠原始的数据量,参数众多的CNN会陷入过拟合中,使用了数据增强后可以大大减轻过拟合,提升泛化能力。进行预测时,则是取图片的四个角加中间共5个位置,并进行左右翻转,一共获得10张图片,对他们进行预测并对10次结果求均值。同时,AlexNet论文中提到了会对图像的RGB数据进行PCA处理,并对主成分做一个标准差为0.1的高斯扰动,增加一些噪声,这个Trick可以让错误率再下降1%。

tf实现

# -*- coding=UTF-8 -*-
import tensorflow as tf
# 输入数据
import input_data
mnist = input_data.read_data_sets("/tmp/data/", one_hot=True)
# 定义网络超参数
learning_rate = 0.001  
training_iters = 200000  
batch_size = 64  
display_step = 20  
# 定义网络参数
n_input = 784  # 输入的维度
n_classes = 10 # 标签的维度
dropout = 0.8  # Dropout 的概率
# 占位符输入
x = tf.placeholder(tf.types.float32, [None, n_input])
y = tf.placeholder(tf.types.float32, [None, n_classes])
keep_prob = tf.placeholder(tf.types.float32)
# 卷积操作
def conv2d(name, l_input, w, b):  
    return tf.nn.relu(tf.nn.bias_add( \
    tf.nn.conv2d(l_input, w, strides=[1, 1, 1, 1], padding='SAME'),b) \
    , name=name)
# 最大下采样操作
def max_pool(name, l_input, k):  
    return tf.nn.max_pool(l_input, ksize=[1, k, k, 1], \
    strides=[1, k, k, 1], padding='SAME', name=name)
# 归一化操作
def norm(name, l_input, lsize=4):  
    return tf.nn.lrn(l_input, lsize, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name=name)
# 定义整个网络
def alex_net(_X, _weights, _biases, _dropout):  
    _X = tf.reshape(_X, shape=[-1, 28, 28, 1]) # 向量转为矩阵
    # 卷积层
    conv1 = conv2d('conv1', _X, _weights['wc1'], _biases['bc1'])
    # 下采样层
    pool1 = max_pool('pool1', conv1, k=2)
    # 归一化层
    norm1 = norm('norm1', pool1, lsize=4)
    # Dropout
    norm1 = tf.nn.dropout(norm1, _dropout)
   
    # 卷积
    conv2 = conv2d('conv2', norm1, _weights['wc2'], _biases['bc2'])
    # 下采样
    pool2 = max_pool('pool2', conv2, k=2)
    # 归一化
    norm2 = norm('norm2', pool2, lsize=4)
    # Dropout
    norm2 = tf.nn.dropout(norm2, _dropout)
   
    # 卷积
    conv3 = conv2d('conv3', norm2, _weights['wc3'], _biases['bc3'])
    # 下采样
    pool3 = max_pool('pool3', conv3, k=2)
    # 归一化
    norm3 = norm('norm3', pool3, lsize=4)
    # Dropout
    norm3 = tf.nn.dropout(norm3, _dropout)
   
    # 全连接层,先把特征图转为向量
    dense1 = tf.reshape(norm3, [-1, _weights['wd1'].get_shape().as_list()[0]])
    dense1 = tf.nn.relu(tf.matmul(dense1, _weights['wd1']) + _biases['bd1'], name='fc1')
    # 全连接层
    dense2 = tf.nn.relu(tf.matmul(dense1, _weights['wd2']) + _biases['bd2'], name='fc2')
    # Relu activation
    # 网络输出层
    out = tf.matmul(dense2, _weights['out']) + _biases['out']
    return out
   
# 存储所有的网络参数
weights = {
    'wc1': tf.Variable(tf.random_normal([3, 3, 1, 64])),
    'wc2': tf.Variable(tf.random_normal([3, 3, 64, 128])),
    'wc3': tf.Variable(tf.random_normal([3, 3, 128, 256])),
    'wd1': tf.Variable(tf.random_normal([4*4*256, 1024])),
    'wd2': tf.Variable(tf.random_normal([1024, 1024])),
    'out': tf.Variable(tf.random_normal([1024, 10]))
}
biases = {
    'bc1': tf.Variable(tf.random_normal([64])),
    'bc2': tf.Variable(tf.random_normal([128])),
    'bc3': tf.Variable(tf.random_normal([256])),
    'bd1': tf.Variable(tf.random_normal([1024])),
    'bd2': tf.Variable(tf.random_normal([1024])),
    'out': tf.Variable(tf.random_normal([n_classes]))
}
# 构建模型
pred = alex_net(x, weights, biases, keep_prob)
# 定义损失函数和学习步骤
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(pred, y))
optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(cost)
# 测试网络
correct_pred = tf.equal(tf.argmax(pred,1), tf.argmax(y,1))
accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32))
# 初始化所有的共享变量
init = tf.initialize_all_variables()
# 开启一个训练
with tf.Session() as sess:
    sess.run(init)
    step = 1  
    # Keep training until reach max iterations
    while step * batch_size < training_iters:
        batch_xs, batch_ys = mnist.train.next_batch(batch_size)
        # 获取批数据
        sess.run(optimizer, feed_dict={x: batch_xs, y: batch_ys, keep_prob: dropout})
        if step % display_step == 0:
            # 计算精度
            acc = sess.run(accuracy, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})
            # 计算损失值
            loss = sess.run(cost, feed_dict={x: batch_xs, y: batch_ys, keep_prob: 1.})
            print "Iter " + str(step*batch_size) + ", Minibatch Loss= " + "{:.6f}".format(loss) + ", Training Accuracy= " + "{:.5f}".format(acc)
        step += 1  
    print "Optimization Finished!"  
    # 计算测试精度
    print "Testing Accuracy:", sess.run(accuracy, feed_dict={x: mnist.test.images[:256], y: mnist.test.labels[:256], keep_prob: 1.})
 
# 以上代码忽略了部分卷积层,全连接层使用了特定的权重。
           

keras实现

输入图片大小为 227*227*3
1. 卷积池化层
  卷积:96个卷积核,11*11*3的卷积核规模 步长为4 卷积后的特征矩阵规模为55*55*96 不填充
  池化:最大池化层,3 * 3池化规模 2步长 池化后的特征矩阵规模为27*27*96
2. 卷积池化层
  卷积:256个卷积核,5 * 5 * 96的卷积核规模 步长为1 填充为2 卷积后的特征矩阵规模为256*27*27
  池化:最大池化层,3 * 3池化规模 2步长 池化后的特征矩阵规模为13*13*256
3. 卷积层
  卷积:384个卷积核,3*3*256的卷积核规模 步长为1 填充为1 卷积后的特征矩阵规模为13*13*384
4. 卷积层
  卷积:384个卷积核,3*3*384的卷积核规模 步长为1 填充为1 卷积后的特征矩阵规模为13*13*384
5. 卷积池化层
  卷积:256个卷积核,3*3*384的卷积核规模 步长为1 填充为2 卷积后的特征矩阵规模为256*13*13
  池化:最大池化层,3 * 3池化规模 2步长 池化后的特征矩阵规模为6*6*256
6. 全连接层
  4096个神经元,6*6*256的卷积核规模 步长为1 不填充 卷积后的特征矩阵规模为1*1*4096
7. 全连接层
  卷积:4096个神经元,1*1*4096的卷积核规模 步长为1 不填充 卷积后的特征矩阵规模为1*1*4096
8. 全连接层
  1000个神经元,与fc7层全连接,经过softmax输出1000个值
           
from keras.models import Sequential
from keras.layers import Dense, Flatten, Dropout,Activation
from keras.layers.convolutional import Conv2D, MaxPooling2D

# AlexNet
model = Sequential()
# 第一层
model.add(Conv2D(filters=96, kernel_size=(11, 11),
                 strides=(4, 4), padding='valid',
                 input_shape=(227, 227, 3),
                 activation='relu'))

model.add(MaxPooling2D(pool_size=(3, 3),
                       strides=(2, 2),
                       padding='valid'))
# 第二层
model.add(Conv2D(filters=256, kernel_size=(5, 5),
                 strides=(1, 1), padding='same',
                 activation='relu'))

model.add(MaxPooling2D(pool_size=(3, 3),
                       strides=(2, 2),
                       padding='valid'))
# 第三层
model.add(Conv2D(filters=384, kernel_size=(3, 3),
                 strides=(1, 1), padding='same',
                 activation='relu'))
# 第四层
model.add(Conv2D(filters=384, kernel_size=(3, 3),
                 strides=(1, 1), padding='same',
                 activation='relu'))
# 第五层
model.add(Conv2D(filters=256, kernel_size=(3, 3),
                 strides=(1, 1), padding='same',
                 activation='relu'))
model.add(MaxPooling2D(pool_size=(3, 3),
                       strides=(2, 2), padding='valid'))
# 第六段
model.add(Flatten())
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
# 第七层
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
# 第八层
model.add(Dense(1000, activation='relu'))
model.add(Dropout(0.5))

# Output Layer
model.add(Dense(2))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='sgd',
              metrics=['accuracy'])
           

VGG16-Keras实现

VGG16有13个卷积层,3个全连接层,5个池化层,由于卷积层和全连接层具有权重系数,

因此也被称为权重层,一共16个权重层,因而得名

VGG16的突出特点是简单,体现在:

  1. 卷积层均采用相同的卷积核参数

    卷积层均表示为conv3-XXX,其中conv3说明该卷积层采用的卷积核的尺寸是3,

    即宽(width)和高(height)均为3,3*3是很小的卷积核尺寸,结合其它参数

    (步幅stride=1,填充方式padding=same),这样就能够使得每一个卷积层(张量)与前一层

    (张量)保持相同的宽和高。XXX代表卷积层的通道数。

  2. 池化层均采用相同的池化核参数

    池化层的参数均为2×

  3. 模型是由若干卷积层和池化层堆叠(stack)的方式构成,

    比较容易形成较深的网络结构(在2014年,16层已经被认为很深了)

VGG16输入224*224*3的图片
VGG16的卷积和池化共分为5块
1. 两个卷积一个池化
  卷积:64个卷积核 3*3的大小 填充方式为 padding=same 步长为1
  池化:2*2的规模 步长为2
2. 两个卷积一个池化
  卷积:128个卷积核 3*3的大小 填充方式为 padding=same 步长为1
  池化:2*2的规模 步长为2
3. 三个卷积一个池化
  卷积:256个卷积核 3*3的大小 填充方式为 padding=same 步长为1
  池化:2*2的规模 步长为2
4. 三个卷积一个池化
  卷积:512个卷积核 3*3的大小 填充方式为 padding=same 步长为1
  池化:2*2的规模 步长为2
5. 三个卷积一个池化
  卷积:512个卷积核 3*3的大小 填充方式为 padding=same 步长为1
  池化:2*2的规模 步长为2
全连接层
1. 4096个神经元
1. 4096个神经元
1. 1000个神经元 激活函数为softmax
           
from keras import Sequential
from keras.layers import Dense, Activation, Conv2D, MaxPooling2D, Flatten, Dropout
from keras.layers import Input
from keras.optimizers import SGD

model = Sequential()
 
# BLOCK 1
model.add(Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block1_conv1', input_shape = (224, 224, 3)))
model.add(Conv2D(filters = 64, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block1_conv2'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block1_pool'))
 
# BLOCK2
model.add(Conv2D(filters = 128, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block2_conv1'))
model.add(Conv2D(filters = 128, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block2_conv2'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block2_pool'))
 
# BLOCK3
model.add(Conv2D(filters = 256, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block3_conv1'))
model.add(Conv2D(filters = 256, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block3_conv2'))
model.add(Conv2D(filters = 256, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block3_conv3'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block3_pool'))
 
# BLOCK4
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block4_conv1'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block4_conv2'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block4_conv3'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block4_pool'))
 
# BLOCK5
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block5_conv1'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block5_conv2'))
model.add(Conv2D(filters = 512, kernel_size = (3, 3), activation = 'relu', padding = 'same', name = 'block5_conv3'))
model.add(MaxPooling2D(pool_size = (2, 2), strides = (2, 2), name = 'block5_pool'))
 
model.add(Flatten())
model.add(Dense(4096, activation = 'relu', name = 'fc1'))
model.add(Dropout(0.5))
model.add(Dense(4096, activation = 'relu', name = 'fc2'))
model.add(Dropout(0.5))
model.add(Dense(1000, activation = 'softmax', name = 'prediction'))
           

Keras还自带了一个VGG16的模型

import keras
model = keras.applications.vgg16.VGG16(include_top=True, weights='imagenet', input_tensor=None, input_shape=None, pooling=None, classes=1000)
           
dl