AlexNet
AlexNet由5个卷积层(包括最大池化层和局部响应归一化层),3个全连接层及1000个Softmax组成。为了减小全连接层的过拟合,我们采用了一种正则化方法“
dropout”。Alexnet的网络结构,采用是两台GPU服务器,所有会看到两个流程
2. 标准化(Local Response Normalization)
使用ReLU f(x)=max(0,x)后,你会发现激活函数之后的值没有了tanh、sigmoid函数那样有一个值域区间,所以一般在ReLU之后会做一个normalization,LRU就是稳重提出。
归一化有什么好处?
1.归一化有助于快速收敛;
2**.对局部神经元的活动创建竞争机制,使得其中响应比较大的值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。**
公式的解释
首先这个公式中的a表示卷积层(包括卷积操作和池化操作)后的输出结果,输出结果的结构是一个四维数组[batch,height,width,channel],
batch:批次数(每一批为一张图片)
height:图片高度,
width:图片宽度,
channel:通道数。可以理解成一批图片中的某一个图片经过卷积操作后输出的神经元个数(或是理解成处理后的图片深度)。
ai(x,y)表示在这个输出结构中的一个位置[a,b,c,d],可以理解成在某一张图中的某一个通道下的某个高度和某个宽度位置的点,即第a张图的第d个通道下的高度为b宽度为c的点。
a,n/2,k,α,β分别表示函数中的input,depth_radius,bias,alpha,beta,其中n/2,k,α,β都是自定义的,特别注意一下∑叠加的方向是沿着通道方向的,即每个点值的平方和是沿着a中的第3维channel方向的,也就是一个点同方向的前面n/2个通道(最小为第0个通道)和后n/2个通道(最大为第d-1个通道)的点的平方和(共n+1个点)。而函数的英文注解中也说明了把input当成是d个3维的矩阵,说白了就是把input的通道数当作3维矩阵的个数,叠加的方向也是在通道方向。i表示第i个核在位置(x,y)运用激活函数ReLU后的输出,n是同一位置上临近的kernal map的数目,N是kernal的总数。参数K,n,alpha,belta都是超参数
一般设置k=2,n=5,alpha=1Xe-4,beta=0.75。
代码实现与理解
Alexnet.py网络框架
# -*- coding=UTF-8 -*-
#定义AlexNet神经网络结构模型
#1.1建立模型图
import tensorflow as tf
import numpy as np
class AlexNet(object):
#keep_prob:dropout概率,num_classes:数据类别数,skip_layer
def __init__(self,x,keep_prob,num_classes,skip_layer,weights_path='DEFAULT'):
self.X=x #输入数据
self.NUM_CLASSES=num_classes#输出类别
self.KEEP_PROB=keep_prob
self.SKIP_LAYER=skip_layer
if weights_path=='DEFAULT':
self.WEIGHTS_PATH='D:\\Python base\\Test\AlexNet\\finetune_alexnet_with_tensorflow\\bvlc_alexnet.npy'
else:
self.WEIGHTS_PATH=weights_path
self.create()
def create(self):
#第一层:卷积层-->最大池化层-->LRN
conv1=conv_layer(self.X,11,11,96,4,4,padding='VALID',name='conv1')#卷96个11*11卷积核,步长为4,不填充
self.conv1=conv1
pool1=max_pool(conv1,3,3,2,2,padding='VALID',name='pool1')#最大池化层,3*3,步长为2
norm1=lrn(pool1,2,2e-05,0.75,name='norml')#局部响应归一化层,k=2,alpha=2e-05,b=0.75
#第二层:卷积层-->最大池化层-->LRN
conv2=conv_layer(norm1,5,5,256,1,1,groups=2,name='conv2')
self.conv2=conv2
pool2=max_pool(conv2,3,3,2,2,padding='VALID',name='pool2')
norm2=lrn(pool2,2,2e-05,0.75,name='norm2')
#第三层:卷积层
conv3=conv_layer(norm2,3,3,384,1,1,name='conv3')
self.conv3=conv3
#第四层:卷积层
conv4=conv_layer(conv3,3,3,384,1,1,groups=2,name='conv4')
self.conv4=conv4
#第五层:卷积层-->最大池化层
conv5=conv_layer(conv4,3,3,256,1,1,groups=2,name='conv5')
self.conv5=conv5
pool5=max_pool(conv5,3,3,2,2,padding='VALID',name='pool5')
#第六层:全连接层,在全连接层后加正则化层
flattened=tf.reshape(pool5,[-1,6*6*256])
fc6=fc_layer(flattened,6*6*256,4096,name='fc6')
dropout6=dropout(fc6,self.KEEP_PROB)
#第七层:全连接层
fc7=fc_layer(dropout6,4096,4096,name='fc7')
dropout7=dropout(fc7,self.KEEP_PROB)
#第八层:全连接层,不带激活函数
self.fc8=fc_layer(dropout7,4096,self.NUM_CLASSES,relu=False,name='fc8')
#加载神经网络预训练参数,将存储于self.WEIGHTS_PATH的预训练参数赋值给那些没有在self.SKIP_LAYER中指定的网络层的参数
def load_initial_weights(self,session):
#下载权重文件
weights_dict=np.load(self.WEIGHTS_PATH,encoding='bytes').item()
for op_name in weights_dict:
if op_name not in self.SKIP_LAYER:
with tf.variable_scope(op_name,reuse=True):
for data in weights_dict[op_name]:
#偏置项
if len(data.shape)==1:
var=tf.get_variable('biases',trainable=False)
session.run(var.assign(data))
#权重
else:
var=tf.get_variable('weights',trainable=False)
session.run(var.assign(data))
#1.2定义卷积层,当groups=1时,AlexNet网络不拆分;当groups=2时,AlexNet网络拆分成上下两个部分。
def conv_layer(x,filter_height,filter_width,num_filters,stride_y,stride_x,name,padding='SAME',groups=1):
#获得输入图像的通道数
input_channels=int(x.get_shape()[-1])
#创建lambda表达式
convovle=lambda i,k:tf.nn.conv2d(i,k,strides=[1,stride_y,stride_x,1],padding=padding)
with tf.variable_scope(name) as scope:
#创建卷积层所需的权重参数和偏置项参数,权重根据卷积的大小设置
weights=tf.get_variable("weights",shape=[filter_height,filter_width,input_channels/groups,num_filters])
biases=tf.get_variable("biases",shape=[num_filters])
if groups==1:
conv=convovle(x,weights)
#当groups不等于1时,拆分输入和权重
else:
input_groups=tf.split(axis=3,num_or_size_splits=groups,value=x)
weight_groups=tf.split(axis=3,num_or_size_splits=groups,value=weights)
output_groups=[convovle(i,k) for i,k in zip(input_groups,weight_groups)]
#单独计算完后,再次根据深度连接两个网络
conv=tf.concat(axis=3,values=output_groups)
#加上偏置项
bias=tf.reshape(tf.nn.bias_add(conv,biases),conv.get_shape().as_list())
#激活函数
relu=tf.nn.relu(bias,name=scope.name)
return relu
#1.6定义全连接层
def fc_layer(x,num_in,num_out,name,relu=True):
with tf.variable_scope(name) as scope:
#创建权重参数和偏置项
weights=tf.get_variable("weights",shape=[num_in,num_out],trainable=True)
biases=tf.get_variable("biases",[num_out],trainable=True)
#计算
act=tf.nn.xw_plus_b(x,weights,biases,name=scope.name)
if relu==True:
relu=tf.nn.relu(act)
return relu
else:
return act
#1.3定义最大池化层
def max_pool(x,filter_height,filter_width,stride_y,stride_x,name,padding='SAME'):
return tf.nn.max_pool(x,ksize=[1,filter_height,filter_width,1],strides=[1,stride_y,stride_x,1],padding=padding,name=name)
#1.4定义局部响应归一化LPN
def lrn(x,radius,alpha,beta,name,bias=1.0):
return tf.nn.local_response_normalization(x,depth_radius=radius,alpha=alpha,beta=beta,bias=bias,name=name)
#1.5定义dropout
def dropout(x,keep_prob):
return tf.nn.dropout(x,keep_prob)
测试模型
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 3 11:14:09 2020
@author: User
"""
#some basic imports and setups
import os
import cv2
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from alexnet import AlexNet
from caffe_classes import class_names
#mean of imagenet dataset in BGR
imagenet_mean = np.array([104., 117., 124.], dtype=np.float32)
current_dir = os.getcwd()#返回当前的工作目录
image_dir = os.path.join(current_dir, 'images')#进入到图像的工作路径
#get list of all images,是一个列表
img_files = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith('.jpeg')]
#load all images
imgs = []
for f in img_files:
img=cv2.imread(f)
imgs.append(img)
#plot images
fig = plt.figure(figsize=(15,6))
for i, img in enumerate(imgs):
fig.add_subplot(1,3,i+1)
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.axis('off')
#placeholder for input and dropout rate
x = tf.placeholder(tf.float32, [1, 227, 227, 3])
keep_prob = tf.placeholder(tf.float32)
#create model with default config ( == no skip_layer and 1000 units in the last layer)
model = AlexNet(x, keep_prob, 1000, [])
#define activation of last layer as score
score = model.fc8 #全连接层的输出结果
#create op to calculate softmax
softmax = tf.nn.softmax(score) #分类
with tf.Session() as sess:
# Initialize all variables
sess.run(tf.global_variables_initializer())
# Load the pretrained weights into the model
model.load_initial_weights(sess)#导入与训练模型
# Create figure handle
fig2 = plt.figure(figsize=(15,6))
# Loop over all images
for i, image in enumerate(imgs):
# Convert image to float32 and resize to (227x227)
img = cv2.resize(image.astype(np.float32), (227,227))
# Subtract the ImageNet mean
img -= imagenet_mean
# Reshape as needed to feed into model
img = img.reshape((1,227,227,3))
# Run the session and calculate the class probability
probs = sess.run(softmax, feed_dict={x: img, keep_prob: 1})
# Get the class name of the class with the highest probability
class_name = class_names[np.argmax(probs)]
# Plot image with class name and prob in the title
fig2.add_subplot(1,3\,i+1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title("Class: " + class_name + ", probability: %.4f" %probs[0,np.argmax(probs)])
plt.axis('off')
微调AlexNet
问题:不知道如何制作train.txt和val.txt
猫狗数据
先将猫和狗从训练集中区分开来,分别放到dog和cat文件夹下:
import glob
import shutil
import os
#数据集目录
path = "D:/Python base/Test/AlexNet/finetune_alexnet_with_tensorflow/trainimage"
#path="D:/Python base/Test/AlexNet/finetune_alexnet_with_tensorflow/dogs-vs-cats/train"
#current_dir = os.getcwd()#返回当前的工作目录
#path = os.path.join(current_dir, 'train')#进入到图像的工作路径
#训练集目录
train_path = path+'/train'
#测试集目录
test_path = path+'/test'
#将某类图片移动到该类的文件夹下
def img_to_file(path):
print("=========开始移动图片============")
#如果没有dog类和cat类文件夹,则新建
if not os.path.exists(path+"/dog"):
os.makedirs(path+"/dog")
if not os.path.exists(path+"/cat"):
os.makedirs(path+"/cat")
print("共:{}张图片".format(len(glob.glob(path+"/*.jpg"))))
#通过glob遍历到所有的.jpg文件
for imgPath in glob.glob(path+"/*.jpg"):
#print(imgPath)
#使用/划分
img=imgPath.strip("\n").replace("\\","/").split("/")
#print(img)
#将图片移动到指定的文件夹中
if img[-1].split(".")[0] == "cat":
shutil.move(imgPath,path+"/cat")
if img[-1].split(".")[0] == "dog":
shutil.move(imgPath,path+"/dog")
print("=========移动图片完成============")
img_to_file(train_path)
print("训练集猫共:{}张图片".format(len(glob.glob(train_path+"/cat/*.jpg"))))
print("训练集狗共:{}张图片".format(len(glob.glob(train_path+"/dog/*.jpg"))))
然后从dog中和cat中分别抽取图片作为测试集
import glob
import shutil
import os
#数据集目录
path = "D:/Python base/Test/AlexNet/finetune_alexnet_with_tensorflow/trainimage"
#path="D:/Python base/Test/AlexNet/finetune_alexnet_with_tensorflow/dogs-vs-cats/train"
#current_dir = os.getcwd()#返回当前的工作目录
#path = os.path.join(current_dir, 'train')#进入到图像的工作路径
#训练集目录
train_path = path+'/train'
#测试集目录
test_path = path+'/test'
#将某类图片移动到该类的文件夹下
def img_to_file(path):
print("=========开始移动图片============")
#如果没有dog类和cat类文件夹,则新建
if not os.path.exists(path+"/dog"):
os.makedirs(path+"/dog")
if not os.path.exists(path+"/cat"):
os.makedirs(path+"/cat")
print("共:{}张图片".format(len(glob.glob(path+"/*.jpg"))))
#通过glob遍历到所有的.jpg文件
for imgPath in glob.glob(path+"/*.jpg"):
#print(imgPath)
#使用/划分
img=imgPath.strip("\n").replace("\\","/").split("/")
#print(img)
#将图片移动到指定的文件夹中
if img[-1].split(".")[0] == "cat":
shutil.move(imgPath,path+"/cat")
if img[-1].split(".")[0] == "dog":
shutil.move(imgPath,path+"/dog")
print("=========移动图片完成============")
img_to_file(train_path)
print("训练集猫共:{}张图片".format(len(glob.glob(train_path+"/cat/*.jpg"))))
print("训练集狗共:{}张图片".format(len(glob.glob(train_path+"/dog/*.jpg"))))
import random
def split_train_test(fileDir,tarDir):
if not os.path.exists(tarDir):
os.makedirs(tarDir)
pathDir = os.listdir(fileDir) #取图片的原始路径
filenumber=len(pathDir)
rate=0.15 #自定义抽取图片的比例,比方说100张抽10张,那就是0.1
picknumber=int(filenumber*rate) #按照rate比例从文件夹中取一定数量图片
sample = random.sample(pathDir, picknumber) #随机选取picknumber数量的样本图片
print("=========开始移动图片============")
for name in sample:
shutil.move(fileDir+name, tarDir+name)
print("=========移动图片完成============")
split_train_test(train_path+'/dog/',test_path+'/dog/')
split_train_test(train_path+'/cat/',test_path+'/cat/')