首先论文中的原始图像大小有误,应该是 227 * 227,但是作者写成了 224 * 224。
第一层:原始图像大小为 227 * 227 * 3(长宽为 227 像素,rgb三通道)。使用的卷积核大小为 11 * 11 * 3,stride(步长)为 4,一共使用 96 个卷积核。得到的数据大小为 55 * 55 * 96 ( (227-11)/4+1=55 )。这里图中显示为 48 的原因是论文中将程序放在了两个 GPU上执行,并只在某些层(第三层和最后的全连接层)在两个 GPU 间进行通信。之后使用大小为 3 * 3 的核,步长为 2,进行最大池化操作,得到数据大小 27 * 27 * 96 ( (55-3)/2+1=27 )。最后使用大小为 5 * 5 的核进行归一化操作,得到第二层的输入大小为 27 * 27 * 96。
第二层:输入大小为 27 * 27 * 48(分到了两个GPU上运行,相当于只考虑了得到的一半特性的相关性,这样只是相当于对求得的特性的重新排列。唯一可能的影响是收敛的速度可能会下降)。使用的卷积核大小为 5 * 5 * 48,步长为 1,pad(填充)为 2,一共使用 256 个卷积核。得到的数据大小为 27 * 27 * 256 ( (27-5+2*2)/1+1=27 )。之后使用大小为 3 * 3 的核,步长为 2,进行最大池化操作,得到数据大小 13 * 13 * 256 ( (27-3)/2+1=13 )。最后使用大小为 5 * 5 的核进行归一化操作,得到第三层的输入大小为 13 * 13 * 256。
第三层:输入大小为 13 * 13 * 256(这里GPU之间进行通信,所以对所有提取的特征进行处理,不需要除2)。使用的卷积核大小为 3 * 3 * 256,步长为 1,pad(填充)为 1,一共使用 384 个卷积核。得到的数据大小为 13 * 13 * 384 ( (13-3+1*2)/1+1=13 )。这一层没有进行池化和归一化操作,因此第四层的输入为 13 * 13 * 384。
第四层:输入大小为 13 * 13 * 192(每个GPU)。使用的卷积核大小为 3 * 3 * 192,步长为 1,填充为 1,一共使用 192 个卷积核。得到的数据大小为 13 * 13 * 192 ( (13-3+1*2)/1+1=13 )。同样没有池化和归一化操作,因此第四层的输入为 13 * 13 * 384。
第五层:输入大小为 13 * 13 * 192(每个GPU)。使用的卷积核大小为 3 * 3 * 192,步长为 1,填充为 1,一共使用 128 个卷积核。得到的数据大小为 13 * 13 * 128 ( (13-3+2*1)/1+1=13 )。之后使用大小为 3 * 3 的核,步长为 2,进行最大池化操作,得到数据大小 6 * 6 * 128 ( (13-3)/2+1=6 )。不进行归一化操作,得到第六层的输入为 6 * 6 * 256。
第六层:输入大小为 6 * 6 * 256(两个GPU间通信)。使用包含 4096 个神经元的全连接网络,相当于将输入拉平,即上一层网络包含 6 * 6 * 256 = 9216 个神经元。得到第七层输入为 1 * 4096。这里为了限制网络大小,防止过拟合使用了 dropout。
第七层:输入大小为 1 * 4096(两GPU间通信)。同样使用包含 4096 个神经元的全连接网络,得到第八层输入为 1 * 4096。这里为了限制网络大小,防止过拟合,同样使用了 dropout。
第八层:输入大小为 1 * 4096(两GPU间通信)。由于是最后的输出层,且最后的分类数为 1000 类,因此使用包含 1000 个神经元的全连接网络,得到原始图片属于这 1000 类的可能性,最后可以通过连接 softmax 层得到最后的分类概率。
总体的逻辑还是比较清楚的,理解了卷积和池化操作的逻辑应该就不难理解了。需要注意的就是由于之中涉及两个 GPU 的操作,所以有些地方可能有些不同。
# coding: UTF-8
'''''''''''''''''''''''''''''''''''''''''''''''''''''
file name: alexnet.py
create time: 2017年03月29日 星期三 17时13分01秒
author: Jipeng Huang
e-mail: [email protected]
github: https://github.com/hjptriplebee
'''''''''''''''''''''''''''''''''''''''''''''''''''''
# based on Frederik Kratzert's alexNet with tensorflow
import tensorflow as tf
import numpy as np
# define different layer functions
# we usually don't do convolution and pooling on batch and channel
def maxPoolLayer(x, kHeight, kWidth, strideX, strideY, name, padding = "SAME"):
"""max-pooling"""
return tf.nn.max_pool(x, ksize=[1, kHeight, kWidth, 1],
strides=[1, strideX, strideY, 1], padding = padding, name = name)
def dropout(x, keepPro, name = None):
"""dropout"""
return tf.nn.dropout(x, keepPro, name)
def LRN(x, R, alpha, beta, name = None, bias = 1.0):
"""LRN"""
return tf.nn.local_response_normalization(x, depth_radius=R, alpha=alpha,
beta = beta, bias=bias, name=name)
def fcLayer(x, inputD, outputD, reluFlag, name):
"""fully-connect"""
with tf.variable_scope(name) as scope:
w = tf.get_variable("w", shape=[inputD, outputD], dtype="float")
b = tf.get_variable("b", [outputD], dtype="float")
out = tf.nn.xw_plus_b(x, w, b, name=scope.name)
if reluFlag:
return tf.nn.relu(out)
else:
return out
def convLayer(x, kHeight, kWidth, strideX, strideY,
featureNum, name, padding="SAME", groups=1):
"""convolution"""
channel = int(x.get_shape()[-1])
conv = lambda a, b: tf.nn.conv2d(a, b, strides=[1, strideY, strideX, 1], padding=padding)
with tf.variable_scope(name) as scope:
w = tf.get_variable("w", shape=[kHeight, kWidth, channel/groups, featureNum])
b = tf.get_variable("b", shape=[featureNum])
xNew = tf.split(value=x, num_or_size_splits=groups, axis=3)
wNew = tf.split(value=w, num_or_size_splits=groups, axis=3)
featureMap = [conv(t1, t2) for t1, t2 in zip(xNew, wNew)]
mergeFeatureMap = tf.concat(axis=3, values=featureMap)
# print mergeFeatureMap.shape
out = tf.nn.bias_add(mergeFeatureMap, b)
return tf.nn.relu(tf.reshape(out, mergeFeatureMap.get_shape().as_list()), name=scope.name)
class alexNet(object):
"""alexNet model"""
def __init__(self, x, keepPro, classNum, skip, modelPath="bvlc_alexnet.npy"):
self.X = x
self.KEEPPRO = keepPro
self.CLASSNUM = classNum
self.SKIP = skip
self.MODELPATH = modelPath
#build CNN
self.buildCNN()
def buildCNN(self):
"""build model"""
conv1 = convLayer(self.X, 11, 11, 4, 4, 96, "conv1", "VALID")
lrn1 = LRN(conv1, 2, 2e-05, 0.75, "norm1")
pool1 = maxPoolLayer(lrn1, 3, 3, 2, 2, "pool1", "VALID")
conv2 = convLayer(pool1, 5, 5, 1, 1, 256, "conv2", groups=2)
lrn2 = LRN(conv2, 2, 2e-05, 0.75, "lrn2")
pool2 = maxPoolLayer(lrn2, 3, 3, 2, 2, "pool2", "VALID")
conv3 = convLayer(pool2, 3, 3, 1, 1, 384, "conv3")
conv4 = convLayer(conv3, 3, 3, 1, 1, 384, "conv4", groups=2)
conv5 = convLayer(conv4, 3, 3, 1, 1, 256, "conv5", groups=2)
pool5 = maxPoolLayer(conv5, 3, 3, 2, 2, "pool5", "VALID")
fcIn = tf.reshape(pool5, [-1, 256 * 6 * 6])
fc1 = fcLayer(fcIn, 256 * 6 * 6, 4096, True, "fc6")
dropout1 = dropout(fc1, self.KEEPPRO)
fc2 = fcLayer(dropout1, 4096, 4096, True, "fc7")
dropout2 = dropout(fc2, self.KEEPPRO)
self.fc3 = fcLayer(dropout2, 4096, self.CLASSNUM, True, "fc8")
def loadModel(self, sess):
"""load model"""
wDict = np.load(self.MODELPATH, encoding="bytes").item()
#for layers in model
for name in wDict:
if name not in self.SKIP:
with tf.variable_scope(name, reuse=True):
for p in wDict[name]:
if len(p.shape) == 1:
#bias
sess.run(tf.get_variable('b', trainable=False).assign(p))
else:
#weights
sess.run(tf.get_variable('w', trainable=False).assign(p))
下面是测试代码
#!/usr/bin/env python
# coding: UTF-8
'''''''''''''''''''''''''''''''''''''''''''''''''''''
file name: testModel.py
create time: 2017年03月31日 星期五 15时48分05秒
author: Jipeng Huang
e-mail: [email protected]
github: https://github.com/hjptriplebee
'''''''''''''''''''''''''''''''''''''''''''''''''''''
import os
import urllib.request
import argparse
import sys
import alexnet
import cv2
import tensorflow as tf
import numpy as np
import caffe_classes
parser = argparse.ArgumentParser(description='Classify some images.')
parser.add_argument('-m', '--mode', choices=['folder', 'url'], default='folder')
parser.add_argument('-p', '--path', help='Specify a path [e.g. testModel]', default='testModel')
args = parser.parse_args(sys.argv[1:])
if args.mode == 'folder':
#get testImage
withPath = lambda f: '{}/{}'.format(args.path, f)
testImg = dict((f, cv2.imread(withPath(f))) for f in os.listdir(args.path) if os.path.isfile(withPath(f)))
elif args.mode == 'url':
def url2img(url):
'''url to image'''
resp = urllib.request.urlopen(url)
image = np.asarray(bytearray(resp.read()), dtype="uint8")
image = cv2.imdecode(image, cv2.IMREAD_COLOR)
return image
testImg = {args.path:url2img(args.path)}
# noinspection PyUnboundLocalVariable
if testImg.values():
#some params
dropoutPro = 1
classNum = 1000
skip = []
imgMean = np.array([104, 117, 124], np.float)
x = tf.placeholder("float", [1, 227, 227, 3])
model = alexnet.alexNet(x, dropoutPro, classNum, skip)
score = model.fc3
softmax = tf.nn.softmax(score)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
model.loadModel(sess)
for key, img in testImg.items():
#img preprocess
resized = cv2.resize(img.astype(np.float), (227, 227)) - imgMean
maxx = np.argmax(sess.run(softmax, feed_dict={x: resized.reshape((1, 227, 227, 3))}))
res = caffe_classes.class_names[maxx]
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, res, (int(img.shape[0]/3), int(img.shape[1]/3)), font, 1, (0, 255, 0), 2)
print("{}: {}\n----".format(key, res))
cv2.imshow("demo", img)
cv2.waitKey(0)
光有这两个文件无法运行,具体的项目可以参考: https://github.com/hjptriplebee/AlexNet_with_tensorflow